Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions build_and_test.py
Comment thread
DerekStride marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Build script: regenerate parser, reinstall package, run integration test."""
import subprocess, sys, os, pathlib, time, shutil

tree_dir = pathlib.Path(r'e:\temp\tree-sitter-sql')
ts_bin = tree_dir / 'tree-sitter.exe'
node_dir = r'e:\temp\node\node-v22.15.0-win-x64'
pyd_file = tree_dir / 'bindings' / 'python' / 'tree_sitter_sql' / '_binding.pyd'

env = os.environ.copy()
env['PATH'] = node_dir + ';' + env.get('PATH', '')

# Step 1: generate
print('1. Running tree-sitter generate ...')
r = subprocess.run([str(ts_bin), 'generate'], cwd=str(tree_dir), env=env, capture_output=True, text=True)
if r.returncode != 0:
print('FAIL:', r.stderr)
sys.exit(1)
print(' OK:', r.stderr.strip() or '(no warnings)')
print(' parser.c:', (tree_dir / 'src' / 'parser.c').stat().st_mtime)

# Step 2: delete old .pyd
if pyd_file.exists():
pyd_file.unlink()
print('2. Deleted old _binding.pyd')

# Step 3: reinstall
print('3. pip install -e ...')
r = subprocess.run([sys.executable, '-m', 'pip', 'install', '-e', '.'],
cwd=str(tree_dir), capture_output=True, text=True)
if r.returncode != 0:
print('FAIL:', r.stderr[-500:])
sys.exit(1)
print(' OK:', r.stdout.strip().split('\n')[-1])
print(' _binding.pyd:', pyd_file.stat().st_mtime)

# Step 4: run test
print('4. Running test_speedy.py ...')
r = subprocess.run([sys.executable, str(tree_dir / 'test_speedy.py')], capture_output=True, text=True)
print(r.stdout)
if r.stderr:
print('STDERR:', r.stderr[:500])
sys.exit(r.returncode)
19 changes: 13 additions & 6 deletions grammar.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,21 @@ export default grammar({
rules: {
program: $ => seq(
// any number of transactions, statements, or blocks with a terminating ;
// set_term and declare_external_function are self-terminating (Firebird dialect)
// '^' is a valid statement terminator in Firebird (after SET TERM ^ ;)
repeat(
seq(
choice(
$.transaction,
$.statement,
$.block,
choice(
$.set_term,
$.declare_external_function,
$.fb_proc_or_trigger,
seq(
choice(
$.transaction,
$.statement,
$.block,
),
choice(';', '^'),
),
';',
),
),
// optionally, a single statement without a terminating ;
Expand Down
2 changes: 1 addition & 1 deletion grammar/expressions.js
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ export default {
['*', 'binary_times'],
['/', 'binary_times'],
['%', 'binary_times'],
['^', 'binary_exp'],
// '^' removed: Firebird uses ^ as statement terminator (SET TERM), not as power/XOR
Comment thread
DerekStride marked this conversation as resolved.
Outdated
['=', 'binary_relation'],
['<', 'binary_relation'],
['<=', 'binary_relation'],
Expand Down
14 changes: 14 additions & 0 deletions grammar/keywords.js
Original file line number Diff line number Diff line change
Expand Up @@ -426,4 +426,18 @@ export default {

keyword_array: _ => make_keyword("array"), // not included in _type since it's a constructor literal

// Firebird dialect keywords
keyword_term: _ => make_keyword("term"),
keyword_domain: _ => make_keyword("domain"),
keyword_exception: _ => make_keyword("exception"),
keyword_generator: _ => make_keyword("generator"),
keyword_active: _ => make_keyword("active"),
keyword_inactive: _ => make_keyword("inactive"),
keyword_position: _ => make_keyword("position"),
keyword_suspend: _ => make_keyword("suspend"),
keyword_exit: _ => make_keyword("exit"),
keyword_variable: _ => make_keyword("variable"),
keyword_entry_point: _ => make_keyword("entry_point"),
keyword_module_name: _ => make_keyword("module_name"),

}
2 changes: 2 additions & 0 deletions grammar/statements/create-function.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ export default {

_tsql_function_body_statement: $ => seq(

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered implementing a separate _fb_function_body_statement & branching further up the call stack?

optional($.keyword_as),
// Firebird: DECLARE VARIABLE name type; appears before BEGIN
repeat($.fb_var_declaration),
$.keyword_begin,
optional($.var_declarations),
choice(
Expand Down
6 changes: 5 additions & 1 deletion grammar/statements/create-procedure.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export default {
optional($._if_not_exists),
$.object_reference,
optional($.function_arguments),
// Firebird output parameters
optional(seq($.keyword_returns, $.function_arguments)),
repeat(
choice(
$.function_language,
Expand Down Expand Up @@ -81,12 +83,14 @@ export default {
optional(';'),
alias($._dollar_quoted_string_end_tag, $.dollar_quote),
),
// T-SQL style (no required RETURN)
// T-SQL / Firebird style (no required RETURN): AS BEGIN ... END
$._tsql_procedure_body_statement,
),

_tsql_procedure_body_statement: $ => seq(
optional($.keyword_as),
// Firebird: DECLARE VARIABLE name type; appears before BEGIN
repeat($.fb_var_declaration),
$.keyword_begin,
optional($.var_declarations),
choice(
Expand Down
105 changes: 78 additions & 27 deletions grammar/statements/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export default {
$.create_extension,
$.create_trigger,
$.create_policy,
// Firebird dialect
$.create_domain,
$.create_exception,
$.create_generator,
prec.left(seq(
$.create_schema,
repeat($._create_statement),
Expand Down Expand Up @@ -484,38 +488,54 @@ export default {
optional($._if_not_exists),
$.object_reference,
choice(
$.keyword_before,
$.keyword_after,
seq($.keyword_instead, $.keyword_of),
),
$._create_trigger_event,
repeat(seq($.keyword_or, $._create_trigger_event)),
$.keyword_on,
$.object_reference,
repeat(
choice(
seq($.keyword_from, $.object_reference),
// Firebird: FOR tablename ACTIVE BEFORE/AFTER event [POSITION n] AS
seq(
$.keyword_for,
$.object_reference,
optional(choice($.keyword_active, $.keyword_inactive)),
choice($.keyword_before, $.keyword_after),
$._create_trigger_event,
repeat(seq($.keyword_or, $._create_trigger_event)),
optional(seq($.keyword_position, $._integer)),
$.keyword_as,
),
// Standard SQL / PostgreSQL / MySQL / SQLite
seq(
choice(
seq($.keyword_not, $.keyword_deferrable),
$.keyword_deferrable,
seq($.keyword_initially, $.keyword_immediate),
seq($.keyword_initially, $.keyword_deferred),
$.keyword_before,
$.keyword_after,
seq($.keyword_instead, $.keyword_of),
),
seq($.keyword_referencing, choice($.keyword_old, $.keyword_new), $.keyword_table, optional($.keyword_as), $.identifier),
seq(
$.keyword_for,
optional($.keyword_each),
choice($.keyword_row, $.keyword_statement),
// mariadb
optional(seq(choice($.keyword_follows, $.keyword_precedes), $.identifier)),
$._create_trigger_event,
repeat(seq($.keyword_or, $._create_trigger_event)),
$.keyword_on,
$.object_reference,
repeat(
choice(
seq($.keyword_from, $.object_reference),
choice(
seq($.keyword_not, $.keyword_deferrable),
$.keyword_deferrable,
seq($.keyword_initially, $.keyword_immediate),
seq($.keyword_initially, $.keyword_deferred),
),
seq($.keyword_referencing, choice($.keyword_old, $.keyword_new), $.keyword_table, optional($.keyword_as), $.identifier),
seq(
$.keyword_for,
optional($.keyword_each),
choice($.keyword_row, $.keyword_statement),
// mariadb
optional(seq(choice($.keyword_follows, $.keyword_precedes), $.identifier)),
),
seq($.keyword_when, wrapped_in_parenthesis($._expression)),
),
),
seq($.keyword_when, wrapped_in_parenthesis($._expression)),
$.keyword_execute,
choice($.keyword_function, $.keyword_procedure),
$.object_reference,
paren_list(field('parameter', $.term)),
),
),
$.keyword_execute,
choice($.keyword_function, $.keyword_procedure),
$.object_reference,
paren_list(field('parameter', $.term)),
),

_create_trigger_event: $ => choice(
Expand Down Expand Up @@ -639,4 +659,35 @@ export default {
),
),

// ── Firebird dialect ────────────────────────────────────────────────────────

// CREATE DOMAIN name [AS] type [DEFAULT literal] [NOT NULL] [CHECK (...)]
create_domain: $ => prec.right(seq(
$.keyword_create,
$.keyword_domain,
$.object_reference,
optional($.keyword_as),
$._type,
optional(seq($.keyword_default, $.literal)),
optional(seq($.keyword_not, $.keyword_null)),
optional(seq($.keyword_check, $.parenthesized_expression)),
optional(seq($.keyword_collate, $.identifier)),
)),

// CREATE [OR ALTER] EXCEPTION name 'message'
create_exception: $ => seq(
$.keyword_create,
optional($._or_replace),
$.keyword_exception,
$.object_reference,
alias(choice($._single_quote_string, $._double_quote_string), $.literal),
),

// CREATE GENERATOR name (Firebird legacy sequence)
create_generator: $ => seq(
$.keyword_create,
$.keyword_generator,
$.object_reference,
),

};
27 changes: 27 additions & 0 deletions grammar/statements/set.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,33 @@ import { comma_list } from "../helpers.js";

export default {

// Firebird SET TERM: self-terminating leaf token — consumes the full
// "SET TERM ^ ;" or "SET TERM ; ^" including the trailing terminator char
// so the program rule does not expect an additional ; or ^ after it.
set_term: _ => /[Ss][Ee][Tt][ \t]+[Tt][Ee][Rr][Mm][ \t]+\S+[ \t]*[;^]/,

// Firebird DECLARE EXTERNAL FUNCTION: self-terminating leaf token — swallows the
// entire declaration (name + CSTRING/INTEGER params + ENTRY_POINT + MODULE_NAME)
// including the trailing semicolon. Using a regex avoids type-system conflicts
// with CSTRING, BY VALUE, NULL, RETURNS, etc. which are not in the SQL grammar.
declare_external_function: _ => /DECLARE[ \t\n\r]+EXTERNAL[ \t\n\r]+FUNCTION[^;]+;/i,

// Firebird local variable declaration: appears before BEGIN in procedure/function bodies.
// Covers both "DECLARE VARIABLE name type;" and "DECLARE name type;" forms.
// The VARIABLE keyword is optional (older Firebird/legacy style omits it).
// Only used inside procedure/function body context, not at top level.
fb_var_declaration: _ => /DECLARE[ \t]+(?:VARIABLE[ \t]+)?\w+[ \t]+[^;]+;/i,

// Firebird CREATE PROCEDURE/TRIGGER/FUNCTION in caret mode: self-terminating.
// Absorbs the entire definition from CREATE ... through END^, where ^ is the
// Firebird caret terminator (always at end of line, never inside the body).
// Pattern: "^ not at end of line" = XOR or other use (allowed inside body);
// "^ at end of line or EOF" = statement terminator (stops the match).
// This prevents the complex procedure body (Firebird variable assignments,
// IF/WHILE, EXECUTE STATEMENT, triple-quote strings) from corrupting the
// GLR parser state and causing subsequent CREATE TABLE nodes to be missed.
fb_proc_or_trigger: _ => /CREATE[ \t\r\n]+(?:OR[ \t\r\n]+REPLACE[ \t\r\n]+)?(?:PROCEDURE|TRIGGER|FUNCTION)(?:[^\^]|\^[^\r\n])*\^/i,

set_statement: $ => seq(
$.keyword_set,
choice(
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"license": "MIT",
"dependencies": {
"node-addon-api": "^7.1.0",
"node-gyp-build": "^4.8.0"
"node-gyp-build": "^4.8.0",
"tree-sitter-cli": "^0.26.8"
},
"devDependencies": {
"commit-and-tag-version": "^12.0.0",
Expand Down
Loading