diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-10-27 19:39:39 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-10-27 19:39:39 (GMT) |
commit | ea28451286d3ea4a772fa174483f9a7a66bb1ab3 (patch) | |
tree | 6ee9d8a7848333a7ceeee3b13d492e40225f8b86 /tcllib/examples/page | |
parent | b5ca09bae0d6a1edce939eea03594dd56383f2c8 (diff) | |
parent | 7c621da28f07e449ad90c387344f07a453927569 (diff) | |
download | blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.zip blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.gz blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.bz2 |
Merge commit '7c621da28f07e449ad90c387344f07a453927569' as 'tcllib'
Diffstat (limited to 'tcllib/examples/page')
-rw-r--r-- | tcllib/examples/page/lemon.html | 892 | ||||
-rw-r--r-- | tcllib/examples/page/lemon.peg | 141 | ||||
-rw-r--r-- | tcllib/examples/page/parse.y | 995 | ||||
-rw-r--r-- | tcllib/examples/page/sql.log | 18 | ||||
-rw-r--r-- | tcllib/examples/page/sql.peg | 643 |
5 files changed, 2689 insertions, 0 deletions
diff --git a/tcllib/examples/page/lemon.html b/tcllib/examples/page/lemon.html new file mode 100644 index 0000000..6a4d6db --- /dev/null +++ b/tcllib/examples/page/lemon.html @@ -0,0 +1,892 @@ +<html> +<head> +<title>The Lemon Parser Generator</title> +</head> +<body bgcolor=white> +<h1 align=center>The Lemon Parser Generator</h1> + +<p>Lemon is an LALR(1) parser generator for C or C++. +It does the same job as ``bison'' and ``yacc''. +But lemon is not another bison or yacc clone. It +uses a different grammar syntax which is designed to +reduce the number of coding errors. Lemon also uses a more +sophisticated parsing engine that is faster than yacc and +bison and which is both reentrant and thread-safe. +Furthermore, Lemon implements features that can be used +to eliminate resource leaks, making is suitable for use +in long-running programs such as graphical user interfaces +or embedded controllers.</p> + +<p>This document is an introduction to the Lemon +parser generator.</p> + +<h2>Theory of Operation</h2> + +<p>The main goal of Lemon is to translate a context free grammar (CFG) +for a particular language into C code that implements a parser for +that language. +The program has two inputs: +<ul> +<li>The grammar specification. +<li>A parser template file. +</ul> +Typically, only the grammar specification is supplied by the programmer. +Lemon comes with a default parser template which works fine for most +applications. But the user is free to substitute a different parser +template if desired.</p> + +<p>Depending on command-line options, Lemon will generate between +one and three files of outputs. +<ul> +<li>C code to implement the parser. +<li>A header file defining an integer ID for each terminal symbol. +<li>An information file that describes the states of the generated parser + automaton. +</ul> +By default, all three of these output files are generated. +The header file is suppressed if the ``-m'' command-line option is +used and the report file is omitted when ``-q'' is selected.</p> + +<p>The grammar specification file uses a ``.y'' suffix, by convention. +In the examples used in this document, we'll assume the name of the +grammar file is ``gram.y''. A typical use of Lemon would be the +following command: +<pre> + lemon gram.y +</pre> +This command will generate three output files named ``gram.c'', +``gram.h'' and ``gram.out''. +The first is C code to implement the parser. The second +is the header file that defines numerical values for all +terminal symbols, and the last is the report that explains +the states used by the parser automaton.</p> + +<h3>Command Line Options</h3> + +<p>The behavior of Lemon can be modified using command-line options. +You can obtain a list of the available command-line options together +with a brief explanation of what each does by typing +<pre> + lemon -? +</pre> +As of this writing, the following command-line options are supported: +<ul> +<li><tt>-b</tt> +<li><tt>-c</tt> +<li><tt>-g</tt> +<li><tt>-m</tt> +<li><tt>-q</tt> +<li><tt>-s</tt> +<li><tt>-x</tt> +</ul> +The ``-b'' option reduces the amount of text in the report file by +printing only the basis of each parser state, rather than the full +configuration. +The ``-c'' option suppresses action table compression. Using -c +will make the parser a little larger and slower but it will detect +syntax errors sooner. +The ``-g'' option causes no output files to be generated at all. +Instead, the input grammar file is printed on standard output but +with all comments, actions and other extraneous text deleted. This +is a useful way to get a quick summary of a grammar. +The ``-m'' option causes the output C source file to be compatible +with the ``makeheaders'' program. +Makeheaders is a program that automatically generates header files +from C source code. When the ``-m'' option is used, the header +file is not output since the makeheaders program will take care +of generated all header files automatically. +The ``-q'' option suppresses the report file. +Using ``-s'' causes a brief summary of parser statistics to be +printed. Like this: +<pre> + Parser statistics: 74 terminals, 70 nonterminals, 179 rules + 340 states, 2026 parser table entries, 0 conflicts +</pre> +Finally, the ``-x'' option causes Lemon to print its version number +and then stops without attempting to read the grammar or generate a parser.</p> + +<h3>The Parser Interface</h3> + +<p>Lemon doesn't generate a complete, working program. It only generates +a few subroutines that implement a parser. This section describes +the interface to those subroutines. It is up to the programmer to +call these subroutines in an appropriate way in order to produce a +complete system.</p> + +<p>Before a program begins using a Lemon-generated parser, the program +must first create the parser. +A new parser is created as follows: +<pre> + void *pParser = ParseAlloc( malloc ); +</pre> +The ParseAlloc() routine allocates and initializes a new parser and +returns a pointer to it. +The actual data structure used to represent a parser is opaque -- +its internal structure is not visible or usable by the calling routine. +For this reason, the ParseAlloc() routine returns a pointer to void +rather than a pointer to some particular structure. +The sole argument to the ParseAlloc() routine is a pointer to the +subroutine used to allocate memory. Typically this means ``malloc()''.</p> + +<p>After a program is finished using a parser, it can reclaim all +memory allocated by that parser by calling +<pre> + ParseFree(pParser, free); +</pre> +The first argument is the same pointer returned by ParseAlloc(). The +second argument is a pointer to the function used to release bulk +memory back to the system.</p> + +<p>After a parser has been allocated using ParseAlloc(), the programmer +must supply the parser with a sequence of tokens (terminal symbols) to +be parsed. This is accomplished by calling the following function +once for each token: +<pre> + Parse(pParser, hTokenID, sTokenData, pArg); +</pre> +The first argument to the Parse() routine is the pointer returned by +ParseAlloc(). +The second argument is a small positive integer that tells the parse the +type of the next token in the data stream. +There is one token type for each terminal symbol in the grammar. +The gram.h file generated by Lemon contains #define statements that +map symbolic terminal symbol names into appropriate integer values. +(A value of 0 for the second argument is a special flag to the +parser to indicate that the end of input has been reached.) +The third argument is the value of the given token. By default, +the type of the third argument is integer, but the grammar will +usually redefine this type to be some kind of structure. +Typically the second argument will be a broad category of tokens +such as ``identifier'' or ``number'' and the third argument will +be the name of the identifier or the value of the number.</p> + +<p>The Parse() function may have either three or four arguments, +depending on the grammar. If the grammar specification file request +it, the Parse() function will have a fourth parameter that can be +of any type chosen by the programmer. The parser doesn't do anything +with this argument except to pass it through to action routines. +This is a convenient mechanism for passing state information down +to the action routines without having to use global variables.</p> + +<p>A typical use of a Lemon parser might look something like the +following: +<pre> + 01 ParseTree *ParseFile(const char *zFilename){ + 02 Tokenizer *pTokenizer; + 03 void *pParser; + 04 Token sToken; + 05 int hTokenId; + 06 ParserState sState; + 07 + 08 pTokenizer = TokenizerCreate(zFilename); + 09 pParser = ParseAlloc( malloc ); + 10 InitParserState(&sState); + 11 while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){ + 12 Parse(pParser, hTokenId, sToken, &sState); + 13 } + 14 Parse(pParser, 0, sToken, &sState); + 15 ParseFree(pParser, free ); + 16 TokenizerFree(pTokenizer); + 17 return sState.treeRoot; + 18 } +</pre> +This example shows a user-written routine that parses a file of +text and returns a pointer to the parse tree. +(We've omitted all error-handling from this example to keep it +simple.) +We assume the existence of some kind of tokenizer which is created +using TokenizerCreate() on line 8 and deleted by TokenizerFree() +on line 16. The GetNextToken() function on line 11 retrieves the +next token from the input file and puts its type in the +integer variable hTokenId. The sToken variable is assumed to be +some kind of structure that contains details about each token, +such as its complete text, what line it occurs on, etc. </p> + +<p>This example also assumes the existence of structure of type +ParserState that holds state information about a particular parse. +An instance of such a structure is created on line 6 and initialized +on line 10. A pointer to this structure is passed into the Parse() +routine as the optional 4th argument. +The action routine specified by the grammar for the parser can use +the ParserState structure to hold whatever information is useful and +appropriate. In the example, we note that the treeRoot field of +the ParserState structure is left pointing to the root of the parse +tree.</p> + +<p>The core of this example as it relates to Lemon is as follows: +<pre> + ParseFile(){ + pParser = ParseAlloc( malloc ); + while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){ + Parse(pParser, hTokenId, sToken); + } + Parse(pParser, 0, sToken); + ParseFree(pParser, free ); + } +</pre> +Basically, what a program has to do to use a Lemon-generated parser +is first create the parser, then send it lots of tokens obtained by +tokenizing an input source. When the end of input is reached, the +Parse() routine should be called one last time with a token type +of 0. This step is necessary to inform the parser that the end of +input has been reached. Finally, we reclaim memory used by the +parser by calling ParseFree().</p> + +<p>There is one other interface routine that should be mentioned +before we move on. +The ParseTrace() function can be used to generate debugging output +from the parser. A prototype for this routine is as follows: +<pre> + ParseTrace(FILE *stream, char *zPrefix); +</pre> +After this routine is called, a short (one-line) message is written +to the designated output stream every time the parser changes states +or calls an action routine. Each such message is prefaced using +the text given by zPrefix. This debugging output can be turned off +by calling ParseTrace() again with a first argument of NULL (0).</p> + +<h3>Differences With YACC and BISON</h3> + +<p>Programmers who have previously used the yacc or bison parser +generator will notice several important differences between yacc and/or +bison and Lemon. +<ul> +<li>In yacc and bison, the parser calls the tokenizer. In Lemon, + the tokenizer calls the parser. +<li>Lemon uses no global variables. Yacc and bison use global variables + to pass information between the tokenizer and parser. +<li>Lemon allows multiple parsers to be running simultaneously. Yacc + and bison do not. +</ul> +These differences may cause some initial confusion for programmers +with prior yacc and bison experience. +But after years of experience using Lemon, I firmly +believe that the Lemon way of doing things is better.</p> + +<h2>Input File Syntax</h2> + +<p>The main purpose of the grammar specification file for Lemon is +to define the grammar for the parser. But the input file also +specifies additional information Lemon requires to do its job. +Most of the work in using Lemon is in writing an appropriate +grammar file.</p> + +<p>The grammar file for lemon is, for the most part, free format. +It does not have sections or divisions like yacc or bison. Any +declaration can occur at any point in the file. +Lemon ignores whitespace (except where it is needed to separate +tokens) and it honors the same commenting conventions as C and C++.</p> + +<h3>Terminals and Nonterminals</h3> + +<p>A terminal symbol (token) is any string of alphanumeric +and underscore characters +that begins with an upper case letter. +A terminal can contain lower class letters after the first character, +but the usual convention is to make terminals all upper case. +A nonterminal, on the other hand, is any string of alphanumeric +and underscore characters than begins with a lower case letter. +Again, the usual convention is to make nonterminals use all lower +case letters.</p> + +<p>In Lemon, terminal and nonterminal symbols do not need to +be declared or identified in a separate section of the grammar file. +Lemon is able to generate a list of all terminals and nonterminals +by examining the grammar rules, and it can always distinguish a +terminal from a nonterminal by checking the case of the first +character of the name.</p> + +<p>Yacc and bison allow terminal symbols to have either alphanumeric +names or to be individual characters included in single quotes, like +this: ')' or '$'. Lemon does not allow this alternative form for +terminal symbols. With Lemon, all symbols, terminals and nonterminals, +must have alphanumeric names.</p> + +<h3>Grammar Rules</h3> + +<p>The main component of a Lemon grammar file is a sequence of grammar +rules. +Each grammar rule consists of a nonterminal symbol followed by +the special symbol ``::='' and then a list of terminals and/or nonterminals. +The rule is terminated by a period. +The list of terminals and nonterminals on the right-hand side of the +rule can be empty. +Rules can occur in any order, except that the left-hand side of the +first rule is assumed to be the start symbol for the grammar (unless +specified otherwise using the <tt>%start</tt> directive described below.) +A typical sequence of grammar rules might look something like this: +<pre> + expr ::= expr PLUS expr. + expr ::= expr TIMES expr. + expr ::= LPAREN expr RPAREN. + expr ::= VALUE. +</pre> +</p> + +<p>There is one non-terminal in this example, ``expr'', and five +terminal symbols or tokens: ``PLUS'', ``TIMES'', ``LPAREN'', +``RPAREN'' and ``VALUE''.</p> + +<p>Like yacc and bison, Lemon allows the grammar to specify a block +of C code that will be executed whenever a grammar rule is reduced +by the parser. +In Lemon, this action is specified by putting the C code (contained +within curly braces <tt>{...}</tt>) immediately after the +period that closes the rule. +For example: +<pre> + expr ::= expr PLUS expr. { printf("Doing an addition...\n"); } +</pre> +</p> + +<p>In order to be useful, grammar actions must normally be linked to +their associated grammar rules. +In yacc and bison, this is accomplished by embedding a ``$$'' in the +action to stand for the value of the left-hand side of the rule and +symbols ``$1'', ``$2'', and so forth to stand for the value of +the terminal or nonterminal at position 1, 2 and so forth on the +right-hand side of the rule. +This idea is very powerful, but it is also very error-prone. The +single most common source of errors in a yacc or bison grammar is +to miscount the number of symbols on the right-hand side of a grammar +rule and say ``$7'' when you really mean ``$8''.</p> + +<p>Lemon avoids the need to count grammar symbols by assigning symbolic +names to each symbol in a grammar rule and then using those symbolic +names in the action. +In yacc or bison, one would write this: +<pre> + expr -> expr PLUS expr { $$ = $1 + $3; }; +</pre> +But in Lemon, the same rule becomes the following: +<pre> + expr(A) ::= expr(B) PLUS expr(C). { A = B+C; } +</pre> +In the Lemon rule, any symbol in parentheses after a grammar rule +symbol becomes a place holder for that symbol in the grammar rule. +This place holder can then be used in the associated C action to +stand for the value of that symbol.<p> + +<p>The Lemon notation for linking a grammar rule with its reduce +action is superior to yacc/bison on several counts. +First, as mentioned above, the Lemon method avoids the need to +count grammar symbols. +Secondly, if a terminal or nonterminal in a Lemon grammar rule +includes a linking symbol in parentheses but that linking symbol +is not actually used in the reduce action, then an error message +is generated. +For example, the rule +<pre> + expr(A) ::= expr(B) PLUS expr(C). { A = B; } +</pre> +will generate an error because the linking symbol ``C'' is used +in the grammar rule but not in the reduce action.</p> + +<p>The Lemon notation for linking grammar rules to reduce actions +also facilitates the use of destructors for reclaiming memory +allocated by the values of terminals and nonterminals on the +right-hand side of a rule.</p> + +<h3>Precedence Rules</h3> + +<p>Lemon resolves parsing ambiguities in exactly the same way as +yacc and bison. A shift-reduce conflict is resolved in favor +of the shift, and a reduce-reduce conflict is resolved by reducing +whichever rule comes first in the grammar file.</p> + +<p>Just like in +yacc and bison, Lemon allows a measure of control +over the resolution of paring conflicts using precedence rules. +A precedence value can be assigned to any terminal symbol +using the %left, %right or %nonassoc directives. Terminal symbols +mentioned in earlier directives have a lower precedence that +terminal symbols mentioned in later directives. For example:</p> + +<p><pre> + %left AND. + %left OR. + %nonassoc EQ NE GT GE LT LE. + %left PLUS MINUS. + %left TIMES DIVIDE MOD. + %right EXP NOT. +</pre></p> + +<p>In the preceding sequence of directives, the AND operator is +defined to have the lowest precedence. The OR operator is one +precedence level higher. And so forth. Hence, the grammar would +attempt to group the ambiguous expression +<pre> + a AND b OR c +</pre> +like this +<pre> + a AND (b OR c). +</pre> +The associativity (left, right or nonassoc) is used to determine +the grouping when the precedence is the same. AND is left-associative +in our example, so +<pre> + a AND b AND c +</pre> +is parsed like this +<pre> + (a AND b) AND c. +</pre> +The EXP operator is right-associative, though, so +<pre> + a EXP b EXP c +</pre> +is parsed like this +<pre> + a EXP (b EXP c). +</pre> +The nonassoc precedence is used for non-associative operators. +So +<pre> + a EQ b EQ c +</pre> +is an error.</p> + +<p>The precedence of non-terminals is transferred to rules as follows: +The precedence of a grammar rule is equal to the precedence of the +left-most terminal symbol in the rule for which a precedence is +defined. This is normally what you want, but in those cases where +you want to precedence of a grammar rule to be something different, +you can specify an alternative precedence symbol by putting the +symbol in square braces after the period at the end of the rule and +before any C-code. For example:</p> + +<p><pre> + expr = MINUS expr. [NOT] +</pre></p> + +<p>This rule has a precedence equal to that of the NOT symbol, not the +MINUS symbol as would have been the case by default.</p> + +<p>With the knowledge of how precedence is assigned to terminal +symbols and individual +grammar rules, we can now explain precisely how parsing conflicts +are resolved in Lemon. Shift-reduce conflicts are resolved +as follows: +<ul> +<li> If either the token to be shifted or the rule to be reduced + lacks precedence information, then resolve in favor of the + shift, but report a parsing conflict. +<li> If the precedence of the token to be shifted is greater than + the precedence of the rule to reduce, then resolve in favor + of the shift. No parsing conflict is reported. +<li> If the precedence of the token it be shifted is less than the + precedence of the rule to reduce, then resolve in favor of the + reduce action. No parsing conflict is reported. +<li> If the precedences are the same and the shift token is + right-associative, then resolve in favor of the shift. + No parsing conflict is reported. +<li> If the precedences are the same the the shift token is + left-associative, then resolve in favor of the reduce. + No parsing conflict is reported. +<li> Otherwise, resolve the conflict by doing the shift and + report the parsing conflict. +</ul> +Reduce-reduce conflicts are resolved this way: +<ul> +<li> If either reduce rule + lacks precedence information, then resolve in favor of the + rule that appears first in the grammar and report a parsing + conflict. +<li> If both rules have precedence and the precedence is different + then resolve the dispute in favor of the rule with the highest + precedence and do not report a conflict. +<li> Otherwise, resolve the conflict by reducing by the rule that + appears first in the grammar and report a parsing conflict. +</ul> + +<h3>Special Directives</h3> + +<p>The input grammar to Lemon consists of grammar rules and special +directives. We've described all the grammar rules, so now we'll +talk about the special directives.</p> + +<p>Directives in lemon can occur in any order. You can put them before +the grammar rules, or after the grammar rules, or in the mist of the +grammar rules. It doesn't matter. The relative order of +directives used to assign precedence to terminals is important, but +other than that, the order of directives in Lemon is arbitrary.</p> + +<p>Lemon supports the following special directives: +<ul> +<li><tt>%code</tt> +<li><tt>%default_destructor</tt> +<li><tt>%default_type</tt> +<li><tt>%destructor</tt> +<li><tt>%extra_argument</tt> +<li><tt>%include</tt> +<li><tt>%left</tt> +<li><tt>%name</tt> +<li><tt>%nonassoc</tt> +<li><tt>%parse_accept</tt> +<li><tt>%parse_failure </tt> +<li><tt>%right</tt> +<li><tt>%stack_overflow</tt> +<li><tt>%stack_size</tt> +<li><tt>%start_symbol</tt> +<li><tt>%syntax_error</tt> +<li><tt>%token_destructor</tt> +<li><tt>%token_prefix</tt> +<li><tt>%token_type</tt> +<li><tt>%type</tt> +</ul> +Each of these directives will be described separately in the +following sections:</p> + +<h4>The <tt>%code</tt> directive</h4> + +<p>The %code directive is used to specify addition C/C++ code that +is added to the end of the main output file. This is similar to +the %include directive except that %include is inserted at the +beginning of the main output file.</p> + +<p>%code is typically used to include some action routines or perhaps +a tokenizer as part of the output file.</p> + +<h4>The <tt>%default_destructor</tt> directive</h4> + +<p>The %default_destructor directive specifies a destructor to +use for non-terminals that do not have their own destructor +specified by a separate %destructor directive. See the documentation +on the %destructor directive below for additional information.</p> + +<p>In some grammers, many different non-terminal symbols have the +same datatype and hence the same destructor. This directive is +a convenience way to specify the same destructor for all those +non-terminals using a single statement.</p> + +<h4>The <tt>%default_type</tt> directive</h4> + +<p>The %default_type directive specifies the datatype of non-terminal +symbols that do no have their own datatype defined using a separate +%type directive. See the documentation on %type below for addition +information.</p> + +<h4>The <tt>%destructor</tt> directive</h4> + +<p>The %destructor directive is used to specify a destructor for +a non-terminal symbol. +(See also the %token_destructor directive which is used to +specify a destructor for terminal symbols.)</p> + +<p>A non-terminal's destructor is called to dispose of the +non-terminal's value whenever the non-terminal is popped from +the stack. This includes all of the following circumstances: +<ul> +<li> When a rule reduces and the value of a non-terminal on + the right-hand side is not linked to C code. +<li> When the stack is popped during error processing. +<li> When the ParseFree() function runs. +</ul> +The destructor can do whatever it wants with the value of +the non-terminal, but its design is to deallocate memory +or other resources held by that non-terminal.</p> + +<p>Consider an example: +<pre> + %type nt {void*} + %destructor nt { free($$); } + nt(A) ::= ID NUM. { A = malloc( 100 ); } +</pre> +This example is a bit contrived but it serves to illustrate how +destructors work. The example shows a non-terminal named +``nt'' that holds values of type ``void*''. When the rule for +an ``nt'' reduces, it sets the value of the non-terminal to +space obtained from malloc(). Later, when the nt non-terminal +is popped from the stack, the destructor will fire and call +free() on this malloced space, thus avoiding a memory leak. +(Note that the symbol ``$$'' in the destructor code is replaced +by the value of the non-terminal.)</p> + +<p>It is important to note that the value of a non-terminal is passed +to the destructor whenever the non-terminal is removed from the +stack, unless the non-terminal is used in a C-code action. If +the non-terminal is used by C-code, then it is assumed that the +C-code will take care of destroying it if it should really +be destroyed. More commonly, the value is used to build some +larger structure and we don't want to destroy it, which is why +the destructor is not called in this circumstance.</p> + +<p>By appropriate use of destructors, it is possible to +build a parser using Lemon that can be used within a long-running +program, such as a GUI, that will not leak memory or other resources. +To do the same using yacc or bison is much more difficult.</p> + +<h4>The <tt>%extra_argument</tt> directive</h4> + +The %extra_argument directive instructs Lemon to add a 4th parameter +to the parameter list of the Parse() function it generates. Lemon +doesn't do anything itself with this extra argument, but it does +make the argument available to C-code action routines, destructors, +and so forth. For example, if the grammar file contains:</p> + +<p><pre> + %extra_argument { MyStruct *pAbc } +</pre></p> + +<p>Then the Parse() function generated will have an 4th parameter +of type ``MyStruct*'' and all action routines will have access to +a variable named ``pAbc'' that is the value of the 4th parameter +in the most recent call to Parse().</p> + +<h4>The <tt>%include</tt> directive</h4> + +<p>The %include directive specifies C code that is included at the +top of the generated parser. You can include any text you want -- +the Lemon parser generator copies it blindly. If you have multiple +%include directives in your grammar file the value of the last +%include directive overwrites all the others.</p. + +<p>The %include directive is very handy for getting some extra #include +preprocessor statements at the beginning of the generated parser. +For example:</p> + +<p><pre> + %include {#include <unistd.h>} +</pre></p> + +<p>This might be needed, for example, if some of the C actions in the +grammar call functions that are prototyed in unistd.h.</p> + +<h4>The <tt>%left</tt> directive</h4> + +The %left directive is used (along with the %right and +%nonassoc directives) to declare precedences of terminal +symbols. Every terminal symbol whose name appears after +a %left directive but before the next period (``.'') is +given the same left-associative precedence value. Subsequent +%left directives have higher precedence. For example:</p> + +<p><pre> + %left AND. + %left OR. + %nonassoc EQ NE GT GE LT LE. + %left PLUS MINUS. + %left TIMES DIVIDE MOD. + %right EXP NOT. +</pre></p> + +<p>Note the period that terminates each %left, %right or %nonassoc +directive.</p> + +<p>LALR(1) grammars can get into a situation where they require +a large amount of stack space if you make heavy use or right-associative +operators. For this reason, it is recommended that you use %left +rather than %right whenever possible.</p> + +<h4>The <tt>%name</tt> directive</h4> + +<p>By default, the functions generated by Lemon all begin with the +five-character string ``Parse''. You can change this string to something +different using the %name directive. For instance:</p> + +<p><pre> + %name Abcde +</pre></p> + +<p>Putting this directive in the grammar file will cause Lemon to generate +functions named +<ul> +<li> AbcdeAlloc(), +<li> AbcdeFree(), +<li> AbcdeTrace(), and +<li> Abcde(). +</ul> +The %name directive allows you to generator two or more different +parsers and link them all into the same executable. +</p> + +<h4>The <tt>%nonassoc</tt> directive</h4> + +<p>This directive is used to assign non-associative precedence to +one or more terminal symbols. See the section on precedence rules +or on the %left directive for additional information.</p> + +<h4>The <tt>%parse_accept</tt> directive</h4> + +<p>The %parse_accept directive specifies a block of C code that is +executed whenever the parser accepts its input string. To ``accept'' +an input string means that the parser was able to process all tokens +without error.</p> + +<p>For example:</p> + +<p><pre> + %parse_accept { + printf("parsing complete!\n"); + } +</pre></p> + + +<h4>The <tt>%parse_failure</tt> directive</h4> + +<p>The %parse_failure directive specifies a block of C code that +is executed whenever the parser fails complete. This code is not +executed until the parser has tried and failed to resolve an input +error using is usual error recovery strategy. The routine is +only invoked when parsing is unable to continue.</p> + +<p><pre> + %parse_failure { + fprintf(stderr,"Giving up. Parser is hopelessly lost...\n"); + } +</pre></p> + +<h4>The <tt>%right</tt> directive</h4> + +<p>This directive is used to assign right-associative precedence to +one or more terminal symbols. See the section on precedence rules +or on the %left directive for additional information.</p> + +<h4>The <tt>%stack_overflow</tt> directive</h4> + +<p>The %stack_overflow directive specifies a block of C code that +is executed if the parser's internal stack ever overflows. Typically +this just prints an error message. After a stack overflow, the parser +will be unable to continue and must be reset.</p> + +<p><pre> + %stack_overflow { + fprintf(stderr,"Giving up. Parser stack overflow\n"); + } +</pre></p> + +<p>You can help prevent parser stack overflows by avoiding the use +of right recursion and right-precedence operators in your grammar. +Use left recursion and and left-precedence operators instead, to +encourage rules to reduce sooner and keep the stack size down. +For example, do rules like this: +<pre> + list ::= list element. // left-recursion. Good! + list ::= . +</pre> +Not like this: +<pre> + list ::= element list. // right-recursion. Bad! + list ::= . +</pre> + +<h4>The <tt>%stack_size</tt> directive</h4> + +<p>If stack overflow is a problem and you can't resolve the trouble +by using left-recursion, then you might want to increase the size +of the parser's stack using this directive. Put an positive integer +after the %stack_size directive and Lemon will generate a parse +with a stack of the requested size. The default value is 100.</p> + +<p><pre> + %stack_size 2000 +</pre></p> + +<h4>The <tt>%start_symbol</tt> directive</h4> + +<p>By default, the start-symbol for the grammar that Lemon generates +is the first non-terminal that appears in the grammar file. But you +can choose a different start-symbol using the %start_symbol directive.</p> + +<p><pre> + %start_symbol prog +</pre></p> + +<h4>The <tt>%token_destructor</tt> directive</h4> + +<p>The %destructor directive assigns a destructor to a non-terminal +symbol. (See the description of the %destructor directive above.) +This directive does the same thing for all terminal symbols.</p> + +<p>Unlike non-terminal symbols which may each have a different data type +for their values, terminals all use the same data type (defined by +the %token_type directive) and so they use a common destructor. Other +than that, the token destructor works just like the non-terminal +destructors.</p> + +<h4>The <tt>%token_prefix</tt> directive</h4> + +<p>Lemon generates #defines that assign small integer constants +to each terminal symbol in the grammar. If desired, Lemon will +add a prefix specified by this directive +to each of the #defines it generates. +So if the default output of Lemon looked like this: +<pre> + #define AND 1 + #define MINUS 2 + #define OR 3 + #define PLUS 4 +</pre> +You can insert a statement into the grammar like this: +<pre> + %token_prefix TOKEN_ +</pre> +to cause Lemon to produce these symbols instead: +<pre> + #define TOKEN_AND 1 + #define TOKEN_MINUS 2 + #define TOKEN_OR 3 + #define TOKEN_PLUS 4 +</pre> + +<h4>The <tt>%token_type</tt> and <tt>%type</tt> directives</h4> + +<p>These directives are used to specify the data types for values +on the parser's stack associated with terminal and non-terminal +symbols. The values of all terminal symbols must be of the same +type. This turns out to be the same data type as the 3rd parameter +to the Parse() function generated by Lemon. Typically, you will +make the value of a terminal symbol by a pointer to some kind of +token structure. Like this:</p> + +<p><pre> + %token_type {Token*} +</pre></p> + +<p>If the data type of terminals is not specified, the default value +is ``int''.</p> + +<p>Non-terminal symbols can each have their own data types. Typically +the data type of a non-terminal is a pointer to the root of a parse-tree +structure that contains all information about that non-terminal. +For example:</p> + +<p><pre> + %type expr {Expr*} +</pre></p> + +<p>Each entry on the parser's stack is actually a union containing +instances of all data types for every non-terminal and terminal symbol. +Lemon will automatically use the correct element of this union depending +on what the corresponding non-terminal or terminal symbol is. But +the grammar designer should keep in mind that the size of the union +will be the size of its largest element. So if you have a single +non-terminal whose data type requires 1K of storage, then your 100 +entry parser stack will require 100K of heap space. If you are willing +and able to pay that price, fine. You just need to know.</p> + +<h3>Error Processing</h3> + +<p>After extensive experimentation over several years, it has been +discovered that the error recovery strategy used by yacc is about +as good as it gets. And so that is what Lemon uses.</p> + +<p>When a Lemon-generated parser encounters a syntax error, it +first invokes the code specified by the %syntax_error directive, if +any. It then enters its error recovery strategy. The error recovery +strategy is to begin popping the parsers stack until it enters a +state where it is permitted to shift a special non-terminal symbol +named ``error''. It then shifts this non-terminal and continues +parsing. But the %syntax_error routine will not be called again +until at least three new tokens have been successfully shifted.</p> + +<p>If the parser pops its stack until the stack is empty, and it still +is unable to shift the error symbol, then the %parse_failed routine +is invoked and the parser resets itself to its start state, ready +to begin parsing a new file. This is what will happen at the very +first syntax error, of course, if there are no instances of the +``error'' non-terminal in your grammar.</p> + +</body> +</html> diff --git a/tcllib/examples/page/lemon.peg b/tcllib/examples/page/lemon.peg new file mode 100644 index 0000000..b0ca105 --- /dev/null +++ b/tcllib/examples/page/lemon.peg @@ -0,0 +1,141 @@ +# -* text -*- +## PE grammar for the grammar specification language accepted by the +## LEMON parser generator. + + +## Questions: +## Are directives always at the beginning of a line ? +## Or can they start within a line ? + +## The ifdef/ifndef/endif directives are not documented. +## Nor are the cmdline options + + +PEG pg::peg::lemon (LemonGrammar) + +LemonGrammar <- SPACE Statement + EOF; + +Statement <- ( Directive + / Rule + ) SPACE; + +Rule <- Identifier Label? ASSIGN Definition DOT Precedence? Codeblock? ; +Definition <- (Identifier Label?)* ; +Label <- LPAREN Identifier RPAREN; +Precedence <- LBRACKET Identifier RBRACKET; + +Directive <- DINTRO ( Code / DefaultDestructor + / DefaultType / Destructor + / ExtraArgument / Include + / Left / Name + / Nonassoc / ParseAccept + / ParseFailure / Right + / StackOverflow / Stacksize + / StartSymbol / SyntaxError + / TokenDestructor / TokenPrefix + / TokenType / Type + / Fallback + ); + +Code <- DCODE Codeblock; +DefaultDestructor <- DDEFDEST Identifier Codeblock; +DefaultType <- DDEFTYPE Codeblock; +Destructor <- DDEST Identifier Codeblock; +ExtraArgument <- DEXTRA Codeblock; +Include <- DINCL Codeblock; +Left <- DLEFT Identifier+ DOT; +Name <- DNAME Identifier ; +Nonassoc <- DNON Identifier+ DOT; +ParseAccept <- DPACC Codeblock; +ParseFailure <- DPFAIL Codeblock; +Right <- DRIGHT Identifier+ DOT; +StackOverflow <- DSTKOVER Codeblock; +Stacksize <- DSTKSZ NaturalNumber; +StartSymbol <- DSTART Identifier; +SyntaxError <- DSYNERR Codeblock; +TokenDestructor <- DTOKDEST Identifier Codeblock; +TokenPrefix <- DTOKPFX Identifier; +TokenType <- DTOKTYPE Codeblock; +Type <- DTYPE Identifier Codeblock; +Fallback <- DFALLBK Identifier+ DOT; + + +# Lexical layer + +match: Codeblock <- LBRACE ( Codeblock + / C_COMMENT + / Cplusplus_COMMENT + / (!RBRACE .) + )* RBRACE ; + + Identifier <- Ident SPACE; +match: Ident <- (<alpha>/'_') (<alnum>/'_')*; + + NaturalNumber <- NatNum SPACE; +match: NatNum <- [0-9]+; + +void: ASSIGN <- '::=' SPACE; +void: DOT <- '.' SPACE; +void: LPAREN <- '(' SPACE; +void: RPAREN <- ')' SPACE; +void: LBRACKET <- '[' SPACE; +void: RBRACKET <- ']' SPACE; +void: LBRACE <- '{'; +void: RBRACE <- '}'; + +void: DINTRO <- '%'; +void: DCODE <- 'code' SPACE; +void: DDEFDEST <- 'default_destructor' SPACE; +void: DDEFTYPE <- 'default_type' SPACE; +void: DDEST <- 'destructor' SPACE; +void: DEXTRA <- 'extra_argument' SPACE; +void: DINCL <- 'include' SPACE; +void: DLEFT <- 'left' SPACE; +void: DNAME <- 'name' SPACE; +void: DNON <- 'nonassoc' SPACE; +void: DPACC <- 'parse_accept' SPACE; +void: DPFAIL <- 'parse_failure' SPACE; +void: DRIGHT <- 'right' SPACE; +void: DSTKOVER <- 'stack_overflow' SPACE; +void: DSTKSZ <- 'stack_size' SPACE; +void: DSTART <- 'start_symbol' SPACE; +void: DSYNERR <- 'syntax_error' SPACE; +void: DTOKDEST <- 'token_destructor' SPACE; +void: DTOKPFX <- 'token_prefix' SPACE; +void: DTOKTYPE <- 'token_type' SPACE; +void: DTYPE <- 'type' SPACE; +void: DFALLBK <- 'fallback' SPACE; + +# These have % in their definition because they +# are not part of the regular directives. +void: DIFDEF <- '%ifdef' SPACE; +void: DIFNDEF <- '%ifndef' SPACE; +void: DENDIF <- '%endif' SPACE; + +# Directives we do not really know about. They are in the SQLite +# parse.y file, but are not documented for LEMON. We treat them as +# whitespace for now, see below. + +Ifdef <- DIFDEF Identifier; +Ifndef <- DIFNDEF Identifier; +Endif <- DENDIF; + + +# Whitespace + +void: SPACE <- ( [ \t\n\r] + / C_COMMENT + / Cplusplus_COMMENT + / Ifndef / Ifdef / Endif + )*; + +C_COMMENT <- CCOM_OPEN (!CCOM_CLOSE .)* CCOM_CLOSE; +CCOM_OPEN <- '/*'; +CCOM_CLOSE <- '*/'; + +Cplusplus_COMMENT <- '//' (!EOL .)* EOL; +EOL <- '\r\n' / '\r' / '\n'; + +EOF <- !.; + +END; diff --git a/tcllib/examples/page/parse.y b/tcllib/examples/page/parse.y new file mode 100644 index 0000000..5ecbb2e --- /dev/null +++ b/tcllib/examples/page/parse.y @@ -0,0 +1,995 @@ +/* +** 2001 September 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 grammar for SQL. Process this file +** using the lemon parser generator to generate C code that runs +** the parser. Lemon will also generate a header file containing +** numeric codes for all of the tokens. +** +** @(#) $Id: parse.y,v 1.1 2005/09/28 04:51:19 andreas_kupries Exp $ +*/ +%token_prefix TK_ +%token_type {Token} +%default_type {Token} +%extra_argument {Parse *pParse} +%syntax_error { + if( pParse->zErrMsg==0 ){ + if( TOKEN.z[0] ){ + sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN); + }else{ + sqlite3ErrorMsg(pParse, "incomplete SQL statement"); + } + } +} +%name sqlite3Parser +%include { +#include "sqliteInt.h" +#include "parse.h" + +/* +** An instance of this structure holds information about the +** LIMIT clause of a SELECT statement. +*/ +struct LimitVal { + Expr *pLimit; /* The LIMIT expression. NULL if there is no limit */ + Expr *pOffset; /* The OFFSET expression. NULL if there is none */ +}; + +/* +** An instance of this structure is used to store the LIKE, +** GLOB, NOT LIKE, and NOT GLOB operators. +*/ +struct LikeOp { + int opcode; /* Either TK_GLOB or TK_LIKE */ + int not; /* True if the NOT keyword is present */ +}; + +/* +** An instance of the following structure describes the event of a +** TRIGGER. "a" is the event type, one of TK_UPDATE, TK_INSERT, +** TK_DELETE, or TK_INSTEAD. If the event is of the form +** +** UPDATE ON (a,b,c) +** +** Then the "b" IdList records the list "a,b,c". +*/ +struct TrigEvent { int a; IdList * b; }; + +/* +** An instance of this structure holds the ATTACH key and the key type. +*/ +struct AttachKey { int type; Token key; }; + +} // end %include + +// These are extra tokens used by the lexer but never seen by the +// parser. We put them in a rule so that the parser generator will +// add them to the parse.h output file. +// +%nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION + COLUMN AGG_FUNCTION. + +// Input is a single SQL command +input ::= cmdlist. +cmdlist ::= cmdlist ecmd. +cmdlist ::= ecmd. +cmdx ::= cmd. { sqlite3FinishCoding(pParse); } +ecmd ::= SEMI. +ecmd ::= explain cmdx SEMI. +explain ::= . { sqlite3BeginParse(pParse, 0); } +%ifndef SQLITE_OMIT_EXPLAIN +explain ::= EXPLAIN. { sqlite3BeginParse(pParse, 1); } +%endif + +///////////////////// Begin and end transactions. //////////////////////////// +// + +cmd ::= BEGIN transtype(Y) trans_opt. {sqlite3BeginTransaction(pParse, Y);} +trans_opt ::= . +trans_opt ::= TRANSACTION. +trans_opt ::= TRANSACTION nm. +%type transtype {int} +transtype(A) ::= . {A = TK_DEFERRED;} +transtype(A) ::= DEFERRED(X). {A = @X;} +transtype(A) ::= IMMEDIATE(X). {A = @X;} +transtype(A) ::= EXCLUSIVE(X). {A = @X;} +cmd ::= COMMIT trans_opt. {sqlite3CommitTransaction(pParse);} +cmd ::= END trans_opt. {sqlite3CommitTransaction(pParse);} +cmd ::= ROLLBACK trans_opt. {sqlite3RollbackTransaction(pParse);} + +///////////////////// The CREATE TABLE statement //////////////////////////// +// +cmd ::= create_table create_table_args. +create_table ::= CREATE(X) temp(T) TABLE nm(Y) dbnm(Z). { + sqlite3StartTable(pParse,&X,&Y,&Z,T,0); +} +%type temp {int} +%ifndef SQLITE_OMIT_TEMPDB +temp(A) ::= TEMP. {A = 1;} +%endif +temp(A) ::= . {A = 0;} +create_table_args ::= LP columnlist conslist_opt(X) RP(Y). { + sqlite3EndTable(pParse,&X,&Y,0); +} +create_table_args ::= AS select(S). { + sqlite3EndTable(pParse,0,0,S); + sqlite3SelectDelete(S); +} +columnlist ::= columnlist COMMA column. +columnlist ::= column. + +// About the only information used for a column is the name of the +// column. The type is always just "text". But the code will accept +// an elaborate typename. Perhaps someday we'll do something with it. +// +column(A) ::= columnid(X) type carglist. { + A.z = X.z; + A.n = (pParse->sLastToken.z-X.z) + pParse->sLastToken.n; +} +columnid(A) ::= nm(X). { + sqlite3AddColumn(pParse,&X); + A = X; +} + + +// An IDENTIFIER can be a generic identifier, or one of several +// keywords. Any non-standard keyword can also be an identifier. +// +%type id {Token} +id(A) ::= ID(X). {A = X;} + +// 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 AFTER ASC ATTACH BEFORE BEGIN CASCADE CONFLICT + DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR + GLOB IGNORE IMMEDIATE INITIALLY INSTEAD LIKE MATCH KEY + OF OFFSET PRAGMA RAISE REPLACE RESTRICT ROW STATEMENT + TEMP TRIGGER VACUUM VIEW +%ifdef SQLITE_OMIT_COMPOUND_SELECT + EXCEPT INTERSECT UNION +%endif + REINDEX RENAME CDATE CTIME CTIMESTAMP ALTER + . + +// Define operator precedence early so that this is the first occurance +// of the operator tokens in the grammer. 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 LIKE GLOB 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. +%right UMINUS UPLUS BITNOT. + +// And "ids" is an identifer-or-string. +// +%type ids {Token} +ids(A) ::= ID(X). {A = X;} +ids(A) ::= STRING(X). {A = X;} + +// The name of a column or table can be any of the following: +// +%type nm {Token} +nm(A) ::= ID(X). {A = X;} +nm(A) ::= STRING(X). {A = X;} +nm(A) ::= JOIN_KW(X). {A = X;} + +type ::= . +type ::= typename(X). {sqlite3AddColumnType(pParse,&X,&X);} +type ::= typename(X) LP signed RP(Y). {sqlite3AddColumnType(pParse,&X,&Y);} +type ::= typename(X) LP signed COMMA signed RP(Y). + {sqlite3AddColumnType(pParse,&X,&Y);} +%type typename {Token} +typename(A) ::= ids(X). {A = X;} +typename(A) ::= typename(X) ids(Y). {A.z=X.z; A.n=Y.n+(Y.z-X.z);} +%type signed {int} +signed(A) ::= plus_num(X). { A = atoi(X.z); } +signed(A) ::= minus_num(X). { A = -atoi(X.z); } +carglist ::= carglist carg. +carglist ::= . +carg ::= CONSTRAINT nm ccons. +carg ::= ccons. +carg ::= DEFAULT term(X). {sqlite3AddDefaultValue(pParse,X);} +carg ::= DEFAULT PLUS term(X). {sqlite3AddDefaultValue(pParse,X);} +carg ::= DEFAULT MINUS term(X). { + Expr *p = sqlite3Expr(TK_UMINUS, X, 0, 0); + sqlite3AddDefaultValue(pParse,p); +} +carg ::= DEFAULT id(X). { + Expr *p = sqlite3Expr(TK_STRING, 0, 0, &X); + sqlite3AddDefaultValue(pParse,p); +} + +// In addition to the type name, we also care about the primary key and +// UNIQUE constraints. +// +ccons ::= NULL onconf. +ccons ::= NOT NULL onconf(R). {sqlite3AddNotNull(pParse, R);} +ccons ::= PRIMARY KEY sortorder onconf(R) autoinc(I). + {sqlite3AddPrimaryKey(pParse,0,R,I);} +ccons ::= UNIQUE onconf(R). {sqlite3CreateIndex(pParse,0,0,0,0,R,0,0);} +ccons ::= CHECK LP expr(X) RP onconf. {sqlite3ExprDelete(X);} +ccons ::= REFERENCES nm(T) idxlist_opt(TA) refargs(R). + {sqlite3CreateForeignKey(pParse,0,&T,TA,R);} +ccons ::= defer_subclause(D). {sqlite3DeferForeignKey(pParse,D);} +ccons ::= COLLATE id(C). {sqlite3AddCollateType(pParse, C.z, C.n);} + +// The optional AUTOINCREMENT keyword +%type autoinc {int} +autoinc(X) ::= . {X = 0;} +autoinc(X) ::= AUTOINCR. {X = 1;} + +// 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 {int} +refargs(A) ::= . { A = OE_Restrict * 0x010101; } +refargs(A) ::= refargs(X) refarg(Y). { A = (X & Y.mask) | Y.value; } +%type refarg {struct {int value; int mask;}} +refarg(A) ::= MATCH nm. { A.value = 0; A.mask = 0x000000; } +refarg(A) ::= ON DELETE refact(X). { A.value = X; A.mask = 0x0000ff; } +refarg(A) ::= ON UPDATE refact(X). { A.value = X<<8; A.mask = 0x00ff00; } +refarg(A) ::= ON INSERT refact(X). { A.value = X<<16; A.mask = 0xff0000; } +%type refact {int} +refact(A) ::= SET NULL. { A = OE_SetNull; } +refact(A) ::= SET DEFAULT. { A = OE_SetDflt; } +refact(A) ::= CASCADE. { A = OE_Cascade; } +refact(A) ::= RESTRICT. { A = OE_Restrict; } +%type defer_subclause {int} +defer_subclause(A) ::= NOT DEFERRABLE init_deferred_pred_opt(X). {A = X;} +defer_subclause(A) ::= DEFERRABLE init_deferred_pred_opt(X). {A = X;} +%type init_deferred_pred_opt {int} +init_deferred_pred_opt(A) ::= . {A = 0;} +init_deferred_pred_opt(A) ::= INITIALLY DEFERRED. {A = 1;} +init_deferred_pred_opt(A) ::= INITIALLY IMMEDIATE. {A = 0;} + +// For the time being, the only constraint we care about is the primary +// key and UNIQUE. Both create indices. +// +conslist_opt(A) ::= . {A.n = 0; A.z = 0;} +conslist_opt(A) ::= COMMA(X) conslist. {A = X;} +conslist ::= conslist COMMA tcons. +conslist ::= conslist tcons. +conslist ::= tcons. +tcons ::= CONSTRAINT nm. +tcons ::= PRIMARY KEY LP idxlist(X) autoinc(I) RP onconf(R). + {sqlite3AddPrimaryKey(pParse,X,R,I);} +tcons ::= UNIQUE LP idxlist(X) RP onconf(R). + {sqlite3CreateIndex(pParse,0,0,0,X,R,0,0);} +tcons ::= CHECK expr onconf. +tcons ::= FOREIGN KEY LP idxlist(FA) RP + REFERENCES nm(T) idxlist_opt(TA) refargs(R) defer_subclause_opt(D). { + sqlite3CreateForeignKey(pParse, FA, &T, TA, R); + sqlite3DeferForeignKey(pParse, D); +} +%type defer_subclause_opt {int} +defer_subclause_opt(A) ::= . {A = 0;} +defer_subclause_opt(A) ::= defer_subclause(X). {A = X;} + +// The following is a non-standard extension that allows us to declare the +// default behavior when there is a constraint conflict. +// +%type onconf {int} +%type orconf {int} +%type resolvetype {int} +onconf(A) ::= . {A = OE_Default;} +onconf(A) ::= ON CONFLICT resolvetype(X). {A = X;} +orconf(A) ::= . {A = OE_Default;} +orconf(A) ::= OR resolvetype(X). {A = X;} +resolvetype(A) ::= raisetype(X). {A = X;} +resolvetype(A) ::= IGNORE. {A = OE_Ignore;} +resolvetype(A) ::= REPLACE. {A = OE_Replace;} + +////////////////////////// The DROP TABLE ///////////////////////////////////// +// +cmd ::= DROP TABLE fullname(X). { + sqlite3DropTable(pParse, X, 0); +} + +///////////////////// The CREATE VIEW statement ///////////////////////////// +// +%ifndef SQLITE_OMIT_VIEW +cmd ::= CREATE(X) temp(T) VIEW nm(Y) dbnm(Z) AS select(S). { + sqlite3CreateView(pParse, &X, &Y, &Z, S, T); +} +cmd ::= DROP VIEW fullname(X). { + sqlite3DropTable(pParse, X, 1); +} +%endif // SQLITE_OMIT_VIEW + +//////////////////////// The SELECT statement ///////////////////////////////// +// +cmd ::= select(X). { + sqlite3Select(pParse, X, SRT_Callback, 0, 0, 0, 0, 0); + sqlite3SelectDelete(X); +} + +%type select {Select*} +%destructor select {sqlite3SelectDelete($$);} +%type oneselect {Select*} +%destructor oneselect {sqlite3SelectDelete($$);} + +select(A) ::= oneselect(X). {A = X;} +%ifndef SQLITE_OMIT_COMPOUND_SELECT +select(A) ::= select(X) multiselect_op(Y) oneselect(Z). { + if( Z ){ + Z->op = Y; + Z->pPrior = X; + } + A = Z; +} +%type multiselect_op {int} +multiselect_op(A) ::= UNION(OP). {A = @OP;} +multiselect_op(A) ::= UNION ALL. {A = TK_ALL;} +multiselect_op(A) ::= INTERSECT(OP). {A = @OP;} +multiselect_op(A) ::= EXCEPT(OP). {A = @OP;} +%endif // SQLITE_OMIT_COMPOUND_SELECT +oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y) + groupby_opt(P) having_opt(Q) orderby_opt(Z) limit_opt(L). { + A = sqlite3SelectNew(W,X,Y,P,Q,Z,D,L.pLimit,L.pOffset); +} + +// The "distinct" nonterminal is true (1) if the DISTINCT keyword is +// present and false (0) if it is not. +// +%type distinct {int} +distinct(A) ::= DISTINCT. {A = 1;} +distinct(A) ::= ALL. {A = 0;} +distinct(A) ::= . {A = 0;} + +// 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_ALL. +// +%type selcollist {ExprList*} +%destructor selcollist {sqlite3ExprListDelete($$);} +%type sclp {ExprList*} +%destructor sclp {sqlite3ExprListDelete($$);} +sclp(A) ::= selcollist(X) COMMA. {A = X;} +sclp(A) ::= . {A = 0;} +selcollist(A) ::= sclp(P) expr(X) as(Y). { + A = sqlite3ExprListAppend(P,X,Y.n?&Y:0); +} +selcollist(A) ::= sclp(P) STAR. { + A = sqlite3ExprListAppend(P, sqlite3Expr(TK_ALL, 0, 0, 0), 0); +} +selcollist(A) ::= sclp(P) nm(X) DOT STAR. { + Expr *pRight = sqlite3Expr(TK_ALL, 0, 0, 0); + Expr *pLeft = sqlite3Expr(TK_ID, 0, 0, &X); + A = sqlite3ExprListAppend(P, sqlite3Expr(TK_DOT, pLeft, pRight, 0), 0); +} + +// An option "AS <id>" phrase that can follow one of the expressions that +// define the result set, or one of the tables in the FROM clause. +// +%type as {Token} +as(X) ::= AS nm(Y). {X = Y;} +as(X) ::= ids(Y). {X = Y;} +as(X) ::= . {X.n = 0;} + + +%type seltablist {SrcList*} +%destructor seltablist {sqlite3SrcListDelete($$);} +%type stl_prefix {SrcList*} +%destructor stl_prefix {sqlite3SrcListDelete($$);} +%type from {SrcList*} +%destructor from {sqlite3SrcListDelete($$);} + +// A complete FROM clause. +// +from(A) ::= . {A = sqliteMalloc(sizeof(*A));} +from(A) ::= FROM seltablist(X). {A = 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(X) joinop(Y). { + A = X; + if( A && A->nSrc>0 ) A->a[A->nSrc-1].jointype = Y; +} +stl_prefix(A) ::= . {A = 0;} +seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) on_opt(N) using_opt(U). { + A = sqlite3SrcListAppend(X,&Y,&D); + if( Z.n ) sqlite3SrcListAddAlias(A,&Z); + if( N ){ + if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pOn = N; } + else { sqlite3ExprDelete(N); } + } + if( U ){ + if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pUsing = U; } + else { sqlite3IdListDelete(U); } + } +} +%ifndef SQLITE_OMIT_SUBQUERY + seltablist(A) ::= stl_prefix(X) LP seltablist_paren(S) RP + as(Z) on_opt(N) using_opt(U). { + A = sqlite3SrcListAppend(X,0,0); + A->a[A->nSrc-1].pSelect = S; + if( Z.n ) sqlite3SrcListAddAlias(A,&Z); + if( N ){ + if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pOn = N; } + else { sqlite3ExprDelete(N); } + } + if( U ){ + if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pUsing = U; } + else { sqlite3IdListDelete(U); } + } + } + + // A seltablist_paren nonterminal represents anything in a FROM that + // is contained inside parentheses. This can be either a subquery or + // a grouping of table and subqueries. + // + %type seltablist_paren {Select*} + %destructor seltablist_paren {sqlite3SelectDelete($$);} + seltablist_paren(A) ::= select(S). {A = S;} + seltablist_paren(A) ::= seltablist(F). { + A = sqlite3SelectNew(0,F,0,0,0,0,0,0,0); + } +%endif // SQLITE_OMIT_SUBQUERY + +%type dbnm {Token} +dbnm(A) ::= . {A.z=0; A.n=0;} +dbnm(A) ::= DOT nm(X). {A = X;} + +%type fullname {SrcList*} +%destructor fullname {sqlite3SrcListDelete($$);} +fullname(A) ::= nm(X) dbnm(Y). {A = sqlite3SrcListAppend(0,&X,&Y);} + +%type joinop {int} +%type joinop2 {int} +joinop(X) ::= COMMA. { X = JT_INNER; } +joinop(X) ::= JOIN. { X = JT_INNER; } +joinop(X) ::= JOIN_KW(A) JOIN. { X = sqlite3JoinType(pParse,&A,0,0); } +joinop(X) ::= JOIN_KW(A) nm(B) JOIN. { X = sqlite3JoinType(pParse,&A,&B,0); } +joinop(X) ::= JOIN_KW(A) nm(B) nm(C) JOIN. + { X = sqlite3JoinType(pParse,&A,&B,&C); } + +%type on_opt {Expr*} +%destructor on_opt {sqlite3ExprDelete($$);} +on_opt(N) ::= ON expr(E). {N = E;} +on_opt(N) ::= . {N = 0;} + +%type using_opt {IdList*} +%destructor using_opt {sqlite3IdListDelete($$);} +using_opt(U) ::= USING LP inscollist(L) RP. {U = L;} +using_opt(U) ::= . {U = 0;} + + +%type orderby_opt {ExprList*} +%destructor orderby_opt {sqlite3ExprListDelete($$);} +%type sortlist {ExprList*} +%destructor sortlist {sqlite3ExprListDelete($$);} +%type sortitem {Expr*} +%destructor sortitem {sqlite3ExprDelete($$);} + +orderby_opt(A) ::= . {A = 0;} +orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;} +sortlist(A) ::= sortlist(X) COMMA sortitem(Y) collate(C) sortorder(Z). { + A = sqlite3ExprListAppend(X,Y,C.n>0?&C:0); + if( A ) A->a[A->nExpr-1].sortOrder = Z; +} +sortlist(A) ::= sortitem(Y) collate(C) sortorder(Z). { + A = sqlite3ExprListAppend(0,Y,C.n>0?&C:0); + if( A && A->a ) A->a[0].sortOrder = Z; +} +sortitem(A) ::= expr(X). {A = X;} + +%type sortorder {int} +%type collate {Token} + +sortorder(A) ::= ASC. {A = SQLITE_SO_ASC;} +sortorder(A) ::= DESC. {A = SQLITE_SO_DESC;} +sortorder(A) ::= . {A = SQLITE_SO_ASC;} +collate(C) ::= . {C.z = 0; C.n = 0;} +collate(C) ::= COLLATE id(X). {C = X;} + +%type groupby_opt {ExprList*} +%destructor groupby_opt {sqlite3ExprListDelete($$);} +groupby_opt(A) ::= . {A = 0;} +groupby_opt(A) ::= GROUP BY exprlist(X). {A = X;} + +%type having_opt {Expr*} +%destructor having_opt {sqlite3ExprDelete($$);} +having_opt(A) ::= . {A = 0;} +having_opt(A) ::= HAVING expr(X). {A = X;} + +%type limit_opt {struct LimitVal} +%destructor limit_opt { + sqlite3ExprDelete($$.pLimit); + sqlite3ExprDelete($$.pOffset); +} +limit_opt(A) ::= . {A.pLimit = 0; A.pOffset = 0;} +limit_opt(A) ::= LIMIT expr(X). {A.pLimit = X; A.pOffset = 0;} +limit_opt(A) ::= LIMIT expr(X) OFFSET expr(Y). + {A.pLimit = X; A.pOffset = Y;} +limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y). + {A.pOffset = X; A.pLimit = Y;} + +/////////////////////////// The DELETE statement ///////////////////////////// +// +cmd ::= DELETE FROM fullname(X) where_opt(Y). {sqlite3DeleteFrom(pParse,X,Y);} + +%type where_opt {Expr*} +%destructor where_opt {sqlite3ExprDelete($$);} + +where_opt(A) ::= . {A = 0;} +where_opt(A) ::= WHERE expr(X). {A = X;} + +%type setlist {ExprList*} +%destructor setlist {sqlite3ExprListDelete($$);} + +////////////////////////// The UPDATE command //////////////////////////////// +// +cmd ::= UPDATE orconf(R) fullname(X) SET setlist(Y) where_opt(Z). + {sqlite3Update(pParse,X,Y,Z,R);} + +setlist(A) ::= setlist(Z) COMMA nm(X) EQ expr(Y). + {A = sqlite3ExprListAppend(Z,Y,&X);} +setlist(A) ::= nm(X) EQ expr(Y). {A = sqlite3ExprListAppend(0,Y,&X);} + +////////////////////////// The INSERT command ///////////////////////////////// +// +cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) + VALUES LP itemlist(Y) RP. + {sqlite3Insert(pParse, X, Y, 0, F, R);} +cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S). + {sqlite3Insert(pParse, X, 0, S, F, R);} + +%type insert_cmd {int} +insert_cmd(A) ::= INSERT orconf(R). {A = R;} +insert_cmd(A) ::= REPLACE. {A = OE_Replace;} + + +%type itemlist {ExprList*} +%destructor itemlist {sqlite3ExprListDelete($$);} + +itemlist(A) ::= itemlist(X) COMMA expr(Y). {A = sqlite3ExprListAppend(X,Y,0);} +itemlist(A) ::= expr(X). {A = sqlite3ExprListAppend(0,X,0);} + +%type inscollist_opt {IdList*} +%destructor inscollist_opt {sqlite3IdListDelete($$);} +%type inscollist {IdList*} +%destructor inscollist {sqlite3IdListDelete($$);} + +inscollist_opt(A) ::= . {A = 0;} +inscollist_opt(A) ::= LP inscollist(X) RP. {A = X;} +inscollist(A) ::= inscollist(X) COMMA nm(Y). {A = sqlite3IdListAppend(X,&Y);} +inscollist(A) ::= nm(Y). {A = sqlite3IdListAppend(0,&Y);} + +/////////////////////////// Expression Processing ///////////////////////////// +// + +%type expr {Expr*} +%destructor expr {sqlite3ExprDelete($$);} +%type term {Expr*} +%destructor term {sqlite3ExprDelete($$);} + +expr(A) ::= term(X). {A = X;} +expr(A) ::= LP(B) expr(X) RP(E). {A = X; sqlite3ExprSpan(A,&B,&E); } +term(A) ::= NULL(X). {A = sqlite3Expr(@X, 0, 0, &X);} +expr(A) ::= ID(X). {A = sqlite3Expr(TK_ID, 0, 0, &X);} +expr(A) ::= JOIN_KW(X). {A = sqlite3Expr(TK_ID, 0, 0, &X);} +expr(A) ::= nm(X) DOT nm(Y). { + Expr *temp1 = sqlite3Expr(TK_ID, 0, 0, &X); + Expr *temp2 = sqlite3Expr(TK_ID, 0, 0, &Y); + A = sqlite3Expr(TK_DOT, temp1, temp2, 0); +} +expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). { + Expr *temp1 = sqlite3Expr(TK_ID, 0, 0, &X); + Expr *temp2 = sqlite3Expr(TK_ID, 0, 0, &Y); + Expr *temp3 = sqlite3Expr(TK_ID, 0, 0, &Z); + Expr *temp4 = sqlite3Expr(TK_DOT, temp2, temp3, 0); + A = sqlite3Expr(TK_DOT, temp1, temp4, 0); +} +term(A) ::= INTEGER(X). {A = sqlite3Expr(@X, 0, 0, &X);} +term(A) ::= FLOAT(X). {A = sqlite3Expr(@X, 0, 0, &X);} +term(A) ::= STRING(X). {A = sqlite3Expr(@X, 0, 0, &X);} +term(A) ::= BLOB(X). {A = sqlite3Expr(@X, 0, 0, &X);} +expr(A) ::= REGISTER(X). {A = sqlite3RegisterExpr(pParse, &X);} +expr(A) ::= VARIABLE(X). { + Token *pToken = &X; + Expr *pExpr = A = sqlite3Expr(TK_VARIABLE, 0, 0, pToken); + sqlite3ExprAssignVarNumber(pParse, pExpr); +} +expr(A) ::= ID(X) LP exprlist(Y) RP(E). { + A = sqlite3ExprFunction(Y, &X); + sqlite3ExprSpan(A,&X,&E); +} +expr(A) ::= ID(X) LP STAR RP(E). { + A = sqlite3ExprFunction(0, &X); + sqlite3ExprSpan(A,&X,&E); +} +term(A) ::= CTIME(OP). {A = sqlite3Expr(@OP,0,0,0);} +term(A) ::= CDATE(OP). {A = sqlite3Expr(@OP,0,0,0);} +term(A) ::= CTIMESTAMP(OP). {A = sqlite3Expr(@OP,0,0,0);} +expr(A) ::= expr(X) AND(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) OR(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) LT(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) GT(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) LE(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) GE(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) NE(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) EQ(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) BITAND(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) BITOR(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) LSHIFT(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) RSHIFT(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) PLUS(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) MINUS(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) STAR(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) SLASH(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) REM(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) CONCAT(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +%type likeop {struct LikeOp} +likeop(A) ::= LIKE. {A.opcode = TK_LIKE; A.not = 0;} +likeop(A) ::= GLOB. {A.opcode = TK_GLOB; A.not = 0;} +likeop(A) ::= NOT LIKE. {A.opcode = TK_LIKE; A.not = 1;} +likeop(A) ::= NOT GLOB. {A.opcode = TK_GLOB; A.not = 1;} +%type escape {Expr*} +escape(X) ::= ESCAPE expr(A). [ESCAPE] {X = A;} +escape(X) ::= . [ESCAPE] {X = 0;} +expr(A) ::= expr(X) likeop(OP) expr(Y) escape(E). [LIKE] { + ExprList *pList = sqlite3ExprListAppend(0, Y, 0); + pList = sqlite3ExprListAppend(pList, X, 0); + if( E ){ + pList = sqlite3ExprListAppend(pList, E, 0); + } + A = sqlite3ExprFunction(pList, 0); + if( A ) A->op = OP.opcode; + if( OP.not ) A = sqlite3Expr(TK_NOT, A, 0, 0); + sqlite3ExprSpan(A, &X->span, &Y->span); +} + +expr(A) ::= expr(X) ISNULL(E). { + A = sqlite3Expr(TK_ISNULL, X, 0, 0); + sqlite3ExprSpan(A,&X->span,&E); +} +expr(A) ::= expr(X) IS NULL(E). { + A = sqlite3Expr(TK_ISNULL, X, 0, 0); + sqlite3ExprSpan(A,&X->span,&E); +} +expr(A) ::= expr(X) NOTNULL(E). { + A = sqlite3Expr(TK_NOTNULL, X, 0, 0); + sqlite3ExprSpan(A,&X->span,&E); +} +expr(A) ::= expr(X) NOT NULL(E). { + A = sqlite3Expr(TK_NOTNULL, X, 0, 0); + sqlite3ExprSpan(A,&X->span,&E); +} +expr(A) ::= expr(X) IS NOT NULL(E). { + A = sqlite3Expr(TK_NOTNULL, X, 0, 0); + sqlite3ExprSpan(A,&X->span,&E); +} +expr(A) ::= NOT(B) expr(X). { + A = sqlite3Expr(@B, X, 0, 0); + sqlite3ExprSpan(A,&B,&X->span); +} +expr(A) ::= BITNOT(B) expr(X). { + A = sqlite3Expr(@B, X, 0, 0); + sqlite3ExprSpan(A,&B,&X->span); +} +expr(A) ::= MINUS(B) expr(X). [UMINUS] { + A = sqlite3Expr(TK_UMINUS, X, 0, 0); + sqlite3ExprSpan(A,&B,&X->span); +} +expr(A) ::= PLUS(B) expr(X). [UPLUS] { + A = sqlite3Expr(TK_UPLUS, X, 0, 0); + sqlite3ExprSpan(A,&B,&X->span); +} +%type between_op {int} +between_op(A) ::= BETWEEN. {A = 0;} +between_op(A) ::= NOT BETWEEN. {A = 1;} +expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { + ExprList *pList = sqlite3ExprListAppend(0, X, 0); + pList = sqlite3ExprListAppend(pList, Y, 0); + A = sqlite3Expr(TK_BETWEEN, W, 0, 0); + if( A ) A->pList = pList; + if( N ) A = sqlite3Expr(TK_NOT, A, 0, 0); + sqlite3ExprSpan(A,&W->span,&Y->span); +} +%ifndef SQLITE_OMIT_SUBQUERY + %type in_op {int} + in_op(A) ::= IN. {A = 0;} + in_op(A) ::= NOT IN. {A = 1;} + expr(A) ::= expr(X) in_op(N) LP exprlist(Y) RP(E). [IN] { + A = sqlite3Expr(TK_IN, X, 0, 0); + if( A ){ + A->pList = Y; + }else{ + sqlite3ExprListDelete(Y); + } + if( N ) A = sqlite3Expr(TK_NOT, A, 0, 0); + sqlite3ExprSpan(A,&X->span,&E); + } + expr(A) ::= LP(B) select(X) RP(E). { + A = sqlite3Expr(TK_SELECT, 0, 0, 0); + if( A ) A->pSelect = X; + if( !A ) sqlite3SelectDelete(X); + sqlite3ExprSpan(A,&B,&E); + } + expr(A) ::= expr(X) in_op(N) LP select(Y) RP(E). [IN] { + A = sqlite3Expr(TK_IN, X, 0, 0); + if( A ) A->pSelect = Y; + if( !A ) sqlite3SelectDelete(Y); + if( N ) A = sqlite3Expr(TK_NOT, A, 0, 0); + sqlite3ExprSpan(A,&X->span,&E); + } + expr(A) ::= expr(X) in_op(N) nm(Y) dbnm(Z). [IN] { + SrcList *pSrc = sqlite3SrcListAppend(0,&Y,&Z); + A = sqlite3Expr(TK_IN, X, 0, 0); + if( A ) A->pSelect = sqlite3SelectNew(0,pSrc,0,0,0,0,0,0,0); + if( N ) A = sqlite3Expr(TK_NOT, A, 0, 0); + sqlite3ExprSpan(A,&X->span,Z.z?&Z:&Y); + } + expr(A) ::= EXISTS(B) LP select(Y) RP(E). { + Expr *p = A = sqlite3Expr(TK_EXISTS, 0, 0, 0); + if( p ){ + p->pSelect = Y; + sqlite3ExprSpan(p,&B,&E); + } + if( !p ) sqlite3SelectDelete(Y); + } +%endif // SQLITE_OMIT_SUBQUERY + +/* CASE expressions */ +expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). { + A = sqlite3Expr(TK_CASE, X, Z, 0); + if( A ) A->pList = Y; + sqlite3ExprSpan(A, &C, &E); +} +%type case_exprlist {ExprList*} +%destructor case_exprlist {sqlite3ExprListDelete($$);} +case_exprlist(A) ::= case_exprlist(X) WHEN expr(Y) THEN expr(Z). { + A = sqlite3ExprListAppend(X, Y, 0); + A = sqlite3ExprListAppend(A, Z, 0); +} +case_exprlist(A) ::= WHEN expr(Y) THEN expr(Z). { + A = sqlite3ExprListAppend(0, Y, 0); + A = sqlite3ExprListAppend(A, Z, 0); +} +%type case_else {Expr*} +case_else(A) ::= ELSE expr(X). {A = X;} +case_else(A) ::= . {A = 0;} +%type case_operand {Expr*} +case_operand(A) ::= expr(X). {A = X;} +case_operand(A) ::= . {A = 0;} + +%type exprlist {ExprList*} +%destructor exprlist {sqlite3ExprListDelete($$);} +%type expritem {Expr*} +%destructor expritem {sqlite3ExprDelete($$);} + +exprlist(A) ::= exprlist(X) COMMA expritem(Y). + {A = sqlite3ExprListAppend(X,Y,0);} +exprlist(A) ::= expritem(X). {A = sqlite3ExprListAppend(0,X,0);} +expritem(A) ::= expr(X). {A = X;} +expritem(A) ::= . {A = 0;} + +///////////////////////////// The CREATE INDEX command /////////////////////// +// +cmd ::= CREATE(S) uniqueflag(U) INDEX nm(X) dbnm(D) + ON nm(Y) LP idxlist(Z) RP(E) onconf(R). { + if( U!=OE_None ) U = R; + if( U==OE_Default) U = OE_Abort; + sqlite3CreateIndex(pParse, &X, &D, sqlite3SrcListAppend(0,&Y,0),Z,U, &S, &E); +} + +%type uniqueflag {int} +uniqueflag(A) ::= UNIQUE. {A = OE_Abort;} +uniqueflag(A) ::= . {A = OE_None;} + +%type idxlist {ExprList*} +%destructor idxlist {sqlite3ExprListDelete($$);} +%type idxlist_opt {ExprList*} +%destructor idxlist_opt {sqlite3ExprListDelete($$);} +%type idxitem {Token} + +idxlist_opt(A) ::= . {A = 0;} +idxlist_opt(A) ::= LP idxlist(X) RP. {A = X;} +idxlist(A) ::= idxlist(X) COMMA idxitem(Y) collate(C) sortorder. { + Expr *p = 0; + if( C.n>0 ){ + p = sqlite3Expr(TK_COLUMN, 0, 0, 0); + if( p ) p->pColl = sqlite3LocateCollSeq(pParse, C.z, C.n); + } + A = sqlite3ExprListAppend(X, p, &Y); +} +idxlist(A) ::= idxitem(Y) collate(C) sortorder. { + Expr *p = 0; + if( C.n>0 ){ + p = sqlite3Expr(TK_COLUMN, 0, 0, 0); + if( p ) p->pColl = sqlite3LocateCollSeq(pParse, C.z, C.n); + } + A = sqlite3ExprListAppend(0, p, &Y); +} +idxitem(A) ::= nm(X). {A = X;} + + +///////////////////////////// The DROP INDEX command ///////////////////////// +// +cmd ::= DROP INDEX fullname(X). {sqlite3DropIndex(pParse, X);} + +///////////////////////////// The VACUUM command ///////////////////////////// +// +cmd ::= VACUUM. {sqlite3Vacuum(pParse,0);} +cmd ::= VACUUM nm. {sqlite3Vacuum(pParse,0);} + +///////////////////////////// The PRAGMA command ///////////////////////////// +// +%ifndef SQLITE_OMIT_PRAGMA +cmd ::= PRAGMA nm(X) dbnm(Z) EQ nm(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);} +cmd ::= PRAGMA nm(X) dbnm(Z) EQ ON(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);} +cmd ::= PRAGMA nm(X) dbnm(Z) EQ plus_num(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);} +cmd ::= PRAGMA nm(X) dbnm(Z) EQ minus_num(Y). { + sqlite3Pragma(pParse,&X,&Z,&Y,1); +} +cmd ::= PRAGMA nm(X) dbnm(Z) LP nm(Y) RP. {sqlite3Pragma(pParse,&X,&Z,&Y,0);} +cmd ::= PRAGMA nm(X) dbnm(Z). {sqlite3Pragma(pParse,&X,&Z,0,0);} +%endif // SQLITE_OMIT_PRAGMA +plus_num(A) ::= plus_opt number(X). {A = X;} +minus_num(A) ::= MINUS number(X). {A = X;} +number(A) ::= INTEGER(X). {A = X;} +number(A) ::= FLOAT(X). {A = X;} +plus_opt ::= PLUS. +plus_opt ::= . + +//////////////////////////// The CREATE TRIGGER command ///////////////////// + +%ifndef SQLITE_OMIT_TRIGGER + +cmd ::= CREATE trigger_decl(A) BEGIN trigger_cmd_list(S) END(Z). { + Token all; + all.z = A.z; + all.n = (Z.z - A.z) + Z.n; + sqlite3FinishTrigger(pParse, S, &all); +} + +trigger_decl(A) ::= temp(T) TRIGGER nm(B) dbnm(Z) trigger_time(C) + trigger_event(D) + ON fullname(E) foreach_clause(F) when_clause(G). { + sqlite3BeginTrigger(pParse, &B, &Z, C, D.a, D.b, E, F, G, T); + A = (Z.n==0?B:Z); +} + +%type trigger_time {int} +trigger_time(A) ::= BEFORE. { A = TK_BEFORE; } +trigger_time(A) ::= AFTER. { A = TK_AFTER; } +trigger_time(A) ::= INSTEAD OF. { A = TK_INSTEAD;} +trigger_time(A) ::= . { A = TK_BEFORE; } + +%type trigger_event {struct TrigEvent} +%destructor trigger_event {sqlite3IdListDelete($$.b);} +trigger_event(A) ::= DELETE(OP). {A.a = @OP; A.b = 0;} +trigger_event(A) ::= INSERT(OP). {A.a = @OP; A.b = 0;} +trigger_event(A) ::= UPDATE(OP). {A.a = @OP; A.b = 0;} +trigger_event(A) ::= UPDATE OF inscollist(X). {A.a = TK_UPDATE; A.b = X;} + +%type foreach_clause {int} +foreach_clause(A) ::= . { A = TK_ROW; } +foreach_clause(A) ::= FOR EACH ROW. { A = TK_ROW; } +foreach_clause(A) ::= FOR EACH STATEMENT. { A = TK_STATEMENT; } + +%type when_clause {Expr*} +when_clause(A) ::= . { A = 0; } +when_clause(A) ::= WHEN expr(X). { A = X; } + +%type trigger_cmd_list {TriggerStep*} +%destructor trigger_cmd_list {sqlite3DeleteTriggerStep($$);} +trigger_cmd_list(A) ::= trigger_cmd(X) SEMI trigger_cmd_list(Y). { + X->pNext = Y; + A = X; +} +trigger_cmd_list(A) ::= . { A = 0; } + +%type trigger_cmd {TriggerStep*} +%destructor trigger_cmd {sqlite3DeleteTriggerStep($$);} +// UPDATE +trigger_cmd(A) ::= UPDATE orconf(R) nm(X) SET setlist(Y) where_opt(Z). + { A = sqlite3TriggerUpdateStep(&X, Y, Z, R); } + +// INSERT +trigger_cmd(A) ::= insert_cmd(R) INTO nm(X) inscollist_opt(F) + VALUES LP itemlist(Y) RP. + {A = sqlite3TriggerInsertStep(&X, F, Y, 0, R);} + +trigger_cmd(A) ::= insert_cmd(R) INTO nm(X) inscollist_opt(F) select(S). + {A = sqlite3TriggerInsertStep(&X, F, 0, S, R);} + +// DELETE +trigger_cmd(A) ::= DELETE FROM nm(X) where_opt(Y). + {A = sqlite3TriggerDeleteStep(&X, Y);} + +// SELECT +trigger_cmd(A) ::= select(X). {A = sqlite3TriggerSelectStep(X); } + +// The special RAISE expression that may occur in trigger programs +expr(A) ::= RAISE(X) LP IGNORE RP(Y). { + A = sqlite3Expr(TK_RAISE, 0, 0, 0); + A->iColumn = OE_Ignore; + sqlite3ExprSpan(A, &X, &Y); +} +expr(A) ::= RAISE(X) LP raisetype(T) COMMA nm(Z) RP(Y). { + A = sqlite3Expr(TK_RAISE, 0, 0, &Z); + A->iColumn = T; + sqlite3ExprSpan(A, &X, &Y); +} +%endif // !SQLITE_OMIT_TRIGGER + +%type raisetype {int} +raisetype(A) ::= ROLLBACK. {A = OE_Rollback;} +raisetype(A) ::= ABORT. {A = OE_Abort;} +raisetype(A) ::= FAIL. {A = OE_Fail;} + + +//////////////////////// DROP TRIGGER statement ////////////////////////////// +%ifndef SQLITE_OMIT_TRIGGER +cmd ::= DROP TRIGGER fullname(X). { + sqlite3DropTrigger(pParse,X); +} +%endif // !SQLITE_OMIT_TRIGGER + +//////////////////////// ATTACH DATABASE file AS name ///////////////////////// +cmd ::= ATTACH database_kw_opt ids(F) AS nm(D) key_opt(K). { + sqlite3Attach(pParse, &F, &D, K.type, &K.key); +} +%type key_opt {struct AttachKey} +key_opt(A) ::= . { A.type = 0; } +key_opt(A) ::= KEY ids(X). { A.type=1; A.key = X; } +key_opt(A) ::= KEY BLOB(X). { A.type=2; A.key = X; } + +database_kw_opt ::= DATABASE. +database_kw_opt ::= . + +//////////////////////// DETACH DATABASE name ///////////////////////////////// +cmd ::= DETACH database_kw_opt nm(D). { + sqlite3Detach(pParse, &D); +} + +////////////////////////// REINDEX collation ////////////////////////////////// +%ifndef SQLITE_OMIT_REINDEX +cmd ::= REINDEX. {sqlite3Reindex(pParse, 0, 0);} +cmd ::= REINDEX nm(X) dbnm(Y). {sqlite3Reindex(pParse, &X, &Y);} +%endif + +//////////////////////// ALTER TABLE table ... //////////////////////////////// +%ifndef SQLITE_OMIT_ALTERTABLE +cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). { + sqlite3AlterRenameTable(pParse,X,&Z); +} +cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column(Y). { + sqlite3AlterFinishAddColumn(pParse, &Y); +} +add_column_fullname ::= fullname(X). { + sqlite3AlterBeginAddColumn(pParse, X); +} +kwcolumn_opt ::= . +kwcolumn_opt ::= COLUMNKW. +%endif diff --git a/tcllib/examples/page/sql.log b/tcllib/examples/page/sql.log new file mode 100644 index 0000000..644931b --- /dev/null +++ b/tcllib/examples/page/sql.log @@ -0,0 +1,18 @@ +Reading grammar from file ../parse.y [Lemon specification] +.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|.........|.........|.........|.........|.........*.........|.........|.........|.........|.........|...... +Stats ME. Microseconds: 27550504 + . Seconds: 27.550504 + . Characters: 35565 + . Microsec/Char: 774.652158021 + . Char/Seconds: 1290.90197406 +<Name: sqlite3Parser +*TD +Startsymbol: input +REALf> + +Read 35565 characters +Writing to file examples/pg/sql.peg [Canonical PEG] + +real 1m0.781s +user 0m58.870s +sys 0m0.140s diff --git a/tcllib/examples/page/sql.peg b/tcllib/examples/page/sql.peg new file mode 100644 index 0000000..e5f11a5 --- /dev/null +++ b/tcllib/examples/page/sql.peg @@ -0,0 +1,643 @@ +# -*- text -*- +## Parsing Expression Grammar 'sqlite3Parser'. +## Layouted by the PG backend 'PEGwriter'. + +PEG sqlite3Parser (input) + +leaf: ABORT <- 'A' 'B' 'O' 'R' 'T' ; + +leaf: ADD <- 'A' 'D' 'D' ; + + add_column_fullname <- fullname ; + +leaf: AFTER <- 'A' 'F' 'T' 'E' 'R' ; + +leaf: ALL <- 'A' 'L' 'L' ; + +leaf: ALTER <- 'A' 'L' 'T' 'E' 'R' ; + +leaf: AND <- 'A' 'N' 'D' ; + +leaf: AS <- 'A' 'S' ; + + as <- ( AS nm + / ids)? ; + +leaf: ASC <- 'A' 'S' 'C' ; + +leaf: ATTACH <- 'A' 'T' 'T' 'A' 'C' 'H' ; + + autoinc <- AUTOINCR? ; + +leaf: AUTOINCR <- 'A' 'U' 'T' 'O' 'I' 'N' 'C' 'R' ; + +leaf: BEFORE <- 'B' 'E' 'F' 'O' 'R' 'E' ; + +leaf: BEGIN <- 'B' 'E' 'G' 'I' 'N' ; + +leaf: BETWEEN <- 'B' 'E' 'T' 'W' 'E' 'E' 'N' ; + + between_op <- BETWEEN + / NOT BETWEEN ; + +leaf: BITAND <- 'B' 'I' 'T' 'A' 'N' 'D' ; + +leaf: BITNOT <- 'B' 'I' 'T' 'N' 'O' 'T' ; + +leaf: BITOR <- 'B' 'I' 'T' 'O' 'R' ; + +leaf: BLOB <- 'B' 'L' 'O' 'B' ; + +leaf: BY <- 'B' 'Y' ; + + carg <- CONSTRAINT nm ccons + / ccons + / DEFAULT term + / DEFAULT PLUS term + / DEFAULT MINUS term + / DEFAULT id ; + + carglist <- (carglist carg)? ; + +leaf: CASCADE <- 'C' 'A' 'S' 'C' 'A' 'D' 'E' ; + +leaf: CASE <- 'C' 'A' 'S' 'E' ; + + case_else <- (ELSE expr)? ; + + case_exprlist <- case_exprlist WHEN expr THEN expr + / WHEN expr THEN expr ; + + case_operand <- expr? ; + + ccons <- NULL onconf + / NOT NULL onconf + / PRIMARY KEY sortorder onconf autoinc + / UNIQUE onconf + / CHECK LP expr RP onconf + / REFERENCES nm idxlist_opt refargs + / defer_subclause + / COLLATE id ; + +leaf: CDATE <- 'C' 'D' 'A' 'T' 'E' ; + +leaf: CHECK <- 'C' 'H' 'E' 'C' 'K' ; + + cmd <- BEGIN transtype trans_opt + / COMMIT trans_opt + / END trans_opt + / ROLLBACK trans_opt + / create_table create_table_args + / DROP TABLE fullname + / CREATE temp VIEW nm dbnm AS select + / DROP VIEW fullname + / select + / DELETE FROM fullname where_opt + / UPDATE orconf fullname SET setlist where_opt + / insert_cmd INTO fullname inscollist_opt VALUES + LP itemlist RP + / insert_cmd INTO fullname inscollist_opt select + / CREATE uniqueflag INDEX nm dbnm ON nm LP idxlist + RP onconf + / DROP INDEX fullname + / VACUUM + / VACUUM nm + / PRAGMA nm dbnm EQ nm + / PRAGMA nm dbnm EQ ON + / PRAGMA nm dbnm EQ plus_num + / PRAGMA nm dbnm EQ minus_num + / PRAGMA nm dbnm LP nm RP + / PRAGMA nm dbnm + / CREATE trigger_decl BEGIN trigger_cmd_list END + / DROP TRIGGER fullname + / ATTACH database_kw_opt ids AS nm key_opt + / DETACH database_kw_opt nm + / REINDEX + / REINDEX nm dbnm + / ALTER TABLE fullname RENAME TO nm + / ALTER TABLE add_column_fullname ADD kwcolumn_opt + column ; + + cmdlist <- cmdlist ecmd + / ecmd ; + + cmdx <- cmd ; + +leaf: COLLATE <- 'C' 'O' 'L' 'L' 'A' 'T' 'E' ; + + collate <- (COLLATE id)? ; + + column <- columnid type carglist ; + + columnid <- nm ; + +leaf: COLUMNKW <- 'C' 'O' 'L' 'U' 'M' 'N' 'K' 'W' ; + + columnlist <- columnlist COMMA column + / column ; + +leaf: COMMA <- 'C' 'O' 'M' 'M' 'A' ; + +leaf: COMMIT <- 'C' 'O' 'M' 'M' 'I' 'T' ; + +leaf: CONCAT <- 'C' 'O' 'N' 'C' 'A' 'T' ; + +leaf: CONFLICT <- 'C' 'O' 'N' 'F' 'L' 'I' 'C' 'T' ; + + conslist <- conslist COMMA tcons + / conslist tcons + / tcons ; + + conslist_opt <- (COMMA conslist)? ; + +leaf: CONSTRAINT <- 'C' 'O' 'N' 'S' 'T' 'R' 'A' 'I' 'N' 'T' ; + +leaf: CREATE <- 'C' 'R' 'E' 'A' 'T' 'E' ; + + create_table <- CREATE temp TABLE nm dbnm ; + + create_table_args <- LP columnlist conslist_opt RP + / AS select ; + +leaf: CTIME <- 'C' 'T' 'I' 'M' 'E' ; + +leaf: CTIMESTAMP <- 'C' 'T' 'I' 'M' 'E' 'S' 'T' 'A' 'M' 'P' ; + +leaf: DATABASE <- 'D' 'A' 'T' 'A' 'B' 'A' 'S' 'E' ; + + database_kw_opt <- DATABASE? ; + + dbnm <- (DOT nm)? ; + +leaf: DEFAULT <- 'D' 'E' 'F' 'A' 'U' 'L' 'T' ; + + defer_subclause <- NOT DEFERRABLE init_deferred_pred_opt + / DEFERRABLE init_deferred_pred_opt ; + + defer_subclause_opt <- defer_subclause? ; + +leaf: DEFERRABLE <- 'D' 'E' 'F' 'E' 'R' 'R' 'A' 'B' 'L' 'E' ; + +leaf: DEFERRED <- 'D' 'E' 'F' 'E' 'R' 'R' 'E' 'D' ; + +leaf: DELETE <- 'D' 'E' 'L' 'E' 'T' 'E' ; + +leaf: DESC <- 'D' 'E' 'S' 'C' ; + +leaf: DETACH <- 'D' 'E' 'T' 'A' 'C' 'H' ; + +leaf: DISTINCT <- 'D' 'I' 'S' 'T' 'I' 'N' 'C' 'T' ; + + distinct <- ( DISTINCT + / ALL)? ; + +leaf: DOT <- 'D' 'O' 'T' ; + +leaf: DROP <- 'D' 'R' 'O' 'P' ; + +leaf: EACH <- 'E' 'A' 'C' 'H' ; + + ecmd <- SEMI + / explain cmdx SEMI ; + +leaf: ELSE <- 'E' 'L' 'S' 'E' ; + +leaf: END <- 'E' 'N' 'D' ; + +leaf: EQ <- 'E' 'Q' ; + +leaf: ESCAPE <- 'E' 'S' 'C' 'A' 'P' 'E' ; + + escape <- (ESCAPE expr)? ; + +leaf: EXCEPT <- 'E' 'X' 'C' 'E' 'P' 'T' ; + +leaf: EXCLUSIVE <- 'E' 'X' 'C' 'L' 'U' 'S' 'I' 'V' 'E' ; + +leaf: EXISTS <- 'E' 'X' 'I' 'S' 'T' 'S' ; + +leaf: EXPLAIN <- 'E' 'X' 'P' 'L' 'A' 'I' 'N' ; + + explain <- EXPLAIN? ; + + expr <- term + / LP expr RP + / ID + / JOIN_KW + / nm DOT nm + / nm DOT nm DOT nm + / REGISTER + / VARIABLE + / ID LP exprlist RP + / ID LP STAR RP + / expr AND expr + / expr OR expr + / expr LT expr + / expr GT expr + / expr LE expr + / expr GE expr + / expr NE expr + / expr EQ expr + / expr BITAND expr + / expr BITOR expr + / expr LSHIFT expr + / expr RSHIFT expr + / expr PLUS expr + / expr MINUS expr + / expr STAR expr + / expr SLASH expr + / expr REM expr + / expr CONCAT expr + / expr likeop expr escape + / expr ISNULL + / expr IS NULL + / expr NOTNULL + / expr NOT NULL + / expr IS NOT NULL + / NOT expr + / BITNOT expr + / MINUS expr + / PLUS expr + / expr between_op expr AND expr + / expr in_op LP exprlist RP + / LP select RP + / expr in_op LP select RP + / expr in_op nm dbnm + / EXISTS LP select RP + / CASE case_operand case_exprlist case_else END + / RAISE LP IGNORE RP + / RAISE LP raisetype COMMA nm RP ; + + expritem <- expr? ; + + exprlist <- exprlist COMMA expritem + / expritem ; + +leaf: FAIL <- 'F' 'A' 'I' 'L' ; + +leaf: FLOAT <- 'F' 'L' 'O' 'A' 'T' ; + +leaf: FOR <- 'F' 'O' 'R' ; + + foreach_clause <- ( FOR EACH ROW + / FOR EACH STATEMENT)? ; + +leaf: FOREIGN <- 'F' 'O' 'R' 'E' 'I' 'G' 'N' ; + +leaf: FROM <- 'F' 'R' 'O' 'M' ; + + from <- (FROM seltablist)? ; + + fullname <- nm dbnm ; + +leaf: GE <- 'G' 'E' ; + +leaf: GLOB <- 'G' 'L' 'O' 'B' ; + +leaf: GROUP <- 'G' 'R' 'O' 'U' 'P' ; + + groupby_opt <- (GROUP BY exprlist)? ; + +leaf: GT <- 'G' 'T' ; + +leaf: HAVING <- 'H' 'A' 'V' 'I' 'N' 'G' ; + + having_opt <- (HAVING expr)? ; + +leaf: ID <- 'I' 'D' ; + + id <- ID ; + + ids <- ID + / STRING ; + + idxitem <- nm ; + + idxlist <- idxlist COMMA idxitem collate sortorder + / idxitem collate sortorder ; + + idxlist_opt <- (LP idxlist RP)? ; + +leaf: IGNORE <- 'I' 'G' 'N' 'O' 'R' 'E' ; + +leaf: IMMEDIATE <- 'I' 'M' 'M' 'E' 'D' 'I' 'A' 'T' 'E' ; + +leaf: IN <- 'I' 'N' ; + + in_op <- IN + / NOT IN ; + +leaf: INDEX <- 'I' 'N' 'D' 'E' 'X' ; + + init_deferred_pred_opt <- ( INITIALLY DEFERRED + / INITIALLY IMMEDIATE)? ; + +leaf: INITIALLY <- 'I' 'N' 'I' 'T' 'I' 'A' 'L' 'L' 'Y' ; + + input <- cmdlist ; + + inscollist <- inscollist COMMA nm + / nm ; + + inscollist_opt <- (LP inscollist RP)? ; + +leaf: INSERT <- 'I' 'N' 'S' 'E' 'R' 'T' ; + + insert_cmd <- INSERT orconf + / REPLACE ; + +leaf: INSTEAD <- 'I' 'N' 'S' 'T' 'E' 'A' 'D' ; + +leaf: INTEGER <- 'I' 'N' 'T' 'E' 'G' 'E' 'R' ; + +leaf: INTERSECT <- 'I' 'N' 'T' 'E' 'R' 'S' 'E' 'C' 'T' ; + +leaf: INTO <- 'I' 'N' 'T' 'O' ; + +leaf: IS <- 'I' 'S' ; + +leaf: ISNULL <- 'I' 'S' 'N' 'U' 'L' 'L' ; + + itemlist <- itemlist COMMA expr + / expr ; + +leaf: JOIN <- 'J' 'O' 'I' 'N' ; + +leaf: JOIN_KW <- 'J' 'O' 'I' 'N' '_' 'K' 'W' ; + + joinop <- COMMA + / JOIN + / JOIN_KW JOIN + / JOIN_KW nm JOIN + / JOIN_KW nm nm JOIN ; + +leaf: KEY <- 'K' 'E' 'Y' ; + + key_opt <- ( KEY ids + / KEY BLOB)? ; + + kwcolumn_opt <- COLUMNKW? ; + +leaf: LE <- 'L' 'E' ; + +leaf: LIKE <- 'L' 'I' 'K' 'E' ; + + likeop <- LIKE + / GLOB + / NOT LIKE + / NOT GLOB ; + +leaf: LIMIT <- 'L' 'I' 'M' 'I' 'T' ; + + limit_opt <- ( LIMIT expr + / LIMIT expr OFFSET expr + / LIMIT expr COMMA expr)? ; + +leaf: LP <- 'L' 'P' ; + +leaf: LSHIFT <- 'L' 'S' 'H' 'I' 'F' 'T' ; + +leaf: LT <- 'L' 'T' ; + +leaf: MATCH <- 'M' 'A' 'T' 'C' 'H' ; + +leaf: MINUS <- 'M' 'I' 'N' 'U' 'S' ; + + minus_num <- MINUS number ; + + multiselect_op <- UNION + / UNION ALL + / INTERSECT + / EXCEPT ; + +leaf: NE <- 'N' 'E' ; + + nm <- ID + / STRING + / JOIN_KW ; + +leaf: NOT <- 'N' 'O' 'T' ; + +leaf: NOTNULL <- 'N' 'O' 'T' 'N' 'U' 'L' 'L' ; + +leaf: NULL <- 'N' 'U' 'L' 'L' ; + + number <- INTEGER + / FLOAT ; + +leaf: OF <- 'O' 'F' ; + +leaf: OFFSET <- 'O' 'F' 'F' 'S' 'E' 'T' ; + +leaf: ON <- 'O' 'N' ; + + on_opt <- (ON expr)? ; + + onconf <- (ON CONFLICT resolvetype)? ; + + oneselect <- SELECT distinct selcollist from where_opt + groupby_opt having_opt orderby_opt limit_opt ; + +leaf: OR <- 'O' 'R' ; + + orconf <- (OR resolvetype)? ; + +leaf: ORDER <- 'O' 'R' 'D' 'E' 'R' ; + + orderby_opt <- (ORDER BY sortlist)? ; + +leaf: PLUS <- 'P' 'L' 'U' 'S' ; + + plus_num <- plus_opt number ; + + plus_opt <- PLUS? ; + +leaf: PRAGMA <- 'P' 'R' 'A' 'G' 'M' 'A' ; + +leaf: PRIMARY <- 'P' 'R' 'I' 'M' 'A' 'R' 'Y' ; + +leaf: RAISE <- 'R' 'A' 'I' 'S' 'E' ; + + raisetype <- ROLLBACK + / ABORT + / FAIL ; + + refact <- SET NULL + / SET DEFAULT + / CASCADE + / RESTRICT ; + + refarg <- MATCH nm + / ON DELETE refact + / ON UPDATE refact + / ON INSERT refact ; + + refargs <- (refargs refarg)? ; + +leaf: REFERENCES <- 'R' 'E' 'F' 'E' 'R' 'E' 'N' 'C' 'E' 'S' ; + +leaf: REGISTER <- 'R' 'E' 'G' 'I' 'S' 'T' 'E' 'R' ; + +leaf: REINDEX <- 'R' 'E' 'I' 'N' 'D' 'E' 'X' ; + +leaf: REM <- 'R' 'E' 'M' ; + +leaf: RENAME <- 'R' 'E' 'N' 'A' 'M' 'E' ; + +leaf: REPLACE <- 'R' 'E' 'P' 'L' 'A' 'C' 'E' ; + + resolvetype <- raisetype + / IGNORE + / REPLACE ; + +leaf: RESTRICT <- 'R' 'E' 'S' 'T' 'R' 'I' 'C' 'T' ; + +leaf: ROLLBACK <- 'R' 'O' 'L' 'L' 'B' 'A' 'C' 'K' ; + +leaf: ROW <- 'R' 'O' 'W' ; + +leaf: RP <- 'R' 'P' ; + +leaf: RSHIFT <- 'R' 'S' 'H' 'I' 'F' 'T' ; + + sclp <- (selcollist COMMA)? ; + + selcollist <- sclp expr as + / sclp STAR + / sclp nm DOT STAR ; + +leaf: SELECT <- 'S' 'E' 'L' 'E' 'C' 'T' ; + + select <- oneselect + / select multiselect_op oneselect ; + + seltablist <- stl_prefix nm dbnm as on_opt using_opt + / stl_prefix LP seltablist_paren RP as on_opt + using_opt ; + + seltablist_paren <- select + / seltablist ; + +leaf: SEMI <- 'S' 'E' 'M' 'I' ; + +leaf: SET <- 'S' 'E' 'T' ; + + setlist <- setlist COMMA nm EQ expr + / nm EQ expr ; + + signed <- plus_num + / minus_num ; + +leaf: SLASH <- 'S' 'L' 'A' 'S' 'H' ; + + sortitem <- expr ; + + sortlist <- sortlist COMMA sortitem collate sortorder + / sortitem collate sortorder ; + + sortorder <- ( ASC + / DESC)? ; + +leaf: STAR <- 'S' 'T' 'A' 'R' ; + +leaf: STATEMENT <- 'S' 'T' 'A' 'T' 'E' 'M' 'E' 'N' 'T' ; + + stl_prefix <- (seltablist joinop)? ; + +leaf: STRING <- 'S' 'T' 'R' 'I' 'N' 'G' ; + +leaf: TABLE <- 'T' 'A' 'B' 'L' 'E' ; + + tcons <- CONSTRAINT nm + / PRIMARY KEY LP idxlist autoinc RP onconf + / UNIQUE LP idxlist RP onconf + / CHECK expr onconf + / FOREIGN KEY LP idxlist RP REFERENCES nm + idxlist_opt refargs defer_subclause_opt ; + +leaf: TEMP <- 'T' 'E' 'M' 'P' ; + + temp <- TEMP? ; + + term <- NULL + / INTEGER + / FLOAT + / STRING + / BLOB + / CTIME + / CDATE + / CTIMESTAMP ; + +leaf: THEN <- 'T' 'H' 'E' 'N' ; + +leaf: TO <- 'T' 'O' ; + + trans_opt <- ( TRANSACTION + / TRANSACTION nm)? ; + +leaf: TRANSACTION <- 'T' 'R' 'A' 'N' 'S' 'A' 'C' 'T' 'I' 'O' 'N' ; + + transtype <- ( DEFERRED + / IMMEDIATE + / EXCLUSIVE)? ; + +leaf: TRIGGER <- 'T' 'R' 'I' 'G' 'G' 'E' 'R' ; + + trigger_cmd <- UPDATE orconf nm SET setlist where_opt + / insert_cmd INTO nm inscollist_opt VALUES LP + itemlist RP + / insert_cmd INTO nm inscollist_opt select + / DELETE FROM nm where_opt + / select ; + + trigger_cmd_list <- (trigger_cmd SEMI trigger_cmd_list)? ; + + trigger_decl <- temp TRIGGER nm dbnm trigger_time trigger_event + ON fullname foreach_clause when_clause ; + + trigger_event <- DELETE + / INSERT + / UPDATE + / UPDATE OF inscollist ; + + trigger_time <- ( BEFORE + / AFTER + / INSTEAD OF)? ; + + type <- ( typename + / typename LP signed RP + / typename LP signed COMMA signed RP)? ; + + typename <- ids + / typename ids ; + +leaf: UNION <- 'U' 'N' 'I' 'O' 'N' ; + +leaf: UNIQUE <- 'U' 'N' 'I' 'Q' 'U' 'E' ; + + uniqueflag <- UNIQUE? ; + +leaf: UPDATE <- 'U' 'P' 'D' 'A' 'T' 'E' ; + +leaf: USING <- 'U' 'S' 'I' 'N' 'G' ; + + using_opt <- (USING LP inscollist RP)? ; + +leaf: VACUUM <- 'V' 'A' 'C' 'U' 'U' 'M' ; + +leaf: VALUES <- 'V' 'A' 'L' 'U' 'E' 'S' ; + +leaf: VARIABLE <- 'V' 'A' 'R' 'I' 'A' 'B' 'L' 'E' ; + +leaf: VIEW <- 'V' 'I' 'E' 'W' ; + +leaf: WHEN <- 'W' 'H' 'E' 'N' ; + + when_clause <- (WHEN expr)? ; + +leaf: WHERE <- 'W' 'H' 'E' 'R' 'E' ; + + where_opt <- (WHERE expr)? ; + +END; + |