From bd89554cadcf82bef29e0236a75bf7bbcae93d90 Mon Sep 17 00:00:00 2001 From: PThorpe92 Date: Mon, 29 Sep 2025 09:44:38 -0400 Subject: [PATCH] Remove vendored parser now that we have our own --- .../sqlite3-parser/.github/dependabot.yml | 11 - .../sqlite3-parser/.github/workflows/rust.yml | 36 - vendored/sqlite3-parser/.gitignore | 6 - vendored/sqlite3-parser/CMakeLists.txt | 5 - vendored/sqlite3-parser/Cargo.toml | 47 - vendored/sqlite3-parser/LICENSE | 24 - vendored/sqlite3-parser/README.md | 79 - vendored/sqlite3-parser/Sync.md | 7 - vendored/sqlite3-parser/benches/keyword.rs | 154 - vendored/sqlite3-parser/build.rs | 331 - vendored/sqlite3-parser/checks.md | 124 - vendored/sqlite3-parser/examples/simple.y | 123 - vendored/sqlite3-parser/examples/sql_check.rs | 55 - vendored/sqlite3-parser/examples/sql_cmd.rs | 26 - vendored/sqlite3-parser/examples/sql_cmds.rs | 42 - .../sqlite3-parser/examples/sql_tokens.rs | 90 - .../sqlite3-parser/sqlparser_bench/Cargo.toml | 19 - .../sqlite3-parser/sqlparser_bench/README.md | 32 - .../benches/sqlparser_bench.rs | 194 - vendored/sqlite3-parser/src/dialect/mod.rs | 409 -- vendored/sqlite3-parser/src/dialect/token.rs | 181 - vendored/sqlite3-parser/src/lexer/mod.rs | 6 - vendored/sqlite3-parser/src/lexer/scan.rs | 173 - .../sqlite3-parser/src/lexer/sql/error.rs | 148 - vendored/sqlite3-parser/src/lexer/sql/mod.rs | 762 --- vendored/sqlite3-parser/src/lexer/sql/test.rs | 375 -- vendored/sqlite3-parser/src/lib.rs | 9 - .../sqlite3-parser/src/parser/ast/check.rs | 358 - vendored/sqlite3-parser/src/parser/ast/fmt.rs | 2416 ------- vendored/sqlite3-parser/src/parser/ast/mod.rs | 2233 ------- vendored/sqlite3-parser/src/parser/mod.rs | 154 - vendored/sqlite3-parser/src/parser/parse.y | 1503 ----- .../sqlite3-parser/src/to_sql_string/expr.rs | 2 - .../sqlite3-parser/src/to_sql_string/mod.rs | 37 - .../src/to_sql_string/stmt/alter_table.rs | 74 - .../src/to_sql_string/stmt/create_table.rs | 121 - .../src/to_sql_string/stmt/create_trigger.rs | 59 - .../stmt/create_virtual_table.rs | 64 - .../src/to_sql_string/stmt/delete.rs | 82 - .../src/to_sql_string/stmt/insert.rs | 94 - .../src/to_sql_string/stmt/mod.rs | 229 - .../src/to_sql_string/stmt/select.rs | 138 - .../src/to_sql_string/stmt/update.rs | 94 - .../sqlite3-parser/third_party/lemon/lemon.c | 5881 ----------------- .../third_party/lemon/lempar.rs | 919 --- 45 files changed, 17926 deletions(-) delete mode 100644 vendored/sqlite3-parser/.github/dependabot.yml delete mode 100644 vendored/sqlite3-parser/.github/workflows/rust.yml delete mode 100644 vendored/sqlite3-parser/.gitignore delete mode 100644 vendored/sqlite3-parser/CMakeLists.txt delete mode 100644 vendored/sqlite3-parser/Cargo.toml delete mode 100644 vendored/sqlite3-parser/LICENSE delete mode 100644 vendored/sqlite3-parser/README.md delete mode 100644 vendored/sqlite3-parser/Sync.md delete mode 100644 vendored/sqlite3-parser/benches/keyword.rs delete mode 100644 vendored/sqlite3-parser/build.rs delete mode 100644 vendored/sqlite3-parser/checks.md delete mode 100644 vendored/sqlite3-parser/examples/simple.y delete mode 100644 vendored/sqlite3-parser/examples/sql_check.rs delete mode 100644 vendored/sqlite3-parser/examples/sql_cmd.rs delete mode 100644 vendored/sqlite3-parser/examples/sql_cmds.rs delete mode 100644 vendored/sqlite3-parser/examples/sql_tokens.rs delete mode 100644 vendored/sqlite3-parser/sqlparser_bench/Cargo.toml delete mode 100644 vendored/sqlite3-parser/sqlparser_bench/README.md delete mode 100644 vendored/sqlite3-parser/sqlparser_bench/benches/sqlparser_bench.rs delete mode 100644 vendored/sqlite3-parser/src/dialect/mod.rs delete mode 100644 vendored/sqlite3-parser/src/dialect/token.rs delete mode 100644 vendored/sqlite3-parser/src/lexer/mod.rs delete mode 100644 vendored/sqlite3-parser/src/lexer/scan.rs delete mode 100644 vendored/sqlite3-parser/src/lexer/sql/error.rs delete mode 100644 vendored/sqlite3-parser/src/lexer/sql/mod.rs delete mode 100644 vendored/sqlite3-parser/src/lexer/sql/test.rs delete mode 100644 vendored/sqlite3-parser/src/lib.rs delete mode 100644 vendored/sqlite3-parser/src/parser/ast/check.rs delete mode 100644 vendored/sqlite3-parser/src/parser/ast/fmt.rs delete mode 100644 vendored/sqlite3-parser/src/parser/ast/mod.rs delete mode 100644 vendored/sqlite3-parser/src/parser/mod.rs delete mode 100644 vendored/sqlite3-parser/src/parser/parse.y delete mode 100644 vendored/sqlite3-parser/src/to_sql_string/expr.rs delete mode 100644 vendored/sqlite3-parser/src/to_sql_string/mod.rs delete mode 100644 vendored/sqlite3-parser/src/to_sql_string/stmt/alter_table.rs delete mode 100644 vendored/sqlite3-parser/src/to_sql_string/stmt/create_table.rs delete mode 100644 vendored/sqlite3-parser/src/to_sql_string/stmt/create_trigger.rs delete mode 100644 vendored/sqlite3-parser/src/to_sql_string/stmt/create_virtual_table.rs delete mode 100644 vendored/sqlite3-parser/src/to_sql_string/stmt/delete.rs delete mode 100644 vendored/sqlite3-parser/src/to_sql_string/stmt/insert.rs delete mode 100644 vendored/sqlite3-parser/src/to_sql_string/stmt/mod.rs delete mode 100644 vendored/sqlite3-parser/src/to_sql_string/stmt/select.rs delete mode 100644 vendored/sqlite3-parser/src/to_sql_string/stmt/update.rs delete mode 100644 vendored/sqlite3-parser/third_party/lemon/lemon.c delete mode 100644 vendored/sqlite3-parser/third_party/lemon/lempar.rs diff --git a/vendored/sqlite3-parser/.github/dependabot.yml b/vendored/sqlite3-parser/.github/dependabot.yml deleted file mode 100644 index e8d486ab3..000000000 --- a/vendored/sqlite3-parser/.github/dependabot.yml +++ /dev/null @@ -1,11 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "cargo" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "weekly" diff --git a/vendored/sqlite3-parser/.github/workflows/rust.yml b/vendored/sqlite3-parser/.github/workflows/rust.yml deleted file mode 100644 index c3cc0a82b..000000000 --- a/vendored/sqlite3-parser/.github/workflows/rust.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: CI - -on: - push: - branches: [master] - pull_request: - branches: [master] -permissions: - contents: read - -jobs: - build: - strategy: - matrix: - os: [ubuntu-latest, windows-latest] - - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v4 - - name: Build - run: cargo build - - name: Run tests - run: cargo test - - direct-minimal-versions: - name: Test min versions - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: hecrj/setup-rust-action@v2 - with: - rust-version: nightly - - run: | - cargo update -Z direct-minimal-versions - cargo test diff --git a/vendored/sqlite3-parser/.gitignore b/vendored/sqlite3-parser/.gitignore deleted file mode 100644 index dc98fe4b6..000000000 --- a/vendored/sqlite3-parser/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -target -rlemon -*.h -*.out -Cargo.lock -cmake-build-debug \ No newline at end of file diff --git a/vendored/sqlite3-parser/CMakeLists.txt b/vendored/sqlite3-parser/CMakeLists.txt deleted file mode 100644 index 466dbc70a..000000000 --- a/vendored/sqlite3-parser/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -cmake_minimum_required(VERSION 3.6) -project(rlemon) - -set(SOURCE_FILES third_party/lemon/lemon.c) -add_executable(rlemon ${SOURCE_FILES}) \ No newline at end of file diff --git a/vendored/sqlite3-parser/Cargo.toml b/vendored/sqlite3-parser/Cargo.toml deleted file mode 100644 index 0b6b39f8e..000000000 --- a/vendored/sqlite3-parser/Cargo.toml +++ /dev/null @@ -1,47 +0,0 @@ -[package] -name = "turso_sqlite3_parser" -version.workspace = true -edition.workspace = true -authors = ["gwenn"] -description = "SQL parser (as understood by SQLite)" -documentation = "http://docs.rs/sqlite3-parser" -repository = "https://github.com/gwenn/lemon-rs" -readme = "README.md" -categories = ["parser-implementations"] -keywords = ["sql", "parser", "scanner", "tokenizer"] -license = "Apache-2.0/MIT" -build = "build.rs" # Lemon preprocessing - -[badges] -maintenance = { status = "experimental" } - -[features] -# FIXME: specific to one parser, not global -YYTRACKMAXSTACKDEPTH = [] -YYNOERRORRECOVERY = [] -YYCOVERAGE = [] -NDEBUG = [] -default = ["YYNOERRORRECOVERY", "NDEBUG"] -serde = ["dep:serde", "indexmap/serde", "bitflags/serde"] - -[dependencies] -log = "0.4.22" -memchr = "2.0" -fallible-iterator = { workspace = true } -bitflags = { workspace = true } -indexmap = { workspace = true } -miette = { workspace = true } -strum = { workspace = true } -strum_macros = {workspace = true } -serde = { workspace = true , optional = true, features = ["derive"] } -smallvec = { version = "1.15.1", features = ["const_generics"] } - -[dev-dependencies] -env_logger = { workspace = true, default-features = false } - -[build-dependencies] -cc = "1.0" - -[lints.rust] -dead_code = "allow" -non_snake_case = "allow" diff --git a/vendored/sqlite3-parser/LICENSE b/vendored/sqlite3-parser/LICENSE deleted file mode 100644 index cf1ab25da..000000000 --- a/vendored/sqlite3-parser/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to diff --git a/vendored/sqlite3-parser/README.md b/vendored/sqlite3-parser/README.md deleted file mode 100644 index 0197a6ef9..000000000 --- a/vendored/sqlite3-parser/README.md +++ /dev/null @@ -1,79 +0,0 @@ -[![Build Status](https://github.com/gwenn/lemon-rs/workflows/CI/badge.svg)](https://github.com/gwenn/lemon-rs/actions) -[![Latest Version](https://img.shields.io/crates/v/sqlite3-parser.svg)](https://crates.io/crates/sqlite3-parser) -[![Docs](https://docs.rs/sqlite3-parser/badge.svg)](https://docs.rs/sqlite3-parser) -[![dependency status](https://deps.rs/repo/github/gwenn/lemon-rs/status.svg)](https://deps.rs/repo/github/gwenn/lemon-rs) - -[LEMON parser generator](https://www.sqlite.org/src/doc/trunk/doc/lemon.html) modified to generate Rust code. - -Lemon source and SQLite3 grammar were last synced as of July 2024. - -## Unsupported - -### Unsupported Grammar syntax - -* `%token_destructor`: Code to execute to destroy token data -* `%default_destructor`: Code for the default non-terminal destructor -* `%destructor`: Code which executes whenever this symbol is - popped from the stack during error processing - -https://www.codeproject.com/Articles/1056460/Generating-a-High-Speed-Parser-Part-Lemon -https://www.sqlite.org/lemon.html - -### SQLite - -[SQLite lexer](http://www.sqlite.org/src/artifact?ci=trunk&filename=src/tokenize.c) and [SQLite parser](http://www.sqlite.org/src/artifact?ci=trunk&filename=src/parse.y) have been ported from C to Rust. -The parser generates an AST. - -Lexer/Parser: - - Keep track of position (line, column). - - Streamable (stop at the end of statement). - - Resumable (restart after the end of statement). - -Lexer and parser have been tested with the following scripts: - * https://github.com/bkiers/sqlite-parser/tree/master/src/test/resources - * https://github.com/codeschool/sqlite-parser/tree/master/test/sql/official-suite which can be updated with script in https://github.com/codeschool/sqlite-parser/tree/master/test/misc - -TODO: - - [ ] Check generated AST (reparse/reinject) - - [ ] [If a keyword in double quotes is used in a context where it cannot be resolved to an identifier but where a string literal is allowed, then the token is understood to be a string literal instead of an identifier.](https://sqlite.org/lang_keywords.html) - - [ ] Tests - - [ ] Do not panic while parsing - - [x] CREATE VIRTUAL TABLE args - - [ ] Zero copy (at least tokens) - -### Unsupported by Rust - -* `#line` directive - -## API change - -* No `ParseAlloc`/`ParseFree` anymore - -## Features not tested - -* NDEBUG -* YYNOERRORRECOVERY -* YYERRORSYMBOL - -## To be fixed - -* RHS are moved. Maybe it is not a problem if they are always used once. - Just add a check in lemon... -* `%extra_argument` is not supported. -* Terminal symbols generated by lemon should be dumped in a specified file. - -## Raison d'être - -* [lemon_rust](https://github.com/rodrigorc/lemon_rust) does the same thing -but with an old version of `lemon`. And it seems not possible to use `yystack` -as a stack because items may be access randomly and the `top+1` item can be used. - -* [lalrpop](https://github.com/nikomatsakis/lalrpop) would be the perfect -alternative but it does not support fallback/streaming -(see [this](https://github.com/nikomatsakis/lalrpop/issues/156) issue) -and compilation/generation is slow. - -## Minimum supported Rust version (MSRV) - -Latest stable Rust version at the time of release. It might compile with older versions. - diff --git a/vendored/sqlite3-parser/Sync.md b/vendored/sqlite3-parser/Sync.md deleted file mode 100644 index 5bd0ff4a7..000000000 --- a/vendored/sqlite3-parser/Sync.md +++ /dev/null @@ -1,7 +0,0 @@ -When some changes happen in the official SQLite repository, -they can be applied locally: - - $SQLITE/tool/lemon.c => $RLEMON/third_party/lemon.c - - $SQLITE/tool/lempar.c => $RLEMON/third_party/lempar.rs - - $SQLITE/tool/mkkeywordhash.c => $RLEMON/src/dialect/mod.rs - - $SQLITE/src/tokenize.c => $RLEMON/src/lexer/sql/mod.rs - - $SQLITE/src/parse.y => $RLEMON/src/parser/parse.y (and $RLEMON/src/dialect/token.rs, $RLEMON/src/dialect/mod.rs) diff --git a/vendored/sqlite3-parser/benches/keyword.rs b/vendored/sqlite3-parser/benches/keyword.rs deleted file mode 100644 index a79250b13..000000000 --- a/vendored/sqlite3-parser/benches/keyword.rs +++ /dev/null @@ -1,154 +0,0 @@ -#![cfg(all(test, not(test)))] // never compile this -#![feature(test)] -extern crate test; - -use test::Bencher; -use turso_sqlite3_parser::dialect::keyword_token; - -static VALUES: [&[u8]; 136] = [ - b"ABORT", - b"ACTION", - b"ADD", - b"AFTER", - b"ALL", - b"ALTER", - b"ANALYZE", - b"AND", - b"AS", - b"ASC", - b"ATTACH", - b"AUTOINCREMENT", - b"BEFORE", - b"BEGIN", - b"BETWEEN", - b"BY", - b"CASCADE", - b"CASE", - b"CAST", - b"CHECK", - b"COLLATE", - b"COLUMN", - b"COMMIT", - b"CONFLICT", - b"CONSTRAINT", - b"CREATE", - b"CROSS", - b"CURRENT", - b"CURRENT_DATE", - b"CURRENT_TIME", - b"CURRENT_TIMESTAMP", - b"DATABASE", - b"DEFAULT", - b"DEFERRABLE", - b"DEFERRED", - b"DELETE", - b"DESC", - b"DETACH", - b"DISTINCT", - b"DO", - b"DROP", - b"EACH", - b"ELSE", - b"END", - b"ESCAPE", - b"EXCEPT", - b"EXCLUSIVE", - b"EXISTS", - b"EXPLAIN", - b"FAIL", - b"FILTER", - b"FOLLOWING", - b"FOR", - b"FOREIGN", - b"FROM", - b"FULL", - b"GLOB", - b"GROUP", - b"HAVING", - b"IF", - b"IGNORE", - b"IMMEDIATE", - b"IN", - b"INDEX", - b"INDEXED", - b"INITIALLY", - b"INNER", - b"INSERT", - b"INSTEAD", - b"INTERSECT", - b"INTO", - b"IS", - b"ISNULL", - b"JOIN", - b"KEY", - b"LEFT", - b"LIKE", - b"LIMIT", - b"MATCH", - b"NATURAL", - b"NO", - b"NOT", - b"NOTHING", - b"NOTNULL", - b"NULL", - b"OF", - b"OFFSET", - b"ON", - b"OR", - b"ORDER", - b"OUTER", - b"OVER", - b"PARTITION", - b"PLAN", - b"PRAGMA", - b"PRECEDING", - b"PRIMARY", - b"QUERY", - b"RAISE", - b"RANGE", - b"RECURSIVE", - b"REFERENCES", - b"REGEXP", - b"REINDEX", - b"RELEASE", - b"RENAME", - b"REPLACE", - b"RESTRICT", - b"RIGHT", - b"ROLLBACK", - b"ROW", - b"ROWS", - b"SAVEPOINT", - b"SELECT", - b"SET", - b"TABLE", - b"TEMP", - b"TEMPORARY", - b"THEN", - b"TO", - b"TRANSACTION", - b"TRIGGER", - b"UNBOUNDED", - b"UNION", - b"UNIQUE", - b"UPDATE", - b"USING", - b"VACUUM", - b"VALUES", - b"VIEW", - b"VIRTUAL", - b"WHEN", - b"WHERE", - b"WINDOW", - b"WITH", - b"WITHOUT", -]; - -#[bench] -fn bench_keyword_token(b: &mut Bencher) { - b.iter(|| { - for value in &VALUES { - assert!(keyword_token(value).is_some()) - } - }); -} diff --git a/vendored/sqlite3-parser/build.rs b/vendored/sqlite3-parser/build.rs deleted file mode 100644 index b052232f2..000000000 --- a/vendored/sqlite3-parser/build.rs +++ /dev/null @@ -1,331 +0,0 @@ -use std::collections::HashMap; -use std::env; -use std::fs::File; -use std::io::{BufWriter, Result, Write}; -use std::path::Path; -use std::process::Command; - -use cc::Build; - -/// generates a trie-like function with nested match expressions for parsing SQL keywords -/// example: input: [["ABORT", "TK_ABORT"], ["ACTION", "TK_ACTION"], ["ADD", "TK_ADD"],] -/// A -/// ├─ B -/// │ ├─ O -/// │ │ ├─ R -/// │ │ │ ├─ T -> TK_ABORT -/// ├─ C -/// │ ├─ T -/// │ │ ├─ I -/// │ │ │ ├─ O -/// │ │ │ │ ├─ N -> TK_ACTION -/// ├─ D -/// │ ├─ D -> TK_ADD -fn build_keyword_map( - writer: &mut impl Write, - func_name: &str, - keywords: &[[&'static str; 2]], -) -> Result<()> { - assert!(!keywords.is_empty()); - let mut min_len = keywords[0][0].len(); - let mut max_len = keywords[0][0].len(); - - struct PathEntry { - result: Option<&'static str>, - sub_entries: HashMap>, - } - - let mut paths = Box::new(PathEntry { - result: None, - sub_entries: HashMap::new(), - }); - - for keyword in keywords { - let keyword_b = keyword[0].as_bytes(); - - if keyword_b.len() < min_len { - min_len = keyword_b.len(); - } - - if keyword_b.len() > max_len { - max_len = keyword_b.len(); - } - - let mut current = &mut paths; - - for &b in keyword_b { - let upper_b = b.to_ascii_uppercase(); - - match current.sub_entries.get(&upper_b) { - Some(_) => { - current = current.sub_entries.get_mut(&upper_b).unwrap(); - } - None => { - let new_entry = Box::new(PathEntry { - result: None, - sub_entries: HashMap::new(), - }); - current.sub_entries.insert(upper_b, new_entry); - current = current.sub_entries.get_mut(&upper_b).unwrap(); - } - } - } - - assert!(current.result.is_none()); - current.result = Some(keyword[1]); - } - - fn write_entry(writer: &mut impl Write, entry: &PathEntry) -> Result<()> { - if let Some(result) = entry.result { - writeln!(writer, "if idx == buf.len() {{")?; - writeln!(writer, "return Some(TokenType::{result});")?; - writeln!(writer, "}}")?; - } - - if entry.sub_entries.is_empty() { - writeln!(writer, "None")?; - return Ok(()); - } - - writeln!(writer, "if idx >= buf.len() {{")?; - writeln!(writer, "return None;")?; - writeln!(writer, "}}")?; - - writeln!(writer, "match buf[idx] {{")?; - for (&b, sub_entry) in &entry.sub_entries { - if b.is_ascii_alphabetic() { - writeln!(writer, "{} | {} => {{", b, b.to_ascii_lowercase())?; - } else { - writeln!(writer, "{b} => {{")?; - } - writeln!(writer, "idx += 1;")?; - write_entry(writer, sub_entry)?; - writeln!(writer, "}}")?; - } - - writeln!(writer, "_ => None")?; - writeln!(writer, "}}")?; - Ok(()) - } - - writeln!( - writer, - "pub(crate) const MAX_KEYWORD_LEN: usize = {max_len};" - )?; - writeln!( - writer, - "pub(crate) const MIN_KEYWORD_LEN: usize = {min_len};" - )?; - writeln!(writer, "/// Check if `word` is a keyword")?; - writeln!( - writer, - "pub fn {func_name}(buf: &[u8]) -> Option {{" - )?; - writeln!( - writer, - "if buf.len() < MIN_KEYWORD_LEN || buf.len() > MAX_KEYWORD_LEN {{" - )?; - writeln!(writer, "return None;")?; - writeln!(writer, "}}")?; - writeln!(writer, "let mut idx = 0;")?; - write_entry(writer, &paths)?; - writeln!(writer, "}}")?; - Ok(()) -} - -fn main() -> Result<()> { - let out_dir = env::var("OUT_DIR").unwrap(); - let out_path = Path::new(&out_dir); - let rlemon = out_path.join("rlemon"); - - let lemon_src_dir = Path::new("third_party").join("lemon"); - let rlemon_src = lemon_src_dir.join("lemon.c"); - - // compile rlemon: - { - assert!(Build::new() - .target(&env::var("HOST").unwrap()) - .get_compiler() - .to_command() - .arg("-o") - .arg(rlemon.clone()) - .arg(rlemon_src) - .status()? - .success()); - } - - let sql_parser = "src/parser/parse.y"; - // run rlemon / generate parser: - { - assert!(Command::new(rlemon) - .arg("-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT") - .arg("-Tthird_party/lemon/lempar.rs") - .arg(format!("-d{out_dir}")) - .arg(sql_parser) - .status()? - .success()); - // TODO ./rlemon -m -Tthird_party/lemon/lempar.rs examples/simple.y - } - - let keywords = out_path.join("keywords.rs"); - let mut keywords = BufWriter::new(File::create(keywords)?); - build_keyword_map( - &mut keywords, - "keyword_token", - &[ - ["ABORT", "TK_ABORT"], - ["ACTION", "TK_ACTION"], - ["ADD", "TK_ADD"], - ["AFTER", "TK_AFTER"], - ["ALL", "TK_ALL"], - ["ALTER", "TK_ALTER"], - ["ALWAYS", "TK_ALWAYS"], - ["ANALYZE", "TK_ANALYZE"], - ["AND", "TK_AND"], - ["AS", "TK_AS"], - ["ASC", "TK_ASC"], - ["ATTACH", "TK_ATTACH"], - ["AUTOINCREMENT", "TK_AUTOINCR"], - ["BEFORE", "TK_BEFORE"], - ["BEGIN", "TK_BEGIN"], - ["BETWEEN", "TK_BETWEEN"], - ["BY", "TK_BY"], - ["CASCADE", "TK_CASCADE"], - ["CASE", "TK_CASE"], - ["CAST", "TK_CAST"], - ["CHECK", "TK_CHECK"], - ["COLLATE", "TK_COLLATE"], - ["COLUMN", "TK_COLUMNKW"], - ["COMMIT", "TK_COMMIT"], - ["CONFLICT", "TK_CONFLICT"], - ["CONSTRAINT", "TK_CONSTRAINT"], - ["CREATE", "TK_CREATE"], - ["CROSS", "TK_JOIN_KW"], - ["CURRENT", "TK_CURRENT"], - ["CURRENT_DATE", "TK_CTIME_KW"], - ["CURRENT_TIME", "TK_CTIME_KW"], - ["CURRENT_TIMESTAMP", "TK_CTIME_KW"], - ["DATABASE", "TK_DATABASE"], - ["DEFAULT", "TK_DEFAULT"], - ["DEFERRABLE", "TK_DEFERRABLE"], - ["DEFERRED", "TK_DEFERRED"], - ["DELETE", "TK_DELETE"], - ["DESC", "TK_DESC"], - ["DETACH", "TK_DETACH"], - ["DISTINCT", "TK_DISTINCT"], - ["DO", "TK_DO"], - ["DROP", "TK_DROP"], - ["EACH", "TK_EACH"], - ["ELSE", "TK_ELSE"], - ["END", "TK_END"], - ["ESCAPE", "TK_ESCAPE"], - ["EXCEPT", "TK_EXCEPT"], - ["EXCLUDE", "TK_EXCLUDE"], - ["EXCLUSIVE", "TK_EXCLUSIVE"], - ["EXISTS", "TK_EXISTS"], - ["EXPLAIN", "TK_EXPLAIN"], - ["FAIL", "TK_FAIL"], - ["FILTER", "TK_FILTER"], - ["FIRST", "TK_FIRST"], - ["FOLLOWING", "TK_FOLLOWING"], - ["FOR", "TK_FOR"], - ["FOREIGN", "TK_FOREIGN"], - ["FROM", "TK_FROM"], - ["FULL", "TK_JOIN_KW"], - ["GENERATED", "TK_GENERATED"], - ["GLOB", "TK_LIKE_KW"], - ["GROUP", "TK_GROUP"], - ["GROUPS", "TK_GROUPS"], - ["HAVING", "TK_HAVING"], - ["IF", "TK_IF"], - ["IGNORE", "TK_IGNORE"], - ["IMMEDIATE", "TK_IMMEDIATE"], - ["IN", "TK_IN"], - ["INDEX", "TK_INDEX"], - ["INDEXED", "TK_INDEXED"], - ["INITIALLY", "TK_INITIALLY"], - ["INNER", "TK_JOIN_KW"], - ["INSERT", "TK_INSERT"], - ["INSTEAD", "TK_INSTEAD"], - ["INTERSECT", "TK_INTERSECT"], - ["INTO", "TK_INTO"], - ["IS", "TK_IS"], - ["ISNULL", "TK_ISNULL"], - ["JOIN", "TK_JOIN"], - ["KEY", "TK_KEY"], - ["LAST", "TK_LAST"], - ["LEFT", "TK_JOIN_KW"], - ["LIKE", "TK_LIKE_KW"], - ["LIMIT", "TK_LIMIT"], - ["MATCH", "TK_MATCH"], - ["MATERIALIZED", "TK_MATERIALIZED"], - ["NATURAL", "TK_JOIN_KW"], - ["NO", "TK_NO"], - ["NOT", "TK_NOT"], - ["NOTHING", "TK_NOTHING"], - ["NOTNULL", "TK_NOTNULL"], - ["NULL", "TK_NULL"], - ["NULLS", "TK_NULLS"], - ["OF", "TK_OF"], - ["OFFSET", "TK_OFFSET"], - ["ON", "TK_ON"], - ["OR", "TK_OR"], - ["ORDER", "TK_ORDER"], - ["OTHERS", "TK_OTHERS"], - ["OUTER", "TK_JOIN_KW"], - ["OVER", "TK_OVER"], - ["PARTITION", "TK_PARTITION"], - ["PLAN", "TK_PLAN"], - ["PRAGMA", "TK_PRAGMA"], - ["PRECEDING", "TK_PRECEDING"], - ["PRIMARY", "TK_PRIMARY"], - ["QUERY", "TK_QUERY"], - ["RAISE", "TK_RAISE"], - ["RANGE", "TK_RANGE"], - ["RECURSIVE", "TK_RECURSIVE"], - ["REFERENCES", "TK_REFERENCES"], - ["REGEXP", "TK_LIKE_KW"], - ["REINDEX", "TK_REINDEX"], - ["RELEASE", "TK_RELEASE"], - ["RENAME", "TK_RENAME"], - ["REPLACE", "TK_REPLACE"], - ["RETURNING", "TK_RETURNING"], - ["RESTRICT", "TK_RESTRICT"], - ["RIGHT", "TK_JOIN_KW"], - ["ROLLBACK", "TK_ROLLBACK"], - ["ROW", "TK_ROW"], - ["ROWS", "TK_ROWS"], - ["SAVEPOINT", "TK_SAVEPOINT"], - ["SELECT", "TK_SELECT"], - ["SET", "TK_SET"], - ["TABLE", "TK_TABLE"], - ["TEMP", "TK_TEMP"], - ["TEMPORARY", "TK_TEMP"], - ["THEN", "TK_THEN"], - ["TIES", "TK_TIES"], - ["TO", "TK_TO"], - ["TRANSACTION", "TK_TRANSACTION"], - ["TRIGGER", "TK_TRIGGER"], - ["UNBOUNDED", "TK_UNBOUNDED"], - ["UNION", "TK_UNION"], - ["UNIQUE", "TK_UNIQUE"], - ["UPDATE", "TK_UPDATE"], - ["USING", "TK_USING"], - ["VACUUM", "TK_VACUUM"], - ["VALUES", "TK_VALUES"], - ["VIEW", "TK_VIEW"], - ["VIRTUAL", "TK_VIRTUAL"], - ["WHEN", "TK_WHEN"], - ["WHERE", "TK_WHERE"], - ["WINDOW", "TK_WINDOW"], - ["WITH", "TK_WITH"], - ["WITHOUT", "TK_WITHOUT"], - ], - )?; - - println!("cargo:rerun-if-changed=third_party/lemon/lemon.c"); - println!("cargo:rerun-if-changed=third_party/lemon/lempar.rs"); - println!("cargo:rerun-if-changed=src/parser/parse.y"); - // TODO examples/simple.y if test - Ok(()) -} diff --git a/vendored/sqlite3-parser/checks.md b/vendored/sqlite3-parser/checks.md deleted file mode 100644 index f95ac75c1..000000000 --- a/vendored/sqlite3-parser/checks.md +++ /dev/null @@ -1,124 +0,0 @@ -# Extra consistency checks - -- `ALTER TABLE ... RENAME TO ...` when old and new table names are the same => `Stmt::check` -- `ALTER TABLE ... ADD COLUMN ...` with new primary key / unique constraint => `Stmt::check` -- `CREATE TABLE ...` - - with duplicated column name => `ColumnDefinition::add_column` - - with STRICT option and invalid or missing column type(s) => `CreateTableBody::check` - - WITHOUT ROWID and without primary key => `CreateTableBody::check` -- `CREATE VIEW ... (...) ...` - - when view columns count does not match select columns count => `Stmt::check` - - with duplicated columns (same name) => `Stmt::check` -- `DELETE FROM ... ORDER BY ...` with ORDER BY but without LIMIT => `Stmt::check` -- `INSERT INTO ... (...) ...` when columns count does not match select columns / values count => `Stmt::check` -- `INSERT INTO ... (...) DEFAULT VALUES` with columns and DEFAULT VALUES => `Stmt::check` -- `SELECT ... EXCEPT|INTERSECT|UNION SELECT ...` when all SELECT does not have the same number of result columns => `SelectBody::push` -- `NATURAL JOIN ...` with ON or USING clause => `FromClause::push` -- `UPDATE ... ORDER BY ...` with ORDER BY but without LIMIT => `Stmt::check` -- `VALUES (...), (...), ...` when all VALUES does not have the same number of terms => `OneSelect::push` -- `WITH ...` with duplicated table name => `CommonTableExpr::add_cte` - -## TODO - -### `CREATE TABLE` - -- [x] qualified (different of `temp`) temporary table - -```sql -sqlite> ATTACH DATABASE ':memory:' AS mem; -sqlite> CREATE TEMPORARY TABLE mem.x AS SELECT 1; -Parse error: temporary table name must be unqualified -``` - -```sql -sqlite> CREATE TEMPORARY TABLE temp.x AS SELECT 1; --- OK -``` - -- [x] must have at least one non-generated column - -```sql -sqlite> CREATE TABLE test(data AS (1)); -Parse error: must have at least one non-generated column -``` - -- [ ] column constraint(s) checks - -```sql -sqlite> CREATE TABLE t(a REFERENCES o(a,b)); -Parse error: foreign key on a should reference only one column of table o - CREATE TABLE t(a REFERENCES o(a,b)); - error here ---^ -sqlite> CREATE TABLE t(a PRIMARY KEY AUTOINCREMENT) WITHOUT ROWID; -Parse error: AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY -sqlite> CREATE TABLE t(a INTEGER PRIMARY KEY AUTOINCREMENT) WITHOUT ROWID; -Parse error: AUTOINCREMENT not allowed on WITHOUT ROWID tables -``` - -- [ ] table constraint(s) checks - -```sql -sqlite> CREATE TABLE test (a, b, FOREIGN KEY (b) REFERENCES test(a,b)); -Parse error: number of columns in foreign key does not match the number of columns in the referenced table -``` - -```sql -sqlite> create table test (a,b, primary key(a), primary key(b)); -Parse error: table "test" has more than one primary key -sqlite> create table test (a primary key, b primary key); -Parse error: table "test" has more than one primary key -sqlite> create table test (a primary key, b, primary key(a)); -Parse error: table "test" has more than one primary key -``` - -### `HAVING` - -- [x] HAVING clause on a non-aggregate query (`GroupBy::having`): grammar already prevents this case (grammar differs from SQLite official grammar). - -```sql -sqlite> SELECT 1 as i HAVING i > 1; -Parse error: HAVING clause on a non-aggregate query -``` - -vs - -``` -[ERROR sqlite3Parser] near HAVING, "Token(None)": syntax error -Err: near HAVING, "None": syntax error at (1, 21) in SELECT 1 as i HAVING i > 1 -``` - -### `SELECT ...` - -- [ ] no duplicated column name in `selcollist`/`Select::columns` - -```sql -sqlite> SELECT 1 as i, 2 as i; --- no error (idem for postgres) -``` - -### `SELECT ... ORDER BY ...` - -- [ ] ORDER BY term does not match any column in the result set (`Select::order_by`) - -```sql -sqlite> SELECT 1 as i ORDER BY j; -Parse error: no such column: j - SELECT 1 as i ORDER BY j; - ^--- error here -``` - -### `WITH` - -- [ ] no duplicated column name in `CommonTableExpr::IndexedColumn` - -### DML - -```sql -sqlite> CREATE TABLE test (n, m); -sqlite> INSERT INTO test (n, n, m) VALUES (1, 0, 1); -- pgsql KO -sqlite> SELECT * FROM test; -1|1 -sqlite> UPDATE test SET n = 1, n = 0; -- pgsql KO -sqlite> SELECT * FROM test; -0|1 -``` diff --git a/vendored/sqlite3-parser/examples/simple.y b/vendored/sqlite3-parser/examples/simple.y deleted file mode 100644 index 34045ad24..000000000 --- a/vendored/sqlite3-parser/examples/simple.y +++ /dev/null @@ -1,123 +0,0 @@ -%token_type { i32 } - -// An extra argument to the constructor for the parser, which is available -// to all actions. -%extra_context {ctx: Context} - -%left PLUS MINUS. -%left DIVIDE TIMES. - -%include { - -use log::{debug, error, log_enabled, Level, LevelFilter, Metadata, Record, SetLoggerError}; - -pub struct Context { - expr: Option, -} - -#[derive(Debug)] -pub enum Operator { - Add, - Subtract, - Multiply, - Divide, -} - -#[derive(Debug)] -pub enum Expr { - Number(i32), - Binary(Operator, Box, Box), -} -impl Expr { - fn binary(op: Operator, lhs: Expr, rhs: Expr) -> Expr { - Expr::Binary(op, Box::new(lhs), Box::new(rhs)) - } -} - -fn main() { - init_logger().unwrap(); - - let r = Context { expr: None }; - let mut p = yyParser::new(r); - p.Parse(TokenType::INTEGER, Some(5)); - p.Parse(TokenType::PLUS, None); - p.Parse(TokenType::INTEGER, Some(10)); - p.Parse(TokenType::TIMES, None); - p.Parse(TokenType::INTEGER, Some(4)); - p.Parse(TokenType::EOF, None); - p.ParseFinalize(); - let s = format!("{:?}", p.ctx.expr); - assert_eq!(s, "Some(Binary(Add, Number(5), Binary(Multiply, Number(10), Number(4))))"); - - let r = Context { expr: None }; - let mut p = yyParser::new(r); - p.Parse(TokenType::INTEGER, Some(15)); - p.Parse(TokenType::DIVIDE, None); - p.Parse(TokenType::INTEGER, Some(5)); - p.Parse(TokenType::EOF, None); - p.ParseFinalize(); - let s = format!("{:?}", p.ctx.expr); - assert_eq!(s, "Some(Binary(Divide, Number(15), Number(5)))"); - - let r = Context { expr: None }; - let mut p = yyParser::new(r); - p.Parse(TokenType::INTEGER, Some(50)); - p.Parse(TokenType::PLUS, None); - p.Parse(TokenType::INTEGER, Some(125)); - p.Parse(TokenType::EOF, None); - p.ParseFinalize(); - let s = format!("{:?}", p.ctx.expr); - assert_eq!(s, "Some(Binary(Add, Number(50), Number(125)))"); - - let r = Context { expr: None }; - let mut p = yyParser::new(r); - p.Parse(TokenType::INTEGER, Some(50)); - p.Parse(TokenType::TIMES, None); - p.Parse(TokenType::INTEGER, Some(125)); - p.Parse(TokenType::PLUS, None); - p.Parse(TokenType::INTEGER, Some(125)); - p.Parse(TokenType::EOF, None); - p.ParseFinalize(); - let s = format!("{:?}", p.ctx.expr); - assert_eq!(s, "Some(Binary(Add, Binary(Multiply, Number(50), Number(125)), Number(125)))"); -} - -static LOGGER: Logger = Logger; -struct Logger; - -impl log::Log for Logger { - fn enabled(&self, metadata: &Metadata) -> bool { - metadata.level() <= Level::Debug - } - - fn log(&self, record: &Record) { - if self.enabled(record.metadata()) { - eprintln!("{} - {}", record.level(), record.args()); - } - } - - fn flush(&self) { - } -} - -fn init_logger() -> Result<(), SetLoggerError> { - log::set_logger(&LOGGER)?; - log::set_max_level(LevelFilter::Debug); - Ok(()) -} -} - -%syntax_error { - let _ = yymajor; - println!("near token {:?}: syntax error", yyminor); -} - -program ::= expr(A). { self.ctx.expr = Some(A); } - -%type expr { Expr } -expr(A) ::= expr(B) MINUS expr(C). { A = Expr::binary(Operator::Subtract, B, C); } -expr(A) ::= expr(B) PLUS expr(C). { A = Expr::binary(Operator::Add, B, C); } -expr(A) ::= expr(B) TIMES expr(C). { A = Expr::binary(Operator::Multiply, B, C); } -expr(A) ::= expr(B) DIVIDE expr(C). { A = Expr::binary(Operator::Divide, B, C); } - -expr(A) ::= INTEGER(B). { A = Expr::Number(B.unwrap()); } diff --git a/vendored/sqlite3-parser/examples/sql_check.rs b/vendored/sqlite3-parser/examples/sql_check.rs deleted file mode 100644 index 154b01f8e..000000000 --- a/vendored/sqlite3-parser/examples/sql_check.rs +++ /dev/null @@ -1,55 +0,0 @@ -use fallible_iterator::FallibleIterator; -use std::env; -use std::fs::read; -use std::panic; - -use turso_sqlite3_parser::lexer::sql::Parser; - -/// Parse specified files and check all commands. -fn main() { - env_logger::init(); - let args = env::args(); - for arg in args.skip(1) { - println!("{arg}"); - let result = panic::catch_unwind(|| { - let input = read(arg.clone()).unwrap(); - let mut parser = Parser::new(&input); - loop { - match parser.next() { - Ok(None) => break, - Err(err) => { - eprintln!("Err: {err} in {arg}"); - break; - } - Ok(Some(cmd)) => { - let input = cmd.to_string(); - let mut checker = Parser::new(input.as_bytes()); - match checker.next() { - Err(err) => { - eprintln!( - "Check Err in {}:{}, {} in\n{}\n{:?}", - arg, - parser.line(), - err, - input, - cmd - ); - } - Ok(None) => { - eprintln!("Check Err in {}:{}, {:?}", arg, parser.line(), cmd); - } - Ok(Some(check)) => { - if cmd != check { - eprintln!("{cmd:?}\n<>\n{check:?}"); - } - } - } - } - } - } - }); - if let Err(e) = result { - eprintln!("Panic: {e:?} in {arg}"); - } - } -} diff --git a/vendored/sqlite3-parser/examples/sql_cmd.rs b/vendored/sqlite3-parser/examples/sql_cmd.rs deleted file mode 100644 index 94a549931..000000000 --- a/vendored/sqlite3-parser/examples/sql_cmd.rs +++ /dev/null @@ -1,26 +0,0 @@ -use std::env; - -use fallible_iterator::FallibleIterator; -use turso_sqlite3_parser::lexer::sql::Parser; - -/// Parse args. -// RUST_LOG=sqlite3Parser=debug -fn main() { - env_logger::init(); - let args = env::args(); - for arg in args.skip(1) { - let mut parser = Parser::new(arg.as_bytes()); - loop { - match parser.next() { - Ok(None) => break, - Err(err) => { - eprintln!("Err: {err} in {arg}"); - break; - } - Ok(Some(cmd)) => { - println!("{cmd}"); - } - } - } - } -} diff --git a/vendored/sqlite3-parser/examples/sql_cmds.rs b/vendored/sqlite3-parser/examples/sql_cmds.rs deleted file mode 100644 index 1b3c6b8de..000000000 --- a/vendored/sqlite3-parser/examples/sql_cmds.rs +++ /dev/null @@ -1,42 +0,0 @@ -use fallible_iterator::FallibleIterator; -use std::env; -use std::fs::read; -use std::panic; - -#[cfg(not(feature = "YYNOERRORRECOVERY"))] -use turso_sqlite3_parser::lexer::sql::Error; -use turso_sqlite3_parser::lexer::sql::Parser; - -/// Parse specified files and print all commands. -fn main() { - env_logger::init(); - let args = env::args(); - for arg in args.skip(1) { - println!("{arg}"); - let result = panic::catch_unwind(|| { - let input = read(arg.clone()).unwrap(); - let mut parser = Parser::new(input.as_ref()); - loop { - match parser.next() { - Ok(None) => break, - Err(err) => { - eprintln!("Err: {err} in {arg}"); - #[cfg(feature = "YYNOERRORRECOVERY")] - break; - #[cfg(not(feature = "YYNOERRORRECOVERY"))] - if let Error::ParserError(..) = err { - } else { - break; - } - } - Ok(Some(cmd)) => { - println!("{cmd}"); - } - } - } - }); - if let Err(e) = result { - eprintln!("Panic: {e:?} in {arg}"); - } - } -} diff --git a/vendored/sqlite3-parser/examples/sql_tokens.rs b/vendored/sqlite3-parser/examples/sql_tokens.rs deleted file mode 100644 index 8121809d0..000000000 --- a/vendored/sqlite3-parser/examples/sql_tokens.rs +++ /dev/null @@ -1,90 +0,0 @@ -use turso_sqlite3_parser::lexer::sql::{TokenType, Tokenizer}; -use turso_sqlite3_parser::lexer::Scanner; - -use std::env; -use std::fs::read; -use std::str; - -/// Tokenize specified files (and do some checks) -fn main() { - use TokenType::*; - let args = env::args(); - for arg in args.skip(1) { - let input = read(arg.clone()).unwrap(); - let tokenizer = Tokenizer::new(); - let mut s = Scanner::new(tokenizer); - loop { - match s.scan(&input) { - Ok((_, None, _)) => break, - Err(err) => { - //eprintln!("{} at line: {}, column: {}", err, s.line(), s.column()); - eprintln!("Err: {err} in {arg}"); - break; - } - Ok((_, Some((token, token_type)), _)) => match token_type { - TK_TEMP => debug_assert!( - b"TEMP".eq_ignore_ascii_case(token) - || b"TEMPORARY".eq_ignore_ascii_case(token) - ), - TK_EQ => debug_assert!(b"=" == token || b"==" == token), - TK_NE => debug_assert!(b"<>" == token || b"!=" == token), - //TK_STRING => debug_assert!(), - //TK_ID => debug_assert!(), - //TK_VARIABLE => debug_assert!(), - TK_BLOB => debug_assert!( - token.len() % 2 == 0 && token.iter().all(u8::is_ascii_hexdigit) - ), - TK_INTEGER => { - if token.len() > 2 - && token[0] == b'0' - && (token[1] == b'x' || token[1] == b'X') - { - if let Err(err) = - i64::from_str_radix(str::from_utf8(&token[2..]).unwrap(), 16) - { - eprintln!("Err: {err} in {arg}"); - } - } else { - /*let raw = str::from_utf8(token).unwrap(); - let res = raw.parse::(); - if res.is_err() { - eprintln!("Err: {} in {}", res.unwrap_err(), arg); - }*/ - debug_assert!(token.iter().all(u8::is_ascii_digit)) - } - } - TK_FLOAT => { - debug_assert!(str::from_utf8(token).unwrap().parse::().is_ok()) - } - TK_CTIME_KW => debug_assert!( - b"CURRENT_DATE".eq_ignore_ascii_case(token) - || b"CURRENT_TIME".eq_ignore_ascii_case(token) - || b"CURRENT_TIMESTAMP".eq_ignore_ascii_case(token) - ), - TK_JOIN_KW => debug_assert!( - b"CROSS".eq_ignore_ascii_case(token) - || b"FULL".eq_ignore_ascii_case(token) - || b"INNER".eq_ignore_ascii_case(token) - || b"LEFT".eq_ignore_ascii_case(token) - || b"NATURAL".eq_ignore_ascii_case(token) - || b"OUTER".eq_ignore_ascii_case(token) - || b"RIGHT".eq_ignore_ascii_case(token) - ), - TK_LIKE_KW => debug_assert!( - b"GLOB".eq_ignore_ascii_case(token) - || b"LIKE".eq_ignore_ascii_case(token) - || b"REGEXP".eq_ignore_ascii_case(token) - ), - _ => match token_type.as_str() { - Some(str) => { - debug_assert!(str.eq_ignore_ascii_case(str::from_utf8(token).unwrap())) - } - _ => { - println!("'{}', {:?}", str::from_utf8(token).unwrap(), token_type); - } - }, - }, - } - } - } -} diff --git a/vendored/sqlite3-parser/sqlparser_bench/Cargo.toml b/vendored/sqlite3-parser/sqlparser_bench/Cargo.toml deleted file mode 100644 index 0366bd63d..000000000 --- a/vendored/sqlite3-parser/sqlparser_bench/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "sqlparser_bench" -version = "0.1.0" -authors = ["Dandandan "] -edition = "2018" - -[dependencies] -turso_sqlite3_parser = { path = "..", default-features = false, features = [ - "YYNOERRORRECOVERY", - "NDEBUG", -] } -fallible-iterator = { workspace = true } - -[dev-dependencies] -criterion = { workspace = true } - -[[bench]] -name = "sqlparser_bench" -harness = false diff --git a/vendored/sqlite3-parser/sqlparser_bench/README.md b/vendored/sqlite3-parser/sqlparser_bench/README.md deleted file mode 100644 index ee76468c4..000000000 --- a/vendored/sqlite3-parser/sqlparser_bench/README.md +++ /dev/null @@ -1,32 +0,0 @@ -Adapted from https://github.com/ballista-compute/sqlparser-rs/tree/main/sqlparser_bench - -## sqlparser-rs - -``` -sqlparser-rs parsing benchmark/sqlparser::select - time: [9.9697 µs 10.068 µs 10.184 µs] -Found 14 outliers among 100 measurements (14.00%) - 5 (5.00%) high mild - 9 (9.00%) high severe -sqlparser-rs parsing benchmark/sqlparser::with_select - time: [59.569 µs 60.088 µs 60.743 µs] -Found 9 outliers among 100 measurements (9.00%) - 3 (3.00%) high mild - 6 (6.00%) high severe -``` - -## sqlite3-parser - -``` -sqlparser-rs parsing benchmark/sqlparser::select - time: [6.5488 µs 6.5773 µs 6.6108 µs] -Found 10 outliers among 100 measurements (10.00%) - 4 (4.00%) high mild - 6 (6.00%) high severe -sqlparser-rs parsing benchmark/sqlparser::with_select - time: [22.182 µs 22.321 µs 22.473 µs] -Found 8 outliers among 100 measurements (8.00%) - 1 (1.00%) low mild - 3 (3.00%) high mild - 4 (4.00%) high severe -``` \ No newline at end of file diff --git a/vendored/sqlite3-parser/sqlparser_bench/benches/sqlparser_bench.rs b/vendored/sqlite3-parser/sqlparser_bench/benches/sqlparser_bench.rs deleted file mode 100644 index 3005fcde1..000000000 --- a/vendored/sqlite3-parser/sqlparser_bench/benches/sqlparser_bench.rs +++ /dev/null @@ -1,194 +0,0 @@ -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use criterion::{criterion_group, criterion_main, Criterion}; -use fallible_iterator::FallibleIterator; -use turso_sqlite3_parser::{dialect::keyword_token, lexer::sql::Parser}; - -fn basic_queries(c: &mut Criterion) { - let mut group = c.benchmark_group("sqlparser-rs parsing benchmark"); - - let string = b"SELECT * FROM `table` WHERE 1 = 1"; - group.bench_with_input("sqlparser::select", &string, |b, &s| { - b.iter(|| { - let mut parser = Parser::new(s); - assert!(parser.next().unwrap().unwrap().readonly()) - }); - }); - - let with_query = b" - WITH derived AS ( - SELECT MAX(a) AS max_a, - COUNT(b) AS b_num, - user_id - FROM `TABLE` - GROUP BY user_id - ) - SELECT * FROM `table` - LEFT JOIN derived USING (user_id) - "; - group.bench_with_input("sqlparser::with_select", &with_query, |b, &s| { - b.iter(|| { - let mut parser = Parser::new(s); - assert!(parser.next().unwrap().unwrap().readonly()) - }); - }); - - static VALUES: [&[u8]; 136] = [ - b"ABORT", - b"ACTION", - b"ADD", - b"AFTER", - b"ALL", - b"ALTER", - b"ANALYZE", - b"AND", - b"AS", - b"ASC", - b"ATTACH", - b"AUTOINCREMENT", - b"BEFORE", - b"BEGIN", - b"BETWEEN", - b"BY", - b"CASCADE", - b"CASE", - b"CAST", - b"CHECK", - b"COLLATE", - b"COLUMN", - b"COMMIT", - b"CONFLICT", - b"CONSTRAINT", - b"CREATE", - b"CROSS", - b"CURRENT", - b"CURRENT_DATE", - b"CURRENT_TIME", - b"CURRENT_TIMESTAMP", - b"DATABASE", - b"DEFAULT", - b"DEFERRABLE", - b"DEFERRED", - b"DELETE", - b"DESC", - b"DETACH", - b"DISTINCT", - b"DO", - b"DROP", - b"EACH", - b"ELSE", - b"END", - b"ESCAPE", - b"EXCEPT", - b"EXCLUSIVE", - b"EXISTS", - b"EXPLAIN", - b"FAIL", - b"FILTER", - b"FOLLOWING", - b"FOR", - b"FOREIGN", - b"FROM", - b"FULL", - b"GLOB", - b"GROUP", - b"HAVING", - b"IF", - b"IGNORE", - b"IMMEDIATE", - b"IN", - b"INDEX", - b"INDEXED", - b"INITIALLY", - b"INNER", - b"INSERT", - b"INSTEAD", - b"INTERSECT", - b"INTO", - b"IS", - b"ISNULL", - b"JOIN", - b"KEY", - b"LEFT", - b"LIKE", - b"LIMIT", - b"MATCH", - b"NATURAL", - b"NO", - b"NOT", - b"NOTHING", - b"NOTNULL", - b"NULL", - b"OF", - b"OFFSET", - b"ON", - b"OR", - b"ORDER", - b"OUTER", - b"OVER", - b"PARTITION", - b"PLAN", - b"PRAGMA", - b"PRECEDING", - b"PRIMARY", - b"QUERY", - b"RAISE", - b"RANGE", - b"RECURSIVE", - b"REFERENCES", - b"REGEXP", - b"REINDEX", - b"RELEASE", - b"RENAME", - b"REPLACE", - b"RESTRICT", - b"RIGHT", - b"ROLLBACK", - b"ROW", - b"ROWS", - b"SAVEPOINT", - b"SELECT", - b"SET", - b"TABLE", - b"TEMP", - b"TEMPORARY", - b"THEN", - b"TO", - b"TRANSACTION", - b"TRIGGER", - b"UNBOUNDED", - b"UNION", - b"UNIQUE", - b"UPDATE", - b"USING", - b"VACUUM", - b"VALUES", - b"VIEW", - b"VIRTUAL", - b"WHEN", - b"WHERE", - b"WINDOW", - b"WITH", - b"WITHOUT", - ]; - group.bench_with_input("keyword_token", &VALUES, |b, &s| { - b.iter(|| { - for value in &s { - assert!(keyword_token(value).is_some()) - } - }); - }); -} - -criterion_group!(benches, basic_queries); -criterion_main!(benches); diff --git a/vendored/sqlite3-parser/src/dialect/mod.rs b/vendored/sqlite3-parser/src/dialect/mod.rs deleted file mode 100644 index f67d95655..000000000 --- a/vendored/sqlite3-parser/src/dialect/mod.rs +++ /dev/null @@ -1,409 +0,0 @@ -//! SQLite dialect - -use std::fmt::Formatter; -use std::str; - -mod token; -pub use token::TokenType; - -/// Token value (lexeme) -#[derive(Clone, Copy)] -pub struct Token<'i>(pub usize, pub &'i [u8], pub usize); - -pub(crate) fn sentinel(start: usize) -> Token<'static> { - Token(start, b"", start) -} - -impl Token<'_> { - /// Access token value - pub fn unwrap(self) -> String { - from_bytes(self.1) - } -} - -impl std::fmt::Debug for Token<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("Token").field(&self.1).finish() - } -} - -impl TokenType { - // TODO try Cow<&'static, str> (Borrowed<&'static str> for keyword and Owned for below), - // => Syntax error on keyword will be better - // => `from_token` will become unnecessary - pub(crate) fn to_token(self, start: usize, value: &[u8], end: usize) -> Token<'_> { - Token(start, value, end) - } -} - -pub(crate) fn from_bytes(bytes: &[u8]) -> String { - unsafe { str::from_utf8_unchecked(bytes).to_owned() } -} - -include!(concat!(env!("OUT_DIR"), "/keywords.rs")); - -pub(crate) fn is_identifier(name: &str) -> bool { - if name.is_empty() { - return false; - } - let bytes = name.as_bytes(); - is_identifier_start(bytes[0]) - && (bytes.len() == 1 || bytes[1..].iter().all(|b| is_identifier_continue(*b))) -} - -pub(crate) fn is_identifier_start(b: u8) -> bool { - b.is_ascii_uppercase() || b == b'_' || b.is_ascii_lowercase() || b > b'\x7F' -} - -pub(crate) fn is_identifier_continue(b: u8) -> bool { - b == b'$' - || b.is_ascii_digit() - || b.is_ascii_uppercase() - || b == b'_' - || b.is_ascii_lowercase() - || b > b'\x7F' -} - -// keyword may become an identifier -// see %fallback in parse.y -pub(crate) fn from_token(_ty: u16, value: Token) -> String { - from_bytes(value.1) -} - -impl TokenType { - /// Return the associated string (mainly for testing) - pub const fn as_str(&self) -> Option<&'static str> { - use TokenType::*; - match self { - TK_ABORT => Some("ABORT"), - TK_ACTION => Some("ACTION"), - TK_ADD => Some("ADD"), - TK_AFTER => Some("AFTER"), - TK_ALL => Some("ALL"), - TK_ALTER => Some("ALTER"), - TK_ANALYZE => Some("ANALYZE"), - TK_ALWAYS => Some("ALWAYS"), - TK_AND => Some("AND"), - TK_AS => Some("AS"), - TK_ASC => Some("ASC"), - TK_ATTACH => Some("ATTACH"), - TK_AUTOINCR => Some("AUTOINCREMENT"), - TK_BEFORE => Some("BEFORE"), - TK_BEGIN => Some("BEGIN"), - TK_BETWEEN => Some("BETWEEN"), - TK_BY => Some("BY"), - TK_CASCADE => Some("CASCADE"), - TK_CASE => Some("CASE"), - TK_CAST => Some("CAST"), - TK_CHECK => Some("CHECK"), - TK_COLLATE => Some("COLLATE"), - TK_COLUMNKW => Some("COLUMN"), - TK_COMMIT => Some("COMMIT"), - TK_CONFLICT => Some("CONFLICT"), - TK_CONSTRAINT => Some("CONSTRAINT"), - TK_CREATE => Some("CREATE"), - TK_CURRENT => Some("CURRENT"), - TK_DATABASE => Some("DATABASE"), - TK_DEFAULT => Some("DEFAULT"), - TK_DEFERRABLE => Some("DEFERRABLE"), - TK_DEFERRED => Some("DEFERRED"), - TK_DELETE => Some("DELETE"), - TK_DESC => Some("DESC"), - TK_DETACH => Some("DETACH"), - TK_DISTINCT => Some("DISTINCT"), - TK_DO => Some("DO"), - TK_DROP => Some("DROP"), - TK_EACH => Some("EACH"), - TK_ELSE => Some("ELSE"), - TK_END => Some("END"), - TK_ESCAPE => Some("ESCAPE"), - TK_EXCEPT => Some("EXCEPT"), - TK_EXCLUDE => Some("EXCLUDE"), - TK_EXCLUSIVE => Some("EXCLUSIVE"), - TK_EXISTS => Some("EXISTS"), - TK_EXPLAIN => Some("EXPLAIN"), - TK_FAIL => Some("FAIL"), - TK_FILTER => Some("FILTER"), - TK_FIRST => Some("FIRST"), - TK_FOLLOWING => Some("FOLLOWING"), - TK_FOR => Some("FOR"), - TK_FOREIGN => Some("FOREIGN"), - TK_FROM => Some("FROM"), - TK_GENERATED => Some("GENERATED"), - TK_GROUP => Some("GROUP"), - TK_GROUPS => Some("GROUPS"), - TK_HAVING => Some("HAVING"), - TK_IF => Some("IF"), - TK_IGNORE => Some("IGNORE"), - TK_IMMEDIATE => Some("IMMEDIATE"), - TK_IN => Some("IN"), - TK_INDEX => Some("INDEX"), - TK_INDEXED => Some("INDEXED"), - TK_INITIALLY => Some("INITIALLY"), - TK_INSERT => Some("INSERT"), - TK_INSTEAD => Some("INSTEAD"), - TK_INTERSECT => Some("INTERSECT"), - TK_INTO => Some("INTO"), - TK_IS => Some("IS"), - TK_ISNULL => Some("ISNULL"), - TK_JOIN => Some("JOIN"), - TK_KEY => Some("KEY"), - TK_LAST => Some("LAST"), - TK_LIMIT => Some("LIMIT"), - TK_MATCH => Some("MATCH"), - TK_MATERIALIZED => Some("MATERIALIZED"), - TK_NO => Some("NO"), - TK_NOT => Some("NOT"), - TK_NOTHING => Some("NOTHING"), - TK_NOTNULL => Some("NOTNULL"), - TK_NULL => Some("NULL"), - TK_NULLS => Some("NULLS"), - TK_OF => Some("OF"), - TK_OFFSET => Some("OFFSET"), - TK_ON => Some("ON"), - TK_OR => Some("OR"), - TK_ORDER => Some("ORDER"), - TK_OTHERS => Some("OTHERS"), - TK_OVER => Some("OVER"), - TK_PARTITION => Some("PARTITION"), - TK_PLAN => Some("PLAN"), - TK_PRAGMA => Some("PRAGMA"), - TK_PRECEDING => Some("PRECEDING"), - TK_PRIMARY => Some("PRIMARY"), - TK_QUERY => Some("QUERY"), - TK_RAISE => Some("RAISE"), - TK_RANGE => Some("RANGE"), - TK_RECURSIVE => Some("RECURSIVE"), - TK_REFERENCES => Some("REFERENCES"), - TK_REINDEX => Some("REINDEX"), - TK_RELEASE => Some("RELEASE"), - TK_RENAME => Some("RENAME"), - TK_REPLACE => Some("REPLACE"), - TK_RETURNING => Some("RETURNING"), - TK_RESTRICT => Some("RESTRICT"), - TK_ROLLBACK => Some("ROLLBACK"), - TK_ROW => Some("ROW"), - TK_ROWS => Some("ROWS"), - TK_SAVEPOINT => Some("SAVEPOINT"), - TK_SELECT => Some("SELECT"), - TK_SET => Some("SET"), - TK_TABLE => Some("TABLE"), - TK_TEMP => Some("TEMP"), // or TEMPORARY - TK_TIES => Some("TIES"), - TK_THEN => Some("THEN"), - TK_TO => Some("TO"), - TK_TRANSACTION => Some("TRANSACTION"), - TK_TRIGGER => Some("TRIGGER"), - TK_UNBOUNDED => Some("UNBOUNDED"), - TK_UNION => Some("UNION"), - TK_UNIQUE => Some("UNIQUE"), - TK_UPDATE => Some("UPDATE"), - TK_USING => Some("USING"), - TK_VACUUM => Some("VACUUM"), - TK_VALUES => Some("VALUES"), - TK_VIEW => Some("VIEW"), - TK_VIRTUAL => Some("VIRTUAL"), - TK_WHEN => Some("WHEN"), - TK_WHERE => Some("WHERE"), - TK_WINDOW => Some("WINDOW"), - TK_WITH => Some("WITH"), - TK_WITHOUT => Some("WITHOUT"), - TK_BITAND => Some("&"), - TK_BITNOT => Some("~"), - TK_BITOR => Some("|"), - TK_COMMA => Some(","), - TK_CONCAT => Some("||"), - TK_DOT => Some("."), - TK_EQ => Some("="), // or == - TK_GT => Some(">"), - TK_GE => Some(">="), - TK_LP => Some("("), - TK_LSHIFT => Some("<<"), - TK_LE => Some("<="), - TK_LT => Some("<"), - TK_MINUS => Some("-"), - TK_NE => Some("!="), // or <> - TK_PLUS => Some("+"), - TK_REM => Some("%"), - TK_RP => Some(")"), - TK_RSHIFT => Some(">>"), - TK_SEMI => Some(";"), - TK_SLASH => Some("/"), - TK_STAR => Some("*"), - _ => None, - } - } -} -#[cfg(test)] -mod tests { - use super::*; - use std::collections::HashMap; - - #[test] - fn test_keyword_token() { - let values = HashMap::from([ - ("ABORT", TokenType::TK_ABORT), - ("ACTION", TokenType::TK_ACTION), - ("ADD", TokenType::TK_ADD), - ("AFTER", TokenType::TK_AFTER), - ("ALL", TokenType::TK_ALL), - ("ALTER", TokenType::TK_ALTER), - ("ALWAYS", TokenType::TK_ALWAYS), - ("ANALYZE", TokenType::TK_ANALYZE), - ("AND", TokenType::TK_AND), - ("AS", TokenType::TK_AS), - ("ASC", TokenType::TK_ASC), - ("ATTACH", TokenType::TK_ATTACH), - ("AUTOINCREMENT", TokenType::TK_AUTOINCR), - ("BEFORE", TokenType::TK_BEFORE), - ("BEGIN", TokenType::TK_BEGIN), - ("BETWEEN", TokenType::TK_BETWEEN), - ("BY", TokenType::TK_BY), - ("CASCADE", TokenType::TK_CASCADE), - ("CASE", TokenType::TK_CASE), - ("CAST", TokenType::TK_CAST), - ("CHECK", TokenType::TK_CHECK), - ("COLLATE", TokenType::TK_COLLATE), - ("COLUMN", TokenType::TK_COLUMNKW), - ("COMMIT", TokenType::TK_COMMIT), - ("CONFLICT", TokenType::TK_CONFLICT), - ("CONSTRAINT", TokenType::TK_CONSTRAINT), - ("CREATE", TokenType::TK_CREATE), - ("CROSS", TokenType::TK_JOIN_KW), - ("CURRENT", TokenType::TK_CURRENT), - ("CURRENT_DATE", TokenType::TK_CTIME_KW), - ("CURRENT_TIME", TokenType::TK_CTIME_KW), - ("CURRENT_TIMESTAMP", TokenType::TK_CTIME_KW), - ("DATABASE", TokenType::TK_DATABASE), - ("DEFAULT", TokenType::TK_DEFAULT), - ("DEFERRABLE", TokenType::TK_DEFERRABLE), - ("DEFERRED", TokenType::TK_DEFERRED), - ("DELETE", TokenType::TK_DELETE), - ("DESC", TokenType::TK_DESC), - ("DETACH", TokenType::TK_DETACH), - ("DISTINCT", TokenType::TK_DISTINCT), - ("DO", TokenType::TK_DO), - ("DROP", TokenType::TK_DROP), - ("EACH", TokenType::TK_EACH), - ("ELSE", TokenType::TK_ELSE), - ("END", TokenType::TK_END), - ("ESCAPE", TokenType::TK_ESCAPE), - ("EXCEPT", TokenType::TK_EXCEPT), - ("EXCLUDE", TokenType::TK_EXCLUDE), - ("EXCLUSIVE", TokenType::TK_EXCLUSIVE), - ("EXISTS", TokenType::TK_EXISTS), - ("EXPLAIN", TokenType::TK_EXPLAIN), - ("FAIL", TokenType::TK_FAIL), - ("FILTER", TokenType::TK_FILTER), - ("FIRST", TokenType::TK_FIRST), - ("FOLLOWING", TokenType::TK_FOLLOWING), - ("FOR", TokenType::TK_FOR), - ("FOREIGN", TokenType::TK_FOREIGN), - ("FROM", TokenType::TK_FROM), - ("FULL", TokenType::TK_JOIN_KW), - ("GENERATED", TokenType::TK_GENERATED), - ("GLOB", TokenType::TK_LIKE_KW), - ("GROUP", TokenType::TK_GROUP), - ("GROUPS", TokenType::TK_GROUPS), - ("HAVING", TokenType::TK_HAVING), - ("IF", TokenType::TK_IF), - ("IGNORE", TokenType::TK_IGNORE), - ("IMMEDIATE", TokenType::TK_IMMEDIATE), - ("IN", TokenType::TK_IN), - ("INDEX", TokenType::TK_INDEX), - ("INDEXED", TokenType::TK_INDEXED), - ("INITIALLY", TokenType::TK_INITIALLY), - ("INNER", TokenType::TK_JOIN_KW), - ("INSERT", TokenType::TK_INSERT), - ("INSTEAD", TokenType::TK_INSTEAD), - ("INTERSECT", TokenType::TK_INTERSECT), - ("INTO", TokenType::TK_INTO), - ("IS", TokenType::TK_IS), - ("ISNULL", TokenType::TK_ISNULL), - ("JOIN", TokenType::TK_JOIN), - ("KEY", TokenType::TK_KEY), - ("LAST", TokenType::TK_LAST), - ("LEFT", TokenType::TK_JOIN_KW), - ("LIKE", TokenType::TK_LIKE_KW), - ("LIMIT", TokenType::TK_LIMIT), - ("MATCH", TokenType::TK_MATCH), - ("MATERIALIZED", TokenType::TK_MATERIALIZED), - ("NATURAL", TokenType::TK_JOIN_KW), - ("NO", TokenType::TK_NO), - ("NOT", TokenType::TK_NOT), - ("NOTHING", TokenType::TK_NOTHING), - ("NOTNULL", TokenType::TK_NOTNULL), - ("NULL", TokenType::TK_NULL), - ("NULLS", TokenType::TK_NULLS), - ("OF", TokenType::TK_OF), - ("OFFSET", TokenType::TK_OFFSET), - ("ON", TokenType::TK_ON), - ("OR", TokenType::TK_OR), - ("ORDER", TokenType::TK_ORDER), - ("OTHERS", TokenType::TK_OTHERS), - ("OUTER", TokenType::TK_JOIN_KW), - ("OVER", TokenType::TK_OVER), - ("PARTITION", TokenType::TK_PARTITION), - ("PLAN", TokenType::TK_PLAN), - ("PRAGMA", TokenType::TK_PRAGMA), - ("PRECEDING", TokenType::TK_PRECEDING), - ("PRIMARY", TokenType::TK_PRIMARY), - ("QUERY", TokenType::TK_QUERY), - ("RAISE", TokenType::TK_RAISE), - ("RANGE", TokenType::TK_RANGE), - ("RECURSIVE", TokenType::TK_RECURSIVE), - ("REFERENCES", TokenType::TK_REFERENCES), - ("REGEXP", TokenType::TK_LIKE_KW), - ("REINDEX", TokenType::TK_REINDEX), - ("RELEASE", TokenType::TK_RELEASE), - ("RENAME", TokenType::TK_RENAME), - ("REPLACE", TokenType::TK_REPLACE), - ("RETURNING", TokenType::TK_RETURNING), - ("RESTRICT", TokenType::TK_RESTRICT), - ("RIGHT", TokenType::TK_JOIN_KW), - ("ROLLBACK", TokenType::TK_ROLLBACK), - ("ROW", TokenType::TK_ROW), - ("ROWS", TokenType::TK_ROWS), - ("SAVEPOINT", TokenType::TK_SAVEPOINT), - ("SELECT", TokenType::TK_SELECT), - ("SET", TokenType::TK_SET), - ("TABLE", TokenType::TK_TABLE), - ("TEMP", TokenType::TK_TEMP), - ("TEMPORARY", TokenType::TK_TEMP), - ("THEN", TokenType::TK_THEN), - ("TIES", TokenType::TK_TIES), - ("TO", TokenType::TK_TO), - ("TRANSACTION", TokenType::TK_TRANSACTION), - ("TRIGGER", TokenType::TK_TRIGGER), - ("UNBOUNDED", TokenType::TK_UNBOUNDED), - ("UNION", TokenType::TK_UNION), - ("UNIQUE", TokenType::TK_UNIQUE), - ("UPDATE", TokenType::TK_UPDATE), - ("USING", TokenType::TK_USING), - ("VACUUM", TokenType::TK_VACUUM), - ("VALUES", TokenType::TK_VALUES), - ("VIEW", TokenType::TK_VIEW), - ("VIRTUAL", TokenType::TK_VIRTUAL), - ("WHEN", TokenType::TK_WHEN), - ("WHERE", TokenType::TK_WHERE), - ("WINDOW", TokenType::TK_WINDOW), - ("WITH", TokenType::TK_WITH), - ("WITHOUT", TokenType::TK_WITHOUT), - ]); - - for (key, value) in &values { - assert!(keyword_token(key.as_bytes()).unwrap() == *value); - assert!( - keyword_token(key.as_bytes().to_ascii_lowercase().as_slice()).unwrap() == *value - ); - } - - assert!(keyword_token(b"").is_none()); - assert!(keyword_token(b"wrong").is_none()); - assert!(keyword_token(b"super wrong").is_none()); - assert!(keyword_token(b"super_wrong").is_none()); - assert!(keyword_token(b"aae26e78-3ba7-4627-8f8f-02623302495a").is_none()); - assert!(keyword_token("Crème Brulée".as_bytes()).is_none()); - assert!(keyword_token("fróm".as_bytes()).is_none()); - } -} diff --git a/vendored/sqlite3-parser/src/dialect/token.rs b/vendored/sqlite3-parser/src/dialect/token.rs deleted file mode 100644 index 72b6cd5b3..000000000 --- a/vendored/sqlite3-parser/src/dialect/token.rs +++ /dev/null @@ -1,181 +0,0 @@ -//! All terminal symbols. - -/// Token classes -// Generated by lemon (parse.h). -// Renamed manually. -// To be keep in sync. -#[non_exhaustive] -#[allow(non_camel_case_types, missing_docs)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)] -#[repr(u16)] -pub enum TokenType { - TK_EOF = 0, - TK_SEMI = 1, - TK_EXPLAIN = 2, - TK_QUERY = 3, - TK_PLAN = 4, - TK_BEGIN = 5, - TK_TRANSACTION = 6, - TK_DEFERRED = 7, - TK_IMMEDIATE = 8, - TK_EXCLUSIVE = 9, - TK_COMMIT = 10, - TK_END = 11, - TK_ROLLBACK = 12, - TK_SAVEPOINT = 13, - TK_RELEASE = 14, - TK_TO = 15, - TK_TABLE = 16, - TK_CREATE = 17, - TK_IF = 18, - TK_NOT = 19, - TK_EXISTS = 20, - TK_TEMP = 21, - TK_LP = 22, - TK_RP = 23, - TK_AS = 24, - TK_COMMA = 25, - TK_WITHOUT = 26, - TK_ABORT = 27, - TK_ACTION = 28, - TK_AFTER = 29, - TK_ANALYZE = 30, - TK_ASC = 31, - TK_ATTACH = 32, - TK_BEFORE = 33, - TK_BY = 34, - TK_CASCADE = 35, - TK_CAST = 36, - TK_CONFLICT = 37, - TK_DATABASE = 38, - TK_DESC = 39, - TK_DETACH = 40, - TK_EACH = 41, - TK_FAIL = 42, - TK_OR = 43, - TK_AND = 44, - TK_IS = 45, - TK_ISNOT = 46, - TK_MATCH = 47, - TK_LIKE_KW = 48, - TK_BETWEEN = 49, - TK_IN = 50, - TK_ISNULL = 51, - TK_NOTNULL = 52, - TK_NE = 53, - TK_EQ = 54, - TK_GT = 55, - TK_LE = 56, - TK_LT = 57, - TK_GE = 58, - TK_ESCAPE = 59, - TK_ID = 60, - TK_COLUMNKW = 61, - TK_DO = 62, - TK_FOR = 63, - TK_IGNORE = 64, - TK_INITIALLY = 65, - TK_INSTEAD = 66, - TK_NO = 67, - TK_KEY = 68, - TK_OF = 69, - TK_OFFSET = 70, - TK_PRAGMA = 71, - TK_RAISE = 72, - TK_RECURSIVE = 73, - TK_REPLACE = 74, - TK_RESTRICT = 75, - TK_ROW = 76, - TK_ROWS = 77, - TK_TRIGGER = 78, - TK_VACUUM = 79, - TK_VIEW = 80, - TK_VIRTUAL = 81, - TK_WITH = 82, - TK_NULLS = 83, - TK_FIRST = 84, - TK_LAST = 85, - TK_CURRENT = 86, - TK_FOLLOWING = 87, - TK_PARTITION = 88, - TK_PRECEDING = 89, - TK_RANGE = 90, - TK_UNBOUNDED = 91, - TK_EXCLUDE = 92, - TK_GROUPS = 93, - TK_OTHERS = 94, - TK_TIES = 95, - TK_GENERATED = 96, - TK_ALWAYS = 97, - TK_MATERIALIZED = 98, - TK_REINDEX = 99, - TK_RENAME = 100, - TK_CTIME_KW = 101, - TK_ANY = 102, - TK_BITAND = 103, - TK_BITOR = 104, - TK_LSHIFT = 105, - TK_RSHIFT = 106, - TK_PLUS = 107, - TK_MINUS = 108, - TK_STAR = 109, - TK_SLASH = 110, - TK_REM = 111, - TK_CONCAT = 112, - TK_PTR = 113, - TK_COLLATE = 114, - TK_BITNOT = 115, - TK_ON = 116, - TK_INDEXED = 117, - TK_STRING = 118, - TK_JOIN_KW = 119, - TK_CONSTRAINT = 120, - TK_DEFAULT = 121, - TK_NULL = 122, - TK_PRIMARY = 123, - TK_UNIQUE = 124, - TK_CHECK = 125, - TK_REFERENCES = 126, - TK_AUTOINCR = 127, - TK_INSERT = 128, - TK_DELETE = 129, - TK_UPDATE = 130, - TK_SET = 131, - TK_DEFERRABLE = 132, - TK_FOREIGN = 133, - TK_DROP = 134, - TK_UNION = 135, - TK_ALL = 136, - TK_EXCEPT = 137, - TK_INTERSECT = 138, - TK_SELECT = 139, - TK_VALUES = 140, - TK_DISTINCT = 141, - TK_DOT = 142, - TK_FROM = 143, - TK_JOIN = 144, - TK_USING = 145, - TK_ORDER = 146, - TK_GROUP = 147, - TK_HAVING = 148, - TK_LIMIT = 149, - TK_WHERE = 150, - TK_RETURNING = 151, - TK_INTO = 152, - TK_NOTHING = 153, - TK_BLOB = 154, - TK_FLOAT = 155, - TK_INTEGER = 156, - TK_VARIABLE = 157, - TK_CASE = 158, - TK_WHEN = 159, - TK_THEN = 160, - TK_ELSE = 161, - TK_INDEX = 162, - TK_ALTER = 163, - TK_ADD = 164, - TK_WINDOW = 165, - TK_OVER = 166, - TK_FILTER = 167, - TK_ILLEGAL = 185, -} diff --git a/vendored/sqlite3-parser/src/lexer/mod.rs b/vendored/sqlite3-parser/src/lexer/mod.rs deleted file mode 100644 index 953ff76a7..000000000 --- a/vendored/sqlite3-parser/src/lexer/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! Streaming SQLite tokenizer - -mod scan; -pub mod sql; - -pub use scan::{ScanError, Scanner, Splitter}; diff --git a/vendored/sqlite3-parser/src/lexer/scan.rs b/vendored/sqlite3-parser/src/lexer/scan.rs deleted file mode 100644 index b84f7d98c..000000000 --- a/vendored/sqlite3-parser/src/lexer/scan.rs +++ /dev/null @@ -1,173 +0,0 @@ -//! Adaptation/port of [Go scanner](http://tip.golang.org/pkg/bufio/#Scanner). - -use std::error::Error; -use std::fmt; - -/// Error with position -pub trait ScanError: Error + Sized { - /// Update the position where the error occurs - fn position(&mut self, line: u64, column: usize, offset: usize); -} - -/// The `(&[u8], TokenType)` is the token. -/// And the `usize` is the amount of bytes to consume. -type SplitResult<'input, TokenType, Error> = - Result<(Option<(&'input [u8], TokenType)>, usize), Error>; - -/// Split function used to tokenize the input -pub trait Splitter: Sized { - /// Potential error raised - type Error: ScanError; - //type Item: ?Sized; - /// Token generated - type TokenType; - - /// The arguments are an initial substring of the remaining unprocessed - /// data. - /// - /// If the returned error is non-nil, scanning stops and the error - /// is returned to the client. - /// - /// The function is never called with an empty data slice. - fn split<'input>( - &mut self, - data: &'input [u8], - ) -> SplitResult<'input, Self::TokenType, Self::Error>; -} - -/// Like a `BufReader` but with a growable buffer. -/// Successive calls to the `scan` method will step through the 'tokens' -/// of a file, skipping the bytes between the tokens. -/// -/// Scanning stops unrecoverably at EOF, the first I/O error, or a token too -/// large to fit in the buffer. When a scan stops, the reader may have -/// advanced arbitrarily far past the last token. -pub struct Scanner { - /// offset in `input` - offset: usize, - /// mark - mark: (usize, u64, usize), - /// The function to tokenize the input. - splitter: S, - /// current line number - line: u64, - /// current column number (byte offset, not char offset) - column: usize, -} - -impl Scanner { - /// Constructor - pub fn new(splitter: S) -> Self { - Self { - offset: 0, - mark: (0, 0, 0), - splitter, - line: 1, - column: 1, - } - } - - /// Current line number - pub fn line(&self) -> u64 { - self.line - } - - /// Current column number (byte offset, not char offset) - pub fn column(&self) -> usize { - self.column - } - - /// Current byte offset in the source string - pub fn offset(&self) -> usize { - self.offset - } - - /// Associated splitter - pub fn splitter(&self) -> &S { - &self.splitter - } - /// Mark current position - pub fn mark(&mut self) { - self.mark = (self.offset, self.line, self.column); - } - /// Reset to mark - pub fn reset_to_mark(&mut self) { - (self.offset, self.line, self.column) = self.mark; - } - - /// Reset the scanner such that it behaves as if it had never been used. - pub fn reset(&mut self) { - self.offset = 0; - self.line = 1; - self.column = 1; - } -} - -type ScanResult<'input, TokenType, Error> = - Result<(usize, Option<(&'input [u8], TokenType)>, usize), Error>; - -impl Scanner { - /// Advance the Scanner to next token. - /// Return the token as a byte slice. - /// Return `None` when the end of the input is reached. - /// Return any error that occurs while reading the input. - pub fn scan<'input>( - &mut self, - input: &'input [u8], - ) -> ScanResult<'input, S::TokenType, S::Error> { - // Loop until we have a token. - loop { - // See if we can get a token with what we already have. - if self.offset < input.len() { - let data = &input[self.offset..]; - match self.splitter.split(data) { - Err(mut e) => { - e.position(self.line, self.column, self.offset); - return Err(e); - } - Ok((None, 0)) => { - // Done - } - Ok((None, amt)) => { - // Ignore/skip this data - self.consume(data, amt); - continue; - } - Ok((tok, amt)) => { - let start = self.offset; - self.consume(data, amt); - return Ok((start, tok, self.offset)); - } - } - } - // We cannot generate a token with what we are holding. - // we are done. - return Ok((self.offset, None, self.offset)); - } - } - - /// Consume `amt` bytes of the buffer. - fn consume(&mut self, data: &[u8], amt: usize) { - debug_assert!(amt <= data.len()); - for byte in &data[..amt] { - if *byte == b'\n' { - self.line += 1; - self.column = 1; - } else { - self.column += 1; - } - } - self.offset += amt; - } -} - -impl fmt::Debug for Scanner { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Scanner") - .field("offset", &self.offset) - .field("mark", &self.mark) - .field("line", &self.line) - .field("column", &self.column) - .finish() - } -} diff --git a/vendored/sqlite3-parser/src/lexer/sql/error.rs b/vendored/sqlite3-parser/src/lexer/sql/error.rs deleted file mode 100644 index 71d46e269..000000000 --- a/vendored/sqlite3-parser/src/lexer/sql/error.rs +++ /dev/null @@ -1,148 +0,0 @@ -use std::error; -use std::fmt; - -use crate::lexer::scan::ScanError; -use crate::parser::ParserError; - -/// SQL lexer and parser errors -#[non_exhaustive] -#[derive(Debug, Clone, miette::Diagnostic)] -#[diagnostic()] -pub enum Error { - /// Lexer error - UnrecognizedToken( - Option<(u64, usize)>, - #[label("here")] Option, - ), - /// Missing quote or double-quote or backtick - UnterminatedLiteral( - Option<(u64, usize)>, - #[label("here")] Option, - ), - /// Missing `]` - UnterminatedBracket( - Option<(u64, usize)>, - #[label("here")] Option, - ), - /// Missing `*/` - UnterminatedBlockComment( - Option<(u64, usize)>, - #[label("here")] Option, - ), - /// Invalid parameter name - BadVariableName( - Option<(u64, usize)>, - #[label("here")] Option, - ), - /// Invalid number format - #[diagnostic(help("Invalid digit in `{3}`"))] - BadNumber( - Option<(u64, usize)>, - #[label("here")] Option, - Option, - String, // Holds the offending number as a string - ), - /// Invalid or missing sign after `!` - ExpectedEqualsSign( - Option<(u64, usize)>, - #[label("here")] Option, - ), - /// BLOB literals are string literals containing hexadecimal data and preceded by a single "x" or "X" character. - MalformedBlobLiteral( - Option<(u64, usize)>, - #[label("here")] Option, - ), - /// Hexadecimal integer literals follow the C-language notation of "0x" or "0X" followed by hexadecimal digits. - MalformedHexInteger( - Option<(u64, usize)>, - #[label("here")] Option, - Option, - #[help] Option<&'static str>, - ), - /// Grammar error - ParserError( - ParserError, - Option<(u64, usize)>, - #[label("syntax error")] Option, - ), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Self::UnrecognizedToken(pos, _) => { - write!(f, "unrecognized token at {:?}", pos.unwrap()) - } - Self::UnterminatedLiteral(pos, _) => { - write!(f, "non-terminated literal at {:?}", pos.unwrap()) - } - Self::UnterminatedBracket(pos, _) => { - write!(f, "non-terminated bracket at {:?}", pos.unwrap()) - } - Self::UnterminatedBlockComment(pos, _) => { - write!(f, "non-terminated block comment at {:?}", pos.unwrap()) - } - Self::BadVariableName(pos, _) => write!(f, "bad variable name at {:?}", pos.unwrap()), - Self::BadNumber(pos, _, _, _) => write!(f, "bad number at {:?}", pos.unwrap()), - Self::ExpectedEqualsSign(pos, _) => write!(f, "expected = sign at {:?}", pos.unwrap()), - Self::MalformedBlobLiteral(pos, _) => { - write!(f, "malformed blob literal at {:?}", pos.unwrap()) - } - Self::MalformedHexInteger(pos, _, _, _) => { - write!(f, "malformed hex integer at {:?}", pos.unwrap()) - } - Self::ParserError(ref msg, Some(pos), _) => write!(f, "{msg} at {pos:?}"), - Self::ParserError(ref msg, _, _) => write!(f, "{msg}"), - } - } -} - -impl error::Error for Error {} - -impl From for Error { - fn from(err: ParserError) -> Self { - Self::ParserError(err, None, None) - } -} - -impl ScanError for Error { - fn position(&mut self, line: u64, column: usize, offset: usize) { - match *self { - Self::UnrecognizedToken(ref mut pos, ref mut src) => { - *pos = Some((line, column)); - *src = Some((offset).into()); - } - Self::UnterminatedLiteral(ref mut pos, ref mut src) => { - *pos = Some((line, column)); - *src = Some((offset).into()); - } - Self::UnterminatedBracket(ref mut pos, ref mut src) => { - *pos = Some((line, column)); - *src = Some((offset).into()); - } - Self::UnterminatedBlockComment(ref mut pos, ref mut src) => { - *pos = Some((line, column)); - *src = Some((offset).into()); - } - Self::BadVariableName(ref mut pos, ref mut src) => { - *pos = Some((line, column)); - *src = Some((offset).into()); - } - Self::ExpectedEqualsSign(ref mut pos, ref mut src) => { - *pos = Some((line, column)); - *src = Some((offset).into()); - } - Self::MalformedBlobLiteral(ref mut pos, ref mut src) => { - *pos = Some((line, column)); - *src = Some((offset).into()); - } - // Exact same handling here - Self::MalformedHexInteger(ref mut pos, ref mut src, len, _) - | Self::BadNumber(ref mut pos, ref mut src, len, _) => { - *pos = Some((line, column)); - *src = Some((offset, len.unwrap_or(0)).into()); - } - Self::ParserError(_, ref mut pos, _) => *pos = Some((line, column)), - } - } -} diff --git a/vendored/sqlite3-parser/src/lexer/sql/mod.rs b/vendored/sqlite3-parser/src/lexer/sql/mod.rs deleted file mode 100644 index 099cfbd68..000000000 --- a/vendored/sqlite3-parser/src/lexer/sql/mod.rs +++ /dev/null @@ -1,762 +0,0 @@ -//! Adaptation/port of [`SQLite` tokenizer](http://www.sqlite.org/src/artifact?ci=trunk&filename=src/tokenize.c) -use fallible_iterator::FallibleIterator; -use memchr::memchr; - -pub use crate::dialect::TokenType; -use crate::dialect::TokenType::*; -use crate::dialect::{is_identifier_continue, is_identifier_start, keyword_token, sentinel}; -use crate::parser::ast::Cmd; -use crate::parser::parse::{yyParser, YYCODETYPE}; -use crate::parser::Context; - -mod error; -#[cfg(test)] -mod test; - -use crate::lexer::scan::ScanError; -use crate::lexer::scan::Splitter; -use crate::lexer::Scanner; -pub use crate::parser::ParserError; -pub use error::Error; - -// TODO Extract scanning stuff and move this into the parser crate -// to make possible to use the tokenizer without depending on the parser... - -/// SQL parser -pub struct Parser<'input> { - input: &'input [u8], - scanner: Scanner, - /// lemon parser - parser: yyParser<'input>, - had_error: bool, -} - -impl<'input> Parser<'input> { - /// Constructor - pub fn new(input: &'input [u8]) -> Self { - let lexer = Tokenizer::new(); - let scanner = Scanner::new(lexer); - let ctx = Context::new(input); - let parser = yyParser::new(ctx); - Parser { - input, - scanner, - parser, - had_error: false, - } - } - /// Parse new `input` - pub fn reset(&mut self, input: &'input [u8]) { - self.input = input; - self.scanner.reset(); - self.had_error = false; - } - /// Current line position in input - pub fn line(&self) -> u64 { - self.scanner.line() - } - /// Current column position in input - pub fn column(&self) -> usize { - self.scanner.column() - } - - /// Current byte offset in input - pub fn offset(&self) -> usize { - self.scanner.offset() - } - - /// Public API for sqlite3ParserFinalize() - pub fn finalize(&mut self) { - self.parser.sqlite3ParserFinalize(); - } -} - -/* - ** Return the id of the next token in input. - */ -fn get_token(scanner: &mut Scanner, input: &[u8]) -> Result { - let mut t = { - let (_, token_type) = match scanner.scan(input)? { - (_, None, _) => { - return Ok(TK_EOF); - } - (_, Some(tuple), _) => tuple, - }; - token_type - }; - if t == TK_ID - || t == TK_STRING - || t == TK_JOIN_KW - || t == TK_WINDOW - || t == TK_OVER - || yyParser::parse_fallback(t as YYCODETYPE) == TK_ID as YYCODETYPE - { - t = TK_ID; - } - Ok(t) -} - -/* - ** The following three functions are called immediately after the tokenizer - ** reads the keywords WINDOW, OVER and FILTER, respectively, to determine - ** whether the token should be treated as a keyword or an SQL identifier. - ** This cannot be handled by the usual lemon %fallback method, due to - ** the ambiguity in some constructions. e.g. - ** - ** SELECT sum(x) OVER ... - ** - ** In the above, "OVER" might be a keyword, or it might be an alias for the - ** sum(x) expression. If a "%fallback ID OVER" directive were added to - ** grammar, then SQLite would always treat "OVER" as an alias, making it - ** impossible to call a window-function without a FILTER clause. - ** - ** WINDOW is treated as a keyword if: - ** - ** * the following token is an identifier, or a keyword that can fallback - ** to being an identifier, and - ** * the token after than one is TK_AS. - ** - ** OVER is a keyword if: - ** - ** * the previous token was TK_RP, and - ** * the next token is either TK_LP or an identifier. - ** - ** FILTER is a keyword if: - ** - ** * the previous token was TK_RP, and - ** * the next token is TK_LP. - */ -fn analyze_window_keyword( - scanner: &mut Scanner, - input: &[u8], -) -> Result { - let t = get_token(scanner, input)?; - if t != TK_ID { - return Ok(TK_ID); - }; - let t = get_token(scanner, input)?; - if t != TK_AS { - return Ok(TK_ID); - }; - Ok(TK_WINDOW) -} -fn analyze_over_keyword( - scanner: &mut Scanner, - input: &[u8], - last_token: TokenType, -) -> Result { - if last_token == TK_RP { - let t = get_token(scanner, input)?; - if t == TK_LP || t == TK_ID { - return Ok(TK_OVER); - } - } - Ok(TK_ID) -} -fn analyze_filter_keyword( - scanner: &mut Scanner, - input: &[u8], - last_token: TokenType, -) -> Result { - if last_token == TK_RP && get_token(scanner, input)? == TK_LP { - return Ok(TK_FILTER); - } - Ok(TK_ID) -} - -macro_rules! try_with_position { - ($scanner:expr, $expr:expr) => { - match $expr { - Ok(val) => val, - Err(err) => { - let mut err = Error::from(err); - err.position($scanner.line(), $scanner.column(), $scanner.offset() - 1); - return Err(err); - } - } - }; -} - -impl FallibleIterator for Parser<'_> { - type Item = Cmd; - type Error = Error; - - fn next(&mut self) -> Result, Error> { - //print!("line: {}, column: {}: ", self.scanner.line(), self.scanner.column()); - // if we have already encountered an error, return None to signal that to fallible_iterator that we are done parsing - if self.had_error { - return Ok(None); - } - self.parser.ctx.reset(); - let mut last_token_parsed = TK_EOF; - let mut eof = false; - loop { - let (start, (value, mut token_type), end) = match self.scanner.scan(self.input)? { - (_, None, _) => { - eof = true; - break; - } - (start, Some(tuple), end) => (start, tuple, end), - }; - - if token_type == TK_ILLEGAL { - // break out of parsing loop and return error - self.parser.sqlite3ParserFinalize(); - self.had_error = true; - return Err(Error::UnrecognizedToken( - Some((self.scanner.line(), self.scanner.column())), - Some(start.into()), - )); - } - - let token = if token_type >= TK_WINDOW { - debug_assert!( - token_type == TK_OVER || token_type == TK_FILTER || token_type == TK_WINDOW - ); - self.scanner.mark(); - if token_type == TK_WINDOW { - token_type = analyze_window_keyword(&mut self.scanner, self.input)?; - } else if token_type == TK_OVER { - token_type = - analyze_over_keyword(&mut self.scanner, self.input, last_token_parsed)?; - } else if token_type == TK_FILTER { - token_type = - analyze_filter_keyword(&mut self.scanner, self.input, last_token_parsed)?; - } - self.scanner.reset_to_mark(); - token_type.to_token(start, value, end) - } else { - token_type.to_token(start, value, end) - }; - //println!("({:?}, {:?})", token_type, token); - try_with_position!(self.scanner, self.parser.sqlite3Parser(token_type, token)); - last_token_parsed = token_type; - if self.parser.ctx.done() { - //println!(); - break; - } - } - if last_token_parsed == TK_EOF { - return Ok(None); // empty input - } - /* Upon reaching the end of input, call the parser two more times - with tokens TK_SEMI and 0, in that order. */ - if eof && self.parser.ctx.is_ok() { - if last_token_parsed != TK_SEMI { - try_with_position!( - self.scanner, - self.parser - .sqlite3Parser(TK_SEMI, sentinel(self.input.len())) - ); - if self.parser.ctx.error().is_some() { - self.had_error = true; - } - } - try_with_position!( - self.scanner, - self.parser - .sqlite3Parser(TK_EOF, sentinel(self.input.len())) - ); - if self.parser.ctx.error().is_some() { - self.had_error = true; - } - } - self.parser.sqlite3ParserFinalize(); - if let Some(e) = self.parser.ctx.error() { - let err = Error::ParserError( - e, - Some((self.scanner.line(), self.scanner.column())), - Some((self.offset() - 1).into()), - ); - self.had_error = true; - return Err(err); - } - let cmd = self.parser.ctx.cmd(); - if let Some(ref cmd) = cmd { - if let Err(e) = cmd.check() { - let err = Error::ParserError( - e, - Some((self.scanner.line(), self.scanner.column())), - Some((self.offset() - 1).into()), - ); - self.had_error = true; - return Err(err); - } - } - Ok(cmd) - } -} - -/// SQL token -pub type Token<'input> = (&'input [u8], TokenType); - -/// SQL lexer -#[derive(Default)] -pub struct Tokenizer {} - -impl Tokenizer { - /// Constructor - pub fn new() -> Self { - Self {} - } -} - -/// ```rust -/// use turso_sqlite3_parser::lexer::sql::Tokenizer; -/// use turso_sqlite3_parser::lexer::Scanner; -/// -/// let tokenizer = Tokenizer::new(); -/// let input = b"PRAGMA parser_trace=ON;"; -/// let mut s = Scanner::new(tokenizer); -/// let Ok((_, Some((token1, _)), _)) = s.scan(input) else { panic!() }; -/// s.scan(input).unwrap(); -/// assert!(b"PRAGMA".eq_ignore_ascii_case(token1)); -/// ``` -impl Splitter for Tokenizer { - type Error = Error; - type TokenType = TokenType; - - fn split<'input>( - &mut self, - data: &'input [u8], - ) -> Result<(Option>, usize), Error> { - if data[0].is_ascii_whitespace() { - // eat as much space as possible - return Ok(( - None, - match data.iter().skip(1).position(|&b| !b.is_ascii_whitespace()) { - Some(i) => i + 1, - _ => data.len(), - }, - )); - } - match data[0] { - b'-' => { - if let Some(b) = data.get(1) { - if *b == b'-' { - // eat comment - if let Some(i) = memchr(b'\n', data) { - Ok((None, i + 1)) - } else { - Ok((None, data.len())) - } - } else if *b == b'>' { - if let Some(b) = data.get(2) { - if *b == b'>' { - return Ok((Some((&data[..3], TK_PTR)), 3)); - } - } - Ok((Some((&data[..2], TK_PTR)), 2)) - } else { - Ok((Some((&data[..1], TK_MINUS)), 1)) - } - } else { - Ok((Some((&data[..1], TK_MINUS)), 1)) - } - } - b'(' => Ok((Some((&data[..1], TK_LP)), 1)), - b')' => Ok((Some((&data[..1], TK_RP)), 1)), - b';' => Ok((Some((&data[..1], TK_SEMI)), 1)), - b'+' => Ok((Some((&data[..1], TK_PLUS)), 1)), - b'*' => Ok((Some((&data[..1], TK_STAR)), 1)), - b'/' => { - if let Some(b) = data.get(1) { - if *b == b'*' { - // eat comment - let mut pb = 0; - let mut end = None; - for (i, b) in data.iter().enumerate().skip(2) { - if *b == b'/' && pb == b'*' { - end = Some(i); - break; - } - pb = *b; - } - if let Some(i) = end { - Ok((None, i + 1)) - } else { - Err(Error::UnterminatedBlockComment(None, None)) - } - } else { - Ok((Some((&data[..1], TK_SLASH)), 1)) - } - } else { - Ok((Some((&data[..1], TK_SLASH)), 1)) - } - } - b'%' => Ok((Some((&data[..1], TK_REM)), 1)), - b'=' => { - if let Some(b) = data.get(1) { - Ok(if *b == b'=' { - (Some((&data[..2], TK_EQ)), 2) - } else { - (Some((&data[..1], TK_EQ)), 1) - }) - } else { - Ok((Some((&data[..1], TK_EQ)), 1)) - } - } - b'<' => { - if let Some(b) = data.get(1) { - Ok(match *b { - b'=' => (Some((&data[..2], TK_LE)), 2), - b'>' => (Some((&data[..2], TK_NE)), 2), - b'<' => (Some((&data[..2], TK_LSHIFT)), 2), - _ => (Some((&data[..1], TK_LT)), 1), - }) - } else { - Ok((Some((&data[..1], TK_LT)), 1)) - } - } - b'>' => { - if let Some(b) = data.get(1) { - Ok(match *b { - b'=' => (Some((&data[..2], TK_GE)), 2), - b'>' => (Some((&data[..2], TK_RSHIFT)), 2), - _ => (Some((&data[..1], TK_GT)), 1), - }) - } else { - Ok((Some((&data[..1], TK_GT)), 1)) - } - } - b'!' => { - if let Some(b) = data.get(1) { - if *b == b'=' { - Ok((Some((&data[..2], TK_NE)), 2)) - } else { - Err(Error::ExpectedEqualsSign(None, None)) - } - } else { - Err(Error::ExpectedEqualsSign(None, None)) - } - } - b'|' => { - if let Some(b) = data.get(1) { - Ok(if *b == b'|' { - (Some((&data[..2], TK_CONCAT)), 2) - } else { - (Some((&data[..1], TK_BITOR)), 1) - }) - } else { - Ok((Some((&data[..1], TK_BITOR)), 1)) - } - } - b',' => Ok((Some((&data[..1], TK_COMMA)), 1)), - b'&' => Ok((Some((&data[..1], TK_BITAND)), 1)), - b'~' => Ok((Some((&data[..1], TK_BITNOT)), 1)), - quote @ (b'`' | b'\'' | b'"') => literal(data, quote), - b'.' => { - if let Some(b) = data.get(1) { - if b.is_ascii_digit() { - fractional_part(data, 0) - } else { - Ok((Some((&data[..1], TK_DOT)), 1)) - } - } else { - Ok((Some((&data[..1], TK_DOT)), 1)) - } - } - b'0'..=b'9' => number(data), - b'[' => { - if let Some(i) = memchr(b']', data) { - // Keep original quotes / '[' ... ’]' - Ok((Some((&data[0..=i], TK_ID)), i + 1)) - } else { - Err(Error::UnterminatedBracket(None, None)) - } - } - b'?' => { - match data.iter().skip(1).position(|&b| !b.is_ascii_digit()) { - Some(i) => { - // do not include the '?' in the token - Ok((Some((&data[1..=i], TK_VARIABLE)), i + 1)) - } - None => { - if !data[1..].is_empty() && data[1..].iter().all(|ch| *ch == b'0') { - return Err(Error::BadVariableName(None, None)); - } - Ok((Some((&data[1..], TK_VARIABLE)), data.len())) - } - } - } - b'$' | b'@' | b'#' | b':' => { - match data - .iter() - .skip(1) - .position(|&b| !is_identifier_continue(b)) - { - Some(0) => Err(Error::BadVariableName(None, None)), - Some(i) => { - // '$' is included as part of the name - Ok((Some((&data[..=i], TK_VARIABLE)), i + 1)) - } - None => { - if data.len() == 1 { - return Err(Error::BadVariableName(None, None)); - } - Ok((Some((data, TK_VARIABLE)), data.len())) - } - } - } - b if is_identifier_start(b) => { - if b == b'x' || b == b'X' { - if let Some(&b'\'') = data.get(1) { - blob_literal(data) - } else { - Ok(self.identifierish(data)) - } - } else { - Ok(self.identifierish(data)) - } - } - // Return TK_ILLEGAL - _ => handle_unrecognized(data), - } - } -} - -fn handle_unrecognized(data: &[u8]) -> Result<(Option>, usize), Error> { - let mut end = 1; - while end < data.len() && !data[end].is_ascii_whitespace() { - end += 1; - } - - Ok((Some((&data[..end], TokenType::TK_ILLEGAL)), end)) -} - -fn literal(data: &[u8], quote: u8) -> Result<(Option>, usize), Error> { - debug_assert_eq!(data[0], quote); - let tt = if quote == b'\'' { TK_STRING } else { TK_ID }; - let mut pb = 0; - let mut end = None; - // data[0] == quote => skip(1) - for (i, b) in data.iter().enumerate().skip(1) { - if *b == quote { - if pb == quote { - // escaped quote - pb = 0; - continue; - } - } else if pb == quote { - end = Some(i); - break; - } - pb = *b; - } - if end.is_some() || pb == quote { - let i = match end { - Some(i) => i, - _ => data.len(), - }; - // keep original quotes in the token - Ok((Some((&data[0..i], tt)), i)) - } else { - Err(Error::UnterminatedLiteral(None, None)) - } -} - -fn blob_literal(data: &[u8]) -> Result<(Option>, usize), Error> { - debug_assert!(data[0] == b'x' || data[0] == b'X'); - debug_assert_eq!(data[1], b'\''); - - let mut end = 2; - let mut valid = true; - while end < data.len() && data[end] != b'\'' { - if !data[end].is_ascii_hexdigit() { - valid = false; - } - end += 1; - } - - let total_len = if end < data.len() { end + 1 } else { end }; - - if !valid || (end - 2) % 2 != 0 || end >= data.len() { - return Ok((Some((&data[..total_len], TokenType::TK_ILLEGAL)), total_len)); - } - - Ok((Some((&data[2..end], TokenType::TK_BLOB)), total_len)) -} - -fn number(data: &[u8]) -> Result<(Option>, usize), Error> { - debug_assert!(data[0].is_ascii_digit()); - if data[0] == b'0' { - if let Some(b) = data.get(1) { - if *b == b'x' || *b == b'X' { - return hex_integer(data); - } - } else { - return Ok((Some((data, TK_INTEGER)), data.len())); - } - } - if let Some((i, b)) = find_end_of_number(data, 1, u8::is_ascii_digit)? { - if b == b'.' { - return fractional_part(data, i); - } else if b == b'e' || b == b'E' { - return exponential_part(data, i); - } else if is_identifier_start(b) { - return Err(Error::BadNumber(None, None, Some(i + 1), unsafe { - String::from_utf8_unchecked(data[..i + 1].to_vec()) - })); - } - Ok((Some((&data[..i], TK_INTEGER)), i)) - } else { - Ok((Some((data, TK_INTEGER)), data.len())) - } -} - -fn hex_integer(data: &[u8]) -> Result<(Option>, usize), Error> { - debug_assert_eq!(data[0], b'0'); - debug_assert!(data[1] == b'x' || data[1] == b'X'); - if let Some((i, b)) = find_end_of_number(data, 2, u8::is_ascii_hexdigit)? { - // Must not be empty (Ox is invalid) - if i == 2 || is_identifier_start(b) { - let (len, help) = if i == 2 && !is_identifier_start(b) { - (i, "Did you forget to add digits after '0x' or '0X'?") - } else { - (i + 1, "There are some invalid digits after '0x' or '0X'") - }; - return Err(Error::MalformedHexInteger( - None, - None, - Some(len), // Length of the malformed hex - Some(help), // Help Message - )); - } - Ok((Some((&data[..i], TK_INTEGER)), i)) - } else { - // Must not be empty (Ox is invalid) - if data.len() == 2 { - return Err(Error::MalformedHexInteger( - None, - None, - Some(2), // Length of the malformed hex - Some("Did you forget to add digits after '0x' or '0X'?"), // Help Message - )); - } - Ok((Some((data, TK_INTEGER)), data.len())) - } -} - -fn fractional_part(data: &[u8], i: usize) -> Result<(Option>, usize), Error> { - debug_assert_eq!(data[i], b'.'); - if let Some((i, b)) = find_end_of_number(data, i + 1, u8::is_ascii_digit)? { - if b == b'e' || b == b'E' { - return exponential_part(data, i); - } else if is_identifier_start(b) { - return Err(Error::BadNumber(None, None, Some(i + 1), unsafe { - String::from_utf8_unchecked(data[..i + 1].to_vec()) - })); - } - Ok((Some((&data[..i], TK_FLOAT)), i)) - } else { - Ok((Some((data, TK_FLOAT)), data.len())) - } -} - -fn exponential_part(data: &[u8], i: usize) -> Result<(Option>, usize), Error> { - debug_assert!(data[i] == b'e' || data[i] == b'E'); - // data[i] == 'e'|'E' - if let Some(b) = data.get(i + 1) { - let i = if *b == b'+' || *b == b'-' { i + 1 } else { i }; - if let Some((j, b)) = find_end_of_number(data, i + 1, u8::is_ascii_digit)? { - if j == i + 1 || is_identifier_start(b) { - let len = if is_identifier_start(b) { j + 1 } else { j }; - return Err(Error::BadNumber(None, None, Some(len), unsafe { - String::from_utf8_unchecked(data[..len].to_vec()) - })); - } - Ok((Some((&data[..j], TK_FLOAT)), j)) - } else { - if data.len() == i + 1 { - return Err(Error::BadNumber(None, None, Some(i + 1), unsafe { - String::from_utf8_unchecked(data[..i + 1].to_vec()) - })); - } - Ok((Some((data, TK_FLOAT)), data.len())) - } - } else { - Err(Error::BadNumber(None, None, Some(data.len()), unsafe { - String::from_utf8_unchecked(data.to_vec()) - })) - } -} - -fn find_end_of_number( - data: &[u8], - i: usize, - test: fn(&u8) -> bool, -) -> Result, Error> { - for (j, &b) in data.iter().enumerate().skip(i) { - if test(&b) { - continue; - } else if b == b'_' { - if j >= 1 && data.get(j - 1).is_some_and(test) && data.get(j + 1).is_some_and(test) { - continue; - } - return Err(Error::BadNumber(None, None, Some(j), unsafe { - String::from_utf8_unchecked(data[..j].to_vec()) - })); - } else { - return Ok(Some((j, b))); - } - } - Ok(None) -} - -impl Tokenizer { - fn identifierish<'input>(&mut self, data: &'input [u8]) -> (Option>, usize) { - debug_assert!(is_identifier_start(data[0])); - // data[0] is_identifier_start => skip(1) - let end = data - .iter() - .skip(1) - .position(|&b| !is_identifier_continue(b)); - let i = match end { - Some(i) => i + 1, - _ => data.len(), - }; - let word = &data[..i]; - (Some((word, keyword_token(word).unwrap_or(TK_ID))), i) - } -} - -#[cfg(test)] -mod tests { - use super::Tokenizer; - use crate::dialect::TokenType; - use crate::lexer::sql::Error; - use crate::lexer::Scanner; - - #[test] - fn fallible_iterator() -> Result<(), Error> { - let tokenizer = Tokenizer::new(); - let input = b"PRAGMA parser_trace=ON;"; - let mut s = Scanner::new(tokenizer); - expect_token(&mut s, input, b"PRAGMA", TokenType::TK_PRAGMA)?; - expect_token(&mut s, input, b"parser_trace", TokenType::TK_ID)?; - Ok(()) - } - - #[test] - fn invalid_number_literal() -> Result<(), Error> { - let tokenizer = Tokenizer::new(); - let input = b"SELECT 1E;"; - let mut s = Scanner::new(tokenizer); - expect_token(&mut s, input, b"SELECT", TokenType::TK_SELECT)?; - let err = s.scan(input).unwrap_err(); - assert!(matches!(err, Error::BadNumber(_, _, _, _))); - Ok(()) - } - - fn expect_token( - s: &mut Scanner, - input: &[u8], - token: &[u8], - token_type: TokenType, - ) -> Result<(), Error> { - let (t, tt) = s.scan(input)?.1.unwrap(); - assert_eq!(token, t); - assert_eq!(token_type, tt); - Ok(()) - } -} diff --git a/vendored/sqlite3-parser/src/lexer/sql/test.rs b/vendored/sqlite3-parser/src/lexer/sql/test.rs deleted file mode 100644 index f75ea50cc..000000000 --- a/vendored/sqlite3-parser/src/lexer/sql/test.rs +++ /dev/null @@ -1,375 +0,0 @@ -use fallible_iterator::FallibleIterator; - -use super::{Error, Parser}; -use crate::parser::ast::fmt::ToTokens; -use crate::parser::{ - ast::{Cmd, ParameterInfo, Stmt}, - ParserError, -}; - -#[test] -fn count_placeholders() { - let ast = parse_cmd(b"SELECT ? WHERE 1 = ?"); - let mut info = ParameterInfo::default(); - ast.to_tokens(&mut info).unwrap(); - assert_eq!(info.count, 2); -} - -#[test] -fn count_numbered_placeholders() { - let ast = parse_cmd(b"SELECT ?1 WHERE 1 = ?2 AND 0 = ?1"); - let mut info = ParameterInfo::default(); - ast.to_tokens(&mut info).unwrap(); - assert_eq!(info.count, 2); -} - -#[test] -fn count_unused_placeholders() { - let ast = parse_cmd(b"SELECT ?1 WHERE 1 = ?3"); - let mut info = ParameterInfo::default(); - ast.to_tokens(&mut info).unwrap(); - assert_eq!(info.count, 3); -} - -#[test] -fn count_named_placeholders() { - let ast = parse_cmd(b"SELECT :x, :y WHERE 1 = :y"); - let mut info = ParameterInfo::default(); - ast.to_tokens(&mut info).unwrap(); - assert_eq!(info.count, 2); - assert_eq!(info.names.len(), 2); - assert!(info.names.contains(":x")); - assert!(info.names.contains(":y")); -} - -#[test] -fn duplicate_column() { - expect_parser_err_msg( - b"CREATE TABLE t (x TEXT, x TEXT)", - "duplicate column name: x", - ); - expect_parser_err_msg( - b"CREATE TABLE t (x TEXT, \"x\" TEXT)", - "duplicate column name: \"x\"", - ); - expect_parser_err_msg( - b"CREATE TABLE t (x TEXT, `x` TEXT)", - "duplicate column name: `x`", - ); -} - -#[test] -fn create_table_without_column() { - expect_parser_err( - b"CREATE TABLE t ()", - ParserError::SyntaxError(")".to_owned()), - ); -} - -#[test] -fn vtab_args() -> Result<(), Error> { - let sql = b"CREATE VIRTUAL TABLE mail USING fts3( - subject VARCHAR(256) NOT NULL, - body TEXT CHECK(length(body)<10240) -);"; - let r = parse_cmd(sql); - let Cmd::Stmt(Stmt::CreateVirtualTable(create_virtual_table)) = r else { - panic!("unexpected AST") - }; - assert_eq!(create_virtual_table.tbl_name.name, "mail"); - assert_eq!(create_virtual_table.module_name.as_str(), "fts3"); - let args = create_virtual_table.args.as_ref().unwrap(); - assert_eq!(args.len(), 2); - assert_eq!(args[0], "subject VARCHAR(256) NOT NULL"); - assert_eq!(args[1], "body TEXT CHECK(length(body)<10240)"); - Ok(()) -} - -#[test] -fn only_semicolons_no_statements() { - let sqls = ["", ";", ";;;"]; - for sql in &sqls { - let r = parse(sql.as_bytes()); - assert_eq!(r.unwrap(), None); - } -} - -#[test] -fn extra_semicolons_between_statements() { - let sqls = [ - "SELECT 1; SELECT 2", - "SELECT 1; SELECT 2;", - "; SELECT 1; SELECT 2", - ";; SELECT 1;; SELECT 2;;", - ]; - for sql in &sqls { - let mut parser = Parser::new(sql.as_bytes()); - assert!(matches!( - parser.next().unwrap(), - Some(Cmd::Stmt(Stmt::Select { .. })) - )); - assert!(matches!( - parser.next().unwrap(), - Some(Cmd::Stmt(Stmt::Select { .. })) - )); - assert_eq!(parser.next().unwrap(), None); - } -} - -#[test] -fn extra_comments_between_statements() { - let sqls = [ - "-- abc\nSELECT 1; --def\nSELECT 2 -- ghj", - "/* abc */ SELECT 1; /* def */ SELECT 2; /* ghj */", - "/* abc */; SELECT 1 /* def */; SELECT 2 /* ghj */", - "/* abc */;; SELECT 1;/* def */; SELECT 2; /* ghj */; /* klm */", - ]; - for sql in &sqls { - let mut parser = Parser::new(sql.as_bytes()); - assert!(matches!( - parser.next().unwrap(), - Some(Cmd::Stmt(Stmt::Select { .. })) - )); - assert!(matches!( - parser.next().unwrap(), - Some(Cmd::Stmt(Stmt::Select { .. })) - )); - assert_eq!(parser.next().unwrap(), None); - } -} - -#[test] -fn insert_mismatch_count() { - expect_parser_err_msg(b"INSERT INTO t (a, b) VALUES (1)", "1 values for 2 columns"); -} - -#[test] -fn insert_default_values() { - expect_parser_err_msg( - b"INSERT INTO t (a) DEFAULT VALUES", - "0 values for 1 columns", - ); -} - -#[test] -fn create_view_mismatch_count() { - expect_parser_err_msg( - b"CREATE VIEW v (c1, c2) AS SELECT 1", - "expected 2 columns for v but got 1", - ); -} - -#[test] -fn create_view_duplicate_column_name() { - expect_parser_err_msg( - b"CREATE VIEW v (c1, c1) AS SELECT 1, 2", - "duplicate column name: c1", - ); -} - -#[test] -fn create_table_without_rowid_missing_pk() { - expect_parser_err_msg( - b"CREATE TABLE t (c1) WITHOUT ROWID", - "PRIMARY KEY missing on table t", - ); -} - -#[test] -fn create_temporary_table_with_qualified_name() { - expect_parser_err_msg( - b"CREATE TEMPORARY TABLE mem.x AS SELECT 1", - "temporary table name must be unqualified", - ); - parse_cmd(b"CREATE TEMPORARY TABLE temp.x AS SELECT 1"); -} - -#[test] -fn create_table_with_only_generated_column() { - expect_parser_err_msg( - b"CREATE TABLE test (data AS (1))", - "must have at least one non-generated column", - ); -} - -#[test] -fn create_strict_table_missing_datatype() { - expect_parser_err_msg(b"CREATE TABLE t (c1) STRICT", "missing datatype for t.c1"); -} - -#[test] -fn create_strict_table_unknown_datatype() { - expect_parser_err_msg( - b"CREATE TABLE t (c1 BOOL) STRICT", - "unknown datatype for t.c1: \"BOOL\"", - ); -} - -#[test] -fn foreign_key_on_column() { - expect_parser_err_msg( - b"CREATE TABLE t (a REFERENCES o(a,b))", - "foreign key on a should reference only one column of table o", - ); -} - -#[test] -fn create_strict_table_generated_column() { - parse_cmd( - b"CREATE TABLE IF NOT EXISTS transactions ( - debit REAL, - credit REAL, - amount REAL GENERATED ALWAYS AS (ifnull(credit, 0.0) -ifnull(debit, 0.0)) - ) STRICT;", - ); -} - -#[test] -fn selects_compound_mismatch_columns_count() { - expect_parser_err_msg( - b"SELECT 1 UNION SELECT 1, 2", - "SELECTs to the left and right of UNION do not have the same number of result columns", - ); -} - -#[test] -fn delete_order_by_without_limit() { - expect_parser_err_msg( - b"DELETE FROM t ORDER BY x", - "ORDER BY without LIMIT on DELETE", - ); -} - -#[test] -fn update_order_by_without_limit() { - expect_parser_err_msg( - b"UPDATE t SET x = 1 ORDER BY x", - "ORDER BY without LIMIT on UPDATE", - ); -} - -#[test] -fn values_mismatch_columns_count() { - expect_parser_err_msg( - b"INSERT INTO t VALUES (1), (1,2)", - "all VALUES must have the same number of terms", - ); -} - -#[test] -fn column_specified_more_than_once() { - expect_parser_err_msg( - b"INSERT INTO t (n, n, m) VALUES (1, 0, 2)", - "column \"n\" specified more than once", - ) -} - -#[test] -fn alter_add_column_primary_key() { - expect_parser_err_msg( - b"ALTER TABLE t ADD COLUMN c PRIMARY KEY", - "Cannot add a PRIMARY KEY column", - ); -} - -#[test] -fn alter_add_column_unique() { - expect_parser_err_msg( - b"ALTER TABLE t ADD COLUMN c UNIQUE", - "Cannot add a UNIQUE column", - ); -} - -#[test] -fn natural_join_on() { - expect_parser_err_msg( - b"SELECT x FROM t NATURAL JOIN t USING (x)", - "a NATURAL join may not have an ON or USING clause", - ); - expect_parser_err_msg( - b"SELECT x FROM t NATURAL JOIN t ON t.x = t.x", - "a NATURAL join may not have an ON or USING clause", - ); -} - -#[test] -fn missing_join_clause() { - expect_parser_err_msg( - b"SELECT a FROM tt ON b", - "a JOIN clause is required before ON", - ); -} - -#[test] -fn cast_without_typename() { - parse_cmd(b"SELECT CAST(a AS ) FROM t"); -} - -#[test] -fn unknown_table_option() { - expect_parser_err_msg(b"CREATE TABLE t (x)o", "unknown table option: o"); - expect_parser_err_msg(b"CREATE TABLE t (x) WITHOUT o", "unknown table option: o"); -} - -#[test] -fn qualified_table_name_within_triggers() { - expect_parser_err_msg( - b"CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN - DELETE FROM main.t2; - END;", - "qualified table names are not allowed on INSERT, UPDATE, and DELETE statements \ - within triggers", - ); -} - -#[test] -fn select_from_error_stops_at_first_error() { - let mut parser = Parser::new(b"SELECT FROM foo;"); - - // First next() call should return the first syntax error - let err = parser.next().unwrap_err(); - assert!(matches!(err, Error::ParserError(_, _, _))); - - // Second next() call should return Ok(None) since parsing should have stopped - assert_eq!(parser.next().unwrap(), None); - - // Third next() call should also return Ok(None) - assert_eq!(parser.next().unwrap(), None); -} - -#[test] -fn indexed_by_clause_within_triggers() { - expect_parser_err_msg( - b"CREATE TRIGGER main.t16err5 AFTER INSERT ON tA BEGIN - UPDATE t16 INDEXED BY t16a SET rowid=rowid+1 WHERE a=1; - END;", - "the INDEXED BY clause is not allowed on UPDATE or DELETE statements \ - within triggers", - ); - expect_parser_err_msg( - b"CREATE TRIGGER main.t16err6 AFTER INSERT ON tA BEGIN - DELETE FROM t16 NOT INDEXED WHERE a=123; - END;", - "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements \ - within triggers", - ); -} - -fn expect_parser_err_msg(input: &[u8], error_msg: &str) { - expect_parser_err(input, ParserError::Custom(error_msg.to_owned())) -} -fn expect_parser_err(input: &[u8], err: ParserError) { - let r = parse(input); - if let Error::ParserError(e, _, _) = r.unwrap_err() { - assert_eq!(e, err); - } else { - panic!("unexpected error type") - }; -} -fn parse_cmd(input: &[u8]) -> Cmd { - parse(input).unwrap().unwrap() -} -fn parse(input: &[u8]) -> Result, Error> { - let mut parser = Parser::new(input); - parser.next() -} diff --git a/vendored/sqlite3-parser/src/lib.rs b/vendored/sqlite3-parser/src/lib.rs deleted file mode 100644 index d5634f9c2..000000000 --- a/vendored/sqlite3-parser/src/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! SQLite3 syntax lexer and parser -#![warn(missing_docs)] - -pub mod dialect; -// In Lemon, the tokenizer calls the parser. -pub mod lexer; -mod parser; -pub use parser::ast; -pub mod to_sql_string; diff --git a/vendored/sqlite3-parser/src/parser/ast/check.rs b/vendored/sqlite3-parser/src/parser/ast/check.rs deleted file mode 100644 index 2df65bc73..000000000 --- a/vendored/sqlite3-parser/src/parser/ast/check.rs +++ /dev/null @@ -1,358 +0,0 @@ -//! Check for additional syntax error -use crate::ast::*; -use crate::custom_err; -use std::fmt::{Display, Formatter}; - -impl Cmd { - /// Statement accessor - pub fn stmt(&self) -> &Stmt { - match self { - Self::Explain(stmt) => stmt, - Self::ExplainQueryPlan(stmt) => stmt, - Self::Stmt(stmt) => stmt, - } - } - /// Like `sqlite3_column_count` but more limited - pub fn column_count(&self) -> ColumnCount { - match self { - Self::Explain(_) => ColumnCount::Fixed(8), - Self::ExplainQueryPlan(_) => ColumnCount::Fixed(4), - Self::Stmt(stmt) => stmt.column_count(), - } - } - /// Like `sqlite3_stmt_isexplain` - pub fn is_explain(&self) -> bool { - matches!(self, Self::Explain(_) | Self::ExplainQueryPlan(_)) - } - /// Like `sqlite3_stmt_readonly` - pub fn readonly(&self) -> bool { - self.stmt().readonly() - } - /// check for extra rules - pub fn check(&self) -> Result<(), ParserError> { - self.stmt().check() - } -} - -/// Column count -pub enum ColumnCount { - /// With `SELECT *` / PRAGMA - Dynamic, - /// Constant count - Fixed(usize), - /// No column - None, -} - -impl ColumnCount { - fn incr(&mut self) { - if let Self::Fixed(n) = self { - *n += 1; - } - } -} - -impl Stmt { - /// Like `sqlite3_column_count` but more limited - pub fn column_count(&self) -> ColumnCount { - match self { - Self::Delete(delete) => { - let Delete { returning, .. } = &**delete; - match returning { - Some(returning) => column_count(returning), - None => ColumnCount::None, - } - } - Self::Insert(insert) => { - let Insert { returning, .. } = &**insert; - match returning { - Some(returning) => column_count(returning), - None => ColumnCount::None, - } - } - Self::Pragma(..) => ColumnCount::Dynamic, - Self::Select(s) => s.column_count(), - Self::Update(update) => { - let Update { returning, .. } = &**update; - match returning { - Some(returning) => column_count(returning), - None => ColumnCount::None, - } - } - _ => ColumnCount::None, - } - } - - /// Like `sqlite3_stmt_readonly` - pub fn readonly(&self) -> bool { - match self { - Self::Attach { .. } => true, - Self::Begin(..) => true, - Self::Commit(..) => true, - Self::Detach(..) => true, - Self::Pragma(..) => true, // TODO check all - Self::Reindex { .. } => true, - Self::Release(..) => true, - Self::Rollback { .. } => true, - Self::Savepoint(..) => true, - Self::Select(..) => true, - _ => false, - } - } - - /// check for extra rules - pub fn check(&self) -> Result<(), ParserError> { - match self { - Self::AlterTable(alter_table) => { - let (_, body) = &**alter_table; - if let AlterTableBody::AddColumn(cd) = body { - for c in cd { - if let ColumnConstraint::PrimaryKey { .. } = c { - return Err(custom_err!("Cannot add a PRIMARY KEY column")); - } - if let ColumnConstraint::Unique(..) = c { - return Err(custom_err!("Cannot add a UNIQUE column")); - } - } - } - Ok(()) - } - Self::CreateTable { - temporary, - tbl_name, - body, - .. - } => { - if *temporary { - if let Some(ref db_name) = tbl_name.db_name { - if db_name != "TEMP" { - return Err(custom_err!("temporary table name must be unqualified")); - } - } - } - body.check(tbl_name) - } - Self::CreateView { - view_name, - columns: Some(columns), - select, - .. - } => { - // SQLite3 engine renames duplicates: - for (i, c) in columns.iter().enumerate() { - for o in &columns[i + 1..] { - if c.col_name == o.col_name { - return Err(custom_err!("duplicate column name: {}", c.col_name,)); - } - } - } - // SQLite3 engine raises this error later (not while parsing): - match select.column_count() { - ColumnCount::Fixed(n) if n != columns.len() => Err(custom_err!( - "expected {} columns for {} but got {}", - columns.len(), - view_name, - n - )), - _ => Ok(()), - } - } - Self::Delete(delete) => { - let Delete { - order_by, limit, .. - } = &**delete; - if order_by.is_some() && limit.is_none() { - return Err(custom_err!("ORDER BY without LIMIT on DELETE")); - } - Ok(()) - } - Self::Insert(insert) => { - let Insert { columns, body, .. } = &**insert; - if columns.is_none() { - return Ok(()); - } - let columns = columns.as_ref().unwrap(); - match body { - InsertBody::Select(select, ..) => match select.body.select.column_count() { - ColumnCount::Fixed(n) if n != columns.len() => { - Err(custom_err!("{} values for {} columns", n, columns.len())) - } - _ => Ok(()), - }, - InsertBody::DefaultValues => { - Err(custom_err!("0 values for {} columns", columns.len())) - } - } - } - Self::Update(update) => { - let Update { - order_by, limit, .. - } = &**update; - if order_by.is_some() && limit.is_none() { - return Err(custom_err!("ORDER BY without LIMIT on UPDATE")); - } - - Ok(()) - } - _ => Ok(()), - } - } -} - -impl CreateTableBody { - /// check for extra rules - pub fn check(&self, tbl_name: &QualifiedName) -> Result<(), ParserError> { - if let Self::ColumnsAndConstraints { - columns, - constraints: _, - options, - } = self - { - let mut generated_count = 0; - for c in columns.values() { - if c.col_name == "rowid" { - return Err(custom_err!("cannot use reserved word: ROWID")); - } - for cs in &c.constraints { - if let ColumnConstraint::Generated { .. } = cs.constraint { - generated_count += 1; - } - } - } - if generated_count == columns.len() { - return Err(custom_err!("must have at least one non-generated column")); - } - - if options.contains(TableOptions::STRICT) { - for c in columns.values() { - match &c.col_type { - Some(Type { name, .. }) => { - // The datatype must be one of following: INT INTEGER REAL TEXT BLOB ANY - if !(name.eq_ignore_ascii_case("INT") - || name.eq_ignore_ascii_case("INTEGER") - || name.eq_ignore_ascii_case("REAL") - || name.eq_ignore_ascii_case("TEXT") - || name.eq_ignore_ascii_case("BLOB") - || name.eq_ignore_ascii_case("ANY")) - { - return Err(custom_err!( - "unknown datatype for {}.{}: \"{}\"", - tbl_name, - c.col_name, - name - )); - } - } - _ => { - // Every column definition must specify a datatype for that column. The freedom to specify a column without a datatype is removed. - return Err(custom_err!( - "missing datatype for {}.{}", - tbl_name, - c.col_name - )); - } - } - } - } - if options.contains(TableOptions::WITHOUT_ROWID) && !self.has_primary_key() { - return Err(custom_err!("PRIMARY KEY missing on table {}", tbl_name,)); - } - } - Ok(()) - } - - /// explicit primary key constraint ? - pub fn has_primary_key(&self) -> bool { - if let Self::ColumnsAndConstraints { - columns, - constraints, - .. - } = self - { - for col in columns.values() { - for c in col { - if let ColumnConstraint::PrimaryKey { .. } = c { - return true; - } - } - } - if let Some(constraints) = constraints { - for c in constraints { - if let TableConstraint::PrimaryKey { .. } = c.constraint { - return true; - } - } - } - } - false - } -} - -impl<'a> IntoIterator for &'a ColumnDefinition { - type Item = &'a ColumnConstraint; - type IntoIter = std::iter::Map< - std::slice::Iter<'a, NamedColumnConstraint>, - fn(&'a NamedColumnConstraint) -> &'a ColumnConstraint, - >; - - fn into_iter(self) -> Self::IntoIter { - self.constraints.iter().map(|nc| &nc.constraint) - } -} - -impl Select { - /// Like `sqlite3_column_count` but more limited - pub fn column_count(&self) -> ColumnCount { - self.body.select.column_count() - } -} - -impl OneSelect { - /// Like `sqlite3_column_count` but more limited - pub fn column_count(&self) -> ColumnCount { - match self { - Self::Select(select) => { - let SelectInner { columns, .. } = &**select; - column_count(columns) - } - Self::Values(values) => { - assert!(!values.is_empty()); // TODO Validate - ColumnCount::Fixed(values[0].len()) - } - } - } - /// Check all VALUES have the same number of terms - pub fn push(values: &mut Vec>, v: Vec) -> Result<(), ParserError> { - if values[0].len() != v.len() { - return Err(custom_err!("all VALUES must have the same number of terms")); - } - values.push(v); - Ok(()) - } -} - -impl Display for QualifiedName { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - self.to_fmt(f) - } -} - -impl ResultColumn { - fn column_count(&self) -> ColumnCount { - match self { - Self::Expr(..) => ColumnCount::Fixed(1), - _ => ColumnCount::Dynamic, - } - } -} -fn column_count(cols: &[ResultColumn]) -> ColumnCount { - assert!(!cols.is_empty()); - let mut count = ColumnCount::Fixed(0); - for col in cols { - match col.column_count() { - ColumnCount::Fixed(_) => count.incr(), - _ => return ColumnCount::Dynamic, - } - } - count -} diff --git a/vendored/sqlite3-parser/src/parser/ast/fmt.rs b/vendored/sqlite3-parser/src/parser/ast/fmt.rs deleted file mode 100644 index 96013bf35..000000000 --- a/vendored/sqlite3-parser/src/parser/ast/fmt.rs +++ /dev/null @@ -1,2416 +0,0 @@ -//! AST node format -use std::fmt::{self, Display, Formatter, Write}; - -use crate::ast::*; -use crate::dialect::TokenType::*; -use crate::to_sql_string::ToSqlContext; - -struct FmtTokenStream<'a, 'b> { - f: &'a mut Formatter<'b>, - spaced: bool, -} - -impl TokenStream for FmtTokenStream<'_, '_> { - type Error = fmt::Error; - - fn append(&mut self, ty: TokenType, value: Option<&str>) -> fmt::Result { - if !self.spaced { - match ty { - TK_COMMA | TK_SEMI | TK_RP | TK_DOT => {} - _ => { - self.f.write_char(' ')?; - self.spaced = true; - } - }; - } - if ty == TK_BLOB { - self.f.write_char('X')?; - self.f.write_char('\'')?; - if let Some(str) = value { - self.f.write_str(str)?; - } - return self.f.write_char('\''); - } else if let Some(str) = ty.as_str() { - self.f.write_str(str)?; - self.spaced = ty == TK_LP || ty == TK_DOT; // str should not be whitespace - } - if let Some(str) = value { - // trick for pretty-print - self.spaced = str.bytes().all(|b| b.is_ascii_whitespace()); - /*if !self.spaced { - self.f.write_char(' ')?; - }*/ - self.f.write_str(str) - } else { - Ok(()) - } - } -} - -struct WriteTokenStream<'a, T: fmt::Write> { - write: &'a mut T, - spaced: bool, -} - -impl TokenStream for WriteTokenStream<'_, T> { - type Error = fmt::Error; - - fn append(&mut self, ty: TokenType, value: Option<&str>) -> fmt::Result { - if !self.spaced { - match ty { - TK_COMMA | TK_SEMI | TK_RP | TK_DOT => {} - _ => { - self.write.write_char(' ')?; - self.spaced = true; - } - }; - } - if ty == TK_BLOB { - self.write.write_char('X')?; - self.write.write_char('\'')?; - if let Some(str) = value { - self.write.write_str(str)?; - } - return self.write.write_char('\''); - } else if let Some(str) = ty.as_str() { - self.write.write_str(str)?; - self.spaced = ty == TK_LP || ty == TK_DOT; // str should not be whitespace - } - if let Some(str) = value { - // trick for pretty-print - self.spaced = str.bytes().all(|b| b.is_ascii_whitespace()); - self.write.write_str(str) - } else { - Ok(()) - } - } -} - -struct BlankContext; - -impl ToSqlContext for BlankContext { - fn get_column_name(&self, _table_id: crate::ast::TableInternalId, _col_idx: usize) -> String { - "".to_string() - } - - fn get_table_name(&self, _id: crate::ast::TableInternalId) -> &str { - "" - } -} - -/// Stream of token -pub trait TokenStream { - /// Potential error raised - type Error; - /// Push token to this stream - fn append(&mut self, ty: TokenType, value: Option<&str>) -> Result<(), Self::Error>; - /// Interspace iterator with commas - fn comma(&mut self, items: I, context: &C) -> Result<(), Self::Error> - where - I: IntoIterator, - I::Item: ToTokens, - { - let iter = items.into_iter(); - for (i, item) in iter.enumerate() { - if i != 0 { - self.append(TK_COMMA, None)?; - } - item.to_tokens_with_context(self, context)?; - } - Ok(()) - } -} - -/// Generate token(s) from AST node -pub trait ToTokens { - /// Send token(s) to the specified stream with context - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error>; - - /// Send token(s) to the specified stream - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { - self.to_tokens_with_context(s, &BlankContext) - } - - /// Format AST node - fn to_fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - self.to_fmt_with_context(f, &BlankContext) - } - - /// Format AST node with context - fn to_fmt_with_context( - &self, - f: &mut Formatter<'_>, - context: &C, - ) -> fmt::Result { - let mut s = FmtTokenStream { f, spaced: true }; - self.to_tokens_with_context(&mut s, context) - } - - /// Format AST node to string - fn format(&self) -> Result { - self.format_with_context(&BlankContext) - } - - /// Format AST node to string with context - fn format_with_context(&self, context: &C) -> Result { - let mut s = String::new(); - let mut w = WriteTokenStream { - write: &mut s, - spaced: true, - }; - - self.to_tokens_with_context(&mut w, context)?; - - Ok(s) - } -} - -impl ToTokens for &T { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - ToTokens::to_tokens_with_context(&**self, s, context) - } -} - -impl ToTokens for String { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - s.append(TK_ANY, Some(self.as_ref())) - } -} - -impl ToTokens for Cmd { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::Explain(stmt) => { - s.append(TK_EXPLAIN, None)?; - stmt.to_tokens_with_context(s, context)?; - } - Self::ExplainQueryPlan(stmt) => { - s.append(TK_EXPLAIN, None)?; - s.append(TK_QUERY, None)?; - s.append(TK_PLAN, None)?; - stmt.to_tokens_with_context(s, context)?; - } - Self::Stmt(stmt) => { - stmt.to_tokens_with_context(s, context)?; - } - } - s.append(TK_SEMI, None) - } -} - -impl Display for Cmd { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - self.to_fmt(f) - } -} - -impl ToTokens for Stmt { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::AlterTable(alter_table) => { - let (tbl_name, body) = &**alter_table; - s.append(TK_ALTER, None)?; - s.append(TK_TABLE, None)?; - tbl_name.to_tokens_with_context(s, context)?; - body.to_tokens_with_context(s, context) - } - Self::Analyze(obj_name) => { - s.append(TK_ANALYZE, None)?; - if let Some(obj_name) = obj_name { - obj_name.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Attach { expr, db_name, key } => { - s.append(TK_ATTACH, None)?; - expr.to_tokens_with_context(s, context)?; - s.append(TK_AS, None)?; - db_name.to_tokens_with_context(s, context)?; - if let Some(key) = key { - s.append(TK_KEY, None)?; - key.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Begin(tx_type, tx_name) => { - s.append(TK_BEGIN, None)?; - if let Some(tx_type) = tx_type { - tx_type.to_tokens_with_context(s, context)?; - } - if let Some(tx_name) = tx_name { - s.append(TK_TRANSACTION, None)?; - tx_name.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Commit(tx_name) => { - s.append(TK_COMMIT, None)?; - if let Some(tx_name) = tx_name { - s.append(TK_TRANSACTION, None)?; - tx_name.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::CreateIndex { - unique, - if_not_exists, - idx_name, - tbl_name, - columns, - where_clause, - } => { - s.append(TK_CREATE, None)?; - if *unique { - s.append(TK_UNIQUE, None)?; - } - s.append(TK_INDEX, None)?; - if *if_not_exists { - s.append(TK_IF, None)?; - s.append(TK_NOT, None)?; - s.append(TK_EXISTS, None)?; - } - idx_name.to_tokens_with_context(s, context)?; - s.append(TK_ON, None)?; - tbl_name.to_tokens_with_context(s, context)?; - s.append(TK_LP, None)?; - comma(columns, s, context)?; - s.append(TK_RP, None)?; - if let Some(where_clause) = where_clause { - s.append(TK_WHERE, None)?; - where_clause.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::CreateTable { - temporary, - if_not_exists, - tbl_name, - body, - } => { - s.append(TK_CREATE, None)?; - if *temporary { - s.append(TK_TEMP, None)?; - } - s.append(TK_TABLE, None)?; - if *if_not_exists { - s.append(TK_IF, None)?; - s.append(TK_NOT, None)?; - s.append(TK_EXISTS, None)?; - } - tbl_name.to_tokens_with_context(s, context)?; - body.to_tokens_with_context(s, context) - } - Self::CreateTrigger(trigger) => { - let CreateTrigger { - temporary, - if_not_exists, - trigger_name, - time, - event, - tbl_name, - for_each_row, - when_clause, - commands, - } = &**trigger; - s.append(TK_CREATE, None)?; - if *temporary { - s.append(TK_TEMP, None)?; - } - s.append(TK_TRIGGER, None)?; - if *if_not_exists { - s.append(TK_IF, None)?; - s.append(TK_NOT, None)?; - s.append(TK_EXISTS, None)?; - } - trigger_name.to_tokens_with_context(s, context)?; - if let Some(time) = time { - time.to_tokens_with_context(s, context)?; - } - event.to_tokens_with_context(s, context)?; - s.append(TK_ON, None)?; - tbl_name.to_tokens_with_context(s, context)?; - if *for_each_row { - s.append(TK_FOR, None)?; - s.append(TK_EACH, None)?; - s.append(TK_ROW, None)?; - } - if let Some(when_clause) = when_clause { - s.append(TK_WHEN, None)?; - when_clause.to_tokens_with_context(s, context)?; - } - s.append(TK_BEGIN, Some("\n"))?; - for command in commands { - command.to_tokens_with_context(s, context)?; - s.append(TK_SEMI, Some("\n"))?; - } - s.append(TK_END, None) - } - Self::CreateView { - temporary, - if_not_exists, - view_name, - columns, - select, - } => { - s.append(TK_CREATE, None)?; - if *temporary { - s.append(TK_TEMP, None)?; - } - s.append(TK_VIEW, None)?; - if *if_not_exists { - s.append(TK_IF, None)?; - s.append(TK_NOT, None)?; - s.append(TK_EXISTS, None)?; - } - view_name.to_tokens_with_context(s, context)?; - if let Some(columns) = columns { - s.append(TK_LP, None)?; - comma(columns, s, context)?; - s.append(TK_RP, None)?; - } - s.append(TK_AS, None)?; - select.to_tokens_with_context(s, context) - } - Self::CreateMaterializedView { - if_not_exists, - view_name, - columns, - select, - } => { - s.append(TK_CREATE, None)?; - s.append(TK_MATERIALIZED, None)?; - s.append(TK_VIEW, None)?; - if *if_not_exists { - s.append(TK_IF, None)?; - s.append(TK_NOT, None)?; - s.append(TK_EXISTS, None)?; - } - view_name.to_tokens_with_context(s, context)?; - if let Some(columns) = columns { - s.append(TK_LP, None)?; - comma(columns, s, context)?; - s.append(TK_RP, None)?; - } - s.append(TK_AS, None)?; - select.to_tokens_with_context(s, context) - } - Self::CreateVirtualTable(create_virtual_table) => { - let CreateVirtualTable { - if_not_exists, - tbl_name, - module_name, - args, - } = &**create_virtual_table; - s.append(TK_CREATE, None)?; - s.append(TK_VIRTUAL, None)?; - s.append(TK_TABLE, None)?; - if *if_not_exists { - s.append(TK_IF, None)?; - s.append(TK_NOT, None)?; - s.append(TK_EXISTS, None)?; - } - tbl_name.to_tokens_with_context(s, context)?; - s.append(TK_USING, None)?; - module_name.to_tokens_with_context(s, context)?; - s.append(TK_LP, None)?; - if let Some(args) = args { - comma(args, s, context)?; - } - s.append(TK_RP, None) - } - Self::Delete(delete) => { - let Delete { - with, - tbl_name, - indexed, - where_clause, - returning, - order_by, - limit, - } = &**delete; - if let Some(with) = with { - with.to_tokens_with_context(s, context)?; - } - s.append(TK_DELETE, None)?; - s.append(TK_FROM, None)?; - tbl_name.to_tokens_with_context(s, context)?; - if let Some(indexed) = indexed { - indexed.to_tokens_with_context(s, context)?; - } - if let Some(where_clause) = where_clause { - s.append(TK_WHERE, None)?; - where_clause.to_tokens_with_context(s, context)?; - } - if let Some(returning) = returning { - s.append(TK_RETURNING, None)?; - comma(returning, s, context)?; - } - if let Some(order_by) = order_by { - s.append(TK_ORDER, None)?; - s.append(TK_BY, None)?; - comma(order_by, s, context)?; - } - if let Some(limit) = limit { - limit.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Detach(expr) => { - s.append(TK_DETACH, None)?; - expr.to_tokens_with_context(s, context) - } - Self::DropIndex { - if_exists, - idx_name, - } => { - s.append(TK_DROP, None)?; - s.append(TK_INDEX, None)?; - if *if_exists { - s.append(TK_IF, None)?; - s.append(TK_EXISTS, None)?; - } - idx_name.to_tokens_with_context(s, context) - } - Self::DropTable { - if_exists, - tbl_name, - } => { - s.append(TK_DROP, None)?; - s.append(TK_TABLE, None)?; - if *if_exists { - s.append(TK_IF, None)?; - s.append(TK_EXISTS, None)?; - } - tbl_name.to_tokens_with_context(s, context) - } - Self::DropTrigger { - if_exists, - trigger_name, - } => { - s.append(TK_DROP, None)?; - s.append(TK_TRIGGER, None)?; - if *if_exists { - s.append(TK_IF, None)?; - s.append(TK_EXISTS, None)?; - } - trigger_name.to_tokens_with_context(s, context) - } - Self::DropView { - if_exists, - view_name, - } => { - s.append(TK_DROP, None)?; - s.append(TK_VIEW, None)?; - if *if_exists { - s.append(TK_IF, None)?; - s.append(TK_EXISTS, None)?; - } - view_name.to_tokens_with_context(s, context) - } - Self::Insert(insert) => { - let Insert { - with, - or_conflict, - tbl_name, - columns, - body, - returning, - } = &**insert; - if let Some(with) = with { - with.to_tokens_with_context(s, context)?; - } - if let Some(ResolveType::Replace) = or_conflict { - s.append(TK_REPLACE, None)?; - } else { - s.append(TK_INSERT, None)?; - if let Some(or_conflict) = or_conflict { - s.append(TK_OR, None)?; - or_conflict.to_tokens_with_context(s, context)?; - } - } - s.append(TK_INTO, None)?; - tbl_name.to_tokens_with_context(s, context)?; - if let Some(columns) = columns { - s.append(TK_LP, None)?; - comma(columns.deref(), s, context)?; - s.append(TK_RP, None)?; - } - body.to_tokens_with_context(s, context)?; - if let Some(returning) = returning { - s.append(TK_RETURNING, None)?; - comma(returning, s, context)?; - } - Ok(()) - } - Self::Pragma(name, value) => { - s.append(TK_PRAGMA, None)?; - name.to_tokens_with_context(s, context)?; - if let Some(value) = value { - value.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Reindex { obj_name } => { - s.append(TK_REINDEX, None)?; - if let Some(obj_name) = obj_name { - obj_name.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Release(name) => { - s.append(TK_RELEASE, None)?; - name.to_tokens_with_context(s, context) - } - Self::Rollback { - tx_name, - savepoint_name, - } => { - s.append(TK_ROLLBACK, None)?; - if let Some(tx_name) = tx_name { - s.append(TK_TRANSACTION, None)?; - tx_name.to_tokens_with_context(s, context)?; - } - if let Some(savepoint_name) = savepoint_name { - s.append(TK_TO, None)?; - savepoint_name.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Savepoint(name) => { - s.append(TK_SAVEPOINT, None)?; - name.to_tokens_with_context(s, context) - } - Self::Select(select) => select.to_tokens_with_context(s, context), - Self::Update(update) => { - let Update { - with, - or_conflict, - tbl_name, - indexed, - sets, - from, - where_clause, - returning, - order_by, - limit, - } = &**update; - if let Some(with) = with { - with.to_tokens_with_context(s, context)?; - } - s.append(TK_UPDATE, None)?; - if let Some(or_conflict) = or_conflict { - s.append(TK_OR, None)?; - or_conflict.to_tokens_with_context(s, context)?; - } - tbl_name.to_tokens_with_context(s, context)?; - if let Some(indexed) = indexed { - indexed.to_tokens_with_context(s, context)?; - } - s.append(TK_SET, None)?; - comma(sets, s, context)?; - if let Some(from) = from { - s.append(TK_FROM, None)?; - from.to_tokens_with_context(s, context)?; - } - if let Some(where_clause) = where_clause { - s.append(TK_WHERE, None)?; - where_clause.to_tokens_with_context(s, context)?; - } - if let Some(returning) = returning { - s.append(TK_RETURNING, None)?; - comma(returning, s, context)?; - } - if let Some(order_by) = order_by { - s.append(TK_ORDER, None)?; - s.append(TK_BY, None)?; - comma(order_by, s, context)?; - } - if let Some(limit) = limit { - limit.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Vacuum(name, expr) => { - s.append(TK_VACUUM, None)?; - if let Some(ref name) = name { - name.to_tokens_with_context(s, context)?; - } - if let Some(ref expr) = expr { - s.append(TK_INTO, None)?; - expr.to_tokens_with_context(s, context)?; - } - Ok(()) - } - } - } -} - -impl ToTokens for Expr { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::Between { - lhs, - not, - start, - end, - } => { - lhs.to_tokens_with_context(s, context)?; - if *not { - s.append(TK_NOT, None)?; - } - s.append(TK_BETWEEN, None)?; - start.to_tokens_with_context(s, context)?; - s.append(TK_AND, None)?; - end.to_tokens_with_context(s, context) - } - Self::Binary(lhs, op, rhs) => { - lhs.to_tokens_with_context(s, context)?; - op.to_tokens_with_context(s, context)?; - rhs.to_tokens_with_context(s, context) - } - Self::Case { - base, - when_then_pairs, - else_expr, - } => { - s.append(TK_CASE, None)?; - if let Some(ref base) = base { - base.to_tokens_with_context(s, context)?; - } - for (when, then) in when_then_pairs { - s.append(TK_WHEN, None)?; - when.to_tokens_with_context(s, context)?; - s.append(TK_THEN, None)?; - then.to_tokens_with_context(s, context)?; - } - if let Some(ref else_expr) = else_expr { - s.append(TK_ELSE, None)?; - else_expr.to_tokens_with_context(s, context)?; - } - s.append(TK_END, None) - } - Self::Cast { expr, type_name } => { - s.append(TK_CAST, None)?; - s.append(TK_LP, None)?; - expr.to_tokens_with_context(s, context)?; - s.append(TK_AS, None)?; - if let Some(ref type_name) = type_name { - type_name.to_tokens_with_context(s, context)?; - } - s.append(TK_RP, None) - } - Self::Collate(expr, collation) => { - expr.to_tokens_with_context(s, context)?; - s.append(TK_COLLATE, None)?; - double_quote(collation, s) - } - Self::DoublyQualified(db_name, tbl_name, col_name) => { - db_name.to_tokens_with_context(s, context)?; - s.append(TK_DOT, None)?; - tbl_name.to_tokens_with_context(s, context)?; - s.append(TK_DOT, None)?; - col_name.to_tokens_with_context(s, context) - } - Self::Exists(subquery) => { - s.append(TK_EXISTS, None)?; - s.append(TK_LP, None)?; - subquery.to_tokens_with_context(s, context)?; - s.append(TK_RP, None) - } - Self::FunctionCall { - name, - distinctness, - args, - order_by, - filter_over, - } => { - name.to_tokens_with_context(s, context)?; - s.append(TK_LP, None)?; - if let Some(distinctness) = distinctness { - distinctness.to_tokens_with_context(s, context)?; - } - if let Some(args) = args { - comma(args, s, context)?; - } - if let Some(order_by) = order_by { - s.append(TK_ORDER, None)?; - s.append(TK_BY, None)?; - comma(order_by, s, context)?; - } - s.append(TK_RP, None)?; - if let Some(filter_over) = filter_over { - filter_over.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::FunctionCallStar { name, filter_over } => { - name.to_tokens_with_context(s, context)?; - s.append(TK_LP, None)?; - s.append(TK_STAR, None)?; - s.append(TK_RP, None)?; - if let Some(filter_over) = filter_over { - filter_over.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Id(id) => id.to_tokens_with_context(s, context), - Self::Column { table, column, .. } => { - s.append(TK_ID, Some(context.get_table_name(*table)))?; - s.append(TK_DOT, None)?; - s.append(TK_ID, Some(&context.get_column_name(*table, *column))) - } - Self::InList { lhs, not, rhs } => { - lhs.to_tokens_with_context(s, context)?; - if *not { - s.append(TK_NOT, None)?; - } - s.append(TK_IN, None)?; - s.append(TK_LP, None)?; - if let Some(rhs) = rhs { - comma(rhs, s, context)?; - } - s.append(TK_RP, None) - } - Self::InSelect { lhs, not, rhs } => { - lhs.to_tokens_with_context(s, context)?; - if *not { - s.append(TK_NOT, None)?; - } - s.append(TK_IN, None)?; - s.append(TK_LP, None)?; - rhs.to_tokens_with_context(s, context)?; - s.append(TK_RP, None) - } - Self::InTable { - lhs, - not, - rhs, - args, - } => { - lhs.to_tokens_with_context(s, context)?; - if *not { - s.append(TK_NOT, None)?; - } - s.append(TK_IN, None)?; - rhs.to_tokens_with_context(s, context)?; - if let Some(args) = args { - s.append(TK_LP, None)?; - comma(args, s, context)?; - s.append(TK_RP, None)?; - } - Ok(()) - } - Self::IsNull(sub_expr) => { - sub_expr.to_tokens_with_context(s, context)?; - s.append(TK_ISNULL, None) - } - Self::Like { - lhs, - not, - op, - rhs, - escape, - } => { - lhs.to_tokens_with_context(s, context)?; - if *not { - s.append(TK_NOT, None)?; - } - op.to_tokens_with_context(s, context)?; - rhs.to_tokens_with_context(s, context)?; - if let Some(escape) = escape { - s.append(TK_ESCAPE, None)?; - escape.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Literal(lit) => lit.to_tokens_with_context(s, context), - Self::Name(name) => name.to_tokens_with_context(s, context), - Self::NotNull(sub_expr) => { - sub_expr.to_tokens_with_context(s, context)?; - s.append(TK_NOTNULL, None) - } - Self::Parenthesized(exprs) => { - s.append(TK_LP, None)?; - comma(exprs, s, context)?; - s.append(TK_RP, None) - } - Self::Qualified(qualifier, qualified) => { - qualifier.to_tokens_with_context(s, context)?; - s.append(TK_DOT, None)?; - qualified.to_tokens_with_context(s, context) - } - Self::Raise(rt, err) => { - s.append(TK_RAISE, None)?; - s.append(TK_LP, None)?; - rt.to_tokens_with_context(s, context)?; - if let Some(err) = err { - s.append(TK_COMMA, None)?; - err.to_tokens_with_context(s, context)?; - } - s.append(TK_RP, None) - } - Self::RowId { .. } => Ok(()), - Self::Subquery(query) => { - s.append(TK_LP, None)?; - query.to_tokens_with_context(s, context)?; - s.append(TK_RP, None) - } - Self::Unary(op, sub_expr) => { - op.to_tokens_with_context(s, context)?; - sub_expr.to_tokens_with_context(s, context) - } - Self::Variable(var) => match var.chars().next() { - Some(c) if c == '$' || c == '@' || c == '#' || c == ':' => { - s.append(TK_VARIABLE, Some(var)) - } - Some(_) => s.append(TK_VARIABLE, Some(&("?".to_owned() + var))), - None => s.append(TK_VARIABLE, Some("?")), - }, - Self::Register(reg) => { - // This is for internal use only, not part of SQL syntax - // Use a special notation that won't conflict with SQL - s.append(TK_VARIABLE, Some(&format!("$r{reg}"))) - } - } - } -} - -impl Display for Expr { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - self.to_fmt(f) - } -} - -impl ToTokens for Literal { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - match self { - Self::Numeric(ref num) => s.append(TK_FLOAT, Some(num)), // TODO Validate TK_FLOAT - Self::String(ref str) => s.append(TK_STRING, Some(str)), - Self::Blob(ref blob) => s.append(TK_BLOB, Some(blob)), - Self::Keyword(ref str) => s.append(TK_ID, Some(str)), // TODO Validate TK_ID - Self::Null => s.append(TK_NULL, None), - Self::CurrentDate => s.append(TK_CTIME_KW, Some("CURRENT_DATE")), - Self::CurrentTime => s.append(TK_CTIME_KW, Some("CURRENT_TIME")), - Self::CurrentTimestamp => s.append(TK_CTIME_KW, Some("CURRENT_TIMESTAMP")), - } - } -} - -impl ToTokens for LikeOperator { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - s.append( - TK_LIKE_KW, - Some(match self { - Self::Glob => "GLOB", - Self::Like => "LIKE", - Self::Match => "MATCH", - Self::Regexp => "REGEXP", - }), - ) - } -} - -impl ToTokens for Operator { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - match self { - Self::Add => s.append(TK_PLUS, None), - Self::And => s.append(TK_AND, None), - Self::ArrowRight => s.append(TK_PTR, Some("->")), - Self::ArrowRightShift => s.append(TK_PTR, Some("->>")), - Self::BitwiseAnd => s.append(TK_BITAND, None), - Self::BitwiseOr => s.append(TK_BITOR, None), - Self::BitwiseNot => s.append(TK_BITNOT, None), - Self::Concat => s.append(TK_CONCAT, None), - Self::Equals => s.append(TK_EQ, None), - Self::Divide => s.append(TK_SLASH, None), - Self::Greater => s.append(TK_GT, None), - Self::GreaterEquals => s.append(TK_GE, None), - Self::Is => s.append(TK_IS, None), - Self::IsNot => { - s.append(TK_IS, None)?; - s.append(TK_NOT, None) - } - Self::LeftShift => s.append(TK_LSHIFT, None), - Self::Less => s.append(TK_LT, None), - Self::LessEquals => s.append(TK_LE, None), - Self::Modulus => s.append(TK_REM, None), - Self::Multiply => s.append(TK_STAR, None), - Self::NotEquals => s.append(TK_NE, None), - Self::Or => s.append(TK_OR, None), - Self::RightShift => s.append(TK_RSHIFT, None), - Self::Subtract => s.append(TK_MINUS, None), - } - } -} - -impl ToTokens for UnaryOperator { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - s.append( - match self { - Self::BitwiseNot => TK_BITNOT, - Self::Negative => TK_MINUS, - Self::Not => TK_NOT, - Self::Positive => TK_PLUS, - }, - None, - ) - } -} - -impl ToTokens for Select { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - if let Some(ref with) = self.with { - with.to_tokens_with_context(s, context)?; - } - self.body.to_tokens_with_context(s, context)?; - if let Some(ref order_by) = self.order_by { - s.append(TK_ORDER, None)?; - s.append(TK_BY, None)?; - comma(order_by, s, context)?; - } - if let Some(ref limit) = self.limit { - limit.to_tokens_with_context(s, context)?; - } - Ok(()) - } -} - -impl ToTokens for SelectBody { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - self.select.to_tokens_with_context(s, context)?; - if let Some(ref compounds) = self.compounds { - for compound in compounds { - compound.to_tokens_with_context(s, context)?; - } - } - Ok(()) - } -} - -impl ToTokens for CompoundSelect { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - self.operator.to_tokens_with_context(s, context)?; - self.select.to_tokens_with_context(s, context) - } -} - -impl ToTokens for CompoundOperator { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - match self { - Self::Union => s.append(TK_UNION, None), - Self::UnionAll => { - s.append(TK_UNION, None)?; - s.append(TK_ALL, None) - } - Self::Except => s.append(TK_EXCEPT, None), - Self::Intersect => s.append(TK_INTERSECT, None), - } - } -} - -impl Display for CompoundOperator { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - self.to_fmt(f) - } -} - -impl ToTokens for OneSelect { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::Select(select) => { - let SelectInner { - distinctness, - columns, - from, - where_clause, - group_by, - window_clause, - } = &**select; - s.append(TK_SELECT, None)?; - if let Some(ref distinctness) = distinctness { - distinctness.to_tokens_with_context(s, context)?; - } - comma(columns, s, context)?; - if let Some(ref from) = from { - s.append(TK_FROM, None)?; - from.to_tokens_with_context(s, context)?; - } - if let Some(ref where_clause) = where_clause { - s.append(TK_WHERE, None)?; - where_clause.to_tokens_with_context(s, context)?; - } - if let Some(ref group_by) = group_by { - group_by.to_tokens_with_context(s, context)?; - } - if let Some(ref window_clause) = window_clause { - s.append(TK_WINDOW, None)?; - comma(window_clause, s, context)?; - } - Ok(()) - } - Self::Values(values) => { - for (i, vals) in values.iter().enumerate() { - if i == 0 { - s.append(TK_VALUES, None)?; - } else { - s.append(TK_COMMA, None)?; - } - s.append(TK_LP, None)?; - comma(vals, s, context)?; - s.append(TK_RP, None)?; - } - Ok(()) - } - } - } -} - -impl ToTokens for FromClause { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - self.select - .as_ref() - .unwrap() - .to_tokens_with_context(s, context)?; - if let Some(ref joins) = self.joins { - for join in joins { - join.to_tokens_with_context(s, context)?; - } - } - Ok(()) - } -} - -impl ToTokens for Distinctness { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - s.append( - match self { - Self::Distinct => TK_DISTINCT, - Self::All => TK_ALL, - }, - None, - ) - } -} - -impl ToTokens for ResultColumn { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::Expr(expr, alias) => { - expr.to_tokens_with_context(s, context)?; - if let Some(alias) = alias { - alias.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Star => s.append(TK_STAR, None), - Self::TableStar(tbl_name) => { - tbl_name.to_tokens_with_context(s, context)?; - s.append(TK_DOT, None)?; - s.append(TK_STAR, None) - } - } - } -} - -impl ToTokens for As { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::As(ref name) => { - s.append(TK_AS, None)?; - name.to_tokens_with_context(s, context) - } - Self::Elided(ref name) => name.to_tokens_with_context(s, context), - } - } -} - -impl ToTokens for JoinedSelectTable { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - self.operator.to_tokens_with_context(s, context)?; - self.table.to_tokens_with_context(s, context)?; - if let Some(ref constraint) = self.constraint { - constraint.to_tokens_with_context(s, context)?; - } - Ok(()) - } -} - -impl ToTokens for SelectTable { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::Table(name, alias, indexed) => { - name.to_tokens_with_context(s, context)?; - if let Some(alias) = alias { - alias.to_tokens_with_context(s, context)?; - } - if let Some(indexed) = indexed { - indexed.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::TableCall(name, exprs, alias) => { - name.to_tokens_with_context(s, context)?; - s.append(TK_LP, None)?; - if let Some(exprs) = exprs { - comma(exprs, s, context)?; - } - s.append(TK_RP, None)?; - if let Some(alias) = alias { - alias.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Select(select, alias) => { - s.append(TK_LP, None)?; - select.to_tokens_with_context(s, context)?; - s.append(TK_RP, None)?; - if let Some(alias) = alias { - alias.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Sub(from, alias) => { - s.append(TK_LP, None)?; - from.to_tokens_with_context(s, context)?; - s.append(TK_RP, None)?; - if let Some(alias) = alias { - alias.to_tokens_with_context(s, context)?; - } - Ok(()) - } - } - } -} - -impl ToTokens for JoinOperator { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::Comma => s.append(TK_COMMA, None), - Self::TypedJoin(join_type) => { - if let Some(ref join_type) = join_type { - join_type.to_tokens_with_context(s, context)?; - } - s.append(TK_JOIN, None) - } - } - } -} - -impl ToTokens for JoinType { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - if self.contains(Self::NATURAL) { - s.append(TK_JOIN_KW, Some("NATURAL"))?; - } - if self.contains(Self::INNER) { - if self.contains(Self::CROSS) { - s.append(TK_JOIN_KW, Some("CROSS"))?; - } - s.append(TK_JOIN_KW, Some("INNER"))?; - } else { - if self.contains(Self::LEFT) { - if self.contains(Self::RIGHT) { - s.append(TK_JOIN_KW, Some("FULL"))?; - } else { - s.append(TK_JOIN_KW, Some("LEFT"))?; - } - } else if self.contains(Self::RIGHT) { - s.append(TK_JOIN_KW, Some("RIGHT"))?; - } - if self.contains(Self::OUTER) { - s.append(TK_JOIN_KW, Some("OUTER"))?; - } - } - Ok(()) - } -} - -impl ToTokens for JoinConstraint { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::On(expr) => { - s.append(TK_ON, None)?; - expr.to_tokens_with_context(s, context) - } - Self::Using(col_names) => { - s.append(TK_USING, None)?; - s.append(TK_LP, None)?; - comma(col_names.deref(), s, context)?; - s.append(TK_RP, None) - } - } - } -} - -impl ToTokens for GroupBy { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - s.append(TK_GROUP, None)?; - s.append(TK_BY, None)?; - comma(&self.exprs, s, context)?; - if let Some(ref having) = self.having { - s.append(TK_HAVING, None)?; - having.to_tokens_with_context(s, context)?; - } - Ok(()) - } -} - -impl ToTokens for Name { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - double_quote(self.as_str(), s) - } -} - -impl Display for Name { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - self.to_fmt(f) - } -} - -impl ToTokens for QualifiedName { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - if let Some(ref db_name) = self.db_name { - db_name.to_tokens_with_context(s, context)?; - s.append(TK_DOT, None)?; - } - self.name.to_tokens_with_context(s, context)?; - if let Some(ref alias) = self.alias { - s.append(TK_AS, None)?; - alias.to_tokens_with_context(s, context)?; - } - Ok(()) - } -} - -impl ToTokens for AlterTableBody { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::RenameTo(name) => { - s.append(TK_RENAME, None)?; - s.append(TK_TO, None)?; - name.to_tokens_with_context(s, context) - } - Self::AddColumn(def) => { - s.append(TK_ADD, None)?; - s.append(TK_COLUMNKW, None)?; - def.to_tokens_with_context(s, context) - } - Self::RenameColumn { old, new } => { - s.append(TK_RENAME, None)?; - s.append(TK_COLUMNKW, None)?; - old.to_tokens_with_context(s, context)?; - s.append(TK_TO, None)?; - new.to_tokens_with_context(s, context) - } - Self::DropColumn(name) => { - s.append(TK_DROP, None)?; - s.append(TK_COLUMNKW, None)?; - name.to_tokens_with_context(s, context) - } - } - } -} - -impl ToTokens for CreateTableBody { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::ColumnsAndConstraints { - columns, - constraints, - options, - } => { - s.append(TK_LP, None)?; - comma(columns.values(), s, context)?; - if let Some(constraints) = constraints { - s.append(TK_COMMA, None)?; - comma(constraints, s, context)?; - } - s.append(TK_RP, None)?; - if options.contains(TableOptions::WITHOUT_ROWID) { - s.append(TK_WITHOUT, None)?; - s.append(TK_ID, Some("ROWID"))?; - } - if options.contains(TableOptions::STRICT) { - s.append(TK_ID, Some("STRICT"))?; - } - Ok(()) - } - Self::AsSelect(select) => { - s.append(TK_AS, None)?; - select.to_tokens_with_context(s, context) - } - } - } -} - -impl ToTokens for ColumnDefinition { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - self.col_name.to_tokens_with_context(s, context)?; - if let Some(ref col_type) = self.col_type { - col_type.to_tokens_with_context(s, context)?; - } - for constraint in &self.constraints { - constraint.to_tokens_with_context(s, context)?; - } - Ok(()) - } -} - -impl ToTokens for NamedColumnConstraint { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - if let Some(ref name) = self.name { - s.append(TK_CONSTRAINT, None)?; - name.to_tokens_with_context(s, context)?; - } - self.constraint.to_tokens_with_context(s, context) - } -} - -impl ToTokens for ColumnConstraint { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::PrimaryKey { - order, - conflict_clause, - auto_increment, - } => { - s.append(TK_PRIMARY, None)?; - s.append(TK_KEY, None)?; - if let Some(order) = order { - order.to_tokens_with_context(s, context)?; - } - if let Some(conflict_clause) = conflict_clause { - s.append(TK_ON, None)?; - s.append(TK_CONFLICT, None)?; - conflict_clause.to_tokens_with_context(s, context)?; - } - if *auto_increment { - s.append(TK_AUTOINCR, None)?; - } - Ok(()) - } - Self::NotNull { - nullable, - conflict_clause, - } => { - if !nullable { - s.append(TK_NOT, None)?; - } - s.append(TK_NULL, None)?; - if let Some(conflict_clause) = conflict_clause { - s.append(TK_ON, None)?; - s.append(TK_CONFLICT, None)?; - conflict_clause.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Unique(conflict_clause) => { - s.append(TK_UNIQUE, None)?; - if let Some(conflict_clause) = conflict_clause { - s.append(TK_ON, None)?; - s.append(TK_CONFLICT, None)?; - conflict_clause.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Check(expr) => { - s.append(TK_CHECK, None)?; - s.append(TK_LP, None)?; - expr.to_tokens_with_context(s, context)?; - s.append(TK_RP, None) - } - Self::Default(expr) => { - s.append(TK_DEFAULT, None)?; - expr.to_tokens_with_context(s, context) - } - Self::Defer(deref_clause) => deref_clause.to_tokens_with_context(s, context), - Self::Collate { collation_name } => { - s.append(TK_COLLATE, None)?; - collation_name.to_tokens_with_context(s, context) - } - Self::ForeignKey { - clause, - deref_clause, - } => { - s.append(TK_REFERENCES, None)?; - clause.to_tokens_with_context(s, context)?; - if let Some(deref_clause) = deref_clause { - deref_clause.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Generated { expr, typ } => { - s.append(TK_AS, None)?; - s.append(TK_LP, None)?; - expr.to_tokens_with_context(s, context)?; - s.append(TK_RP, None)?; - if let Some(typ) = typ { - typ.to_tokens_with_context(s, context)?; - } - Ok(()) - } - } - } -} - -impl ToTokens for NamedTableConstraint { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - if let Some(ref name) = self.name { - s.append(TK_CONSTRAINT, None)?; - name.to_tokens_with_context(s, context)?; - } - self.constraint.to_tokens_with_context(s, context) - } -} - -impl ToTokens for TableConstraint { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::PrimaryKey { - columns, - auto_increment, - conflict_clause, - } => { - s.append(TK_PRIMARY, None)?; - s.append(TK_KEY, None)?; - s.append(TK_LP, None)?; - comma(columns, s, context)?; - if *auto_increment { - s.append(TK_AUTOINCR, None)?; - } - s.append(TK_RP, None)?; - if let Some(conflict_clause) = conflict_clause { - s.append(TK_ON, None)?; - s.append(TK_CONFLICT, None)?; - conflict_clause.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Unique { - columns, - conflict_clause, - } => { - s.append(TK_UNIQUE, None)?; - s.append(TK_LP, None)?; - comma(columns, s, context)?; - s.append(TK_RP, None)?; - if let Some(conflict_clause) = conflict_clause { - s.append(TK_ON, None)?; - s.append(TK_CONFLICT, None)?; - conflict_clause.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Check(expr) => { - s.append(TK_CHECK, None)?; - s.append(TK_LP, None)?; - expr.to_tokens_with_context(s, context)?; - s.append(TK_RP, None) - } - Self::ForeignKey { - columns, - clause, - deref_clause, - } => { - s.append(TK_FOREIGN, None)?; - s.append(TK_KEY, None)?; - s.append(TK_LP, None)?; - comma(columns, s, context)?; - s.append(TK_RP, None)?; - s.append(TK_REFERENCES, None)?; - clause.to_tokens_with_context(s, context)?; - if let Some(deref_clause) = deref_clause { - deref_clause.to_tokens_with_context(s, context)?; - } - Ok(()) - } - } - } -} - -impl ToTokens for SortOrder { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - s.append( - match self { - Self::Asc => TK_ASC, - Self::Desc => TK_DESC, - }, - None, - ) - } -} - -impl ToTokens for NullsOrder { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - s.append(TK_NULLS, None)?; - s.append( - match self { - Self::First => TK_FIRST, - Self::Last => TK_LAST, - }, - None, - ) - } -} - -impl ToTokens for ForeignKeyClause { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - self.tbl_name.to_tokens_with_context(s, context)?; - if let Some(ref columns) = self.columns { - s.append(TK_LP, None)?; - comma(columns, s, context)?; - s.append(TK_RP, None)?; - } - for arg in &self.args { - arg.to_tokens_with_context(s, context)?; - } - Ok(()) - } -} - -impl ToTokens for RefArg { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::OnDelete(ref action) => { - s.append(TK_ON, None)?; - s.append(TK_DELETE, None)?; - action.to_tokens_with_context(s, context) - } - Self::OnInsert(ref action) => { - s.append(TK_ON, None)?; - s.append(TK_INSERT, None)?; - action.to_tokens_with_context(s, context) - } - Self::OnUpdate(ref action) => { - s.append(TK_ON, None)?; - s.append(TK_UPDATE, None)?; - action.to_tokens_with_context(s, context) - } - Self::Match(ref name) => { - s.append(TK_MATCH, None)?; - name.to_tokens_with_context(s, context) - } - } - } -} - -impl ToTokens for RefAct { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - match self { - Self::SetNull => { - s.append(TK_SET, None)?; - s.append(TK_NULL, None) - } - Self::SetDefault => { - s.append(TK_SET, None)?; - s.append(TK_DEFAULT, None) - } - Self::Cascade => s.append(TK_CASCADE, None), - Self::Restrict => s.append(TK_RESTRICT, None), - Self::NoAction => { - s.append(TK_NO, None)?; - s.append(TK_ACTION, None) - } - } - } -} - -impl ToTokens for DeferSubclause { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - if !self.deferrable { - s.append(TK_NOT, None)?; - } - s.append(TK_DEFERRABLE, None)?; - if let Some(init_deferred) = self.init_deferred { - init_deferred.to_tokens_with_context(s, context)?; - } - Ok(()) - } -} - -impl ToTokens for InitDeferredPred { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - s.append(TK_INITIALLY, None)?; - s.append( - match self { - Self::InitiallyDeferred => TK_DEFERRED, - Self::InitiallyImmediate => TK_IMMEDIATE, - }, - None, - ) - } -} - -impl ToTokens for IndexedColumn { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - self.col_name.to_tokens_with_context(s, context)?; - if let Some(ref collation_name) = self.collation_name { - s.append(TK_COLLATE, None)?; - collation_name.to_tokens_with_context(s, context)?; - } - if let Some(order) = self.order { - order.to_tokens_with_context(s, context)?; - } - Ok(()) - } -} - -impl ToTokens for Indexed { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::IndexedBy(ref name) => { - s.append(TK_INDEXED, None)?; - s.append(TK_BY, None)?; - name.to_tokens_with_context(s, context) - } - Self::NotIndexed => { - s.append(TK_NOT, None)?; - s.append(TK_INDEXED, None) - } - } - } -} - -impl ToTokens for SortedColumn { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - self.expr.to_tokens_with_context(s, context)?; - if let Some(ref order) = self.order { - order.to_tokens_with_context(s, context)?; - } - if let Some(ref nulls) = self.nulls { - nulls.to_tokens_with_context(s, context)?; - } - Ok(()) - } -} - -impl ToTokens for Limit { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - s.append(TK_LIMIT, None)?; - self.expr.to_tokens_with_context(s, context)?; - if let Some(ref offset) = self.offset { - s.append(TK_OFFSET, None)?; - offset.to_tokens_with_context(s, context)?; - } - Ok(()) - } -} - -impl ToTokens for InsertBody { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::Select(select, upsert) => { - select.to_tokens_with_context(s, context)?; - if let Some(upsert) = upsert { - upsert.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::DefaultValues => { - s.append(TK_DEFAULT, None)?; - s.append(TK_VALUES, None) - } - } - } -} - -impl ToTokens for Set { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - if self.col_names.len() == 1 { - comma(self.col_names.deref(), s, context)?; - } else { - s.append(TK_LP, None)?; - comma(self.col_names.deref(), s, context)?; - s.append(TK_RP, None)?; - } - s.append(TK_EQ, None)?; - self.expr.to_tokens_with_context(s, context) - } -} - -impl ToTokens for PragmaBody { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::Equals(value) => { - s.append(TK_EQ, None)?; - value.to_tokens_with_context(s, context) - } - Self::Call(value) => { - s.append(TK_LP, None)?; - value.to_tokens_with_context(s, context)?; - s.append(TK_RP, None) - } - } - } -} - -impl ToTokens for TriggerTime { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - match self { - Self::Before => s.append(TK_BEFORE, None), - Self::After => s.append(TK_AFTER, None), - Self::InsteadOf => { - s.append(TK_INSTEAD, None)?; - s.append(TK_OF, None) - } - } - } -} - -impl ToTokens for TriggerEvent { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::Delete => s.append(TK_DELETE, None), - Self::Insert => s.append(TK_INSERT, None), - Self::Update => s.append(TK_UPDATE, None), - Self::UpdateOf(ref col_names) => { - s.append(TK_UPDATE, None)?; - s.append(TK_OF, None)?; - comma(col_names.deref(), s, context) - } - } - } -} - -impl ToTokens for TriggerCmd { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::Update(update) => { - let TriggerCmdUpdate { - or_conflict, - tbl_name, - sets, - from, - where_clause, - } = &**update; - s.append(TK_UPDATE, None)?; - if let Some(or_conflict) = or_conflict { - s.append(TK_OR, None)?; - or_conflict.to_tokens_with_context(s, context)?; - } - tbl_name.to_tokens_with_context(s, context)?; - s.append(TK_SET, None)?; - comma(sets, s, context)?; - if let Some(from) = from { - s.append(TK_FROM, None)?; - from.to_tokens_with_context(s, context)?; - } - if let Some(where_clause) = where_clause { - s.append(TK_WHERE, None)?; - where_clause.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Insert(insert) => { - let TriggerCmdInsert { - or_conflict, - tbl_name, - col_names, - select, - upsert, - returning, - } = &**insert; - if let Some(ResolveType::Replace) = or_conflict { - s.append(TK_REPLACE, None)?; - } else { - s.append(TK_INSERT, None)?; - if let Some(or_conflict) = or_conflict { - s.append(TK_OR, None)?; - or_conflict.to_tokens_with_context(s, context)?; - } - } - s.append(TK_INTO, None)?; - tbl_name.to_tokens_with_context(s, context)?; - if let Some(col_names) = col_names { - s.append(TK_LP, None)?; - comma(col_names.deref(), s, context)?; - s.append(TK_RP, None)?; - } - select.to_tokens_with_context(s, context)?; - if let Some(upsert) = upsert { - upsert.to_tokens_with_context(s, context)?; - } - if let Some(returning) = returning { - s.append(TK_RETURNING, None)?; - comma(returning, s, context)?; - } - Ok(()) - } - Self::Delete(delete) => { - s.append(TK_DELETE, None)?; - s.append(TK_FROM, None)?; - delete.tbl_name.to_tokens_with_context(s, context)?; - if let Some(where_clause) = &delete.where_clause { - s.append(TK_WHERE, None)?; - where_clause.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Select(select) => select.to_tokens_with_context(s, context), - } - } -} - -impl ToTokens for ResolveType { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - s.append( - match self { - Self::Rollback => TK_ROLLBACK, - Self::Abort => TK_ABORT, - Self::Fail => TK_FAIL, - Self::Ignore => TK_IGNORE, - Self::Replace => TK_REPLACE, - }, - None, - ) - } -} - -impl ToTokens for With { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - s.append(TK_WITH, None)?; - if self.recursive { - s.append(TK_RECURSIVE, None)?; - } - comma(&self.ctes, s, context) - } -} - -impl ToTokens for CommonTableExpr { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - self.tbl_name.to_tokens_with_context(s, context)?; - if let Some(ref columns) = self.columns { - s.append(TK_LP, None)?; - comma(columns, s, context)?; - s.append(TK_RP, None)?; - } - s.append(TK_AS, None)?; - match self.materialized { - Materialized::Any => {} - Materialized::Yes => { - s.append(TK_MATERIALIZED, None)?; - } - Materialized::No => { - s.append(TK_NOT, None)?; - s.append(TK_MATERIALIZED, None)?; - } - }; - s.append(TK_LP, None)?; - self.select.to_tokens_with_context(s, context)?; - s.append(TK_RP, None) - } -} - -impl ToTokens for Type { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self.size { - None => s.append(TK_ID, Some(&self.name)), - Some(ref size) => { - s.append(TK_ID, Some(&self.name))?; // TODO check there is no forbidden chars - s.append(TK_LP, None)?; - size.to_tokens_with_context(s, context)?; - s.append(TK_RP, None) - } - } - } -} - -impl ToTokens for TypeSize { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::MaxSize(size) => size.to_tokens_with_context(s, context), - Self::TypeSize(size1, size2) => { - size1.to_tokens_with_context(s, context)?; - s.append(TK_COMMA, None)?; - size2.to_tokens_with_context(s, context) - } - } - } -} - -impl ToTokens for TransactionType { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - s.append( - match self { - Self::Deferred => TK_DEFERRED, - Self::Immediate => TK_IMMEDIATE, - Self::Exclusive => TK_EXCLUSIVE, - }, - None, - ) - } -} - -impl ToTokens for Upsert { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - s.append(TK_ON, None)?; - s.append(TK_CONFLICT, None)?; - if let Some(ref index) = self.index { - index.to_tokens_with_context(s, context)?; - } - self.do_clause.to_tokens_with_context(s, context)?; - if let Some(ref next) = self.next { - next.to_tokens_with_context(s, context)?; - } - Ok(()) - } -} - -impl ToTokens for UpsertIndex { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - s.append(TK_LP, None)?; - comma(&self.targets, s, context)?; - s.append(TK_RP, None)?; - if let Some(ref where_clause) = self.where_clause { - s.append(TK_WHERE, None)?; - where_clause.to_tokens_with_context(s, context)?; - } - Ok(()) - } -} - -impl ToTokens for UpsertDo { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::Set { sets, where_clause } => { - s.append(TK_DO, None)?; - s.append(TK_UPDATE, None)?; - s.append(TK_SET, None)?; - comma(sets, s, context)?; - if let Some(where_clause) = where_clause { - s.append(TK_WHERE, None)?; - where_clause.to_tokens_with_context(s, context)?; - } - Ok(()) - } - Self::Nothing => { - s.append(TK_DO, None)?; - s.append(TK_NOTHING, None) - } - } - } -} - -impl ToTokens for FunctionTail { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - if let Some(ref filter_clause) = self.filter_clause { - s.append(TK_FILTER, None)?; - s.append(TK_LP, None)?; - s.append(TK_WHERE, None)?; - filter_clause.to_tokens_with_context(s, context)?; - s.append(TK_RP, None)?; - } - if let Some(ref over_clause) = self.over_clause { - s.append(TK_OVER, None)?; - over_clause.to_tokens_with_context(s, context)?; - } - Ok(()) - } -} - -impl ToTokens for Over { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::Window(ref window) => window.to_tokens_with_context(s, context), - Self::Name(ref name) => name.to_tokens_with_context(s, context), - } - } -} - -impl ToTokens for WindowDef { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - self.name.to_tokens_with_context(s, context)?; - s.append(TK_AS, None)?; - self.window.to_tokens_with_context(s, context) - } -} - -impl ToTokens for Window { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - s.append(TK_LP, None)?; - if let Some(ref base) = self.base { - base.to_tokens_with_context(s, context)?; - } - if let Some(ref partition_by) = self.partition_by { - s.append(TK_PARTITION, None)?; - s.append(TK_BY, None)?; - comma(partition_by, s, context)?; - } - if let Some(ref order_by) = self.order_by { - s.append(TK_ORDER, None)?; - s.append(TK_BY, None)?; - comma(order_by, s, context)?; - } - if let Some(ref frame_clause) = self.frame_clause { - frame_clause.to_tokens_with_context(s, context)?; - } - s.append(TK_RP, None) - } -} - -impl ToTokens for FrameClause { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - self.mode.to_tokens_with_context(s, context)?; - if let Some(ref end) = self.end { - s.append(TK_BETWEEN, None)?; - self.start.to_tokens_with_context(s, context)?; - s.append(TK_AND, None)?; - end.to_tokens_with_context(s, context)?; - } else { - self.start.to_tokens_with_context(s, context)?; - } - if let Some(ref exclude) = self.exclude { - s.append(TK_EXCLUDE, None)?; - exclude.to_tokens_with_context(s, context)?; - } - Ok(()) - } -} - -impl ToTokens for FrameMode { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - s.append( - match self { - Self::Groups => TK_GROUPS, - Self::Range => TK_RANGE, - Self::Rows => TK_ROWS, - }, - None, - ) - } -} - -impl ToTokens for FrameBound { - fn to_tokens_with_context( - &self, - s: &mut S, - context: &C, - ) -> Result<(), S::Error> { - match self { - Self::CurrentRow => { - s.append(TK_CURRENT, None)?; - s.append(TK_ROW, None) - } - Self::Following(value) => { - value.to_tokens_with_context(s, context)?; - s.append(TK_FOLLOWING, None) - } - Self::Preceding(value) => { - value.to_tokens_with_context(s, context)?; - s.append(TK_PRECEDING, None) - } - Self::UnboundedFollowing => { - s.append(TK_UNBOUNDED, None)?; - s.append(TK_FOLLOWING, None) - } - Self::UnboundedPreceding => { - s.append(TK_UNBOUNDED, None)?; - s.append(TK_PRECEDING, None) - } - } - } -} - -impl ToTokens for FrameExclude { - fn to_tokens_with_context( - &self, - s: &mut S, - _: &C, - ) -> Result<(), S::Error> { - match self { - Self::NoOthers => { - s.append(TK_NO, None)?; - s.append(TK_OTHERS, None) - } - Self::CurrentRow => { - s.append(TK_CURRENT, None)?; - s.append(TK_ROW, None) - } - Self::Group => s.append(TK_GROUP, None), - Self::Ties => s.append(TK_TIES, None), - } - } -} - -fn comma( - items: I, - s: &mut S, - context: &C, -) -> Result<(), S::Error> -where - I: IntoIterator, - I::Item: ToTokens, -{ - s.comma(items, context) -} - -// TK_ID: [...] / `...` / "..." / some keywords / non keywords -fn double_quote(name: &str, s: &mut S) -> Result<(), S::Error> { - if name.is_empty() { - return s.append(TK_ID, Some("\"\"")); - } - s.append(TK_ID, Some(name)) -} diff --git a/vendored/sqlite3-parser/src/parser/ast/mod.rs b/vendored/sqlite3-parser/src/parser/ast/mod.rs deleted file mode 100644 index b67c51507..000000000 --- a/vendored/sqlite3-parser/src/parser/ast/mod.rs +++ /dev/null @@ -1,2233 +0,0 @@ -//! Abstract Syntax Tree - -pub mod check; -pub mod fmt; - -use std::num::ParseIntError; -use std::ops::Deref; -use std::str::{self, Bytes, FromStr}; - -use strum_macros::{EnumIter, EnumString}; - -use fmt::{ToTokens, TokenStream}; -use indexmap::{IndexMap, IndexSet}; - -use crate::custom_err; -use crate::dialect::TokenType::{self, *}; -use crate::dialect::{from_bytes, from_token, Token}; -use crate::parser::{parse::YYCODETYPE, ParserError}; - -/// `?` or `$` Prepared statement arg placeholder(s) -#[derive(Default)] -pub struct ParameterInfo { - /// Number of SQL parameters in a prepared statement, like `sqlite3_bind_parameter_count` - pub count: u32, - /// Parameter name(s) if any - pub names: IndexSet, -} - -// https://sqlite.org/lang_expr.html#parameters -impl TokenStream for ParameterInfo { - type Error = ParseIntError; - - fn append(&mut self, ty: TokenType, value: Option<&str>) -> Result<(), Self::Error> { - if ty == TK_VARIABLE { - if let Some(variable) = value { - if variable == "?" { - self.count = self.count.saturating_add(1); - } else if variable.as_bytes()[0] == b'?' { - let n = u32::from_str(&variable[1..])?; - if n > self.count { - self.count = n; - } - } else if self.names.insert(variable.to_owned()) { - self.count = self.count.saturating_add(1); - } - } - } - Ok(()) - } -} - -/// Statement or Explain statement -// https://sqlite.org/syntax/sql-stmt.html -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Cmd { - /// `EXPLAIN` statement - Explain(Stmt), - /// `EXPLAIN QUERY PLAN` statement - ExplainQueryPlan(Stmt), - /// statement - Stmt(Stmt), -} - -pub(crate) enum ExplainKind { - Explain, - QueryPlan, -} - -/// SQL statement -// https://sqlite.org/syntax/sql-stmt.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Stmt { - /// `ALTER TABLE`: table name, body - AlterTable(Box<(QualifiedName, AlterTableBody)>), - /// `ANALYSE`: object name - Analyze(Option), - /// `ATTACH DATABASE` - Attach { - /// filename - // TODO distinction between ATTACH and ATTACH DATABASE - expr: Box, - /// schema name - db_name: Box, - /// password - key: Option>, - }, - /// `BEGIN`: tx type, tx name - Begin(Option, Option), - /// `COMMIT`/`END`: tx name - Commit(Option), // TODO distinction between COMMIT and END - /// `CREATE INDEX` - CreateIndex { - /// `UNIQUE` - unique: bool, - /// `IF NOT EXISTS` - if_not_exists: bool, - /// index name - idx_name: Box, - /// table name - tbl_name: Name, - /// indexed columns or expressions - columns: Vec, - /// partial index - where_clause: Option>, - }, - /// `CREATE TABLE` - CreateTable { - /// `TEMPORARY` - temporary: bool, // TODO distinction between TEMP and TEMPORARY - /// `IF NOT EXISTS` - if_not_exists: bool, - /// table name - tbl_name: QualifiedName, - /// table body - body: Box, - }, - /// `CREATE TRIGGER` - CreateTrigger(Box), - /// `CREATE VIEW` - CreateView { - /// `TEMPORARY` - temporary: bool, - /// `IF NOT EXISTS` - if_not_exists: bool, - /// view name - view_name: QualifiedName, - /// columns - columns: Option>, - /// query - select: Box, - }, - /// `CREATE VIRTUAL TABLE` - CreateVirtualTable(Box), - /// `DELETE` - Delete(Box), - /// `DETACH DATABASE`: db name - Detach(Box), // TODO distinction between DETACH and DETACH DATABASE - /// `DROP INDEX` - DropIndex { - /// `IF EXISTS` - if_exists: bool, - /// index name - idx_name: QualifiedName, - }, - /// `DROP TABLE` - DropTable { - /// `IF EXISTS` - if_exists: bool, - /// table name - tbl_name: QualifiedName, - }, - /// `DROP TRIGGER` - DropTrigger { - /// `IF EXISTS` - if_exists: bool, - /// trigger name - trigger_name: QualifiedName, - }, - /// `DROP VIEW` - DropView { - /// `IF EXISTS` - if_exists: bool, - /// view name - view_name: QualifiedName, - }, - /// `INSERT` - Insert(Box), - /// `PRAGMA`: pragma name, body - Pragma(Box, Option>), - /// `REINDEX` - Reindex { - /// collation or index or table name - obj_name: Option, - }, - /// `RELEASE`: savepoint name - Release(Name), // TODO distinction between RELEASE and RELEASE SAVEPOINT - /// `ROLLBACK` - Rollback { - /// transaction name - tx_name: Option, - /// savepoint name - savepoint_name: Option, // TODO distinction between TO and TO SAVEPOINT - }, - /// `SAVEPOINT`: savepoint name - Savepoint(Name), - /// `SELECT` - Select(Box), - /// call to a built-in function - FunctionCall { - /// function name - name: Name, - /// `DISTINCT` - distinctness: Option, - /// arguments - args: Option>, - /// `ORDER BY` - order_by: Option>, - /// `FILTER` - filter_over: Option, - }, - /// Function call expression with '*' as arg - FunctionCallStar { - /// function name - name: Name, - /// `FILTER` - filter_over: Option, - }, - /// Identifier - Id(Name), - /// Column - Column { - /// the x in `x.y.z`. index of the db in catalog. - database: Option, - /// the y in `x.y.z`. index of the table in catalog. - table: TableInternalId, - /// the z in `x.y.z`. index of the column in the table. - column: usize, - /// is the column a rowid alias - is_rowid_alias: bool, - }, - /// `ROWID` - RowId { - /// the x in `x.y.z`. index of the db in catalog. - database: Option, - /// the y in `x.y.z`. index of the table in catalog. - table: TableInternalId, - }, - /// `IN` - InList { - /// expression - lhs: Box, - /// `NOT` - not: bool, - /// values - rhs: Option>, - }, - /// `IN` subselect - InSelect { - /// expression - lhs: Box, - /// `NOT` - not: bool, - /// subquery - rhs: Box), - /// Unary expression - Unary(UnaryOperator, Box), - /// Parameters - Variable(String), -} - -impl Expr { - /// Constructor - pub fn parenthesized(x: Self) -> Self { - Self::Parenthesized(vec![x]) - } - /// Constructor - pub fn id(xt: YYCODETYPE, x: Token) -> Self { - Self::Id(Name::from_token(xt, x)) - } - /// Constructor - pub fn collate(x: Self, ct: YYCODETYPE, c: Token) -> Self { - Self::Collate(Box::new(x), from_token(ct, c)) - } - /// Constructor - pub fn cast(x: Self, type_name: Option) -> Self { - Self::Cast { - expr: Box::new(x), - type_name, - } - } - /// Constructor - pub fn binary(left: Self, op: YYCODETYPE, right: Self) -> Self { - Self::Binary(Box::new(left), Operator::from(op), Box::new(right)) - } - /// Constructor - pub fn ptr(left: Self, op: Token, right: Self) -> Self { - let mut ptr = Operator::ArrowRight; - if op.1 == b"->>" { - ptr = Operator::ArrowRightShift; - } - Self::Binary(Box::new(left), ptr, Box::new(right)) - } - /// Constructor - pub fn like(lhs: Self, not: bool, op: LikeOperator, rhs: Self, escape: Option) -> Self { - Self::Like { - lhs: Box::new(lhs), - not, - op, - rhs: Box::new(rhs), - escape: escape.map(Box::new), - } - } - /// Constructor - pub fn not_null(x: Self, op: YYCODETYPE) -> Self { - if op == TK_ISNULL as YYCODETYPE { - Self::IsNull(Box::new(x)) - } else if op == TK_NOTNULL as YYCODETYPE { - Self::NotNull(Box::new(x)) - } else { - unreachable!() - } - } - /// Constructor - pub fn unary(op: UnaryOperator, x: Self) -> Self { - Self::Unary(op, Box::new(x)) - } - /// Constructor - pub fn between(lhs: Self, not: bool, start: Self, end: Self) -> Self { - Self::Between { - lhs: Box::new(lhs), - not, - start: Box::new(start), - end: Box::new(end), - } - } - /// Constructor - pub fn in_list(lhs: Self, not: bool, rhs: Option>) -> Self { - Self::InList { - lhs: Box::new(lhs), - not, - rhs, - } - } - /// Constructor - pub fn in_select(lhs: Self, not: bool, rhs: Select) -> Self { - Self::InSelect { - lhs: Box::new(lhs), - not, - rhs: Box::new(rhs), - } - } - /// Constructor - pub fn in_table(lhs: Self, not: bool, rhs: QualifiedName, args: Option>) -> Self { - Self::InTable { - lhs: Box::new(lhs), - not, - rhs, - args, - } - } - /// Constructor - pub fn sub_query(query: Select) -> Self { - Self::Subquery(Box::new(query)) - } -} - -/// SQL literal -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Literal { - /// Number - Numeric(String), - /// String - // TODO Check that string is already quoted and correctly escaped - String(String), - /// BLOB - // TODO Check that string is valid (only hexa) - Blob(String), - /// Keyword - Keyword(String), - /// `NULL` - Null, - /// `CURRENT_DATE` - CurrentDate, - /// `CURRENT_TIME` - CurrentTime, - /// `CURRENT_TIMESTAMP` - CurrentTimestamp, -} - -impl Literal { - /// Constructor - pub fn from_ctime_kw(token: Token) -> Self { - if b"CURRENT_DATE".eq_ignore_ascii_case(token.1) { - Self::CurrentDate - } else if b"CURRENT_TIME".eq_ignore_ascii_case(token.1) { - Self::CurrentTime - } else if b"CURRENT_TIMESTAMP".eq_ignore_ascii_case(token.1) { - Self::CurrentTimestamp - } else { - unreachable!() - } - } -} - -/// Textual comparison operator in an expression -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum LikeOperator { - /// `GLOB` - Glob, - /// `LIKE` - Like, - /// `MATCH` - Match, - /// `REGEXP` - Regexp, -} - -impl LikeOperator { - /// Constructor - pub fn from_token(token_type: YYCODETYPE, token: Token) -> Self { - if token_type == TK_MATCH as YYCODETYPE { - return Self::Match; - } else if token_type == TK_LIKE_KW as YYCODETYPE { - let token = token.1; - if b"LIKE".eq_ignore_ascii_case(token) { - return Self::Like; - } else if b"GLOB".eq_ignore_ascii_case(token) { - return Self::Glob; - } else if b"REGEXP".eq_ignore_ascii_case(token) { - return Self::Regexp; - } - } - unreachable!() - } -} - -/// SQL operators -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Operator { - /// `+` - Add, - /// `AND` - And, - /// `->` - ArrowRight, - /// `->>` - ArrowRightShift, - /// `&` - BitwiseAnd, - /// `|` - BitwiseOr, - /// `~` - BitwiseNot, - /// String concatenation (`||`) - Concat, - /// `=` or `==` - Equals, - /// `/` - Divide, - /// `>` - Greater, - /// `>=` - GreaterEquals, - /// `IS` - Is, - /// `IS NOT` - IsNot, - /// `<<` - LeftShift, - /// `<` - Less, - /// `<=` - LessEquals, - /// `%` - Modulus, - /// `*` - Multiply, - /// `!=` or `<>` - NotEquals, - /// `OR` - Or, - /// `>>` - RightShift, - /// `-` - Subtract, -} - -impl From for Operator { - fn from(token_type: YYCODETYPE) -> Self { - match token_type { - x if x == TK_AND as YYCODETYPE => Self::And, - x if x == TK_OR as YYCODETYPE => Self::Or, - x if x == TK_LT as YYCODETYPE => Self::Less, - x if x == TK_GT as YYCODETYPE => Self::Greater, - x if x == TK_GE as YYCODETYPE => Self::GreaterEquals, - x if x == TK_LE as YYCODETYPE => Self::LessEquals, - x if x == TK_EQ as YYCODETYPE => Self::Equals, - x if x == TK_NE as YYCODETYPE => Self::NotEquals, - x if x == TK_BITAND as YYCODETYPE => Self::BitwiseAnd, - x if x == TK_BITOR as YYCODETYPE => Self::BitwiseOr, - x if x == TK_BITNOT as YYCODETYPE => Self::BitwiseNot, - x if x == TK_LSHIFT as YYCODETYPE => Self::LeftShift, - x if x == TK_RSHIFT as YYCODETYPE => Self::RightShift, - x if x == TK_PLUS as YYCODETYPE => Self::Add, - x if x == TK_MINUS as YYCODETYPE => Self::Subtract, - x if x == TK_STAR as YYCODETYPE => Self::Multiply, - x if x == TK_SLASH as YYCODETYPE => Self::Divide, - x if x == TK_REM as YYCODETYPE => Self::Modulus, - x if x == TK_CONCAT as YYCODETYPE => Self::Concat, - x if x == TK_IS as YYCODETYPE => Self::Is, - x if x == TK_NOT as YYCODETYPE => Self::IsNot, - _ => unreachable!(), - } - } -} - -impl Operator { - /// returns whether order of operations can be ignored - pub fn is_commutative(&self) -> bool { - matches!( - self, - Operator::Add - | Operator::Multiply - | Operator::BitwiseAnd - | Operator::BitwiseOr - | Operator::Equals - | Operator::NotEquals - ) - } - - /// Returns true if this operator is a comparison operator that may need affinity conversion - pub fn is_comparison(&self) -> bool { - matches!( - self, - Self::Equals - | Self::NotEquals - | Self::Less - | Self::LessEquals - | Self::Greater - | Self::GreaterEquals - | Self::Is - | Self::IsNot - ) - } -} - -/// Unary operators -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum UnaryOperator { - /// bitwise negation (`~`) - BitwiseNot, - /// negative-sign - Negative, - /// `NOT` - Not, - /// positive-sign - Positive, -} - -impl From for UnaryOperator { - fn from(token_type: YYCODETYPE) -> Self { - match token_type { - x if x == TK_BITNOT as YYCODETYPE => Self::BitwiseNot, - x if x == TK_MINUS as YYCODETYPE => Self::Negative, - x if x == TK_NOT as YYCODETYPE => Self::Not, - x if x == TK_PLUS as YYCODETYPE => Self::Positive, - _ => unreachable!(), - } - } -} - -/// `SELECT` statement -// https://sqlite.org/lang_select.html -// https://sqlite.org/syntax/factored-select-stmt.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Select { - /// CTE - pub with: Option, - /// body - pub body: SelectBody, - /// `ORDER BY` - pub order_by: Option>, // ORDER BY term does not match any column in the result set - /// `LIMIT` - pub limit: Option>, -} - -/// `SELECT` body -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct SelectBody { - /// first select - pub select: Box, - /// compounds - pub compounds: Option>, -} - -impl SelectBody { - pub(crate) fn push(&mut self, cs: CompoundSelect) -> Result<(), ParserError> { - use crate::ast::check::ColumnCount; - if let ColumnCount::Fixed(n) = self.select.column_count() { - if let ColumnCount::Fixed(m) = cs.select.column_count() { - if n != m { - return Err(custom_err!( - "SELECTs to the left and right of {} do not have the same number of result columns", - cs.operator - )); - } - } - } - if let Some(ref mut v) = self.compounds { - v.push(cs); - } else { - self.compounds = Some(vec![cs]); - } - Ok(()) - } -} - -/// Compound select -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct CompoundSelect { - /// operator - pub operator: CompoundOperator, - /// select - pub select: Box, -} - -/// Compound operators -// https://sqlite.org/syntax/compound-operator.html -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum CompoundOperator { - /// `UNION` - Union, - /// `UNION ALL` - UnionAll, - /// `EXCEPT` - Except, - /// `INTERSECT` - Intersect, -} - -/// `SELECT` core -// https://sqlite.org/syntax/select-core.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum OneSelect { - /// `SELECT` - Select(Box), - /// `VALUES` - Values(Vec>), -} - -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -/// `SELECT` core -pub struct SelectInner { - /// `DISTINCT` - pub distinctness: Option, - /// columns - pub columns: Vec, - /// `FROM` clause - pub from: Option, - /// `WHERE` clause - pub where_clause: Option, - /// `GROUP BY` - pub group_by: Option, - /// `WINDOW` definition - pub window_clause: Option>, -} - -/// `SELECT` ... `FROM` clause -// https://sqlite.org/syntax/join-clause.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct FromClause { - /// table - pub select: Option>, // FIXME mandatory - /// `JOIN`ed tabled - pub joins: Option>, - /// A default join operator - pub op: Option, // FIXME transient -} -impl FromClause { - pub(crate) fn empty() -> Self { - Self { - select: None, - joins: None, - op: None, - } - } - - pub(crate) fn push( - &mut self, - table: SelectTable, - jc: Option, - ) -> Result<(), ParserError> { - let op = self.op.take(); - if let Some(op) = op { - let jst = JoinedSelectTable { - operator: op, - table, - constraint: jc, - }; - if jst.operator.is_natural() && jst.constraint.is_some() { - return Err(custom_err!( - "a NATURAL join may not have an ON or USING clause" - )); - } - if let Some(ref mut joins) = self.joins { - joins.push(jst); - } else { - self.joins = Some(vec![jst]); - } - } else { - if jc.is_some() { - return Err(custom_err!("a JOIN clause is required before ON")); - } - debug_assert!(self.select.is_none()); - debug_assert!(self.joins.is_none()); - self.select = Some(Box::new(table)); - } - Ok(()) - } - - pub(crate) fn push_op(&mut self, op: JoinOperator) { - self.op = Some(op); - } -} - -/// `SELECT` distinctness -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Distinctness { - /// `DISTINCT` - Distinct, - /// `ALL` - All, -} - -/// `SELECT` or `RETURNING` result column -// https://sqlite.org/syntax/result-column.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum ResultColumn { - /// expression - Expr(Expr, Option), - /// `*` - Star, - /// table name.`*` - TableStar(Name), -} - -/// Alias -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum As { - /// `AS` - As(Name), - /// no `AS` - Elided(Name), // FIXME Ids -} - -/// `JOIN` clause -// https://sqlite.org/syntax/join-clause.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct JoinedSelectTable { - /// operator - pub operator: JoinOperator, - /// table - pub table: SelectTable, - /// constraint - pub constraint: Option, -} - -/// Table or subquery -// https://sqlite.org/syntax/table-or-subquery.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum SelectTable { - /// table - Table(QualifiedName, Option, Option), - /// table function call - TableCall(QualifiedName, Option>, Option), - /// `SELECT` subquery - Select(Box), -} - -impl CreateTableBody { - /// Constructor - pub fn columns_and_constraints( - columns: IndexMap, - constraints: Option>, - options: TableOptions, - ) -> Result { - Ok(Self::ColumnsAndConstraints { - columns, - constraints, - options, - }) - } - - /// Constructor from Vec of column definition - pub fn columns_and_constraints_from_definition( - columns_vec: Vec, - constraints: Option>, - options: TableOptions, - ) -> Result { - let mut columns = IndexMap::new(); - for def in columns_vec { - columns.insert(def.col_name.clone(), def); - } - Ok(Self::ColumnsAndConstraints { - columns, - constraints, - options, - }) - } -} - -/// Table column definition -// https://sqlite.org/syntax/column-def.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct ColumnDefinition { - /// column name - pub col_name: Name, - /// column type - pub col_type: Option, - /// column constraints - pub constraints: Vec, -} - -impl ColumnDefinition { - /// Constructor - pub fn add_column(columns: &mut IndexMap, mut cd: Self) -> Result<(), ParserError> { - let col_name = &cd.col_name; - if columns.contains_key(col_name) { - // TODO unquote - return Err(custom_err!("duplicate column name: {}", col_name)); - } - // https://github.com/sqlite/sqlite/blob/e452bf40a14aca57fd9047b330dff282f3e4bbcc/src/build.c#L1511-L1514 - if let Some(ref mut col_type) = cd.col_type { - let mut split = col_type.name.split_ascii_whitespace(); - let truncate = if split - .next_back() - .is_some_and(|s| s.eq_ignore_ascii_case("ALWAYS")) - && split - .next_back() - .is_some_and(|s| s.eq_ignore_ascii_case("GENERATED")) - { - let mut generated = false; - for constraint in &cd.constraints { - if let ColumnConstraint::Generated { .. } = constraint.constraint { - generated = true; - break; - } - } - generated - } else { - false - }; - if truncate { - // str_split_whitespace_remainder - let new_type: Vec<&str> = split.collect(); - col_type.name = new_type.join(" "); - } - } - for constraint in &cd.constraints { - if let ColumnConstraint::ForeignKey { - clause: - ForeignKeyClause { - tbl_name, columns, .. - }, - .. - } = &constraint.constraint - { - // The child table may reference the primary key of the parent without specifying the primary key column - if columns.as_ref().map_or(0, Vec::len) > 1 { - return Err(custom_err!( - "foreign key on {} should reference only one column of table {}", - col_name, - tbl_name - )); - } - } - } - columns.insert(col_name.clone(), cd); - Ok(()) - } -} - -/// Named column constraint -// https://sqlite.org/syntax/column-constraint.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct NamedColumnConstraint { - /// constraint name - pub name: Option, - /// constraint - pub constraint: ColumnConstraint, -} - -/// Column constraint -// https://sqlite.org/syntax/column-constraint.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum ColumnConstraint { - /// `PRIMARY KEY` - PrimaryKey { - /// `ASC` / `DESC` - order: Option, - /// `ON CONFLICT` clause - conflict_clause: Option, - /// `AUTOINCREMENT` - auto_increment: bool, - }, - /// `NULL` - NotNull { - /// `NOT` - nullable: bool, - /// `ON CONFLICT` clause - conflict_clause: Option, - }, - /// `UNIQUE` - Unique(Option), - /// `CHECK` - Check(Expr), - /// `DEFAULT` - Default(Expr), - /// `DEFERRABLE` - Defer(DeferSubclause), // FIXME - /// `COLLATE` - Collate { - /// collation name - collation_name: Name, // FIXME Ids - }, - /// `REFERENCES` foreign-key clause - ForeignKey { - /// clause - clause: ForeignKeyClause, - /// `DEFERRABLE` - deref_clause: Option, - }, - /// `GENERATED` - Generated { - /// expression - expr: Expr, - /// `STORED` / `VIRTUAL` - typ: Option, - }, -} - -/// Named table constraint -// https://sqlite.org/syntax/table-constraint.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct NamedTableConstraint { - /// constraint name - pub name: Option, - /// constraint - pub constraint: TableConstraint, -} - -/// Table constraint -// https://sqlite.org/syntax/table-constraint.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum TableConstraint { - /// `PRIMARY KEY` - PrimaryKey { - /// columns - columns: Vec, - /// `AUTOINCREMENT` - auto_increment: bool, - /// `ON CONFLICT` clause - conflict_clause: Option, - }, - /// `UNIQUE` - Unique { - /// columns - columns: Vec, - /// `ON CONFLICT` clause - conflict_clause: Option, - }, - /// `CHECK` - Check(Expr), - /// `FOREIGN KEY` - ForeignKey { - /// columns - columns: Vec, - /// `REFERENCES` - clause: ForeignKeyClause, - /// `DEFERRABLE` - deref_clause: Option, - }, -} - -bitflags::bitflags! { - /// `CREATE TABLE` options - #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] - #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] - pub struct TableOptions: u8 { - /// None - const NONE = 0; - /// `WITHOUT ROWID` - const WITHOUT_ROWID = 1; - /// `STRICT` - const STRICT = 2; - } -} - -/// Sort orders -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum SortOrder { - /// `ASC` - Asc, - /// `DESC` - Desc, -} - -/// `NULLS FIRST` or `NULLS LAST` -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum NullsOrder { - /// `NULLS FIRST` - First, - /// `NULLS LAST` - Last, -} - -/// `REFERENCES` clause -// https://sqlite.org/syntax/foreign-key-clause.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct ForeignKeyClause { - /// foreign table name - pub tbl_name: Name, - /// foreign table columns - pub columns: Option>, - /// referential action(s) / deferrable option(s) - pub args: Vec, -} - -/// foreign-key reference args -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum RefArg { - /// `ON DELETE` - OnDelete(RefAct), - /// `ON INSERT` - OnInsert(RefAct), - /// `ON UPDATE` - OnUpdate(RefAct), - /// `MATCH` - Match(Name), -} - -/// foreign-key reference actions -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum RefAct { - /// `SET NULL` - SetNull, - /// `SET DEFAULT` - SetDefault, - /// `CASCADE` - Cascade, - /// `RESTRICT` - Restrict, - /// `NO ACTION` - NoAction, -} - -/// foreign-key defer clause -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct DeferSubclause { - /// `DEFERRABLE` - pub deferrable: bool, - /// `INITIALLY` `DEFERRED` / `IMMEDIATE` - pub init_deferred: Option, -} - -/// `INITIALLY` `DEFERRED` / `IMMEDIATE` -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum InitDeferredPred { - /// `INITIALLY DEFERRED` - InitiallyDeferred, - /// `INITIALLY IMMEDIATE` - InitiallyImmediate, // default -} - -/// Indexed column -// https://sqlite.org/syntax/indexed-column.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct IndexedColumn { - /// column name - pub col_name: Name, - /// `COLLATE` - pub collation_name: Option, // FIXME Ids - /// `ORDER BY` - pub order: Option, -} - -/// `INDEXED BY` / `NOT INDEXED` -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Indexed { - /// `INDEXED BY`: idx name - IndexedBy(Name), - /// `NOT INDEXED` - NotIndexed, -} - -/// Sorted column -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct SortedColumn { - /// expression - pub expr: Expr, - /// `ASC` / `DESC` - pub order: Option, - /// `NULLS FIRST` / `NULLS LAST` - pub nulls: Option, -} - -/// `LIMIT` -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Limit { - /// count - pub expr: Expr, - /// `OFFSET` - pub offset: Option, // TODO distinction between LIMIT offset, count and LIMIT count OFFSET offset -} - -/// `INSERT` body -// https://sqlite.org/lang_insert.html -// https://sqlite.org/syntax/insert-stmt.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum InsertBody { - /// `SELECT` or `VALUES` - Select(Box), -} - -/// `UPDATE` trigger command -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct TriggerCmdUpdate { - /// `OR` - pub or_conflict: Option, - /// table name - pub tbl_name: Name, - /// `SET` assignments - pub sets: Vec, - /// `FROM` - pub from: Option, - /// `WHERE` clause - pub where_clause: Option, -} - -/// `INSERT` trigger command -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct TriggerCmdInsert { - /// `OR` - pub or_conflict: Option, - /// table name - pub tbl_name: Name, - /// `COLUMNS` - pub col_names: Option, - /// `SELECT` or `VALUES` - pub select: Box, -} - -impl CommonTableExpr { - /// Constructor - pub fn add_cte(ctes: &mut Vec, cte: Self) -> Result<(), ParserError> { - if ctes.iter().any(|c| c.tbl_name == cte.tbl_name) { - return Err(custom_err!("duplicate WITH table name: {}", cte.tbl_name)); - } - ctes.push(cte); - Ok(()) - } -} - -/// Column type -// https://sqlite.org/syntax/type-name.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Type { - /// type name - pub name: String, // TODO Validate: Ids+ - /// type size - pub size: Option, -} - -/// Column type size limit(s) -// https://sqlite.org/syntax/type-name.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum TypeSize { - /// maximum size - MaxSize(Box), - /// precision - TypeSize(Box, Box), -} - -/// Transaction types -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum TransactionType { - /// `DEFERRED` - Deferred, // default - /// `IMMEDIATE` - Immediate, - /// `EXCLUSIVE` - Exclusive, -} - -/// Upsert clause -// https://sqlite.org/lang_upsert.html -// https://sqlite.org/syntax/upsert-clause.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Upsert { - /// conflict targets - pub index: Option>, - /// `DO` clause - pub do_clause: Box, - /// next upsert - pub next: Option>, -} - -/// Upsert conflict targets -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct UpsertIndex { - /// columns - pub targets: Vec, - /// `WHERE` clause - pub where_clause: Option, -} - -/// Upsert `DO` action -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum UpsertDo { - /// `SET` - Set { - /// assignments - sets: Vec, - /// `WHERE` clause - where_clause: Option, - }, - /// `NOTHING` - Nothing, -} - -/// Function call tail -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct FunctionTail { - /// `FILTER` clause - pub filter_clause: Option>, - /// `OVER` clause - pub over_clause: Option>, -} - -/// Function call `OVER` clause -// https://sqlite.org/syntax/over-clause.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Over { - /// Window definition - Window(Window), - /// Window name - Name(Name), -} - -/// `OVER` window definition -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct WindowDef { - /// window name - pub name: Name, - /// window definition - pub window: Window, -} - -/// Window definition -// https://sqlite.org/syntax/window-defn.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Window { - /// base window name - pub base: Option, - /// `PARTITION BY` - pub partition_by: Option>, - /// `ORDER BY` - pub order_by: Option>, - /// frame spec - pub frame_clause: Option, -} - -/// Frame specification -// https://sqlite.org/syntax/frame-spec.html -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct FrameClause { - /// unit - pub mode: FrameMode, - /// start bound - pub start: FrameBound, - /// end bound - pub end: Option, - /// `EXCLUDE` - pub exclude: Option, -} - -/// Frame modes -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum FrameMode { - /// `GROUPS` - Groups, - /// `RANGE` - Range, - /// `ROWS` - Rows, -} - -/// Frame bounds -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum FrameBound { - /// `CURRENT ROW` - CurrentRow, - /// `FOLLOWING` - Following(Box), - /// `PRECEDING` - Preceding(Box), - /// `UNBOUNDED FOLLOWING` - UnboundedFollowing, - /// `UNBOUNDED PRECEDING` - UnboundedPreceding, -} - -/// Frame exclusions -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum FrameExclude { - /// `NO OTHERS` - NoOthers, - /// `CURRENT ROW` - CurrentRow, - /// `GROUP` - Group, - /// `TIES` - Ties, -} - -#[cfg(test)] -mod test { - use super::{Name, PragmaName}; - use strum::IntoEnumIterator; - - #[test] - fn test_dequote() { - assert_eq!(name("x"), "x"); - assert_eq!(name("`x`"), "x"); - assert_eq!(name("`x``y`"), "x`y"); - assert_eq!(name(r#""x""#), "x"); - assert_eq!(name(r#""x""y""#), "x\"y"); - assert_eq!(name("[x]"), "x"); - } - - #[test] - // pragma pragma_list expects this to be sorted. We can avoid allocations there if we keep - // the list sorted. - fn pragma_list_sorted() { - let pragma_strings: Vec = PragmaName::iter().map(|x| x.to_string()).collect(); - let mut pragma_strings_sorted = pragma_strings.clone(); - pragma_strings_sorted.sort(); - assert_eq!(pragma_strings, pragma_strings_sorted); - } - - fn name(s: &'static str) -> Name { - Name::from_str(s) - } -} diff --git a/vendored/sqlite3-parser/src/parser/mod.rs b/vendored/sqlite3-parser/src/parser/mod.rs deleted file mode 100644 index 58fdc62f9..000000000 --- a/vendored/sqlite3-parser/src/parser/mod.rs +++ /dev/null @@ -1,154 +0,0 @@ -//! SQLite parser - -pub mod ast; -pub mod parse { - #![expect(unused_braces)] - #![expect(clippy::if_same_then_else)] - #![expect(clippy::absurd_extreme_comparisons)] // FIXME - #![expect(clippy::needless_return)] - #![expect(clippy::upper_case_acronyms)] - #![expect(clippy::manual_range_patterns)] - - include!(concat!(env!("OUT_DIR"), "/parse.rs")); -} - -use crate::dialect::Token; -use ast::{Cmd, ExplainKind, Name, Stmt}; - -/// Parser error -#[derive(Debug, Clone, PartialEq)] -pub enum ParserError { - /// Syntax error - SyntaxError(String), - /// Unexpected EOF - UnexpectedEof, - /// Custom error - Custom(String), -} - -impl std::fmt::Display for ParserError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Self::SyntaxError(s) => { - write!(f, "near \"{s}\": syntax error") - } - Self::UnexpectedEof => f.write_str("unexpected end of input"), - Self::Custom(s) => f.write_str(s), - } - } -} - -impl std::error::Error for ParserError {} - -/// Custom error constructor -#[macro_export] -macro_rules! custom_err { - ($msg:literal $(,)?) => { - $crate::parser::ParserError::Custom($msg.to_owned()) - }; - ($err:expr $(,)?) => { - $crate::parser::ParserError::Custom(format!($err)) - }; - ($fmt:expr, $($arg:tt)*) => { - $crate::parser::ParserError::Custom(format!($fmt, $($arg)*)) - }; -} - -/// Parser context -pub struct Context<'input> { - input: &'input [u8], - explain: Option, - stmt: Option, - constraint_name: Option, // transient - module_arg: Option<(usize, usize)>, // Complete text of a module argument - module_args: Option>, // CREATE VIRTUAL TABLE args - done: bool, - error: Option, -} - -impl<'input> Context<'input> { - pub fn new(input: &'input [u8]) -> Self { - Context { - input, - explain: None, - stmt: None, - constraint_name: None, - module_arg: None, - module_args: None, - done: false, - error: None, - } - } - - /// Consume parsed command - pub fn cmd(&mut self) -> Option { - if let Some(stmt) = self.stmt.take() { - match self.explain.take() { - Some(ExplainKind::Explain) => Some(Cmd::Explain(stmt)), - Some(ExplainKind::QueryPlan) => Some(Cmd::ExplainQueryPlan(stmt)), - None => Some(Cmd::Stmt(stmt)), - } - } else { - None - } - } - - fn constraint_name(&mut self) -> Option { - self.constraint_name.take() - } - fn no_constraint_name(&self) -> bool { - self.constraint_name.is_none() - } - - fn vtab_arg_init(&mut self) { - self.add_module_arg(); - self.module_arg = None; - } - fn vtab_arg_extend(&mut self, any: Token) { - if let Some((_, ref mut n)) = self.module_arg { - *n = any.2 - } else { - self.module_arg = Some((any.0, any.2)) - } - } - fn add_module_arg(&mut self) { - if let Some((start, end)) = self.module_arg.take() { - if let Ok(arg) = std::str::from_utf8(&self.input[start..end]) { - self.module_args.get_or_insert(vec![]).push(arg.to_owned()); - } // FIXME error handling - } - } - fn module_args(&mut self) -> Option> { - self.add_module_arg(); - self.module_args.take() - } - - /// This routine is called after a single SQL statement has been parsed. - fn sqlite3_finish_coding(&mut self) { - self.done = true; - } - - /// Return `true` if parser completes either successfully or with an error. - pub fn done(&self) -> bool { - self.done || self.error.is_some() - } - - pub fn is_ok(&self) -> bool { - self.error.is_none() - } - - /// Consume error generated by parser - pub fn error(&mut self) -> Option { - self.error.take() - } - - pub fn reset(&mut self) { - self.explain = None; - self.stmt = None; - self.constraint_name = None; - self.module_arg = None; - self.module_args = None; - self.done = false; - self.error = None; - } -} diff --git a/vendored/sqlite3-parser/src/parser/parse.y b/vendored/sqlite3-parser/src/parser/parse.y deleted file mode 100644 index d599318c5..000000000 --- a/vendored/sqlite3-parser/src/parser/parse.y +++ /dev/null @@ -1,1503 +0,0 @@ -%include { -/* -** 2001-09-15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains SQLite's SQL parser. -** -** The canonical source code to this file ("parse.y") is a Lemon grammar -** file that specifies the input grammar and actions to take while parsing. -** That input file is processed by Lemon to generate a C-language -** implementation of a parser for the given grammar. You might be reading -** this comment as part of the translated C-code. Edits should be made -** to the original parse.y sources. -*/ -} - -// All token codes are small integers with #defines that begin with "TK_" -%token_prefix TK_ - -// The type of the data attached to each token is Token. This is also the -// default type for non-terminals. -// -%token_type "Token<'i>" -%default_type "Token<'i>" - -// An extra argument to the constructor for the parser, which is available -// to all actions. -%extra_context {ctx: Context} - -// This code runs whenever there is a syntax error -// -%syntax_error { - if TokenType::TK_EOF as YYCODETYPE == yymajor { - trace!(target: TARGET, "incomplete input"); - self.ctx.error = Some(ParserError::UnexpectedEof); - } else { - trace!(target: TARGET, "near \"{yyminor:?}\": syntax error"); - self.ctx.error = Some(ParserError::SyntaxError(from_bytes(yyminor.1))); - } -} - -// The name of the generated procedure that implements the parser -// is as follows: -%name sqlite3Parser - -// The following text is included near the beginning of the C source -// code file that implements the parser. -// -%include { -use crate::custom_err; -use crate::parser::ast::*; -use crate::parser::{Context, ParserError}; -use crate::dialect::{from_bytes, from_token, Token, TokenType}; -use indexmap::IndexMap; -use log::trace; - -#[expect(non_camel_case_types)] -type sqlite3ParserError = crate::parser::ParserError; -} // end %include - -// Input is a single SQL command -input ::= cmdlist. -cmdlist ::= cmdlist ecmd. -cmdlist ::= ecmd. -ecmd ::= SEMI. -ecmd ::= cmdx SEMI. -%ifndef SQLITE_OMIT_EXPLAIN -ecmd ::= explain cmdx SEMI. {NEVER-REDUCE} -explain ::= EXPLAIN. { self.ctx.explain = Some(ExplainKind::Explain); } -explain ::= EXPLAIN QUERY PLAN. { self.ctx.explain = Some(ExplainKind::QueryPlan); } -%endif SQLITE_OMIT_EXPLAIN -cmdx ::= cmd. { self.ctx.sqlite3_finish_coding(); } - -///////////////////// Begin and end transactions. //////////////////////////// -// - -cmd ::= BEGIN transtype(Y) trans_opt(X). {self.ctx.stmt = Some(Stmt::Begin(Y, X));} -%type trans_opt {Option} -trans_opt(A) ::= . {A = None;} -trans_opt(A) ::= TRANSACTION. {A = None;} -trans_opt(A) ::= TRANSACTION nm(X). {A = Some(X);} -%type transtype {Option} -transtype(A) ::= . {A = None;} -transtype(A) ::= DEFERRED. {A = Some(TransactionType::Deferred);} -transtype(A) ::= IMMEDIATE. {A = Some(TransactionType::Immediate);} -transtype(A) ::= EXCLUSIVE. {A = Some(TransactionType::Exclusive);} -cmd ::= COMMIT|END trans_opt(X). {self.ctx.stmt = Some(Stmt::Commit(X));} -cmd ::= ROLLBACK trans_opt(X). {self.ctx.stmt = Some(Stmt::Rollback{tx_name: X, savepoint_name: None});} - -savepoint_opt ::= SAVEPOINT. -savepoint_opt ::= . -cmd ::= SAVEPOINT nm(X). { - self.ctx.stmt = Some(Stmt::Savepoint(X)); -} -cmd ::= RELEASE savepoint_opt nm(X). { - self.ctx.stmt = Some(Stmt::Release(X)); -} -cmd ::= ROLLBACK trans_opt(Y) TO savepoint_opt nm(X). { - self.ctx.stmt = Some(Stmt::Rollback{tx_name: Y, savepoint_name: Some(X)}); -} - -///////////////////// The CREATE TABLE statement //////////////////////////// -// -cmd ::= createkw temp(T) TABLE ifnotexists(E) fullname(Y) create_table_args(X). { - self.ctx.stmt = Some(Stmt::CreateTable{ temporary: T, if_not_exists: E, tbl_name: Y, body: Box::new(X) }); -} -createkw(A) ::= CREATE(A). - -%type ifnotexists {bool} -ifnotexists(A) ::= . {A = false;} -ifnotexists(A) ::= IF NOT EXISTS. {A = true;} -%type temp {bool} -%ifndef SQLITE_OMIT_TEMPDB -temp(A) ::= TEMP. {A = true;} -%endif SQLITE_OMIT_TEMPDB -temp(A) ::= . {A = false;} - -%type create_table_args {CreateTableBody} -create_table_args(A) ::= LP columnlist(C) conslist_opt(X) RP table_option_set(F). { - A = CreateTableBody::columns_and_constraints(C, X, F)?; -} -create_table_args(A) ::= AS select(S). { - A = CreateTableBody::AsSelect(Box::new(S)); -} -%type table_option_set {TableOptions} -%type table_option {TableOptions} -table_option_set(A) ::= . {A = TableOptions::NONE;} -table_option_set(A) ::= table_option(A). -table_option_set(A) ::= table_option_set(X) COMMA table_option(Y). {A = X|Y;} -table_option(A) ::= WITHOUT nm(X). { - let option = X; - if option == "rowid" { - A = TableOptions::WITHOUT_ROWID; - }else{ - return Err(custom_err!("unknown table option: {}", option)); - } -} -table_option(A) ::= nm(X). { - let option = X; - if option == "strict" { - A = TableOptions::STRICT; - }else{ - return Err(custom_err!("unknown table option: {}", option)); - } -} -%type columnlist {IndexMap} -columnlist(A) ::= columnlist(A) COMMA columnname(X) carglist(Y). { - let col = X; - let cd = ColumnDefinition{ col_name: col.0, col_type: col.1, constraints: Y }; - ColumnDefinition::add_column(A, cd)?; -} -columnlist(A) ::= columnname(X) carglist(Y). { - let col = X; - let cd = ColumnDefinition{ col_name: col.0, col_type: col.1, constraints: Y }; - let mut map = IndexMap::new(); - ColumnDefinition::add_column(&mut map, cd)?; - A = map; -} -%type columnname {(Name, Option)} -columnname(A) ::= nm(X) typetoken(Y). {A = (X, Y);} - -// Declare some tokens early in order to influence their values, to -// improve performance and reduce the executable size. The goal here is -// to get the "jump" operations in ISNULL through ESCAPE to have numeric -// values that are early enough so that all jump operations are clustered -// at the beginning. Also, operators like NE and EQ need to be adjacent, -// and all of the comparison operators need to be clustered together. -// Various assert() statements throughout the code enforce these restrictions. -// -%token ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST. -%token CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL. -%token OR AND NOT IS ISNOT MATCH LIKE_KW BETWEEN IN ISNULL NOTNULL NE EQ. -%token GT LE LT GE ESCAPE. - -// The following directive causes tokens ABORT, AFTER, ASC, etc. to -// fallback to ID if they will not parse as their original value. -// This obviates the need for the "id" nonterminal. -// -%fallback ID - ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST COLUMNKW - CONFLICT DATABASE DEFERRED DESC DETACH DO - EACH END EXCLUSIVE EXPLAIN FAIL FOR - IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN - QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW ROWS - ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT - NULLS FIRST LAST -%ifdef SQLITE_OMIT_COMPOUND_SELECT - EXCEPT INTERSECT UNION -%endif SQLITE_OMIT_COMPOUND_SELECT -%ifndef SQLITE_OMIT_WINDOWFUNC - CURRENT FOLLOWING PARTITION PRECEDING RANGE UNBOUNDED - EXCLUDE GROUPS OTHERS TIES -%endif SQLITE_OMIT_WINDOWFUNC -%ifndef SQLITE_OMIT_GENERATED_COLUMNS - GENERATED ALWAYS -%endif - MATERIALIZED - REINDEX RENAME CTIME_KW IF - . -%wildcard ANY. - -// Define operator precedence early so that this is the first occurrence -// of the operator tokens in the grammar. Keeping the operators together -// causes them to be assigned integer values that are close together, -// which keeps parser tables smaller. -// -// The token values assigned to these symbols is determined by the order -// in which lemon first sees them. It must be the case that ISNULL/NOTNULL, -// NE/EQ, GT/LE, and GE/LT are separated by only a single value. See -// the sqlite3ExprIfFalse() routine for additional information on this -// constraint. -// -%left OR. -%left AND. -%right NOT. -%left IS MATCH LIKE_KW BETWEEN IN ISNULL NOTNULL NE EQ. -%left GT LE LT GE. -%right ESCAPE. -%left BITAND BITOR LSHIFT RSHIFT. -%left PLUS MINUS. -%left STAR SLASH REM. -%left CONCAT PTR. -%left COLLATE. -%right BITNOT. -%nonassoc ON. - -// An IDENTIFIER can be a generic identifier, or one of several -// keywords. Any non-standard keyword can also be an identifier. -// -%token_class id ID|INDEXED. - -// And "ids" is an identifier-or-string. -// -%token_class ids ID|STRING. - -// An identifier or a join-keyword -// -%token_class idj ID|INDEXED|JOIN_KW. - -// The name of a column or table can be any of the following: -// -%type nm {Name} -nm(A) ::= idj(X). { A = Name::from_token(@X, X); } -nm(A) ::= STRING(X). { A = Name::from_token(@X, X); } - -// A typetoken is really zero or more tokens that form a type name such -// as can be found after the column name in a CREATE TABLE statement. -// Multiple tokens are concatenated to form the value of the typetoken. -// -%type typetoken {Option} -typetoken(A) ::= . {A = None;} -typetoken(A) ::= typename(X). {A = Some(Type{ name: X, size: None });} -typetoken(A) ::= typename(X) LP signed(Y) RP. { - A = Some(Type{ name: X, size: Some(TypeSize::MaxSize(Box::new(Y))) }); -} -typetoken(A) ::= typename(X) LP signed(Y) COMMA signed(Z) RP. { - A = Some(Type{ name: X, size: Some(TypeSize::TypeSize(Box::new(Y), Box::new(Z))) }); -} -%type typename {String} -typename(A) ::= ids(X). {A=from_token(@X, X);} -typename(A) ::= typename(A) ids(Y). {let ids=from_token(@Y, Y); A.push(' '); A.push_str(&ids);} -%type signed {Expr} -signed ::= plus_num. -signed ::= minus_num. - -// The scanpt non-terminal takes a value which is a pointer to the -// input text just past the last token that has been shifted into -// the parser. By surrounding some phrase in the grammar with two -// scanpt non-terminals, we can capture the input text for that phrase. -// For example: -// -// something ::= .... scanpt(A) phrase scanpt(Z). -// -// The text that is parsed as "phrase" is a string starting at A -// and containing (int)(Z-A) characters. There might be some extra -// whitespace on either end of the text, but that can be removed in -// post-processing, if needed. -// - -// "carglist" is a list of additional constraints that come after the -// column name and column type in a CREATE TABLE statement. -// -%type carglist {Vec} -carglist(A) ::= carglist(A) ccons(X). {if self.ctx.no_constraint_name() { let cc = X; A.push(cc); }} -carglist(A) ::= . {A = vec![];} -%type ccons {NamedColumnConstraint} -ccons ::= CONSTRAINT nm(X). { self.ctx.constraint_name = Some(X);} -ccons(A) ::= DEFAULT term(X). { - let name = self.ctx.constraint_name(); - let constraint = ColumnConstraint::Default(X); - A = NamedColumnConstraint{ name, constraint }; -} -ccons(A) ::= DEFAULT LP expr(X) RP. { - let name = self.ctx.constraint_name(); - let constraint = ColumnConstraint::Default(Expr::parenthesized(X)); - A = NamedColumnConstraint{ name, constraint }; -} -ccons(A) ::= DEFAULT PLUS term(X). { - let name = self.ctx.constraint_name(); - let constraint = ColumnConstraint::Default(Expr::Unary(UnaryOperator::Positive, Box::new(X))); - A = NamedColumnConstraint{ name, constraint }; -} -ccons(A) ::= DEFAULT MINUS term(X). { - let name = self.ctx.constraint_name(); - let constraint = ColumnConstraint::Default(Expr::Unary(UnaryOperator::Negative, Box::new(X))); - A = NamedColumnConstraint{ name, constraint }; -} -ccons(A) ::= DEFAULT id(X). { - let name = self.ctx.constraint_name(); - let constraint = ColumnConstraint::Default(Expr::id(@X, X)); - A = NamedColumnConstraint{ name, constraint }; -} - -// In addition to the type name, we also care about the primary key and -// UNIQUE constraints. -// -ccons(A) ::= NULL onconf(R). { - let name = self.ctx.constraint_name(); - let constraint = ColumnConstraint::NotNull{ nullable: true, conflict_clause: R}; - A = NamedColumnConstraint{ name, constraint }; -} -ccons(A) ::= NOT NULL onconf(R). { - let name = self.ctx.constraint_name(); - let constraint = ColumnConstraint::NotNull{ nullable: false, conflict_clause: R}; - A = NamedColumnConstraint{ name, constraint }; -} -ccons(A) ::= PRIMARY KEY sortorder(Z) onconf(R) autoinc(I). { - let name = self.ctx.constraint_name(); - let constraint = ColumnConstraint::PrimaryKey{ order: Z, conflict_clause: R, auto_increment: I }; - A = NamedColumnConstraint{ name, constraint }; -} -ccons(A) ::= UNIQUE onconf(R). { - let name = self.ctx.constraint_name(); - let constraint = ColumnConstraint::Unique(R); - A = NamedColumnConstraint{ name, constraint }; -} -ccons(A) ::= CHECK LP expr(X) RP. { - let name = self.ctx.constraint_name(); - let constraint = ColumnConstraint::Check(X); - A = NamedColumnConstraint{ name, constraint }; -} -ccons(A) ::= REFERENCES nm(T) eidlist_opt(TA) refargs(R). { - let name = self.ctx.constraint_name(); - let clause = ForeignKeyClause{ tbl_name: T, columns: TA, args: R }; - let constraint = ColumnConstraint::ForeignKey{ clause, deref_clause: None }; // FIXME deref_clause - A = NamedColumnConstraint{ name, constraint }; -} -ccons(A) ::= defer_subclause(D). { - let constraint = ColumnConstraint::Defer(D); - A = NamedColumnConstraint{ name: None, constraint }; -} -ccons(A) ::= COLLATE ids(C). { - let name = self.ctx.constraint_name(); - let constraint = ColumnConstraint::Collate{ collation_name: Name::from_token(@C, C) }; - A = NamedColumnConstraint{ name, constraint }; -} -ccons(A) ::= GENERATED ALWAYS AS generated(X). { - let name = self.ctx.constraint_name(); - let constraint = X; - A = NamedColumnConstraint{ name, constraint }; -} -ccons(A) ::= AS generated(X). { - let name = self.ctx.constraint_name(); - let constraint = X; - A = NamedColumnConstraint{ name, constraint }; -} -%type generated {ColumnConstraint} -generated(X) ::= LP expr(E) RP. { - X = ColumnConstraint::Generated{ expr: E, typ: None }; -} -generated(X) ::= LP expr(E) RP ID(TYPE). { - X = ColumnConstraint::Generated{ expr: E, typ: Some(Name::from_token(@TYPE, TYPE)) }; -} - -// The optional AUTOINCREMENT keyword -%type autoinc {bool} -autoinc(X) ::= . {X = false;} -autoinc(X) ::= AUTOINCR. {X = true;} - -// The next group of rules parses the arguments to a REFERENCES clause -// that determine if the referential integrity checking is deferred or -// or immediate and which determine what action to take if a ref-integ -// check fails. -// -%type refargs {Vec} -refargs(A) ::= . { A = vec![]; /* EV: R-19803-45884 */} -refargs(A) ::= refargs(A) refarg(Y). { let ra = Y; A.push(ra); } -%type refarg {RefArg} -refarg(A) ::= MATCH nm(X). { A = RefArg::Match(X); } -refarg(A) ::= ON INSERT refact(X). { A = RefArg::OnInsert(X); } -refarg(A) ::= ON DELETE refact(X). { A = RefArg::OnDelete(X); } -refarg(A) ::= ON UPDATE refact(X). { A = RefArg::OnUpdate(X); } -%type refact {RefAct} -refact(A) ::= SET NULL. { A = RefAct::SetNull; /* EV: R-33326-45252 */} -refact(A) ::= SET DEFAULT. { A = RefAct::SetDefault; /* EV: R-33326-45252 */} -refact(A) ::= CASCADE. { A = RefAct::Cascade; /* EV: R-33326-45252 */} -refact(A) ::= RESTRICT. { A = RefAct::Restrict; /* EV: R-33326-45252 */} -refact(A) ::= NO ACTION. { A = RefAct::NoAction; /* EV: R-33326-45252 */} -%type defer_subclause {DeferSubclause} -defer_subclause(A) ::= NOT DEFERRABLE init_deferred_pred_opt(X). {A = DeferSubclause{ deferrable: false, init_deferred: X };} -defer_subclause(A) ::= DEFERRABLE init_deferred_pred_opt(X). {A = DeferSubclause{ deferrable: true, init_deferred: X };} -%type init_deferred_pred_opt {Option} -init_deferred_pred_opt(A) ::= . {A = None;} -init_deferred_pred_opt(A) ::= INITIALLY DEFERRED. {A = Some(InitDeferredPred::InitiallyDeferred);} -init_deferred_pred_opt(A) ::= INITIALLY IMMEDIATE. {A = Some(InitDeferredPred::InitiallyImmediate);} - -%type conslist_opt {Option>} -conslist_opt(A) ::= . {A = None;} -conslist_opt(A) ::= COMMA conslist(X). {A = Some(X);} -%type conslist {Vec} -conslist(A) ::= conslist(A) tconscomma tcons(X). {if self.ctx.no_constraint_name() { let tc = X; A.push(tc); }} -conslist(A) ::= tcons(X). {if self.ctx.no_constraint_name() { let tc = X; A = vec![tc]; } else { A = vec![]; }} -tconscomma ::= COMMA. { self.ctx.constraint_name = None;} // TODO Validate: useful ? -tconscomma ::= . -%type tcons {NamedTableConstraint} -tcons ::= CONSTRAINT nm(X). { self.ctx.constraint_name = Some(X)} -tcons(A) ::= PRIMARY KEY LP sortlist(X) autoinc(I) RP onconf(R). { - let name = self.ctx.constraint_name(); - let constraint = TableConstraint::PrimaryKey{ columns: X, auto_increment: I, conflict_clause: R }; - A = NamedTableConstraint{ name, constraint }; -} -tcons(A) ::= UNIQUE LP sortlist(X) RP onconf(R). { - let name = self.ctx.constraint_name(); - let constraint = TableConstraint::Unique{ columns: X, conflict_clause: R }; - A = NamedTableConstraint{ name, constraint }; -} -tcons(A) ::= CHECK LP expr(E) RP onconf. { - let name = self.ctx.constraint_name(); - let constraint = TableConstraint::Check(E); - A = NamedTableConstraint{ name, constraint }; -} -tcons(A) ::= FOREIGN KEY LP eidlist(FA) RP - REFERENCES nm(T) eidlist_opt(TA) refargs(R) defer_subclause_opt(D). { - let name = self.ctx.constraint_name(); - let clause = ForeignKeyClause{ tbl_name: T, columns: TA, args: R }; - let constraint = TableConstraint::ForeignKey{ columns: FA, clause, deref_clause: D }; - A = NamedTableConstraint{ name, constraint }; -} -%type defer_subclause_opt {Option} -defer_subclause_opt(A) ::= . {A = None;} -defer_subclause_opt(A) ::= defer_subclause(X). {A = Some(X);} - -// The following is a non-standard extension that allows us to declare the -// default behavior when there is a constraint conflict. -// -%type onconf {Option} -%type orconf {Option} -%type resolvetype {ResolveType} -onconf(A) ::= . {A = None;} -onconf(A) ::= ON CONFLICT resolvetype(X). {A = Some(X);} -orconf(A) ::= . {A = None;} -orconf(A) ::= OR resolvetype(X). {A = Some(X);} -resolvetype(A) ::= raisetype(A). -resolvetype(A) ::= IGNORE. {A = ResolveType::Ignore;} -resolvetype(A) ::= REPLACE. {A = ResolveType::Replace;} - -////////////////////////// The DROP TABLE ///////////////////////////////////// -// -cmd ::= DROP TABLE ifexists(E) fullname(X). { - self.ctx.stmt = Some(Stmt::DropTable{ if_exists: E, tbl_name: X}); -} -%type ifexists {bool} -ifexists(A) ::= IF EXISTS. {A = true;} -ifexists(A) ::= . {A = false;} - -///////////////////// The CREATE VIEW statement ///////////////////////////// -// -%ifndef SQLITE_OMIT_VIEW -cmd ::= createkw temp(T) VIEW ifnotexists(E) fullname(Y) eidlist_opt(C) - AS select(S). { - self.ctx.stmt = Some(Stmt::CreateView{ temporary: T, if_not_exists: E, view_name: Y, columns: C, - select: Box::new(S) }); -} -cmd ::= createkw MATERIALIZED VIEW ifnotexists(E) fullname(Y) eidlist_opt(C) - AS select(S). { - self.ctx.stmt = Some(Stmt::CreateMaterializedView{ if_not_exists: E, view_name: Y, columns: C, - select: Box::new(S) }); -} -cmd ::= DROP VIEW ifexists(E) fullname(X). { - self.ctx.stmt = Some(Stmt::DropView{ if_exists: E, view_name: X }); -} -%endif SQLITE_OMIT_VIEW - -//////////////////////// The SELECT statement ///////////////////////////////// -// -cmd ::= select(X). { - self.ctx.stmt = Some(Stmt::Select(Box::new(X))); -} - -%type select {Select} -%type selectnowith {SelectBody} -%type oneselect {OneSelect} - -%include { -} - -%ifndef SQLITE_OMIT_CTE -select(A) ::= WITH wqlist(W) selectnowith(X) orderby_opt(Z) limit_opt(L). { - A = Select{ with: Some(With { recursive: false, ctes: W }), body: X, order_by: Z, limit: L }; -} -select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X) orderby_opt(Z) limit_opt(L). { - A = Select{ with: Some(With { recursive: true, ctes: W }), body: X, order_by: Z, limit: L }; -} -%endif /* SQLITE_OMIT_CTE */ -select(A) ::= selectnowith(X) orderby_opt(Z) limit_opt(L). { - A = Select{ with: None, body: X, order_by: Z, limit: L }; /*A-overwrites-X*/ -} - -selectnowith(A) ::= oneselect(X). { - A = SelectBody{ select: Box::new(X), compounds: None }; -} -%ifndef SQLITE_OMIT_COMPOUND_SELECT -selectnowith(A) ::= selectnowith(A) multiselect_op(Y) oneselect(Z). { - let cs = CompoundSelect{ operator: Y, select: Box::new(Z) }; - A.push(cs)?; -} -%type multiselect_op {CompoundOperator} -multiselect_op(A) ::= UNION. {A = CompoundOperator::Union;} -multiselect_op(A) ::= UNION ALL. {A = CompoundOperator::UnionAll;} -multiselect_op(A) ::= EXCEPT. {A = CompoundOperator::Except;} -multiselect_op(A) ::= INTERSECT. {A = CompoundOperator::Intersect;} -%endif SQLITE_OMIT_COMPOUND_SELECT - -oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y) - groupby_opt(P). { - A = OneSelect::Select(Box::new(SelectInner{ distinctness: D, columns: W, from: X, where_clause: Y, - group_by: P, window_clause: None })); - } -%ifndef SQLITE_OMIT_WINDOWFUNC -oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y) - groupby_opt(P) window_clause(R). { - A = OneSelect::Select(Box::new(SelectInner{ distinctness: D, columns: W, from: X, where_clause: Y, - group_by: P, window_clause: Some(R) })); -} -%endif - - -oneselect(A) ::= values(X). { A = OneSelect::Values(X); } - -%type values {Vec>} -values(A) ::= VALUES LP nexprlist(X) RP. { - A = vec![X]; -} -values(A) ::= values(A) COMMA LP nexprlist(Y) RP. { - let exprs = Y; - OneSelect::push(A, exprs)?; -} - -// The "distinct" nonterminal is true (1) if the DISTINCT keyword is -// present and false (0) if it is not. -// -%type distinct {Option} -distinct(A) ::= DISTINCT. {A = Some(Distinctness::Distinct);} -distinct(A) ::= ALL. {A = Some(Distinctness::All);} -distinct(A) ::= . {A = None;} - -// selcollist is a list of expressions that are to become the return -// values of the SELECT statement. The "*" in statements like -// "SELECT * FROM ..." is encoded as a special expression with an -// opcode of TK_ASTERISK. -// -%type selcollist {Vec} -%type sclp {Vec} -sclp(A) ::= selcollist(A) COMMA. -sclp(A) ::= . {A = Vec::::new();} -selcollist(A) ::= sclp(A) expr(X) as(Y). { - let rc = ResultColumn::Expr(X, Y); - A.push(rc); -} -selcollist(A) ::= sclp(A) STAR. { - let rc = ResultColumn::Star; - A.push(rc); -} -selcollist(A) ::= sclp(A) nm(X) DOT STAR. { - let rc = ResultColumn::TableStar(X); - A.push(rc); -} - -// An option "AS " phrase that can follow one of the expressions that -// define the result set, or one of the tables in the FROM clause. -// -%type as {Option} -as(X) ::= AS nm(Y). {X = Some(As::As(Y));} -as(X) ::= ids(Y). {X = Some(As::Elided(Name::from_token(@Y, Y)));} -as(X) ::= . {X = None;} - - -%type seltablist {FromClause} -%type stl_prefix {FromClause} -%type from {Option} - -// A complete FROM clause. -// -from(A) ::= . {A = None;} -from(A) ::= FROM seltablist(X). { - A = Some(X); -} - -// "seltablist" is a "Select Table List" - the content of the FROM clause -// in a SELECT statement. "stl_prefix" is a prefix of this list. -// -stl_prefix(A) ::= seltablist(A) joinop(Y). { - let op = Y; - A.push_op(op); -} -stl_prefix(A) ::= . {A = FromClause::empty();} -seltablist(A) ::= stl_prefix(A) fullname(Y) as(Z) indexed_opt(I) - on_using(N). { - let st = SelectTable::Table(Y, Z, I); - let jc = N; - A.push(st, jc)?; -} -seltablist(A) ::= stl_prefix(A) fullname(Y) LP exprlist(E) RP as(Z) - on_using(N). { - let st = SelectTable::TableCall(Y, E, Z); - let jc = N; - A.push(st, jc)?; -} -%ifndef SQLITE_OMIT_SUBQUERY - seltablist(A) ::= stl_prefix(A) LP select(S) RP - as(Z) on_using(N). { - let st = SelectTable::Select(Box::new(S), Z); - let jc = N; - A.push(st, jc)?; - } - seltablist(A) ::= stl_prefix(A) LP seltablist(F) RP - as(Z) on_using(N). { - let st = SelectTable::Sub(F, Z); - let jc = N; - A.push(st, jc)?; - } -%endif SQLITE_OMIT_SUBQUERY - -%type fullname {QualifiedName} -fullname(A) ::= nm(X). { - A = QualifiedName::single(X); -} -fullname(A) ::= nm(X) DOT nm(Y). { - A = QualifiedName::fullname(X, Y); -} - -%type xfullname {QualifiedName} -xfullname(A) ::= nm(X). - {A = QualifiedName::single(X); /*A-overwrites-X*/} -xfullname(A) ::= nm(X) DOT nm(Y). - {A = QualifiedName::fullname(X, Y); /*A-overwrites-X*/} -xfullname(A) ::= nm(X) DOT nm(Y) AS nm(Z). { - A = QualifiedName::xfullname(X, Y, Z); /*A-overwrites-X*/ -} -xfullname(A) ::= nm(X) AS nm(Z). { - A = QualifiedName::alias(X, Z); /*A-overwrites-X*/ -} - -%type joinop {JoinOperator} -joinop(X) ::= COMMA. { X = JoinOperator::Comma; } -joinop(X) ::= JOIN. { X = JoinOperator::TypedJoin(None); } -joinop(X) ::= JOIN_KW(A) JOIN. - {X = JoinOperator::from(A, None, None)?; /*X-overwrites-A*/} -joinop(X) ::= JOIN_KW(A) nm(B) JOIN. - {X = JoinOperator::from(A, Some(B), None)?; /*X-overwrites-A*/} -joinop(X) ::= JOIN_KW(A) nm(B) nm(C) JOIN. - {X = JoinOperator::from(A, Some(B), Some(C))?;/*X-overwrites-A*/} - -// There is a parsing ambiguity in an upsert statement that uses a -// SELECT on the RHS of a the INSERT: -// -// INSERT INTO tab SELECT * FROM aaa JOIN bbb ON CONFLICT ... -// here ----^^ -// -// When the ON token is encountered, the parser does not know if it is -// the beginning of an ON CONFLICT clause, or the beginning of an ON -// clause associated with the JOIN. The conflict is resolved in favor -// of the JOIN. If an ON CONFLICT clause is intended, insert a dummy -// WHERE clause in between, like this: -// -// INSERT INTO tab SELECT * FROM aaa JOIN bbb WHERE true ON CONFLICT ... -// -// The [AND] and [OR] precedence marks in the rules for on_using cause the -// ON in this context to always be interpreted as belonging to the JOIN. -// -%type on_using {Option} -on_using(N) ::= ON expr(E). {N = Some(JoinConstraint::On(E));} -on_using(N) ::= USING LP idlist(L) RP. {N = Some(JoinConstraint::Using(L));} -on_using(N) ::= . [OR] {N = None;} - -// Note that this block abuses the Token type just a little. If there is -// no "INDEXED BY" clause, the returned token is empty (z==0 && n==0). If -// there is an INDEXED BY clause, then the token is populated as per normal, -// with z pointing to the token data and n containing the number of bytes -// in the token. -// -// If there is a "NOT INDEXED" clause, then (z==0 && n==1), which is -// normally illegal. The sqlite3SrcListIndexedBy() function -// recognizes and interprets this as a special case. -// -%type indexed_opt {Option} -indexed_opt(A) ::= . {A = None;} -indexed_opt(A) ::= INDEXED BY nm(X). {A = Some(Indexed::IndexedBy(X));} -indexed_opt(A) ::= NOT INDEXED. {A = Some(Indexed::NotIndexed);} - -%type orderby_opt {Option>} - -// the sortlist non-terminal stores a list of expression where each -// expression is optionally followed by ASC or DESC to indicate the -// sort order. -// -%type sortlist {Vec} - -orderby_opt(A) ::= . {A = None;} -orderby_opt(A) ::= ORDER BY sortlist(X). {A = Some(X);} -sortlist(A) ::= sortlist(A) COMMA expr(Y) sortorder(Z) nulls(X). { - let sc = SortedColumn { expr: Y, order: Z, nulls: X }; - A.push(sc); -} -sortlist(A) ::= expr(Y) sortorder(Z) nulls(X). { - A = vec![SortedColumn { expr: Y, order: Z, nulls: X }]; /*A-overwrites-Y*/ -} - -%type sortorder {Option} - -sortorder(A) ::= ASC. {A = Some(SortOrder::Asc);} -sortorder(A) ::= DESC. {A = Some(SortOrder::Desc);} -sortorder(A) ::= . {A = None;} - -%type nulls {Option} -nulls(A) ::= NULLS FIRST. {A = Some(NullsOrder::First);} -nulls(A) ::= NULLS LAST. {A = Some(NullsOrder::Last);} -nulls(A) ::= . {A = None;} - -%type groupby_opt {Option} -groupby_opt(A) ::= . {A = None;} -groupby_opt(A) ::= GROUP BY nexprlist(X) having_opt(Y). {A = Some(GroupBy{ exprs: X, having: Y.map(Box::new) });} - -%type having_opt {Option} -having_opt(A) ::= . {A = None;} -having_opt(A) ::= HAVING expr(X). {A = Some(X);} - -%type limit_opt {Option>} - -// The destructor for limit_opt will never fire in the current grammar. -// The limit_opt non-terminal only occurs at the end of a single production -// rule for SELECT statements. As soon as the rule that create the -// limit_opt non-terminal reduces, the SELECT statement rule will also -// reduce. So there is never a limit_opt non-terminal on the stack -// except as a transient. So there is never anything to destroy. -// -//%destructor limit_opt {sqlite3ExprDelete(pParse->db, $$);} -limit_opt(A) ::= . {A = None;} -limit_opt(A) ::= LIMIT expr(X). - {A = Some(Box::new(Limit{ expr: X, offset: None }));} -limit_opt(A) ::= LIMIT expr(X) OFFSET expr(Y). - {A = Some(Box::new(Limit{ expr: X, offset: Some(Y) }));} -limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y). - {A = Some(Box::new(Limit{ expr: X, offset: Some(Y) }));} - -/////////////////////////// The DELETE statement ///////////////////////////// -// -%if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER -cmd ::= with(C) DELETE FROM xfullname(X) indexed_opt(I) where_opt_ret(W) - orderby_opt(O) limit_opt(L). { - let (where_clause, returning) = W; - self.ctx.stmt = Some(Stmt::Delete(Box::new(Delete{ with: C, tbl_name: X, indexed: I, where_clause: where_clause.map(Box::new), returning, - order_by: O, limit: L }))); -} -%else -cmd ::= with(C) DELETE FROM xfullname(X) indexed_opt(I) where_opt_ret(W). { - let (where_clause, returning) = W; - self.ctx.stmt = Some(Stmt::Delete(Box::new(Delete{ with: C, tbl_name: X, indexed: I, where_clause: where_clause.map(Box::new), returning, - order_by: None, limit: None }))); -} -%endif - -%type where_opt {Option} -%type where_opt_ret {(Option, Option>)} - -where_opt(A) ::= . {A = None;} -where_opt(A) ::= WHERE expr(X). {A = Some(X);} -where_opt_ret(A) ::= . {A = (None, None);} -where_opt_ret(A) ::= WHERE expr(X). {A = (Some(X), None);} -where_opt_ret(A) ::= RETURNING selcollist(X). - {A = (None, Some(X));} -where_opt_ret(A) ::= WHERE expr(X) RETURNING selcollist(Y). - {A = (Some(X), Some(Y));} - -////////////////////////// The UPDATE command //////////////////////////////// -// -%if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER -cmd ::= with(C) UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F) - where_opt_ret(W) orderby_opt(O) limit_opt(L). { - let (where_clause, returning) = W; - self.ctx.stmt = Some(Stmt::Update(Box::new(Update{ with: C, or_conflict: R, tbl_name: X, indexed: I, sets: Y, from: F, - where_clause: where_clause.map(Box::new), returning, order_by: O, limit: L }))); -} -%else -cmd ::= with(C) UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F) - where_opt_ret(W). { - let (where_clause, returning) = W; - self.ctx.stmt = Some(Stmt::Update(Box::new(Update{ with: C, or_conflict: R, tbl_name: X, indexed: I, sets: Y, from: F, - where_clause: where_clause.map(Box::new), returning, order_by: None, limit: None }))); -} -%endif - -%type reidlist {Names} - -reidlist(A) ::= reidlist(A) COMMA nm(Y). - {let id = Y; A.insert(id)?;} -reidlist(A) ::= nm(Y). - { A = Names::new(Y); /*A-overwrites-Y*/} - -%type setlist {Vec} - -setlist(A) ::= setlist(A) COMMA nm(X) EQ expr(Y). { - let s = Set{ col_names: Names::single(X), expr: Y }; - A.push(s); -} -setlist(A) ::= setlist(A) COMMA LP reidlist(X) RP EQ expr(Y). { - let s = Set{ col_names: X, expr: Y }; - A.push(s); -} -setlist(A) ::= nm(X) EQ expr(Y). { - A = vec![Set{ col_names: Names::single(X), expr: Y }]; - -} -setlist(A) ::= LP reidlist(X) RP EQ expr(Y). { - A = vec![Set{ col_names: X, expr: Y }]; -} - -////////////////////////// The INSERT command ///////////////////////////////// -// -cmd ::= with(W) insert_cmd(R) INTO xfullname(X) idlist_opt(F) select(S) - upsert(U). { - let (upsert, returning) = U; - let body = InsertBody::Select(Box::new(S), upsert); - self.ctx.stmt = Some(Stmt::Insert(Box::new(Insert{ with: W, or_conflict: R, tbl_name: X, columns: F, - body, returning }))); -} -cmd ::= with(W) insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES returning(Y). -{ - let body = InsertBody::DefaultValues; - self.ctx.stmt = Some(Stmt::Insert(Box::new(Insert{ with: W, or_conflict: R, tbl_name: X, columns: F, - body, returning: Y }))); -} - -%type upsert {(Option, Option>)} - -// Because upsert only occurs at the tip end of the INSERT rule for cmd, -// there is never a case where the value of the upsert pointer will not -// be destroyed by the cmd action. So comment-out the destructor to -// avoid unreachable code. -//%destructor upsert {sqlite3UpsertDelete(pParse->db,$$);} -upsert(A) ::= . { A = (None, None); } -upsert(A) ::= RETURNING selcollist(X). { A = (None, Some(X)); } -upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) - DO UPDATE SET setlist(Z) where_opt(W) upsert(N). - { let index = UpsertIndex{ targets: T, where_clause: TW }; - let do_clause = UpsertDo::Set{ sets: Z, where_clause: W }; - let (next, returning) = N; - A = (Some(Upsert{ index: Some(Box::new(index)), do_clause: Box::new(do_clause), next: next.map(Box::new) }), returning);} -upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) DO NOTHING upsert(N). - { let index = UpsertIndex{ targets: T, where_clause: TW }; - let (next, returning) = N; - A = (Some(Upsert{ index: Some(Box::new(index)), do_clause: Box::new(UpsertDo::Nothing), next: next.map(Box::new) }), returning); } -upsert(A) ::= ON CONFLICT DO NOTHING returning(R). - { A = (Some(Upsert{ index: None, do_clause: Box::new(UpsertDo::Nothing), next: None }), R); } -upsert(A) ::= ON CONFLICT DO UPDATE SET setlist(Z) where_opt(W) returning(R). - { let do_clause = UpsertDo::Set{ sets: Z, where_clause: W }; - A = (Some(Upsert{ index: None, do_clause: Box::new(do_clause), next: None }), R);} - -%type returning {Option>} -returning(A) ::= RETURNING selcollist(X). {A = Some(X);} -returning(A) ::= . {A = None;} - -%type insert_cmd {Option} -insert_cmd(A) ::= INSERT orconf(R). {A = R;} -insert_cmd(A) ::= REPLACE. {A = Some(ResolveType::Replace);} - -%type idlist_opt {Option} -%type idlist {DistinctNames} -idlist_opt(A) ::= . {A = None;} -idlist_opt(A) ::= LP idlist(X) RP. {A = Some(X);} -idlist(A) ::= idlist(A) COMMA nm(Y). - {let id = Y; A.insert(id)?;} -idlist(A) ::= nm(Y). - { A = DistinctNames::new(Y); /*A-overwrites-Y*/} - -/////////////////////////// Expression Processing ///////////////////////////// -// - -%type expr {Expr} -%type term {Expr} - -%include { -} - -expr(A) ::= term(A). -expr(A) ::= LP expr(X) RP. {A = Expr::parenthesized(X);} -expr(A) ::= idj(X). {A= Expr::id(@X, X); /*A-overwrites-X*/} -expr(A) ::= nm(X) DOT nm(Y). { - A = Expr::Qualified(X, Y); /*A-overwrites-X*/ -} -expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). { - A = Expr::DoublyQualified(X, Y, Z); /*A-overwrites-X*/ -} -term(A) ::= NULL. {A=Expr::Literal(Literal::Null);} -term(A) ::= BLOB(X). {A=Expr::Literal(Literal::Blob(X.unwrap())); /*A-overwrites-X*/} -term(A) ::= STRING(X). {A=Expr::Literal(Literal::String(X.unwrap())); /*A-overwrites-X*/} -term(A) ::= FLOAT|INTEGER(X). { - A = Expr::Literal(Literal::Numeric(X.unwrap())); /*A-overwrites-X*/ -} -expr(A) ::= VARIABLE(X). { - A = Expr::Variable(X.unwrap()); /*A-overwrites-X*/ -} -expr(A) ::= expr(X) COLLATE ids(C). { - A = Expr::collate(X, @C, C); /*A-overwrites-X*/ -} -%ifndef SQLITE_OMIT_CAST -expr(A) ::= CAST LP expr(E) AS typetoken(T) RP. { - A = Expr::cast(E, T); -} -%endif SQLITE_OMIT_CAST - -expr(A) ::= idj(X) LP distinct(D) exprlist(Y) RP. { - A = Expr::FunctionCall{ name: Name::from_token(@X, X), distinctness: D, args: Y, order_by: None, filter_over: None }; /*A-overwrites-X*/ -} -expr(A) ::= idj(X) LP distinct(D) exprlist(Y) ORDER BY sortlist(O) RP. { - A = Expr::FunctionCall{ name: Name::from_token(@X, X), distinctness: D, args: Y, order_by: Some(O), filter_over: None }; /*A-overwrites-X*/ -} -expr(A) ::= idj(X) LP STAR RP. { - A = Expr::FunctionCallStar{ name: Name::from_token(@X, X), filter_over: None }; /*A-overwrites-X*/ -} - -%ifndef SQLITE_OMIT_WINDOWFUNC -expr(A) ::= idj(X) LP distinct(D) exprlist(Y) RP filter_over(Z). { - A = Expr::FunctionCall{ name: Name::from_token(@X, X), distinctness: D, args: Y, order_by: None, filter_over: Some(Z) }; /*A-overwrites-X*/ -} -expr(A) ::= idj(X) LP distinct(D) exprlist(Y) ORDER BY sortlist(O) RP filter_over(Z). { - A = Expr::FunctionCall{ name: Name::from_token(@X, X), distinctness: D, args: Y, order_by: Some(O), filter_over: Some(Z) }; /*A-overwrites-X*/ -} -expr(A) ::= idj(X) LP STAR RP filter_over(Z). { - A = Expr::FunctionCallStar{ name: Name::from_token(@X, X), filter_over: Some(Z) }; /*A-overwrites-X*/ -} -%endif - -term(A) ::= CTIME_KW(OP). { - A = Expr::Literal(Literal::from_ctime_kw(OP)); -} - -expr(A) ::= LP nexprlist(X) COMMA expr(Y) RP. { - let mut x = X; - x.push(Y); - A = Expr::Parenthesized(x); -} - -expr(A) ::= expr(X) AND(OP) expr(Y). {A=Expr::binary(X,@OP,Y); /*A-overwrites-X*/} -expr(A) ::= expr(X) OR(OP) expr(Y). {A=Expr::binary(X,@OP,Y); /*A-overwrites-X*/} -expr(A) ::= expr(X) LT|GT|GE|LE(OP) expr(Y). - {A=Expr::binary(X,@OP,Y); /*A-overwrites-X*/} -expr(A) ::= expr(X) EQ|NE(OP) expr(Y). {A=Expr::binary(X,@OP,Y); /*A-overwrites-X*/} -expr(A) ::= expr(X) BITAND|BITOR|LSHIFT|RSHIFT(OP) expr(Y). - {A=Expr::binary(X,@OP,Y); /*A-overwrites-X*/} -expr(A) ::= expr(X) PLUS|MINUS(OP) expr(Y). - {A=Expr::binary(X,@OP,Y); /*A-overwrites-X*/} -expr(A) ::= expr(X) STAR|SLASH|REM(OP) expr(Y). - {A=Expr::binary(X,@OP,Y); /*A-overwrites-X*/} -expr(A) ::= expr(X) CONCAT(OP) expr(Y). {A=Expr::binary(X,@OP,Y); /*A-overwrites-X*/} -%type likeop {(bool, LikeOperator)} -likeop(A) ::= LIKE_KW|MATCH(X). {A=(false, LikeOperator::from_token(@X, X)); /*A-overwrite-X*/} -likeop(A) ::= NOT LIKE_KW|MATCH(X). {A=(true, LikeOperator::from_token(@X, X)); /*A-overwrite-X*/} -expr(A) ::= expr(X) likeop(OP) expr(Y). [LIKE_KW] { - let op = OP; - A=Expr::like(X,op.0,op.1,Y,None); /*A-overwrites-X*/ -} -expr(A) ::= expr(X) likeop(OP) expr(Y) ESCAPE expr(E). [LIKE_KW] { - let op = OP; - A=Expr::like(X,op.0,op.1,Y,Some(E)); /*A-overwrites-X*/ -} - -expr(A) ::= expr(X) ISNULL|NOTNULL(E). {A = Expr::not_null(X, @E); /*A-overwrites-X*/} -expr(A) ::= expr(X) NOT NULL. {A = Expr::not_null(X, TokenType::TK_NOTNULL as YYCODETYPE); /*A-overwrites-X*/} - -%include { -} - -// expr1 IS expr2 -// expr1 IS NOT expr2 -// -// If expr2 is NULL then code as TK_ISNULL or TK_NOTNULL. If expr2 -// is any other expression, code as TK_IS or TK_ISNOT. -// -expr(A) ::= expr(X) IS(OP) expr(Y). { - A = Expr::binary(X, @OP, Y); /*A-overwrites-X*/ -} -expr(A) ::= expr(X) IS NOT expr(Y). { - A = Expr::binary(X, TokenType::TK_NOT as YYCODETYPE, Y); /*A-overwrites-X*/ -} -expr(A) ::= expr(X) IS NOT DISTINCT FROM expr(Y). { - A = Expr::binary(X, TokenType::TK_IS as YYCODETYPE, Y); /*A-overwrites-X*/ -} -expr(A) ::= expr(X) IS DISTINCT FROM expr(Y). { - A = Expr::binary(X, TokenType::TK_NOT as YYCODETYPE, Y); /*A-overwrites-X*/ -} - -expr(A) ::= NOT(B) expr(X). - {A = Expr::unary(UnaryOperator::from(@B), X);/*A-overwrites-B*/} -expr(A) ::= BITNOT(B) expr(X). - {A = Expr::unary(UnaryOperator::from(@B), X);/*A-overwrites-B*/} -expr(A) ::= PLUS|MINUS(B) expr(X). [BITNOT] { - A = Expr::unary(UnaryOperator::from(@B), X);/*A-overwrites-B*/ -} - -expr(A) ::= expr(B) PTR(C) expr(D). { - A = Expr::ptr(B, C, D); -} - -%type between_op {bool} -between_op(A) ::= BETWEEN. {A = false;} -between_op(A) ::= NOT BETWEEN. {A = true;} -expr(A) ::= expr(B) between_op(N) expr(X) AND expr(Y). [BETWEEN] { - A = Expr::between(B, N, X, Y);/*A-overwrites-B*/ -} -%ifndef SQLITE_OMIT_SUBQUERY - %type in_op {bool} - in_op(A) ::= IN. {A = false;} - in_op(A) ::= NOT IN. {A = true;} - expr(A) ::= expr(X) in_op(N) LP exprlist(Y) RP. [IN] { - A = Expr::in_list(X, N, Y);/*A-overwrites-X*/ - } - expr(A) ::= LP select(X) RP. { - A = Expr::sub_query(X); - } - expr(A) ::= expr(X) in_op(N) LP select(Y) RP. [IN] { - A = Expr::in_select(X, N, Y);/*A-overwrites-X*/ - } - expr(A) ::= expr(X) in_op(N) fullname(Y) paren_exprlist(E). [IN] { - A = Expr::in_table(X, N, Y, E);/*A-overwrites-X*/ - } - expr(A) ::= EXISTS LP select(Y) RP. { - A = Expr::Exists(Box::new(Y)); - } -%endif SQLITE_OMIT_SUBQUERY - -/* CASE expressions */ -expr(A) ::= CASE case_operand(X) case_exprlist(Y) case_else(Z) END. { - A = Expr::Case{ base: X.map(Box::new), when_then_pairs: Y, else_expr: Z.map(Box::new)}; -} -%type case_exprlist {Vec<(Expr, Expr)>} -case_exprlist(A) ::= case_exprlist(A) WHEN expr(Y) THEN expr(Z). { - let pair = (Y, Z); - A.push(pair); -} -case_exprlist(A) ::= WHEN expr(Y) THEN expr(Z). { - A = vec![(Y, Z)]; -} -%type case_else {Option} -case_else(A) ::= ELSE expr(X). {A = Some(X);} -case_else(A) ::= . {A = None;} -%type case_operand {Option} -case_operand(A) ::= expr(X). {A = Some(X); /*A-overwrites-X*/} -case_operand(A) ::= . {A = None;} - -%type exprlist {Option>} -%type nexprlist {Vec} - -exprlist(A) ::= nexprlist(X). {A = Some(X);} -exprlist(A) ::= . {A = None;} -nexprlist(A) ::= nexprlist(A) COMMA expr(Y). - { let expr = Y; A.push(expr);} -nexprlist(A) ::= expr(Y). - {A = vec![Y]; /*A-overwrites-Y*/} - -%ifndef SQLITE_OMIT_SUBQUERY -/* A paren_exprlist is an optional expression list contained inside -** of parenthesis */ -%type paren_exprlist {Option>} -paren_exprlist(A) ::= . {A = None;} -paren_exprlist(A) ::= LP exprlist(X) RP. {A = X;} -%endif SQLITE_OMIT_SUBQUERY - - -///////////////////////////// The CREATE INDEX command /////////////////////// -// -cmd ::= createkw uniqueflag(U) INDEX ifnotexists(NE) fullname(X) - ON nm(Y) LP sortlist(Z) RP where_opt(W). { - self.ctx.stmt = Some(Stmt::CreateIndex { unique: U, if_not_exists: NE, idx_name: Box::new(X), - tbl_name: Y, columns: Z, where_clause: W.map(Box::new) }); -} - -%type uniqueflag {bool} -uniqueflag(A) ::= UNIQUE. {A = true;} -uniqueflag(A) ::= . {A = false;} - - -// The eidlist non-terminal (Expression Id List) generates an ExprList -// from a list of identifiers. The identifier names are in ExprList.a[].zName. -// This list is stored in an ExprList rather than an IdList so that it -// can be easily sent to sqlite3ColumnsExprList(). -// -// eidlist is grouped with CREATE INDEX because it used to be the non-terminal -// used for the arguments to an index. That is just an historical accident. -// -// IMPORTANT COMPATIBILITY NOTE: Some prior versions of SQLite accepted -// COLLATE clauses and ASC or DESC keywords on ID lists in inappropriate -// places - places that might have been stored in the sqlite_schema table. -// Those extra features were ignored. But because they might be in some -// (busted) old databases, we need to continue parsing them when loading -// historical schemas. -// -%type eidlist {Vec} -%type eidlist_opt {Option>} - -%include { -} // end %include - -eidlist_opt(A) ::= . {A = None;} -eidlist_opt(A) ::= LP eidlist(X) RP. {A = Some(X);} -eidlist(A) ::= eidlist(A) COMMA nm(Y) collate(C) sortorder(Z). { - let ic = IndexedColumn{ col_name: Y, collation_name: C, order: Z }; - A.push(ic); -} -eidlist(A) ::= nm(Y) collate(C) sortorder(Z). { - A = vec![IndexedColumn{ col_name: Y, collation_name: C, order: Z }]; /*A-overwrites-Y*/ -} - -%type collate {Option} -collate(C) ::= . {C = None;} -collate(C) ::= COLLATE ids(X). {C = Some(Name::from_token(@X, X));} - - -///////////////////////////// The DROP INDEX command ///////////////////////// -// -cmd ::= DROP INDEX ifexists(E) fullname(X). {self.ctx.stmt = Some(Stmt::DropIndex{if_exists: E, idx_name: X});} - -///////////////////////////// The VACUUM command ///////////////////////////// -// -%if !SQLITE_OMIT_VACUUM && !SQLITE_OMIT_ATTACH -%type vinto {Option} -cmd ::= VACUUM vinto(Y). {self.ctx.stmt = Some(Stmt::Vacuum(None, Y.map(Box::new)));} -cmd ::= VACUUM nm(X) vinto(Y). {self.ctx.stmt = Some(Stmt::Vacuum(Some(X), Y.map(Box::new)));} -vinto(A) ::= INTO expr(X). {A = Some(X);} -vinto(A) ::= . {A = None;} -%endif - -///////////////////////////// The PRAGMA command ///////////////////////////// -// -%ifndef SQLITE_OMIT_PRAGMA -cmd ::= PRAGMA fullname(X). {self.ctx.stmt = Some(Stmt::Pragma(Box::new(X), None));} -cmd ::= PRAGMA fullname(X) EQ nmnum(Y). {self.ctx.stmt = Some(Stmt::Pragma(Box::new(X), Some(Box::new(PragmaBody::Equals(Y)))));} -cmd ::= PRAGMA fullname(X) LP nmnum(Y) RP. {self.ctx.stmt = Some(Stmt::Pragma(Box::new(X), Some(Box::new(PragmaBody::Call(Y)))));} -cmd ::= PRAGMA fullname(X) EQ minus_num(Y). - {self.ctx.stmt = Some(Stmt::Pragma(Box::new(X), Some(Box::new(PragmaBody::Equals(Y)))));} -cmd ::= PRAGMA fullname(X) LP minus_num(Y) RP. - {self.ctx.stmt = Some(Stmt::Pragma(Box::new(X), Some(Box::new(PragmaBody::Call(Y)))));} - -%type nmnum {Expr} -nmnum(A) ::= plus_num(A). -nmnum(A) ::= nm(X). {A = Expr::Name(X);} -nmnum(A) ::= ON(X). {A = Expr::Literal(Literal::Keyword(from_token(@X, X)));} -nmnum(A) ::= DELETE(X). {A = Expr::Literal(Literal::Keyword(from_token(@X, X)));} -nmnum(A) ::= DEFAULT(X). {A = Expr::Literal(Literal::Keyword(from_token(@X, X)));} -%endif SQLITE_OMIT_PRAGMA -%token_class number INTEGER|FLOAT. -%type plus_num {Expr} -plus_num(A) ::= PLUS number(X). {A = Expr::unary(UnaryOperator::Positive, Expr::Literal(Literal::Numeric(X.unwrap())));} -plus_num(A) ::= number(X). {A = Expr::Literal(Literal::Numeric(X.unwrap()));} -%type minus_num {Expr} -minus_num(A) ::= MINUS number(X). {A = Expr::unary(UnaryOperator::Negative, Expr::Literal(Literal::Numeric(X.unwrap())));} -//////////////////////////// The CREATE TRIGGER command ///////////////////// - -%ifndef SQLITE_OMIT_TRIGGER - -cmd ::= createkw temp(T) TRIGGER ifnotexists(NOERR) fullname(B) trigger_time(C) trigger_event(D) - ON fullname(E) foreach_clause(X) when_clause(G) BEGIN trigger_cmd_list(S) END. { - self.ctx.stmt = Some(Stmt::CreateTrigger(Box::new(CreateTrigger{ - temporary: T, if_not_exists: NOERR, trigger_name: B, time: C, event: D, tbl_name: E, - for_each_row: X, when_clause: G, commands: S - }))); -} - -%type trigger_time {Option} -trigger_time(A) ::= BEFORE. { A = Some(TriggerTime::Before); } -trigger_time(A) ::= AFTER. { A = Some(TriggerTime::After); } -trigger_time(A) ::= INSTEAD OF. { A = Some(TriggerTime::InsteadOf);} -trigger_time(A) ::= . { A = None; } - -%type trigger_event {TriggerEvent} -trigger_event(A) ::= DELETE. {A = TriggerEvent::Delete;} -trigger_event(A) ::= INSERT. {A = TriggerEvent::Insert;} -trigger_event(A) ::= UPDATE. {A = TriggerEvent::Update;} -trigger_event(A) ::= UPDATE OF idlist(X).{A = TriggerEvent::UpdateOf(X);} - -%type foreach_clause {bool} -foreach_clause(A) ::= . { A = false; } -foreach_clause(A) ::= FOR EACH ROW. { A = true; } - -%type when_clause {Option} -when_clause(A) ::= . { A = None; } -when_clause(A) ::= WHEN expr(X). { A = Some(X); } - -%type trigger_cmd_list {Vec} -trigger_cmd_list(A) ::= trigger_cmd_list(A) trigger_cmd(X) SEMI. { - let tc = X; - A.push(tc); -} -trigger_cmd_list(A) ::= trigger_cmd(X) SEMI. { - A = vec![X]; -} - -// Disallow qualified table names on INSERT, UPDATE, and DELETE statements -// within a trigger. The table to INSERT, UPDATE, or DELETE is always in -// the same database as the table that the trigger fires on. -// -%type trnm {Name} -trnm(A) ::= nm(A). -trnm(A) ::= nm DOT nm(X). { - A = X; - return Err(custom_err!("qualified table names are not allowed on INSERT, UPDATE, and DELETE \ - statements within triggers")); -} - -// Disallow the INDEX BY and NOT INDEXED clauses on UPDATE and DELETE -// statements within triggers. We make a specific error message for this -// since it is an exception to the default grammar rules. -// -tridxby ::= . -tridxby ::= INDEXED BY nm. { - return Err(custom_err!( - "the INDEXED BY clause is not allowed on UPDATE or DELETE statements \ - within triggers")); -} -tridxby ::= NOT INDEXED. { - return Err(custom_err!( - "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements \ - within triggers")); -} - - - -%type trigger_cmd {TriggerCmd} -// UPDATE -trigger_cmd(A) ::= - UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) from(F) where_opt(Z). - {A = TriggerCmd::Update(Box::new(TriggerCmdUpdate{ or_conflict: R, tbl_name: X, sets: Y, from: F, where_clause: Z }));} - -// INSERT -trigger_cmd(A) ::= insert_cmd(R) INTO - trnm(X) idlist_opt(F) select(S) upsert(U). { - let (upsert, returning) = U; - A = TriggerCmd::Insert(Box::new(TriggerCmdInsert{ or_conflict: R, tbl_name: X, col_names: F, select: Box::new(S), upsert, returning }));/*A-overwrites-R*/ -} -// DELETE -trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y). - {A = TriggerCmd::Delete(Box::new(TriggerCmdDelete{ tbl_name: X, where_clause: Y }));} - -// SELECT -trigger_cmd(A) ::= select(X). - {A = TriggerCmd::Select(Box::new(X)); /*A-overwrites-X*/} - -// The special RAISE expression that may occur in trigger programs -expr(A) ::= RAISE LP IGNORE RP. { - A = Expr::Raise(ResolveType::Ignore, None); -} -expr(A) ::= RAISE LP raisetype(T) COMMA expr(Z) RP. { - A = Expr::Raise(T, Some(Box::new(Z))); -} -%endif !SQLITE_OMIT_TRIGGER - -%type raisetype {ResolveType} -raisetype(A) ::= ROLLBACK. {A = ResolveType::Rollback;} -raisetype(A) ::= ABORT. {A = ResolveType::Abort;} -raisetype(A) ::= FAIL. {A = ResolveType::Fail;} - - -//////////////////////// DROP TRIGGER statement ////////////////////////////// -%ifndef SQLITE_OMIT_TRIGGER -cmd ::= DROP TRIGGER ifexists(NOERR) fullname(X). { - self.ctx.stmt = Some(Stmt::DropTrigger{ if_exists: NOERR, trigger_name: X}); -} -%endif !SQLITE_OMIT_TRIGGER - -//////////////////////// ATTACH DATABASE file AS name ///////////////////////// -%ifndef SQLITE_OMIT_ATTACH -cmd ::= ATTACH database_kw_opt expr(F) AS expr(D) key_opt(K). { - self.ctx.stmt = Some(Stmt::Attach{ expr: Box::new(F), db_name: Box::new(D), key: K.map(Box::new) }); -} -cmd ::= DETACH database_kw_opt expr(D). { - self.ctx.stmt = Some(Stmt::Detach(Box::new(D))); -} - -%type key_opt {Option} -key_opt(A) ::= . { A = None; } -key_opt(A) ::= KEY expr(X). { A = Some(X); } - -database_kw_opt ::= DATABASE. -database_kw_opt ::= . -%endif SQLITE_OMIT_ATTACH - -////////////////////////// REINDEX collation ////////////////////////////////// -%ifndef SQLITE_OMIT_REINDEX -cmd ::= REINDEX. {self.ctx.stmt = Some(Stmt::Reindex { obj_name: None });} -cmd ::= REINDEX fullname(X). {self.ctx.stmt = Some(Stmt::Reindex { obj_name: Some(X) });} -%endif SQLITE_OMIT_REINDEX - -/////////////////////////////////// ANALYZE /////////////////////////////////// -%ifndef SQLITE_OMIT_ANALYZE -cmd ::= ANALYZE. {self.ctx.stmt = Some(Stmt::Analyze(None));} -cmd ::= ANALYZE fullname(X). {self.ctx.stmt = Some(Stmt::Analyze(Some(X)));} -%endif - -//////////////////////// ALTER TABLE table ... //////////////////////////////// -%ifndef SQLITE_OMIT_ALTERTABLE -cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). { - self.ctx.stmt = Some(Stmt::AlterTable(Box::new((X, AlterTableBody::RenameTo(Z))))); -} -cmd ::= ALTER TABLE fullname(X) - ADD kwcolumn_opt columnname(Y) carglist(C). { - let (col_name, col_type) = Y; - let cd = ColumnDefinition{ col_name, col_type, constraints: C }; - self.ctx.stmt = Some(Stmt::AlterTable(Box::new((X, AlterTableBody::AddColumn(cd))))); -} -cmd ::= ALTER TABLE fullname(X) RENAME kwcolumn_opt nm(Y) TO nm(Z). { - self.ctx.stmt = Some(Stmt::AlterTable(Box::new((X, AlterTableBody::RenameColumn{ old: Y, new: Z })))); -} -cmd ::= ALTER TABLE fullname(X) DROP kwcolumn_opt nm(Y). { - self.ctx.stmt = Some(Stmt::AlterTable(Box::new((X, AlterTableBody::DropColumn(Y))))); -} - -kwcolumn_opt ::= . -kwcolumn_opt ::= COLUMNKW. -%endif SQLITE_OMIT_ALTERTABLE - -//////////////////////// CREATE VIRTUAL TABLE ... ///////////////////////////// -%ifndef SQLITE_OMIT_VIRTUALTABLE -cmd ::= create_vtab(X). {self.ctx.stmt = Some(X);} -cmd ::= create_vtab(X) LP vtabarglist RP. { - let mut stmt = X; - if let Stmt::CreateVirtualTable(ref mut create_virtual_table) = stmt { - create_virtual_table.args = self.ctx.module_args(); - } - self.ctx.stmt = Some(stmt); -} -%type create_vtab {Stmt} -create_vtab(A) ::= createkw VIRTUAL TABLE ifnotexists(E) - fullname(X) USING nm(Z). { - A = Stmt::CreateVirtualTable(Box::new(CreateVirtualTable{ if_not_exists: E, tbl_name: X, module_name: Z, args: None })); -} -vtabarglist ::= vtabarg. -vtabarglist ::= vtabarglist COMMA vtabarg. -vtabarg ::= . {self.ctx.vtab_arg_init();} -vtabarg ::= vtabarg vtabargtoken. -vtabargtoken ::= ANY(X). { let x = X; self.ctx.vtab_arg_extend(x);} -vtabargtoken ::= lp anylist RP(X). {let x = X; self.ctx.vtab_arg_extend(x);} -lp ::= LP(X). {let x = X; self.ctx.vtab_arg_extend(x);} -anylist ::= . -anylist ::= anylist LP anylist RP. -anylist ::= anylist ANY. -%endif SQLITE_OMIT_VIRTUALTABLE - - -//////////////////////// COMMON TABLE EXPRESSIONS //////////////////////////// -%type with {Option} -%type wqlist {Vec} -%type wqitem {CommonTableExpr} -// %destructor wqitem {sqlite3CteDelete(pParse->db, $$);} // not reachable - -with(A) ::= . { A = None; } -%ifndef SQLITE_OMIT_CTE -with(A) ::= WITH wqlist(W). { A = Some(With{ recursive: false, ctes: W }); } -with(A) ::= WITH RECURSIVE wqlist(W). { A = Some(With{ recursive: true, ctes: W }); } - -%type wqas {Materialized} -wqas(A) ::= AS. {A = Materialized::Any;} -wqas(A) ::= AS MATERIALIZED. {A = Materialized::Yes;} -wqas(A) ::= AS NOT MATERIALIZED. {A = Materialized::No;} -wqitem(A) ::= nm(X) eidlist_opt(Y) wqas(M) LP select(Z) RP. { - A = CommonTableExpr{ tbl_name: X, columns: Y, materialized: M, select: Box::new(Z) }; /*A-overwrites-X*/ -} -wqlist(A) ::= wqitem(X). { - A = vec![X]; /*A-overwrites-X*/ -} -wqlist(A) ::= wqlist(A) COMMA wqitem(X). { - let cte = X; - CommonTableExpr::add_cte(A, cte)?; -} -%endif SQLITE_OMIT_CTE - -//////////////////////// WINDOW FUNCTION EXPRESSIONS ///////////////////////// -// These must be at the end of this file. Specifically, the rules that -// introduce tokens WINDOW, OVER and FILTER must appear last. This causes -// the integer values assigned to these tokens to be larger than all other -// tokens that may be output by the tokenizer except TK_SPACE and TK_ILLEGAL. -// -%ifndef SQLITE_OMIT_WINDOWFUNC -%type windowdefn_list {Vec} -windowdefn_list(A) ::= windowdefn(Z). { A = vec![Z]; } -windowdefn_list(A) ::= windowdefn_list(A) COMMA windowdefn(Z). { - let w = Z; - A.push(w); -} - -%type windowdefn {WindowDef} -windowdefn(A) ::= nm(X) AS LP window(Y) RP. { - A = WindowDef { name: X, window: Y}; -} - -%type window {Window} - -%type frame_opt {Option} - -%type filter_clause {Expr} - -%type over_clause {Over} - -%type filter_over {FunctionTail} - -%type range_or_rows {FrameMode} - -%type frame_bound {FrameBound} -%type frame_bound_s {FrameBound} -%type frame_bound_e {FrameBound} - -window(A) ::= PARTITION BY nexprlist(X) orderby_opt(Y) frame_opt(Z). { - A = Window{ base: None, partition_by: Some(X), order_by: Y, frame_clause: Z}; -} -window(A) ::= nm(W) PARTITION BY nexprlist(X) orderby_opt(Y) frame_opt(Z). { - A = Window{ base: Some(W), partition_by: Some(X), order_by: Y, frame_clause: Z}; -} -window(A) ::= ORDER BY sortlist(Y) frame_opt(Z). { - A = Window{ base: None, partition_by: None, order_by: Some(Y), frame_clause: Z}; -} -window(A) ::= nm(W) ORDER BY sortlist(Y) frame_opt(Z). { - A = Window{ base: Some(W), partition_by: None, order_by: Some(Y), frame_clause: Z}; -} -window(A) ::= frame_opt(Z). { - A = Window{ base: None, partition_by: None, order_by: None, frame_clause: Z}; -} -window(A) ::= nm(W) frame_opt(Z). { - A = Window{ base: Some(W), partition_by: None, order_by: None, frame_clause: Z}; -} - -frame_opt(A) ::= . { - A = None; -} -frame_opt(A) ::= range_or_rows(X) frame_bound_s(Y) frame_exclude_opt(Z). { - A = Some(FrameClause{ mode: X, start: Y, end: None, exclude: Z }); -} -frame_opt(A) ::= range_or_rows(X) BETWEEN frame_bound_s(Y) AND - frame_bound_e(Z) frame_exclude_opt(W). { - A = Some(FrameClause{ mode: X, start: Y, end: Some(Z), exclude: W }); -} - -range_or_rows(A) ::= RANGE. { A = FrameMode::Range; } -range_or_rows(A) ::= ROWS. { A = FrameMode::Rows; } -range_or_rows(A) ::= GROUPS. { A = FrameMode::Groups; } - - -frame_bound_s(A) ::= frame_bound(X). {A = X;} -frame_bound_s(A) ::= UNBOUNDED PRECEDING. {A = FrameBound::UnboundedPreceding;} -frame_bound_e(A) ::= frame_bound(X). {A = X;} -frame_bound_e(A) ::= UNBOUNDED FOLLOWING. {A = FrameBound::UnboundedFollowing;} - -frame_bound(A) ::= expr(X) PRECEDING. { A = FrameBound::Preceding(Box::new(X)); } -frame_bound(A) ::= CURRENT ROW. { A = FrameBound::CurrentRow; } -frame_bound(A) ::= expr(X) FOLLOWING. { A = FrameBound::Following(Box::new(X)); } - -%type frame_exclude_opt {Option} -frame_exclude_opt(A) ::= . {A = None;} -frame_exclude_opt(A) ::= EXCLUDE frame_exclude(X). {A = Some(X);} - -%type frame_exclude {FrameExclude} -frame_exclude(A) ::= NO OTHERS. { A = FrameExclude::NoOthers; } -frame_exclude(A) ::= CURRENT ROW. { A = FrameExclude::CurrentRow; } -frame_exclude(A) ::= GROUP. { A = FrameExclude::Group; } -frame_exclude(A) ::= TIES. { A = FrameExclude::Ties; } - -%type window_clause {Vec} -window_clause(A) ::= WINDOW windowdefn_list(B). { A = B; } - -filter_over(A) ::= filter_clause(F) over_clause(O). { - A = FunctionTail{ filter_clause: Some(Box::new(F)), over_clause: Some(Box::new(O)) }; -} -filter_over(A) ::= over_clause(O). { - A = FunctionTail{ filter_clause: None, over_clause: Some(Box::new(O)) }; -} -filter_over(A) ::= filter_clause(F). { - A = FunctionTail{ filter_clause: Some(Box::new(F)), over_clause: None }; -} - -over_clause(A) ::= OVER LP window(Z) RP. { - A = Over::Window(Z); -} -over_clause(A) ::= OVER nm(Z). { - A = Over::Name(Z); -} - -filter_clause(A) ::= FILTER LP WHERE expr(X) RP. { A = X; } -%endif /* SQLITE_OMIT_WINDOWFUNC */ diff --git a/vendored/sqlite3-parser/src/to_sql_string/expr.rs b/vendored/sqlite3-parser/src/to_sql_string/expr.rs deleted file mode 100644 index 5c6016f6a..000000000 --- a/vendored/sqlite3-parser/src/to_sql_string/expr.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[cfg(test)] -mod tests {} diff --git a/vendored/sqlite3-parser/src/to_sql_string/mod.rs b/vendored/sqlite3-parser/src/to_sql_string/mod.rs deleted file mode 100644 index 782cc8516..000000000 --- a/vendored/sqlite3-parser/src/to_sql_string/mod.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! ToSqlString trait definition and implementations - -mod expr; -mod stmt; - -use crate::ast::TableInternalId; - -/// Context to be used in ToSqlString -pub trait ToSqlContext { - /// Given an id, get the table name - /// - /// Currently not considering aliases - fn get_table_name(&self, id: TableInternalId) -> &str; - /// Given a table id and a column index, get the column name - fn get_column_name(&self, table_id: TableInternalId, col_idx: usize) -> String; -} - -#[cfg(test)] -mod tests { - use super::ToSqlContext; - - struct TestContext; - - impl ToSqlContext for TestContext { - fn get_column_name( - &self, - _table_id: crate::ast::TableInternalId, - _col_idx: usize, - ) -> String { - "placeholder_column".to_string() - } - - fn get_table_name(&self, _id: crate::ast::TableInternalId) -> &str { - "placeholder_table" - } - } -} diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/alter_table.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/alter_table.rs deleted file mode 100644 index 7dabd3913..000000000 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/alter_table.rs +++ /dev/null @@ -1,74 +0,0 @@ -#[cfg(test)] -mod tests { - use crate::to_sql_string_test; - - to_sql_string_test!( - test_alter_table_rename, - "ALTER TABLE t RENAME TO new_table_name" - ); - - to_sql_string_test!( - test_alter_table_add_column, - "ALTER TABLE t ADD COLUMN c INTEGER" - ); - - to_sql_string_test!( - test_alter_table_add_column_with_default, - "ALTER TABLE t ADD COLUMN c TEXT DEFAULT 'value'" - ); - - to_sql_string_test!( - test_alter_table_add_column_not_null_default, - "ALTER TABLE t ADD COLUMN c REAL NOT NULL DEFAULT 0.0" - ); - - to_sql_string_test!( - test_alter_table_add_column_unique, - "ALTER TABLE t ADD COLUMN c TEXT UNIQUE", - ignore = "ParserError = Cannot add a UNIQUE column" - ); - - to_sql_string_test!( - test_alter_table_rename_column, - "ALTER TABLE t RENAME COLUMN old_name TO new_name" - ); - - to_sql_string_test!(test_alter_table_drop_column, "ALTER TABLE t DROP COLUMN c"); - - to_sql_string_test!( - test_alter_table_add_column_check, - "ALTER TABLE t ADD COLUMN c INTEGER CHECK (c > 0)" - ); - - to_sql_string_test!( - test_alter_table_add_column_foreign_key, - "ALTER TABLE t ADD COLUMN c INTEGER REFERENCES t2 (id) ON DELETE CASCADE" - ); - - to_sql_string_test!( - test_alter_table_add_column_collate, - "ALTER TABLE t ADD COLUMN c TEXT COLLATE NOCASE" - ); - - to_sql_string_test!( - test_alter_table_add_column_primary_key, - "ALTER TABLE t ADD COLUMN c INTEGER PRIMARY KEY", - ignore = "ParserError = Cannot add a PRIMARY KEY column" - ); - - to_sql_string_test!( - test_alter_table_add_column_primary_key_autoincrement, - "ALTER TABLE t ADD COLUMN c INTEGER PRIMARY KEY AUTOINCREMENT", - ignore = "ParserError = Cannot add a PRIMARY KEY column" - ); - - to_sql_string_test!( - test_alter_table_add_generated_column, - "ALTER TABLE t ADD COLUMN c_generated AS (a + b) STORED" - ); - - to_sql_string_test!( - test_alter_table_add_column_schema, - "ALTER TABLE schema_name.t ADD COLUMN c INTEGER" - ); -} diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/create_table.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/create_table.rs deleted file mode 100644 index 3167413e8..000000000 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/create_table.rs +++ /dev/null @@ -1,121 +0,0 @@ -#[cfg(test)] -mod tests { - use crate::to_sql_string_test; - - to_sql_string_test!( - test_create_table_simple, - "CREATE TABLE t (a INTEGER, b TEXT)" - ); - - to_sql_string_test!( - test_create_table_primary_key, - "CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)" - ); - - to_sql_string_test!( - test_create_table_multi_primary_key, - "CREATE TABLE t (a INTEGER, b TEXT, PRIMARY KEY (a, b))" - ); - - to_sql_string_test!( - test_create_table_data_types, - "CREATE TABLE t (a INTEGER, b TEXT, c REAL, d BLOB, e NUMERIC)" - ); - - to_sql_string_test!( - test_create_table_foreign_key, - "CREATE TABLE t2 (id INTEGER PRIMARY KEY, t_id INTEGER, FOREIGN KEY (t_id) REFERENCES t (id))" - ); - - to_sql_string_test!( - test_create_table_foreign_key_cascade, - "CREATE TABLE t2 (id INTEGER PRIMARY KEY, t_id INTEGER, FOREIGN KEY (t_id) REFERENCES t (id) ON DELETE CASCADE)" - ); - - to_sql_string_test!( - test_create_table_unique, - "CREATE TABLE t (a INTEGER UNIQUE, b TEXT)" - ); - - to_sql_string_test!( - test_create_table_not_null, - "CREATE TABLE t (a INTEGER NOT NULL, b TEXT)" - ); - - to_sql_string_test!( - test_create_table_check, - "CREATE TABLE t (a INTEGER CHECK (a > 0), b TEXT)" - ); - - to_sql_string_test!( - test_create_table_default, - "CREATE TABLE t (a INTEGER DEFAULT 0, b TEXT)" - ); - - to_sql_string_test!( - test_create_table_multiple_constraints, - "CREATE TABLE t (a INTEGER NOT NULL UNIQUE, b TEXT DEFAULT 'default')" - ); - - to_sql_string_test!( - test_create_table_generated_column, - "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER AS (a + b))" - ); - - to_sql_string_test!( - test_create_table_generated_stored, - "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER AS (a + b) STORED)" - ); - - to_sql_string_test!( - test_create_table_generated_virtual, - "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER AS (a + b) VIRTUAL)" - ); - - to_sql_string_test!( - test_create_table_quoted_columns, - "CREATE TABLE t (\"select\" INTEGER, \"from\" TEXT)" - ); - - to_sql_string_test!( - test_create_table_quoted_table, - "CREATE TABLE \"my table\" (a INTEGER)" - ); - - to_sql_string_test!( - test_create_table_if_not_exists, - "CREATE TABLE IF NOT EXISTS t (a INTEGER)" - ); - - to_sql_string_test!(test_create_temp_table, "CREATE TEMP TABLE t (a INTEGER)"); - - to_sql_string_test!( - test_create_table_without_rowid, - "CREATE TABLE t (a INTEGER PRIMARY KEY, b TEXT) WITHOUT ROWID" - ); - - to_sql_string_test!( - test_create_table_named_primary_key, - "CREATE TABLE t (a INTEGER CONSTRAINT pk_a PRIMARY KEY)" - ); - - to_sql_string_test!( - test_create_table_named_unique, - "CREATE TABLE t (a INTEGER, CONSTRAINT unique_a UNIQUE (a))" - ); - - to_sql_string_test!( - test_create_table_named_foreign_key, - "CREATE TABLE t2 (id INTEGER, t_id INTEGER, CONSTRAINT fk_t FOREIGN KEY (t_id) REFERENCES t (id))" - ); - - to_sql_string_test!( - test_create_table_complex, - "CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER NOT NULL, b TEXT DEFAULT 'default', c INTEGER AS (a * 2), CONSTRAINT unique_a UNIQUE (a))" - ); - - to_sql_string_test!( - test_create_table_multiple_foreign_keys, - "CREATE TABLE t3 (id INTEGER PRIMARY KEY, t1_id INTEGER, t2_id INTEGER, FOREIGN KEY (t1_id) REFERENCES t1 (id), FOREIGN KEY (t2_id) REFERENCES t2 (id))" - ); -} diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/create_trigger.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/create_trigger.rs deleted file mode 100644 index 5f6ba39c0..000000000 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/create_trigger.rs +++ /dev/null @@ -1,59 +0,0 @@ -#[cfg(test)] -mod tests { - use crate::to_sql_string_test; - - to_sql_string_test!( - test_log_employee_insert, - "CREATE TRIGGER log_employee_insert - AFTER INSERT ON employees - FOR EACH ROW - BEGIN - INSERT INTO employee_log (action, employee_id, timestamp) - VALUES ('INSERT', NEW.id, CURRENT_TIMESTAMP); - END" - ); - - to_sql_string_test!( - test_log_salary_update, - "CREATE TRIGGER log_salary_update - AFTER UPDATE OF salary ON employees - FOR EACH ROW - BEGIN - INSERT INTO employee_log (action, employee_id, old_value, new_value, timestamp) - VALUES ('UPDATE', OLD.id, OLD.salary, NEW.salary, CURRENT_TIMESTAMP); - END" - ); - - to_sql_string_test!( - test_log_employee_delete, - "CREATE TRIGGER log_employee_delete - AFTER DELETE ON employees - FOR EACH ROW - BEGIN - INSERT INTO employee_log (action, employee_id, timestamp) - VALUES ('DELETE', OLD.id, CURRENT_TIMESTAMP); - END" - ); - - to_sql_string_test!( - test_check_salary_insert, - "CREATE TRIGGER check_salary_insert - BEFORE INSERT ON employees - FOR EACH ROW - WHEN NEW.salary < 0 - BEGIN - SELECT RAISE (FAIL, 'Salary cannot be negative'); - END" - ); - - to_sql_string_test!( - test_insert_employee_dept, - "CREATE TRIGGER insert_employee_dept - INSTEAD OF INSERT ON employee_dept - FOR EACH ROW - BEGIN - INSERT INTO departments (name) SELECT NEW.department WHERE NOT EXISTS (SELECT 1 FROM departments WHERE name = NEW.department); - INSERT INTO employees (name, department_id) VALUES (NEW.name, (SELECT id FROM departments WHERE name = NEW.department)); - END" - ); -} diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/create_virtual_table.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/create_virtual_table.rs deleted file mode 100644 index 8887bf932..000000000 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/create_virtual_table.rs +++ /dev/null @@ -1,64 +0,0 @@ -#[cfg(test)] -mod tests { - use crate::to_sql_string_test; - - to_sql_string_test!( - test_create_virtual_table_fts5_basic, - "CREATE VIRTUAL TABLE docs USING fts5 (title, content)" - ); - - to_sql_string_test!( - test_create_virtual_table_fts5_tokenizer, - "CREATE VIRTUAL TABLE docs USING fts5 (title, content, tokenize = 'porter')" - ); - - to_sql_string_test!( - test_create_virtual_table_fts5_unindexed, - "CREATE VIRTUAL TABLE docs USING fts5 (title, content, metadata UNINDEXED)" - ); - - to_sql_string_test!( - test_create_virtual_table_fts5_prefix, - "CREATE VIRTUAL TABLE docs USING fts5 (title, content, tokenize = 'unicode61', prefix = '2 4')" - ); - - to_sql_string_test!( - test_create_virtual_table_fts5_contentless, - "CREATE VIRTUAL TABLE docs USING fts5 (title, content, content = '')" - ); - - to_sql_string_test!( - test_create_virtual_table_fts5_external_content, - "CREATE VIRTUAL TABLE docs_fts USING fts5 (title, content, content = 'documents')" - ); - - to_sql_string_test!( - test_create_virtual_table_rtree, - "CREATE VIRTUAL TABLE geo USING rtree (id, min_x, max_x, min_y, max_y)" - ); - - to_sql_string_test!( - test_create_virtual_table_rtree_aux, - "CREATE VIRTUAL TABLE geo USING rtree (id, min_x, max_x, min_y, max_y, +name TEXT, +category INTEGER)" - ); - - to_sql_string_test!( - test_create_virtual_table_if_not_exists, - "CREATE VIRTUAL TABLE IF NOT EXISTS docs USING fts5 (title, content)" - ); - - to_sql_string_test!( - test_create_virtual_table_fts4, - "CREATE VIRTUAL TABLE docs USING fts4 (title, content, matchinfo = 'fts3')" - ); - - to_sql_string_test!( - test_create_virtual_table_fts5_detail, - "CREATE VIRTUAL TABLE docs USING fts5 (title, body TEXT, detail = 'none')" - ); - - to_sql_string_test!( - test_create_virtual_table_schema, - "CREATE VIRTUAL TABLE main.docs USING fts5 (title, content)" - ); -} diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/delete.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/delete.rs deleted file mode 100644 index 18dd8b783..000000000 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/delete.rs +++ /dev/null @@ -1,82 +0,0 @@ -#[cfg(test)] -mod tests { - use crate::to_sql_string_test; - - // Basic DELETE from a single table - to_sql_string_test!(test_delete_all, "DELETE FROM employees"); - - // DELETE with a simple WHERE clause - to_sql_string_test!(test_delete_with_where, "DELETE FROM employees WHERE id = 1"); - - // DELETE with multiple WHERE conditions - to_sql_string_test!( - test_delete_with_multi_where, - "DELETE FROM employees WHERE salary < 50000 AND department_id = 3" - ); - - // DELETE with IN clause - to_sql_string_test!( - test_delete_with_in, - "DELETE FROM employees WHERE id IN (1, 2, 3)" - ); - - // DELETE with subquery in WHERE - to_sql_string_test!( - test_delete_with_subquery, - "DELETE FROM employees WHERE department_id IN (SELECT id FROM departments WHERE name = 'Sales')" - ); - - // DELETE with EXISTS clause - to_sql_string_test!( - test_delete_with_exists, - "DELETE FROM employees WHERE EXISTS (SELECT 1 FROM orders WHERE orders.employee_id = employees.id AND orders.status = 'pending')" - ); - - // DELETE with RETURNING clause - to_sql_string_test!( - test_delete_with_returning, - "DELETE FROM employees WHERE salary < 30000 RETURNING id, name" - ); - - // DELETE with LIMIT clause - to_sql_string_test!( - test_delete_with_limit, - "DELETE FROM employees WHERE salary < 40000 LIMIT 5" - ); - - // DELETE with ORDER BY and LIMIT - to_sql_string_test!( - test_delete_with_order_by_limit, - "DELETE FROM employees WHERE salary < 40000 ORDER BY id DESC LIMIT 5" - ); - - // DELETE from schema-qualified table - to_sql_string_test!( - test_delete_schema_qualified, - "DELETE FROM main.employees WHERE id = 1" - ); - - // DELETE with BETWEEN clause - to_sql_string_test!( - test_delete_with_between, - "DELETE FROM employees WHERE salary BETWEEN 30000 AND 50000" - ); - - // DELETE with NULL check - to_sql_string_test!( - test_delete_with_null, - "DELETE FROM employees WHERE department_id IS NULL" - ); - - // DELETE with LIKE clause - to_sql_string_test!( - test_delete_with_like, - "DELETE FROM employees WHERE name LIKE 'J%'" - ); - - // DELETE with complex expression in WHERE - to_sql_string_test!( - test_delete_with_complex_expression, - "DELETE FROM employees WHERE (salary * 1.1) > 60000 AND department_id != 1" - ); -} diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/insert.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/insert.rs deleted file mode 100644 index 89daf151c..000000000 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/insert.rs +++ /dev/null @@ -1,94 +0,0 @@ -#[cfg(test)] -mod tests { - use crate::to_sql_string_test; - - // Basic INSERT with all columns - to_sql_string_test!( - test_insert_basic, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000)" - ); - - // INSERT with multiple rows - to_sql_string_test!( - test_insert_multiple_rows, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000), (2, 'Jane Smith', 60000)" - ); - - // INSERT with specific columns - to_sql_string_test!( - test_insert_specific_columns, - "INSERT INTO employees (name, salary) VALUES ('Alice Brown', 55000)" - ); - - // INSERT with DEFAULT VALUES - to_sql_string_test!( - test_insert_default_values, - "INSERT INTO employees DEFAULT VALUES" - ); - - // INSERT with SELECT subquery - to_sql_string_test!( - test_insert_select_subquery, - "INSERT INTO employees (id, name, salary) SELECT id, name, salary FROM temp_employees WHERE salary > 40000" - ); - - // INSERT with ON CONFLICT IGNORE - to_sql_string_test!( - test_insert_on_conflict_ignore, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT (id) DO NOTHING" - ); - - // INSERT with ON CONFLICT REPLACE - to_sql_string_test!( - test_insert_on_conflict_replace, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT (id) DO UPDATE SET name = excluded.name, salary = excluded.salary" - ); - - // INSERT with RETURNING clause - to_sql_string_test!( - test_insert_with_returning, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) RETURNING id, name" - ); - - // INSERT with NULL values - to_sql_string_test!( - test_insert_with_null, - "INSERT INTO employees (id, name, salary, department_id) VALUES (1, 'John Doe', NULL, NULL)" - ); - - // INSERT with expression in VALUES - to_sql_string_test!( - test_insert_with_expression, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000 * 1.1)" - ); - - // INSERT into schema-qualified table - to_sql_string_test!( - test_insert_schema_qualified, - "INSERT INTO main.employees (id, name, salary) VALUES (1, 'John Doe', 50000)" - ); - - // INSERT with subquery and JOIN - to_sql_string_test!( - test_insert_subquery_join, - "INSERT INTO employees (id, name, department_id) SELECT e.id, e.name, d.id FROM temp_employees e JOIN departments d ON e.dept_name = d.name" - ); - - // INSERT with all columns from SELECT - to_sql_string_test!( - test_insert_all_columns_select, - "INSERT INTO employees SELECT * FROM temp_employees" - ); - - // INSERT with ON CONFLICT and WHERE clause - to_sql_string_test!( - test_insert_on_conflict_where, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT (id) DO UPDATE SET salary = excluded.salary WHERE excluded.salary > employees.salary" - ); - - // INSERT with quoted column names (reserved words) - to_sql_string_test!( - test_insert_quoted_columns, - "INSERT INTO employees (\"select\", \"from\") VALUES (1, 'data')" - ); -} diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/mod.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/mod.rs deleted file mode 100644 index b57396e57..000000000 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/mod.rs +++ /dev/null @@ -1,229 +0,0 @@ -mod alter_table; -mod create_table; -mod create_trigger; -mod create_virtual_table; -mod delete; -mod insert; -mod select; -mod update; - -#[cfg(test)] -mod tests { - use crate::to_sql_string::ToSqlContext; - - #[macro_export] - /// Create a test that first parses then input, the converts the parsed ast back to a string and compares with original input - macro_rules! to_sql_string_test { - ($test_name:ident, $input:expr) => { - #[test] - fn $test_name() { - use $crate::parser::ast::fmt::ToTokens; - let context = $crate::to_sql_string::stmt::tests::TestContext; - let input = $input.split_whitespace().collect::>().join(" "); - let mut parser = $crate::lexer::sql::Parser::new(input.as_bytes()); - let cmd = fallible_iterator::FallibleIterator::next(&mut parser) - .unwrap() - .unwrap(); - assert_eq!( - input, - cmd.stmt().format_with_context(&context).unwrap().replace('\n', " "), - ); - } - }; - ($test_name:ident, $input:expr, $($attribute:meta),*) => { - #[test] - $(#[$attribute])* - fn $test_name() { - use $crate::parser::ast::fmt::ToTokens; - let context = $crate::to_sql_string::stmt::tests::TestContext; - let input = $input.split_whitespace().collect::>().join(" "); - let mut parser = $crate::lexer::sql::Parser::new(input.as_bytes()); - let cmd = fallible_iterator::FallibleIterator::next(&mut parser) - .unwrap() - .unwrap(); - assert_eq!( - input, - cmd.stmt().format_with_context(&context).unwrap().replace('\n', " "), - ); - } - } - } - - pub(crate) struct TestContext; - - // Placeholders for compilation - // Context only necessary parsing inside turso_core or in the simulator - impl ToSqlContext for TestContext { - fn get_column_name( - &self, - _table_id: crate::ast::TableInternalId, - _col_idx: usize, - ) -> String { - todo!() - } - - fn get_table_name(&self, _id: crate::ast::TableInternalId) -> &str { - todo!() - } - } - - to_sql_string_test!(test_analyze, "ANALYZE"); - - to_sql_string_test!( - test_analyze_table, - "ANALYZE table", - ignore = "parser can't parse table name" - ); - - to_sql_string_test!( - test_analyze_schema_table, - "ANALYZE schema.table", - ignore = "parser can't parse schema.table name" - ); - - to_sql_string_test!(test_attach, "ATTACH './test.db' AS test_db"); - - to_sql_string_test!(test_transaction, "BEGIN"); - - to_sql_string_test!(test_transaction_deferred, "BEGIN DEFERRED"); - - to_sql_string_test!(test_transaction_immediate, "BEGIN IMMEDIATE"); - - to_sql_string_test!(test_transaction_exclusive, "BEGIN EXCLUSIVE"); - - to_sql_string_test!(test_commit, "COMMIT"); - - // Test a simple index on a single column - to_sql_string_test!( - test_create_index_simple, - "CREATE INDEX idx_name ON employees (last_name)" - ); - - // Test a unique index to enforce uniqueness on a column - to_sql_string_test!( - test_create_unique_index, - "CREATE UNIQUE INDEX idx_unique_email ON users (email)" - ); - - // Test a multi-column index - to_sql_string_test!( - test_create_index_multi_column, - "CREATE INDEX idx_name_salary ON employees (last_name, salary)" - ); - - // Test a partial index with a WHERE clause - to_sql_string_test!( - test_create_partial_index, - "CREATE INDEX idx_active_users ON users (username) WHERE active = true" - ); - - // Test an index on an expression - to_sql_string_test!( - test_create_index_on_expression, - "CREATE INDEX idx_upper_name ON employees (UPPER (last_name))" - ); - - // Test an index with descending order - to_sql_string_test!( - test_create_index_descending, - "CREATE INDEX idx_salary_desc ON employees (salary DESC)" - ); - - // Test an index with mixed ascending and descending orders on multiple columns - to_sql_string_test!( - test_create_index_mixed_order, - "CREATE INDEX idx_name_asc_salary_desc ON employees (last_name ASC, salary DESC)" - ); - - // Test 1: View with DISTINCT keyword - to_sql_string_test!( - test_create_view_distinct, - "CREATE VIEW view_distinct AS SELECT DISTINCT name FROM employees" - ); - - // Test 2: View with LIMIT clause - to_sql_string_test!( - test_create_view_limit, - "CREATE VIEW view_limit AS SELECT id, name FROM employees LIMIT 10" - ); - - // Test 3: View with CASE expression - to_sql_string_test!( - test_create_view_case, - "CREATE VIEW view_case AS SELECT name, CASE WHEN salary > 70000 THEN 'High' ELSE 'Low' END AS salary_level FROM employees" - ); - - // Test 4: View with LEFT JOIN - to_sql_string_test!( - test_create_view_left_join, - "CREATE VIEW view_left_join AS SELECT e.name, d.name AS department FROM employees e LEFT OUTER JOIN departments d ON e.department_id = d.id" - ); - - // Test 5: View with HAVING clause - to_sql_string_test!( - test_create_view_having, - "CREATE VIEW view_having AS SELECT department_id, AVG (salary) AS avg_salary FROM employees GROUP BY department_id HAVING AVG (salary) > 55000" - ); - - // Test 6: View with CTE (Common Table Expression) - to_sql_string_test!( - test_create_view_cte, - "CREATE VIEW view_cte AS WITH high_earners AS (SELECT * FROM employees WHERE salary > 80000) SELECT id, name FROM high_earners" - ); - - // Test 7: View with multiple conditions in WHERE - to_sql_string_test!( - test_create_view_multi_where, - "CREATE VIEW view_multi_where AS SELECT id, name FROM employees WHERE salary > 50000 AND department_id = 3" - ); - - // Test 8: View with NULL handling - to_sql_string_test!( - test_create_view_null, - "CREATE VIEW view_null AS SELECT name, COALESCE (salary, 0) AS salary FROM employees" - ); - - // Test 9: View with subquery in WHERE clause - to_sql_string_test!( - test_create_view_subquery_where, - "CREATE VIEW view_subquery_where AS SELECT name FROM employees WHERE department_id IN (SELECT id FROM departments WHERE name = 'Sales')" - ); - - // Test 10: View with arithmetic expression - to_sql_string_test!( - test_create_view_arithmetic, - "CREATE VIEW view_arithmetic AS SELECT name, salary * 1.1 AS adjusted_salary FROM employees" - ); - - to_sql_string_test!(test_detach, "DETACH 'x.db'"); - - to_sql_string_test!(test_drop_index, "DROP INDEX schema_name.test_index"); - - to_sql_string_test!(test_drop_table, "DROP TABLE schema_name.test_table"); - - to_sql_string_test!(test_drop_trigger, "DROP TRIGGER schema_name.test_trigger"); - - to_sql_string_test!(test_drop_view, "DROP VIEW schema_name.test_view"); - - to_sql_string_test!(test_pragma_equals, "PRAGMA schema_name.Pragma_name = 1"); - - to_sql_string_test!(test_pragma_call, "PRAGMA schema_name.Pragma_name_2 (1)"); - - to_sql_string_test!(test_reindex, "REINDEX schema_name.test_table"); - - to_sql_string_test!(test_reindex_2, "REINDEX"); - - to_sql_string_test!(test_release, "RELEASE savepoint_name"); - - to_sql_string_test!(test_rollback, "ROLLBACK"); - - to_sql_string_test!(test_rollback_2, "ROLLBACK TO savepoint_name"); - - to_sql_string_test!(test_savepoint, "SAVEPOINT savepoint_name"); - - to_sql_string_test!(test_vacuum, "VACUUM"); - - to_sql_string_test!(test_vacuum_2, "VACUUM schema_name"); - - to_sql_string_test!(test_vacuum_3, "VACUUM schema_name INTO test.db"); -} diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/select.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/select.rs deleted file mode 100644 index 68e4cc4a9..000000000 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/select.rs +++ /dev/null @@ -1,138 +0,0 @@ -#[cfg(test)] -mod tests { - use crate::to_sql_string_test; - - to_sql_string_test!(test_select_basic, "SELECT 1"); - - to_sql_string_test!(test_select_table, "SELECT * FROM t"); - - to_sql_string_test!(test_select_table_2, "SELECT a FROM t"); - - to_sql_string_test!(test_select_multiple_columns, "SELECT a, b, c FROM t"); - - to_sql_string_test!(test_select_with_alias, "SELECT a AS col1 FROM t"); - - to_sql_string_test!(test_select_with_table_alias, "SELECT t1.a FROM t AS t1"); - - to_sql_string_test!(test_select_with_where, "SELECT a FROM t WHERE b = 1"); - - to_sql_string_test!( - test_select_with_multiple_conditions, - "SELECT a FROM t WHERE b = 1 AND c > 2" - ); - - to_sql_string_test!(test_select_with_order_by, "SELECT a FROM t ORDER BY a DESC"); - - to_sql_string_test!(test_select_with_limit, "SELECT a FROM t LIMIT 10"); - - to_sql_string_test!(test_select_with_offset, "SELECT a FROM t LIMIT 10 OFFSET 5"); - - to_sql_string_test!( - test_select_with_join, - "SELECT a FROM t JOIN t2 ON t.b = t2.b" - ); - - to_sql_string_test!( - test_select_with_group_by, - "SELECT a, COUNT (*) FROM t GROUP BY a" - ); - - to_sql_string_test!( - test_select_with_having, - "SELECT a, COUNT (*) FROM t GROUP BY a HAVING COUNT (*) > 1" - ); - - to_sql_string_test!(test_select_with_distinct, "SELECT DISTINCT a FROM t"); - - to_sql_string_test!(test_select_with_function, "SELECT COUNT (a) FROM t"); - - to_sql_string_test!( - test_select_with_subquery, - "SELECT a FROM (SELECT b FROM t) AS sub" - ); - - to_sql_string_test!( - test_select_nested_subquery, - "SELECT a FROM (SELECT b FROM (SELECT c FROM t WHERE c > 10) AS sub1 WHERE b < 20) AS sub2" - ); - - to_sql_string_test!( - test_select_multiple_joins, - "SELECT t1.a, t2.b, t3.c FROM t1 JOIN t2 ON t1.id = t2.id LEFT OUTER JOIN t3 ON t2.id = t3.id" - ); - - to_sql_string_test!( - test_select_with_cte, - "WITH cte AS (SELECT a FROM t WHERE b = 1) SELECT a FROM cte WHERE a > 10" - ); - - to_sql_string_test!( - test_select_with_window_function, - "SELECT a, ROW_NUMBER () OVER (PARTITION BY b ORDER BY c DESC) AS rn FROM t" - ); - - to_sql_string_test!( - test_select_with_complex_where, - "SELECT a FROM t WHERE b IN (1, 2, 3) AND c BETWEEN 10 AND 20 OR d IS NULL" - ); - - to_sql_string_test!( - test_select_with_case, - "SELECT CASE WHEN a > 0 THEN 'positive' ELSE 'non-positive' END AS result FROM t" - ); - - to_sql_string_test!(test_select_with_aggregate_and_join, "SELECT t1.a, COUNT (t2.b) FROM t1 LEFT OUTER JOIN t2 ON t1.id = t2.id GROUP BY t1.a HAVING COUNT (t2.b) > 5"); - - to_sql_string_test!(test_select_with_multiple_ctes, "WITH cte1 AS (SELECT a FROM t WHERE b = 1), cte2 AS (SELECT c FROM t2 WHERE d = 2) SELECT cte1.a, cte2.c FROM cte1 JOIN cte2 ON cte1.a = cte2.c"); - - to_sql_string_test!( - test_select_with_union, - "SELECT a FROM t1 UNION SELECT b FROM t2" - ); - - to_sql_string_test!( - test_select_with_union_all, - "SELECT a FROM t1 UNION ALL SELECT b FROM t2" - ); - - to_sql_string_test!( - test_select_with_exists, - "SELECT a FROM t WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.b = t.a)" - ); - - to_sql_string_test!( - test_select_with_correlated_subquery, - "SELECT a, (SELECT COUNT (*) FROM t2 WHERE t2.b = t.a) AS count_b FROM t" - ); - - to_sql_string_test!( - test_select_with_complex_order_by, - "SELECT a, b FROM t ORDER BY CASE WHEN a IS NULL THEN 1 ELSE 0 END, b ASC, c DESC" - ); - - to_sql_string_test!( - test_select_with_full_outer_join, - "SELECT t1.a, t2.b FROM t1 FULL OUTER JOIN t2 ON t1.id = t2.id", - ignore = "OUTER JOIN is incorrectly parsed in parser" - ); - - to_sql_string_test!(test_select_with_aggregate_window, "SELECT a, SUM (b) OVER (PARTITION BY c ORDER BY d ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS running_sum FROM t"); - - to_sql_string_test!( - test_select_with_exclude, - "SELECT - c.name, - o.order_id, - o.order_amount, - SUM (o.order_amount) OVER (PARTITION BY c.id - ORDER BY o.order_date - ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - EXCLUDE CURRENT ROW) AS running_total_excluding_current -FROM customers c -JOIN orders o ON c.id = o.customer_id -WHERE EXISTS (SELECT 1 - FROM orders o2 - WHERE o2.customer_id = c.id - AND o2.order_amount > 1000)" - ); -} diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/update.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/update.rs deleted file mode 100644 index 90e818d7e..000000000 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/update.rs +++ /dev/null @@ -1,94 +0,0 @@ -#[cfg(test)] -mod tests { - use crate::to_sql_string_test; - - // Basic UPDATE with a single column - to_sql_string_test!( - test_update_single_column, - "UPDATE employees SET salary = 55000" - ); - - // UPDATE with multiple columns - to_sql_string_test!( - test_update_multiple_columns, - "UPDATE employees SET salary = 60000, name = 'John Smith'" - ); - - // UPDATE with a WHERE clause - to_sql_string_test!( - test_update_with_where, - "UPDATE employees SET salary = 60000 WHERE id = 1" - ); - - // UPDATE with multiple WHERE conditions - to_sql_string_test!( - test_update_with_multi_where, - "UPDATE employees SET salary = 65000 WHERE department_id = 3 AND salary < 50000" - ); - - // UPDATE with a subquery in SET - to_sql_string_test!( - test_update_with_subquery_set, - "UPDATE employees SET department_id = (SELECT id FROM departments WHERE name = 'Sales') WHERE id = 1" - ); - - // UPDATE with a subquery in WHERE - to_sql_string_test!( - test_update_with_subquery_where, - "UPDATE employees SET salary = 70000 WHERE department_id IN (SELECT id FROM departments WHERE name = 'Marketing')" - ); - - // UPDATE with EXISTS clause - to_sql_string_test!( - test_update_with_exists, - "UPDATE employees SET salary = 75000 WHERE EXISTS (SELECT 1 FROM orders WHERE orders.employee_id = employees.id AND orders.status = 'pending')" - ); - - // UPDATE with FROM clause (join-like behavior) - to_sql_string_test!( - test_update_with_from, - "UPDATE employees SET salary = 80000 FROM departments WHERE employees.department_id = departments.id AND departments.name = 'Engineering'" - ); - - // UPDATE with RETURNING clause - to_sql_string_test!( - test_update_with_returning, - "UPDATE employees SET salary = 60000 WHERE id = 1 RETURNING id, name, salary" - ); - - // UPDATE with expression in SET - to_sql_string_test!( - test_update_with_expression, - "UPDATE employees SET salary = salary * 1.1 WHERE department_id = 2" - ); - - // UPDATE with NULL value - to_sql_string_test!( - test_update_with_null, - "UPDATE employees SET department_id = NULL WHERE id = 1" - ); - - // UPDATE with schema-qualified table - to_sql_string_test!( - test_update_schema_qualified, - "UPDATE main.employees SET salary = 65000 WHERE id = 1" - ); - - // UPDATE with CASE expression - to_sql_string_test!( - test_update_with_case, - "UPDATE employees SET salary = CASE WHEN salary < 50000 THEN 55000 ELSE salary * 1.05 END WHERE department_id = 3" - ); - - // UPDATE with LIKE clause in WHERE - to_sql_string_test!( - test_update_with_like, - "UPDATE employees SET name = 'Updated' WHERE name LIKE 'J%'" - ); - - // UPDATE with ON CONFLICT (upsert-like behavior) - to_sql_string_test!( - test_update_with_on_conflict, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT (id) DO UPDATE SET name = excluded.name, salary = excluded.salary" - ); -} diff --git a/vendored/sqlite3-parser/third_party/lemon/lemon.c b/vendored/sqlite3-parser/third_party/lemon/lemon.c deleted file mode 100644 index 2c438a52f..000000000 --- a/vendored/sqlite3-parser/third_party/lemon/lemon.c +++ /dev/null @@ -1,5881 +0,0 @@ -/* -** This file contains all sources (including headers) to the LEMON -** LALR(1) parser generator. The sources have been combined into a -** single file to make it easy to include LEMON in the source tree -** and Makefile of another program. -** -** The author of this program disclaims copyright. -*/ -#include -#include -#include -#include -#include -#include - -#define ISSPACE(X) isspace((unsigned char)(X)) -#define ISDIGIT(X) isdigit((unsigned char)(X)) -#define ISALNUM(X) isalnum((unsigned char)(X)) -#define ISALPHA(X) isalpha((unsigned char)(X)) -#define ISUPPER(X) isupper((unsigned char)(X)) -#define ISLOWER(X) islower((unsigned char)(X)) - - -#ifndef __WIN32__ -# if defined(_WIN32) || defined(WIN32) -# define __WIN32__ -# endif -#endif - -#ifdef __WIN32__ -#ifdef __cplusplus -extern "C" { -#endif -extern int access(const char *path, int mode); -#ifdef __cplusplus -} -#endif -#else -#include -#endif - -/* #define PRIVATE static */ -#define PRIVATE - -#ifdef TEST -#define MAXRHS 5 /* Set low to exercise exception code */ -#else -#define MAXRHS 1000 -#endif - -extern void memory_error(); -static int showPrecedenceConflict = 0; -static char *msort(char*,char**,int(*)(const char*,const char*)); - -/* -** Compilers are getting increasingly pedantic about type conversions -** as C evolves ever closer to Ada.... To work around the latest problems -** we have to define the following variant of strlen(). -*/ -#define lemonStrlen(X) ((int)strlen(X)) - -/* -** Header on the linked list of memory allocations. -*/ -typedef struct MemChunk MemChunk; -struct MemChunk { - MemChunk *pNext; - size_t sz; - /* Actually memory follows */ -}; - -/* -** Global linked list of all memory allocations. -*/ -static MemChunk *memChunkList = 0; - -/* -** Wrappers around malloc(), calloc(), realloc() and free(). -** -** All memory allocations are kept on a doubly-linked list. The -** lemon_free_all() function can be called prior to exit to clean -** up any memory leaks. -** -** This is not necessary. But compilers and getting increasingly -** fussy about memory leaks, even in command-line programs like Lemon -** where they do not matter. So this code is provided to hush the -** warnings. -*/ -static void *lemon_malloc(size_t nByte){ - MemChunk *p; - if( nByte<0 ) return 0; - p = malloc( nByte + sizeof(MemChunk) ); - if( p==0 ){ - fprintf(stderr, "Out of memory. Failed to allocate %lld bytes.\n", - (long long int)nByte); - exit(1); - } - p->pNext = memChunkList; - p->sz = nByte; - memChunkList = p; - return (void*)&p[1]; -} -static void *lemon_calloc(size_t nElem, size_t sz){ - void *p = lemon_malloc(nElem*sz); - memset(p, 0, nElem*sz); - return p; -} -static void lemon_free(void *pOld){ - if( pOld ){ - MemChunk *p = (MemChunk*)pOld; - p--; - memset(pOld, 0, p->sz); - } -} -static void *lemon_realloc(void *pOld, size_t nNew){ - void *pNew; - MemChunk *p; - if( pOld==0 ) return lemon_malloc(nNew); - p = (MemChunk*)pOld; - p--; - if( p->sz>=nNew ) return pOld; - pNew = lemon_malloc( nNew ); - memcpy(pNew, pOld, p->sz); - return pNew; -} - -/* Free all outstanding memory allocations. -** Do this right before exiting. -*/ -static void lemon_free_all(void){ - while( memChunkList ){ - MemChunk *pNext = memChunkList->pNext; - free( memChunkList ); - memChunkList = pNext; - } -} - -/* -** Compilers are starting to complain about the use of sprintf() and strcpy(), -** saying they are unsafe. So we define our own versions of those routines too. -** -** There are three routines here: lemon_sprintf(), lemon_vsprintf(), and -** lemon_addtext(). The first two are replacements for sprintf() and vsprintf(). -** The third is a helper routine for vsnprintf() that adds texts to the end of a -** buffer, making sure the buffer is always zero-terminated. -** -** The string formatter is a minimal subset of stdlib sprintf() supporting only -** a few simply conversions: -** -** %d -** %s -** %.*s -** -*/ -static void lemon_addtext( - char *zBuf, /* The buffer to which text is added */ - int *pnUsed, /* Slots of the buffer used so far */ - const char *zIn, /* Text to add */ - int nIn, /* Bytes of text to add. -1 to use strlen() */ - int iWidth /* Field width. Negative to left justify */ -){ - if( nIn<0 ) for(nIn=0; zIn[nIn]; nIn++){} - while( iWidth>nIn ){ zBuf[(*pnUsed)++] = ' '; iWidth--; } - if( nIn==0 ) return; - memcpy(&zBuf[*pnUsed], zIn, nIn); - *pnUsed += nIn; - while( (-iWidth)>nIn ){ zBuf[(*pnUsed)++] = ' '; iWidth++; } - zBuf[*pnUsed] = 0; -} -static int lemon_vsprintf(char *str, const char *zFormat, va_list ap){ - int i, j, k, c; - int nUsed = 0; - const char *z; - char zTemp[50]; - str[0] = 0; - for(i=j=0; (c = zFormat[i])!=0; i++){ - if( c=='%' ){ - int iWidth = 0; - lemon_addtext(str, &nUsed, &zFormat[j], i-j, 0); - c = zFormat[++i]; - if( ISDIGIT(c) || (c=='-' && ISDIGIT(zFormat[i+1])) ){ - if( c=='-' ) i++; - while( ISDIGIT(zFormat[i]) ) iWidth = iWidth*10 + zFormat[i++] - '0'; - if( c=='-' ) iWidth = -iWidth; - c = zFormat[i]; - } - if( c=='d' ){ - int v = va_arg(ap, int); - if( v<0 ){ - lemon_addtext(str, &nUsed, "-", 1, iWidth); - v = -v; - }else if( v==0 ){ - lemon_addtext(str, &nUsed, "0", 1, iWidth); - } - k = 0; - while( v>0 ){ - k++; - zTemp[sizeof(zTemp)-k] = (v%10) + '0'; - v /= 10; - } - lemon_addtext(str, &nUsed, &zTemp[sizeof(zTemp)-k], k, iWidth); - }else if( c=='s' ){ - z = va_arg(ap, const char*); - lemon_addtext(str, &nUsed, z, -1, iWidth); - }else if( c=='.' && memcmp(&zFormat[i], ".*s", 3)==0 ){ - i += 2; - k = va_arg(ap, int); - z = va_arg(ap, const char*); - lemon_addtext(str, &nUsed, z, k, iWidth); - }else if( c=='%' ){ - lemon_addtext(str, &nUsed, "%", 1, 0); - }else{ - fprintf(stderr, "illegal format\n"); - exit(1); - } - j = i+1; - } - } - lemon_addtext(str, &nUsed, &zFormat[j], i-j, 0); - return nUsed; -} -static int lemon_sprintf(char *str, const char *format, ...){ - va_list ap; - int rc; - va_start(ap, format); - rc = lemon_vsprintf(str, format, ap); - va_end(ap); - return rc; -} -static void lemon_strcpy(char *dest, const char *src){ - while( (*(dest++) = *(src++))!=0 ){} -} -static void lemon_strcat(char *dest, const char *src){ - while( *dest ) dest++; - lemon_strcpy(dest, src); -} - - -/* a few forward declarations... */ -struct rule; -struct lemon; -struct action; - -static struct action *Action_new(void); -static struct action *Action_sort(struct action *); - -/********** From the file "build.h" ************************************/ -void FindRulePrecedences(struct lemon*); -void FindFirstSets(struct lemon*); -void FindStates(struct lemon*); -void FindLinks(struct lemon*); -void FindFollowSets(struct lemon*); -void FindActions(struct lemon*); - -/********* From the file "configlist.h" *********************************/ -void Configlist_init(void); -struct config *Configlist_add(struct rule *, int); -struct config *Configlist_addbasis(struct rule *, int); -void Configlist_closure(struct lemon *); -void Configlist_sort(void); -void Configlist_sortbasis(void); -struct config *Configlist_return(void); -struct config *Configlist_basis(void); -void Configlist_eat(struct config *); -void Configlist_reset(void); - -/********* From the file "error.h" ***************************************/ -void ErrorMsg(const char *, int,const char *, ...); - -/****** From the file "option.h" ******************************************/ -enum option_type { OPT_FLAG=1, OPT_INT, OPT_DBL, OPT_STR, - OPT_FFLAG, OPT_FINT, OPT_FDBL, OPT_FSTR}; -struct s_options { - enum option_type type; - const char *label; - char *arg; - const char *message; -}; -int OptInit(char**,struct s_options*,FILE*); -int OptNArgs(void); -char *OptArg(int); - -void OptPrint(void); - -/******** From the file "parse.h" *****************************************/ -void Parse(struct lemon *lemp); - -/********* From the file "plink.h" ***************************************/ -struct plink *Plink_new(void); -void Plink_add(struct plink **, struct config *); -void Plink_copy(struct plink **, struct plink *); -void Plink_delete(struct plink *); - -/********** From the file "report.h" *************************************/ -void Reprint(struct lemon *); -void ReportOutput(struct lemon *); -void ReportTable(struct lemon *, int, int); -void ReportHeader(struct lemon *); -void CompressTables(struct lemon *); -void ResortStates(struct lemon *); - -/********** From the file "set.h" ****************************************/ -void SetSize(int); /* All sets will be of size N */ -char *SetNew(void); /* A new set for element 0..N */ -void SetFree(char*); /* Deallocate a set */ -int SetAdd(char*,int); /* Add element to a set */ -int SetUnion(char *,char *); /* A <- A U B, thru element N */ -#define SetFind(X,Y) (X[Y]) /* True if Y is in set X */ - -/********** From the file "struct.h" *************************************/ -/* -** Principal data structures for the LEMON parser generator. -*/ - -typedef enum {LEMON_FALSE=0, LEMON_TRUE} Boolean; - -/* Symbols (terminals and nonterminals) of the grammar are stored -** in the following: */ -enum symbol_type { - TERMINAL, - NONTERMINAL, - MULTITERMINAL -}; -enum e_assoc { - LEFT, - RIGHT, - NONE, - UNK -}; -struct symbol { - const char *name; /* Name of the symbol */ - int index; /* Index number for this symbol */ - enum symbol_type type; /* Symbols are all either TERMINALS or NTs */ - struct rule *rule; /* Linked list of rules of this (if an NT) */ - struct symbol *fallback; /* fallback token in case this token doesn't parse */ - int prec; /* Precedence if defined (-1 otherwise) */ - enum e_assoc assoc; /* Associativity if precedence is defined */ - char *firstset; /* First-set for all rules of this symbol */ - Boolean lambda; /* True if NT and can generate an empty string */ - int useCnt; /* Number of times used */ - char *datatype; /* The data type of information held by this - ** object. Only used if type==NONTERMINAL */ - int dtnum; /* The data type number. In the parser, the value - ** stack is a union. The .yy%d element of this - ** union is the correct data type for this object */ - int bContent; /* True if this symbol ever carries content - if - ** it is ever more than just syntax */ - /* The following fields are used by MULTITERMINALs only */ - int nsubsym; /* Number of constituent symbols in the MULTI */ - struct symbol **subsym; /* Array of constituent symbols */ -}; - -/* Each production rule in the grammar is stored in the following -** structure. */ -struct rule { - struct symbol *lhs; /* Left-hand side of the rule */ - const char *lhsalias; /* Alias for the LHS (NULL if none) */ - int lhsStart; /* True if left-hand side is the start symbol */ - int ruleline; /* Line number for the rule */ - int nrhs; /* Number of RHS symbols */ - struct symbol **rhs; /* The RHS symbols */ - const char **rhsalias; /* An alias for each RHS symbol (NULL if none) */ - int line; /* Line number at which code begins */ - const char *code; /* The code executed when this rule is reduced */ - const char *codePrefix; /* Setup code before code[] above */ - const char *codeSuffix; /* Breakdown code after code[] above */ - struct symbol *precsym; /* Precedence symbol for this rule */ - int index; /* An index number for this rule */ - int iRule; /* Rule number as used in the generated tables */ - Boolean noCode; /* True if this rule has no associated C code */ - Boolean codeEmitted; /* True if the code has been emitted already */ - Boolean canReduce; /* True if this rule is ever reduced */ - Boolean doesReduce; /* Reduce actions occur after optimization */ - Boolean neverReduce; /* Reduce is theoretically possible, but prevented - ** by actions or other outside implementation */ - struct rule *nextlhs; /* Next rule with the same LHS */ - struct rule *next; /* Next rule in the global list */ -}; - -/* A configuration is a production rule of the grammar together with -** a mark (dot) showing how much of that rule has been processed so far. -** Configurations also contain a follow-set which is a list of terminal -** symbols which are allowed to immediately follow the end of the rule. -** Every configuration is recorded as an instance of the following: */ -enum cfgstatus { - COMPLETE, - INCOMPLETE -}; -struct config { - struct rule *rp; /* The rule upon which the configuration is based */ - int dot; /* The parse point */ - char *fws; /* Follow-set for this configuration only */ - struct plink *fplp; /* Follow-set forward propagation links */ - struct plink *bplp; /* Follow-set backwards propagation links */ - struct state *stp; /* Pointer to state which contains this */ - enum cfgstatus status; /* used during followset and shift computations */ - struct config *next; /* Next configuration in the state */ - struct config *bp; /* The next basis configuration */ -}; - -enum e_action { - SHIFT, - ACCEPT, - REDUCE, - ERROR, - SSCONFLICT, /* A shift/shift conflict */ - SRCONFLICT, /* Was a reduce, but part of a conflict */ - RRCONFLICT, /* Was a reduce, but part of a conflict */ - SH_RESOLVED, /* Was a shift. Precedence resolved conflict */ - RD_RESOLVED, /* Was reduce. Precedence resolved conflict */ - NOT_USED, /* Deleted by compression */ - SHIFTREDUCE /* Shift first, then reduce */ -}; - -/* Every shift or reduce operation is stored as one of the following */ -struct action { - struct symbol *sp; /* The look-ahead symbol */ - enum e_action type; - union { - struct state *stp; /* The new state, if a shift */ - struct rule *rp; /* The rule, if a reduce */ - } x; - struct symbol *spOpt; /* SHIFTREDUCE optimization to this symbol */ - struct action *next; /* Next action for this state */ -}; - -/* Each state of the generated parser's finite state machine -** is encoded as an instance of the following structure. */ -struct state { - struct config *bp; /* The basis configurations for this state */ - struct config *cfp; /* All configurations in this set */ - int statenum; /* Sequential number for this state */ - struct action *ap; /* List of actions for this state */ - int nTknAct, nNtAct; /* Number of actions on terminals and nonterminals */ - int iTknOfst, iNtOfst; /* yy_action[] offset for terminals and nonterms */ - int iDfltReduce; /* Default action is to REDUCE by this rule */ - struct rule *pDfltReduce;/* The default REDUCE rule. */ - int autoReduce; /* True if this is an auto-reduce state */ -}; -#define NO_OFFSET (-2147483647) - -/* A followset propagation link indicates that the contents of one -** configuration followset should be propagated to another whenever -** the first changes. */ -struct plink { - struct config *cfp; /* The configuration to which linked */ - struct plink *next; /* The next propagate link */ -}; - -/* The state vector for the entire parser generator is recorded as -** follows. (LEMON uses no global variables and makes little use of -** static variables. Fields in the following structure can be thought -** of as begin global variables in the program.) */ -struct lemon { - struct state **sorted; /* Table of states sorted by state number */ - struct rule *rule; /* List of all rules */ - struct rule *startRule; /* First rule */ - int nstate; /* Number of states */ - int nxstate; /* nstate with tail degenerate states removed */ - int nrule; /* Number of rules */ - int nruleWithAction; /* Number of rules with actions */ - int nsymbol; /* Number of terminal and nonterminal symbols */ - int nterminal; /* Number of terminal symbols */ - int minShiftReduce; /* Minimum shift-reduce action value */ - int errAction; /* Error action value */ - int accAction; /* Accept action value */ - int noAction; /* No-op action value */ - int minReduce; /* Minimum reduce action */ - int maxAction; /* Maximum action value of any kind */ - struct symbol **symbols; /* Sorted array of pointers to symbols */ - int errorcnt; /* Number of errors */ - struct symbol *errsym; /* The error symbol */ - struct symbol *wildcard; /* Token that matches anything */ - char *name; /* Name of the generated parser */ - char *arg; /* Declaration of the 3rd argument to parser */ - char *ctx; /* Declaration of 2nd argument to constructor */ - char *tokentype; /* Type of terminal symbols in the parser stack */ - char *vartype; /* The default type of non-terminal symbols */ - char *start; /* Name of the start symbol for the grammar */ - char *stacksize; /* Size of the parser stack */ - char *include; /* Code to put at the start of the C file */ - char *error; /* Code to execute when an error is seen */ - char *overflow; /* Code to execute on a stack overflow */ - char *failure; /* Code to execute on parser failure */ - char *accept; /* Code to execute when the parser excepts */ - char *extracode; /* Code appended to the generated file */ - char *filename; /* Name of the input file */ - char *outname; /* Name of the current output file */ - char *tokenprefix; /* A prefix added to token names in the .h file */ - int nconflict; /* Number of parsing conflicts */ - int nactiontab; /* Number of entries in the yy_action[] table */ - int nlookaheadtab; /* Number of entries in yy_lookahead[] */ - int tablesize; /* Total table size of all tables in bytes */ - int basisflag; /* Print only basis configurations */ - int printPreprocessed; /* Show preprocessor output on stdout */ - int has_fallback; /* True if any %fallback is seen in the grammar */ - int nolinenosflag; /* True if #line statements should not be printed */ - int argc; /* Number of command-line arguments */ - char **argv; /* Command-line arguments */ -}; - -#define MemoryCheck(X) if((X)==0){ \ - extern void memory_error(); \ - memory_error(); \ -} - -/**************** From the file "table.h" *********************************/ -/* -** All code in this file has been automatically generated -** from a specification in the file -** "table.q" -** by the associative array code building program "aagen". -** Do not edit this file! Instead, edit the specification -** file, then rerun aagen. -*/ -/* -** Code for processing tables in the LEMON parser generator. -*/ -/* Routines for handling a strings */ - -const char *Strsafe(const char *); - -void Strsafe_init(void); -int Strsafe_insert(const char *); -const char *Strsafe_find(const char *); - -/* Routines for handling symbols of the grammar */ - -struct symbol *Symbol_new(const char *); -int Symbolcmpp(const void *, const void *); -void Symbol_init(void); -int Symbol_insert(struct symbol *, const char *); -struct symbol *Symbol_find(const char *); - -int Symbol_count(void); -struct symbol **Symbol_arrayof(void); - -/* Routines to manage the state table */ - -int Configcmp(const char *, const char *); -struct state *State_new(void); -void State_init(void); -int State_insert(struct state *, struct config *); -struct state *State_find(struct config *); -struct state **State_arrayof(void); - -/* Routines used for efficiency in Configlist_add */ - -void Configtable_init(void); -int Configtable_insert(struct config *); -struct config *Configtable_find(struct config *); -void Configtable_clear(int(*)(struct config *)); - -/****************** From the file "action.c" *******************************/ -/* -** Routines processing parser actions in the LEMON parser generator. -*/ - -/* Allocate a new parser action */ -static struct action *Action_new(void){ - static struct action *actionfreelist = 0; - struct action *newaction; - - if( actionfreelist==0 ){ - int i; - int amt = 100; - actionfreelist = (struct action *)lemon_calloc(amt, sizeof(struct action)); - if( actionfreelist==0 ){ - fprintf(stderr,"Unable to allocate memory for a new parser action."); - exit(1); - } - for(i=0; inext; - return newaction; -} - -/* Compare two actions for sorting purposes. Return negative, zero, or -** positive if the first action is less than, equal to, or greater than -** the first -*/ -static int actioncmp( - struct action *ap1, - struct action *ap2 -){ - int rc; - rc = ap1->sp->index - ap2->sp->index; - if( rc==0 ){ - rc = (int)ap1->type - (int)ap2->type; - } - if( rc==0 && (ap1->type==REDUCE || ap1->type==SHIFTREDUCE) ){ - rc = ap1->x.rp->index - ap2->x.rp->index; - } - if( rc==0 ){ - rc = (int) (ap2 - ap1); - } - return rc; -} - -/* Sort parser actions */ -static struct action *Action_sort( - struct action *ap -){ - ap = (struct action *)msort((char *)ap,(char **)&ap->next, - (int(*)(const char*,const char*))actioncmp); - return ap; -} - -void Action_add( - struct action **app, - enum e_action type, - struct symbol *sp, - char *arg -){ - struct action *newaction; - newaction = Action_new(); - newaction->next = *app; - *app = newaction; - newaction->type = type; - newaction->sp = sp; - newaction->spOpt = 0; - if( type==SHIFT ){ - newaction->x.stp = (struct state *)arg; - }else{ - newaction->x.rp = (struct rule *)arg; - } -} -/********************** New code to implement the "acttab" module ***********/ -/* -** This module implements routines use to construct the yy_action[] table. -*/ - -/* -** The state of the yy_action table under construction is an instance of -** the following structure. -** -** The yy_action table maps the pair (state_number, lookahead) into an -** action_number. The table is an array of integers pairs. The state_number -** determines an initial offset into the yy_action array. The lookahead -** value is then added to this initial offset to get an index X into the -** yy_action array. If the aAction[X].lookahead equals the value of the -** of the lookahead input, then the value of the action_number output is -** aAction[X].action. If the lookaheads do not match then the -** default action for the state_number is returned. -** -** All actions associated with a single state_number are first entered -** into aLookahead[] using multiple calls to acttab_action(). Then the -** actions for that single state_number are placed into the aAction[] -** array with a single call to acttab_insert(). The acttab_insert() call -** also resets the aLookahead[] array in preparation for the next -** state number. -*/ -struct lookahead_action { - int lookahead; /* Value of the lookahead token */ - int action; /* Action to take on the given lookahead */ -}; -typedef struct acttab acttab; -struct acttab { - int nAction; /* Number of used slots in aAction[] */ - int nActionAlloc; /* Slots allocated for aAction[] */ - struct lookahead_action - *aAction, /* The yy_action[] table under construction */ - *aLookahead; /* A single new transaction set */ - int mnLookahead; /* Minimum aLookahead[].lookahead */ - int mnAction; /* Action associated with mnLookahead */ - int mxLookahead; /* Maximum aLookahead[].lookahead */ - int nLookahead; /* Used slots in aLookahead[] */ - int nLookaheadAlloc; /* Slots allocated in aLookahead[] */ - int nterminal; /* Number of terminal symbols */ - int nsymbol; /* total number of symbols */ -}; - -/* Return the number of entries in the yy_action table */ -#define acttab_lookahead_size(X) ((X)->nAction) - -/* The value for the N-th entry in yy_action */ -#define acttab_yyaction(X,N) ((X)->aAction[N].action) - -/* The value for the N-th entry in yy_lookahead */ -#define acttab_yylookahead(X,N) ((X)->aAction[N].lookahead) - -/* Free all memory associated with the given acttab */ -void acttab_free(acttab *p){ - lemon_free( p->aAction ); - lemon_free( p->aLookahead ); - lemon_free( p ); -} - -/* Allocate a new acttab structure */ -acttab *acttab_alloc(int nsymbol, int nterminal){ - acttab *p = (acttab *) lemon_calloc( 1, sizeof(*p) ); - if( p==0 ){ - fprintf(stderr,"Unable to allocate memory for a new acttab."); - exit(1); - } - memset(p, 0, sizeof(*p)); - p->nsymbol = nsymbol; - p->nterminal = nterminal; - return p; -} - -/* Add a new action to the current transaction set. -** -** This routine is called once for each lookahead for a particular -** state. -*/ -void acttab_action(acttab *p, int lookahead, int action){ - if( p->nLookahead>=p->nLookaheadAlloc ){ - p->nLookaheadAlloc += 25; - p->aLookahead = (struct lookahead_action *) lemon_realloc( p->aLookahead, - sizeof(p->aLookahead[0])*p->nLookaheadAlloc ); - if( p->aLookahead==0 ){ - fprintf(stderr,"malloc failed\n"); - exit(1); - } - } - if( p->nLookahead==0 ){ - p->mxLookahead = lookahead; - p->mnLookahead = lookahead; - p->mnAction = action; - }else{ - if( p->mxLookaheadmxLookahead = lookahead; - if( p->mnLookahead>lookahead ){ - p->mnLookahead = lookahead; - p->mnAction = action; - } - } - p->aLookahead[p->nLookahead].lookahead = lookahead; - p->aLookahead[p->nLookahead].action = action; - p->nLookahead++; -} - -/* -** Add the transaction set built up with prior calls to acttab_action() -** into the current action table. Then reset the transaction set back -** to an empty set in preparation for a new round of acttab_action() calls. -** -** Return the offset into the action table of the new transaction. -** -** If the makeItSafe parameter is true, then the offset is chosen so that -** it is impossible to overread the yy_lookaside[] table regardless of -** the lookaside token. This is done for the terminal symbols, as they -** come from external inputs and can contain syntax errors. When makeItSafe -** is false, there is more flexibility in selecting offsets, resulting in -** a smaller table. For non-terminal symbols, which are never syntax errors, -** makeItSafe can be false. -*/ -int acttab_insert(acttab *p, int makeItSafe){ - int i, j, k, n, end; - assert( p->nLookahead>0 ); - - /* Make sure we have enough space to hold the expanded action table - ** in the worst case. The worst case occurs if the transaction set - ** must be appended to the current action table - */ - n = p->nsymbol + 1; - if( p->nAction + n >= p->nActionAlloc ){ - int oldAlloc = p->nActionAlloc; - p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20; - p->aAction = (struct lookahead_action *) lemon_realloc( p->aAction, - sizeof(p->aAction[0])*p->nActionAlloc); - if( p->aAction==0 ){ - fprintf(stderr,"malloc failed\n"); - exit(1); - } - for(i=oldAlloc; inActionAlloc; i++){ - p->aAction[i].lookahead = -1; - p->aAction[i].action = -1; - } - } - - /* Scan the existing action table looking for an offset that is a - ** duplicate of the current transaction set. Fall out of the loop - ** if and when the duplicate is found. - ** - ** i is the index in p->aAction[] where p->mnLookahead is inserted. - */ - end = makeItSafe ? p->mnLookahead : 0; - for(i=p->nAction-1; i>=end; i--){ - if( p->aAction[i].lookahead==p->mnLookahead ){ - /* All lookaheads and actions in the aLookahead[] transaction - ** must match against the candidate aAction[i] entry. */ - if( p->aAction[i].action!=p->mnAction ) continue; - for(j=0; jnLookahead; j++){ - k = p->aLookahead[j].lookahead - p->mnLookahead + i; - if( k<0 || k>=p->nAction ) break; - if( p->aLookahead[j].lookahead!=p->aAction[k].lookahead ) break; - if( p->aLookahead[j].action!=p->aAction[k].action ) break; - } - if( jnLookahead ) continue; - - /* No possible lookahead value that is not in the aLookahead[] - ** transaction is allowed to match aAction[i] */ - n = 0; - for(j=0; jnAction; j++){ - if( p->aAction[j].lookahead<0 ) continue; - if( p->aAction[j].lookahead==j+p->mnLookahead-i ) n++; - } - if( n==p->nLookahead ){ - break; /* An exact match is found at offset i */ - } - } - } - - /* If no existing offsets exactly match the current transaction, find an - ** an empty offset in the aAction[] table in which we can add the - ** aLookahead[] transaction. - */ - if( inAction, which means the - ** transaction will be appended. */ - i = makeItSafe ? p->mnLookahead : 0; - for(; inActionAlloc - p->mxLookahead; i++){ - if( p->aAction[i].lookahead<0 ){ - for(j=0; jnLookahead; j++){ - k = p->aLookahead[j].lookahead - p->mnLookahead + i; - if( k<0 ) break; - if( p->aAction[k].lookahead>=0 ) break; - } - if( jnLookahead ) continue; - for(j=0; jnAction; j++){ - if( p->aAction[j].lookahead==j+p->mnLookahead-i ) break; - } - if( j==p->nAction ){ - break; /* Fits in empty slots */ - } - } - } - } - /* Insert transaction set at index i. */ -#if 0 - printf("Acttab:"); - for(j=0; jnLookahead; j++){ - printf(" %d", p->aLookahead[j].lookahead); - } - printf(" inserted at %d\n", i); -#endif - for(j=0; jnLookahead; j++){ - k = p->aLookahead[j].lookahead - p->mnLookahead + i; - p->aAction[k] = p->aLookahead[j]; - if( k>=p->nAction ) p->nAction = k+1; - } - if( makeItSafe && i+p->nterminal>=p->nAction ) p->nAction = i+p->nterminal+1; - p->nLookahead = 0; - - /* Return the offset that is added to the lookahead in order to get the - ** index into yy_action of the action */ - return i - p->mnLookahead; -} - -/* -** Return the size of the action table without the trailing syntax error -** entries. -*/ -int acttab_action_size(acttab *p){ - int n = p->nAction; - while( n>0 && p->aAction[n-1].lookahead<0 ){ n--; } - return n; -} - -/********************** From the file "build.c" *****************************/ -/* -** Routines to construction the finite state machine for the LEMON -** parser generator. -*/ - -/* Find a precedence symbol of every rule in the grammar. -** -** Those rules which have a precedence symbol coded in the input -** grammar using the "[symbol]" construct will already have the -** rp->precsym field filled. Other rules take as their precedence -** symbol the first RHS symbol with a defined precedence. If there -** are not RHS symbols with a defined precedence, the precedence -** symbol field is left blank. -*/ -void FindRulePrecedences(struct lemon *xp) -{ - struct rule *rp; - for(rp=xp->rule; rp; rp=rp->next){ - if( rp->precsym==0 ){ - int i, j; - for(i=0; inrhs && rp->precsym==0; i++){ - struct symbol *sp = rp->rhs[i]; - if( sp->type==MULTITERMINAL ){ - for(j=0; jnsubsym; j++){ - if( sp->subsym[j]->prec>=0 ){ - rp->precsym = sp->subsym[j]; - break; - } - } - }else if( sp->prec>=0 ){ - rp->precsym = rp->rhs[i]; - } - } - } - } - return; -} - -/* Find all nonterminals which will generate the empty string. -** Then go back and compute the first sets of every nonterminal. -** The first set is the set of all terminal symbols which can begin -** a string generated by that nonterminal. -*/ -void FindFirstSets(struct lemon *lemp) -{ - int i, j; - struct rule *rp; - int progress; - - for(i=0; insymbol; i++){ - lemp->symbols[i]->lambda = LEMON_FALSE; - } - for(i=lemp->nterminal; insymbol; i++){ - lemp->symbols[i]->firstset = SetNew(); - } - - /* First compute all lambdas */ - do{ - progress = 0; - for(rp=lemp->rule; rp; rp=rp->next){ - if( rp->lhs->lambda ) continue; - for(i=0; inrhs; i++){ - struct symbol *sp = rp->rhs[i]; - assert( sp->type==NONTERMINAL || sp->lambda==LEMON_FALSE ); - if( sp->lambda==LEMON_FALSE ) break; - } - if( i==rp->nrhs ){ - rp->lhs->lambda = LEMON_TRUE; - progress = 1; - } - } - }while( progress ); - - /* Now compute all first sets */ - do{ - struct symbol *s1, *s2; - progress = 0; - for(rp=lemp->rule; rp; rp=rp->next){ - s1 = rp->lhs; - for(i=0; inrhs; i++){ - s2 = rp->rhs[i]; - if( s2->type==TERMINAL ){ - progress += SetAdd(s1->firstset,s2->index); - break; - }else if( s2->type==MULTITERMINAL ){ - for(j=0; jnsubsym; j++){ - progress += SetAdd(s1->firstset,s2->subsym[j]->index); - } - break; - }else if( s1==s2 ){ - if( s1->lambda==LEMON_FALSE ) break; - }else{ - progress += SetUnion(s1->firstset,s2->firstset); - if( s2->lambda==LEMON_FALSE ) break; - } - } - } - }while( progress ); - return; -} - -/* Compute all LR(0) states for the grammar. Links -** are added to between some states so that the LR(1) follow sets -** can be computed later. -*/ -PRIVATE struct state *getstate(struct lemon *); /* forward reference */ -void FindStates(struct lemon *lemp) -{ - struct symbol *sp; - struct rule *rp; - - Configlist_init(); - - /* Find the start symbol */ - if( lemp->start ){ - sp = Symbol_find(lemp->start); - if( sp==0 ){ - ErrorMsg(lemp->filename,0, - "The specified start symbol \"%s\" is not " - "in a nonterminal of the grammar. \"%s\" will be used as the start " - "symbol instead.",lemp->start,lemp->startRule->lhs->name); - lemp->errorcnt++; - sp = lemp->startRule->lhs; - } - }else if( lemp->startRule ){ - sp = lemp->startRule->lhs; - }else{ - ErrorMsg(lemp->filename,0,"Internal error - no start rule\n"); - exit(1); - } - - /* Make sure the start symbol doesn't occur on the right-hand side of - ** any rule. Report an error if it does. (YACC would generate a new - ** start symbol in this case.) */ - for(rp=lemp->rule; rp; rp=rp->next){ - int i; - for(i=0; inrhs; i++){ - if( rp->rhs[i]==sp ){ /* FIX ME: Deal with multiterminals */ - ErrorMsg(lemp->filename,0, - "The start symbol \"%s\" occurs on the " - "right-hand side of a rule. This will result in a parser which " - "does not work properly.",sp->name); - lemp->errorcnt++; - } - } - } - - /* The basis configuration set for the first state - ** is all rules which have the start symbol as their - ** left-hand side */ - for(rp=sp->rule; rp; rp=rp->nextlhs){ - struct config *newcfp; - rp->lhsStart = 1; - newcfp = Configlist_addbasis(rp,0); - SetAdd(newcfp->fws,0); - } - - /* Compute the first state. All other states will be - ** computed automatically during the computation of the first one. - ** The returned pointer to the first state is not used. */ - (void)getstate(lemp); - return; -} - -/* Return a pointer to a state which is described by the configuration -** list which has been built from calls to Configlist_add. -*/ -PRIVATE void buildshifts(struct lemon *, struct state *); /* Forwd ref */ -PRIVATE struct state *getstate(struct lemon *lemp) -{ - struct config *cfp, *bp; - struct state *stp; - - /* Extract the sorted basis of the new state. The basis was constructed - ** by prior calls to "Configlist_addbasis()". */ - Configlist_sortbasis(); - bp = Configlist_basis(); - - /* Get a state with the same basis */ - stp = State_find(bp); - if( stp ){ - /* A state with the same basis already exists! Copy all the follow-set - ** propagation links from the state under construction into the - ** preexisting state, then return a pointer to the preexisting state */ - struct config *x, *y; - for(x=bp, y=stp->bp; x && y; x=x->bp, y=y->bp){ - Plink_copy(&y->bplp,x->bplp); - Plink_delete(x->fplp); - x->fplp = x->bplp = 0; - } - cfp = Configlist_return(); - Configlist_eat(cfp); - }else{ - /* This really is a new state. Construct all the details */ - Configlist_closure(lemp); /* Compute the configuration closure */ - Configlist_sort(); /* Sort the configuration closure */ - cfp = Configlist_return(); /* Get a pointer to the config list */ - stp = State_new(); /* A new state structure */ - MemoryCheck(stp); - stp->bp = bp; /* Remember the configuration basis */ - stp->cfp = cfp; /* Remember the configuration closure */ - stp->statenum = lemp->nstate++; /* Every state gets a sequence number */ - stp->ap = 0; /* No actions, yet. */ - State_insert(stp,stp->bp); /* Add to the state table */ - buildshifts(lemp,stp); /* Recursively compute successor states */ - } - return stp; -} - -/* -** Return true if two symbols are the same. -*/ -int same_symbol(struct symbol *a, struct symbol *b) -{ - int i; - if( a==b ) return 1; - if( a->type!=MULTITERMINAL ) return 0; - if( b->type!=MULTITERMINAL ) return 0; - if( a->nsubsym!=b->nsubsym ) return 0; - for(i=0; insubsym; i++){ - if( a->subsym[i]!=b->subsym[i] ) return 0; - } - return 1; -} - -/* Construct all successor states to the given state. A "successor" -** state is any state which can be reached by a shift action. -*/ -PRIVATE void buildshifts(struct lemon *lemp, struct state *stp) -{ - struct config *cfp; /* For looping thru the config closure of "stp" */ - struct config *bcfp; /* For the inner loop on config closure of "stp" */ - struct config *newcfg; /* */ - struct symbol *sp; /* Symbol following the dot in configuration "cfp" */ - struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */ - struct state *newstp; /* A pointer to a successor state */ - - /* Each configuration becomes complete after it contributes to a successor - ** state. Initially, all configurations are incomplete */ - for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE; - - /* Loop through all configurations of the state "stp" */ - for(cfp=stp->cfp; cfp; cfp=cfp->next){ - if( cfp->status==COMPLETE ) continue; /* Already used by inner loop */ - if( cfp->dot>=cfp->rp->nrhs ) continue; /* Can't shift this config */ - Configlist_reset(); /* Reset the new config set */ - sp = cfp->rp->rhs[cfp->dot]; /* Symbol after the dot */ - - /* For every configuration in the state "stp" which has the symbol "sp" - ** following its dot, add the same configuration to the basis set under - ** construction but with the dot shifted one symbol to the right. */ - for(bcfp=cfp; bcfp; bcfp=bcfp->next){ - if( bcfp->status==COMPLETE ) continue; /* Already used */ - if( bcfp->dot>=bcfp->rp->nrhs ) continue; /* Can't shift this one */ - bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */ - if( !same_symbol(bsp,sp) ) continue; /* Must be same as for "cfp" */ - bcfp->status = COMPLETE; /* Mark this config as used */ - newcfg = Configlist_addbasis(bcfp->rp,bcfp->dot+1); - Plink_add(&newcfg->bplp,bcfp); - } - - /* Get a pointer to the state described by the basis configuration set - ** constructed in the preceding loop */ - newstp = getstate(lemp); - - /* The state "newstp" is reached from the state "stp" by a shift action - ** on the symbol "sp" */ - if( sp->type==MULTITERMINAL ){ - int i; - for(i=0; insubsym; i++){ - Action_add(&stp->ap,SHIFT,sp->subsym[i],(char*)newstp); - } - }else{ - Action_add(&stp->ap,SHIFT,sp,(char *)newstp); - } - } -} - -/* -** Construct the propagation links -*/ -void FindLinks(struct lemon *lemp) -{ - int i; - struct config *cfp, *other; - struct state *stp; - struct plink *plp; - - /* Housekeeping detail: - ** Add to every propagate link a pointer back to the state to - ** which the link is attached. */ - for(i=0; instate; i++){ - stp = lemp->sorted[i]; - for(cfp=stp?stp->cfp:0; cfp; cfp=cfp->next){ - cfp->stp = stp; - } - } - - /* Convert all backlinks into forward links. Only the forward - ** links are used in the follow-set computation. */ - for(i=0; instate; i++){ - stp = lemp->sorted[i]; - for(cfp=stp?stp->cfp:0; cfp; cfp=cfp->next){ - for(plp=cfp->bplp; plp; plp=plp->next){ - other = plp->cfp; - Plink_add(&other->fplp,cfp); - } - } - } -} - -/* Compute all followsets. -** -** A followset is the set of all symbols which can come immediately -** after a configuration. -*/ -void FindFollowSets(struct lemon *lemp) -{ - int i; - struct config *cfp; - struct plink *plp; - int progress; - int change; - - for(i=0; instate; i++){ - assert( lemp->sorted[i]!=0 ); - for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ - cfp->status = INCOMPLETE; - } - } - - do{ - progress = 0; - for(i=0; instate; i++){ - assert( lemp->sorted[i]!=0 ); - for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ - if( cfp->status==COMPLETE ) continue; - for(plp=cfp->fplp; plp; plp=plp->next){ - change = SetUnion(plp->cfp->fws,cfp->fws); - if( change ){ - plp->cfp->status = INCOMPLETE; - progress = 1; - } - } - cfp->status = COMPLETE; - } - } - }while( progress ); -} - -static int resolve_conflict(struct action *,struct action *); - -/* Compute the reduce actions, and resolve conflicts. -*/ -void FindActions(struct lemon *lemp) -{ - int i,j; - struct config *cfp; - struct state *stp; - struct symbol *sp; - struct rule *rp; - - /* Add all of the reduce actions - ** A reduce action is added for each element of the followset of - ** a configuration which has its dot at the extreme right. - */ - for(i=0; instate; i++){ /* Loop over all states */ - stp = lemp->sorted[i]; - for(cfp=stp->cfp; cfp; cfp=cfp->next){ /* Loop over all configurations */ - if( cfp->rp->nrhs==cfp->dot ){ /* Is dot at extreme right? */ - for(j=0; jnterminal; j++){ - if( SetFind(cfp->fws,j) ){ - /* Add a reduce action to the state "stp" which will reduce by the - ** rule "cfp->rp" if the lookahead symbol is "lemp->symbols[j]" */ - Action_add(&stp->ap,REDUCE,lemp->symbols[j],(char *)cfp->rp); - } - } - } - } - } - - /* Add the accepting token */ - if( lemp->start ){ - sp = Symbol_find(lemp->start); - if( sp==0 ){ - if( lemp->startRule==0 ){ - fprintf(stderr, "internal error on source line %d: no start rule\n", - __LINE__); - exit(1); - } - sp = lemp->startRule->lhs; - } - }else{ - sp = lemp->startRule->lhs; - } - /* Add to the first state (which is always the starting state of the - ** finite state machine) an action to ACCEPT if the lookahead is the - ** start nonterminal. */ - Action_add(&lemp->sorted[0]->ap,ACCEPT,sp,0); - - /* Resolve conflicts */ - for(i=0; instate; i++){ - struct action *ap, *nap; - stp = lemp->sorted[i]; - /* assert( stp->ap ); */ - stp->ap = Action_sort(stp->ap); - for(ap=stp->ap; ap && ap->next; ap=ap->next){ - for(nap=ap->next; nap && nap->sp==ap->sp; nap=nap->next){ - /* The two actions "ap" and "nap" have the same lookahead. - ** Figure out which one should be used */ - lemp->nconflict += resolve_conflict(ap,nap); - } - } - } - - /* Report an error for each rule that can never be reduced. */ - for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = LEMON_FALSE; - for(i=0; instate; i++){ - struct action *ap; - for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){ - if( ap->type==REDUCE ) ap->x.rp->canReduce = LEMON_TRUE; - } - } - for(rp=lemp->rule; rp; rp=rp->next){ - if( rp->canReduce ) continue; - ErrorMsg(lemp->filename,rp->ruleline,"This rule can not be reduced.\n"); - lemp->errorcnt++; - } -} - -/* Resolve a conflict between the two given actions. If the -** conflict can't be resolved, return non-zero. -** -** NO LONGER TRUE: -** To resolve a conflict, first look to see if either action -** is on an error rule. In that case, take the action which -** is not associated with the error rule. If neither or both -** actions are associated with an error rule, then try to -** use precedence to resolve the conflict. -** -** If either action is a SHIFT, then it must be apx. This -** function won't work if apx->type==REDUCE and apy->type==SHIFT. -*/ -static int resolve_conflict( - struct action *apx, - struct action *apy -){ - struct symbol *spx, *spy; - int errcnt = 0; - assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */ - if( apx->type==SHIFT && apy->type==SHIFT ){ - apy->type = SSCONFLICT; - errcnt++; - } - if( apx->type==SHIFT && apy->type==REDUCE ){ - spx = apx->sp; - spy = apy->x.rp->precsym; - if( spy==0 || spx->prec<0 || spy->prec<0 ){ - /* Not enough precedence information. */ - apy->type = SRCONFLICT; - errcnt++; - }else if( spx->prec>spy->prec ){ /* higher precedence wins */ - apy->type = RD_RESOLVED; - }else if( spx->precprec ){ - apx->type = SH_RESOLVED; - }else if( spx->prec==spy->prec && spx->assoc==RIGHT ){ /* Use operator */ - apy->type = RD_RESOLVED; /* associativity */ - }else if( spx->prec==spy->prec && spx->assoc==LEFT ){ /* to break tie */ - apx->type = SH_RESOLVED; - }else{ - assert( spx->prec==spy->prec && spx->assoc==NONE ); - apx->type = ERROR; - } - }else if( apx->type==REDUCE && apy->type==REDUCE ){ - spx = apx->x.rp->precsym; - spy = apy->x.rp->precsym; - if( spx==0 || spy==0 || spx->prec<0 || - spy->prec<0 || spx->prec==spy->prec ){ - apy->type = RRCONFLICT; - errcnt++; - }else if( spx->prec>spy->prec ){ - apy->type = RD_RESOLVED; - }else if( spx->precprec ){ - apx->type = RD_RESOLVED; - } - }else{ - assert( - apx->type==SH_RESOLVED || - apx->type==RD_RESOLVED || - apx->type==SSCONFLICT || - apx->type==SRCONFLICT || - apx->type==RRCONFLICT || - apy->type==SH_RESOLVED || - apy->type==RD_RESOLVED || - apy->type==SSCONFLICT || - apy->type==SRCONFLICT || - apy->type==RRCONFLICT - ); - /* The REDUCE/SHIFT case cannot happen because SHIFTs come before - ** REDUCEs on the list. If we reach this point it must be because - ** the parser conflict had already been resolved. */ - } - return errcnt; -} -/********************* From the file "configlist.c" *************************/ -/* -** Routines to processing a configuration list and building a state -** in the LEMON parser generator. -*/ - -static struct config *freelist = 0; /* List of free configurations */ -static struct config *current = 0; /* Top of list of configurations */ -static struct config **currentend = 0; /* Last on list of configs */ -static struct config *basis = 0; /* Top of list of basis configs */ -static struct config **basisend = 0; /* End of list of basis configs */ - -/* Return a pointer to a new configuration */ -PRIVATE struct config *newconfig(void){ - return (struct config*)lemon_calloc(1, sizeof(struct config)); -} - -/* The configuration "old" is no longer used */ -PRIVATE void deleteconfig(struct config *old) -{ - old->next = freelist; - freelist = old; -} - -/* Initialized the configuration list builder */ -void Configlist_init(void){ - current = 0; - currentend = ¤t; - basis = 0; - basisend = &basis; - Configtable_init(); - return; -} - -/* Initialized the configuration list builder */ -void Configlist_reset(void){ - current = 0; - currentend = ¤t; - basis = 0; - basisend = &basis; - Configtable_clear(0); - return; -} - -/* Add another configuration to the configuration list */ -struct config *Configlist_add( - struct rule *rp, /* The rule */ - int dot /* Index into the RHS of the rule where the dot goes */ -){ - struct config *cfp, model; - - assert( currentend!=0 ); - model.rp = rp; - model.dot = dot; - cfp = Configtable_find(&model); - if( cfp==0 ){ - cfp = newconfig(); - cfp->rp = rp; - cfp->dot = dot; - cfp->fws = SetNew(); - cfp->stp = 0; - cfp->fplp = cfp->bplp = 0; - cfp->next = 0; - cfp->bp = 0; - *currentend = cfp; - currentend = &cfp->next; - Configtable_insert(cfp); - } - return cfp; -} - -/* Add a basis configuration to the configuration list */ -struct config *Configlist_addbasis(struct rule *rp, int dot) -{ - struct config *cfp, model; - - assert( basisend!=0 ); - assert( currentend!=0 ); - model.rp = rp; - model.dot = dot; - cfp = Configtable_find(&model); - if( cfp==0 ){ - cfp = newconfig(); - cfp->rp = rp; - cfp->dot = dot; - cfp->fws = SetNew(); - cfp->stp = 0; - cfp->fplp = cfp->bplp = 0; - cfp->next = 0; - cfp->bp = 0; - *currentend = cfp; - currentend = &cfp->next; - *basisend = cfp; - basisend = &cfp->bp; - Configtable_insert(cfp); - } - return cfp; -} - -/* Compute the closure of the configuration list */ -void Configlist_closure(struct lemon *lemp) -{ - struct config *cfp, *newcfp; - struct rule *rp, *newrp; - struct symbol *sp, *xsp; - int i, dot; - - assert( currentend!=0 ); - for(cfp=current; cfp; cfp=cfp->next){ - rp = cfp->rp; - dot = cfp->dot; - if( dot>=rp->nrhs ) continue; - sp = rp->rhs[dot]; - if( sp->type==NONTERMINAL ){ - if( sp->rule==0 && sp!=lemp->errsym ){ - ErrorMsg(lemp->filename,rp->line,"Nonterminal \"%s\" has no rules.", - sp->name); - lemp->errorcnt++; - } - for(newrp=sp->rule; newrp; newrp=newrp->nextlhs){ - newcfp = Configlist_add(newrp,0); - for(i=dot+1; inrhs; i++){ - xsp = rp->rhs[i]; - if( xsp->type==TERMINAL ){ - SetAdd(newcfp->fws,xsp->index); - break; - }else if( xsp->type==MULTITERMINAL ){ - int k; - for(k=0; knsubsym; k++){ - SetAdd(newcfp->fws, xsp->subsym[k]->index); - } - break; - }else{ - SetUnion(newcfp->fws,xsp->firstset); - if( xsp->lambda==LEMON_FALSE ) break; - } - } - if( i==rp->nrhs ) Plink_add(&cfp->fplp,newcfp); - } - } - } - return; -} - -/* Sort the configuration list */ -void Configlist_sort(void){ - current = (struct config*)msort((char*)current,(char**)&(current->next), - Configcmp); - currentend = 0; - return; -} - -/* Sort the basis configuration list */ -void Configlist_sortbasis(void){ - basis = (struct config*)msort((char*)current,(char**)&(current->bp), - Configcmp); - basisend = 0; - return; -} - -/* Return a pointer to the head of the configuration list and -** reset the list */ -struct config *Configlist_return(void){ - struct config *old; - old = current; - current = 0; - currentend = 0; - return old; -} - -/* Return a pointer to the head of the configuration list and -** reset the list */ -struct config *Configlist_basis(void){ - struct config *old; - old = basis; - basis = 0; - basisend = 0; - return old; -} - -/* Free all elements of the given configuration list */ -void Configlist_eat(struct config *cfp) -{ - struct config *nextcfp; - for(; cfp; cfp=nextcfp){ - nextcfp = cfp->next; - assert( cfp->fplp==0 ); - assert( cfp->bplp==0 ); - if( cfp->fws ) SetFree(cfp->fws); - deleteconfig(cfp); - } - return; -} -/***************** From the file "error.c" *********************************/ -/* -** Code for printing error message. -*/ - -void ErrorMsg(const char *filename, int lineno, const char *format, ...){ - va_list ap; - fprintf(stderr, "%s:%d: ", filename, lineno); - va_start(ap, format); - vfprintf(stderr,format,ap); - va_end(ap); - fprintf(stderr, "\n"); -} -/**************** From the file "main.c" ************************************/ -/* -** Main program file for the LEMON parser generator. -*/ - -/* Report an out-of-memory condition and abort. This function -** is used mostly by the "MemoryCheck" macro in struct.h -*/ -void memory_error(void){ - fprintf(stderr,"Out of memory. Aborting...\n"); - exit(1); -} - -static int nDefine = 0; /* Number of -D options on the command line */ -static int nDefineUsed = 0; /* Number of -D options actually used */ -static char **azDefine = 0; /* Name of the -D macros */ -static char *bDefineUsed = 0; /* True for every -D macro actually used */ - -/* This routine is called with the argument to each -D command-line option. -** Add the macro defined to the azDefine array. -*/ -static void handle_D_option(char *z){ - char **paz; - nDefine++; - azDefine = (char **) lemon_realloc(azDefine, sizeof(azDefine[0])*nDefine); - if( azDefine==0 ){ - fprintf(stderr,"out of memory\n"); - exit(1); - } - bDefineUsed = (char*)lemon_realloc(bDefineUsed, nDefine); - if( bDefineUsed==0 ){ - fprintf(stderr,"out of memory\n"); - exit(1); - } - bDefineUsed[nDefine-1] = 0; - paz = &azDefine[nDefine-1]; - *paz = (char *) lemon_malloc( lemonStrlen(z)+1 ); - if( *paz==0 ){ - fprintf(stderr,"out of memory\n"); - exit(1); - } - lemon_strcpy(*paz, z); - for(z=*paz; *z && *z!='='; z++){} - *z = 0; -} - -/* Rember the name of the output directory -*/ -static char *outputDir = NULL; -static void handle_d_option(char *z){ - outputDir = (char *) lemon_malloc( lemonStrlen(z)+1 ); - if( outputDir==0 ){ - fprintf(stderr,"out of memory\n"); - exit(1); - } - lemon_strcpy(outputDir, z); -} - -static char *user_templatename = NULL; -static void handle_T_option(char *z){ - user_templatename = (char *) lemon_malloc( lemonStrlen(z)+1 ); - if( user_templatename==0 ){ - memory_error(); - } - lemon_strcpy(user_templatename, z); -} - -/* Merge together to lists of rules ordered by rule.iRule */ -static struct rule *Rule_merge(struct rule *pA, struct rule *pB){ - struct rule *pFirst = 0; - struct rule **ppPrev = &pFirst; - while( pA && pB ){ - if( pA->iRuleiRule ){ - *ppPrev = pA; - ppPrev = &pA->next; - pA = pA->next; - }else{ - *ppPrev = pB; - ppPrev = &pB->next; - pB = pB->next; - } - } - if( pA ){ - *ppPrev = pA; - }else{ - *ppPrev = pB; - } - return pFirst; -} - -/* -** Sort a list of rules in order of increasing iRule value -*/ -static struct rule *Rule_sort(struct rule *rp){ - unsigned int i; - struct rule *pNext; - struct rule *x[32]; - memset(x, 0, sizeof(x)); - while( rp ){ - pNext = rp->next; - rp->next = 0; - for(i=0; iindex = i; - qsort(lem.symbols,lem.nsymbol,sizeof(struct symbol*), Symbolcmpp); - for(i=0; iindex = i; - while( lem.symbols[i-1]->type==MULTITERMINAL ){ i--; } - assert( strcmp(lem.symbols[i-1]->name,"{default}")==0 ); - lem.nsymbol = i - 1; - for(i=1; ISUPPER(lem.symbols[i]->name[0]); i++); - lem.nterminal = i; - - /* Assign sequential rule numbers. Start with 0. Put rules that have no - ** reduce action C-code associated with them last, so that the switch() - ** statement that selects reduction actions will have a smaller jump table. - */ - for(i=0, rp=lem.rule; rp; rp=rp->next){ - rp->iRule = rp->code ? i++ : -1; - } - lem.nruleWithAction = i; - for(rp=lem.rule; rp; rp=rp->next){ - if( rp->iRule<0 ) rp->iRule = i++; - } - lem.startRule = lem.rule; - lem.rule = Rule_sort(lem.rule); - - /* Generate a reprint of the grammar, if requested on the command line */ - if( rpflag ){ - Reprint(&lem); - }else{ - /* Initialize the size for all follow and first sets */ - SetSize(lem.nterminal+1); - - /* Find the precedence for every production rule (that has one) */ - FindRulePrecedences(&lem); - - /* Compute the lambda-nonterminals and the first-sets for every - ** nonterminal */ - FindFirstSets(&lem); - - /* Compute all LR(0) states. Also record follow-set propagation - ** links so that the follow-set can be computed later */ - lem.nstate = 0; - FindStates(&lem); - lem.sorted = State_arrayof(); - - /* Tie up loose ends on the propagation links */ - FindLinks(&lem); - - /* Compute the follow set of every reducible configuration */ - FindFollowSets(&lem); - - /* Compute the action tables */ - FindActions(&lem); - - /* Compress the action tables */ - if( compress==0 ) CompressTables(&lem); - - /* Reorder and renumber the states so that states with fewer choices - ** occur at the end. This is an optimization that helps make the - ** generated parser tables smaller. */ - if( noResort==0 ) ResortStates(&lem); - - /* Generate a report of the parser generated. (the "y.output" file) */ - if( !quiet ) ReportOutput(&lem); - - /* Generate the source code for the parser */ - ReportTable(&lem, mhflag, sqlFlag); - - /* Produce a header file for use by the scanner. (This step is - ** omitted if the "-m" option is used because makeheaders will - ** generate the file for us.) */ - if( !mhflag ) ReportHeader(&lem); - } - if( statistics ){ - printf("Parser statistics:\n"); - stats_line("terminal symbols", lem.nterminal); - stats_line("non-terminal symbols", lem.nsymbol - lem.nterminal); - stats_line("total symbols", lem.nsymbol); - stats_line("rules", lem.nrule); - stats_line("states", lem.nxstate); - stats_line("conflicts", lem.nconflict); - stats_line("action table entries", lem.nactiontab); - stats_line("lookahead table entries", lem.nlookaheadtab); - stats_line("total table size (bytes)", lem.tablesize); - } - if( lem.nconflict > 0 ){ - fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict); - } - - /* return 0 on success, 1 on failure. */ - exitcode = ((lem.errorcnt > 0) || (lem.nconflict > 0)) ? 1 : 0; - lemon_free_all(); - exit(exitcode); - return (exitcode); -} -/******************** From the file "msort.c" *******************************/ -/* -** A generic merge-sort program. -** -** USAGE: -** Let "ptr" be a pointer to some structure which is at the head of -** a null-terminated list. Then to sort the list call: -** -** ptr = msort(ptr,&(ptr->next),cmpfnc); -** -** In the above, "cmpfnc" is a pointer to a function which compares -** two instances of the structure and returns an integer, as in -** strcmp. The second argument is a pointer to the pointer to the -** second element of the linked list. This address is used to compute -** the offset to the "next" field within the structure. The offset to -** the "next" field must be constant for all structures in the list. -** -** The function returns a new pointer which is the head of the list -** after sorting. -** -** ALGORITHM: -** Merge-sort. -*/ - -/* -** Return a pointer to the next structure in the linked list. -*/ -#define NEXT(A) (*(char**)(((char*)A)+offset)) - -/* -** Inputs: -** a: A sorted, null-terminated linked list. (May be null). -** b: A sorted, null-terminated linked list. (May be null). -** cmp: A pointer to the comparison function. -** offset: Offset in the structure to the "next" field. -** -** Return Value: -** A pointer to the head of a sorted list containing the elements -** of both a and b. -** -** Side effects: -** The "next" pointers for elements in the lists a and b are -** changed. -*/ -static char *merge( - char *a, - char *b, - int (*cmp)(const char*,const char*), - int offset -){ - char *ptr, *head; - - if( a==0 ){ - head = b; - }else if( b==0 ){ - head = a; - }else{ - if( (*cmp)(a,b)<=0 ){ - ptr = a; - a = NEXT(a); - }else{ - ptr = b; - b = NEXT(b); - } - head = ptr; - while( a && b ){ - if( (*cmp)(a,b)<=0 ){ - NEXT(ptr) = a; - ptr = a; - a = NEXT(a); - }else{ - NEXT(ptr) = b; - ptr = b; - b = NEXT(b); - } - } - if( a ) NEXT(ptr) = a; - else NEXT(ptr) = b; - } - return head; -} - -/* -** Inputs: -** list: Pointer to a singly-linked list of structures. -** next: Pointer to pointer to the second element of the list. -** cmp: A comparison function. -** -** Return Value: -** A pointer to the head of a sorted list containing the elements -** originally in list. -** -** Side effects: -** The "next" pointers for elements in list are changed. -*/ -#define LISTSIZE 30 -static char *msort( - char *list, - char **next, - int (*cmp)(const char*,const char*) -){ - unsigned long offset; - char *ep; - char *set[LISTSIZE]; - int i; - offset = (unsigned long)((char*)next - (char*)list); - for(i=0; istate = WAITING_FOR_DECL_KEYWORD; - }else if( ISLOWER(x[0]) ){ - psp->lhs = Symbol_new(x); - psp->nrhs = 0; - psp->lhsalias = 0; - psp->state = WAITING_FOR_ARROW; - }else if( x[0]=='{' ){ - if( psp->prevrule==0 ){ - ErrorMsg(psp->filename,psp->tokenlineno, - "There is no prior rule upon which to attach the code " - "fragment which begins on this line."); - psp->errorcnt++; - }else if( psp->prevrule->code!=0 ){ - ErrorMsg(psp->filename,psp->tokenlineno, - "Code fragment beginning on this line is not the first " - "to follow the previous rule."); - psp->errorcnt++; - }else if( strcmp(x, "{NEVER-REDUCE")==0 ){ - psp->prevrule->neverReduce = 1; - }else{ - psp->prevrule->line = psp->tokenlineno; - psp->prevrule->code = &x[1]; - psp->prevrule->noCode = 0; - } - }else if( x[0]=='[' ){ - psp->state = PRECEDENCE_MARK_1; - }else{ - ErrorMsg(psp->filename,psp->tokenlineno, - "Token \"%s\" should be either \"%%\" or a nonterminal name.", - x); - psp->errorcnt++; - } - break; - case PRECEDENCE_MARK_1: - if( !ISUPPER(x[0]) ){ - ErrorMsg(psp->filename,psp->tokenlineno, - "The precedence symbol must be a terminal."); - psp->errorcnt++; - }else if( psp->prevrule==0 ){ - ErrorMsg(psp->filename,psp->tokenlineno, - "There is no prior rule to assign precedence \"[%s]\".",x); - psp->errorcnt++; - }else if( psp->prevrule->precsym!=0 ){ - ErrorMsg(psp->filename,psp->tokenlineno, - "Precedence mark on this line is not the first " - "to follow the previous rule."); - psp->errorcnt++; - }else{ - psp->prevrule->precsym = Symbol_new(x); - } - psp->state = PRECEDENCE_MARK_2; - break; - case PRECEDENCE_MARK_2: - if( x[0]!=']' ){ - ErrorMsg(psp->filename,psp->tokenlineno, - "Missing \"]\" on precedence mark."); - psp->errorcnt++; - } - psp->state = WAITING_FOR_DECL_OR_RULE; - break; - case WAITING_FOR_ARROW: - if( x[0]==':' && x[1]==':' && x[2]=='=' ){ - psp->state = IN_RHS; - }else if( x[0]=='(' ){ - psp->state = LHS_ALIAS_1; - }else{ - ErrorMsg(psp->filename,psp->tokenlineno, - "Expected to see a \":\" following the LHS symbol \"%s\".", - psp->lhs->name); - psp->errorcnt++; - psp->state = RESYNC_AFTER_RULE_ERROR; - } - break; - case LHS_ALIAS_1: - if( ISALPHA(x[0]) ){ - psp->lhsalias = x; - psp->state = LHS_ALIAS_2; - }else{ - ErrorMsg(psp->filename,psp->tokenlineno, - "\"%s\" is not a valid alias for the LHS \"%s\"\n", - x,psp->lhs->name); - psp->errorcnt++; - psp->state = RESYNC_AFTER_RULE_ERROR; - } - break; - case LHS_ALIAS_2: - if( x[0]==')' ){ - psp->state = LHS_ALIAS_3; - }else{ - ErrorMsg(psp->filename,psp->tokenlineno, - "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); - psp->errorcnt++; - psp->state = RESYNC_AFTER_RULE_ERROR; - } - break; - case LHS_ALIAS_3: - if( x[0]==':' && x[1]==':' && x[2]=='=' ){ - psp->state = IN_RHS; - }else{ - ErrorMsg(psp->filename,psp->tokenlineno, - "Missing \"->\" following: \"%s(%s)\".", - psp->lhs->name,psp->lhsalias); - psp->errorcnt++; - psp->state = RESYNC_AFTER_RULE_ERROR; - } - break; - case IN_RHS: - if( x[0]=='.' ){ - struct rule *rp; - rp = (struct rule *)lemon_calloc( sizeof(struct rule) + - sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs, 1); - if( rp==0 ){ - ErrorMsg(psp->filename,psp->tokenlineno, - "Can't allocate enough memory for this rule."); - psp->errorcnt++; - psp->prevrule = 0; - }else{ - int i; - rp->ruleline = psp->tokenlineno; - rp->rhs = (struct symbol**)&rp[1]; - rp->rhsalias = (const char**)&(rp->rhs[psp->nrhs]); - for(i=0; inrhs; i++){ - rp->rhs[i] = psp->rhs[i]; - rp->rhsalias[i] = psp->alias[i]; - if( rp->rhsalias[i]!=0 ){ rp->rhs[i]->bContent = 1; } - } - rp->lhs = psp->lhs; - rp->lhsalias = psp->lhsalias; - rp->nrhs = psp->nrhs; - rp->code = 0; - rp->noCode = 1; - rp->precsym = 0; - rp->index = psp->gp->nrule++; - rp->nextlhs = rp->lhs->rule; - rp->lhs->rule = rp; - rp->next = 0; - if( psp->firstrule==0 ){ - psp->firstrule = psp->lastrule = rp; - }else{ - psp->lastrule->next = rp; - psp->lastrule = rp; - } - psp->prevrule = rp; - } - psp->state = WAITING_FOR_DECL_OR_RULE; - }else if( ISALPHA(x[0]) ){ - if( psp->nrhs>=MAXRHS ){ - ErrorMsg(psp->filename,psp->tokenlineno, - "Too many symbols on RHS of rule beginning at \"%s\".", - x); - psp->errorcnt++; - psp->state = RESYNC_AFTER_RULE_ERROR; - }else{ - psp->rhs[psp->nrhs] = Symbol_new(x); - psp->alias[psp->nrhs] = 0; - psp->nrhs++; - } - }else if( (x[0]=='|' || x[0]=='/') && psp->nrhs>0 && ISUPPER(x[1]) ){ - struct symbol *msp = psp->rhs[psp->nrhs-1]; - if( msp->type!=MULTITERMINAL ){ - struct symbol *origsp = msp; - msp = (struct symbol *) lemon_calloc(1,sizeof(*msp)); - memset(msp, 0, sizeof(*msp)); - msp->type = MULTITERMINAL; - msp->nsubsym = 1; - msp->subsym = (struct symbol**)lemon_calloc(1,sizeof(struct symbol*)); - msp->subsym[0] = origsp; - msp->name = origsp->name; - psp->rhs[psp->nrhs-1] = msp; - } - msp->nsubsym++; - msp->subsym = (struct symbol **) lemon_realloc(msp->subsym, - sizeof(struct symbol*)*msp->nsubsym); - msp->subsym[msp->nsubsym-1] = Symbol_new(&x[1]); - if( ISLOWER(x[1]) || ISLOWER(msp->subsym[0]->name[0]) ){ - ErrorMsg(psp->filename,psp->tokenlineno, - "Cannot form a compound containing a non-terminal"); - psp->errorcnt++; - } - }else if( x[0]=='(' && psp->nrhs>0 ){ - psp->state = RHS_ALIAS_1; - }else{ - ErrorMsg(psp->filename,psp->tokenlineno, - "Illegal character on RHS of rule: \"%s\".",x); - psp->errorcnt++; - psp->state = RESYNC_AFTER_RULE_ERROR; - } - break; - case RHS_ALIAS_1: - if( ISALPHA(x[0]) ){ - psp->alias[psp->nrhs-1] = x; - psp->state = RHS_ALIAS_2; - }else{ - ErrorMsg(psp->filename,psp->tokenlineno, - "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n", - x,psp->rhs[psp->nrhs-1]->name); - psp->errorcnt++; - psp->state = RESYNC_AFTER_RULE_ERROR; - } - break; - case RHS_ALIAS_2: - if( x[0]==')' ){ - psp->state = IN_RHS; - }else{ - ErrorMsg(psp->filename,psp->tokenlineno, - "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); - psp->errorcnt++; - psp->state = RESYNC_AFTER_RULE_ERROR; - } - break; - case WAITING_FOR_DECL_KEYWORD: - if( ISALPHA(x[0]) ){ - psp->declkeyword = x; - psp->declargslot = 0; - psp->decllinenoslot = 0; - psp->insertLineMacro = 1; - psp->state = WAITING_FOR_DECL_ARG; - if( strcmp(x,"name")==0 ){ - psp->declargslot = &(psp->gp->name); - psp->insertLineMacro = 0; - }else if( strcmp(x,"include")==0 ){ - psp->declargslot = &(psp->gp->include); - }else if( strcmp(x,"code")==0 ){ - psp->declargslot = &(psp->gp->extracode); - }else if( strcmp(x,"token_prefix")==0 ){ - psp->declargslot = &psp->gp->tokenprefix; - psp->insertLineMacro = 0; - }else if( strcmp(x,"syntax_error")==0 ){ - psp->declargslot = &(psp->gp->error); - }else if( strcmp(x,"parse_accept")==0 ){ - psp->declargslot = &(psp->gp->accept); - }else if( strcmp(x,"parse_failure")==0 ){ - psp->declargslot = &(psp->gp->failure); - }else if( strcmp(x,"stack_overflow")==0 ){ - psp->declargslot = &(psp->gp->overflow); - }else if( strcmp(x,"extra_argument")==0 ){ - psp->declargslot = &(psp->gp->arg); - psp->insertLineMacro = 0; - }else if( strcmp(x,"extra_context")==0 ){ - psp->declargslot = &(psp->gp->ctx); - psp->insertLineMacro = 0; - }else if( strcmp(x,"token_type")==0 ){ - psp->declargslot = &(psp->gp->tokentype); - psp->insertLineMacro = 0; - }else if( strcmp(x,"default_type")==0 ){ - psp->declargslot = &(psp->gp->vartype); - psp->insertLineMacro = 0; - }else if( strcmp(x,"stack_size")==0 ){ - psp->declargslot = &(psp->gp->stacksize); - psp->insertLineMacro = 0; - }else if( strcmp(x,"start_symbol")==0 ){ - psp->declargslot = &(psp->gp->start); - psp->insertLineMacro = 0; - }else if( strcmp(x,"left")==0 ){ - psp->preccounter++; - psp->declassoc = LEFT; - psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; - }else if( strcmp(x,"right")==0 ){ - psp->preccounter++; - psp->declassoc = RIGHT; - psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; - }else if( strcmp(x,"nonassoc")==0 ){ - psp->preccounter++; - psp->declassoc = NONE; - psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; - }else if( strcmp(x,"type")==0 ){ - psp->state = WAITING_FOR_DATATYPE_SYMBOL; - }else if( strcmp(x,"fallback")==0 ){ - psp->fallback = 0; - psp->state = WAITING_FOR_FALLBACK_ID; - }else if( strcmp(x,"token")==0 ){ - psp->state = WAITING_FOR_TOKEN_NAME; - }else if( strcmp(x,"wildcard")==0 ){ - psp->state = WAITING_FOR_WILDCARD_ID; - }else if( strcmp(x,"token_class")==0 ){ - psp->state = WAITING_FOR_CLASS_ID; - }else{ - ErrorMsg(psp->filename,psp->tokenlineno, - "Unknown declaration keyword: \"%%%s\".",x); - psp->errorcnt++; - psp->state = RESYNC_AFTER_DECL_ERROR; - } - }else{ - ErrorMsg(psp->filename,psp->tokenlineno, - "Illegal declaration keyword: \"%s\".",x); - psp->errorcnt++; - psp->state = RESYNC_AFTER_DECL_ERROR; - } - break; - case WAITING_FOR_DATATYPE_SYMBOL: - if( !ISALPHA(x[0]) ){ - ErrorMsg(psp->filename,psp->tokenlineno, - "Symbol name missing after %%type keyword"); - psp->errorcnt++; - psp->state = RESYNC_AFTER_DECL_ERROR; - }else{ - struct symbol *sp = Symbol_find(x); - if((sp) && (sp->datatype)){ - ErrorMsg(psp->filename,psp->tokenlineno, - "Symbol %%type \"%s\" already defined", x); - psp->errorcnt++; - psp->state = RESYNC_AFTER_DECL_ERROR; - }else{ - if (!sp){ - sp = Symbol_new(x); - } - psp->declargslot = &sp->datatype; - psp->insertLineMacro = 0; - psp->state = WAITING_FOR_DECL_ARG; - } - } - break; - case WAITING_FOR_PRECEDENCE_SYMBOL: - if( x[0]=='.' ){ - psp->state = WAITING_FOR_DECL_OR_RULE; - }else if( ISUPPER(x[0]) ){ - struct symbol *sp; - sp = Symbol_new(x); - if( sp->prec>=0 ){ - ErrorMsg(psp->filename,psp->tokenlineno, - "Symbol \"%s\" has already be given a precedence.",x); - psp->errorcnt++; - }else{ - sp->prec = psp->preccounter; - sp->assoc = psp->declassoc; - } - }else{ - ErrorMsg(psp->filename,psp->tokenlineno, - "Can't assign a precedence to \"%s\".",x); - psp->errorcnt++; - } - break; - case WAITING_FOR_DECL_ARG: - if( x[0]=='{' || x[0]=='\"' || ISALNUM(x[0]) ){ - const char *zOld, *zNew; - char *zBuf, *z; - int nOld, n, nLine = 0, nNew, nBack; - int addLineMacro; - char zLine[50]; - zNew = x; - if( zNew[0]=='"' || zNew[0]=='{' ) zNew++; - nNew = lemonStrlen(zNew); - if( *psp->declargslot ){ - zOld = *psp->declargslot; - }else{ - zOld = ""; - } - nOld = lemonStrlen(zOld); - n = nOld + nNew + 20; - addLineMacro = !psp->gp->nolinenosflag - && psp->insertLineMacro - && psp->tokenlineno>1 - && (psp->decllinenoslot==0 || psp->decllinenoslot[0]!=0); - if( addLineMacro ){ - for(z=psp->filename, nBack=0; *z; z++){ - if( *z=='\\' ) nBack++; - } - lemon_sprintf(zLine, "//line %d ", psp->tokenlineno); - nLine = lemonStrlen(zLine); - n += nLine + lemonStrlen(psp->filename) + nBack; - } - *psp->declargslot = (char *) lemon_realloc(*psp->declargslot, n); - zBuf = *psp->declargslot + nOld; - if( addLineMacro ){ - if( nOld && zBuf[-1]!='\n' ){ - *(zBuf++) = '\n'; - } - memcpy(zBuf, zLine, nLine); - zBuf += nLine; - *(zBuf++) = '"'; - for(z=psp->filename; *z; z++){ - if( *z=='\\' ){ - *(zBuf++) = '\\'; - } - *(zBuf++) = *z; - } - *(zBuf++) = '"'; - *(zBuf++) = '\n'; - } - if( psp->decllinenoslot && psp->decllinenoslot[0]==0 ){ - psp->decllinenoslot[0] = psp->tokenlineno; - } - memcpy(zBuf, zNew, nNew); - zBuf += nNew; - *zBuf = 0; - psp->state = WAITING_FOR_DECL_OR_RULE; - }else{ - ErrorMsg(psp->filename,psp->tokenlineno, - "Illegal argument to %%%s: %s",psp->declkeyword,x); - psp->errorcnt++; - psp->state = RESYNC_AFTER_DECL_ERROR; - } - break; - case WAITING_FOR_FALLBACK_ID: - if( x[0]=='.' ){ - psp->state = WAITING_FOR_DECL_OR_RULE; - }else if( !ISUPPER(x[0]) ){ - ErrorMsg(psp->filename, psp->tokenlineno, - "%%fallback argument \"%s\" should be a token", x); - psp->errorcnt++; - }else{ - struct symbol *sp = Symbol_new(x); - if( psp->fallback==0 ){ - psp->fallback = sp; - }else if( sp->fallback ){ - ErrorMsg(psp->filename, psp->tokenlineno, - "More than one fallback assigned to token %s", x); - psp->errorcnt++; - }else{ - sp->fallback = psp->fallback; - psp->gp->has_fallback = 1; - } - } - break; - case WAITING_FOR_TOKEN_NAME: - /* Tokens do not have to be declared before use. But they can be - ** in order to control their assigned integer number. The number for - ** each token is assigned when it is first seen. So by including - ** - ** %token ONE TWO THREE. - ** - ** early in the grammar file, that assigns small consecutive values - ** to each of the tokens ONE TWO and THREE. - */ - if( x[0]=='.' ){ - psp->state = WAITING_FOR_DECL_OR_RULE; - }else if( !ISUPPER(x[0]) ){ - ErrorMsg(psp->filename, psp->tokenlineno, - "%%token argument \"%s\" should be a token", x); - psp->errorcnt++; - }else{ - (void)Symbol_new(x); - } - break; - case WAITING_FOR_WILDCARD_ID: - if( x[0]=='.' ){ - psp->state = WAITING_FOR_DECL_OR_RULE; - }else if( !ISUPPER(x[0]) ){ - ErrorMsg(psp->filename, psp->tokenlineno, - "%%wildcard argument \"%s\" should be a token", x); - psp->errorcnt++; - }else{ - struct symbol *sp = Symbol_new(x); - if( psp->gp->wildcard==0 ){ - psp->gp->wildcard = sp; - }else{ - ErrorMsg(psp->filename, psp->tokenlineno, - "Extra wildcard to token: %s", x); - psp->errorcnt++; - } - } - break; - case WAITING_FOR_CLASS_ID: - if( !ISLOWER(x[0]) ){ - ErrorMsg(psp->filename, psp->tokenlineno, - "%%token_class must be followed by an identifier: %s", x); - psp->errorcnt++; - psp->state = RESYNC_AFTER_DECL_ERROR; - }else if( Symbol_find(x) ){ - ErrorMsg(psp->filename, psp->tokenlineno, - "Symbol \"%s\" already used", x); - psp->errorcnt++; - psp->state = RESYNC_AFTER_DECL_ERROR; - }else{ - psp->tkclass = Symbol_new(x); - psp->tkclass->type = MULTITERMINAL; - psp->state = WAITING_FOR_CLASS_TOKEN; - } - break; - case WAITING_FOR_CLASS_TOKEN: - if( x[0]=='.' ){ - psp->state = WAITING_FOR_DECL_OR_RULE; - }else if( ISUPPER(x[0]) || ((x[0]=='|' || x[0]=='/') && ISUPPER(x[1])) ){ - struct symbol *msp = psp->tkclass; - msp->nsubsym++; - msp->subsym = (struct symbol **) lemon_realloc(msp->subsym, - sizeof(struct symbol*)*msp->nsubsym); - if( !ISUPPER(x[0]) ) x++; - msp->subsym[msp->nsubsym-1] = Symbol_new(x); - }else{ - ErrorMsg(psp->filename, psp->tokenlineno, - "%%token_class argument \"%s\" should be a token", x); - psp->errorcnt++; - psp->state = RESYNC_AFTER_DECL_ERROR; - } - break; - case RESYNC_AFTER_RULE_ERROR: -/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; -** break; */ - case RESYNC_AFTER_DECL_ERROR: - if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; - if( x[0]=='%' ) psp->state = WAITING_FOR_DECL_KEYWORD; - break; - } -} - -/* The text in the input is part of the argument to an %ifdef or %ifndef. -** Evaluate the text as a boolean expression. Return true or false. -*/ -static int eval_preprocessor_boolean(char *z, int lineno){ - int neg = 0; - int res = 0; - int okTerm = 1; - int i; - for(i=0; z[i]!=0; i++){ - if( ISSPACE(z[i]) ) continue; - if( z[i]=='!' ){ - if( !okTerm ) goto pp_syntax_error; - neg = !neg; - continue; - } - if( z[i]=='|' && z[i+1]=='|' ){ - if( okTerm ) goto pp_syntax_error; - if( res ) return 1; - i++; - okTerm = 1; - continue; - } - if( z[i]=='&' && z[i+1]=='&' ){ - if( okTerm ) goto pp_syntax_error; - if( !res ) return 0; - i++; - okTerm = 1; - continue; - } - if( z[i]=='(' ){ - int k; - int n = 1; - if( !okTerm ) goto pp_syntax_error; - for(k=i+1; z[k]; k++){ - if( z[k]==')' ){ - n--; - if( n==0 ){ - z[k] = 0; - res = eval_preprocessor_boolean(&z[i+1], -1); - z[k] = ')'; - if( res<0 ){ - i = i-res; - goto pp_syntax_error; - } - i = k; - break; - } - }else if( z[k]=='(' ){ - n++; - }else if( z[k]==0 ){ - i = k; - goto pp_syntax_error; - } - } - if( neg ){ - res = !res; - neg = 0; - } - okTerm = 0; - continue; - } - if( ISALPHA(z[i]) ){ - int j, k, n; - if( !okTerm ) goto pp_syntax_error; - for(k=i+1; ISALNUM(z[k]) || z[k]=='_'; k++){} - n = k - i; - res = 0; - for(j=0; j0 ){ - fprintf(stderr, "%%if syntax error on line %d.\n", lineno); - fprintf(stderr, " %.*s <-- syntax error here\n", i+1, z); - exit(1); - }else{ - return -(i+1); - } -} - -/* Run the preprocessor over the input file text. The global variables -** azDefine[0] through azDefine[nDefine-1] contains the names of all defined -** macros. This routine looks for "%ifdef" and "%ifndef" and "%endif" and -** comments them out. Text in between is also commented out as appropriate. -*/ -static void preprocess_input(char *z){ - int i, j, k; - int exclude = 0; - int start = 0; - int lineno = 1; - int start_lineno = 1; - for(i=0; z[i]; i++){ - if( z[i]=='\n' ) lineno++; - if( z[i]!='%' || (i>0 && z[i-1]!='\n') ) continue; - if( strncmp(&z[i],"%endif",6)==0 && ISSPACE(z[i+6]) ){ - if( exclude ){ - exclude--; - if( exclude==0 ){ - for(j=start; jfilename; - ps.errorcnt = 0; - ps.state = INITIALIZE; - - /* Begin by reading the input file */ - fp = fopen(ps.filename,"rb"); - if( fp==0 ){ - ErrorMsg(ps.filename,0,"Can't open this file for reading."); - gp->errorcnt++; - return; - } - fseek(fp,0,2); - filesize = ftell(fp); - rewind(fp); - filebuf = (char *)lemon_malloc( filesize+1 ); - if( filesize>100000000 || filebuf==0 ){ - ErrorMsg(ps.filename,0,"Input file too large."); - lemon_free(filebuf); - gp->errorcnt++; - fclose(fp); - return; - } - if( fread(filebuf,1,filesize,fp)!=filesize ){ - ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.", - filesize); - lemon_free(filebuf); - gp->errorcnt++; - fclose(fp); - return; - } - fclose(fp); - filebuf[filesize] = 0; - - /* Make an initial pass through the file to handle %ifdef and %ifndef */ - preprocess_input(filebuf); - if( gp->printPreprocessed ){ - printf("%s\n", filebuf); - return; - } - - /* Now scan the text of the input file */ - lineno = 1; - for(cp=filebuf; (c= *cp)!=0; ){ - if( c=='\n' ) lineno++; /* Keep track of the line number */ - if( ISSPACE(c) ){ cp++; continue; } /* Skip all white space */ - if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments */ - cp+=2; - while( (c= *cp)!=0 && c!='\n' ) cp++; - continue; - } - if( c=='/' && cp[1]=='*' ){ /* Skip C style comments */ - cp+=2; - if( (*cp)=='/' ) cp++; - while( (c= *cp)!=0 && (c!='/' || cp[-1]!='*') ){ - if( c=='\n' ) lineno++; - cp++; - } - if( c ) cp++; - continue; - } - ps.tokenstart = cp; /* Mark the beginning of the token */ - ps.tokenlineno = lineno; /* Linenumber on which token begins */ - if( c=='\"' ){ /* String literals */ - cp++; - while( (c= *cp)!=0 && c!='\"' ){ - if( c=='\n' ) lineno++; - cp++; - } - if( c==0 ){ - ErrorMsg(ps.filename,startline, - "String starting on this line is not terminated before " - "the end of the file."); - ps.errorcnt++; - nextcp = cp; - }else{ - nextcp = cp+1; - } - }else if( c=='{' ){ /* A block of C code */ - int level; - cp++; - for(level=1; (c= *cp)!=0 && (level>1 || c!='}'); cp++){ - if( c=='\n' ) lineno++; - else if( c=='{' ) level++; - else if( c=='}' ) level--; - else if( c=='/' && cp[1]=='*' ){ /* Skip comments */ - int prevc; - cp = &cp[2]; - prevc = 0; - while( (c= *cp)!=0 && (c!='/' || prevc!='*') ){ - if( c=='\n' ) lineno++; - prevc = c; - cp++; - } - }else if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments too */ - cp = &cp[2]; - while( (c= *cp)!=0 && c!='\n' ) cp++; - if( c ) lineno++; - }else if( c=='\'' || c=='\"' ){ /* String a character literals */ - int startchar, prevc; - startchar = c; - prevc = 0; - for(cp++; (c= *cp)!=0 && (c!=startchar || prevc=='\\'); cp++){ - if( c=='\n' ) lineno++; - if( prevc=='\\' ) prevc = 0; - else prevc = c; - } - } - } - if( c==0 ){ - ErrorMsg(ps.filename,ps.tokenlineno, - "C code starting on this line is not terminated before " - "the end of the file."); - ps.errorcnt++; - nextcp = cp; - }else{ - nextcp = cp+1; - } - }else if( ISALNUM(c) ){ /* Identifiers */ - while( (c= *cp)!=0 && (ISALNUM(c) || c=='_') ) cp++; - nextcp = cp; - }else if( c==':' && cp[1]==':' && cp[2]=='=' ){ /* The operator "::=" */ - cp += 3; - nextcp = cp; - }else if( (c=='/' || c=='|') && ISALPHA(cp[1]) ){ - cp += 2; - while( (c = *cp)!=0 && (ISALNUM(c) || c=='_') ) cp++; - nextcp = cp; - }else{ /* All other (one character) operators */ - cp++; - nextcp = cp; - } - c = *cp; - *cp = 0; /* Null terminate the token */ - parseonetoken(&ps); /* Parse the token */ - *cp = (char)c; /* Restore the buffer */ - cp = nextcp; - } - lemon_free(filebuf); /* Release the buffer after parsing */ - gp->rule = ps.firstrule; - gp->errorcnt = ps.errorcnt; -} -/*************************** From the file "plink.c" *********************/ -/* -** Routines processing configuration follow-set propagation links -** in the LEMON parser generator. -*/ -static struct plink *plink_freelist = 0; - -/* Allocate a new plink */ -struct plink *Plink_new(void){ - struct plink *newlink; - - if( plink_freelist==0 ){ - int i; - int amt = 100; - plink_freelist = (struct plink *)lemon_calloc( amt, sizeof(struct plink) ); - if( plink_freelist==0 ){ - fprintf(stderr, - "Unable to allocate memory for a new follow-set propagation link.\n"); - exit(1); - } - for(i=0; inext; - return newlink; -} - -/* Add a plink to a plink list */ -void Plink_add(struct plink **plpp, struct config *cfp) -{ - struct plink *newlink; - newlink = Plink_new(); - newlink->next = *plpp; - *plpp = newlink; - newlink->cfp = cfp; -} - -/* Transfer every plink on the list "from" to the list "to" */ -void Plink_copy(struct plink **to, struct plink *from) -{ - struct plink *nextpl; - while( from ){ - nextpl = from->next; - from->next = *to; - *to = from; - from = nextpl; - } -} - -/* Delete every plink on the list */ -void Plink_delete(struct plink *plp) -{ - struct plink *nextpl; - - while( plp ){ - nextpl = plp->next; - plp->next = plink_freelist; - plink_freelist = plp; - plp = nextpl; - } -} -/*********************** From the file "report.c" **************************/ -/* -** Procedures for generating reports and tables in the LEMON parser generator. -*/ - -/* Generate a filename with the given suffix. -*/ -PRIVATE char *file_makename(struct lemon *lemp, const char *suffix) -{ - char *name; - char *cp; - char *filename = lemp->filename; - int sz; - - if( outputDir ){ - cp = strrchr(filename, '/'); - if( cp ) filename = cp + 1; - } - sz = lemonStrlen(filename); - sz += lemonStrlen(suffix); - if( outputDir ) sz += lemonStrlen(outputDir) + 1; - sz += 5; - name = (char*)lemon_malloc( sz ); - if( name==0 ){ - fprintf(stderr,"Can't allocate space for a filename.\n"); - exit(1); - } - name[0] = 0; - if( outputDir ){ - lemon_strcpy(name, outputDir); - lemon_strcat(name, "/"); - } - lemon_strcat(name,filename); - cp = strrchr(name,'.'); - if( cp ) *cp = 0; - lemon_strcat(name,suffix); - return name; -} - -/* Open a file with a name based on the name of the input file, -** but with a different (specified) suffix, and return a pointer -** to the stream */ -PRIVATE FILE *file_open( - struct lemon *lemp, - const char *suffix, - const char *mode -){ - FILE *fp; - - if( lemp->outname ) lemon_free(lemp->outname); - lemp->outname = file_makename(lemp, suffix); - fp = fopen(lemp->outname,mode); - if( fp==0 && *mode=='w' ){ - fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname); - lemp->errorcnt++; - return 0; - } - return fp; -} - -/* Print the text of a rule -*/ -void rule_print(FILE *out, struct rule *rp){ - int i, j; - fprintf(out, "%s",rp->lhs->name); - /* if( rp->lhsalias ) fprintf(out,"(%s)",rp->lhsalias); */ - fprintf(out," ::="); - for(i=0; inrhs; i++){ - struct symbol *sp = rp->rhs[i]; - if( sp->type==MULTITERMINAL ){ - fprintf(out," %s", sp->subsym[0]->name); - for(j=1; jnsubsym; j++){ - fprintf(out,"|%s", sp->subsym[j]->name); - } - }else{ - fprintf(out," %s", sp->name); - } - /* if( rp->rhsalias[i] ) fprintf(out,"(%s)",rp->rhsalias[i]); */ - } -} - -/* Duplicate the input file without comments and without actions -** on rules */ -void Reprint(struct lemon *lemp) -{ - struct rule *rp; - struct symbol *sp; - int i, j, maxlen, len, ncolumns, skip; - printf("// Reprint of input file \"%s\".\n// Symbols:\n",lemp->filename); - maxlen = 10; - for(i=0; insymbol; i++){ - sp = lemp->symbols[i]; - len = lemonStrlen(sp->name); - if( len>maxlen ) maxlen = len; - } - ncolumns = 76/(maxlen+5); - if( ncolumns<1 ) ncolumns = 1; - skip = (lemp->nsymbol + ncolumns - 1)/ncolumns; - for(i=0; insymbol; j+=skip){ - sp = lemp->symbols[j]; - assert( sp->index==j ); - printf(" %3d %-*.*s",j,maxlen,maxlen,sp->name); - } - printf("\n"); - } - for(rp=lemp->rule; rp; rp=rp->next){ - rule_print(stdout, rp); - printf("."); - if( rp->precsym ) printf(" [%s]",rp->precsym->name); - /* if( rp->code ) printf("\n %s",rp->code); */ - printf("\n"); - } -} - -/* Print a single rule. -*/ -void RulePrint(FILE *fp, struct rule *rp, int iCursor){ - struct symbol *sp; - int i, j; - fprintf(fp,"%s ::=",rp->lhs->name); - for(i=0; i<=rp->nrhs; i++){ - if( i==iCursor ) fprintf(fp," *"); - if( i==rp->nrhs ) break; - sp = rp->rhs[i]; - if( sp->type==MULTITERMINAL ){ - fprintf(fp," %s", sp->subsym[0]->name); - for(j=1; jnsubsym; j++){ - fprintf(fp,"|%s",sp->subsym[j]->name); - } - }else{ - fprintf(fp," %s", sp->name); - } - } -} - -/* Print the rule for a configuration. -*/ -void ConfigPrint(FILE *fp, struct config *cfp){ - RulePrint(fp, cfp->rp, cfp->dot); -} - -/* #define TEST */ -#if 0 -/* Print a set */ -PRIVATE void SetPrint(out,set,lemp) -FILE *out; -char *set; -struct lemon *lemp; -{ - int i; - char *spacer; - spacer = ""; - fprintf(out,"%12s[",""); - for(i=0; interminal; i++){ - if( SetFind(set,i) ){ - fprintf(out,"%s%s",spacer,lemp->symbols[i]->name); - spacer = " "; - } - } - fprintf(out,"]\n"); -} - -/* Print a plink chain */ -PRIVATE void PlinkPrint(out,plp,tag) -FILE *out; -struct plink *plp; -char *tag; -{ - while( plp ){ - fprintf(out,"%12s%s (state %2d) ","",tag,plp->cfp->stp->statenum); - ConfigPrint(out,plp->cfp); - fprintf(out,"\n"); - plp = plp->next; - } -} -#endif - -/* Print an action to the given file descriptor. Return FALSE if -** nothing was actually printed. -*/ -int PrintAction( - struct action *ap, /* The action to print */ - FILE *fp, /* Print the action here */ - int indent /* Indent by this amount */ -){ - int result = 1; - switch( ap->type ){ - case SHIFT: { - struct state *stp = ap->x.stp; - fprintf(fp,"%*s shift %-7d",indent,ap->sp->name,stp->statenum); - break; - } - case REDUCE: { - struct rule *rp = ap->x.rp; - fprintf(fp,"%*s reduce %-7d",indent,ap->sp->name,rp->iRule); - RulePrint(fp, rp, -1); - break; - } - case SHIFTREDUCE: { - struct rule *rp = ap->x.rp; - fprintf(fp,"%*s shift-reduce %-7d",indent,ap->sp->name,rp->iRule); - RulePrint(fp, rp, -1); - break; - } - case ACCEPT: - fprintf(fp,"%*s accept",indent,ap->sp->name); - break; - case ERROR: - fprintf(fp,"%*s error",indent,ap->sp->name); - break; - case SRCONFLICT: - case RRCONFLICT: - fprintf(fp,"%*s reduce %-7d ** Parsing conflict **", - indent,ap->sp->name,ap->x.rp->iRule); - break; - case SSCONFLICT: - fprintf(fp,"%*s shift %-7d ** Parsing conflict **", - indent,ap->sp->name,ap->x.stp->statenum); - break; - case SH_RESOLVED: - if( showPrecedenceConflict ){ - fprintf(fp,"%*s shift %-7d -- dropped by precedence", - indent,ap->sp->name,ap->x.stp->statenum); - }else{ - result = 0; - } - break; - case RD_RESOLVED: - if( showPrecedenceConflict ){ - fprintf(fp,"%*s reduce %-7d -- dropped by precedence", - indent,ap->sp->name,ap->x.rp->iRule); - }else{ - result = 0; - } - break; - case NOT_USED: - result = 0; - break; - } - if( result && ap->spOpt ){ - fprintf(fp," /* because %s==%s */", ap->sp->name, ap->spOpt->name); - } - return result; -} - -/* Generate the "*.out" log file */ -void ReportOutput(struct lemon *lemp) -{ - int i, n; - struct state *stp; - struct config *cfp; - struct action *ap; - struct rule *rp; - FILE *fp; - - fp = file_open(lemp,".out","wb"); - if( fp==0 ) return; - for(i=0; inxstate; i++){ - stp = lemp->sorted[i]; - fprintf(fp,"State %d:\n",stp->statenum); - if( lemp->basisflag ) cfp=stp->bp; - else cfp=stp->cfp; - while( cfp ){ - char buf[20]; - if( cfp->dot==cfp->rp->nrhs ){ - lemon_sprintf(buf,"(%d)",cfp->rp->iRule); - fprintf(fp," %5s ",buf); - }else{ - fprintf(fp," "); - } - ConfigPrint(fp,cfp); - fprintf(fp,"\n"); -#if 0 - SetPrint(fp,cfp->fws,lemp); - PlinkPrint(fp,cfp->fplp,"To "); - PlinkPrint(fp,cfp->bplp,"From"); -#endif - if( lemp->basisflag ) cfp=cfp->bp; - else cfp=cfp->next; - } - fprintf(fp,"\n"); - for(ap=stp->ap; ap; ap=ap->next){ - if( PrintAction(ap,fp,30) ) fprintf(fp,"\n"); - } - fprintf(fp,"\n"); - } - fprintf(fp, "----------------------------------------------------\n"); - fprintf(fp, "Symbols:\n"); - fprintf(fp, "The first-set of non-terminals is shown after the name.\n\n"); - for(i=0; insymbol; i++){ - int j; - struct symbol *sp; - - sp = lemp->symbols[i]; - fprintf(fp, " %3d: %s", i, sp->name); - if( sp->type==NONTERMINAL ){ - fprintf(fp, ":"); - if( sp->lambda ){ - fprintf(fp, " "); - } - for(j=0; jnterminal; j++){ - if( sp->firstset && SetFind(sp->firstset, j) ){ - fprintf(fp, " %s", lemp->symbols[j]->name); - } - } - } - if( sp->prec>=0 ) fprintf(fp," (precedence=%d)", sp->prec); - fprintf(fp, "\n"); - } - fprintf(fp, "----------------------------------------------------\n"); - fprintf(fp, "Syntax-only Symbols:\n"); - fprintf(fp, "The following symbols never carry semantic content.\n\n"); - for(i=n=0; insymbol; i++){ - int w; - struct symbol *sp = lemp->symbols[i]; - if( sp->bContent ) continue; - w = (int)strlen(sp->name); - if( n>0 && n+w>75 ){ - fprintf(fp,"\n"); - n = 0; - } - if( n>0 ){ - fprintf(fp, " "); - n++; - } - fprintf(fp, "%s", sp->name); - n += w; - } - if( n>0 ) fprintf(fp, "\n"); - fprintf(fp, "----------------------------------------------------\n"); - fprintf(fp, "Rules:\n"); - for(rp=lemp->rule; rp; rp=rp->next){ - fprintf(fp, "%4d: ", rp->iRule); - rule_print(fp, rp); - fprintf(fp,"."); - if( rp->precsym ){ - fprintf(fp," [%s precedence=%d]", - rp->precsym->name, rp->precsym->prec); - } - fprintf(fp,"\n"); - } - fclose(fp); - return; -} - -/* Search for the file "name" which is in the same directory as -** the executable */ -PRIVATE char *pathsearch(char *argv0, char *name, int modemask) -{ - const char *pathlist; - char *pathbufptr = 0; - char *pathbuf = 0; - char *path,*cp; - char c; - -#ifdef __WIN32__ - cp = strrchr(argv0,'\\'); -#else - cp = strrchr(argv0,'/'); -#endif - if( cp ){ - c = *cp; - *cp = 0; - path = (char *)lemon_malloc( lemonStrlen(argv0) + lemonStrlen(name) + 2 ); - if( path ) lemon_sprintf(path,"%s/%s",argv0,name); - *cp = c; - }else{ - pathlist = getenv("PATH"); - if( pathlist==0 ) pathlist = ".:/bin:/usr/bin"; - pathbuf = (char *) lemon_malloc( lemonStrlen(pathlist) + 1 ); - path = (char *)lemon_malloc( lemonStrlen(pathlist)+lemonStrlen(name)+2 ); - if( (pathbuf != 0) && (path!=0) ){ - pathbufptr = pathbuf; - lemon_strcpy(pathbuf, pathlist); - while( *pathbuf ){ - cp = strchr(pathbuf,':'); - if( cp==0 ) cp = &pathbuf[lemonStrlen(pathbuf)]; - c = *cp; - *cp = 0; - lemon_sprintf(path,"%s/%s",pathbuf,name); - *cp = c; - if( c==0 ) pathbuf[0] = 0; - else pathbuf = &cp[1]; - if( access(path,modemask)==0 ) break; - } - } - lemon_free(pathbufptr); - } - return path; -} - -/* Given an action, compute the integer value for that action -** which is to be put in the action table of the generated machine. -** Return negative if no action should be generated. -*/ -PRIVATE int compute_action(struct lemon *lemp, struct action *ap) -{ - int act; - switch( ap->type ){ - case SHIFT: act = ap->x.stp->statenum; break; - case SHIFTREDUCE: { - /* Since a SHIFT is inherient after a prior REDUCE, convert any - ** SHIFTREDUCE action with a nonterminal on the LHS into a simple - ** REDUCE action: */ - if( ap->sp->index>=lemp->nterminal - && (lemp->errsym==0 || ap->sp->index!=lemp->errsym->index) - ){ - act = lemp->minReduce + ap->x.rp->iRule; - }else{ - act = lemp->minShiftReduce + ap->x.rp->iRule; - } - break; - } - case REDUCE: act = lemp->minReduce + ap->x.rp->iRule; break; - case ERROR: act = lemp->errAction; break; - case ACCEPT: act = lemp->accAction; break; - default: act = -1; break; - } - return act; -} - -#define LINESIZE 1000 -/* The next cluster of routines are for reading the template file -** and writing the results to the generated parser */ -/* The first function transfers data from "in" to "out" until -** a line is seen which begins with "%%". The line number is -** tracked. -** -** if name!=0, then any word that begin with "Parse" is changed to -** begin with *name instead. -*/ -PRIVATE void tplt_xfer(char *name, FILE *in, FILE *out, int *lineno) -{ - int i, iStart; - char line[LINESIZE]; - while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){ - (*lineno)++; - iStart = 0; - if( name ){ - for(i=0; line[i]; i++){ - if( line[i]=='P' && strncmp(&line[i],"Parse",5)==0 - && (i==0 || !ISALPHA(line[i-1])) - ){ - if( i>iStart ) fprintf(out,"%.*s",i-iStart,&line[iStart]); - fprintf(out,"%s",name); - i += 4; - iStart = i+1; - } - } - } - fprintf(out,"%s",&line[iStart]); - } -} - -/* Skip forward past the header of the template file to the first "%%" -*/ -PRIVATE void tplt_skip_header(FILE *in, int *lineno) -{ - char line[LINESIZE]; - while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){ - (*lineno)++; - } -} - -/* The next function finds the template file and opens it, returning -** a pointer to the opened file. */ -PRIVATE FILE *tplt_open(struct lemon *lemp) -{ - static char templatename[] = "lempar.rs"; - char buf[1000]; - FILE *in; - char *tpltname; - char *toFree = 0; - char *cp; - - /* first, see if user specified a template filename on the command line. */ - if (user_templatename != 0) { - if( access(user_templatename,004)==-1 ){ - fprintf(stderr,"Can't find the parser driver template file \"%s\".\n", - user_templatename); - lemp->errorcnt++; - return 0; - } - in = fopen(user_templatename,"rb"); - if( in==0 ){ - fprintf(stderr,"Can't open the template file \"%s\".\n", - user_templatename); - lemp->errorcnt++; - return 0; - } - return in; - } - - cp = strrchr(lemp->filename,'.'); - if( cp ){ - lemon_sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename); - }else{ - lemon_sprintf(buf,"%s.lt",lemp->filename); - } - if( access(buf,004)==0 ){ - tpltname = buf; - }else if( access(templatename,004)==0 ){ - tpltname = templatename; - }else{ - toFree = tpltname = pathsearch(lemp->argv[0],templatename,0); - } - if( tpltname==0 ){ - fprintf(stderr,"Can't find the parser driver template file \"%s\".\n", - templatename); - lemp->errorcnt++; - return 0; - } - in = fopen(tpltname,"rb"); - if( in==0 ){ - fprintf(stderr,"Can't open the template file \"%s\".\n",tpltname); - lemp->errorcnt++; - } - lemon_free(toFree); - return in; -} - -/* Print a #line directive line to the output file. */ -PRIVATE void tplt_linedir(FILE *out, int lineno, char *filename) -{ - fprintf(out,"//line %d \"",lineno); - while( *filename ){ - if( *filename == '\\' ) putc('\\',out); - putc(*filename,out); - filename++; - } - fprintf(out,"\"\n"); -} - -/* Print a string to the file and keep the linenumber up to date */ -PRIVATE void tplt_print(FILE *out, struct lemon *lemp, char *str, int *lineno) -{ - if( str==0 ) return; - while( *str ){ - putc(*str,out); - if( *str=='\n' ) (*lineno)++; - str++; - } - if( str[-1]!='\n' ){ - putc('\n',out); - (*lineno)++; - } - if (!lemp->nolinenosflag) { - //(*lineno)++; tplt_linedir(out,*lineno,lemp->outname); - } - return; -} - -/* -** Append text to a dynamically allocated string. If zText is 0 then -** reset the string to be empty again. Always return the complete text -** of the string (which is overwritten with each call). -** -** n bytes of zText are stored. If n==0 then all of zText up to the first -** \000 terminator is stored. zText can contain up to two instances of -** %d. The values of p1 and p2 are written into the first and second -** %d. -** -** If n==-1, then the previous character is overwritten. -*/ -PRIVATE char *append_str(const char *zText, int n, int p1, int p2){ - static char empty[1] = { 0 }; - static char *z = 0; - static int alloced = 0; - static int used = 0; - int c; - char zInt[40]; - if( zText==0 ){ - if( used==0 && z!=0 ) z[0] = 0; - used = 0; - return z; - } - if( n<=0 ){ - if( n<0 ){ - used += n; - assert( used>=0 ); - } - n = lemonStrlen(zText); - } - if( (int) (n+sizeof(zInt)*2+used) >= alloced ){ - alloced = n + sizeof(zInt)*2 + used + 200; - z = (char *) lemon_realloc(z, alloced); - } - if( z==0 ) return empty; - while( n-- > 0 ){ - c = *(zText++); - if( c=='%' && n>0 && zText[0]=='d' ){ - lemon_sprintf(zInt, "%d", p1); - p1 = p2; - lemon_strcpy(&z[used], zInt); - used += lemonStrlen(&z[used]); - zText++; - n--; - }else{ - z[used++] = (char)c; - } - } - z[used] = 0; - return z; -} - -/* -** Write and transform the rp->code string so that symbols are expanded. -** Populate the rp->codePrefix and rp->codeSuffix strings, as appropriate. -** -** Return 1 if the expanded code requires that "yylhsminor" local variable -** to be defined. -*/ -PRIVATE int translate_code(struct lemon *lemp, struct rule *rp){ - char *cp, *xp; - int i; - int rc = 0; /* True if yylhsminor is used */ - int dontUseRhs0 = 0; /* If true, use of left-most RHS label is illegal */ - const char *zSkip = 0; /* The zOvwrt comment within rp->code, or NULL */ - char lhsused = 0; /* True if the LHS element has been used */ - char lhsdirect; /* True if LHS writes directly into stack */ - char used[MAXRHS]; /* True for each RHS element which is used */ - char zLhs[50]; /* Convert the LHS symbol into this string */ - char zOvwrt[900]; /* Comment that to allow LHS to overwrite RHS */ - char lhsaccess = 0; - char lhswrite = 0; - - for(i=0; inrhs; i++) used[i] = 0; - lhsused = 0; - - if( rp->code==0 ){ - static char newlinestr[2] = { '\n', '\0' }; - rp->code = newlinestr; - rp->line = rp->ruleline; - rp->noCode = 1; - }else{ - rp->noCode = 0; - } - - - if( rp->nrhs==0 ){ - /* If there are no RHS symbols, then writing directly to the LHS is ok */ - lhsdirect = 1; - }else if( rp->rhsalias[0]==0 ){ - /* The left-most RHS symbol has no value. LHS direct is ok. But - ** we have to call the destructor on the RHS symbol first. */ - lhsdirect = 1; - }else if( rp->lhsalias==0 ){ - /* There is no LHS value symbol. */ - lhsdirect = 1; - }else if( strcmp(rp->lhsalias,rp->rhsalias[0])==0 ){ - /* The LHS symbol and the left-most RHS symbol are the same, so - ** direct writing is allowed */ - lhsdirect = 1; - lhsused = 1; - used[0] = 1; - if( rp->lhs->dtnum!=rp->rhs[0]->dtnum ){ - ErrorMsg(lemp->filename,rp->ruleline, - "%s(%s) and %s(%s) share the same label but have " - "different datatypes.", - rp->lhs->name, rp->lhsalias, rp->rhs[0]->name, rp->rhsalias[0]); - lemp->errorcnt++; - } - }else{ - lemon_sprintf(zOvwrt, "/*%s-overwrites-%s*/", - rp->lhsalias, rp->rhsalias[0]); - zSkip = strstr(rp->code, zOvwrt); - if( zSkip!=0 ){ - /* The code contains a special comment that indicates that it is safe - ** for the LHS label to overwrite left-most RHS label. */ - lhsdirect = 1; - }else{ - lhsdirect = 0; - } - } - if( lhsdirect ){ - sprintf(zLhs, "self[%d]",1-rp->nrhs); - }else{ - rc = 1; - sprintf(zLhs, "yylhsminor"); - } - - append_str(0,0,0,0); - - /* This const cast is wrong but harmless, if we're careful. */ - for(cp=(char *)rp->code; *cp; cp++){ - if( cp==zSkip ){ - append_str(zOvwrt,0,0,0); - cp += lemonStrlen(zOvwrt)-1; - dontUseRhs0 = 1; - continue; - } - if( ISALPHA(*cp) && (cp==rp->code || (!ISALNUM(cp[-1]) && cp[-1]!='_')) ){ - char saved; - for(xp= &cp[1]; ISALNUM(*xp) || *xp=='_'; xp++); - saved = *xp; - *xp = 0; - if( rp->lhsalias && strcmp(cp,rp->lhsalias)==0 ){ - append_str(zLhs,0,0,0); - cp = xp; - lhsused = 1; - lhsaccess = 1; - }else{ - for(i=0; inrhs; i++){ - if( rp->rhsalias[i] && strcmp(cp,rp->rhsalias[i])==0 ){ - if( i==0 && dontUseRhs0 ){ - ErrorMsg(lemp->filename,rp->ruleline, - "Label %s used after '%s'.", - rp->rhsalias[0], zOvwrt); - lemp->errorcnt++; - }else if( cp!=rp->code && cp[-1]=='@' ){ - /* If the argument is of the form @X then substituted - ** the token number of X, not the value of X */ - append_str("self[%d].major",-1,i-rp->nrhs+1,0); - }else{ - struct symbol *sp = rp->rhs[i]; - int dtnum; - if( sp->type==MULTITERMINAL ){ - dtnum = sp->subsym[0]->dtnum; - }else{ - dtnum = sp->dtnum; - } - append_str("self.yy_move(%d).yy%d()",0,i-rp->nrhs+1, dtnum); - } - cp = xp; - used[i] = 1; - break; - } - } - } - *xp = saved; - } - if( lhsaccess ){ - if( cp[0] == '=' ){ - }else if ( !ISSPACE(*cp) ){ - append_str(".yy%d_ref()",0,rp->lhs->dtnum,0); - lhsaccess = 0; - } - } - if( lhswrite ){ - if( cp[0] == ';' ){ - append_str(")",0,0,0); - lhswrite = 0; - } - } - if( lhsaccess ){ - if( cp[0] == '=' ){ - lhswrite = cp[1] != '='; - if( lhswrite && lhsdirect ) { - append_str(".minor",0,0,0); - } - } - } - append_str(cp, 1, 0, 0); - if( lhsaccess ){ - if( cp[0] == '=' ){ - lhsaccess = 0; - if( lhswrite ) { - append_str(" YYMINORTYPE::yy%d(",0,rp->lhs->dtnum,0); - } - } - } - } /* End loop */ - - /* Main code generation completed */ - cp = append_str(0,0,0,0); - if( cp && cp[0] ) rp->code = Strsafe(cp); - append_str(0,0,0,0); - - /* Check to make sure the LHS has been used */ - if( rp->lhsalias && !lhsused ){ - ErrorMsg(lemp->filename,rp->ruleline, - "Label \"%s\" for \"%s(%s)\" is never used.", - rp->lhsalias,rp->lhs->name,rp->lhsalias); - lemp->errorcnt++; - } - - /* - ** Generate error messages for unused labels and duplicate labels. - */ - for(i=0; inrhs; i++){ - if( rp->rhsalias[i] ){ - if( i>0 ){ - int j; - if( rp->lhsalias && strcmp(rp->lhsalias,rp->rhsalias[i])==0 ){ - ErrorMsg(lemp->filename,rp->ruleline, - "%s(%s) has the same label as the LHS but is not the left-most " - "symbol on the RHS.", - rp->rhs[i]->name, rp->rhsalias[i]); - lemp->errorcnt++; - } - for(j=0; jrhsalias[j] && strcmp(rp->rhsalias[j],rp->rhsalias[i])==0 ){ - ErrorMsg(lemp->filename,rp->ruleline, - "Label %s used for multiple symbols on the RHS of a rule.", - rp->rhsalias[i]); - lemp->errorcnt++; - break; - } - } - } - if( !used[i] ){ - ErrorMsg(lemp->filename,rp->ruleline, - "Label %s for \"%s(%s)\" is never used.", - rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]); - lemp->errorcnt++; - } - } - } - - /* If unable to write LHS values directly into the stack, write the - ** saved LHS value now. */ - if( lhsdirect==0 ){ - append_str(" self[%d].minor = ", 0, 1-rp->nrhs, 0); - append_str(zLhs, 0, 0, 0); - append_str(";\n", 0, 0, 0); - } - - /* Suffix code generation complete */ - cp = append_str(0,0,0,0); - if( cp && cp[0] ){ - rp->codeSuffix = Strsafe(cp); - rp->noCode = 0; - } - - return rc; -} - -/* -** Generate code which executes when the rule "rp" is reduced. Write -** the code to "out". Make sure lineno stays up-to-date. -*/ -PRIVATE void emit_code( - FILE *out, - struct rule *rp, - struct lemon *lemp, - int *lineno -){ - const char *cp; - - /* Setup code prior to the #line directive */ - if( rp->codePrefix && rp->codePrefix[0] ){ - fprintf(out, "{%s", rp->codePrefix); - for(cp=rp->codePrefix; *cp; cp++){ if( *cp=='\n' ) (*lineno)++; } - } - - /* Generate code to do the reduce action */ - if( rp->code ){ - if( !lemp->nolinenosflag ){ - (*lineno)++; - tplt_linedir(out,rp->line,lemp->filename); - } - fprintf(out,"{%s",rp->code); - for(cp=rp->code; *cp; cp++){ if( *cp=='\n' ) (*lineno)++; } - fprintf(out,"}\n"); (*lineno)++; - /*if( !lemp->nolinenosflag ){ - (*lineno)++; - tplt_linedir(out,*lineno,lemp->outname); - }*/ - } - - /* Generate breakdown code that occurs after the #line directive */ - if( rp->codeSuffix && rp->codeSuffix[0] ){ - fprintf(out, "%s", rp->codeSuffix); - for(cp=rp->codeSuffix; *cp; cp++){ if( *cp=='\n' ) (*lineno)++; } - } - - if( rp->codePrefix ){ - fprintf(out, "}\n"); (*lineno)++; - } - - return; -} - -/* -** Print the definition of the union used for the parser's data stack. -** This union contains fields for every possible data type for tokens -** and nonterminals. In the process of computing and printing this -** union, also set the ".dtnum" field of every terminal and nonterminal -** symbol. -*/ -void print_stack_union( - FILE *out, /* The output stream */ - struct lemon *lemp, /* The main info structure for this parser */ - int *plineno /* Pointer to the line number */ -){ - int lineno; /* The line number of the output */ - char **types; /* A hash table of datatypes */ - int arraysize; /* Size of the "types" array */ - int maxdtlength; /* Maximum length of any ".datatype" field. */ - char *stddt; /* Standardized name for a datatype */ - int i,j; /* Loop counters */ - unsigned hash; /* For hashing the name of a type */ - const char *name; /* Name of the parser */ - - /* Allocate and initialize types[] and allocate stddt[] */ - arraysize = lemp->nsymbol * 2; - types = (char**)lemon_calloc( arraysize, sizeof(char*) ); - if( types==0 ){ - fprintf(stderr,"Out of memory.\n"); - exit(1); - } - for(i=0; ivartype ){ - maxdtlength = lemonStrlen(lemp->vartype); - } - for(i=0; insymbol; i++){ - int len; - struct symbol *sp = lemp->symbols[i]; - if( sp->datatype==0 ) continue; - len = lemonStrlen(sp->datatype); - if( len>maxdtlength ) maxdtlength = len; - } - stddt = (char*)lemon_malloc( maxdtlength*2 + 1 ); - if( stddt==0 ){ - fprintf(stderr,"Out of memory.\n"); - exit(1); - } - - /* Build a hash table of datatypes. The ".dtnum" field of each symbol - ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is - ** used for terminal symbols. If there is no %default_type defined then - ** 0 is also used as the .dtnum value for nonterminals which do not specify - ** a datatype using the %type directive. - */ - for(i=0; insymbol; i++){ - struct symbol *sp = lemp->symbols[i]; - char *cp; - if( sp==lemp->errsym ){ - sp->dtnum = arraysize+1; - continue; - } - if( sp->type!=NONTERMINAL || (sp->datatype==0 && lemp->vartype==0) ){ - sp->dtnum = 0; - continue; - } - cp = sp->datatype; - if( cp==0 ) cp = lemp->vartype; - j = 0; - while( ISSPACE(*cp) ) cp++; - while( *cp ) stddt[j++] = *cp++; - while( j>0 && ISSPACE(stddt[j-1]) ) j--; - stddt[j] = 0; - if( lemp->tokentype && strcmp(stddt, lemp->tokentype)==0 ){ - sp->dtnum = 0; - continue; - } - hash = 0; - for(j=0; stddt[j]; j++){ - hash = hash*53 + stddt[j]; - } - hash = (hash & 0x7fffffff)%arraysize; - while( types[hash] ){ - if( strcmp(types[hash],stddt)==0 ){ - sp->dtnum = hash + 1; - break; - } - hash++; - if( hash>=(unsigned)arraysize ) hash = 0; - } - if( types[hash]==0 ){ - sp->dtnum = hash + 1; - types[hash] = (char*)lemon_malloc( lemonStrlen(stddt)+1 ); - if( types[hash]==0 ){ - fprintf(stderr,"Out of memory.\n"); - exit(1); - } - lemon_strcpy(types[hash],stddt); - } - } - - /* Print out the definition of YYTOKENTYPE and YYMINORTYPE */ - name = lemp->name ? lemp->name : "Parse"; - lineno = *plineno; - fprintf(out,"#[expect(non_camel_case_types)]\n"); lineno++; - fprintf(out,"type %sTOKENTYPE<'i> = %s;\n",name, - lemp->tokentype?lemp->tokentype:"()"); lineno++; - fprintf(out,"#[expect(non_camel_case_types)]\n"); lineno++; - fprintf(out,"enum YYMINORTYPE<'i> {\n"); lineno++; - fprintf(out," yyinit(),\n"); lineno++; - fprintf(out," yy0(%sTOKENTYPE<'i>),\n",name); lineno++; - for(i=0; ierrsym && lemp->errsym->useCnt ){ - fprintf(out," yy%d(i32),\n",lemp->errsym->dtnum); lineno++; - } - lemon_free(stddt); - fprintf(out,"}\n"); lineno++; - - fprintf(out,"impl<'i> Default for YYMINORTYPE<'i> {\n"); lineno++; - fprintf(out," fn default() -> YYMINORTYPE<'i> {\n"); lineno++; - fprintf(out," YYMINORTYPE::yyinit()\n"); lineno++; - fprintf(out," }\n"); lineno++; - fprintf(out,"}\n"); lineno++; - - fprintf(out,"impl<'i> yyStackEntry<'i> {\n"); lineno++; - fprintf(out," fn yy0(self) -> %sTOKENTYPE<'i> {\n",name); lineno++; - fprintf(out," if let YYMINORTYPE::yy0(v) = self.minor {\n"); lineno++; - fprintf(out," v\n"); lineno++; - fprintf(out," } else {\n"); lineno++; - fprintf(out," unreachable!()\n"); lineno++; - fprintf(out," }\n"); lineno++; - fprintf(out," }\n"); lineno++; - for(i=0; i %s {\n",i+1,types[i]); lineno++; - fprintf(out," if let YYMINORTYPE::yy%d(v) = self.minor {\n",i+1); lineno++; - fprintf(out," v\n"); lineno++; - fprintf(out," } else {\n"); lineno++; - fprintf(out," unreachable!()\n"); lineno++; // TODO } else if YYMINORTYPE::yyinit() { return None; - fprintf(out," }\n"); lineno++; - fprintf(out," }\n"); lineno++; - fprintf(out," #[allow(dead_code)]\n"); lineno++; // TODO generates only when needed - fprintf(out," fn yy%d_ref(&mut self) -> &mut %s {\n",i+1,types[i]); lineno++; - fprintf(out," if let YYMINORTYPE::yy%d(ref mut v) = self.minor {\n",i+1); lineno++; - fprintf(out," v\n"); lineno++; - fprintf(out," } else {\n"); lineno++; - fprintf(out," unreachable!()\n"); lineno++; - fprintf(out," }\n"); lineno++; - fprintf(out," }\n"); lineno++; - lemon_free(types[i]); - } - lemon_free(types); - if( lemp->errsym && lemp->errsym->useCnt ){ - fprintf(out," fn yy%d(self) -> i32 {\n",lemp->errsym->dtnum); lineno++; - fprintf(out," if let YYMINORTYPE::yy%d(v) = self.minor {\n",lemp->errsym->dtnum); lineno++; - fprintf(out," v\n"); lineno++; - fprintf(out," } else {\n"); lineno++; - fprintf(out," unreachable!()\n"); lineno++; - fprintf(out," }\n"); lineno++; - fprintf(out," }\n"); lineno++; - } - fprintf(out,"}\n"); lineno++; - - *plineno = lineno; -} - -/* -** Return the name of a C datatype able to represent values between -** lwr and upr, inclusive. If pnByte!=NULL then also write the sizeof -** for that type (1, 2, or 4) into *pnByte. -*/ -static const char *minimum_size_type(int lwr, int upr, int *pnByte){ - const char *zType = "i32"; - int nByte = 4; - if( lwr>=0 ){ - if( upr<=255 ){ - zType = "u8"; - nByte = 1; - }else if( upr<65535 ){ - zType = "u16"; - nByte = 2; - }else{ - zType = "u32"; - nByte = 4; - } - }else if( lwr>=-127 && upr<=127 ){ - zType = "i8"; - nByte = 1; - }else if( lwr>=-32767 && upr<32767 ){ - zType = "i16"; - nByte = 2; - } - if( pnByte ) *pnByte = nByte; - return zType; -} - -/* -** Each state contains a set of token transaction and a set of -** nonterminal transactions. Each of these sets makes an instance -** of the following structure. An array of these structures is used -** to order the creation of entries in the yy_action[] table. -*/ -struct axset { - struct state *stp; /* A pointer to a state */ - int isTkn; /* True to use tokens. False for non-terminals */ - int nAction; /* Number of actions */ - int iOrder; /* Original order of action sets */ -}; - -/* -** Compare to axset structures for sorting purposes -*/ -static int axset_compare(const void *a, const void *b){ - struct axset *p1 = (struct axset*)a; - struct axset *p2 = (struct axset*)b; - int c; - c = p2->nAction - p1->nAction; - if( c==0 ){ - c = p1->iOrder - p2->iOrder; - } - assert( c!=0 || p1==p2 ); - return c; -} - -/* -** Write text on "out" that describes the rule "rp". -*/ -static void writeRuleText(FILE *out, struct rule *rp){ - int j; - fprintf(out,"%s ::=", rp->lhs->name); - for(j=0; jnrhs; j++){ - struct symbol *sp = rp->rhs[j]; - if( sp->type!=MULTITERMINAL ){ - fprintf(out," %s", sp->name); - }else{ - int k; - fprintf(out," %s", sp->subsym[0]->name); - for(k=1; knsubsym; k++){ - fprintf(out,"|%s",sp->subsym[k]->name); - } - } - } -} - - -/* Generate Rust source code for the parser */ -void ReportTable( - struct lemon *lemp, - int mhflag, /* Output in makeheaders format if true */ - int sqlFlag /* Generate the *.sql file too */ -){ - FILE *out, *in, *sql; - int lineno; - struct state *stp; - struct action *ap; - struct rule *rp; - struct acttab *pActtab; - int i, j, n, sz; - int nLookAhead; - int szActionType; /* sizeof(YYACTIONTYPE) */ - int szCodeType; /* sizeof(YYCODETYPE) */ - int mnTknOfst, mxTknOfst; - int mnNtOfst, mxNtOfst; - struct axset *ax; - char *prefix; - - lemp->minShiftReduce = lemp->nstate; - lemp->errAction = lemp->minShiftReduce + lemp->nrule; - lemp->accAction = lemp->errAction + 1; - lemp->noAction = lemp->accAction + 1; - lemp->minReduce = lemp->noAction + 1; - lemp->maxAction = lemp->minReduce + lemp->nrule; - - in = tplt_open(lemp); - if( in==0 ) return; - out = file_open(lemp,".rs","wb"); - if( out==0 ){ - fclose(in); - return; - } - if( sqlFlag==0 ){ - sql = 0; - }else{ - sql = file_open(lemp, ".sql", "wb"); - if( sql==0 ){ - fclose(in); - fclose(out); - return; - } - fprintf(sql, - "BEGIN;\n" - "CREATE TABLE symbol(\n" - " id INTEGER PRIMARY KEY,\n" - " name TEXT NOT NULL,\n" - " isTerminal BOOLEAN NOT NULL,\n" - " fallback INTEGER REFERENCES symbol" - " DEFERRABLE INITIALLY DEFERRED\n" - ");\n" - ); - for(i=0; insymbol; i++){ - fprintf(sql, - "INSERT INTO symbol(id,name,isTerminal,fallback)" - "VALUES(%d,'%s',%s", - i, lemp->symbols[i]->name, - interminal ? "TRUE" : "FALSE" - ); - if( lemp->symbols[i]->fallback ){ - fprintf(sql, ",%d);\n", lemp->symbols[i]->fallback->index); - }else{ - fprintf(sql, ",NULL);\n"); - } - } - fprintf(sql, - "CREATE TABLE rule(\n" - " ruleid INTEGER PRIMARY KEY,\n" - " lhs INTEGER REFERENCES symbol(id),\n" - " txt TEXT\n" - ");\n" - "CREATE TABLE rulerhs(\n" - " ruleid INTEGER REFERENCES rule(ruleid),\n" - " pos INTEGER,\n" - " sym INTEGER REFERENCES symbol(id)\n" - ");\n" - ); - for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){ - assert( i==rp->iRule ); - fprintf(sql, - "INSERT INTO rule(ruleid,lhs,txt)VALUES(%d,%d,'", - rp->iRule, rp->lhs->index - ); - writeRuleText(sql, rp); - fprintf(sql,"');\n"); - for(j=0; jnrhs; j++){ - struct symbol *sp = rp->rhs[j]; - if( sp->type!=MULTITERMINAL ){ - fprintf(sql, - "INSERT INTO rulerhs(ruleid,pos,sym)VALUES(%d,%d,%d);\n", - i,j,sp->index - ); - }else{ - int k; - for(k=0; knsubsym; k++){ - fprintf(sql, - "INSERT INTO rulerhs(ruleid,pos,sym)VALUES(%d,%d,%d);\n", - i,j,sp->subsym[k]->index - ); - } - } - } - } - fprintf(sql, "COMMIT;\n"); - } - lineno = 1; - - fprintf(out, - "/* This file is automatically generated by Lemon from input grammar\n" - "** source file \"%s\"", lemp->filename); lineno++; - if( nDefineUsed==0 ){ - fprintf(out, ".\n*/\n"); lineno += 2; - }else{ - fprintf(out, " with these options:\n**\n"); lineno += 2; - for(i=0; iinclude==0 ) lemp->include = ""; - for(i=0; ISSPACE(lemp->include[i]); i++){ - if( lemp->include[i]=='\n' ){ - lemp->include += i+1; - i = -1; - } - } - if( lemp->include[0]=='/' ){ - tplt_skip_header(in,&lineno); - }else{ - tplt_xfer(lemp->name,in,out,&lineno); - } - - /* Generate the include code, if any */ - tplt_print(out,lemp,lemp->include,&lineno); - tplt_xfer(lemp->name,in,out,&lineno); - - /* Generate #defines for all tokens */ - if( lemp->tokenprefix ) prefix = lemp->tokenprefix; - else prefix = ""; - if( mhflag ){ - fprintf(out,"#[expect(non_camel_case_types)]\n"); lineno++; - fprintf(out,"#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]\n"); lineno++; - fprintf(out,"#[repr(%s)]\n", - minimum_size_type(0, lemp->nsymbol+1, 0)); lineno++; - fprintf(out,"pub enum TokenType {\n"); lineno++; - fprintf(out," %sEOF = 0,\n",prefix); - for(i=1; interminal; i++){ - fprintf(out," %s%s = %d,\n",prefix,lemp->symbols[i]->name,i); - lineno++; - } - fprintf(out,"}\n"); lineno++; - } - tplt_xfer(lemp->name,in,out,&lineno); - - /* Generate the defines */ - fprintf(out,"pub type YYCODETYPE = %s; // unsigned\n", - minimum_size_type(0, lemp->nsymbol+1, &szCodeType)); lineno++; - fprintf(out,"const YYNOCODE: YYCODETYPE = %d;\n",lemp->nsymbol); lineno++; - fprintf(out,"type YYACTIONTYPE = %s; // unsigned\n", - minimum_size_type(0,lemp->maxAction,&szActionType)); lineno++; - if( lemp->wildcard ){ - fprintf(out,"const YYWILDCARD: YYCODETYPE = %d;\n", - lemp->wildcard->index); lineno++; - } else { - fprintf(out,"const YYWILDCARD: YYCODETYPE = 0; // No wildcard\n"); lineno++; - } - print_stack_union(out,lemp,&lineno); - if( lemp->stacksize ){ - fprintf(out,"const YYSTACKDEPTH: usize = %s;\n",lemp->stacksize); lineno++; - } else { - // from sqlite: The default value is 100. A typical application will use less than about 20 levels of the stack. Developers whose applications contain SQL statements that need more than 100 LALR(1) stack entries should seriously consider refactoring their SQL as it is likely to be well beyond the ability of any human to comprehend. - fprintf(out, "const YYSTACKDEPTH: usize = 100;\n"); lineno++; - } - if( lemp->errsym && lemp->errsym->useCnt ){ - fprintf(out,"const YYERRORSYMBOL: YYCODETYPE = %d;\n",lemp->errsym->index); lineno++; - //fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++; - } else { - fprintf(out,"const YYERRORSYMBOL: YYCODETYPE = 0; // No error symbol\n"); lineno++; - } - if( lemp->has_fallback ){ - fprintf(out,"const YYFALLBACK: bool = true;\n"); lineno++; - } else { - fprintf(out,"const YYFALLBACK: bool = false;\n"); lineno++; - } - - /* Compute the action table, but do not output it yet. The action - ** table must be computed before generating the YYNSTATE macro because - ** we need to know how many states can be eliminated. - */ - ax = (struct axset *) lemon_calloc(lemp->nxstate*2, sizeof(ax[0])); - if( ax==0 ){ - fprintf(stderr,"malloc failed\n"); - exit(1); - } - for(i=0; inxstate; i++){ - stp = lemp->sorted[i]; - ax[i*2].stp = stp; - ax[i*2].isTkn = 1; - ax[i*2].nAction = stp->nTknAct; - ax[i*2+1].stp = stp; - ax[i*2+1].isTkn = 0; - ax[i*2+1].nAction = stp->nNtAct; - } - mxTknOfst = mnTknOfst = 0; - mxNtOfst = mnNtOfst = 0; - /* In an effort to minimize the action table size, use the heuristic - ** of placing the largest action sets first */ - for(i=0; inxstate*2; i++) ax[i].iOrder = i; - qsort(ax, lemp->nxstate*2, sizeof(ax[0]), axset_compare); - pActtab = acttab_alloc(lemp->nsymbol, lemp->nterminal); - for(i=0; inxstate*2 && ax[i].nAction>0; i++){ - stp = ax[i].stp; - if( ax[i].isTkn ){ - for(ap=stp->ap; ap; ap=ap->next){ - int action; - if( ap->sp->index>=lemp->nterminal ) continue; - action = compute_action(lemp, ap); - if( action<0 ) continue; - acttab_action(pActtab, ap->sp->index, action); - } - stp->iTknOfst = acttab_insert(pActtab, 1); - if( stp->iTknOfstiTknOfst; - if( stp->iTknOfst>mxTknOfst ) mxTknOfst = stp->iTknOfst; - }else{ - for(ap=stp->ap; ap; ap=ap->next){ - int action; - if( ap->sp->indexnterminal ) continue; - if( ap->sp->index==lemp->nsymbol ) continue; - action = compute_action(lemp, ap); - if( action<0 ) continue; - acttab_action(pActtab, ap->sp->index, action); - } - stp->iNtOfst = acttab_insert(pActtab, 0); - if( stp->iNtOfstiNtOfst; - if( stp->iNtOfst>mxNtOfst ) mxNtOfst = stp->iNtOfst; - } -#if 0 /* Uncomment for a trace of how the yy_action[] table fills out */ - { int jj, nn; - for(jj=nn=0; jjnAction; jj++){ - if( pActtab->aAction[jj].action<0 ) nn++; - } - printf("%4d: State %3d %s n: %2d size: %5d freespace: %d\n", - i, stp->statenum, ax[i].isTkn ? "Token" : "Var ", - ax[i].nAction, pActtab->nAction, nn); - } -#endif - } - lemon_free(ax); - - /* Mark rules that are actually used for reduce actions after all - ** optimizations have been applied - */ - for(rp=lemp->rule; rp; rp=rp->next) rp->doesReduce = LEMON_FALSE; - for(i=0; inxstate; i++){ - for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){ - if( ap->type==REDUCE || ap->type==SHIFTREDUCE ){ - ap->x.rp->doesReduce = 1; - } - } - } - - /* Finish rendering the constants now that the action table has - ** been computed */ - fprintf(out,"#[cfg(any(feature = \"YYCOVERAGE\", not(feature = \"NDEBUG\")))]\n"); lineno++; - fprintf(out,"const YYNSTATE: YYACTIONTYPE = %d;\n",lemp->nxstate); lineno++; - fprintf(out,"const YYNRULE: usize = %d;\n",lemp->nrule); lineno++; - fprintf(out,"#[cfg(not(feature = \"NDEBUG\"))]\n"); lineno++; - fprintf(out,"const YYNRULE_WITH_ACTION: YYACTIONTYPE = %d;\n",lemp->nruleWithAction); - lineno++; - fprintf(out,"const YYNTOKEN: YYACTIONTYPE = %d;\n",lemp->nterminal); lineno++; - fprintf(out,"const YY_MAX_SHIFT: YYACTIONTYPE = %d;\n",lemp->nxstate-1); lineno++; - i = lemp->minShiftReduce; - fprintf(out,"const YY_MIN_SHIFTREDUCE: YYACTIONTYPE = %d;\n",i); lineno++; - i += lemp->nrule; - fprintf(out,"const YY_MAX_SHIFTREDUCE: YYACTIONTYPE = %d;\n", i-1); lineno++; - fprintf(out,"const YY_ERROR_ACTION: YYACTIONTYPE = %d;\n", lemp->errAction); lineno++; - fprintf(out,"const YY_ACCEPT_ACTION: YYACTIONTYPE = %d;\n", lemp->accAction); lineno++; - fprintf(out,"//const YY_NO_ACTION: YYACTIONTYPE = %d;\n", lemp->noAction); lineno++; - fprintf(out,"const YY_MIN_REDUCE: YYACTIONTYPE = %d;\n", lemp->minReduce); lineno++; - i = lemp->minReduce + lemp->nrule; - fprintf(out,"//const YY_MAX_REDUCE: YYACTIONTYPE = %d;\n", i-1); lineno++; - tplt_xfer(lemp->name,in,out,&lineno); - - /* Now output the action table and its associates: - ** - ** yy_action[] A single table containing all actions. - ** yy_lookahead[] A table containing the lookahead for each entry in - ** yy_action. Used to detect hash collisions. - ** yy_shift_ofst[] For each state, the offset into yy_action for - ** shifting terminals. - ** yy_reduce_ofst[] For each state, the offset into yy_action for - ** shifting non-terminals after a reduce. - ** yy_default[] Default action for each state. - */ - - /* Output the yy_action table */ - lemp->nactiontab = n = acttab_action_size(pActtab); - lemp->tablesize += n*szActionType; - fprintf(out,"macro_rules! YY_ACTTAB_COUNT {() => {%d}}\n", n); lineno++; - fprintf(out,"#[rustfmt::skip]\n"); lineno++; - fprintf(out,"#[expect(non_upper_case_globals)]\n"); lineno++; - fprintf(out,"static yy_action: [YYACTIONTYPE; %d] = [\n", n); lineno++; - for(i=j=0; inoAction; - if( j==0 ) fprintf(out," /* %5d */ ", i); - fprintf(out, " %4d,", action); - if( j==9 || i==n-1 ){ - fprintf(out, "\n"); lineno++; - j = 0; - }else{ - j++; - } - } - fprintf(out, "];\n"); lineno++; - - /* Output the yy_lookahead table */ - lemp->nlookaheadtab = n = acttab_lookahead_size(pActtab); - lemp->tablesize += n*szCodeType; - fprintf(out,"#[rustfmt::skip]\n"); lineno++; - fprintf(out,"#[expect(non_upper_case_globals)]\n"); lineno++; - nLookAhead = lemp->nterminal + lemp->nactiontab; - fprintf(out,"static yy_lookahead: [YYCODETYPE; %d] = [\n", nLookAhead); lineno++; - for(i=j=0; insymbol; - if( j==0 ) fprintf(out," /* %5d */ ", i); - fprintf(out, " %4d,", la); - if( j==9 ){ - fprintf(out, "\n"); lineno++; - j = 0; - }else{ - j++; - } - } - /* Add extra entries to the end of the yy_lookahead[] table so that - ** yy_shift_ofst[]+iToken will always be a valid index into the array, - ** even for the largest possible value of yy_shift_ofst[] and iToken. */ - while( interminal); - if( j==9 ){ - fprintf(out, "\n"); lineno++; - j = 0; - }else{ - j++; - } - i++; - } - if( j>0 ){ fprintf(out, "\n"); lineno++; } - fprintf(out, "];\n"); lineno++; - - /* Output the yy_shift_ofst[] table */ - n = lemp->nxstate; - while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--; - fprintf(out, "#[expect(non_camel_case_types)]\n"); lineno++; - fprintf(out, "type YY_SHIFT_TYPE = %s;\n", - minimum_size_type(mnTknOfst, lemp->nterminal+lemp->nactiontab, &sz)); lineno++; - fprintf(out, "const YY_SHIFT_COUNT: YYACTIONTYPE = %d;\n", n-1); lineno++; - fprintf(out, "//const YY_SHIFT_MIN: YY_SHIFT_TYPE = %d;\n", mnTknOfst); lineno++; - fprintf(out, "//const YY_SHIFT_MAX: YY_SHIFT_TYPE = %d;\n", mxTknOfst); lineno++; - fprintf(out, "#[rustfmt::skip]\n"); lineno++; - fprintf(out, "#[expect(non_upper_case_globals)]\n"); lineno++; - fprintf(out, "static yy_shift_ofst: [YY_SHIFT_TYPE; %d] = [\n", n); lineno++; - lemp->tablesize += n*sz; - for(i=j=0; isorted[i]; - ofst = stp->iTknOfst; - if( ofst==NO_OFFSET ) ofst = lemp->nactiontab; - if( j==0 ) fprintf(out," /* %5d */ ", i); - fprintf(out, " %4d,", ofst); - if( j==9 || i==n-1 ){ - fprintf(out, "\n"); lineno++; - j = 0; - }else{ - j++; - } - } - fprintf(out, "];\n"); lineno++; - - /* Output the yy_reduce_ofst[] table */ - fprintf(out, "#[expect(non_camel_case_types)]\n"); lineno++; - fprintf(out, "type YY_REDUCE_TYPE = %s;\n", - minimum_size_type(mnNtOfst-1, mxNtOfst, &sz)); lineno++; - n = lemp->nxstate; - while( n>0 && lemp->sorted[n-1]->iNtOfst==NO_OFFSET ) n--; - fprintf(out, "const YY_REDUCE_COUNT: YYACTIONTYPE = %d;\n", n-1); lineno++; - fprintf(out, "//const YY_REDUCE_MIN: YY_REDUCE_TYPE = %d;\n", mnNtOfst); lineno++; - fprintf(out, "//const YY_REDUCE_MAX: YY_REDUCE_TYPE = %d;\n", mxNtOfst); lineno++; - fprintf(out, "#[rustfmt::skip]\n"); lineno++; - fprintf(out, "#[expect(non_upper_case_globals)]\n"); lineno++; - fprintf(out, "static yy_reduce_ofst: [YY_REDUCE_TYPE; %d] = [\n", n); lineno++; - lemp->tablesize += n*sz; - for(i=j=0; isorted[i]; - ofst = stp->iNtOfst; - if( ofst==NO_OFFSET ) ofst = mnNtOfst - 1; - if( j==0 ) fprintf(out," /* %5d */ ", i); - fprintf(out, " %4d,", ofst); - if( j==9 || i==n-1 ){ - fprintf(out, "\n"); lineno++; - j = 0; - }else{ - j++; - } - } - fprintf(out, "];\n"); lineno++; - - /* Output the default action table */ - n = lemp->nxstate; - fprintf(out, "#[rustfmt::skip]\n"); lineno++; - fprintf(out, "#[expect(non_upper_case_globals)]\n"); lineno++; - fprintf(out, "static yy_default: [YYACTIONTYPE; %d] = [\n", n); lineno++; - lemp->tablesize += n*szActionType; - for(i=j=0; isorted[i]; - if( j==0 ) fprintf(out," /* %5d */ ", i); - if( stp->iDfltReduce<0 ){ - fprintf(out, " %4d,", lemp->errAction); - }else{ - fprintf(out, " %4d,", stp->iDfltReduce + lemp->minReduce); - } - if( j==9 || i==n-1 ){ - fprintf(out, "\n"); lineno++; - j = 0; - }else{ - j++; - } - } - fprintf(out, "];\n"); lineno++; - tplt_xfer(lemp->name,in,out,&lineno); - - /* Generate the table of fallback tokens. - */ - fprintf(out, "#[expect(non_upper_case_globals)]\n"); lineno++; - if( lemp->has_fallback ){ - int mx = lemp->nterminal - 1; - /* 2019-08-28: Generate fallback entries for every token to avoid - ** having to do a range check on the index */ - /* while( mx>0 && lemp->symbols[mx]->fallback==0 ){ mx--; } */ - fprintf(out, "static yyFallback: [YYCODETYPE; %d] = [\n", mx+1); lineno++; - lemp->tablesize += (mx+1)*szCodeType; - for(i=0; i<=mx; i++){ - struct symbol *p = lemp->symbols[i]; - if( p->fallback==0 ){ - fprintf(out, " 0, /* %10s => nothing */\n", p->name); - }else{ - fprintf(out, " %3d, /* %10s => %s */\n", p->fallback->index, - p->name, p->fallback->name); - } - lineno++; - } - fprintf(out, "];\n"); lineno++; - } else { - fprintf(out, "static yyFallback: [YYCODETYPE; 0] = [];\n"); lineno++; - } - tplt_xfer(lemp->name, in, out, &lineno); - - /* Generate %extra_context field declaration - */ - if( lemp->ctx && lemp->ctx[0] ){ - fprintf(out," pub %s<'input>,\n",lemp->ctx); lineno++; - } - tplt_xfer(lemp->name, in, out, &lineno); - - /* Generate a table containing the symbolic name of every symbol - */ - fprintf(out, "#[rustfmt::skip]\n"); lineno++; - fprintf(out, "#[expect(non_upper_case_globals)]\n"); lineno++; - fprintf(out, "static yyTokenName: [&str; %d] = [\n", lemp->nsymbol); lineno++; - for(i=0; insymbol; i++){ - fprintf(out," /* %4d */ \"%s\",\n",i, lemp->symbols[i]->name); lineno++; - } - if( (i&3)!=0 ){ fprintf(out,"\n"); lineno++; } - fprintf(out, "];\n"); lineno++; - - tplt_xfer(lemp->name,in,out,&lineno); - - /* Generate a table containing a text string that describes every - ** rule in the rule set of the grammar. This information is used - ** when tracing REDUCE actions. - */ - for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){ - assert( rp->iRule==i ); - fprintf(out," /* %3d */ \"", i); - writeRuleText(out, rp); - fprintf(out,"\",\n"); lineno++; - } - tplt_xfer(lemp->name,in,out,&lineno); - - /* Generate %extra_context parameter declaration */ - if( lemp->ctx && lemp->ctx[0] ){ - fprintf(out," %s,\n",lemp->ctx); lineno++; - } - tplt_xfer(lemp->name, in, out, &lineno); - - /* Generate %extra_context field initialization */ - if( lemp->ctx && lemp->ctx[0] ){ - char *name = strtok(lemp->ctx,":"); - if (name) { - fprintf(out," %s,\n",name); lineno++; - } - } - tplt_xfer(lemp->name, in, out, &lineno); - - /* Generate code which executes whenever the parser stack overflows */ - tplt_print(out,lemp,lemp->overflow,&lineno); - tplt_xfer(lemp->name,in,out,&lineno); - - /* Generate the tables of rule information. yyRuleInfoLhs[] and - ** yyRuleInfoNRhs[]. - ** - ** Note: This code depends on the fact that rules are number - ** sequentially beginning with 0. - */ - for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){ - fprintf(out," %4d, /* (%d)", rp->lhs->index, i); - rule_print(out, rp); - fprintf(out," */\n"); lineno++; - } - tplt_xfer(lemp->name,in,out,&lineno); - for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){ - fprintf(out," %3d, /* (%d)", -rp->nrhs, i); - rule_print(out, rp); - fprintf(out," */\n"); lineno++; - } - tplt_xfer(lemp->name,in,out,&lineno); - - /* Generate code which execution during each REDUCE action */ - i = 0; - for(rp=lemp->rule; rp; rp=rp->next){ - i += translate_code(lemp, rp); - } - if( i ){ - //fprintf(out," YYMINORTYPE yylhsminor;\n"); lineno++; - } - /* First output rules other than the default: rule */ - for(rp=lemp->rule; rp; rp=rp->next){ - struct rule *rp2; /* Other rules with the same action */ - if( rp->codeEmitted ) continue; - if( rp->noCode ){ - /* No C code actions, so this will be part of the "default:" rule */ - continue; - } - fprintf(out," %d /* ", rp->iRule); - writeRuleText(out, rp); - fprintf(out, " */\n"); lineno++; - for(rp2=rp->next; rp2; rp2=rp2->next){ - if( rp2->code==rp->code && rp2->codePrefix==rp->codePrefix - && rp2->codeSuffix==rp->codeSuffix ){ - fprintf(out," | %d /* ", rp2->iRule); - writeRuleText(out, rp2); - fprintf(out," */\n"); lineno++; - rp2->codeEmitted = 1; - } - } - fprintf(out," => {\n"); lineno++; - emit_code(out,rp,lemp,&lineno); - fprintf(out," }\n"); lineno++; - rp->codeEmitted = 1; - } - /* Finally, output the default: rule. We choose as the default: all - ** empty actions. */ - fprintf(out," _ => {\n"); lineno++; - for(rp=lemp->rule; rp; rp=rp->next){ - if( rp->codeEmitted ) continue; - assert( rp->noCode ); - fprintf(out," /* (%d) ", rp->iRule); - writeRuleText(out, rp); - if( rp->neverReduce ){ - fprintf(out, " (NEVER REDUCES) * debug_assert_ne!(yyruleno, %d); FIXME */\n", - rp->iRule); lineno++; - }else if( rp->doesReduce ){ - fprintf(out, " * debug_assert_eq!(yyruleno, %d); */\n", rp->iRule); lineno++; - }else{ - fprintf(out, " (OPTIMIZED OUT) */ debug_assert_ne!(yyruleno, %d);\n", - rp->iRule); lineno++; - } - } - fprintf(out," }\n"); lineno++; - tplt_xfer(lemp->name,in,out,&lineno); - - /* Generate code which executes if a parse fails */ - tplt_print(out,lemp,lemp->failure,&lineno); - tplt_xfer(lemp->name,in,out,&lineno); - - /* Generate code which executes when a syntax error occurs */ - tplt_print(out,lemp,lemp->error,&lineno); - tplt_xfer(lemp->name,in,out,&lineno); - - /* Generate code which executes when the parser accepts its input */ - tplt_print(out,lemp,lemp->accept,&lineno); - tplt_xfer(lemp->name,in,out,&lineno); - - /* Append any addition code the user desires */ - tplt_print(out,lemp,lemp->extracode,&lineno); - - acttab_free(pActtab); - fclose(in); - fclose(out); - if( sql ) fclose(sql); - return; -} - -/* Generate a header file for the parser */ -void ReportHeader(struct lemon *lemp) -{ - FILE *out; - const char *prefix; - int i; - - if( lemp->tokenprefix ) prefix = lemp->tokenprefix; - else prefix = ""; - out = file_open(lemp,".h","wb"); - if( out ){ - fprintf(out,"#[non_exhaustive]\n"); - fprintf(out,"#[expect(non_camel_case_types)]\n"); - fprintf(out,"#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]\n"); - fprintf(out,"#[repr(%s)]\n", - minimum_size_type(0, lemp->nsymbol+1, 0)); - fprintf(out,"pub enum TokenType {\n"); - fprintf(out," %sEOF = 0,\n",prefix); - for(i=1; interminal; i++){ - fprintf(out," %s%s = %d,\n",prefix,lemp->symbols[i]->name,i); - } - fprintf(out,"}\n"); - fclose(out); - } - return; -} - -/* Reduce the size of the action tables, if possible, by making use -** of defaults. -** -** In this version, we take the most frequent REDUCE action and make -** it the default. Except, there is no default if the wildcard token -** is a possible look-ahead. -*/ -void CompressTables(struct lemon *lemp) -{ - struct state *stp; - struct action *ap, *ap2, *nextap; - struct rule *rp, *rp2, *rbest; - int nbest, n; - int i; - int usesWildcard; - - for(i=0; instate; i++){ - stp = lemp->sorted[i]; - nbest = 0; - rbest = 0; - usesWildcard = 0; - - for(ap=stp->ap; ap; ap=ap->next){ - if( ap->type==SHIFT && ap->sp==lemp->wildcard ){ - usesWildcard = 1; - } - if( ap->type!=REDUCE ) continue; - rp = ap->x.rp; - if( rp->lhsStart ) continue; - if( rp==rbest ) continue; - n = 1; - for(ap2=ap->next; ap2; ap2=ap2->next){ - if( ap2->type!=REDUCE ) continue; - rp2 = ap2->x.rp; - if( rp2==rbest ) continue; - if( rp2==rp ) n++; - } - if( n>nbest ){ - nbest = n; - rbest = rp; - } - } - - /* Do not make a default if the number of rules to default - ** is not at least 1 or if the wildcard token is a possible - ** lookahead. - */ - if( nbest<1 || usesWildcard ) continue; - - - /* Combine matching REDUCE actions into a single default */ - for(ap=stp->ap; ap; ap=ap->next){ - if( ap->type==REDUCE && ap->x.rp==rbest ) break; - } - assert( ap ); - ap->sp = Symbol_new("{default}"); - for(ap=ap->next; ap; ap=ap->next){ - if( ap->type==REDUCE && ap->x.rp==rbest ) ap->type = NOT_USED; - } - stp->ap = Action_sort(stp->ap); - - for(ap=stp->ap; ap; ap=ap->next){ - if( ap->type==SHIFT ) break; - if( ap->type==REDUCE && ap->x.rp!=rbest ) break; - } - if( ap==0 ){ - stp->autoReduce = 1; - stp->pDfltReduce = rbest; - } - } - - /* Make a second pass over all states and actions. Convert - ** every action that is a SHIFT to an autoReduce state into - ** a SHIFTREDUCE action. - */ - for(i=0; instate; i++){ - stp = lemp->sorted[i]; - for(ap=stp->ap; ap; ap=ap->next){ - struct state *pNextState; - if( ap->type!=SHIFT ) continue; - pNextState = ap->x.stp; - if( pNextState->autoReduce && pNextState->pDfltReduce!=0 ){ - ap->type = SHIFTREDUCE; - ap->x.rp = pNextState->pDfltReduce; - } - } - } - - /* If a SHIFTREDUCE action specifies a rule that has a single RHS term - ** (meaning that the SHIFTREDUCE will land back in the state where it - ** started) and if there is no C-code associated with the reduce action, - ** then we can go ahead and convert the action to be the same as the - ** action for the RHS of the rule. - */ - for(i=0; instate; i++){ - stp = lemp->sorted[i]; - for(ap=stp->ap; ap; ap=nextap){ - nextap = ap->next; - if( ap->type!=SHIFTREDUCE ) continue; - rp = ap->x.rp; - if( rp->noCode==0 ) continue; - if( rp->nrhs!=1 ) continue; -#if 1 - /* Only apply this optimization to non-terminals. It would be OK to - ** apply it to terminal symbols too, but that makes the parser tables - ** larger. */ - if( ap->sp->indexnterminal ) continue; -#endif - /* If we reach this point, it means the optimization can be applied */ - nextap = ap; - for(ap2=stp->ap; ap2 && (ap2==ap || ap2->sp!=rp->lhs); ap2=ap2->next){} - assert( ap2!=0 ); - ap->spOpt = ap2->sp; - ap->type = ap2->type; - ap->x = ap2->x; - } - } -} - - -/* -** Compare two states for sorting purposes. The smaller state is the -** one with the most non-terminal actions. If they have the same number -** of non-terminal actions, then the smaller is the one with the most -** token actions. -*/ -static int stateResortCompare(const void *a, const void *b){ - const struct state *pA = *(const struct state**)a; - const struct state *pB = *(const struct state**)b; - int n; - - n = pB->nNtAct - pA->nNtAct; - if( n==0 ){ - n = pB->nTknAct - pA->nTknAct; - if( n==0 ){ - n = pB->statenum - pA->statenum; - } - } - assert( n!=0 ); - return n; -} - - -/* -** Renumber and resort states so that states with fewer choices -** occur at the end. Except, keep state 0 as the first state. -*/ -void ResortStates(struct lemon *lemp) -{ - int i; - struct state *stp; - struct action *ap; - - for(i=0; instate; i++){ - stp = lemp->sorted[i]; - stp->nTknAct = stp->nNtAct = 0; - stp->iDfltReduce = -1; /* Init dflt action to "syntax error" */ - stp->iTknOfst = NO_OFFSET; - stp->iNtOfst = NO_OFFSET; - for(ap=stp->ap; ap; ap=ap->next){ - int iAction = compute_action(lemp,ap); - if( iAction>=0 ){ - if( ap->sp->indexnterminal ){ - stp->nTknAct++; - }else if( ap->sp->indexnsymbol ){ - stp->nNtAct++; - }else{ - assert( stp->autoReduce==0 || stp->pDfltReduce==ap->x.rp ); - stp->iDfltReduce = iAction; - } - } - } - } - qsort(&lemp->sorted[1], lemp->nstate-1, sizeof(lemp->sorted[0]), - stateResortCompare); - for(i=0; instate; i++){ - lemp->sorted[i]->statenum = i; - } - lemp->nxstate = lemp->nstate; - while( lemp->nxstate>1 && lemp->sorted[lemp->nxstate-1]->autoReduce ){ - lemp->nxstate--; - } -} - - -/***************** From the file "set.c" ************************************/ -/* -** Set manipulation routines for the LEMON parser generator. -*/ - -static int size = 0; - -/* Set the set size */ -void SetSize(int n) -{ - size = n+1; -} - -/* Allocate a new set */ -char *SetNew(void){ - char *s; - s = (char*)lemon_calloc( size, 1); - if( s==0 ){ - memory_error(); - } - return s; -} - -/* Deallocate a set */ -void SetFree(char *s) -{ - lemon_free(s); -} - -/* Add a new element to the set. Return TRUE if the element was added -** and FALSE if it was already there. */ -int SetAdd(char *s, int e) -{ - int rv; - assert( e>=0 && esize = 1024; - x1a->count = 0; - x1a->tbl = (x1node*)lemon_calloc(1024, sizeof(x1node) + sizeof(x1node*)); - if( x1a->tbl==0 ){ - lemon_free(x1a); - x1a = 0; - }else{ - int i; - x1a->ht = (x1node**)&(x1a->tbl[1024]); - for(i=0; i<1024; i++) x1a->ht[i] = 0; - } - } -} -/* Insert a new record into the array. Return TRUE if successful. -** Prior data with the same key is NOT overwritten */ -int Strsafe_insert(const char *data) -{ - x1node *np; - unsigned h; - unsigned ph; - - if( x1a==0 ) return 0; - ph = strhash(data); - h = ph & (x1a->size-1); - np = x1a->ht[h]; - while( np ){ - if( strcmp(np->data,data)==0 ){ - /* An existing entry with the same key is found. */ - /* Fail because overwrite is not allows. */ - return 0; - } - np = np->next; - } - if( x1a->count>=x1a->size ){ - /* Need to make the hash table bigger */ - int i,arrSize; - struct s_x1 array; - array.size = arrSize = x1a->size*2; - array.count = x1a->count; - array.tbl = (x1node*)lemon_calloc(arrSize, sizeof(x1node)+sizeof(x1node*)); - if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ - array.ht = (x1node**)&(array.tbl[arrSize]); - for(i=0; icount; i++){ - x1node *oldnp, *newnp; - oldnp = &(x1a->tbl[i]); - h = strhash(oldnp->data) & (arrSize-1); - newnp = &(array.tbl[i]); - if( array.ht[h] ) array.ht[h]->from = &(newnp->next); - newnp->next = array.ht[h]; - newnp->data = oldnp->data; - newnp->from = &(array.ht[h]); - array.ht[h] = newnp; - } - /* lemon_free(x1a->tbl); // This program was originally for 16-bit machines. - ** Don't worry about freeing memory on modern platforms. */ - *x1a = array; - } - /* Insert the new data */ - h = ph & (x1a->size-1); - np = &(x1a->tbl[x1a->count++]); - np->data = data; - if( x1a->ht[h] ) x1a->ht[h]->from = &(np->next); - np->next = x1a->ht[h]; - x1a->ht[h] = np; - np->from = &(x1a->ht[h]); - return 1; -} - -/* Return a pointer to data assigned to the given key. Return NULL -** if no such key. */ -const char *Strsafe_find(const char *key) -{ - unsigned h; - x1node *np; - - if( x1a==0 ) return 0; - h = strhash(key) & (x1a->size-1); - np = x1a->ht[h]; - while( np ){ - if( strcmp(np->data,key)==0 ) break; - np = np->next; - } - return np ? np->data : 0; -} - -/* Return a pointer to the (terminal or nonterminal) symbol "x". -** Create a new symbol if this is the first time "x" has been seen. -*/ -struct symbol *Symbol_new(const char *x) -{ - struct symbol *sp; - - sp = Symbol_find(x); - if( sp==0 ){ - sp = (struct symbol *)lemon_calloc(1, sizeof(struct symbol) ); - MemoryCheck(sp); - sp->name = Strsafe(x); - sp->type = ISUPPER(*x) ? TERMINAL : NONTERMINAL; - sp->rule = 0; - sp->fallback = 0; - sp->prec = -1; - sp->assoc = UNK; - sp->firstset = 0; - sp->lambda = LEMON_FALSE; - sp->datatype = 0; - sp->useCnt = 0; - Symbol_insert(sp,sp->name); - } - sp->useCnt++; - return sp; -} - -/* Compare two symbols for sorting purposes. Return negative, -** zero, or positive if a is less then, equal to, or greater -** than b. -** -** Symbols that begin with upper case letters (terminals or tokens) -** must sort before symbols that begin with lower case letters -** (non-terminals). And MULTITERMINAL symbols (created using the -** %token_class directive) must sort at the very end. Other than -** that, the order does not matter. -** -** We find experimentally that leaving the symbols in their original -** order (the order they appeared in the grammar file) gives the -** smallest parser tables in SQLite. -*/ -int Symbolcmpp(const void *_a, const void *_b) -{ - const struct symbol *a = *(const struct symbol **) _a; - const struct symbol *b = *(const struct symbol **) _b; - int i1 = a->type==MULTITERMINAL ? 3 : a->name[0]>'Z' ? 2 : 1; - int i2 = b->type==MULTITERMINAL ? 3 : b->name[0]>'Z' ? 2 : 1; - return i1==i2 ? a->index - b->index : i1 - i2; -} - -/* There is one instance of the following structure for each -** associative array of type "x2". -*/ -struct s_x2 { - int size; /* The number of available slots. */ - /* Must be a power of 2 greater than or */ - /* equal to 1 */ - int count; /* Number of currently slots filled */ - struct s_x2node *tbl; /* The data stored here */ - struct s_x2node **ht; /* Hash table for lookups */ -}; - -/* There is one instance of this structure for every data element -** in an associative array of type "x2". -*/ -typedef struct s_x2node { - struct symbol *data; /* The data */ - const char *key; /* The key */ - struct s_x2node *next; /* Next entry with the same hash */ - struct s_x2node **from; /* Previous link */ -} x2node; - -/* There is only one instance of the array, which is the following */ -static struct s_x2 *x2a; - -/* Allocate a new associative array */ -void Symbol_init(void){ - if( x2a ) return; - x2a = (struct s_x2*)lemon_malloc( sizeof(struct s_x2) ); - if( x2a ){ - x2a->size = 128; - x2a->count = 0; - x2a->tbl = (x2node*)lemon_calloc(128, sizeof(x2node) + sizeof(x2node*)); - if( x2a->tbl==0 ){ - lemon_free(x2a); - x2a = 0; - }else{ - int i; - x2a->ht = (x2node**)&(x2a->tbl[128]); - for(i=0; i<128; i++) x2a->ht[i] = 0; - } - } -} -/* Insert a new record into the array. Return TRUE if successful. -** Prior data with the same key is NOT overwritten */ -int Symbol_insert(struct symbol *data, const char *key) -{ - x2node *np; - unsigned h; - unsigned ph; - - if( x2a==0 ) return 0; - ph = strhash(key); - h = ph & (x2a->size-1); - np = x2a->ht[h]; - while( np ){ - if( strcmp(np->key,key)==0 ){ - /* An existing entry with the same key is found. */ - /* Fail because overwrite is not allows. */ - return 0; - } - np = np->next; - } - if( x2a->count>=x2a->size ){ - /* Need to make the hash table bigger */ - int i,arrSize; - struct s_x2 array; - array.size = arrSize = x2a->size*2; - array.count = x2a->count; - array.tbl = (x2node*)lemon_calloc(arrSize, sizeof(x2node)+sizeof(x2node*)); - if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ - array.ht = (x2node**)&(array.tbl[arrSize]); - for(i=0; icount; i++){ - x2node *oldnp, *newnp; - oldnp = &(x2a->tbl[i]); - h = strhash(oldnp->key) & (arrSize-1); - newnp = &(array.tbl[i]); - if( array.ht[h] ) array.ht[h]->from = &(newnp->next); - newnp->next = array.ht[h]; - newnp->key = oldnp->key; - newnp->data = oldnp->data; - newnp->from = &(array.ht[h]); - array.ht[h] = newnp; - } - /* lemon_free(x2a->tbl); // This program was originally written for 16-bit - ** machines. Don't worry about freeing this trivial amount of memory - ** on modern platforms. Just leak it. */ - *x2a = array; - } - /* Insert the new data */ - h = ph & (x2a->size-1); - np = &(x2a->tbl[x2a->count++]); - np->key = key; - np->data = data; - if( x2a->ht[h] ) x2a->ht[h]->from = &(np->next); - np->next = x2a->ht[h]; - x2a->ht[h] = np; - np->from = &(x2a->ht[h]); - return 1; -} - -/* Return a pointer to data assigned to the given key. Return NULL -** if no such key. */ -struct symbol *Symbol_find(const char *key) -{ - unsigned h; - x2node *np; - - if( x2a==0 ) return 0; - h = strhash(key) & (x2a->size-1); - np = x2a->ht[h]; - while( np ){ - if( strcmp(np->key,key)==0 ) break; - np = np->next; - } - return np ? np->data : 0; -} - -/* Return the size of the array */ -int Symbol_count() -{ - return x2a ? x2a->count : 0; -} - -/* Return an array of pointers to all data in the table. -** The array is obtained from malloc. Return NULL if memory allocation -** problems, or if the array is empty. */ -struct symbol **Symbol_arrayof() -{ - struct symbol **array; - int i,arrSize; - if( x2a==0 ) return 0; - arrSize = x2a->count; - array = (struct symbol **)lemon_calloc(arrSize, sizeof(struct symbol *)); - if( array ){ - for(i=0; itbl[i].data; - } - return array; -} - -/* Compare two configurations */ -int Configcmp(const char *_a,const char *_b) -{ - const struct config *a = (struct config *) _a; - const struct config *b = (struct config *) _b; - int x; - x = a->rp->index - b->rp->index; - if( x==0 ) x = a->dot - b->dot; - return x; -} - -/* Compare two states */ -PRIVATE int statecmp(struct config *a, struct config *b) -{ - int rc; - for(rc=0; rc==0 && a && b; a=a->bp, b=b->bp){ - rc = a->rp->index - b->rp->index; - if( rc==0 ) rc = a->dot - b->dot; - } - if( rc==0 ){ - if( a ) rc = 1; - if( b ) rc = -1; - } - return rc; -} - -/* Hash a state */ -PRIVATE unsigned statehash(struct config *a) -{ - unsigned h=0; - while( a ){ - h = h*571 + a->rp->index*37 + a->dot; - a = a->bp; - } - return h; -} - -/* Allocate a new state structure */ -struct state *State_new() -{ - struct state *newstate; - newstate = (struct state *)lemon_calloc(1, sizeof(struct state) ); - MemoryCheck(newstate); - return newstate; -} - -/* There is one instance of the following structure for each -** associative array of type "x3". -*/ -struct s_x3 { - int size; /* The number of available slots. */ - /* Must be a power of 2 greater than or */ - /* equal to 1 */ - int count; /* Number of currently slots filled */ - struct s_x3node *tbl; /* The data stored here */ - struct s_x3node **ht; /* Hash table for lookups */ -}; - -/* There is one instance of this structure for every data element -** in an associative array of type "x3". -*/ -typedef struct s_x3node { - struct state *data; /* The data */ - struct config *key; /* The key */ - struct s_x3node *next; /* Next entry with the same hash */ - struct s_x3node **from; /* Previous link */ -} x3node; - -/* There is only one instance of the array, which is the following */ -static struct s_x3 *x3a; - -/* Allocate a new associative array */ -void State_init(void){ - if( x3a ) return; - x3a = (struct s_x3*)lemon_malloc( sizeof(struct s_x3) ); - if( x3a ){ - x3a->size = 128; - x3a->count = 0; - x3a->tbl = (x3node*)lemon_calloc(128, sizeof(x3node) + sizeof(x3node*)); - if( x3a->tbl==0 ){ - lemon_free(x3a); - x3a = 0; - }else{ - int i; - x3a->ht = (x3node**)&(x3a->tbl[128]); - for(i=0; i<128; i++) x3a->ht[i] = 0; - } - } -} -/* Insert a new record into the array. Return TRUE if successful. -** Prior data with the same key is NOT overwritten */ -int State_insert(struct state *data, struct config *key) -{ - x3node *np; - unsigned h; - unsigned ph; - - if( x3a==0 ) return 0; - ph = statehash(key); - h = ph & (x3a->size-1); - np = x3a->ht[h]; - while( np ){ - if( statecmp(np->key,key)==0 ){ - /* An existing entry with the same key is found. */ - /* Fail because overwrite is not allows. */ - return 0; - } - np = np->next; - } - if( x3a->count>=x3a->size ){ - /* Need to make the hash table bigger */ - int i,arrSize; - struct s_x3 array; - array.size = arrSize = x3a->size*2; - array.count = x3a->count; - array.tbl = (x3node*)lemon_calloc(arrSize, sizeof(x3node)+sizeof(x3node*)); - if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ - array.ht = (x3node**)&(array.tbl[arrSize]); - for(i=0; icount; i++){ - x3node *oldnp, *newnp; - oldnp = &(x3a->tbl[i]); - h = statehash(oldnp->key) & (arrSize-1); - newnp = &(array.tbl[i]); - if( array.ht[h] ) array.ht[h]->from = &(newnp->next); - newnp->next = array.ht[h]; - newnp->key = oldnp->key; - newnp->data = oldnp->data; - newnp->from = &(array.ht[h]); - array.ht[h] = newnp; - } - lemon_free(x3a->tbl); - *x3a = array; - } - /* Insert the new data */ - h = ph & (x3a->size-1); - np = &(x3a->tbl[x3a->count++]); - np->key = key; - np->data = data; - if( x3a->ht[h] ) x3a->ht[h]->from = &(np->next); - np->next = x3a->ht[h]; - x3a->ht[h] = np; - np->from = &(x3a->ht[h]); - return 1; -} - -/* Return a pointer to data assigned to the given key. Return NULL -** if no such key. */ -struct state *State_find(struct config *key) -{ - unsigned h; - x3node *np; - - if( x3a==0 ) return 0; - h = statehash(key) & (x3a->size-1); - np = x3a->ht[h]; - while( np ){ - if( statecmp(np->key,key)==0 ) break; - np = np->next; - } - return np ? np->data : 0; -} - -/* Return an array of pointers to all data in the table. -** The array is obtained from malloc. Return NULL if memory allocation -** problems, or if the array is empty. */ -struct state **State_arrayof(void) -{ - struct state **array; - int i,arrSize; - if( x3a==0 ) return 0; - arrSize = x3a->count; - array = (struct state **)lemon_calloc(arrSize, sizeof(struct state *)); - if( array ){ - for(i=0; itbl[i].data; - } - return array; -} - -/* Hash a configuration */ -PRIVATE unsigned confighash(struct config *a) -{ - unsigned h=0; - h = h*571 + a->rp->index*37 + a->dot; - return h; -} - -/* There is one instance of the following structure for each -** associative array of type "x4". -*/ -struct s_x4 { - int size; /* The number of available slots. */ - /* Must be a power of 2 greater than or */ - /* equal to 1 */ - int count; /* Number of currently slots filled */ - struct s_x4node *tbl; /* The data stored here */ - struct s_x4node **ht; /* Hash table for lookups */ -}; - -/* There is one instance of this structure for every data element -** in an associative array of type "x4". -*/ -typedef struct s_x4node { - struct config *data; /* The data */ - struct s_x4node *next; /* Next entry with the same hash */ - struct s_x4node **from; /* Previous link */ -} x4node; - -/* There is only one instance of the array, which is the following */ -static struct s_x4 *x4a; - -/* Allocate a new associative array */ -void Configtable_init(void){ - if( x4a ) return; - x4a = (struct s_x4*)lemon_malloc( sizeof(struct s_x4) ); - if( x4a ){ - x4a->size = 64; - x4a->count = 0; - x4a->tbl = (x4node*)lemon_calloc(64, sizeof(x4node) + sizeof(x4node*)); - if( x4a->tbl==0 ){ - lemon_free(x4a); - x4a = 0; - }else{ - int i; - x4a->ht = (x4node**)&(x4a->tbl[64]); - for(i=0; i<64; i++) x4a->ht[i] = 0; - } - } -} -/* Insert a new record into the array. Return TRUE if successful. -** Prior data with the same key is NOT overwritten */ -int Configtable_insert(struct config *data) -{ - x4node *np; - unsigned h; - unsigned ph; - - if( x4a==0 ) return 0; - ph = confighash(data); - h = ph & (x4a->size-1); - np = x4a->ht[h]; - while( np ){ - if( Configcmp((const char *) np->data,(const char *) data)==0 ){ - /* An existing entry with the same key is found. */ - /* Fail because overwrite is not allows. */ - return 0; - } - np = np->next; - } - if( x4a->count>=x4a->size ){ - /* Need to make the hash table bigger */ - int i,arrSize; - struct s_x4 array; - array.size = arrSize = x4a->size*2; - array.count = x4a->count; - array.tbl = (x4node*)lemon_calloc(arrSize, - sizeof(x4node) + sizeof(x4node*)); - if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ - array.ht = (x4node**)&(array.tbl[arrSize]); - for(i=0; icount; i++){ - x4node *oldnp, *newnp; - oldnp = &(x4a->tbl[i]); - h = confighash(oldnp->data) & (arrSize-1); - newnp = &(array.tbl[i]); - if( array.ht[h] ) array.ht[h]->from = &(newnp->next); - newnp->next = array.ht[h]; - newnp->data = oldnp->data; - newnp->from = &(array.ht[h]); - array.ht[h] = newnp; - } - *x4a = array; - } - /* Insert the new data */ - h = ph & (x4a->size-1); - np = &(x4a->tbl[x4a->count++]); - np->data = data; - if( x4a->ht[h] ) x4a->ht[h]->from = &(np->next); - np->next = x4a->ht[h]; - x4a->ht[h] = np; - np->from = &(x4a->ht[h]); - return 1; -} - -/* Return a pointer to data assigned to the given key. Return NULL -** if no such key. */ -struct config *Configtable_find(struct config *key) -{ - int h; - x4node *np; - - if( x4a==0 ) return 0; - h = confighash(key) & (x4a->size-1); - np = x4a->ht[h]; - while( np ){ - if( Configcmp((const char *) np->data,(const char *) key)==0 ) break; - np = np->next; - } - return np ? np->data : 0; -} - -/* Remove all data from the table. Pass each data to the function "f" -** as it is removed. ("f" may be null to avoid this step.) */ -void Configtable_clear(int(*f)(struct config *)) -{ - int i; - if( x4a==0 || x4a->count==0 ) return; - if( f ) for(i=0; icount; i++) (*f)(x4a->tbl[i].data); - for(i=0; isize; i++) x4a->ht[i] = 0; - x4a->count = 0; - return; -} diff --git a/vendored/sqlite3-parser/third_party/lemon/lempar.rs b/vendored/sqlite3-parser/third_party/lemon/lempar.rs deleted file mode 100644 index 9d49af888..000000000 --- a/vendored/sqlite3-parser/third_party/lemon/lempar.rs +++ /dev/null @@ -1,919 +0,0 @@ -/* -** 2000-05-29 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Driver template for the LEMON parser generator. -** -** The "lemon" program processes an LALR(1) input grammar file, then uses -** this template to construct a parser. The "lemon" program inserts text -** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the -** interstitial "-" characters) contained in this template is changed into -** the value of the %name directive from the grammar. Otherwise, the content -** of this template is copied straight through into the generate parser -** source file. -** -** The following is the concatenation of all %include directives from the -** input grammar file: -*/ -/************ Begin %include sections from the grammar ************************/ -%% -/**************** End of %include directives **********************************/ -/* These constants specify the various numeric values for terminal symbols. -***************** Begin token definitions *************************************/ -%% -/**************** End token definitions ***************************************/ - -/* The next sections is a series of control #defines. -** various aspects of the generated parser. -** YYCODETYPE is the data type used to store the integer codes -** that represent terminal and non-terminal symbols. -** "unsigned char" is used if there are fewer than -** 256 symbols. Larger types otherwise. -** YYNOCODE is a number of type YYCODETYPE that is not used for -** any terminal or nonterminal symbol. -** YYFALLBACK If defined, this indicates that one or more tokens -** (also known as: "terminal symbols") have fall-back -** values which should be used if the original symbol -** would not parse. This permits keywords to sometimes -** be used as identifiers, for example. -** YYACTIONTYPE is the data type used for "action codes" - numbers -** that indicate what to do in response to the next -** token. -** ParseTOKENTYPE is the data type used for minor type for terminal -** symbols. Background: A "minor type" is a semantic -** value associated with a terminal or non-terminal -** symbols. For example, for an "ID" terminal symbol, -** the minor type might be the name of the identifier. -** Each non-terminal can have a different minor type. -** Terminal symbols all have the same minor type, though. -** This macros defines the minor type for terminal -** symbols. -** YYMINORTYPE is the data type used for all minor types. -** This is typically a union of many types, one of -** which is ParseTOKENTYPE. The entry in the union -** for terminal symbols is called "yy0". -** YYSTACKDEPTH is the maximum depth of the parser's stack. If -** zero the stack is dynamically sized using realloc() -** YYERRORSYMBOL is the code number of the error symbol. If not -** defined, then do no error processing. -** YYNSTATE the combined number of states. -** YYNRULE the number of rules in the grammar -** YYNTOKEN Number of terminal symbols -** YY_MAX_SHIFT Maximum value for shift actions -** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions -** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions -** YY_ERROR_ACTION The yy_action[] code for syntax error -** YY_ACCEPT_ACTION The yy_action[] code for accept -** YY_NO_ACTION The yy_action[] code for no-op -** YY_MIN_REDUCE Minimum value for reduce actions -** YY_MAX_REDUCE Maximum value for reduce actions -*/ -/************* Begin control #defines *****************************************/ -%% -/************* End control #defines *******************************************/ - -/* Next are the tables used to determine what action to take based on the -** current state and lookahead token. These tables are used to implement -** functions that take a state number and lookahead value and return an -** action integer. -** -** Suppose the action integer is N. Then the action is determined as -** follows -** -** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead -** token onto the stack and goto state N. -** -** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then -** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE. -** -** N == YY_ERROR_ACTION A syntax error has occurred. -** -** N == YY_ACCEPT_ACTION The parser accepts its input. -** -** N == YY_NO_ACTION No such action. Denotes unused -** slots in the yy_action[] table. -** -** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE -** and YY_MAX_REDUCE -** -** The action table is constructed as a single large table named yy_action[]. -** Given state S and lookahead X, the action is computed as either: -** -** (A) N = yy_action[ yy_shift_ofst[S] + X ] -** (B) N = yy_default[S] -** -** The (A) formula is preferred. The B formula is used instead if -** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X. -** -** The formulas above are for computing the action when the lookahead is -** a terminal symbol. If the lookahead is a non-terminal (as occurs after -** a reduce action) then the yy_reduce_ofst[] array is used in place of -** the yy_shift_ofst[] array. -** -** The following are the tables generated in this section: -** -** yy_action[] A single table containing all actions. -** yy_lookahead[] A table containing the lookahead for each entry in -** yy_action. Used to detect hash collisions. -** yy_shift_ofst[] For each state, the offset into yy_action for -** shifting terminals. -** yy_reduce_ofst[] For each state, the offset into yy_action for -** shifting non-terminals after a reduce. -** yy_default[] Default action for each state. -** -*********** Begin parsing tables **********************************************/ -%% -/********** End of lemon-generated parsing tables *****************************/ - -/* The next table maps tokens (terminal symbols) into fallback tokens. -** If a construct like the following: -** -** %fallback ID X Y Z. -** -** appears in the grammar, then ID becomes a fallback token for X, Y, -** and Z. Whenever one of the tokens X, Y, or Z is input to the parser -** but it does not parse, the type of the token is changed to ID and -** the parse is retried before an error is thrown. -** -** This feature can be used, for example, to cause some keywords in a language -** to revert to identifiers if they keyword does not apply in the context where -** it appears. -*/ -%% - -/* The following structure represents a single element of the -** parser's stack. Information stored includes: -** -** + The state number for the parser at this level of the stack. -** -** + The value of the token stored at this level of the stack. -** (In other words, the "major" token.) -** -** + The semantic value stored at this level of the stack. This is -** the information used by the action routines in the grammar. -** It is sometimes called the "minor" token. -** -** After the "shift" half of a SHIFTREDUCE action, the stateno field -** actually contains the reduce action for the second half of the -** SHIFTREDUCE. -*/ -#[expect(non_camel_case_types)] -#[derive(Default)] -pub struct yyStackEntry<'i> { - stateno: YYACTIONTYPE, /* The state-number, or reduce action in SHIFTREDUCE */ - major: YYCODETYPE, /* The major token value. This is the code - ** number for the token at this stack level */ - minor: YYMINORTYPE<'i>, /* The user-supplied minor token value. This - ** is the value of the token */ -} - -/* The state of the parser is completely contained in an instance of -** the following structure */ -#[expect(non_camel_case_types)] -pub struct yyParser<'input> { - yyidx: usize, /* Index to top element of the stack */ - #[cfg(feature = "YYTRACKMAXSTACKDEPTH")] - yyhwm: usize, /* High-water mark of the stack */ - //#[cfg(not(feature = "YYNOERRORRECOVERY"))] - yyerrcnt: i32, /* Shifts left before out of the error */ -%% /* A place to hold %extra_context */ - yystack: smallvec::SmallVec<[yyStackEntry<'input>; YYSTACKDEPTH]>, /* The parser's stack */ -} - -use std::cmp::Ordering; -use std::ops::Neg; -impl<'input> yyParser<'input> { - #[inline] - fn shift(&self, shift: i8) -> usize { - assert!(shift <= 1); - match shift.cmp(&0) { - Ordering::Equal => self.yyidx, - Ordering::Greater => self.yyidx + shift as usize, - Ordering::Less => self.yyidx.checked_sub(shift.neg() as usize).unwrap(), - } - } - - #[inline] - fn yyidx_shift(&mut self, shift: i8) { - match shift.cmp(&0) { - Ordering::Greater => self.yyidx += shift as usize, - Ordering::Less => self.yyidx = self.yyidx.checked_sub(shift.neg() as usize).unwrap(), - Ordering::Equal => {} - } - } - - #[inline] - fn yy_move(&mut self, shift: i8) -> yyStackEntry<'input> { - let idx = self.shift(shift); - - // TODO: The compiler optimizes `std::mem::take` to two `memcpy` - // but `yyStackEntry` requires 208 bytes, so it is not worth it (maybe). - #[cfg(not(target_family = "wasm"))] - assert_eq!(std::mem::size_of::(), 208); - - std::mem::take(&mut self.yystack[idx]) - } - - #[inline] - fn push(&mut self, entry: yyStackEntry<'input>) { - if self.yyidx == self.yystack.len() { - self.yystack.push(entry); - } else { - self.yystack[self.yyidx] = entry; - } - } -} - -use std::ops::{Index, IndexMut}; -impl<'input> Index for yyParser<'input> { - type Output = yyStackEntry<'input>; - - #[inline] - fn index(&self, shift: i8) -> &yyStackEntry<'input> { - let idx = self.shift(shift); - &self.yystack[idx] - } -} -impl<'input> IndexMut for yyParser<'input> { - #[inline] - fn index_mut(&mut self, shift: i8) -> &mut yyStackEntry<'input> { - let idx = self.shift(shift); - &mut self.yystack[idx] - } -} - -#[cfg(not(feature = "NDEBUG"))] -use log::{debug, log_enabled, Level::Debug}; -static TARGET: &str = "Parse"; - -/* For tracing shifts, the names of all terminals and nonterminals -** are required. The following table supplies these names */ -#[cfg(any(feature = "YYCOVERAGE", not(feature = "NDEBUG")))] -%% - -/* For tracing reduce actions, the names of all rules are required. -*/ -#[cfg(not(feature = "NDEBUG"))] -#[rustfmt::skip] -#[expect(non_upper_case_globals)] -static yyRuleName: [&str; YYNRULE] = [ -%% -]; - -/* -** Try to increase the size of the parser stack. Return the number -** of errors. Return 0 on success. -*/ -impl yyParser<'_> { - #[inline] - fn yy_grow_stack_if_needed(&mut self) -> bool { - false - } - #[inline] - fn yy_grow_stack_for_push(&mut self) -> bool { - // yystack is not prefilled with zero value like in C. - if self.yyidx == self.yystack.len() { - self.yystack.push(yyStackEntry::default()); - } else if self.yyidx + 1 == self.yystack.len() { - self.yystack.push(yyStackEntry::default()); - } - false - } -} - -/* Initialize a new parser. -*/ -impl yyParser<'_> { - pub fn new( -%% /* Optional %extra_context parameter */ - ) -> yyParser { - yyParser { - yyidx: 0, - #[cfg(feature = "YYTRACKMAXSTACKDEPTH")] - yyhwm: 0, - yystack: smallvec::smallvec![yyStackEntry::default()], - //#[cfg(not(feature = "YYNOERRORRECOVERY"))] - yyerrcnt: -1, -%% /* Optional %extra_context store */ - } - } -} - -/* -** Pop the parser's stack once. -*/ -impl yyParser<'_> { - #[inline] - fn yy_pop_parser_stack(&mut self) { - use std::mem::take; - let _yytos = take(&mut self.yystack[self.yyidx]); - self.yyidx = self.yyidx.checked_sub(1).unwrap(); - //assert_eq!(self.yyidx+1, self.yystack.len()); - #[cfg(not(feature = "NDEBUG"))] - { - debug!( - target: TARGET, - "Popping {}", yyTokenName[_yytos.major as usize] - ); - } - } -} - -/* -** Clear all secondary memory allocations from the parser -*/ -impl yyParser<'_> { - #[expect(non_snake_case)] - #[inline] - pub fn ParseFinalize(&mut self) { - while self.yyidx > 0 { - self.yy_pop_parser_stack(); - } - // TODO check all elements remaining in yystack are yyinit() - } -} - -/* -** Return the peak depth of the stack for a parser. -*/ -#[cfg(feature = "YYTRACKMAXSTACKDEPTH")] -impl yyParser<'_> { - #[expect(non_snake_case)] - #[inline] - pub fn ParseStackPeak(&self) -> usize { - self.yyhwm - } - #[inline] - fn yyhwm_incr(&mut self) { - if self.yyidx > self.yyhwm { - self.yyhwm += 1; - assert_eq!(self.yyhwm, self.yyidx); - } - } -} -#[cfg(not(feature = "YYTRACKMAXSTACKDEPTH"))] -impl yyParser<'_> { - #[inline] - fn yyhwm_incr(&mut self) {} -} - -/* This array of booleans keeps track of the parser statement -** coverage. The element yycoverage[X][Y] is set when the parser -** is in state X and has a lookahead token Y. In a well-tested -** systems, every element of this matrix should end up being set. -*/ -// #[cfg(feature = "YYCOVERAGE")] -// static yycoverage: [[bool; YYNTOKEN]; YYNSTATE] = []; - -/* -** Write into out a description of every state/lookahead combination that -** -** (1) has not been used by the parser, and -** (2) is not a syntax error. -** -** Return the number of missed state/lookahead combinations. -*/ -#[cfg(feature = "YYCOVERAGE")] -fn ParseCoverage(/*FILE *out*/) -> i32 { - //int stateno, iLookAhead, i; - let nMissed = 0; - /*for(stateno=0; stateno YYACTIONTYPE { - if stateno > YY_MAX_SHIFT { - return stateno; - } - assert!(stateno <= YY_SHIFT_COUNT); - #[cfg(feature = "YYCOVERAGE")] - { - //yycoverage[stateno][iLookAhead] = true; - } - loop { - let mut i = yy_shift_ofst[stateno as usize] as usize; - assert!(i <= YY_ACTTAB_COUNT!()); - assert!(i + usize::from(YYNTOKEN) <= yy_lookahead.len()); - assert_ne!(iLookAhead, YYNOCODE); - assert!((iLookAhead as YYACTIONTYPE) < YYNTOKEN); - i += iLookAhead as usize; - if yy_lookahead[i] != iLookAhead { - if YYFALLBACK { - let iFallback = yyFallback[iLookAhead as usize]; /* Fallback token */ - if iFallback != 0 { - #[cfg(not(feature = "NDEBUG"))] - { - debug!( - target: TARGET, - "FALLBACK {} => {}", - yyTokenName[iLookAhead as usize], - yyTokenName[iFallback as usize] - ); - } - assert_eq!(yyFallback[iFallback as usize], 0); /* Fallback loop must terminate */ - iLookAhead = iFallback; - continue; - } - } - if YYWILDCARD > 0 { - let j = i - iLookAhead as usize + YYWILDCARD as usize; - if yy_lookahead[j] == YYWILDCARD && iLookAhead > 0 { - #[cfg(not(feature = "NDEBUG"))] - { - debug!( - target: TARGET, - "WILDCARD {} => {}", - yyTokenName[iLookAhead as usize], - yyTokenName[YYWILDCARD as usize] - ); - } - return yy_action[j]; - } - } /* YYWILDCARD */ - return yy_default[stateno as usize]; - } else { - return yy_action[i]; - } - } -} - -/* -** Find the appropriate action for a parser given the non-terminal -** look-ahead token iLookAhead. -*/ -#[expect(non_snake_case)] -fn yy_find_reduce_action( - stateno: YYACTIONTYPE, /* Current state number */ - iLookAhead: YYCODETYPE, /* The look-ahead token */ -) -> YYACTIONTYPE { - if YYERRORSYMBOL > 0 { - if stateno > YY_REDUCE_COUNT { - return yy_default[stateno as usize]; - } - } else { - assert!(stateno <= YY_REDUCE_COUNT); - } - let mut i: i32 = yy_reduce_ofst[stateno as usize].into(); - assert_ne!(iLookAhead, YYNOCODE); - i += i32::from(iLookAhead); - if YYERRORSYMBOL > 0 { - if !(0..YY_ACTTAB_COUNT!()).contains(&i) || yy_lookahead[i as usize] != iLookAhead { - return yy_default[stateno as usize]; - } - } else { - assert!((0..YY_ACTTAB_COUNT!()).contains(&i)); - assert_eq!(yy_lookahead[i as usize], iLookAhead); - } - yy_action[i as usize] -} - - /******** Begin %stack_overflow code ****************************************** -%% - ******** End %stack_overflow code ********************************************/ - -/* -** Print tracing information for a SHIFT action -*/ -impl yyParser<'_> { - #[expect(non_snake_case)] - #[cfg(feature = "NDEBUG")] - #[inline] - fn yyTraceShift(&self, _: YYACTIONTYPE, _: &str) { - } - #[expect(non_snake_case)] - #[cfg(not(feature = "NDEBUG"))] - fn yyTraceShift(&self, yyNewState: YYACTIONTYPE, zTag: &str) { - let yytos = &self[0]; - if yyNewState < YYNSTATE { - debug!( - target: TARGET, - "{} '{}', go to state {}", zTag, yyTokenName[yytos.major as usize], yyNewState - ); - } else { - debug!( - target: TARGET, - "{} '{}', pending reduce {:?}", - zTag, - yyTokenName[yytos.major as usize], - yyNewState.checked_sub(YY_MIN_REDUCE) - ); - } - } -} - -/* -** Perform a shift action. -*/ -impl<'input> yyParser<'input> { - #[expect(non_snake_case)] - fn yy_shift( - &mut self, - mut yyNewState: YYACTIONTYPE, /* The new state to shift in */ - yyMajor: YYCODETYPE, /* The major token to shift in */ - yyMinor: ParseTOKENTYPE<'input>, /* The minor token to shift in */ - ) { - self.yyidx_shift(1); - self.yyhwm_incr(); - if self.yy_grow_stack_if_needed() { - return; - } - if yyNewState > YY_MAX_SHIFT { - yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; - } - let yytos = yyStackEntry { - stateno: yyNewState, - major: yyMajor, - minor: YYMINORTYPE::yy0(yyMinor), - }; - self.push(yytos); - self.yyTraceShift(yyNewState, "Shift"); - } -} - -/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side -** of that rule */ -#[expect(non_upper_case_globals)] -static yyRuleInfoLhs: [YYCODETYPE; YYNRULE] = [ -%% -]; - -/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number -** of symbols on the right-hand side of that rule. */ -#[expect(non_upper_case_globals)] -static yyRuleInfoNRhs: [i8; YYNRULE] = [ -%% -]; - -/* -** Perform a reduce action and the shift that must immediately -** follow the reduce. -** -** The yyLookahead and yyLookaheadToken parameters provide reduce actions -** access to the lookahead token (if any). The yyLookahead will be YYNOCODE -** if the lookahead token has already been consumed. As this procedure is -** only called from one place, optimizing compilers will in-line it, which -** means that the extra parameters have no performance impact. -*/ -impl yyParser<'_> { - fn yy_reduce( - &mut self, - yyruleno: YYACTIONTYPE, /* Number of the rule by which to reduce */ - yy_look_ahead: YYCODETYPE, /* Lookahead token, or YYNOCODE if none */ - yy_lookahead_token: &ParseTOKENTYPE, /* Value of the lookahead token */ - ) -> Result { - let _ = yy_look_ahead; - let _ = yy_lookahead_token; - - let yylhsminor: YYMINORTYPE<'_>; - match yyruleno { - /* Beginning here are the reduction cases. A typical example - ** follows: - ** case 0: - ** #line - ** { ... } // User supplied code - ** #line - ** break; - */ -/********** Begin reduce actions **********************************************/ -%% -/********** End reduce actions ************************************************/ - }; - let yygoto: YYCODETYPE = yyRuleInfoLhs[yyruleno as usize]; /* The next state */ - let yysize: i8 = yyRuleInfoNRhs[yyruleno as usize]; /* Amount to pop the stack */ - let yyact: YYACTIONTYPE = yy_find_reduce_action(self[yysize].stateno, yygoto); /* The next action */ - - /* There are no SHIFTREDUCE actions on nonterminals because the table - ** generator has simplified them to pure REDUCE actions. */ - assert!(!(yyact > YY_MAX_SHIFT && yyact <= YY_MAX_SHIFTREDUCE)); - - /* It is not possible for a REDUCE to be followed by an error */ - assert_ne!(yyact, YY_ERROR_ACTION); - - self.yyidx_shift(yysize + 1); - { - let yymsp = &mut self[0]; - yymsp.stateno = yyact; - yymsp.major = yygoto; - } - self.yyTraceShift(yyact, "... then shift"); - Ok(yyact) - } -} - -/* -** The following code executes when the parse fails -*/ -impl yyParser<'_> { - #[cfg(not(feature = "YYNOERRORRECOVERY"))] - fn yy_parse_failed(&mut self) { - #[cfg(not(feature = "NDEBUG"))] - { - error!(target: TARGET, "Fail!"); - } - while self.yyidx > 0 { - self.yy_pop_parser_stack(); - } - /* Here code is inserted which will be executed whenever the - ** parser fails */ - /************ Begin %parse_failure code ***************************************/ -%% - /************ End %parse_failure code *****************************************/ - } - #[cfg(feature = "YYNOERRORRECOVERY")] - fn yy_parse_failed(&mut self) {} -} - -/* -** The following code executes when a syntax error first occurs. -*/ -impl yyParser<'_> { - fn yy_syntax_error( - &mut self, - yymajor: YYCODETYPE, /* The major type of the error token */ - yyminor: &ParseTOKENTYPE, /* The minor type of the error token */ - ) { - /************ Begin %syntax_error code ****************************************/ -%% - /************ End %syntax_error code ******************************************/ - } -} - -/* -** The following is executed when the parser accepts -*/ -impl yyParser<'_> { - fn yy_accept(&mut self) { - #[cfg(not(feature = "NDEBUG"))] - { - debug!(target: TARGET, "Accept!"); - } - if cfg!(not(feature = "YYNOERRORRECOVERY")) { - self.yyerrcnt = -1; - } - assert_eq!(self.yyidx, 0); - /* Here code is inserted which will be executed whenever the - ** parser accepts */ - /*********** Begin %parse_accept code *****************************************/ -%% - /*********** End %parse_accept code *******************************************/ - } -} - -/* The main parser program. -** The first argument is a pointer to a structure obtained from -** "ParseAlloc" which describes the current state of the parser. -** The second argument is the major token number. The third is -** the minor token. The fourth optional argument is whatever the -** user wants (and specified in the grammar) and is available for -** use by the action routines. -** -** Inputs: -**
    -**
  • A pointer to the parser (an opaque structure.) -**
  • The major token number. -**
  • The minor token number. -**
  • An option argument of a grammar-specified type. -**
-** -** Outputs: -** None. -*/ -impl<'input> yyParser<'input> { - #[expect(non_snake_case)] - pub fn Parse( - &mut self, - yymajor: TokenType, /* The major token code number */ - yyminor: ParseTOKENTYPE<'input>, /* The value for the token */ - ) -> Result<(), ParseError> { - let mut yymajor = yymajor as YYCODETYPE; - //#[cfg(all(not(feature = "YYERRORSYMBOL"), not(feature = "YYNOERRORRECOVERY")))] - let mut yyendofinput: bool = false; /* True if we are at the end of input */ - //#[cfg(feature = "YYERRORSYMBOL")] - let mut yyerrorhit: bool = false; /* True if yymajor has invoked an error */ - - //assert_ne!( self[0], null ); - if YYERRORSYMBOL == 0 && cfg!(not(feature = "YYNOERRORRECOVERY")) { - yyendofinput = yymajor == 0; - } - - let mut yyact: YYACTIONTYPE = self[0].stateno; /* The parser action. */ - #[cfg(not(feature = "NDEBUG"))] - { - if yyact < YY_MIN_REDUCE { - debug!( - target: TARGET, - "Input '{}' in state {}", yyTokenName[yymajor as usize], yyact - ); - } else { - debug!( - target: TARGET, - "Input '{}' with pending reduce {}", - yyTokenName[yymajor as usize], - yyact - YY_MIN_REDUCE - ); - } - } - - loop { - assert_eq!(yyact, self[0].stateno); - yyact = yy_find_shift_action(yymajor, yyact); - if yyact >= YY_MIN_REDUCE { - let yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */ - #[cfg(not(feature = "NDEBUG"))] - { - assert!((yyruleno as usize) < yyRuleName.len()); - let yysize = yyRuleInfoNRhs[yyruleno as usize]; - let action = if yyruleno < YYNRULE_WITH_ACTION { - "" - } else { - " without external action" - }; - if yysize != 0 { - debug!( - target: TARGET, - "Reduce {} [{}]{}, pop back to state {}.", - yyruleno, - yyRuleName[yyruleno as usize], - action, - self[yysize].stateno - ); - } else { - debug!( - target: TARGET, - "Reduce {} [{}]{}.", yyruleno, yyRuleName[yyruleno as usize], action - ); - } - } - /* Check that the stack is large enough to grow by a single entry - ** if the RHS of the rule is empty. This ensures that there is room - ** enough on the stack to push the LHS value */ - if yyRuleInfoNRhs[yyruleno as usize] == 0 { - self.yyhwm_incr(); - if self.yy_grow_stack_for_push() { - break; - } - } - yyact = self.yy_reduce(yyruleno, yymajor, &yyminor)?; - } else if yyact <= YY_MAX_SHIFTREDUCE { - self.yy_shift(yyact, yymajor, yyminor); - if cfg!(not(feature = "YYNOERRORRECOVERY")) { - self.yyerrcnt -= 1; - } - break; - } else if yyact == YY_ACCEPT_ACTION { - self.yyidx_shift(-1); - self.yy_accept(); - return Ok(()); - } else { - assert_eq!(yyact, YY_ERROR_ACTION); - #[cfg(not(feature = "NDEBUG"))] - { - debug!(target: TARGET, "Syntax Error!"); - } - if YYERRORSYMBOL > 0 { - /* A syntax error has occurred. - ** The response to an error depends upon whether or not the - ** grammar defines an error token "ERROR". - ** - ** This is what we do if the grammar does define ERROR: - ** - ** * Call the %syntax_error function. - ** - ** * Begin popping the stack until we enter a state where - ** it is legal to shift the error symbol, then shift - ** the error symbol. - ** - ** * Set the error count to three. - ** - ** * Begin accepting and shifting new tokens. No new error - ** processing will occur until three tokens have been - ** shifted successfully. - ** - */ - if self.yyerrcnt < 0 { - self.yy_syntax_error(yymajor, &yyminor); - } - let yymx = self[0].major; - if yymx == YYERRORSYMBOL || yyerrorhit { - #[cfg(not(feature = "NDEBUG"))] - { - debug!( - target: TARGET, - "Discard input token {}", yyTokenName[yymajor as usize] - ); - } - yymajor = YYNOCODE; - } else { - while self.yyidx > 0 { - yyact = yy_find_reduce_action(self[0].stateno, YYERRORSYMBOL); - if yyact <= YY_MAX_SHIFTREDUCE { - break; - } - self.yy_pop_parser_stack(); - } - if self.yyidx <= 0 || yymajor == 0 { - self.yy_parse_failed(); - if cfg!(not(feature = "YYNOERRORRECOVERY")) { - self.yyerrcnt = -1; - } - yymajor = YYNOCODE; - } else if yymx != YYERRORSYMBOL { - self.yy_shift(yyact, YYERRORSYMBOL, yyminor); - } - } - self.yyerrcnt = 3; - yyerrorhit = true; - if yymajor == YYNOCODE { - break; - } - yyact = self[0].stateno; - } else if cfg!(feature = "YYNOERRORRECOVERY") { - /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to - ** do any kind of error recovery. Instead, simply invoke the syntax - ** error routine and continue going as if nothing had happened. - ** - ** Applications can set this macro (for example inside %include) if - ** they intend to abandon the parse upon the first syntax error seen. - */ - self.yy_syntax_error(yymajor, &yyminor); - break; - } else { - /* YYERRORSYMBOL is not defined */ - /* This is what we do if the grammar does not define ERROR: - ** - ** * Report an error message, and throw away the input token. - ** - ** * If the input token is $, then fail the parse. - ** - ** As before, subsequent error messages are suppressed until - ** three input tokens have been successfully shifted. - */ - if self.yyerrcnt <= 0 { - self.yy_syntax_error(yymajor, &yyminor); - } - self.yyerrcnt = 3; - if yyendofinput { - self.yy_parse_failed(); - if cfg!(not(feature = "YYNOERRORRECOVERY")) { - self.yyerrcnt = -1; - } - } - break; - } - } - if self.yyidx <= 0 { - break; - } - } - #[cfg(not(feature = "NDEBUG"))] - { - if log_enabled!(target: TARGET, Debug) { - let msg = self.yystack[1..=self.yyidx] - .iter() - .map(|entry| yyTokenName[entry.major as usize]) - .collect::>() - .join(" "); - debug!(target: TARGET, "Return. Stack=[{}]", msg); - } - } - return Ok(()); - } - - /* - ** Return the fallback token corresponding to canonical token iToken, or - ** 0 if iToken has no fallback. - */ - #[inline] - pub fn parse_fallback(i_token: YYCODETYPE) -> YYCODETYPE { - if YYFALLBACK { - return yyFallback[i_token as usize]; - } - 0 - } -}