root ::= header test_block_list finish
header ::= "set testdir [file dirname $argv0]" ws "source $testdir/tester.tcl" ws "set testprefix" ws identifier ws "set ::timeout 60000" ws (variable_definition ws)*
variable_definition ::= "set" ws identifier ws (tcl_list | tcl_value) ws
test_block_list ::= (test_block ws)+
test_block ::= do_test | do_execsql_test | procedure_definition | ifcapable_block | conditional_block | tcl_statement
do_test ::= "do_test" ws test_name ws "{" ws (tcl_statement ws)* "}" ws expected_result
do_execsql_test ::= "do_execsql_test" ws test_name ws "{" ws sql_statements ws "}" ws expected_result
procedure_definition ::= "proc" ws identifier ws parameter_list ws "{" ws (tcl_statement ws)* "}" ws
conditional_block ::= "if" ws condition ws "{" ws (tcl_statement ws)* "}" ws ("else" ws "{" ws (tcl_statement ws)* "}" ws)?
ifcapable_block ::= "ifcapable" ws identifier ws "{" ws (test_block ws)+ "}" ws
tcl_statement ::= catchsql_command | execsql_command | db_command | lappend_command | return_command | tcl_simple_command
tcl_simple_command ::= identifier ws (tcl_value ws)*
catchsql_command ::= "catchsql" ws tcl_value ws
execsql_command ::= "execsql" ws "{" ws sql_statements ws "}" ws
db_command ::= ("db" | "db2") ws identifier ws tcl_value*
lappend_command ::= "lappend" ws identifier ws tcl_value ws
return_command ::= "return" ws tcl_value
parameter_list ::= identifier | "{" ws (identifier ws)+ "}" ws
test_name ::= identifier "-" digit+ "." digit+
sql_statements ::= sql_statement (";" ws sql_statement)* ";"?
sql_statement ::= begin_stmt | commit_stmt | rollback_stmt | pragma_stmt | create_table_stmt | create_index_stmt | insert_stmt | select_stmt | with_stmt
begin_stmt ::= "BEGIN" ws ("EXCLUSIVE" | "IMMEDIATE")?
commit_stmt ::= "COMMIT"
rollback_stmt ::= "ROLLBACK"
pragma_stmt ::= "PRAGMA" ws identifier ( ws "=" ws literal )?
create_table_stmt ::= "CREATE TABLE" ws identifier "(" ws column_def_list ws ")"
column_def_list ::= column_def ( ws "," ws column_def )*
column_def ::= identifier ( ws type_name )? ( ws column_constraint )*
column_constraint ::= "PRIMARY KEY" | "NOT NULL" | "UNIQUE" | "DEFAULT" ws literal
create_index_stmt ::= "CREATE INDEX" ws identifier ws "ON" ws identifier "(" ws identifier ws ")"
insert_stmt ::= "INSERT INTO" ws identifier ( "(" ws column_list ws ")" )? ws ( "VALUES" ws "(" ws value_list ws ")" | "SELECT" ws select_expr )
column_list ::= identifier ( ws "," ws identifier )*
value_list ::= literal ( ws "," ws literal )*
select_stmt ::= "SELECT" ws select_expr ws "FROM" ws identifier ( ws "WHERE" ws condition_expr )?
select_expr ::= "*" | "count(*)" | identifier | literal | identifier ws "(" ws "*" ws ")"
condition_expr ::= identifier ws "=" ws ( identifier | literal )
with_stmt ::= "WITH" ws identifier "(" ws identifier ws ")" ws "AS" ws "(" ws sql_statement ( ws "UNION ALL" ws sql_statement )* ws ")" ws sql_statement
condition ::= tcl_value ws ( "==" | "!=" | ">" | "<" | ">=" | "<=" ) ws tcl_value | identifier
tcl_value ::= literal | identifier | "$" identifier | "$::" identifier | tcl_list | "{" ws sql_statements ws "}"
tcl_list ::= "{" ws (tcl_value ws)* "}" ws
expected_result ::= literal | tcl_list
literal ::= numeric_literal | string_literal
numeric_literal ::= digit+ ( "." digit+ )?
string_literal ::= "\"" [^"]* "\"" | "'" [^']* "'"
identifier ::= letter ( letter | digit | "_" )*
letter ::= [A-Za-z]
digit ::= [0-9]
finish ::= "finish_test" ws
ws ::= [ \t\n\r]*
