first commit
This commit is contained in:
commit
b21727a3fe
84
README.md
Normal file
84
README.md
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# LEMON.JS - LALR(1) Parser Generator for JavaScript
|
||||||
|
|
||||||
|
Lemon.JS is an LALR(1) parser generator for JavaScript based on Lemon parser generator for C included in SQLite package distribution.
|
||||||
|
|
||||||
|
## Parser Code Base
|
||||||
|
|
||||||
|
Files `lemon.c`, `lempar.c`, `lemon.html` are extracted from SQLite v3.17.0. Original parser generator code is slightly fixed to produce JavaScript compatible statements. Parser template translated from C to JavaScript. Source comments mostly not touched to keep it easy diff against original file.
|
||||||
|
|
||||||
|
Both original C version and patched JS version are included for side by side comparison for reference.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Compile lenon-js.c with any C compiler and place in anywhere with lempar.js side by side.
|
||||||
|
|
||||||
|
## Compilation
|
||||||
|
|
||||||
|
Prerequisites: C compiler, for example GCC.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gcc -o lemon-js -O2 lemon-js.c
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lemon-js <filename>.y
|
||||||
|
```
|
||||||
|
|
||||||
|
See http://www.hwaci.com/sw/lemon/lemon.html for more details.
|
||||||
|
|
||||||
|
## Special Directives
|
||||||
|
|
||||||
|
See lemon.html for additional documentation.
|
||||||
|
|
||||||
|
- %name - Set parser class name (default is "Parse")
|
||||||
|
- %include - Include code in the beginning of file (usefull for imports)
|
||||||
|
- %code - Include code in the end of file (usefull for exports or main code)
|
||||||
|
- %token_destructor - Define code which will be executed on token destruction.
|
||||||
|
- %default_destructor
|
||||||
|
- %token_prefix - Define token name prefix.
|
||||||
|
- %syntax_error - Define custom error handler for syntax erorrs.
|
||||||
|
- %parse_accept - Define handler for all accepted tokens.
|
||||||
|
- %parse_failure - Define handler for parse errors.
|
||||||
|
- %stack_overflow - Define handler for stack overflow.
|
||||||
|
- %extra_argument - **NOT SUPPORTED**
|
||||||
|
- %token_type - **NOT SUPPORTED**
|
||||||
|
- %default_type - **NOT SUPPORTED**
|
||||||
|
- %stack_size - Set default stack size.
|
||||||
|
- %start_symbol
|
||||||
|
- %left - Set left associative tokens.
|
||||||
|
- %right - Set right associative tokens.
|
||||||
|
- %nonassoc - Set non associative tokens.
|
||||||
|
- %destructor - Define custom parser destructor.
|
||||||
|
- %type - **NOT SUPPORTED**
|
||||||
|
- %fallback - Define fallback logic for tokens.
|
||||||
|
- %wildcard - Define WILDCARD token.
|
||||||
|
- %token_class - **NOT SUPPORTED**
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- some expressions, for example, regular expression `/\*/` could break lemon parser in `%code` or `%include` sections.
|
||||||
|
- the best place to put something like `module.exports = ParserName;` or `export default ParserName;` is in `%code` section.
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- add some tests for different options
|
||||||
|
- document variables
|
||||||
|
- YYNOERRORRECOVERY ?
|
||||||
|
- YYERRORSYMBOL ?
|
||||||
|
- rename methods, variables, get rid of YY prefixes?
|
||||||
|
- enable asserts, could be usefull for testing
|
||||||
|
|
||||||
|
## Alternative Lexers
|
||||||
|
|
||||||
|
- https://github.com/tantaman/lexed.js
|
||||||
|
- https://github.com/aaditmshah/lexer
|
||||||
|
- https://github.com/YuhangGe/jslex
|
||||||
|
|
||||||
|
## Alternative Parsers
|
||||||
|
|
||||||
|
- https://github.com/sormy/flex-js
|
||||||
|
- http://jscc.brobston.com
|
||||||
|
- http://zaach.github.io/jison/
|
||||||
|
- https://pegjs.org
|
987
documentation/lemon.html
Normal file
987
documentation/lemon.html
Normal file
@ -0,0 +1,987 @@
|
|||||||
|
<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.
|
||||||
|
It does the same job as "bison" and "yacc".
|
||||||
|
But lemon is not a bison or yacc clone. Lemon
|
||||||
|
uses a different grammar syntax which is designed to
|
||||||
|
reduce the number of coding errors. Lemon also uses a
|
||||||
|
parsing engine that is faster than yacc and
|
||||||
|
bison and which is both reentrant and threadsafe.
|
||||||
|
(Update: Since the previous sentence was written, bison
|
||||||
|
has also been updated so that it too can generate a
|
||||||
|
reentrant and threadsafe parser.)
|
||||||
|
Lemon also 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><b>-b</b>
|
||||||
|
Show only the basis for each parser state in the report file.
|
||||||
|
<li><b>-c</b>
|
||||||
|
Do not compress the generated action tables.
|
||||||
|
<li><b>-D<i>name</i></b>
|
||||||
|
Define C preprocessor macro <i>name</i>. This macro is useable by
|
||||||
|
"%ifdef" lines in the grammar file.
|
||||||
|
<li><b>-g</b>
|
||||||
|
Do not generate a parser. Instead write the input grammar to standard
|
||||||
|
output with all comments, actions, and other extraneous text removed.
|
||||||
|
<li><b>-l</b>
|
||||||
|
Omit "#line" directives in the generated parser C code.
|
||||||
|
<li><b>-m</b>
|
||||||
|
Cause the output C source code to be compatible with the "makeheaders"
|
||||||
|
program.
|
||||||
|
<li><b>-p</b>
|
||||||
|
Display all conflicts that are resolved by
|
||||||
|
<a href='#precrules'>precedence rules</a>.
|
||||||
|
<li><b>-q</b>
|
||||||
|
Suppress generation of the report file.
|
||||||
|
<li><b>-r</b>
|
||||||
|
Do not sort or renumber the parser states as part of optimization.
|
||||||
|
<li><b>-s</b>
|
||||||
|
Show parser statistics before existing.
|
||||||
|
<li><b>-T<i>file</i></b>
|
||||||
|
Use <i>file</i> as the template for the generated C-code parser implementation.
|
||||||
|
<li><b>-x</b>
|
||||||
|
Print the Lemon version number.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<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 requests
|
||||||
|
it (via the <a href='#extraarg'><tt>extra_argument</tt> directive</a>),
|
||||||
|
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.
|
||||||
|
(All error-handling code is omitted 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>
|
||||||
|
|
||||||
|
<p><i>Updated as of 2016-02-16:</i>
|
||||||
|
The text above was written in the 1990s.
|
||||||
|
We are told that Bison has lately been enhanced to support the
|
||||||
|
tokenizer-calls-parser paradigm used by Lemon, and to obviate the
|
||||||
|
need for global variables.</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/or underscore characters
|
||||||
|
that begins with an upper case letter.
|
||||||
|
A terminal can contain lowercase 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>
|
||||||
|
|
||||||
|
<a name='precrules'></a>
|
||||||
|
<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
|
||||||
|
<a href='#pleft'>%left</a>,
|
||||||
|
<a href='#pright'>%right</a> or
|
||||||
|
<a href='#pnonassoc'>%nonassoc</a> 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 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>%endif</tt>
|
||||||
|
<li><tt>%extra_argument</tt>
|
||||||
|
<li><tt>%fallback</tt>
|
||||||
|
<li><tt>%ifdef</tt>
|
||||||
|
<li><tt>%ifndef</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_class</tt>
|
||||||
|
<li><tt>%token_destructor</tt>
|
||||||
|
<li><tt>%token_prefix</tt>
|
||||||
|
<li><tt>%token_type</tt>
|
||||||
|
<li><tt>%type</tt>
|
||||||
|
<li><tt>%wildcard</tt>
|
||||||
|
</ul>
|
||||||
|
Each of these directives will be described separately in the
|
||||||
|
following sections:</p>
|
||||||
|
|
||||||
|
<a name='pcode'></a>
|
||||||
|
<h4>The <tt>%code</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %code directive is used to specify addition C code that
|
||||||
|
is added to the end of the main output file. This is similar to
|
||||||
|
the <a href='#pinclude'>%include</a> 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 or even the "main()" function
|
||||||
|
as part of the output file.</p>
|
||||||
|
|
||||||
|
<a name='default_destructor'></a>
|
||||||
|
<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 <a name='#destructor'>%destructor</a> 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>
|
||||||
|
|
||||||
|
<a name='default_type'></a>
|
||||||
|
<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
|
||||||
|
<a href='#ptype'>%type</a> directive.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a name='destructor'></a>
|
||||||
|
<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 <a href='#token_destructor'>%token_destructor</a>
|
||||||
|
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.
|
||||||
|
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>Destructors help avoid memory leaks by automatically freeing
|
||||||
|
allocated objects when they go out of scope.
|
||||||
|
To do the same using yacc or bison is much more difficult.</p>
|
||||||
|
|
||||||
|
<a name="extraarg"></a>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<a name='pfallback'></a>
|
||||||
|
<h4>The <tt>%fallback</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %fallback directive specifies an alternative meaning for one
|
||||||
|
or more tokens. The alternative meaning is tried if the original token
|
||||||
|
would have generated a syntax error.
|
||||||
|
|
||||||
|
<p>The %fallback directive was added to support robust parsing of SQL
|
||||||
|
syntax in <a href="https://www.sqlite.org/">SQLite</a>.
|
||||||
|
The SQL language contains a large assortment of keywords, each of which
|
||||||
|
appears as a different token to the language parser. SQL contains so
|
||||||
|
many keywords, that it can be difficult for programmers to keep up with
|
||||||
|
them all. Programmers will, therefore, sometimes mistakenly use an
|
||||||
|
obscure language keyword for an identifier. The %fallback directive
|
||||||
|
provides a mechanism to tell the parser: "If you are unable to parse
|
||||||
|
this keyword, try treating it as an identifier instead."
|
||||||
|
|
||||||
|
<p>The syntax of %fallback is as follows:
|
||||||
|
|
||||||
|
<blockquote>
|
||||||
|
<tt>%fallback</tt> <i>ID</i> <i>TOKEN...</i> <b>.</b>
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<p>In words, the %fallback directive is followed by a list of token names
|
||||||
|
terminated by a period. The first token name is the fallback token - the
|
||||||
|
token to which all the other tokens fall back to. The second and subsequent
|
||||||
|
arguments are tokens which fall back to the token identified by the first
|
||||||
|
argument.
|
||||||
|
|
||||||
|
<a name='pifdef'></a>
|
||||||
|
<h4>The <tt>%ifdef</tt>, <tt>%ifndef</tt>, and <tt>%endif</tt> directives.</h4>
|
||||||
|
|
||||||
|
<p>The %ifdef, %ifndef, and %endif directives are similar to
|
||||||
|
#ifdef, #ifndef, and #endif in the C-preprocessor, just not as general.
|
||||||
|
Each of these directives must begin at the left margin. No whitespace
|
||||||
|
is allowed between the "%" and the directive name.
|
||||||
|
|
||||||
|
<p>Grammar text in between "%ifdef MACRO" and the next nested "%endif" is
|
||||||
|
ignored unless the "-DMACRO" command-line option is used. Grammar text
|
||||||
|
betwen "%ifndef MACRO" and the next nested "%endif" is included except when
|
||||||
|
the "-DMACRO" command-line option is used.
|
||||||
|
|
||||||
|
<p>Note that the argument to %ifdef and %ifndef must be a single
|
||||||
|
preprocessor symbol name, not a general expression. There is no "%else"
|
||||||
|
directive.
|
||||||
|
|
||||||
|
|
||||||
|
<a name='pinclude'></a>
|
||||||
|
<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, their values are concatenated
|
||||||
|
so that all %include code ultimately appears near the top of the
|
||||||
|
generated parser, in the same order as it appeared in the grammer.</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>
|
||||||
|
|
||||||
|
<a name='pleft'></a>
|
||||||
|
<h4>The <tt>%left</tt> directive</h4>
|
||||||
|
|
||||||
|
The %left directive is used (along with the <a href='#pright'>%right</a> and
|
||||||
|
<a href='#pnonassoc'>%nonassoc</a> 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>
|
||||||
|
|
||||||
|
<a name='pname'></a>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<a name='pnonassoc'></a>
|
||||||
|
<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
|
||||||
|
<a href='#precrules'>precedence rules</a>
|
||||||
|
or on the <a href='#pleft'>%left</a> directive for additional information.</p>
|
||||||
|
|
||||||
|
<a name='parse_accept'></a>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<a name='parse_failure'></a>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<a name='pright'></a>
|
||||||
|
<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
|
||||||
|
<a href='#precrules'>precedence rules</a>
|
||||||
|
or on the <a href='#pleft'>%left</a> directive for additional information.</p>
|
||||||
|
|
||||||
|
<a name='stack_overflow'></a>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<a name='stack_size'></a>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<a name='start_symbol'></a>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<a name='token_destructor'></a>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<a name='token_prefix'></a>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<a name='token_type'></a><a name='ptype'></a>
|
||||||
|
<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 "void*".</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>
|
||||||
|
|
||||||
|
<a name='pwildcard'></a>
|
||||||
|
<h4>The <tt>%wildcard</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %wildcard directive is followed by a single token name and a
|
||||||
|
period. This directive specifies that the identified token should
|
||||||
|
match any input token.
|
||||||
|
|
||||||
|
<p>When the generated parser has the choice of matching an input against
|
||||||
|
the wildcard token and some other token, the other token is always used.
|
||||||
|
The wildcard token is only matched if there are no other alternatives.
|
||||||
|
|
||||||
|
<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>
|
45
examples/calculator-c.y
Normal file
45
examples/calculator-c.y
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
%token_type {int}
|
||||||
|
|
||||||
|
%left PLUS MINUS.
|
||||||
|
%left DIVIDE TIMES.
|
||||||
|
|
||||||
|
%include {
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "calculator-c.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
%code {
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
void* pParser = ParseAlloc(malloc);
|
||||||
|
ParseTrace(stderr, "> ");
|
||||||
|
Parse(pParser, INTEGER, 1);
|
||||||
|
Parse(pParser, PLUS, 0);
|
||||||
|
Parse(pParser, INTEGER, 2);
|
||||||
|
Parse(pParser, TIMES, 0);
|
||||||
|
Parse(pParser, INTEGER, 10);
|
||||||
|
Parse(pParser, DIVIDE, 0);
|
||||||
|
Parse(pParser, INTEGER, 2);
|
||||||
|
Parse(pParser, 0, 0);
|
||||||
|
ParseFree(pParser, free);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%syntax_error {
|
||||||
|
fprintf(stderr, "Syntax error\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
program ::= expr(A). { printf("Result=%d\n", A); }
|
||||||
|
expr(A) ::= expr(B) MINUS expr(C). { A = B - C; }
|
||||||
|
expr(A) ::= expr(B) PLUS expr(C). { A = B + C; }
|
||||||
|
expr(A) ::= expr(B) TIMES expr(C). { A = B * C; }
|
||||||
|
expr(A) ::= expr(B) DIVIDE expr(C). {
|
||||||
|
if (C != 0) {
|
||||||
|
A = B / C;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Divide by zero\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expr(A) ::= INTEGER(B). { A = B; }
|
917
examples/calculator-js.js
Normal file
917
examples/calculator-js.js
Normal file
@ -0,0 +1,917 @@
|
|||||||
|
/*
|
||||||
|
** 2000-05-29
|
||||||
|
**
|
||||||
|
** The author disclaims copyright to this source code. In place of
|
||||||
|
** a legal notice, here is a blessing:
|
||||||
|
**
|
||||||
|
** May you do good and not evil.
|
||||||
|
** May you find forgiveness for yourself and forgive others.
|
||||||
|
** May you share freely, never taking more than you give.
|
||||||
|
**
|
||||||
|
** Based on SQLite distribution v3.17.0
|
||||||
|
** Adopted for JavaScript by Artem Butusov <art.sormy@gmail.com>
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** Driver template for the LEMON parser generator.
|
||||||
|
**
|
||||||
|
** The "lemon" program processes an LALR(1) input grammar file, then uses
|
||||||
|
** this template to construct a parser. The "lemon" program inserts text
|
||||||
|
** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the
|
||||||
|
** interstitial "-" characters) contained in this template is changed into
|
||||||
|
** the value of the %name directive from the grammar. Otherwise, the content
|
||||||
|
** of this template is copied straight through into the generate parser
|
||||||
|
** source file.
|
||||||
|
**
|
||||||
|
** The following is the concatenation of all %include directives from the
|
||||||
|
** input grammar file:
|
||||||
|
*/
|
||||||
|
/************ Begin %include sections from the grammar ************************/
|
||||||
|
// line 8 "examples/calculator-js.y"
|
||||||
|
|
||||||
|
// include something
|
||||||
|
// line 33 "examples/calculator-js.js"
|
||||||
|
/**************** End of %include directives **********************************/
|
||||||
|
function Parser() {
|
||||||
|
/* These constants specify the various numeric values for terminal symbols
|
||||||
|
** in a format understandable to "makeheaders".
|
||||||
|
***************** Begin makeheaders token definitions *************************/
|
||||||
|
this.TOKEN_PLUS = 1;
|
||||||
|
this.TOKEN_MINUS = 2;
|
||||||
|
this.TOKEN_DIVIDE = 3;
|
||||||
|
this.TOKEN_TIMES = 4;
|
||||||
|
this.TOKEN_INTEGER = 5;
|
||||||
|
/**************** End makeheaders token definitions ***************************/
|
||||||
|
|
||||||
|
/* The next sections is a series of control #defines.
|
||||||
|
** various aspects of the generated parser.
|
||||||
|
** YYNOCODE is a number of type YYCODETYPE that is not used for
|
||||||
|
** any terminal or nonterminal symbol.
|
||||||
|
** YYFALLBACK If defined, this indicates that one or more tokens
|
||||||
|
** (also known as: "terminal symbols") have fall-back
|
||||||
|
** values which should be used if the original symbol
|
||||||
|
** would not parse. This permits keywords to sometimes
|
||||||
|
** be used as identifiers, for example.
|
||||||
|
** YYSTACKDEPTH is the maximum depth of the parser's stack. If
|
||||||
|
** zero the stack is dynamically sized using realloc()
|
||||||
|
** YYERRORSYMBOL is the code number of the error symbol. If not
|
||||||
|
** defined, then do no error processing.
|
||||||
|
** YYNSTATE the combined number of states.
|
||||||
|
** YYNRULE the number of rules in the grammar
|
||||||
|
** YY_MAX_SHIFT Maximum value for shift actions
|
||||||
|
** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
|
||||||
|
** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
|
||||||
|
** YY_MIN_REDUCE Maximum value for reduce actions
|
||||||
|
** YY_ERROR_ACTION The yy_action[] code for syntax error
|
||||||
|
** YY_ACCEPT_ACTION The yy_action[] code for accept
|
||||||
|
** YY_NO_ACTION The yy_action[] code for no-op
|
||||||
|
*/
|
||||||
|
/************* Begin control #defines *****************************************/
|
||||||
|
this.YYNOCODE = 10;
|
||||||
|
this.YYSTACKDEPTH = 100;
|
||||||
|
this.YYFALLBACK = false;
|
||||||
|
this.YYNSTATE = 8;
|
||||||
|
this.YYNRULE = 6;
|
||||||
|
this.YY_MAX_SHIFT = 7;
|
||||||
|
this.YY_MIN_SHIFTREDUCE = 11;
|
||||||
|
this.YY_MAX_SHIFTREDUCE = 16;
|
||||||
|
this.YY_MIN_REDUCE = 17;
|
||||||
|
this.YY_MAX_REDUCE = 22;
|
||||||
|
this.YY_ERROR_ACTION = 23;
|
||||||
|
this.YY_ACCEPT_ACTION = 24;
|
||||||
|
this.YY_NO_ACTION = 25;
|
||||||
|
/************* End control #defines *******************************************/
|
||||||
|
|
||||||
|
/* Define the yytestcase() macro to be a no-op if is not already defined
|
||||||
|
** otherwise.
|
||||||
|
**
|
||||||
|
** Applications can choose to define yytestcase() in the %include section
|
||||||
|
** to a macro that can assist in verifying code coverage. For production
|
||||||
|
** code the yytestcase() macro should be turned off. But it is useful
|
||||||
|
** for testing.
|
||||||
|
*/
|
||||||
|
if (!this.yytestcase) {
|
||||||
|
this.yytestcase = function () {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Next are the tables used to determine what action to take based on the
|
||||||
|
** current state and lookahead token. These tables are used to implement
|
||||||
|
** functions that take a state number and lookahead value and return an
|
||||||
|
** action integer.
|
||||||
|
**
|
||||||
|
** Suppose the action integer is N. Then the action is determined as
|
||||||
|
** follows
|
||||||
|
**
|
||||||
|
** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead
|
||||||
|
** token onto the stack and goto state N.
|
||||||
|
**
|
||||||
|
** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then
|
||||||
|
** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE.
|
||||||
|
**
|
||||||
|
** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
|
||||||
|
** and YY_MAX_REDUCE
|
||||||
|
**
|
||||||
|
** N == YY_ERROR_ACTION A syntax error has occurred.
|
||||||
|
**
|
||||||
|
** N == YY_ACCEPT_ACTION The parser accepts its input.
|
||||||
|
**
|
||||||
|
** N == YY_NO_ACTION No such action. Denotes unused
|
||||||
|
** slots in the yy_action[] table.
|
||||||
|
**
|
||||||
|
** The action table is constructed as a single large table named yy_action[].
|
||||||
|
** Given state S and lookahead X, the action is computed as either:
|
||||||
|
**
|
||||||
|
** (A) N = yy_action[ yy_shift_ofst[S] + X ]
|
||||||
|
** (B) N = yy_default[S]
|
||||||
|
**
|
||||||
|
** The (A) formula is preferred. The B formula is used instead if:
|
||||||
|
** (1) The yy_shift_ofst[S]+X value is out of range, or
|
||||||
|
** (2) yy_lookahead[yy_shift_ofst[S]+X] is not equal to X, or
|
||||||
|
** (3) yy_shift_ofst[S] equal YY_SHIFT_USE_DFLT.
|
||||||
|
** (Implementation note: YY_SHIFT_USE_DFLT is chosen so that
|
||||||
|
** YY_SHIFT_USE_DFLT+X will be out of range for all possible lookaheads X.
|
||||||
|
** Hence only tests (1) and (2) need to be evaluated.)
|
||||||
|
**
|
||||||
|
** The formulas above are for computing the action when the lookahead is
|
||||||
|
** a terminal symbol. If the lookahead is a non-terminal (as occurs after
|
||||||
|
** a reduce action) then the yy_reduce_ofst[] array is used in place of
|
||||||
|
** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
|
||||||
|
** YY_SHIFT_USE_DFLT.
|
||||||
|
**
|
||||||
|
** The following are the tables generated in this section:
|
||||||
|
**
|
||||||
|
** yy_action[] A single table containing all actions.
|
||||||
|
** yy_lookahead[] A table containing the lookahead for each entry in
|
||||||
|
** yy_action. Used to detect hash collisions.
|
||||||
|
** yy_shift_ofst[] For each state, the offset into yy_action for
|
||||||
|
** shifting terminals.
|
||||||
|
** yy_reduce_ofst[] For each state, the offset into yy_action for
|
||||||
|
** shifting non-terminals after a reduce.
|
||||||
|
** yy_default[] Default action for each state.
|
||||||
|
**
|
||||||
|
*********** Begin parsing tables **********************************************/
|
||||||
|
this.yy_action = [
|
||||||
|
/* 0 */ 17, 3, 4, 1, 2, 24, 5, 1, 2, 15,
|
||||||
|
/* 10 */ 16, 14, 19, 19, 6, 7,
|
||||||
|
];
|
||||||
|
this.yy_lookahead = [
|
||||||
|
/* 0 */ 0, 1, 2, 3, 4, 7, 8, 3, 4, 8,
|
||||||
|
/* 10 */ 5, 8, 9, 9, 8, 8,
|
||||||
|
];
|
||||||
|
this.YY_SHIFT_USE_DFLT = 16;
|
||||||
|
this.YY_SHIFT_COUNT = 7;
|
||||||
|
this.YY_SHIFT_MIN = 0;
|
||||||
|
this.YY_SHIFT_MAX = 5;
|
||||||
|
this.yy_shift_ofst = [
|
||||||
|
/* 0 */ 5, 5, 5, 5, 5, 0, 4, 4,
|
||||||
|
];
|
||||||
|
this.YY_REDUCE_USE_DFLT = -3;
|
||||||
|
this.YY_REDUCE_COUNT = 4;
|
||||||
|
this.YY_REDUCE_MIN = -2;
|
||||||
|
this.YY_REDUCE_MAX = 7;
|
||||||
|
this.yy_reduce_ofst = [
|
||||||
|
/* 0 */ -2, 1, 3, 6, 7,
|
||||||
|
];
|
||||||
|
this.yy_default = [
|
||||||
|
/* 0 */ 23, 23, 23, 23, 23, 23, 19, 18,
|
||||||
|
];
|
||||||
|
/********** End of lemon-generated parsing tables *****************************/
|
||||||
|
|
||||||
|
/* The next table maps tokens (terminal symbols) into fallback tokens.
|
||||||
|
** If a construct like the following:
|
||||||
|
**
|
||||||
|
** %fallback ID X Y Z.
|
||||||
|
**
|
||||||
|
** appears in the grammar, then ID becomes a fallback token for X, Y,
|
||||||
|
** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
|
||||||
|
** but it does not parse, the type of the token is changed to ID and
|
||||||
|
** the parse is retried before an error is thrown.
|
||||||
|
**
|
||||||
|
** This feature can be used, for example, to cause some keywords in a language
|
||||||
|
** to revert to identifiers if they keyword does not apply in the context where
|
||||||
|
** it appears.
|
||||||
|
*/
|
||||||
|
this.yyFallback = [
|
||||||
|
];
|
||||||
|
|
||||||
|
/* The following structure represents a single element of the
|
||||||
|
** parser's stack. Information stored includes:
|
||||||
|
**
|
||||||
|
** + The state number for the parser at this level of the stack.
|
||||||
|
**
|
||||||
|
** + The value of the token stored at this level of the stack.
|
||||||
|
** (In other words, the "major" token.)
|
||||||
|
**
|
||||||
|
** + The semantic value stored at this level of the stack. This is
|
||||||
|
** the information used by the action routines in the grammar.
|
||||||
|
** It is sometimes called the "minor" token.
|
||||||
|
**
|
||||||
|
** After the "shift" half of a SHIFTREDUCE action, the stateno field
|
||||||
|
** actually contains the reduce action for the second half of the
|
||||||
|
** SHIFTREDUCE.
|
||||||
|
*/
|
||||||
|
//{
|
||||||
|
// stateno, /* The state-number, or reduce action in SHIFTREDUCE */
|
||||||
|
// major, /* The major token value. This is the code
|
||||||
|
// ** number for the token at this stack level */
|
||||||
|
// minor, /* The user-supplied minor token value. This
|
||||||
|
// ** is the value of the token */
|
||||||
|
//}
|
||||||
|
|
||||||
|
/* The state of the parser is completely contained in an instance of
|
||||||
|
** the following structure */
|
||||||
|
this.yyhwm = 0; /* High-water mark of the stack */
|
||||||
|
this.yyerrcnt = -1; /* Shifts left before out of the error */
|
||||||
|
this.yystack = null; /* The parser's stack */
|
||||||
|
this.yyidx = -1; /* Stack index of current element in the stack */
|
||||||
|
|
||||||
|
this.yyTraceCallback = null;
|
||||||
|
this.yyTracePrompt = "";
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Turn parser tracing on by giving a stream to which to write the trace
|
||||||
|
** and a prompt to preface each trace message. Tracing is turned off
|
||||||
|
** by making either argument NULL
|
||||||
|
**
|
||||||
|
** Inputs:
|
||||||
|
** <ul>
|
||||||
|
** <li> A callback to which trace output should be written.
|
||||||
|
** If NULL, then tracing is turned off.
|
||||||
|
** <li> A prefix string written at the beginning of every
|
||||||
|
** line of trace output. Default is "".
|
||||||
|
** </ul>
|
||||||
|
**
|
||||||
|
** Outputs:
|
||||||
|
** None.
|
||||||
|
*/
|
||||||
|
this.setTraceCallback = function (callback, prompt) {
|
||||||
|
this.yyTraceCallback = callback;
|
||||||
|
this.yyTracePrompt = prompt || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.trace = function (message) {
|
||||||
|
this.yyTraceCallback(this.yyTracePrompt + message + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For tracing shifts, the names of all terminals and nonterminals
|
||||||
|
** are required. The following table supplies these names */
|
||||||
|
this.yyTokenName = [
|
||||||
|
"$", "PLUS", "MINUS", "DIVIDE",
|
||||||
|
"TIMES", "INTEGER", "error", "program",
|
||||||
|
"expr",
|
||||||
|
];
|
||||||
|
|
||||||
|
/* For tracing reduce actions, the names of all rules are required.
|
||||||
|
*/
|
||||||
|
this.yyRuleName = [
|
||||||
|
/* 0 */ "program ::= expr",
|
||||||
|
/* 1 */ "expr ::= expr MINUS expr",
|
||||||
|
/* 2 */ "expr ::= expr PLUS expr",
|
||||||
|
/* 3 */ "expr ::= expr TIMES expr",
|
||||||
|
/* 4 */ "expr ::= expr DIVIDE expr",
|
||||||
|
/* 5 */ "expr ::= INTEGER",
|
||||||
|
];
|
||||||
|
/*
|
||||||
|
** Try to increase the size of the parser stack. Return the number
|
||||||
|
** of errors. Return 0 on success.
|
||||||
|
*/
|
||||||
|
this.yyGrowStack = function () {
|
||||||
|
// fix me: yystksz*2 + 100
|
||||||
|
this.yystack.push({
|
||||||
|
stateno: undefined,
|
||||||
|
major: undefined,
|
||||||
|
minor: undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize a new parser that has already been allocated.
|
||||||
|
*/
|
||||||
|
this.init = function () {
|
||||||
|
this.yyhwm = 0;
|
||||||
|
this.yyerrcnt = -1;
|
||||||
|
this.yyidx = 0;
|
||||||
|
if (this.YYSTACKDEPTH <= 0) {
|
||||||
|
this.yystack = [];
|
||||||
|
this.yyGrowStack();
|
||||||
|
} else {
|
||||||
|
this.yystack = new Array(this.YYSTACKDEPTH);
|
||||||
|
for (var i = 0; i < this.YYSTACKDEPTH; i++) {
|
||||||
|
this.yystack[i] = {
|
||||||
|
stateno: undefined,
|
||||||
|
major: undefined,
|
||||||
|
minor: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var yytos = this.yystack[0];
|
||||||
|
yytos.stateno = 0;
|
||||||
|
yytos.major = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following function deletes the "minor type" or semantic value
|
||||||
|
** associated with a symbol. The symbol can be either a terminal
|
||||||
|
** or nonterminal. "yymajor" is the symbol code, and "yypminor" is
|
||||||
|
** a pointer to the value to be deleted. The code used to do the
|
||||||
|
** deletions is derived from the %destructor and/or %token_destructor
|
||||||
|
** directives of the input grammar.
|
||||||
|
*/
|
||||||
|
this.yy_destructor = function (
|
||||||
|
yymajor, /* Type code for object to destroy */
|
||||||
|
yyminor /* The object to be destroyed */
|
||||||
|
) {
|
||||||
|
switch (yymajor) {
|
||||||
|
/* Here is inserted the actions which take place when a
|
||||||
|
** terminal or non-terminal is destroyed. This can happen
|
||||||
|
** when the symbol is popped from the stack during a
|
||||||
|
** reduce or during error processing or when a parser is
|
||||||
|
** being destroyed before it is finished parsing.
|
||||||
|
**
|
||||||
|
** Note: during a reduce, the only symbols destroyed are those
|
||||||
|
** which appear on the RHS of the rule, but which are *not* used
|
||||||
|
** inside the C code.
|
||||||
|
*/
|
||||||
|
/********* Begin destructor definitions ***************************************/
|
||||||
|
/********* End destructor definitions *****************************************/
|
||||||
|
default: break; /* If no destructor action specified: do nothing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Pop the parser's stack once.
|
||||||
|
**
|
||||||
|
** If there is a destructor routine associated with the token which
|
||||||
|
** is popped from the stack, then call it.
|
||||||
|
*/
|
||||||
|
this.yy_pop_parser_stack = function () {
|
||||||
|
// assert( pParser->yytos!=0 );
|
||||||
|
// assert( pParser->yytos > pParser->yystack );
|
||||||
|
var yytos = this.yystack[this.yyidx];
|
||||||
|
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Popping " + this.yyTokenName[yytos.major]);
|
||||||
|
}
|
||||||
|
this.yy_destructor(yytos.major, yytos.minor);
|
||||||
|
|
||||||
|
this.yyidx--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Clear all secondary memory allocations from the parser
|
||||||
|
*/
|
||||||
|
this.finalize = function () {
|
||||||
|
while (this.yyidx > 0) {
|
||||||
|
this.yy_pop_parser_stack();
|
||||||
|
}
|
||||||
|
this.yystack = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Return the peak depth of the stack for a parser.
|
||||||
|
*/
|
||||||
|
this.getStackPeak = function () {
|
||||||
|
return this.yyhwm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Find the appropriate action for a parser given the terminal
|
||||||
|
** look-ahead token iLookAhead.
|
||||||
|
*/
|
||||||
|
this.yy_find_shift_action = function (
|
||||||
|
iLookAhead /* The look-ahead token */
|
||||||
|
) {
|
||||||
|
var yytos = this.yystack[this.yyidx];
|
||||||
|
var stateno = yytos.stateno;
|
||||||
|
|
||||||
|
if (stateno >= this.YY_MIN_REDUCE) {
|
||||||
|
return stateno;
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert( stateno <= YY_SHIFT_COUNT );
|
||||||
|
|
||||||
|
do {
|
||||||
|
var i = this.yy_shift_ofst[stateno];
|
||||||
|
// assert( iLookAhead!=YYNOCODE );
|
||||||
|
i += iLookAhead;
|
||||||
|
if (i < 0 || i >= this.yy_action.length || this.yy_lookahead[i] != iLookAhead) {
|
||||||
|
if (this.YYFALLBACK) {
|
||||||
|
var iFallback; /* Fallback token */
|
||||||
|
if ((iLookAhead < this.yyFallback.length)
|
||||||
|
&& (iFallback = this.yyFallback[iLookAhead]) != 0
|
||||||
|
) {
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("FALLBACK " + this.yyTokenName[iLookAhead] + " => " + this.yyTokenName[iFallback]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
|
||||||
|
iLookAhead = iFallback;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.YYWILDCARD) {
|
||||||
|
var j = i - iLookAhead + this.YYWILDCARD;
|
||||||
|
var cond1 = (this.YY_SHIFT_MIN + this.YYWILDCARD) < 0 ? j >= 0 : true;
|
||||||
|
var cond2 = (this.YY_SHIFT_MAX + this.YYWILDCARD) >= this.yy_action.length ? j < this.yy_action.length : true;
|
||||||
|
if (cond1 && cond2 && this.yy_lookahead[j] == this.YYWILDCARD && iLookAhead > 0) {
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("WILDCARD " + this.yyTokenName[iLookAhead] + " => " + this.yyTokenName[this.YYWILDCARD]);
|
||||||
|
}
|
||||||
|
return this.yy_action[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.yy_default[stateno];
|
||||||
|
} else {
|
||||||
|
return this.yy_action[i];
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Find the appropriate action for a parser given the non-terminal
|
||||||
|
** look-ahead token iLookAhead.
|
||||||
|
*/
|
||||||
|
this.yy_find_reduce_action = function (
|
||||||
|
stateno, /* Current state number */
|
||||||
|
iLookAhead /* The look-ahead token */
|
||||||
|
) {
|
||||||
|
if (this.YYERRORSYMBOL) {
|
||||||
|
if (stateno > this.YY_REDUCE_COUNT) {
|
||||||
|
return this.yy_default[stateno];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// assert( stateno<=YY_REDUCE_COUNT );
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = this.yy_reduce_ofst[stateno];
|
||||||
|
// assert( i!=YY_REDUCE_USE_DFLT );
|
||||||
|
// assert( iLookAhead!=YYNOCODE );
|
||||||
|
i += iLookAhead;
|
||||||
|
|
||||||
|
if (this.YYERRORSYMBOL) {
|
||||||
|
if (i < 0 || i >= this.yy_action.length || this.yy_lookahead[i] != iLookAhead) {
|
||||||
|
return this.yy_default[stateno];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// assert( i>=0 && i<YY_ACTTAB_COUNT );
|
||||||
|
// assert( yy_lookahead[i]==iLookAhead );
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.yy_action[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following routine is called if the stack overflows.
|
||||||
|
*/
|
||||||
|
this.yyStackOverflow = function () {
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Stack Overflow!");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (this.yyidx > 0) {
|
||||||
|
this.yy_pop_parser_stack();
|
||||||
|
}
|
||||||
|
/* Here code is inserted which will execute if the parser
|
||||||
|
** stack every overflows */
|
||||||
|
/******** Begin %stack_overflow code ******************************************/
|
||||||
|
/******** End %stack_overflow code ********************************************/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Print tracing information for a SHIFT action
|
||||||
|
*/
|
||||||
|
this.yyTraceShift = function (yyNewState) {
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
var yytos = this.yystack[this.yyidx];
|
||||||
|
if (yyNewState < this.YYNSTATE) {
|
||||||
|
this.trace("Shift '" + this.yyTokenName[yytos.major] + "', go to state " + yyNewState);
|
||||||
|
} else {
|
||||||
|
this.trace("Shift '" + this.yyTokenName[yytos.major] + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Perform a shift action.
|
||||||
|
*/
|
||||||
|
this.yy_shift = function (
|
||||||
|
yyNewState, /* The new state to shift in */
|
||||||
|
yyMajor, /* The major token to shift in */
|
||||||
|
yyMinor /* The minor token to shift in */
|
||||||
|
) {
|
||||||
|
this.yyidx++;
|
||||||
|
|
||||||
|
if (this.yyidx > this.yyhwm) {
|
||||||
|
this.yyhwm++;
|
||||||
|
// assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.YYSTACKDEPTH > 0) {
|
||||||
|
if (this.yyidx >= this.YYSTACKDEPTH) {
|
||||||
|
this.yyidx--;
|
||||||
|
this.yyStackOverflow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.yyidx >= this.yystack.length) {
|
||||||
|
this.yyGrowStack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (yyNewState > this.YY_MAX_SHIFT) {
|
||||||
|
yyNewState += this.YY_MIN_REDUCE - this.YY_MIN_SHIFTREDUCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
var yytos = this.yystack[this.yyidx];
|
||||||
|
yytos.stateno = yyNewState;
|
||||||
|
yytos.major = yyMajor;
|
||||||
|
yytos.minor = yyMinor;
|
||||||
|
|
||||||
|
this.yyTraceShift(yyNewState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following table contains information about every rule that
|
||||||
|
** is used during the reduce.
|
||||||
|
*/
|
||||||
|
//{
|
||||||
|
// lhs, /* Symbol on the left-hand side of the rule */
|
||||||
|
// nrhs, /* Number of right-hand side symbols in the rule */
|
||||||
|
//}
|
||||||
|
this.yyRuleInfo = [
|
||||||
|
{ lhs: 7, nrhs: 1 },
|
||||||
|
{ lhs: 8, nrhs: 3 },
|
||||||
|
{ lhs: 8, nrhs: 3 },
|
||||||
|
{ lhs: 8, nrhs: 3 },
|
||||||
|
{ lhs: 8, nrhs: 3 },
|
||||||
|
{ lhs: 8, nrhs: 1 },
|
||||||
|
];
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Perform a reduce action and the shift that must immediately
|
||||||
|
** follow the reduce.
|
||||||
|
*/
|
||||||
|
this.yy_reduce = function (
|
||||||
|
yyruleno /* Number of the rule by which to reduce */
|
||||||
|
){
|
||||||
|
var yymsp = this.yystack[this.yyidx]; /* The top of the parser's stack */
|
||||||
|
|
||||||
|
if (yyruleno < this.yyRuleName.length) {
|
||||||
|
var yysize = this.yyRuleInfo[yyruleno].nrhs;
|
||||||
|
var ruleName = this.yyRuleName[yyruleno];
|
||||||
|
var newStateNo = this.yystack[this.yyidx - yysize].stateno;
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Reduce [" + ruleName + "], go to state " + newStateNo + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the stack is large enough to grow by a single entry
|
||||||
|
** if the RHS of the rule is empty. This ensures that there is room
|
||||||
|
** enough on the stack to push the LHS value */
|
||||||
|
if (this.yyRuleInfo[yyruleno].nrhs == 0) {
|
||||||
|
if (this.yyidx > this.yyhwm) {
|
||||||
|
this.yyhwm++;
|
||||||
|
// assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack));
|
||||||
|
}
|
||||||
|
if (this.YYSTACKDEPTH > 0) {
|
||||||
|
if (this.yyidx >= this.YYSTACKDEPTH - 1) {
|
||||||
|
this.yyStackOverflow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.yyidx >= this.yystack.length - 1) {
|
||||||
|
this.yyGrowStack();
|
||||||
|
yymsp = this.yystack[this.yyidx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var yylhsminor;
|
||||||
|
switch (yyruleno) {
|
||||||
|
/* Beginning here are the reduction cases. A typical example
|
||||||
|
** follows:
|
||||||
|
** case 0:
|
||||||
|
** #line <lineno> <grammarfile>
|
||||||
|
** { ... } // User supplied code
|
||||||
|
** #line <lineno> <thisfile>
|
||||||
|
** break;
|
||||||
|
*/
|
||||||
|
/********** Begin reduce actions **********************************************/
|
||||||
|
case 0: /* program ::= expr */
|
||||||
|
// line 63 "examples/calculator-js.y"
|
||||||
|
{ console.log("Result=" + this.yystack[this.yyidx + 0].minor); }
|
||||||
|
// line 602 "examples/calculator-js.js"
|
||||||
|
break;
|
||||||
|
case 1: /* expr ::= expr MINUS expr */
|
||||||
|
// line 64 "examples/calculator-js.y"
|
||||||
|
{ yylhsminor = this.yystack[this.yyidx + -2].minor - this.yystack[this.yyidx + 0].minor; }
|
||||||
|
// line 607 "examples/calculator-js.js"
|
||||||
|
this.yystack[this.yyidx + -2].minor = yylhsminor;
|
||||||
|
break;
|
||||||
|
case 2: /* expr ::= expr PLUS expr */
|
||||||
|
// line 65 "examples/calculator-js.y"
|
||||||
|
{ yylhsminor = this.yystack[this.yyidx + -2].minor + this.yystack[this.yyidx + 0].minor; }
|
||||||
|
// line 613 "examples/calculator-js.js"
|
||||||
|
this.yystack[this.yyidx + -2].minor = yylhsminor;
|
||||||
|
break;
|
||||||
|
case 3: /* expr ::= expr TIMES expr */
|
||||||
|
// line 66 "examples/calculator-js.y"
|
||||||
|
{ yylhsminor = this.yystack[this.yyidx + -2].minor * this.yystack[this.yyidx + 0].minor; }
|
||||||
|
// line 619 "examples/calculator-js.js"
|
||||||
|
this.yystack[this.yyidx + -2].minor = yylhsminor;
|
||||||
|
break;
|
||||||
|
case 4: /* expr ::= expr DIVIDE expr */
|
||||||
|
// line 67 "examples/calculator-js.y"
|
||||||
|
{
|
||||||
|
if (this.yystack[this.yyidx + 0].minor != 0) {
|
||||||
|
yylhsminor = this.yystack[this.yyidx + -2].minor / this.yystack[this.yyidx + 0].minor;
|
||||||
|
} else {
|
||||||
|
throw new Error("Divide by zero");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// line 631 "examples/calculator-js.js"
|
||||||
|
this.yystack[this.yyidx + -2].minor = yylhsminor;
|
||||||
|
break;
|
||||||
|
case 5: /* expr ::= INTEGER */
|
||||||
|
// line 74 "examples/calculator-js.y"
|
||||||
|
{ yylhsminor = this.yystack[this.yyidx + 0].minor; }
|
||||||
|
// line 637 "examples/calculator-js.js"
|
||||||
|
this.yystack[this.yyidx + 0].minor = yylhsminor;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
/********** End reduce actions ************************************************/
|
||||||
|
};
|
||||||
|
// assert( yyruleno<sizeof(yyRuleInfo)/sizeof(yyRuleInfo[0]) );
|
||||||
|
|
||||||
|
var yygoto = this.yyRuleInfo[yyruleno].lhs; /* The next state */
|
||||||
|
var yysize = this.yyRuleInfo[yyruleno].nrhs; /* Amount to pop the stack */
|
||||||
|
var yyact = this.yy_find_reduce_action( /* The next action */
|
||||||
|
this.yystack[this.yyidx - yysize].stateno,
|
||||||
|
yygoto
|
||||||
|
);
|
||||||
|
if (yyact <= this.YY_MAX_SHIFTREDUCE) {
|
||||||
|
if (yyact > this.YY_MAX_SHIFT) {
|
||||||
|
yyact += this.YY_MIN_REDUCE - this.YY_MIN_SHIFTREDUCE;
|
||||||
|
}
|
||||||
|
this.yyidx -= yysize - 1;
|
||||||
|
yymsp = this.yystack[this.yyidx];
|
||||||
|
yymsp.stateno = yyact;
|
||||||
|
yymsp.major = yygoto;
|
||||||
|
this.yyTraceShift(yyact);
|
||||||
|
} else {
|
||||||
|
// assert( yyact == YY_ACCEPT_ACTION );
|
||||||
|
this.yyidx -= yysize;
|
||||||
|
this.yy_accept();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following code executes when the parse fails
|
||||||
|
*/
|
||||||
|
this.yy_parse_failed = function () {
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Fail!");
|
||||||
|
}
|
||||||
|
while (this.yyidx > 0) {
|
||||||
|
this.yy_pop_parser_stack();
|
||||||
|
}
|
||||||
|
/* Here code is inserted which will be executed whenever the
|
||||||
|
** parser fails */
|
||||||
|
/************ Begin %parse_failure code ***************************************/
|
||||||
|
/************ End %parse_failure code *****************************************/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following code executes when a syntax error first occurs.
|
||||||
|
*/
|
||||||
|
this.yy_syntax_error = function (
|
||||||
|
yymajor, /* The major type of the error token */
|
||||||
|
yyminor /* The minor type of the error token */
|
||||||
|
) {
|
||||||
|
var TOKEN = yyminor;
|
||||||
|
/************ Begin %syntax_error code ****************************************/
|
||||||
|
// line 59 "examples/calculator-js.y"
|
||||||
|
|
||||||
|
console.log("Syntax error");
|
||||||
|
// line 696 "examples/calculator-js.js"
|
||||||
|
/************ End %syntax_error code ******************************************/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following is executed when the parser accepts
|
||||||
|
*/
|
||||||
|
this.yy_accept = function () {
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Accept!");
|
||||||
|
}
|
||||||
|
if (!this.YYNOERRORRECOVERY) {
|
||||||
|
this.yyerrcnt = -1;
|
||||||
|
}
|
||||||
|
// assert( yypParser->yytos==yypParser->yystack );
|
||||||
|
/* Here code is inserted which will be executed whenever the
|
||||||
|
** parser accepts */
|
||||||
|
/*********** Begin %parse_accept code *****************************************/
|
||||||
|
/*********** End %parse_accept code *******************************************/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The main parser program.
|
||||||
|
** The first argument is a pointer to a structure obtained from
|
||||||
|
** "ParserAlloc" which describes the current state of the parser.
|
||||||
|
** The second argument is the major token number. The third is
|
||||||
|
** the minor token. The fourth optional argument is whatever the
|
||||||
|
** user wants (and specified in the grammar) and is available for
|
||||||
|
** use by the action routines.
|
||||||
|
**
|
||||||
|
** Inputs:
|
||||||
|
** <ul>
|
||||||
|
** <li> A pointer to the parser (an opaque structure.)
|
||||||
|
** <li> The major token number.
|
||||||
|
** <li> The minor token number.
|
||||||
|
** <li> An option argument of a grammar-specified type.
|
||||||
|
** </ul>
|
||||||
|
**
|
||||||
|
** Outputs:
|
||||||
|
** None.
|
||||||
|
*/
|
||||||
|
this.parse = function (
|
||||||
|
yymajor, /* The major token code number */
|
||||||
|
yyminor /* The value for the token */
|
||||||
|
) {
|
||||||
|
var yyact; /* The parser action. */
|
||||||
|
var yyendofinput; /* True if we are at the end of input */
|
||||||
|
var yyerrorhit = 0; /* True if yymajor has invoked an error */
|
||||||
|
|
||||||
|
//assert( yypParser->yytos!=0 );
|
||||||
|
|
||||||
|
if (yymajor === undefined || yymajor === null) {
|
||||||
|
yymajor = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
yyendofinput = yymajor == 0;
|
||||||
|
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Input '" + this.yyTokenName[yymajor] + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
yyact = this.yy_find_shift_action(yymajor);
|
||||||
|
if (yyact <= this.YY_MAX_SHIFTREDUCE) { // check me?
|
||||||
|
this.yy_shift(yyact, yymajor, yyminor);
|
||||||
|
if (!this.YYNOERRORRECOVERY) {
|
||||||
|
this.yyerrcnt--;
|
||||||
|
}
|
||||||
|
yymajor = this.YYNOCODE;
|
||||||
|
} else if (yyact <= this.YY_MAX_REDUCE) { // check me?
|
||||||
|
this.yy_reduce(yyact - this.YY_MIN_REDUCE); // check me?
|
||||||
|
} else {
|
||||||
|
// assert( yyact == YY_ERROR_ACTION );
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Syntax Error!");
|
||||||
|
}
|
||||||
|
if (this.YYERRORSYMBOL) {
|
||||||
|
/* A syntax error has occurred.
|
||||||
|
** The response to an error depends upon whether or not the
|
||||||
|
** grammar defines an error token "ERROR".
|
||||||
|
**
|
||||||
|
** This is what we do if the grammar does define ERROR:
|
||||||
|
**
|
||||||
|
** * Call the %syntax_error function.
|
||||||
|
**
|
||||||
|
** * Begin popping the stack until we enter a state where
|
||||||
|
** it is legal to shift the error symbol, then shift
|
||||||
|
** the error symbol.
|
||||||
|
**
|
||||||
|
** * Set the error count to three.
|
||||||
|
**
|
||||||
|
** * Begin accepting and shifting new tokens. No new error
|
||||||
|
** processing will occur until three tokens have been
|
||||||
|
** shifted successfully.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
if (this.yyerrcnt < 0) {
|
||||||
|
this.yy_syntax_error(yymajor, yyminor);
|
||||||
|
}
|
||||||
|
var yymx = this.yystack[this.yyidx].major;
|
||||||
|
if (yymx == this.YYERRORSYMBOL || yyerrorhit) {
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Discard input token " + this.yyTokenName[yymajor]);
|
||||||
|
}
|
||||||
|
this.yy_destructor(yymajor, yyminor);
|
||||||
|
yymajor = this.YYNOCODE;
|
||||||
|
} else {
|
||||||
|
while (this.yyidx >= 0
|
||||||
|
&& yymx != this.YYERRORSYMBOL
|
||||||
|
&& (yyact = this.yy_find_reduce_action(
|
||||||
|
this.yystack[this.yyidx].stateno,
|
||||||
|
this.YYERRORSYMBOL)) >= this.YY_MIN_REDUCE // check me?
|
||||||
|
) {
|
||||||
|
this.yy_pop_parser_stack();
|
||||||
|
}
|
||||||
|
if (this.yyidx < 0 || yymajor == 0) {
|
||||||
|
this.yy_destructor(yymajor, yyminor);
|
||||||
|
this.yy_parse_failed();
|
||||||
|
if (!this.YYNOERRORRECOVERY) {
|
||||||
|
this.yyerrcnt = -1;
|
||||||
|
}
|
||||||
|
yymajor = this.YYNOCODE;
|
||||||
|
} else if (yymx != this.YYERRORSYMBOL) {
|
||||||
|
this.yy_shift(yyact, this.YYERRORSYMBOL, yyminor); // check me?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.yyerrcnt = 3;
|
||||||
|
yyerrorhit = 1;
|
||||||
|
} else if (this.YYNOERRORRECOVERY) {
|
||||||
|
/* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
|
||||||
|
** do any kind of error recovery. Instead, simply invoke the syntax
|
||||||
|
** error routine and continue going as if nothing had happened.
|
||||||
|
**
|
||||||
|
** Applications can set this macro (for example inside %include) if
|
||||||
|
** they intend to abandon the parse upon the first syntax error seen.
|
||||||
|
*/
|
||||||
|
this.yy_syntax_error(yymajor, yyminor);
|
||||||
|
this.yy_destructor(yymajor, yyminor);
|
||||||
|
yymajor = this.YYNOCODE;
|
||||||
|
} else { /* YYERRORSYMBOL is not defined */
|
||||||
|
/* This is what we do if the grammar does not define ERROR:
|
||||||
|
**
|
||||||
|
** * Report an error message, and throw away the input token.
|
||||||
|
**
|
||||||
|
** * If the input token is $, then fail the parse.
|
||||||
|
**
|
||||||
|
** As before, subsequent error messages are suppressed until
|
||||||
|
** three input tokens have been successfully shifted.
|
||||||
|
*/
|
||||||
|
if (this.yyerrcnt <= 0) {
|
||||||
|
this.yy_syntax_error(yymajor, yyminor);
|
||||||
|
}
|
||||||
|
this.yyerrcnt = 3;
|
||||||
|
this.yy_destructor(yymajor, yyminor);
|
||||||
|
if (yyendofinput) {
|
||||||
|
this.yy_parse_failed();
|
||||||
|
if (!this.YYNOERRORRECOVERY) {
|
||||||
|
this.yyerrcnt = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yymajor = this.YYNOCODE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (yymajor != this.YYNOCODE && this.yyidx > 0);
|
||||||
|
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
var remainingTokens = [];
|
||||||
|
for (var i = 1; i <= this.yyidx; i++) {
|
||||||
|
remainingTokens.push(this.yyTokenName[this.yystack[i].major]);
|
||||||
|
}
|
||||||
|
this.trace("Return. Stack=[" + remainingTokens.join(" ") + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
|
||||||
|
} // function Parser()
|
||||||
|
// line 12 "examples/calculator-js.y"
|
||||||
|
|
||||||
|
var Lexer = require('../lexer/lexer');
|
||||||
|
|
||||||
|
var parser = new Parser();
|
||||||
|
|
||||||
|
parser.setTraceCallback(function (value) {
|
||||||
|
process.stdout.write(value);
|
||||||
|
}, '> ');
|
||||||
|
|
||||||
|
var lexer = new Lexer();
|
||||||
|
|
||||||
|
lexer.addRule(/\d+/, function (value) {
|
||||||
|
return { major: parser.TOKEN_INTEGER, minor: parseInt(value, 10) };
|
||||||
|
});
|
||||||
|
lexer.addRule('+', function (value) {
|
||||||
|
return { major: parser.TOKEN_PLUS, minor: null };
|
||||||
|
});
|
||||||
|
lexer.addRule('-', function (value) {
|
||||||
|
return { major: parser.TOKEN_MINUS, minor: null };
|
||||||
|
});
|
||||||
|
lexer.addRule('/', function (value) {
|
||||||
|
return { major: parser.TOKEN_DIVIDE, minor: null };
|
||||||
|
});
|
||||||
|
lexer.addRule('*', function (value) {
|
||||||
|
return { major: parser.TOKEN_TIMES, minor: null };
|
||||||
|
});
|
||||||
|
lexer.addRule(/\s+/, function () {});
|
||||||
|
|
||||||
|
var data = '';
|
||||||
|
|
||||||
|
process.stdin.on('data', function (chunk) {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
process.stdin.on('end', function () {
|
||||||
|
var token;
|
||||||
|
|
||||||
|
lexer.setInput(data);
|
||||||
|
|
||||||
|
while (token = lexer.lex()) {
|
||||||
|
parser.parse(token.major, token.minor);
|
||||||
|
}
|
||||||
|
|
||||||
|
parser.parse();
|
||||||
|
});
|
||||||
|
// line 918 "examples/calculator-js.js"
|
102
examples/calculator-js.out
Normal file
102
examples/calculator-js.out
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
State 0:
|
||||||
|
program ::= * expr
|
||||||
|
expr ::= * expr MINUS expr
|
||||||
|
expr ::= * expr PLUS expr
|
||||||
|
expr ::= * expr TIMES expr
|
||||||
|
expr ::= * expr DIVIDE expr
|
||||||
|
expr ::= * INTEGER
|
||||||
|
|
||||||
|
INTEGER shift-reduce 5 expr ::= INTEGER
|
||||||
|
program accept
|
||||||
|
expr shift 5
|
||||||
|
|
||||||
|
State 1:
|
||||||
|
expr ::= * expr MINUS expr
|
||||||
|
expr ::= * expr PLUS expr
|
||||||
|
expr ::= * expr TIMES expr
|
||||||
|
expr ::= * expr DIVIDE expr
|
||||||
|
expr ::= expr DIVIDE * expr
|
||||||
|
expr ::= * INTEGER
|
||||||
|
|
||||||
|
INTEGER shift-reduce 5 expr ::= INTEGER
|
||||||
|
expr shift-reduce 4 expr ::= expr DIVIDE expr
|
||||||
|
|
||||||
|
State 2:
|
||||||
|
expr ::= * expr MINUS expr
|
||||||
|
expr ::= * expr PLUS expr
|
||||||
|
expr ::= * expr TIMES expr
|
||||||
|
expr ::= expr TIMES * expr
|
||||||
|
expr ::= * expr DIVIDE expr
|
||||||
|
expr ::= * INTEGER
|
||||||
|
|
||||||
|
INTEGER shift-reduce 5 expr ::= INTEGER
|
||||||
|
expr shift-reduce 3 expr ::= expr TIMES expr
|
||||||
|
|
||||||
|
State 3:
|
||||||
|
expr ::= * expr MINUS expr
|
||||||
|
expr ::= * expr PLUS expr
|
||||||
|
expr ::= expr PLUS * expr
|
||||||
|
expr ::= * expr TIMES expr
|
||||||
|
expr ::= * expr DIVIDE expr
|
||||||
|
expr ::= * INTEGER
|
||||||
|
|
||||||
|
INTEGER shift-reduce 5 expr ::= INTEGER
|
||||||
|
expr shift 6
|
||||||
|
|
||||||
|
State 4:
|
||||||
|
expr ::= * expr MINUS expr
|
||||||
|
expr ::= expr MINUS * expr
|
||||||
|
expr ::= * expr PLUS expr
|
||||||
|
expr ::= * expr TIMES expr
|
||||||
|
expr ::= * expr DIVIDE expr
|
||||||
|
expr ::= * INTEGER
|
||||||
|
|
||||||
|
INTEGER shift-reduce 5 expr ::= INTEGER
|
||||||
|
expr shift 7
|
||||||
|
|
||||||
|
State 5:
|
||||||
|
(0) program ::= expr *
|
||||||
|
expr ::= expr * MINUS expr
|
||||||
|
expr ::= expr * PLUS expr
|
||||||
|
expr ::= expr * TIMES expr
|
||||||
|
expr ::= expr * DIVIDE expr
|
||||||
|
|
||||||
|
$ reduce 0 program ::= expr
|
||||||
|
PLUS shift 3
|
||||||
|
MINUS shift 4
|
||||||
|
DIVIDE shift 1
|
||||||
|
TIMES shift 2
|
||||||
|
|
||||||
|
State 6:
|
||||||
|
expr ::= expr * MINUS expr
|
||||||
|
expr ::= expr * PLUS expr
|
||||||
|
(2) expr ::= expr PLUS expr *
|
||||||
|
expr ::= expr * TIMES expr
|
||||||
|
expr ::= expr * DIVIDE expr
|
||||||
|
|
||||||
|
DIVIDE shift 1
|
||||||
|
TIMES shift 2
|
||||||
|
{default} reduce 2 expr ::= expr PLUS expr
|
||||||
|
|
||||||
|
State 7:
|
||||||
|
expr ::= expr * MINUS expr
|
||||||
|
(1) expr ::= expr MINUS expr *
|
||||||
|
expr ::= expr * PLUS expr
|
||||||
|
expr ::= expr * TIMES expr
|
||||||
|
expr ::= expr * DIVIDE expr
|
||||||
|
|
||||||
|
DIVIDE shift 1
|
||||||
|
TIMES shift 2
|
||||||
|
{default} reduce 1 expr ::= expr MINUS expr
|
||||||
|
|
||||||
|
----------------------------------------------------
|
||||||
|
Symbols:
|
||||||
|
0: $:
|
||||||
|
1: PLUS
|
||||||
|
2: MINUS
|
||||||
|
3: DIVIDE
|
||||||
|
4: TIMES
|
||||||
|
5: INTEGER
|
||||||
|
6: error:
|
||||||
|
7: program: INTEGER
|
||||||
|
8: expr: INTEGER
|
74
examples/calculator-js.y
Normal file
74
examples/calculator-js.y
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
%name Parser
|
||||||
|
|
||||||
|
%token_prefix TOKEN_
|
||||||
|
|
||||||
|
%left PLUS MINUS.
|
||||||
|
%left DIVIDE TIMES.
|
||||||
|
|
||||||
|
%include {
|
||||||
|
// include something
|
||||||
|
}
|
||||||
|
|
||||||
|
%code {
|
||||||
|
var Lexer = require('../lexer/lexer');
|
||||||
|
|
||||||
|
var parser = new Parser();
|
||||||
|
|
||||||
|
parser.setTraceCallback(function (value) {
|
||||||
|
process.stdout.write(value);
|
||||||
|
}, '> ');
|
||||||
|
|
||||||
|
var lexer = new Lexer();
|
||||||
|
|
||||||
|
lexer.addRule(/\d+/, function (value) {
|
||||||
|
return { major: parser.TOKEN_INTEGER, minor: parseInt(value, 10) };
|
||||||
|
});
|
||||||
|
lexer.addRule('+', function (value) {
|
||||||
|
return { major: parser.TOKEN_PLUS, minor: null };
|
||||||
|
});
|
||||||
|
lexer.addRule('-', function (value) {
|
||||||
|
return { major: parser.TOKEN_MINUS, minor: null };
|
||||||
|
});
|
||||||
|
lexer.addRule('/', function (value) {
|
||||||
|
return { major: parser.TOKEN_DIVIDE, minor: null };
|
||||||
|
});
|
||||||
|
lexer.addRule('*', function (value) {
|
||||||
|
return { major: parser.TOKEN_TIMES, minor: null };
|
||||||
|
});
|
||||||
|
lexer.addRule(/\s+/, function () {});
|
||||||
|
|
||||||
|
var data = '';
|
||||||
|
|
||||||
|
process.stdin.on('data', function (chunk) {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
process.stdin.on('end', function () {
|
||||||
|
var token;
|
||||||
|
|
||||||
|
lexer.setInput(data);
|
||||||
|
|
||||||
|
while (token = lexer.lex()) {
|
||||||
|
parser.parse(token.major, token.minor);
|
||||||
|
}
|
||||||
|
|
||||||
|
parser.parse();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
%syntax_error {
|
||||||
|
console.log("Syntax error");
|
||||||
|
}
|
||||||
|
|
||||||
|
program ::= expr(A). { console.log("Result=" + A); }
|
||||||
|
expr(A) ::= expr(B) MINUS expr(C). { A = B - C; }
|
||||||
|
expr(A) ::= expr(B) PLUS expr(C). { A = B + C; }
|
||||||
|
expr(A) ::= expr(B) TIMES expr(C). { A = B * C; }
|
||||||
|
expr(A) ::= expr(B) DIVIDE expr(C). {
|
||||||
|
if (C != 0) {
|
||||||
|
A = B / C;
|
||||||
|
} else {
|
||||||
|
throw new Error("Divide by zero");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expr(A) ::= INTEGER(B). { A = B; }
|
BIN
lemon-src/lemon-js
Executable file
BIN
lemon-src/lemon-js
Executable file
Binary file not shown.
5442
lemon-src/lemon-js.c
Normal file
5442
lemon-src/lemon-js.c
Normal file
File diff suppressed because it is too large
Load Diff
5436
lemon-src/lemon.c
Normal file
5436
lemon-src/lemon.c
Normal file
File diff suppressed because it is too large
Load Diff
775
lemon-src/lempar.js
Normal file
775
lemon-src/lempar.js
Normal file
@ -0,0 +1,775 @@
|
|||||||
|
/*
|
||||||
|
** 2000-05-29
|
||||||
|
**
|
||||||
|
** The author disclaims copyright to this source code. In place of
|
||||||
|
** a legal notice, here is a blessing:
|
||||||
|
**
|
||||||
|
** May you do good and not evil.
|
||||||
|
** May you find forgiveness for yourself and forgive others.
|
||||||
|
** May you share freely, never taking more than you give.
|
||||||
|
**
|
||||||
|
** Based on SQLite distribution v3.17.0
|
||||||
|
** Adopted for JavaScript by Artem Butusov <art.sormy@gmail.com>
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** Driver template for the LEMON parser generator.
|
||||||
|
**
|
||||||
|
** The "lemon" program processes an LALR(1) input grammar file, then uses
|
||||||
|
** this template to construct a parser. The "lemon" program inserts text
|
||||||
|
** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the
|
||||||
|
** interstitial "-" characters) contained in this template is changed into
|
||||||
|
** the value of the %name directive from the grammar. Otherwise, the content
|
||||||
|
** of this template is copied straight through into the generate parser
|
||||||
|
** source file.
|
||||||
|
**
|
||||||
|
** The following is the concatenation of all %include directives from the
|
||||||
|
** input grammar file:
|
||||||
|
*/
|
||||||
|
/************ Begin %include sections from the grammar ************************/
|
||||||
|
%%
|
||||||
|
/**************** End of %include directives **********************************/
|
||||||
|
function Parse() {
|
||||||
|
/* These constants specify the various numeric values for terminal symbols
|
||||||
|
** in a format understandable to "makeheaders".
|
||||||
|
***************** Begin makeheaders token definitions *************************/
|
||||||
|
%%
|
||||||
|
/**************** End makeheaders token definitions ***************************/
|
||||||
|
|
||||||
|
/* The next sections is a series of control #defines.
|
||||||
|
** various aspects of the generated parser.
|
||||||
|
** YYNOCODE is a number of type YYCODETYPE that is not used for
|
||||||
|
** any terminal or nonterminal symbol.
|
||||||
|
** YYFALLBACK If defined, this indicates that one or more tokens
|
||||||
|
** (also known as: "terminal symbols") have fall-back
|
||||||
|
** values which should be used if the original symbol
|
||||||
|
** would not parse. This permits keywords to sometimes
|
||||||
|
** be used as identifiers, for example.
|
||||||
|
** YYSTACKDEPTH is the maximum depth of the parser's stack. If
|
||||||
|
** zero the stack is dynamically sized using realloc()
|
||||||
|
** YYERRORSYMBOL is the code number of the error symbol. If not
|
||||||
|
** defined, then do no error processing.
|
||||||
|
** YYNSTATE the combined number of states.
|
||||||
|
** YYNRULE the number of rules in the grammar
|
||||||
|
** YY_MAX_SHIFT Maximum value for shift actions
|
||||||
|
** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
|
||||||
|
** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
|
||||||
|
** YY_MIN_REDUCE Maximum value for reduce actions
|
||||||
|
** YY_ERROR_ACTION The yy_action[] code for syntax error
|
||||||
|
** YY_ACCEPT_ACTION The yy_action[] code for accept
|
||||||
|
** YY_NO_ACTION The yy_action[] code for no-op
|
||||||
|
*/
|
||||||
|
/************* Begin control #defines *****************************************/
|
||||||
|
%%
|
||||||
|
/************* End control #defines *******************************************/
|
||||||
|
|
||||||
|
/* Define the yytestcase() macro to be a no-op if is not already defined
|
||||||
|
** otherwise.
|
||||||
|
**
|
||||||
|
** Applications can choose to define yytestcase() in the %include section
|
||||||
|
** to a macro that can assist in verifying code coverage. For production
|
||||||
|
** code the yytestcase() macro should be turned off. But it is useful
|
||||||
|
** for testing.
|
||||||
|
*/
|
||||||
|
if (!this.yytestcase) {
|
||||||
|
this.yytestcase = function () {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Next are the tables used to determine what action to take based on the
|
||||||
|
** current state and lookahead token. These tables are used to implement
|
||||||
|
** functions that take a state number and lookahead value and return an
|
||||||
|
** action integer.
|
||||||
|
**
|
||||||
|
** Suppose the action integer is N. Then the action is determined as
|
||||||
|
** follows
|
||||||
|
**
|
||||||
|
** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead
|
||||||
|
** token onto the stack and goto state N.
|
||||||
|
**
|
||||||
|
** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then
|
||||||
|
** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE.
|
||||||
|
**
|
||||||
|
** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
|
||||||
|
** and YY_MAX_REDUCE
|
||||||
|
**
|
||||||
|
** N == YY_ERROR_ACTION A syntax error has occurred.
|
||||||
|
**
|
||||||
|
** N == YY_ACCEPT_ACTION The parser accepts its input.
|
||||||
|
**
|
||||||
|
** N == YY_NO_ACTION No such action. Denotes unused
|
||||||
|
** slots in the yy_action[] table.
|
||||||
|
**
|
||||||
|
** The action table is constructed as a single large table named yy_action[].
|
||||||
|
** Given state S and lookahead X, the action is computed as either:
|
||||||
|
**
|
||||||
|
** (A) N = yy_action[ yy_shift_ofst[S] + X ]
|
||||||
|
** (B) N = yy_default[S]
|
||||||
|
**
|
||||||
|
** The (A) formula is preferred. The B formula is used instead if:
|
||||||
|
** (1) The yy_shift_ofst[S]+X value is out of range, or
|
||||||
|
** (2) yy_lookahead[yy_shift_ofst[S]+X] is not equal to X, or
|
||||||
|
** (3) yy_shift_ofst[S] equal YY_SHIFT_USE_DFLT.
|
||||||
|
** (Implementation note: YY_SHIFT_USE_DFLT is chosen so that
|
||||||
|
** YY_SHIFT_USE_DFLT+X will be out of range for all possible lookaheads X.
|
||||||
|
** Hence only tests (1) and (2) need to be evaluated.)
|
||||||
|
**
|
||||||
|
** The formulas above are for computing the action when the lookahead is
|
||||||
|
** a terminal symbol. If the lookahead is a non-terminal (as occurs after
|
||||||
|
** a reduce action) then the yy_reduce_ofst[] array is used in place of
|
||||||
|
** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
|
||||||
|
** YY_SHIFT_USE_DFLT.
|
||||||
|
**
|
||||||
|
** The following are the tables generated in this section:
|
||||||
|
**
|
||||||
|
** yy_action[] A single table containing all actions.
|
||||||
|
** yy_lookahead[] A table containing the lookahead for each entry in
|
||||||
|
** yy_action. Used to detect hash collisions.
|
||||||
|
** yy_shift_ofst[] For each state, the offset into yy_action for
|
||||||
|
** shifting terminals.
|
||||||
|
** yy_reduce_ofst[] For each state, the offset into yy_action for
|
||||||
|
** shifting non-terminals after a reduce.
|
||||||
|
** yy_default[] Default action for each state.
|
||||||
|
**
|
||||||
|
*********** Begin parsing tables **********************************************/
|
||||||
|
%%
|
||||||
|
/********** End of lemon-generated parsing tables *****************************/
|
||||||
|
|
||||||
|
/* The next table maps tokens (terminal symbols) into fallback tokens.
|
||||||
|
** If a construct like the following:
|
||||||
|
**
|
||||||
|
** %fallback ID X Y Z.
|
||||||
|
**
|
||||||
|
** appears in the grammar, then ID becomes a fallback token for X, Y,
|
||||||
|
** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
|
||||||
|
** but it does not parse, the type of the token is changed to ID and
|
||||||
|
** the parse is retried before an error is thrown.
|
||||||
|
**
|
||||||
|
** This feature can be used, for example, to cause some keywords in a language
|
||||||
|
** to revert to identifiers if they keyword does not apply in the context where
|
||||||
|
** it appears.
|
||||||
|
*/
|
||||||
|
this.yyFallback = [
|
||||||
|
%%
|
||||||
|
];
|
||||||
|
|
||||||
|
/* The following structure represents a single element of the
|
||||||
|
** parser's stack. Information stored includes:
|
||||||
|
**
|
||||||
|
** + The state number for the parser at this level of the stack.
|
||||||
|
**
|
||||||
|
** + The value of the token stored at this level of the stack.
|
||||||
|
** (In other words, the "major" token.)
|
||||||
|
**
|
||||||
|
** + The semantic value stored at this level of the stack. This is
|
||||||
|
** the information used by the action routines in the grammar.
|
||||||
|
** It is sometimes called the "minor" token.
|
||||||
|
**
|
||||||
|
** After the "shift" half of a SHIFTREDUCE action, the stateno field
|
||||||
|
** actually contains the reduce action for the second half of the
|
||||||
|
** SHIFTREDUCE.
|
||||||
|
*/
|
||||||
|
//{
|
||||||
|
// stateno, /* The state-number, or reduce action in SHIFTREDUCE */
|
||||||
|
// major, /* The major token value. This is the code
|
||||||
|
// ** number for the token at this stack level */
|
||||||
|
// minor, /* The user-supplied minor token value. This
|
||||||
|
// ** is the value of the token */
|
||||||
|
//}
|
||||||
|
|
||||||
|
/* The state of the parser is completely contained in an instance of
|
||||||
|
** the following structure */
|
||||||
|
this.yyhwm = 0; /* High-water mark of the stack */
|
||||||
|
this.yyerrcnt = -1; /* Shifts left before out of the error */
|
||||||
|
this.yystack = null; /* The parser's stack */
|
||||||
|
this.yyidx = -1; /* Stack index of current element in the stack */
|
||||||
|
|
||||||
|
this.yyTraceCallback = null;
|
||||||
|
this.yyTracePrompt = "";
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Turn parser tracing on by giving a stream to which to write the trace
|
||||||
|
** and a prompt to preface each trace message. Tracing is turned off
|
||||||
|
** by making either argument NULL
|
||||||
|
**
|
||||||
|
** Inputs:
|
||||||
|
** <ul>
|
||||||
|
** <li> A callback to which trace output should be written.
|
||||||
|
** If NULL, then tracing is turned off.
|
||||||
|
** <li> A prefix string written at the beginning of every
|
||||||
|
** line of trace output. Default is "".
|
||||||
|
** </ul>
|
||||||
|
**
|
||||||
|
** Outputs:
|
||||||
|
** None.
|
||||||
|
*/
|
||||||
|
this.setTraceCallback = function (callback, prompt) {
|
||||||
|
this.yyTraceCallback = callback;
|
||||||
|
this.yyTracePrompt = prompt || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.trace = function (message) {
|
||||||
|
this.yyTraceCallback(this.yyTracePrompt + message + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For tracing shifts, the names of all terminals and nonterminals
|
||||||
|
** are required. The following table supplies these names */
|
||||||
|
this.yyTokenName = [
|
||||||
|
%%
|
||||||
|
];
|
||||||
|
|
||||||
|
/* For tracing reduce actions, the names of all rules are required.
|
||||||
|
*/
|
||||||
|
this.yyRuleName = [
|
||||||
|
%%
|
||||||
|
];
|
||||||
|
/*
|
||||||
|
** Try to increase the size of the parser stack. Return the number
|
||||||
|
** of errors. Return 0 on success.
|
||||||
|
*/
|
||||||
|
this.yyGrowStack = function () {
|
||||||
|
// fix me: yystksz*2 + 100
|
||||||
|
this.yystack.push({
|
||||||
|
stateno: undefined,
|
||||||
|
major: undefined,
|
||||||
|
minor: undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize a new parser that has already been allocated.
|
||||||
|
*/
|
||||||
|
this.init = function () {
|
||||||
|
this.yyhwm = 0;
|
||||||
|
this.yyerrcnt = -1;
|
||||||
|
this.yyidx = 0;
|
||||||
|
if (this.YYSTACKDEPTH <= 0) {
|
||||||
|
this.yystack = [];
|
||||||
|
this.yyGrowStack();
|
||||||
|
} else {
|
||||||
|
this.yystack = new Array(this.YYSTACKDEPTH);
|
||||||
|
for (var i = 0; i < this.YYSTACKDEPTH; i++) {
|
||||||
|
this.yystack[i] = {
|
||||||
|
stateno: undefined,
|
||||||
|
major: undefined,
|
||||||
|
minor: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var yytos = this.yystack[0];
|
||||||
|
yytos.stateno = 0;
|
||||||
|
yytos.major = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following function deletes the "minor type" or semantic value
|
||||||
|
** associated with a symbol. The symbol can be either a terminal
|
||||||
|
** or nonterminal. "yymajor" is the symbol code, and "yypminor" is
|
||||||
|
** a pointer to the value to be deleted. The code used to do the
|
||||||
|
** deletions is derived from the %destructor and/or %token_destructor
|
||||||
|
** directives of the input grammar.
|
||||||
|
*/
|
||||||
|
this.yy_destructor = function (
|
||||||
|
yymajor, /* Type code for object to destroy */
|
||||||
|
yyminor /* The object to be destroyed */
|
||||||
|
) {
|
||||||
|
switch (yymajor) {
|
||||||
|
/* Here is inserted the actions which take place when a
|
||||||
|
** terminal or non-terminal is destroyed. This can happen
|
||||||
|
** when the symbol is popped from the stack during a
|
||||||
|
** reduce or during error processing or when a parser is
|
||||||
|
** being destroyed before it is finished parsing.
|
||||||
|
**
|
||||||
|
** Note: during a reduce, the only symbols destroyed are those
|
||||||
|
** which appear on the RHS of the rule, but which are *not* used
|
||||||
|
** inside the C code.
|
||||||
|
*/
|
||||||
|
/********* Begin destructor definitions ***************************************/
|
||||||
|
%%
|
||||||
|
/********* End destructor definitions *****************************************/
|
||||||
|
default: break; /* If no destructor action specified: do nothing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Pop the parser's stack once.
|
||||||
|
**
|
||||||
|
** If there is a destructor routine associated with the token which
|
||||||
|
** is popped from the stack, then call it.
|
||||||
|
*/
|
||||||
|
this.yy_pop_parser_stack = function () {
|
||||||
|
// assert( pParser->yytos!=0 );
|
||||||
|
// assert( pParser->yytos > pParser->yystack );
|
||||||
|
var yytos = this.yystack[this.yyidx];
|
||||||
|
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Popping " + this.yyTokenName[yytos.major]);
|
||||||
|
}
|
||||||
|
this.yy_destructor(yytos.major, yytos.minor);
|
||||||
|
|
||||||
|
this.yyidx--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Clear all secondary memory allocations from the parser
|
||||||
|
*/
|
||||||
|
this.finalize = function () {
|
||||||
|
while (this.yyidx > 0) {
|
||||||
|
this.yy_pop_parser_stack();
|
||||||
|
}
|
||||||
|
this.yystack = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Return the peak depth of the stack for a parser.
|
||||||
|
*/
|
||||||
|
this.getStackPeak = function () {
|
||||||
|
return this.yyhwm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Find the appropriate action for a parser given the terminal
|
||||||
|
** look-ahead token iLookAhead.
|
||||||
|
*/
|
||||||
|
this.yy_find_shift_action = function (
|
||||||
|
iLookAhead /* The look-ahead token */
|
||||||
|
) {
|
||||||
|
var yytos = this.yystack[this.yyidx];
|
||||||
|
var stateno = yytos.stateno;
|
||||||
|
|
||||||
|
if (stateno >= this.YY_MIN_REDUCE) {
|
||||||
|
return stateno;
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert( stateno <= YY_SHIFT_COUNT );
|
||||||
|
|
||||||
|
do {
|
||||||
|
var i = this.yy_shift_ofst[stateno];
|
||||||
|
// assert( iLookAhead!=YYNOCODE );
|
||||||
|
i += iLookAhead;
|
||||||
|
if (i < 0 || i >= this.yy_action.length || this.yy_lookahead[i] != iLookAhead) {
|
||||||
|
if (this.YYFALLBACK) {
|
||||||
|
var iFallback; /* Fallback token */
|
||||||
|
if ((iLookAhead < this.yyFallback.length)
|
||||||
|
&& (iFallback = this.yyFallback[iLookAhead]) != 0
|
||||||
|
) {
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("FALLBACK " + this.yyTokenName[iLookAhead] + " => " + this.yyTokenName[iFallback]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
|
||||||
|
iLookAhead = iFallback;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.YYWILDCARD) {
|
||||||
|
var j = i - iLookAhead + this.YYWILDCARD;
|
||||||
|
var cond1 = (this.YY_SHIFT_MIN + this.YYWILDCARD) < 0 ? j >= 0 : true;
|
||||||
|
var cond2 = (this.YY_SHIFT_MAX + this.YYWILDCARD) >= this.yy_action.length ? j < this.yy_action.length : true;
|
||||||
|
if (cond1 && cond2 && this.yy_lookahead[j] == this.YYWILDCARD && iLookAhead > 0) {
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("WILDCARD " + this.yyTokenName[iLookAhead] + " => " + this.yyTokenName[this.YYWILDCARD]);
|
||||||
|
}
|
||||||
|
return this.yy_action[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.yy_default[stateno];
|
||||||
|
} else {
|
||||||
|
return this.yy_action[i];
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Find the appropriate action for a parser given the non-terminal
|
||||||
|
** look-ahead token iLookAhead.
|
||||||
|
*/
|
||||||
|
this.yy_find_reduce_action = function (
|
||||||
|
stateno, /* Current state number */
|
||||||
|
iLookAhead /* The look-ahead token */
|
||||||
|
) {
|
||||||
|
if (this.YYERRORSYMBOL) {
|
||||||
|
if (stateno > this.YY_REDUCE_COUNT) {
|
||||||
|
return this.yy_default[stateno];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// assert( stateno<=YY_REDUCE_COUNT );
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = this.yy_reduce_ofst[stateno];
|
||||||
|
// assert( i!=YY_REDUCE_USE_DFLT );
|
||||||
|
// assert( iLookAhead!=YYNOCODE );
|
||||||
|
i += iLookAhead;
|
||||||
|
|
||||||
|
if (this.YYERRORSYMBOL) {
|
||||||
|
if (i < 0 || i >= this.yy_action.length || this.yy_lookahead[i] != iLookAhead) {
|
||||||
|
return this.yy_default[stateno];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// assert( i>=0 && i<YY_ACTTAB_COUNT );
|
||||||
|
// assert( yy_lookahead[i]==iLookAhead );
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.yy_action[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following routine is called if the stack overflows.
|
||||||
|
*/
|
||||||
|
this.yyStackOverflow = function () {
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Stack Overflow!");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (this.yyidx > 0) {
|
||||||
|
this.yy_pop_parser_stack();
|
||||||
|
}
|
||||||
|
/* Here code is inserted which will execute if the parser
|
||||||
|
** stack every overflows */
|
||||||
|
/******** Begin %stack_overflow code ******************************************/
|
||||||
|
%%
|
||||||
|
/******** End %stack_overflow code ********************************************/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Print tracing information for a SHIFT action
|
||||||
|
*/
|
||||||
|
this.yyTraceShift = function (yyNewState) {
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
var yytos = this.yystack[this.yyidx];
|
||||||
|
if (yyNewState < this.YYNSTATE) {
|
||||||
|
this.trace("Shift '" + this.yyTokenName[yytos.major] + "', go to state " + yyNewState);
|
||||||
|
} else {
|
||||||
|
this.trace("Shift '" + this.yyTokenName[yytos.major] + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Perform a shift action.
|
||||||
|
*/
|
||||||
|
this.yy_shift = function (
|
||||||
|
yyNewState, /* The new state to shift in */
|
||||||
|
yyMajor, /* The major token to shift in */
|
||||||
|
yyMinor /* The minor token to shift in */
|
||||||
|
) {
|
||||||
|
this.yyidx++;
|
||||||
|
|
||||||
|
if (this.yyidx > this.yyhwm) {
|
||||||
|
this.yyhwm++;
|
||||||
|
// assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.YYSTACKDEPTH > 0) {
|
||||||
|
if (this.yyidx >= this.YYSTACKDEPTH) {
|
||||||
|
this.yyidx--;
|
||||||
|
this.yyStackOverflow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.yyidx >= this.yystack.length) {
|
||||||
|
this.yyGrowStack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (yyNewState > this.YY_MAX_SHIFT) {
|
||||||
|
yyNewState += this.YY_MIN_REDUCE - this.YY_MIN_SHIFTREDUCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
var yytos = this.yystack[this.yyidx];
|
||||||
|
yytos.stateno = yyNewState;
|
||||||
|
yytos.major = yyMajor;
|
||||||
|
yytos.minor = yyMinor;
|
||||||
|
|
||||||
|
this.yyTraceShift(yyNewState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following table contains information about every rule that
|
||||||
|
** is used during the reduce.
|
||||||
|
*/
|
||||||
|
//{
|
||||||
|
// lhs, /* Symbol on the left-hand side of the rule */
|
||||||
|
// nrhs, /* Number of right-hand side symbols in the rule */
|
||||||
|
//}
|
||||||
|
this.yyRuleInfo = [
|
||||||
|
%%
|
||||||
|
];
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Perform a reduce action and the shift that must immediately
|
||||||
|
** follow the reduce.
|
||||||
|
*/
|
||||||
|
this.yy_reduce = function (
|
||||||
|
yyruleno /* Number of the rule by which to reduce */
|
||||||
|
){
|
||||||
|
var yymsp = this.yystack[this.yyidx]; /* The top of the parser's stack */
|
||||||
|
|
||||||
|
if (yyruleno < this.yyRuleName.length) {
|
||||||
|
var yysize = this.yyRuleInfo[yyruleno].nrhs;
|
||||||
|
var ruleName = this.yyRuleName[yyruleno];
|
||||||
|
var newStateNo = this.yystack[this.yyidx - yysize].stateno;
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Reduce [" + ruleName + "], go to state " + newStateNo + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the stack is large enough to grow by a single entry
|
||||||
|
** if the RHS of the rule is empty. This ensures that there is room
|
||||||
|
** enough on the stack to push the LHS value */
|
||||||
|
if (this.yyRuleInfo[yyruleno].nrhs == 0) {
|
||||||
|
if (this.yyidx > this.yyhwm) {
|
||||||
|
this.yyhwm++;
|
||||||
|
// assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack));
|
||||||
|
}
|
||||||
|
if (this.YYSTACKDEPTH > 0) {
|
||||||
|
if (this.yyidx >= this.YYSTACKDEPTH - 1) {
|
||||||
|
this.yyStackOverflow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.yyidx >= this.yystack.length - 1) {
|
||||||
|
this.yyGrowStack();
|
||||||
|
yymsp = this.yystack[this.yyidx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var yylhsminor;
|
||||||
|
switch (yyruleno) {
|
||||||
|
/* Beginning here are the reduction cases. A typical example
|
||||||
|
** follows:
|
||||||
|
** case 0:
|
||||||
|
** #line <lineno> <grammarfile>
|
||||||
|
** { ... } // User supplied code
|
||||||
|
** #line <lineno> <thisfile>
|
||||||
|
** break;
|
||||||
|
*/
|
||||||
|
/********** Begin reduce actions **********************************************/
|
||||||
|
%%
|
||||||
|
/********** End reduce actions ************************************************/
|
||||||
|
};
|
||||||
|
// assert( yyruleno<sizeof(yyRuleInfo)/sizeof(yyRuleInfo[0]) );
|
||||||
|
|
||||||
|
var yygoto = this.yyRuleInfo[yyruleno].lhs; /* The next state */
|
||||||
|
var yysize = this.yyRuleInfo[yyruleno].nrhs; /* Amount to pop the stack */
|
||||||
|
var yyact = this.yy_find_reduce_action( /* The next action */
|
||||||
|
this.yystack[this.yyidx - yysize].stateno,
|
||||||
|
yygoto
|
||||||
|
);
|
||||||
|
if (yyact <= this.YY_MAX_SHIFTREDUCE) {
|
||||||
|
if (yyact > this.YY_MAX_SHIFT) {
|
||||||
|
yyact += this.YY_MIN_REDUCE - this.YY_MIN_SHIFTREDUCE;
|
||||||
|
}
|
||||||
|
this.yyidx -= yysize - 1;
|
||||||
|
yymsp = this.yystack[this.yyidx];
|
||||||
|
yymsp.stateno = yyact;
|
||||||
|
yymsp.major = yygoto;
|
||||||
|
this.yyTraceShift(yyact);
|
||||||
|
} else {
|
||||||
|
// assert( yyact == YY_ACCEPT_ACTION );
|
||||||
|
this.yyidx -= yysize;
|
||||||
|
this.yy_accept();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following code executes when the parse fails
|
||||||
|
*/
|
||||||
|
this.yy_parse_failed = function () {
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Fail!");
|
||||||
|
}
|
||||||
|
while (this.yyidx > 0) {
|
||||||
|
this.yy_pop_parser_stack();
|
||||||
|
}
|
||||||
|
/* Here code is inserted which will be executed whenever the
|
||||||
|
** parser fails */
|
||||||
|
/************ Begin %parse_failure code ***************************************/
|
||||||
|
%%
|
||||||
|
/************ End %parse_failure code *****************************************/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following code executes when a syntax error first occurs.
|
||||||
|
*/
|
||||||
|
this.yy_syntax_error = function (
|
||||||
|
yymajor, /* The major type of the error token */
|
||||||
|
yyminor /* The minor type of the error token */
|
||||||
|
) {
|
||||||
|
var TOKEN = yyminor;
|
||||||
|
/************ Begin %syntax_error code ****************************************/
|
||||||
|
%%
|
||||||
|
/************ End %syntax_error code ******************************************/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following is executed when the parser accepts
|
||||||
|
*/
|
||||||
|
this.yy_accept = function () {
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Accept!");
|
||||||
|
}
|
||||||
|
if (!this.YYNOERRORRECOVERY) {
|
||||||
|
this.yyerrcnt = -1;
|
||||||
|
}
|
||||||
|
// assert( yypParser->yytos==yypParser->yystack );
|
||||||
|
/* Here code is inserted which will be executed whenever the
|
||||||
|
** parser accepts */
|
||||||
|
/*********** Begin %parse_accept code *****************************************/
|
||||||
|
%%
|
||||||
|
/*********** End %parse_accept code *******************************************/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The main parser program.
|
||||||
|
** The first argument is a pointer to a structure obtained from
|
||||||
|
** "ParseAlloc" which describes the current state of the parser.
|
||||||
|
** The second argument is the major token number. The third is
|
||||||
|
** the minor token. The fourth optional argument is whatever the
|
||||||
|
** user wants (and specified in the grammar) and is available for
|
||||||
|
** use by the action routines.
|
||||||
|
**
|
||||||
|
** Inputs:
|
||||||
|
** <ul>
|
||||||
|
** <li> A pointer to the parser (an opaque structure.)
|
||||||
|
** <li> The major token number.
|
||||||
|
** <li> The minor token number.
|
||||||
|
** <li> An option argument of a grammar-specified type.
|
||||||
|
** </ul>
|
||||||
|
**
|
||||||
|
** Outputs:
|
||||||
|
** None.
|
||||||
|
*/
|
||||||
|
this.parse = function (
|
||||||
|
yymajor, /* The major token code number */
|
||||||
|
yyminor /* The value for the token */
|
||||||
|
) {
|
||||||
|
var yyact; /* The parser action. */
|
||||||
|
var yyendofinput; /* True if we are at the end of input */
|
||||||
|
var yyerrorhit = 0; /* True if yymajor has invoked an error */
|
||||||
|
|
||||||
|
//assert( yypParser->yytos!=0 );
|
||||||
|
|
||||||
|
if (yymajor === undefined || yymajor === null) {
|
||||||
|
yymajor = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
yyendofinput = yymajor == 0;
|
||||||
|
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Input '" + this.yyTokenName[yymajor] + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
yyact = this.yy_find_shift_action(yymajor);
|
||||||
|
if (yyact <= this.YY_MAX_SHIFTREDUCE) { // check me?
|
||||||
|
this.yy_shift(yyact, yymajor, yyminor);
|
||||||
|
if (!this.YYNOERRORRECOVERY) {
|
||||||
|
this.yyerrcnt--;
|
||||||
|
}
|
||||||
|
yymajor = this.YYNOCODE;
|
||||||
|
} else if (yyact <= this.YY_MAX_REDUCE) { // check me?
|
||||||
|
this.yy_reduce(yyact - this.YY_MIN_REDUCE); // check me?
|
||||||
|
} else {
|
||||||
|
// assert( yyact == YY_ERROR_ACTION );
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Syntax Error!");
|
||||||
|
}
|
||||||
|
if (this.YYERRORSYMBOL) {
|
||||||
|
/* A syntax error has occurred.
|
||||||
|
** The response to an error depends upon whether or not the
|
||||||
|
** grammar defines an error token "ERROR".
|
||||||
|
**
|
||||||
|
** This is what we do if the grammar does define ERROR:
|
||||||
|
**
|
||||||
|
** * Call the %syntax_error function.
|
||||||
|
**
|
||||||
|
** * Begin popping the stack until we enter a state where
|
||||||
|
** it is legal to shift the error symbol, then shift
|
||||||
|
** the error symbol.
|
||||||
|
**
|
||||||
|
** * Set the error count to three.
|
||||||
|
**
|
||||||
|
** * Begin accepting and shifting new tokens. No new error
|
||||||
|
** processing will occur until three tokens have been
|
||||||
|
** shifted successfully.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
if (this.yyerrcnt < 0) {
|
||||||
|
this.yy_syntax_error(yymajor, yyminor);
|
||||||
|
}
|
||||||
|
var yymx = this.yystack[this.yyidx].major;
|
||||||
|
if (yymx == this.YYERRORSYMBOL || yyerrorhit) {
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
this.trace("Discard input token " + this.yyTokenName[yymajor]);
|
||||||
|
}
|
||||||
|
this.yy_destructor(yymajor, yyminor);
|
||||||
|
yymajor = this.YYNOCODE;
|
||||||
|
} else {
|
||||||
|
while (this.yyidx >= 0
|
||||||
|
&& yymx != this.YYERRORSYMBOL
|
||||||
|
&& (yyact = this.yy_find_reduce_action(
|
||||||
|
this.yystack[this.yyidx].stateno,
|
||||||
|
this.YYERRORSYMBOL)) >= this.YY_MIN_REDUCE // check me?
|
||||||
|
) {
|
||||||
|
this.yy_pop_parser_stack();
|
||||||
|
}
|
||||||
|
if (this.yyidx < 0 || yymajor == 0) {
|
||||||
|
this.yy_destructor(yymajor, yyminor);
|
||||||
|
this.yy_parse_failed();
|
||||||
|
if (!this.YYNOERRORRECOVERY) {
|
||||||
|
this.yyerrcnt = -1;
|
||||||
|
}
|
||||||
|
yymajor = this.YYNOCODE;
|
||||||
|
} else if (yymx != this.YYERRORSYMBOL) {
|
||||||
|
this.yy_shift(yyact, this.YYERRORSYMBOL, yyminor); // check me?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.yyerrcnt = 3;
|
||||||
|
yyerrorhit = 1;
|
||||||
|
} else if (this.YYNOERRORRECOVERY) {
|
||||||
|
/* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
|
||||||
|
** do any kind of error recovery. Instead, simply invoke the syntax
|
||||||
|
** error routine and continue going as if nothing had happened.
|
||||||
|
**
|
||||||
|
** Applications can set this macro (for example inside %include) if
|
||||||
|
** they intend to abandon the parse upon the first syntax error seen.
|
||||||
|
*/
|
||||||
|
this.yy_syntax_error(yymajor, yyminor);
|
||||||
|
this.yy_destructor(yymajor, yyminor);
|
||||||
|
yymajor = this.YYNOCODE;
|
||||||
|
} else { /* YYERRORSYMBOL is not defined */
|
||||||
|
/* This is what we do if the grammar does not define ERROR:
|
||||||
|
**
|
||||||
|
** * Report an error message, and throw away the input token.
|
||||||
|
**
|
||||||
|
** * If the input token is $, then fail the parse.
|
||||||
|
**
|
||||||
|
** As before, subsequent error messages are suppressed until
|
||||||
|
** three input tokens have been successfully shifted.
|
||||||
|
*/
|
||||||
|
if (this.yyerrcnt <= 0) {
|
||||||
|
this.yy_syntax_error(yymajor, yyminor);
|
||||||
|
}
|
||||||
|
this.yyerrcnt = 3;
|
||||||
|
this.yy_destructor(yymajor, yyminor);
|
||||||
|
if (yyendofinput) {
|
||||||
|
this.yy_parse_failed();
|
||||||
|
if (!this.YYNOERRORRECOVERY) {
|
||||||
|
this.yyerrcnt = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yymajor = this.YYNOCODE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (yymajor != this.YYNOCODE && this.yyidx > 0);
|
||||||
|
|
||||||
|
if (this.yyTraceCallback) {
|
||||||
|
var remainingTokens = [];
|
||||||
|
for (var i = 1; i <= this.yyidx; i++) {
|
||||||
|
remainingTokens.push(this.yyTokenName[this.yystack[i].major]);
|
||||||
|
}
|
||||||
|
this.trace("Return. Stack=[" + remainingTokens.join(" ") + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
|
||||||
|
} // function Parse()
|
54
main.js
Normal file
54
main.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* Created by Aleksey Chichenkov <a.chichenkov@initi.ru> on 1/28/19.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var js_beautify = require("js-beautify");
|
||||||
|
var args = require("args-parser")(process.argv);
|
||||||
|
var fs = require("fs");
|
||||||
|
var exec = require('child_process').exec;
|
||||||
|
|
||||||
|
var program_path = "./lemon-src/lemon-js";
|
||||||
|
var parser_path = "parsers/filters/";
|
||||||
|
var file_name = "parser.y";
|
||||||
|
var temp_file_name = "temp_parser.y";
|
||||||
|
|
||||||
|
|
||||||
|
var update_parser_y = function () {
|
||||||
|
var source_parser_y = fs.readFileSync(parser_path + file_name, "utf8");
|
||||||
|
|
||||||
|
var result = /&&.*?REPLACER\{(.*?)\}&&/gm.exec(source_parser_y);
|
||||||
|
if(result) {
|
||||||
|
var file_path = result[1];
|
||||||
|
var process_code = fs.readFileSync(file_path, "utf8");
|
||||||
|
|
||||||
|
source_parser_y = source_parser_y.replace(/&&.*?REPLACER\{(.*?)\}&&/gm, process_code);
|
||||||
|
|
||||||
|
fs.writeFileSync(parser_path + temp_file_name, source_parser_y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var post_process_parser = function () {
|
||||||
|
var out_js = fs.readFileSync(parser_path + "temp_parser.js", "utf8");
|
||||||
|
out_js = js_beautify(out_js, {indent_size: 4, space_in_empty_paren: true});
|
||||||
|
fs.writeFileSync(parser_path + "parser.js", out_js);
|
||||||
|
|
||||||
|
var temp_parser_out = fs.readFileSync(parser_path + "temp_parser.out", "utf8");
|
||||||
|
fs.writeFileSync(parser_path + "parser.out", temp_parser_out);
|
||||||
|
};
|
||||||
|
|
||||||
|
var start = function () {
|
||||||
|
update_parser_y();
|
||||||
|
|
||||||
|
exec(program_path + " " + parser_path + temp_file_name + " -l", function(err, stdout, stderr) {
|
||||||
|
err && console.log("ERROR: ", err);
|
||||||
|
err && process.exit(1);
|
||||||
|
|
||||||
|
post_process_parser();
|
||||||
|
|
||||||
|
fs.unlinkSync(parser_path + temp_file_name);
|
||||||
|
fs.unlinkSync(parser_path + "temp_parser.js");
|
||||||
|
fs.unlinkSync(parser_path + "temp_parser.out");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
start();
|
223
package-lock.json
generated
Normal file
223
package-lock.json
generated
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
{
|
||||||
|
"name": "lemon-js-generator",
|
||||||
|
"requires": true,
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": {
|
||||||
|
"version": "10.12.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
|
||||||
|
"integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ=="
|
||||||
|
},
|
||||||
|
"@types/semver": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ=="
|
||||||
|
},
|
||||||
|
"abbrev": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||||
|
},
|
||||||
|
"args-parser": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/args-parser/-/args-parser-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-YlO/zWlNJ5/mPqr9eNYo0UoF/6k="
|
||||||
|
},
|
||||||
|
"balanced-match": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||||
|
},
|
||||||
|
"brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"requires": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commander": {
|
||||||
|
"version": "2.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
|
||||||
|
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
|
||||||
|
},
|
||||||
|
"concat-map": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||||
|
},
|
||||||
|
"config-chain": {
|
||||||
|
"version": "1.1.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz",
|
||||||
|
"integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==",
|
||||||
|
"requires": {
|
||||||
|
"ini": "^1.3.4",
|
||||||
|
"proto-list": "~1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"editorconfig": {
|
||||||
|
"version": "0.15.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.2.tgz",
|
||||||
|
"integrity": "sha512-GWjSI19PVJAM9IZRGOS+YKI8LN+/sjkSjNyvxL5ucqP9/IqtYNXBaQ/6c/hkPNYQHyOHra2KoXZI/JVpuqwmcQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "^10.11.7",
|
||||||
|
"@types/semver": "^5.5.0",
|
||||||
|
"commander": "^2.19.0",
|
||||||
|
"lru-cache": "^4.1.3",
|
||||||
|
"semver": "^5.6.0",
|
||||||
|
"sigmund": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||||
|
},
|
||||||
|
"glob": {
|
||||||
|
"version": "7.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
|
||||||
|
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
|
||||||
|
"requires": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||||
|
"requires": {
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||||
|
},
|
||||||
|
"ini": {
|
||||||
|
"version": "1.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||||
|
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
|
||||||
|
},
|
||||||
|
"js-beautify": {
|
||||||
|
"version": "1.8.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.8.9.tgz",
|
||||||
|
"integrity": "sha512-MwPmLywK9RSX0SPsUJjN7i+RQY9w/yC17Lbrq9ViEefpLRgqAR2BgrMN2AbifkUuhDV8tRauLhLda/9+bE0YQA==",
|
||||||
|
"requires": {
|
||||||
|
"config-chain": "^1.1.12",
|
||||||
|
"editorconfig": "^0.15.2",
|
||||||
|
"glob": "^7.1.3",
|
||||||
|
"mkdirp": "~0.5.0",
|
||||||
|
"nopt": "~4.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lru-cache": {
|
||||||
|
"version": "4.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
|
||||||
|
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
|
||||||
|
"requires": {
|
||||||
|
"pseudomap": "^1.0.2",
|
||||||
|
"yallist": "^2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minimatch": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
|
"requires": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minimist": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||||
|
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||||
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||||
|
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||||
|
"requires": {
|
||||||
|
"minimist": "0.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nopt": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
|
||||||
|
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
|
||||||
|
"requires": {
|
||||||
|
"abbrev": "1",
|
||||||
|
"osenv": "^0.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
|
"requires": {
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"os-homedir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
|
||||||
|
},
|
||||||
|
"os-tmpdir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
|
||||||
|
},
|
||||||
|
"osenv": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
|
||||||
|
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
|
||||||
|
"requires": {
|
||||||
|
"os-homedir": "^1.0.0",
|
||||||
|
"os-tmpdir": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
||||||
|
},
|
||||||
|
"proto-list": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
|
||||||
|
"integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk="
|
||||||
|
},
|
||||||
|
"pseudomap": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
|
||||||
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "5.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
|
||||||
|
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg=="
|
||||||
|
},
|
||||||
|
"sigmund": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
|
||||||
|
},
|
||||||
|
"wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||||
|
},
|
||||||
|
"yallist": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||||
|
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
package.json
Normal file
14
package.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "lemon-js-generator",
|
||||||
|
"requires": true,
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"license": "MIT",
|
||||||
|
"author": {
|
||||||
|
"name": "chichenkov",
|
||||||
|
"email": "rolahd@yandex.ru"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"args-parser": "^1.1.0",
|
||||||
|
"js-beautify": "^1.8.9"
|
||||||
|
}
|
||||||
|
}
|
1922
parsers/filters/lexer.js
Normal file
1922
parsers/filters/lexer.js
Normal file
File diff suppressed because it is too large
Load Diff
1152
parsers/filters/parser.js
Normal file
1152
parsers/filters/parser.js
Normal file
File diff suppressed because it is too large
Load Diff
155
parsers/filters/parser.out
Normal file
155
parsers/filters/parser.out
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
State 0:
|
||||||
|
main ::= * expr
|
||||||
|
string ::= * STRING_LITERAL
|
||||||
|
id ::= * string
|
||||||
|
id ::= * ID
|
||||||
|
eq ::= * id EQ literal
|
||||||
|
and ::= * expr AND expr
|
||||||
|
expr ::= * eq
|
||||||
|
expr ::= * and
|
||||||
|
expr ::= * LCB expr RCB
|
||||||
|
|
||||||
|
STRING_LITERAL shift-reduce 3 string ::= STRING_LITERAL
|
||||||
|
ID shift-reduce 5 id ::= ID
|
||||||
|
LCB shift 1
|
||||||
|
main accept
|
||||||
|
expr shift 6
|
||||||
|
string shift-reduce 4 id ::= string
|
||||||
|
id shift 11
|
||||||
|
eq shift-reduce 8 expr ::= eq
|
||||||
|
and shift-reduce 9 expr ::= and
|
||||||
|
|
||||||
|
State 1:
|
||||||
|
string ::= * STRING_LITERAL
|
||||||
|
id ::= * string
|
||||||
|
id ::= * ID
|
||||||
|
eq ::= * id EQ literal
|
||||||
|
and ::= * expr AND expr
|
||||||
|
expr ::= * eq
|
||||||
|
expr ::= * and
|
||||||
|
expr ::= * LCB expr RCB
|
||||||
|
expr ::= LCB * expr RCB
|
||||||
|
|
||||||
|
STRING_LITERAL shift-reduce 3 string ::= STRING_LITERAL
|
||||||
|
ID shift-reduce 5 id ::= ID
|
||||||
|
LCB shift 1
|
||||||
|
expr shift 5
|
||||||
|
string shift-reduce 4 id ::= string
|
||||||
|
id shift 11
|
||||||
|
eq shift-reduce 8 expr ::= eq
|
||||||
|
and shift-reduce 9 expr ::= and
|
||||||
|
|
||||||
|
State 2:
|
||||||
|
string ::= * STRING_LITERAL
|
||||||
|
id ::= * string
|
||||||
|
id ::= * ID
|
||||||
|
eq ::= * id EQ literal
|
||||||
|
and ::= * expr AND expr
|
||||||
|
and ::= expr AND * expr
|
||||||
|
expr ::= * eq
|
||||||
|
expr ::= * and
|
||||||
|
expr ::= * LCB expr RCB
|
||||||
|
|
||||||
|
STRING_LITERAL shift-reduce 3 string ::= STRING_LITERAL
|
||||||
|
ID shift-reduce 5 id ::= ID
|
||||||
|
LCB shift 1
|
||||||
|
expr shift-reduce 7 and ::= expr AND expr
|
||||||
|
string shift-reduce 4 id ::= string
|
||||||
|
id shift 11
|
||||||
|
eq shift-reduce 8 expr ::= eq
|
||||||
|
and shift-reduce 9 expr ::= and
|
||||||
|
|
||||||
|
State 3:
|
||||||
|
integer ::= * INTEGER_LITERAL
|
||||||
|
literal ::= * integer
|
||||||
|
eq ::= id EQ * literal
|
||||||
|
address_literal ::= * ADDRESS LSB address_literal_content_or_empty RSB
|
||||||
|
literal ::= * address_literal
|
||||||
|
|
||||||
|
INTEGER_LITERAL shift-reduce 1 integer ::= INTEGER_LITERAL
|
||||||
|
ADDRESS shift 10
|
||||||
|
integer shift-reduce 2 literal ::= integer
|
||||||
|
literal shift-reduce 6 eq ::= id EQ literal
|
||||||
|
address_literal shift-reduce 16 literal ::= address_literal
|
||||||
|
|
||||||
|
State 4:
|
||||||
|
address_literal_content ::= * STRING_LITERAL
|
||||||
|
address_literal_content ::= * address_literal_content COMMA STRING_LITERAL
|
||||||
|
address_literal_content_or_empty ::= * address_literal_content
|
||||||
|
(14) address_literal_content_or_empty ::= *
|
||||||
|
address_literal ::= ADDRESS LSB * address_literal_content_or_empty RSB
|
||||||
|
|
||||||
|
STRING_LITERAL shift-reduce 11 address_literal_content ::= STRING_LITERAL
|
||||||
|
address_literal_content shift 9
|
||||||
|
address_literal_content_or_empty shift 7
|
||||||
|
{default} reduce 14 address_literal_content_or_empty ::=
|
||||||
|
|
||||||
|
State 5:
|
||||||
|
and ::= expr * AND expr
|
||||||
|
expr ::= LCB expr * RCB
|
||||||
|
|
||||||
|
AND shift 2
|
||||||
|
RCB shift-reduce 10 expr ::= LCB expr RCB
|
||||||
|
|
||||||
|
State 6:
|
||||||
|
(0) main ::= expr *
|
||||||
|
and ::= expr * AND expr
|
||||||
|
|
||||||
|
$ reduce 0 main ::= expr
|
||||||
|
AND shift 2
|
||||||
|
|
||||||
|
State 7:
|
||||||
|
address_literal ::= ADDRESS LSB address_literal_content_or_empty * RSB
|
||||||
|
|
||||||
|
RSB shift-reduce 15 address_literal ::= ADDRESS LSB address_literal_content_or_empty RSB
|
||||||
|
|
||||||
|
State 8:
|
||||||
|
address_literal_content ::= address_literal_content COMMA * STRING_LITERAL
|
||||||
|
|
||||||
|
STRING_LITERAL shift-reduce 12 address_literal_content ::= address_literal_content COMMA STRING_LITERAL
|
||||||
|
|
||||||
|
State 9:
|
||||||
|
address_literal_content ::= address_literal_content * COMMA STRING_LITERAL
|
||||||
|
(13) address_literal_content_or_empty ::= address_literal_content *
|
||||||
|
|
||||||
|
COMMA shift 8
|
||||||
|
{default} reduce 13 address_literal_content_or_empty ::= address_literal_content
|
||||||
|
|
||||||
|
State 10:
|
||||||
|
address_literal ::= ADDRESS * LSB address_literal_content_or_empty RSB
|
||||||
|
|
||||||
|
LSB shift 4
|
||||||
|
|
||||||
|
State 11:
|
||||||
|
eq ::= id * EQ literal
|
||||||
|
|
||||||
|
EQ shift 3
|
||||||
|
|
||||||
|
----------------------------------------------------
|
||||||
|
Symbols:
|
||||||
|
0: $:
|
||||||
|
1: OR
|
||||||
|
2: AND
|
||||||
|
3: NOT
|
||||||
|
4: INTEGER_LITERAL
|
||||||
|
5: STRING_LITERAL
|
||||||
|
6: ID
|
||||||
|
7: EQ
|
||||||
|
8: LCB
|
||||||
|
9: RCB
|
||||||
|
10: COMMA
|
||||||
|
11: ADDRESS
|
||||||
|
12: LSB
|
||||||
|
13: RSB
|
||||||
|
14: error:
|
||||||
|
15: main: STRING_LITERAL ID LCB
|
||||||
|
16: expr: STRING_LITERAL ID LCB
|
||||||
|
17: integer: INTEGER_LITERAL
|
||||||
|
18: literal: INTEGER_LITERAL ADDRESS
|
||||||
|
19: string: STRING_LITERAL
|
||||||
|
20: id: STRING_LITERAL ID
|
||||||
|
21: eq: STRING_LITERAL ID
|
||||||
|
22: and: STRING_LITERAL ID LCB
|
||||||
|
23: address_literal_content: STRING_LITERAL
|
||||||
|
24: address_literal_content_or_empty: <lambda> STRING_LITERAL
|
||||||
|
25: address_literal: ADDRESS
|
152
parsers/filters/parser.y
Normal file
152
parsers/filters/parser.y
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
%name Parser
|
||||||
|
|
||||||
|
%token_prefix TOKEN_
|
||||||
|
|
||||||
|
%left OR.
|
||||||
|
%left AND.
|
||||||
|
%right NOT.
|
||||||
|
|
||||||
|
%include {
|
||||||
|
// include something
|
||||||
|
}
|
||||||
|
|
||||||
|
%code {
|
||||||
|
&&REPLACER{process.js}&&
|
||||||
|
}
|
||||||
|
|
||||||
|
%syntax_error {
|
||||||
|
console.log("Syntax error");
|
||||||
|
}
|
||||||
|
|
||||||
|
main ::= expr(A) . {
|
||||||
|
_result.root_node = A
|
||||||
|
}
|
||||||
|
|
||||||
|
integer(A) ::= INTEGER_LITERAL(B) . {
|
||||||
|
A = new Node({
|
||||||
|
type: "INTEGER_LITERAL",
|
||||||
|
lexeme: B.lexeme,
|
||||||
|
start: B.start,
|
||||||
|
end: B.end
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
literal(A) ::= integer(B) . {
|
||||||
|
A = new Node({
|
||||||
|
type: "literal",
|
||||||
|
children: [B]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
string(A) ::= STRING_LITERAL(B) . {
|
||||||
|
A = new Node({
|
||||||
|
type: "STRING_LITERAL",
|
||||||
|
lexeme: B.lexeme,
|
||||||
|
start: B.start,
|
||||||
|
end: B.end
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
id(A) ::= string(B) . {
|
||||||
|
A = new Node({
|
||||||
|
type: "id",
|
||||||
|
children: [B]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
id(A) ::= ID(B) . {
|
||||||
|
A = new Node({
|
||||||
|
type: "ID",
|
||||||
|
lexeme: B.lexeme,
|
||||||
|
start: B.start,
|
||||||
|
end: B.end
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
eq(A) ::= id(B) EQ(C) literal(D) . {
|
||||||
|
A = new Node({
|
||||||
|
type: "eq",
|
||||||
|
children: [
|
||||||
|
B,
|
||||||
|
new Node({
|
||||||
|
type: "EQ",
|
||||||
|
lexeme: C.lexeme,
|
||||||
|
start: C.start,
|
||||||
|
end: C.end
|
||||||
|
}),
|
||||||
|
D
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
and(A) ::= expr(B) AND expr(D) . {
|
||||||
|
A = new Node({
|
||||||
|
type: "and",
|
||||||
|
children: [
|
||||||
|
B,
|
||||||
|
D
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
expr(A) ::= eq(B) . {
|
||||||
|
A = new Node({
|
||||||
|
type: "expr",
|
||||||
|
children: [B]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
expr(A) ::= and(B) . {
|
||||||
|
A = B;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr(A) ::= LCB expr(C) RCB . {
|
||||||
|
A = C;
|
||||||
|
}
|
||||||
|
|
||||||
|
address_literal_content(A) ::= STRING_LITERAL(B) . {
|
||||||
|
A = new Node({
|
||||||
|
children: [
|
||||||
|
new Node({
|
||||||
|
type: "STRING_LITERAL",
|
||||||
|
lexeme: B.lexeme,
|
||||||
|
start: B.start,
|
||||||
|
end: B.end
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
address_literal_content(A) ::= address_literal_content(B) COMMA STRING_LITERAL(C) . {
|
||||||
|
B.add(new Node({
|
||||||
|
type: "STRING_LITERAL",
|
||||||
|
lexeme: C.lexeme,
|
||||||
|
start: C.start,
|
||||||
|
end: C.end
|
||||||
|
}));
|
||||||
|
A = B;
|
||||||
|
}
|
||||||
|
|
||||||
|
address_literal_content_or_empty(A) ::= address_literal_content(B) . {
|
||||||
|
A = B;
|
||||||
|
}
|
||||||
|
|
||||||
|
address_literal_content_or_empty(A) ::= . {
|
||||||
|
A = new Node({
|
||||||
|
type: "address_literal_content"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
address_literal(A) ::= ADDRESS LSB address_literal_content_or_empty(C) RSB . {
|
||||||
|
A = new Node({
|
||||||
|
type: "address_literal",
|
||||||
|
children: C.children
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
literal(A) ::= address_literal(B) . {
|
||||||
|
A = new Node({
|
||||||
|
type: "literal",
|
||||||
|
children: [B]
|
||||||
|
});
|
||||||
|
}
|
370
process.js
Normal file
370
process.js
Normal file
@ -0,0 +1,370 @@
|
|||||||
|
/**
|
||||||
|
* Created by Aleksey Chichenkov <a.chichenkov@initi.ru> on 1/28/19.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var fs = require("fs");
|
||||||
|
var Lexer = require('./lexer.js');
|
||||||
|
|
||||||
|
var tokens = (function () {
|
||||||
|
|
||||||
|
var std = (function () {
|
||||||
|
var protos = "__protos__";
|
||||||
|
var keys = "__keys__";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return unique data
|
||||||
|
*
|
||||||
|
* @param {Object[]} _arr - prototypes of inheritance classes
|
||||||
|
* @param {Object} _main - prototype of resulting class
|
||||||
|
*
|
||||||
|
* @return {Object}
|
||||||
|
* */
|
||||||
|
var unique = function (_arr, _main) {
|
||||||
|
var result = Object.create(null);
|
||||||
|
var to_remove = [];
|
||||||
|
|
||||||
|
for (var i = 0, e = _arr.length; i != e; ++i) {
|
||||||
|
var item = _arr[i];
|
||||||
|
|
||||||
|
for (var key in item) {
|
||||||
|
if (key in result) {
|
||||||
|
to_remove.push(key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result[key] = item[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keys in item) {
|
||||||
|
for (var ii = 0, ee = item[keys].length; ii != ee; ++ii) {
|
||||||
|
var key = item[keys][ii];
|
||||||
|
if (key in result) {
|
||||||
|
to_remove.push(key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result[key] = item[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i != to_remove.length; ++i) {
|
||||||
|
delete result[to_remove[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var key in _main) {
|
||||||
|
result[key] = _main[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create OOP class
|
||||||
|
*
|
||||||
|
* @param {Function[]} _constrs - inheritance classes
|
||||||
|
* @param {Object} _proto - prototype of resulting class
|
||||||
|
* @param {Object?} _static - static data
|
||||||
|
*
|
||||||
|
* @return {Function}
|
||||||
|
* */
|
||||||
|
var class_creator = function (_constrs, _proto, _static) {
|
||||||
|
_constrs = _constrs || [];
|
||||||
|
_proto = _proto || [];
|
||||||
|
_static = _static || [];
|
||||||
|
|
||||||
|
var constr;
|
||||||
|
if (_proto && _proto.hasOwnProperty("constructor")) {
|
||||||
|
constr = _proto.constructor;
|
||||||
|
delete _proto.constructor;
|
||||||
|
} else {
|
||||||
|
constr = function () {
|
||||||
|
for (var i = 0; i != _constrs.length; ++i) {
|
||||||
|
_constrs[i].apply(this, arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var proto = Object.create(null);
|
||||||
|
Object.defineProperty(proto, protos, {
|
||||||
|
"value": []
|
||||||
|
});
|
||||||
|
Object.defineProperty(proto, keys, {
|
||||||
|
"value": []
|
||||||
|
});
|
||||||
|
|
||||||
|
/************************FOR MEMBERS*******************************/
|
||||||
|
for (var i = 0, e = _constrs.length; i != e; ++i) {
|
||||||
|
proto[protos].push(_constrs[i].prototype);
|
||||||
|
}
|
||||||
|
|
||||||
|
var m_un = unique(proto[protos], _proto);
|
||||||
|
for (var key in m_un) {
|
||||||
|
proto[keys].push(key);
|
||||||
|
|
||||||
|
Object.defineProperty(proto, key, {
|
||||||
|
"value": m_un[key]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/************************FOR MEMBERS END***************************/
|
||||||
|
|
||||||
|
/************************FOR STATICS*******************************/
|
||||||
|
var s_un = unique(_constrs, _static);
|
||||||
|
for (var key in s_un) {
|
||||||
|
Object.defineProperty(constr, key, {
|
||||||
|
"value": s_un[key],
|
||||||
|
"enumerable": true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/************************FOR STATICS END***************************/
|
||||||
|
|
||||||
|
|
||||||
|
Object.defineProperties(constr, {
|
||||||
|
"pr": {
|
||||||
|
"value": proto
|
||||||
|
},
|
||||||
|
"prototype": {
|
||||||
|
"value": proto
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.freeze(proto);
|
||||||
|
Object.freeze(constr);
|
||||||
|
|
||||||
|
return constr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if target has prototype
|
||||||
|
*
|
||||||
|
* @param {Object} _target - checkable instance
|
||||||
|
* @param {Object} _proto - posible prototype
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
var check = function (_target, _proto) {
|
||||||
|
for (var i = 0; i != _target[protos].length; ++i) {
|
||||||
|
var t_proto = _target[protos][i];
|
||||||
|
if (t_proto == _proto) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t_proto[protos]) {
|
||||||
|
if (check(t_proto, _proto))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if target is instance of class
|
||||||
|
*
|
||||||
|
* @param {Object} _target - checkable instance
|
||||||
|
* @param {Function} _constr - posible constructor
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
var class_check = function (_target, _constr) {
|
||||||
|
if (_target instanceof _constr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return check(_target, _constr.prototype);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
class: class_creator,
|
||||||
|
class_check: class_check
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
var tools = {
|
||||||
|
merge: function (_obj) {
|
||||||
|
var target = Object.create(null);
|
||||||
|
var i = 0, e = arguments.length;
|
||||||
|
for (; i != e; ++i) {
|
||||||
|
var options = arguments[i];
|
||||||
|
|
||||||
|
for (var key in options) {
|
||||||
|
if (options[key] === undefined && target === options[key])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
target[key] = options[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var Node = std.class([], {
|
||||||
|
constructor: function Node(_options) {
|
||||||
|
var base = tools.merge({
|
||||||
|
children: []
|
||||||
|
}, _options);
|
||||||
|
|
||||||
|
this.children = base.children;
|
||||||
|
},
|
||||||
|
add: function (_n) {
|
||||||
|
this.children.push(_n);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var Lexeme = std.class([Node], {
|
||||||
|
constructor: function Lexeme(_options) {
|
||||||
|
var base = tools.merge({
|
||||||
|
start: -1,
|
||||||
|
end: -1,
|
||||||
|
type: null,
|
||||||
|
value: null
|
||||||
|
}, _options);
|
||||||
|
|
||||||
|
Node.call(this, base);
|
||||||
|
|
||||||
|
this.start = base.start;
|
||||||
|
this.end = base.end;
|
||||||
|
this.type = base.type;
|
||||||
|
this.value = base.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var Rule = std.class([Node], {
|
||||||
|
constructor: function NonTerminal(_options) {
|
||||||
|
var base = tools.merge({}, _options);
|
||||||
|
|
||||||
|
Node.call(this, base);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var string_literal = std.class([Rule], {
|
||||||
|
constructor: function string_literal(_options) {
|
||||||
|
var base = tools.merge({}, _options);
|
||||||
|
|
||||||
|
Rule.call(this, base);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var integer_literal = std.class([Rule], {
|
||||||
|
constructor: function integer_literal(_options) {
|
||||||
|
var base = tools.merge({}, _options);
|
||||||
|
|
||||||
|
Rule.call(this, base);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var id = std.class([Rule], {
|
||||||
|
constructor: function id(_options) {
|
||||||
|
var base = tools.merge({}, _options);
|
||||||
|
|
||||||
|
Rule.call(this, base);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var literal = std.class([Rule], {
|
||||||
|
constructor: function literal(_options) {
|
||||||
|
var base = tools.merge({}, _options);
|
||||||
|
|
||||||
|
Rule.call(this, base);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var eq = std.class([Rule], {
|
||||||
|
constructor: function eq(_options) {
|
||||||
|
var base = tools.merge({
|
||||||
|
id: null,
|
||||||
|
EQ: null,
|
||||||
|
literal: null
|
||||||
|
}, _options);
|
||||||
|
|
||||||
|
Rule.call(this, base);
|
||||||
|
|
||||||
|
this.id = base.id;
|
||||||
|
this.EQ = base.EQ;
|
||||||
|
this.literal = base.literal;
|
||||||
|
},
|
||||||
|
set_id: function (_n) {
|
||||||
|
this._id = _n;
|
||||||
|
},
|
||||||
|
set_EQ: function (_n) {
|
||||||
|
this._EQ = _n;
|
||||||
|
},
|
||||||
|
set_literal: function (_n) {
|
||||||
|
this._literal = _n;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var and = std.class([Rule], {
|
||||||
|
constructor: function and(_options) {
|
||||||
|
var base = tools.merge({
|
||||||
|
lexpr: null,
|
||||||
|
AND: null,
|
||||||
|
rexpr: null
|
||||||
|
}, _options);
|
||||||
|
|
||||||
|
Rule.call(this, base);
|
||||||
|
|
||||||
|
this.lexpr = base.lexpr;
|
||||||
|
this.AND = base.AND;
|
||||||
|
this.rexpr = base.rexpr;
|
||||||
|
},
|
||||||
|
set_lexpr: function (_n) {
|
||||||
|
this._lexpr = _n;
|
||||||
|
},
|
||||||
|
set_AND: function (_n) {
|
||||||
|
this._AND = _n;
|
||||||
|
},
|
||||||
|
set_rexpr: function (_n) {
|
||||||
|
this._rexpr = _n;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var expr = std.class([Rule], {
|
||||||
|
constructor: function expr(_options) {
|
||||||
|
var base = tools.merge({}, _options);
|
||||||
|
|
||||||
|
Rule.call(this, base);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
// terminal
|
||||||
|
LEXEME: Lexeme,
|
||||||
|
|
||||||
|
// non terminal
|
||||||
|
string_literal: string_literal,
|
||||||
|
integer_literal: integer_literal,
|
||||||
|
id: id,
|
||||||
|
literal: literal,
|
||||||
|
eq: eq,
|
||||||
|
and: and,
|
||||||
|
expr: expr,
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var _result = {};
|
||||||
|
var LemonJS = function (_input) {
|
||||||
|
var parser = new Parser();
|
||||||
|
var lexer = new Lexer(_input);
|
||||||
|
var token;
|
||||||
|
while (token = lexer.next()) {
|
||||||
|
console.log("PARSE", token.lexeme);
|
||||||
|
parser.parse(parser["TOKEN_" + token.lexeme], token);
|
||||||
|
}
|
||||||
|
parser.parse();
|
||||||
|
return _result;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
fs.mkdirSync("tests");
|
||||||
|
|
||||||
|
var test_and = LemonJS("abc == 1 and abc1 == 2 and (bbc == 5)");
|
||||||
|
fs.writeFileSync("tests/out_test_and.json", JSON.stringify(test_and, true, 3));
|
||||||
|
|
||||||
|
var test_address = LemonJS('abc == Address ["a", "b", "c"]');
|
||||||
|
fs.writeFileSync("tests/out_tree_address.json", JSON.stringify(test_address, true, 3));
|
153
tests/out_test_and.json
Normal file
153
tests/out_test_and.json
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
{
|
||||||
|
"root_node": {
|
||||||
|
"type": "and",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "and",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "expr",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "eq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "ID",
|
||||||
|
"children": [],
|
||||||
|
"lexeme": "ID",
|
||||||
|
"start": 0,
|
||||||
|
"end": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "EQ",
|
||||||
|
"children": [],
|
||||||
|
"lexeme": "EQ",
|
||||||
|
"start": 4,
|
||||||
|
"end": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "literal",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "INTEGER_LITERAL",
|
||||||
|
"children": [],
|
||||||
|
"lexeme": "INTEGER_LITERAL",
|
||||||
|
"start": 7,
|
||||||
|
"end": 8
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "expr",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "eq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "ID",
|
||||||
|
"children": [],
|
||||||
|
"lexeme": "ID",
|
||||||
|
"start": 13,
|
||||||
|
"end": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "EQ",
|
||||||
|
"children": [],
|
||||||
|
"lexeme": "EQ",
|
||||||
|
"start": 18,
|
||||||
|
"end": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "literal",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "INTEGER_LITERAL",
|
||||||
|
"children": [],
|
||||||
|
"lexeme": "INTEGER_LITERAL",
|
||||||
|
"start": 21,
|
||||||
|
"end": 22
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "expr",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "eq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "ID",
|
||||||
|
"children": [],
|
||||||
|
"lexeme": "ID",
|
||||||
|
"start": 28,
|
||||||
|
"end": 31
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "EQ",
|
||||||
|
"children": [],
|
||||||
|
"lexeme": "EQ",
|
||||||
|
"start": 32,
|
||||||
|
"end": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "literal",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "INTEGER_LITERAL",
|
||||||
|
"children": [],
|
||||||
|
"lexeme": "INTEGER_LITERAL",
|
||||||
|
"start": 35,
|
||||||
|
"end": 36
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
}
|
||||||
|
}
|
69
tests/out_tree_address.json
Normal file
69
tests/out_tree_address.json
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"root_node": {
|
||||||
|
"type": "expr",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "eq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "ID",
|
||||||
|
"children": [],
|
||||||
|
"lexeme": "ID",
|
||||||
|
"start": 0,
|
||||||
|
"end": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "EQ",
|
||||||
|
"children": [],
|
||||||
|
"lexeme": "EQ",
|
||||||
|
"start": 4,
|
||||||
|
"end": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "literal",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "address_literal",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "STRING_LITERAL",
|
||||||
|
"children": [],
|
||||||
|
"lexeme": "STRING_LITERAL",
|
||||||
|
"start": 16,
|
||||||
|
"end": 19
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "STRING_LITERAL",
|
||||||
|
"children": [],
|
||||||
|
"lexeme": "STRING_LITERAL",
|
||||||
|
"start": 21,
|
||||||
|
"end": 24
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "STRING_LITERAL",
|
||||||
|
"children": [],
|
||||||
|
"lexeme": "STRING_LITERAL",
|
||||||
|
"start": 26,
|
||||||
|
"end": 29
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lexeme": null,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user