[llvm-commits] [test-suite] r48828 [1/2] - in /test-suite/trunk/MultiSource/Applications: ./ lemon/ sqlite3/
Evan Cheng
evan.cheng at apple.com
Wed Mar 26 10:03:57 PDT 2008
Author: evancheng
Date: Wed Mar 26 12:03:57 2008
New Revision: 48828
URL: http://llvm.org/viewvc/llvm-project?rev=48828&view=rev
Log:
Added sqlite3 and lemon to llvm test suite. Thanks to Török Edwin!
Added:
test-suite/trunk/MultiSource/Applications/lemon/
test-suite/trunk/MultiSource/Applications/lemon/Makefile
test-suite/trunk/MultiSource/Applications/lemon/README
test-suite/trunk/MultiSource/Applications/lemon/copyright-release.html
test-suite/trunk/MultiSource/Applications/lemon/ecmascript.y
test-suite/trunk/MultiSource/Applications/lemon/example1.y
test-suite/trunk/MultiSource/Applications/lemon/example2.y
test-suite/trunk/MultiSource/Applications/lemon/example3.y
test-suite/trunk/MultiSource/Applications/lemon/example4.y
test-suite/trunk/MultiSource/Applications/lemon/example5.y
test-suite/trunk/MultiSource/Applications/lemon/example_COPYING
test-suite/trunk/MultiSource/Applications/lemon/lemon.c
test-suite/trunk/MultiSource/Applications/lemon/lempar.c
test-suite/trunk/MultiSource/Applications/lemon/lighttpd_COPYING
test-suite/trunk/MultiSource/Applications/lemon/lighttpd_configparser.y
test-suite/trunk/MultiSource/Applications/lemon/lighttpd_mod_ssi_exprparser.y
test-suite/trunk/MultiSource/Applications/lemon/parse.y
test-suite/trunk/MultiSource/Applications/lemon/wireshark_COPYING
test-suite/trunk/MultiSource/Applications/lemon/wireshark_dtd_grammar.lemon
test-suite/trunk/MultiSource/Applications/lemon/wireshark_grammar.lemon
test-suite/trunk/MultiSource/Applications/lemon/wireshark_mate_grammar.lemon
test-suite/trunk/MultiSource/Applications/lemon/xapian_COPYING
test-suite/trunk/MultiSource/Applications/lemon/xapian_queryparser.lemony
test-suite/trunk/MultiSource/Applications/sqlite3/
test-suite/trunk/MultiSource/Applications/sqlite3/Makefile
test-suite/trunk/MultiSource/Applications/sqlite3/README
test-suite/trunk/MultiSource/Applications/sqlite3/VERSION
test-suite/trunk/MultiSource/Applications/sqlite3/commands
test-suite/trunk/MultiSource/Applications/sqlite3/copyright-release.html
test-suite/trunk/MultiSource/Applications/sqlite3/shell.c
test-suite/trunk/MultiSource/Applications/sqlite3/speedtest.tcl
test-suite/trunk/MultiSource/Applications/sqlite3/sqlite3.c
Modified:
test-suite/trunk/MultiSource/Applications/Makefile
Modified: test-suite/trunk/MultiSource/Applications/Makefile
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/Makefile?rev=48828&r1=48827&r2=48828&view=diff
==============================================================================
--- test-suite/trunk/MultiSource/Applications/Makefile (original)
+++ test-suite/trunk/MultiSource/Applications/Makefile Wed Mar 26 12:03:57 2008
@@ -5,7 +5,7 @@
include $(LEVEL)/Makefile.config
PARALLEL_DIRS = Burg aha sgefa siod lambda-0.1.3 d spiff hbd treecc SPASS \
- hexxagon oggenc JM viterbi minisat SIBsim4 ClamAV
+ hexxagon oggenc JM viterbi minisat SIBsim4 ClamAV sqlite3 lemon
# Obsequi uses Linux-only features; need to fix that
ifeq ($(OS),Linux)
Added: test-suite/trunk/MultiSource/Applications/lemon/Makefile
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/Makefile?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/Makefile (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/Makefile Wed Mar 26 12:03:57 2008
@@ -0,0 +1,12 @@
+LEVEL = ../../../
+
+Source = lemon.c
+
+PROG = lemon
+RUN_OPTIONS = parse.y example1.y example2.y example3.y example4.y example5.y lighttpd_configparser.y lighttpd_mod_ssi_exprparser.y wireshark_dtd_grammar.lemon wireshark_grammar.lemon wireshark_mate_grammar.lemon xapian_queryparser.lemony ecmascript.y
+
+include $(LEVEL)/Makefile.config
+
+include ../../Makefile.multisrc
+DIFFPROG := $(PROGDIR)/DiffOutput.sh "diff "
+
Added: test-suite/trunk/MultiSource/Applications/lemon/README
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/README?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/README (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/README Wed Mar 26 12:03:57 2008
@@ -0,0 +1,13 @@
+lemon.c, lempar.c, parse.y are from SQLite3, which is in the public domain (copyright-release.html)
+
+Homepage of lemon is http://www.hwaci.com/sw/lemon/lemon.html
+
+example[1-5].y is from the Lemon Parser Tutorial, covered by example_COPYING http://sourceforge.net/project/showfiles.php?group_id=79320&package_id=124528
+
+lighttpd_mod_ssi_exprparser.y, and lighttpd_configparser.y is from lighttpd-1.4.19,
+covered by lighttpd_COPYING
+
+wireshark_grammar.lemon, wireshark_dtd_grammar.lemon, wireshark_mate_grammar.lemon is from wireshark-0.99.8, covered by wireshark_COPYING
+
+xapian_queryparser.lemony is from xapian-core-1.0.5, covered by xapian_COPYING
+
Added: test-suite/trunk/MultiSource/Applications/lemon/copyright-release.html
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/copyright-release.html?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/copyright-release.html (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/copyright-release.html Wed Mar 26 12:03:57 2008
@@ -0,0 +1,109 @@
+<html>
+<body bgcolor="white">
+<h1 align="center">
+Copyright Release for<br>
+Contributions To SQLite
+</h1>
+
+<p>
+SQLite is software that implements an embeddable SQL database engine.
+SQLite is available for free download from http://www.sqlite.org/.
+The principal author and maintainer of SQLite has disclaimed all
+copyright interest in his contributions to SQLite
+and thus released his contributions into the public domain.
+In order to keep the SQLite software unencumbered by copyright
+claims, the principal author asks others who may from time to
+time contribute changes and enhancements to likewise disclaim
+their own individual copyright interest.
+</p>
+
+<p>
+Because the SQLite software found at http://www.sqlite.org/ is in the
+public domain, anyone is free to download the SQLite software
+from that website, make changes to the software, use, distribute,
+or sell the modified software, under either the original name or
+under some new name, without any need to obtain permission, pay
+royalties, acknowledge the original source of the software, or
+in any other way compensate, identify, or notify the original authors.
+Nobody is in any way compelled to contribute their SQLite changes and
+enhancements back to the SQLite website. This document concerns
+only changes and enhancements to SQLite that are intentionally and
+deliberately contributed back to the SQLite website.
+</p>
+
+<p>
+For the purposes of this document, "SQLite software" shall mean any
+computer source code, documentation, makefiles, test scripts, or
+other information that is published on the SQLite website,
+http://www.sqlite.org/. Precompiled binaries are excluded from
+the definition of "SQLite software" in this document because the
+process of compiling the software may introduce information from
+outside sources which is not properly a part of SQLite.
+</p>
+
+<p>
+The header comments on the SQLite source files exhort the reader to
+share freely and to never take more than one gives.
+In the spirit of that exhortation I make the following declarations:
+</p>
+
+<ol>
+<li><p>
+I dedicate to the public domain
+any and all copyright interest in the SQLite software that
+was publicly available on the SQLite website (http://www.sqlite.org/) prior
+to the date of the signature below and any changes or enhancements to
+the SQLite software
+that I may cause to be published on that website in the future.
+I make this dedication for the benefit of the public at large and
+to the detriment of my heirs and successors. I intend this
+dedication to be an overt act of relinquishment in perpetuity of
+all present and future rights to the SQLite software under copyright
+law.
+</p></li>
+
+<li><p>
+To the best of my knowledge and belief, the changes and enhancements that
+I have contributed to SQLite are either originally written by me
+or are derived from prior works which I have verified are also
+in the public domain and are not subject to claims of copyright
+by other parties.
+</p></li>
+
+<li><p>
+To the best of my knowledge and belief, no individual, business, organization,
+government, or other entity has any copyright interest
+in the SQLite software as it existed on the
+SQLite website as of the date on the signature line below.
+</p></li>
+
+<li><p>
+I agree never to publish any additional information
+to the SQLite website (by CVS, email, scp, FTP, or any other means) unless
+that information is an original work of authorship by me or is derived from
+prior published versions of SQLite.
+I agree never to copy and paste code into the SQLite code base from
+other sources.
+I agree never to publish on the SQLite website any information that
+would violate a law or breach a contract.
+</p></li>
+</ol>
+
+<p>
+<table width="100%" cellpadding="0" cellspacing="0">
+<tr>
+<td width="60%" valign="top">
+Signature:
+<p> </p>
+<p> </p>
+<p> </p>
+</td><td valign="top" align="left">
+Date:
+</td></tr>
+<td colspan=2>
+Name (printed):
+</td>
+</tr>
+</table>
+</body>
+</html>
Added: test-suite/trunk/MultiSource/Applications/lemon/ecmascript.y
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/ecmascript.y?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/ecmascript.y (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/ecmascript.y Wed Mar 26 12:03:57 2008
@@ -0,0 +1,270 @@
+%include {
+#include <stdlib.h>
+#include <string.h>
+#include "lexglobal.h"
+}
+%token_type{int}
+%nonassoc PRECEDENCE_KEYWORD PRECEDENCE_IDENTIFIER.
+
+program ::= source_elements.
+
+
+
+identifier ::= IDENTIFIER_NAME. [PRECEDENCE_IDENTIFIER]
+
+
+
+literal ::= NULL_LITERAL.
+literal ::= BOOLEAN_LITERAL.
+literal ::= NumericLiteral.
+literal ::= StringLiteral.
+
+primary_expression ::= THIS.
+primary_expression ::= identifier.
+primary_expression ::= literal.
+primary_expression ::= array_literal.
+primary_expression ::= object_literal.
+primary_expression ::= PAR_OPEN expression PAR_CLOSE.
+
+elision_opt ::= .
+elision_opt ::= elision.
+
+array_literal ::= BRACKET_OPEN elision_opt BRACKET_CLOSE
+ |BRACKET_OPEN element_list BRACKET_CLOSE
+ |BRACKET_OPEN element_list COMMA elision_opt BRACKET_CLOSE.
+
+assignment_operator ::= EQUAL.
+assignment_operator ::= ASSIGNMENT_OPERATOR_NOEQUAL.
+
+elision ::= COMMA.
+elision ::= elision COMMA.
+
+element_list ::= elision_opt assignment_expression.
+element_list ::= element_list COMMA elision_opt COMMA assignment_expression.
+
+object_literal ::= CURLY_BRACE_OPEN CURLY_BRACE_CLOSE
+ |CURLY_BRACE_OPEN property_name_and_value_list CURLY_BRACE_CLOSE.
+
+property_name_and_value_list ::= property_name COLON assignment_expression.
+property_name_and_value_list ::= property_name_and_value_list COMMA property_name COLON assignment_expression.
+
+property_name ::= identifier.
+property_name ::= StringLiteral
+ |NumericLiteral.
+
+
+/*
+
+temp phony decls
+
+*/
+
+function_expression ::= .
+/*------------------*/
+member_expression ::= primary_expression.
+member_expression ::= function_expression.
+member_expression ::= member_expression BRACKET_OPEN expression BRACKET_CLOSE.
+member_expression ::= member_expression DOT identifier.
+member_expression ::= NEW member_expression arguments.
+
+new_expression ::= member_expression.
+new_expression ::= NEW new_expression.
+
+call_expression ::= member_expression arguments.
+call_expression ::= call_expression arguments.
+call_expression ::= call_expression BRACKET_OPEN expression BRACKET_CLOSE.
+call_expression ::= call_expression DOT identifier.
+
+arguments ::= PAR_OPEN PAR_CLOSE
+ |PAR_OPEN arguments_list PAR_CLOSE.
+
+arguments_list ::= assignment_expression.
+arguments_list ::= arguments_list COMMA assignment_expression.
+
+lefthandside_expression ::= new_expression.
+lefthandside_expression ::= call_expression.
+
+postfix_expression ::= lefthandside_expression.
+postfix_expression ::= lefthandside_expression NOLF_PLUSPLUS.
+postfix_expression ::= lefthandside_expression NOLF_MINUSMINUS.
+
+unary_expression ::= postfix_expression.
+unary_expression ::= DELETE|VOID|TYPEOF|PLUSPLUS|MINUSMINUS|MINUS|TILDE|EXCLAMATION unary_expression.
+
+multiplicative_expression ::= unary_expression.
+multiplicative_expression ::= multiplicative_expression MULTIPLY|DIVIDE|PERCENT unary_expression.
+
+additive_expression ::= multiplicative_expression.
+additive_expression ::= additive_expression PLUS|MINUS multiplicative_expression.
+
+shift_expression ::= additive_expression.
+shift_expression ::= shift_expression SHIFT_LEFT|SHIFT_RIGHT|DOUBLESHIFT_RIGHT additive_expression.
+
+relational_expression ::= shift_expression.
+relational_expression ::= relational_expression LESS|GREATER|LESSEQUAL|GREATEREQUAL|INSTANCEOF|IN shift_expression.
+
+relational_expression_noin ::= shift_expression.
+relational_expression_noin ::= relational_expression LESS|GREATER|LESSEQUAL|GREATEREQUAL|INSTANCEOF shift_expression.
+
+equality_expression ::= relational_expression.
+equality_expression ::= equality_expression EQUAL_EQUAL|NOT_EQUAL|TRIPLE_EQUAL|NOT_DOUBLEEQUAL relational_expression.
+
+equality_expression_noin ::= relational_expression_noin.
+equality_expression_noin ::= equality_expression_noin EQUAL_EQUAL|NOT_EQUAL|TRIPLE_EQUAL|NOT_DOUBLEEQUAL relational_expression_noin.
+
+bitwise_and_expression ::= equality_expression.
+bitwise_and_expression ::= bitwise_and_expression AND equality_expression.
+
+bitwise_and_expression_noin ::= equality_expression_noin.
+bitwise_and_expression_noin ::= bitwise_and_expression_noin AND equality_expression_noin.
+
+bitwise_xor_expression ::= bitwise_and_expression.
+bitwise_xor_expression ::= bitwise_xor_expression XOR bitwise_and_expression.
+
+
+bitwise_xor_expression_noin ::= bitwise_and_expression_noin.
+bitwise_xor_expression_noin ::= bitwise_xor_expression_noin XOR bitwise_and_expression_noin.
+
+bitwise_or_expression ::= bitwise_xor_expression.
+bitwise_or_expression ::= bitwise_or_expression OR bitwise_xor_expression.
+
+
+bitwise_or_expression_noin ::= bitwise_xor_expression_noin.
+bitwise_or_expression_noin ::= bitwise_or_expression_noin OR bitwise_xor_expression_noin.
+
+logical_and_expression ::= bitwise_or_expression.
+logical_and_expression ::= logical_and_expression AND_AND bitwise_or_expression.
+
+logical_and_expression_noin ::= bitwise_or_expression_noin.
+logical_and_expression_noin ::= logical_and_expression_noin AND_AND bitwise_or_expression_noin.
+
+
+logical_or_expression ::= logical_and_expression.
+logical_or_expression ::= logical_or_expression OR_OR logical_and_expression.
+
+logical_or_expression_noin ::= logical_and_expression_noin.
+logical_or_expression_noin ::= logical_or_expression_noin OR_OR logical_and_expression_noin.
+
+conditional_expression ::= logical_or_expression.
+conditional_expression ::= logical_or_expression QUESTIONMARK assignment_expression COLON assignment_expression.
+
+conditional_expression_noin ::= logical_or_expression_noin.
+conditional_expression_noin ::= logical_or_expression_noin QUESTIONMARK assignment_expression_noin COLON assignment_expression_noin.
+
+assignment_expression ::= conditional_expression.
+assignment_expression ::= lefthandside_expression assignment_operator assignment_expression.
+
+assignment_expression_noin ::= conditional_expression_noin.
+assignment_expression_noin ::= lefthandside_expression assignment_operator assignment_expression_noin.
+
+expression ::= assignment_expression.
+expression ::= expression COMMA assignment_expression.
+expression_opt ::= expression.
+
+expression_noin_opt ::= expression.
+
+statement ::= block.
+statement ::= variable_statement.
+statement ::= empty_statement.
+statement ::= expression_statement.
+statement ::= if_statement.
+statement ::= iteration_statement.
+statement ::= continue_statement.
+statement ::= break_statement.
+statement ::= return_statement.
+statement ::= with_statement.
+statement ::= labelled_statement.
+statement ::= throw_statement.
+statement ::= try_statement.
+
+block ::= CURLY_BRACE_OPEN statement_list_opt CURLY_BRACE_CLOSE.
+
+statement_list_opt ::= .
+statement_list_opt ::= statement_list.
+
+statement_list ::= statement.
+statement_list ::= statement_list statement.
+
+variable_statement ::= VAR variable_declaration_list SEMICOLON.
+
+variable_declaration_list ::= variable_declaration.
+variable_declaration_list ::= variable_declaration_list COMMA variable_declaration.
+
+variable_declaration_list_noin ::= variable_declaration_noin.
+variable_declaration_list_noin ::= variable_declaration_list_noin COMMA variable_declaration_noin.
+
+variable_declaration ::= identifier initialiser_opt.
+
+variable_declaration_noin ::= identifier initialiser_noin_opt.
+
+initialiser_opt ::= .
+
+initialiser_noin_opt ::= .
+initialiser_noin_opt ::= initaliser_noin.
+
+initaliser_noin ::= EQUAL assignment_expression_noin.
+
+empty_statement ::= SEMICOLON.
+
+expression_statement ::= expression SEMICOLON.
+ /*TODO:IMPLEMENT THIS RULE: lookahead not-contains {, function*/
+
+if_statement ::= IF PAR_OPEN expression PAR_CLOSE ELSE statement.
+if_statement ::= IF PAR_OPEN expression PAR_CLOSE statement.
+
+
+iteration_statement ::= DO statement WHILE PAR_OPEN expression PAR_CLOSE SEMICOLON.
+iteration_statement ::= WHILE PAR_OPEN expression PAR_CLOSE statement.
+iteration_statement ::= FOR PAR_OPEN expression_noin_opt SEMICOLON expression_opt SEMICOLON expression_opt PAR_CLOSE statement.
+iteration_statement ::= FOR PAR_OPEN VAR variable_declaration_list_noin SEMICOLON expression_opt SEMICOLON expression_opt PAR_CLOSE statement.
+iteration_statement ::= FOR PAR_OPEN lefthandside_expression IN expression PAR_CLOSE statement.
+iteration_statement ::= FOR PAR_OPEN VAR variable_declaration_noin IN expression PAR_CLOSE statement.
+
+continue_statement ::= CONTINUE_NOLF identifier_opt SEMICOLON.
+
+break_statement ::= BREAK_NOLF identifier_opt SEMICOLON.
+
+return_statement ::= RETURN_NOLF expression_opt SEMICOLON.
+
+with_statement ::= WITH PAR_OPEN expression PAR_CLOSE statement.
+
+
+
+
+
+
+labelled_statement ::= identifier COLON statement.
+
+throw_statement ::= THROW_NOLF expression SEMICOLON.
+
+try_statement ::= TRY block catch.
+try_statement ::= TRY block finally.
+try_statement ::= TRY block catch finally.
+
+catch ::= CATCH PAR_OPEN identifier PAR_CLOSE block.
+
+finally ::= FINALLY block.
+
+identifier_opt ::= .
+identifier_opt ::= identifier.
+
+function_declaration ::= FUNCTION identifier PAR_OPEN formal_parameter_list_opt PAR_CLOSE CURLY_BRACE_OPEN function_body CURLY_BRACE_CLOSE.
+
+function_expression ::= FUNCTION identifier_opt PAR_OPEN formal_parameter_list_opt PAR_CLOSE CURLY_BRACE_OPEN function_body CURLY_BRACE_CLOSE.
+
+formal_parameter_list_opt ::= .
+formal_parameter_list_opt ::= formal_parameter_list.
+
+formal_parameter_list ::= identifier.
+formal_parameter_list ::= formal_parameter_list COMMA identifier.
+
+function_body ::= source_elements.
+
+
+source_elements ::= source_element.
+source_elements ::= source_elements source_element.
+
+source_element ::= statement.
+source_element ::= function_declaration.
+
+
Added: test-suite/trunk/MultiSource/Applications/lemon/example1.y
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/example1.y?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/example1.y (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/example1.y Wed Mar 26 12:03:57 2008
@@ -0,0 +1,68 @@
+/* Copyright (GPL) 2004 mchirico at users.sourceforge.net or mchirico at comcast.net
+
+ Simple lemon parser example.
+
+
+ $ ./lemon example1.y
+
+ The above statement will create example1.c.
+
+ The command below adds main and the
+ necessary "Parse" calls to the
+ end of this example1.c.
+
+
+ $ cat <<EOF >>example1.c
+ int main()
+ {
+ void* pParser = ParseAlloc (malloc);
+ Parse (pParser, INTEGER, 1);
+ Parse (pParser, PLUS, 0);
+ Parse (pParser, INTEGER, 2);
+ Parse (pParser, 0, 0);
+ ParseFree(pParser, free );
+ }
+ EOF
+
+
+ $ g++ -o ex1 example1.c
+ $ ./ex1
+
+ See the Makefile, as most all of this is
+ done automatically.
+
+ Downloads:
+ http://prdownloads.sourceforge.net/souptonuts/lemon_examples.tar.gz?download
+
+*/
+
+%token_type {int}
+
+%left PLUS MINUS.
+%left DIVIDE TIMES.
+
+%include {
+#include <iostream>
+#include "example1.h"
+}
+
+%syntax_error {
+ std::cout << "Syntax error!" << std::endl;
+}
+
+program ::= expr(A). { std::cout << "Result=" << A << std::endl; }
+
+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{
+ std::cout << "divide by zero" << std::endl;
+ }
+} /* end of DIVIDE */
+
+expr(A) ::= INTEGER(B). { A = B; }
+
Added: test-suite/trunk/MultiSource/Applications/lemon/example2.y
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/example2.y?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/example2.y (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/example2.y Wed Mar 26 12:03:57 2008
@@ -0,0 +1,107 @@
+/* Copyright (GPL) 2004 mchirico at users.sourceforge.net or mchirico at comcast.net
+ Simple lemon parser example.
+
+ Download:
+ http://prdownloads.sourceforge.net/souptonuts/lemon_examples.tar.gz?download
+
+
+ To compile this example see the Makefile. Or
+ this is what is taking place.
+
+ $ ./lemon example2.y
+ $ cat main_part2 >> example2.c
+ $ g++ -o ex2 -O2 -s -pipe example2.c
+
+ Next, run ./ex2
+
+ $ ./ex2
+
+ Which will give the following output:
+
+ Result.value=17 <---------------------------\
+ Result.n=4 |
+ Result.value=-9 |
+ Result.n=4 |
+ Result.value=78 |
+ Result.n=10 |
+ |
+ Now looking at a section of main_part2 |
+ we can see how 4 PLUS 13 in implemented. |
+ |
+ struct Token t0,t1; |
+ struct Token mToken; |
+ |
+ t0.value=4; |
+ t0.n=0; |
+ |
+ t1.value=13; |
+ t1.n=0; |
+ // Below 4 PLUS 14 ----------------- /
+ Parse (pParser, NUM, t0);
+ Parse (pParser, PLUS, t0);
+ Parse (pParser, NUM, t1);
+ Parse (pParser, 0, t0);
+
+
+
+
+
+
+
+
+
+*/
+
+%include {
+#include <iostream>
+#include "ex2def.h"
+#include "example2.h"
+}
+
+
+%token_type {Token}
+%default_type {Token}
+
+
+%type expr {Token}
+%type NUM {Token}
+
+%left PLUS MINUS.
+%left DIVIDE TIMES.
+
+
+
+%syntax_error {
+ std::cout << "Syntax error!" << std::endl;
+}
+
+program ::= expr(A). {
+ std::cout << "Result.value=" << A.value << std::endl;
+ std::cout << "Result.n=" << A.n << std::endl;
+
+ }
+
+
+expr(A) ::= expr(B) MINUS expr(C). { A.value = B.value - C.value;
+ A.n = B.n+1 + C.n+1;
+ }
+
+expr(A) ::= expr(B) PLUS expr(C). { A.value = B.value + C.value;
+ A.n = B.n+1 + C.n+1;
+ }
+
+expr(A) ::= expr(B) TIMES expr(C). { A.value = B.value * C.value;
+ A.n = B.n+1 + C.n+1;
+
+ }
+expr(A) ::= expr(B) DIVIDE expr(C). {
+
+ if(C.value != 0){
+ A.value = B.value / C.value;
+ A.n = B.n+1 + C.n+1;
+ }else{
+ std::cout << "divide by zero" << std::endl;
+ }
+} /* end of DIVIDE */
+expr(A) ::= NUM(B). { A.value = B.value; A.n = B.n+1; }
+
Added: test-suite/trunk/MultiSource/Applications/lemon/example3.y
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/example3.y?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/example3.y (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/example3.y Wed Mar 26 12:03:57 2008
@@ -0,0 +1,100 @@
+/* Copyright (GPL) 2004 mchirico at users.sourceforge.net or mchirico at comcast.net
+ Simple lemon parser example.
+
+
+ $ ./lemon example2.y
+ $ cat main_part2 >> example2.c
+ $ g++ -o ex2 -O2 -s -pipe example2.c
+
+ Now, to run the example.
+
+ $ ./ex2
+
+ The output will be as follows:
+
+
+ Result.value=17 <-------------------
+ Result.n=4 |
+ Result.value=-9 |
+ Result.n=4 |
+ Result.value=78 |
+ Result.n=10 |
+ |
+ Take a look at main_part2 |
+ |
+ |
+ t0.value=4; |
+ t0.n=0; |
+ |
+ t1.value=13; |
+ t1.n=0; |
+ |
+ //Note below is 4 PLUS 17 ------
+ Parse (pParser, NUM, t0);
+ Parse (pParser, PLUS, t0);
+ Parse (pParser, NUM, t1);
+ Parse (pParser, 0, t0);
+
+*/
+
+%include {
+#include <iostream>
+#include "ex3def.h"
+#include "example3.h"
+
+ void token_destructor(Token t)
+ {
+ std::cout << "In token_destructor t.value= " << t.value << std::endl;
+ std::cout << "In token_destructor t.n= " << t.n << std::endl;
+ }
+
+}
+
+%token_type {Token}
+%default_type {Token}
+%token_destructor { token_destructor($$); }
+
+%type expr {Token}
+%type id {Token}
+
+%left PLUS MINUS.
+%left DIVIDE TIMES.
+
+%parse_accept {
+ printf("parsing complete!\n\n\n");
+}
+
+%syntax_error {
+ std::cout << "Syntax error!" << std::endl;
+}
+
+program ::= expr(A). {
+ std::cout << "Result.value=" << A.value << std::endl;
+ std::cout << "Result.n=" << A.n << std::endl;
+
+ }
+
+expr(A) ::= expr(B) MINUS expr(C). { A.value = B.value - C.value;
+ A.n = B.n+1 + C.n+1;
+ }
+
+expr(A) ::= expr(B) PLUS expr(C). { A.value = B.value + C.value;
+ A.n = B.n+1 + C.n+1;
+ }
+
+expr(A) ::= expr(B) TIMES expr(C). { A.value = B.value * C.value;
+ A.n = B.n+1 + C.n+1;
+
+ }
+expr(A) ::= expr(B) DIVIDE expr(C). {
+
+ if(C.value != 0){
+ A.value = B.value / C.value;
+ A.n = B.n+1 + C.n+1;
+ }else{
+ std::cout << "divide by zero" << std::endl;
+ }
+} /* end of DIVIDE */
+expr(A) ::= NUM(B). { A.value = B.value; A.n = B.n+1; }
+
+
Added: test-suite/trunk/MultiSource/Applications/lemon/example4.y
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/example4.y?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/example4.y (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/example4.y Wed Mar 26 12:03:57 2008
@@ -0,0 +1,87 @@
+/* Copyright (GPL) 2004 mchirico at users.sourceforge.net or mchirico at comcast.net
+ Simple lemon parser example.
+
+
+ $ ./lemon example2.y
+
+
+
+*/
+
+%include {
+#include <iostream>
+#include "ex4def.h"
+#include "example4.h"
+
+
+ void token_destructor(Token t)
+ {
+ std::cout << "In token_destructor t.value= " << t.value << std::endl;
+ std::cout << "In token_destructor t.n= " << t.n << std::endl;
+ }
+
+
+}
+
+
+%token_type {Token}
+%default_type {Token}
+%token_destructor { token_destructor($$); }
+
+%type expr {Token}
+%type NUM {Token}
+
+
+
+
+%left PLUS MINUS.
+%left DIVIDE TIMES.
+
+%parse_accept {
+ printf("parsing complete!\n\n\n");
+}
+
+
+%syntax_error {
+ std::cout << "Syntax error!" << std::endl;
+}
+
+/* This is to terminate with a new line */
+main ::= in.
+in ::= .
+in ::= in state NEWLINE.
+
+
+
+state ::= expr(A). {
+ std::cout << "Result.value=" << A.value << std::endl;
+ std::cout << "Result.n=" << A.n << std::endl;
+
+ }
+
+
+
+expr(A) ::= expr(B) MINUS expr(C). { A.value = B.value - C.value;
+ A.n = B.n+1 + C.n+1;
+ }
+
+expr(A) ::= expr(B) PLUS expr(C). { A.value = B.value + C.value;
+ A.n = B.n+1 + C.n+1;
+ }
+
+expr(A) ::= expr(B) TIMES expr(C). { A.value = B.value * C.value;
+ A.n = B.n+1 + C.n+1;
+
+ }
+expr(A) ::= expr(B) DIVIDE expr(C). {
+
+ if(C.value != 0){
+ A.value = B.value / C.value;
+ A.n = B.n+1 + C.n+1;
+ }else{
+ std::cout << "divide by zero" << std::endl;
+ }
+} /* end of DIVIDE */
+expr(A) ::= NUM(B). { A.value = B.value; A.n = B.n+1; }
+
+
Added: test-suite/trunk/MultiSource/Applications/lemon/example5.y
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/example5.y?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/example5.y (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/example5.y Wed Mar 26 12:03:57 2008
@@ -0,0 +1,95 @@
+/* Copyright (GPL) 2004 mchirico at users.sourceforge.net or mchirico at comcast.net
+ Simple lemon parser example.
+
+
+ $ ./lemon example2.y
+
+
+
+*/
+
+%include {
+#include <iostream>
+#include "ex5def.h"
+#include "example5.h"
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include "lexglobal.h"
+#define BUFS 1024
+
+
+
+ void token_destructor(Token t)
+ {
+ std::cout << "In token_destructor t.value= " << t.value << std::endl;
+ std::cout << "In token_destructor t.n= " << t.n << std::endl;
+ }
+
+
+}
+
+
+%token_type {Token}
+%default_type {Token}
+%token_destructor { token_destructor($$); }
+
+%type expr {Token}
+%type id {Token}
+
+
+
+
+%left PLUS MINUS.
+%left DIVIDE TIMES.
+
+%parse_accept {
+ printf("parsing complete!\n\n\n");
+}
+
+
+%syntax_error {
+ std::cout << "Syntax error!" << std::endl;
+}
+
+/* This is to terminate with a new line */
+main ::= in.
+in ::= .
+in ::= in state NEWLINE.
+
+
+
+state ::= expr(A). {
+ std::cout << "Result.value=" << A.value << std::endl;
+ std::cout << "Result.n=" << A.n << std::endl;
+
+ }
+
+
+
+expr(A) ::= expr(B) MINUS expr(C). { A.value = B.value - C.value;
+ A.n = B.n+1 + C.n+1;
+ }
+
+expr(A) ::= expr(B) PLUS expr(C). { A.value = B.value + C.value;
+ A.n = B.n+1 + C.n+1;
+ }
+
+expr(A) ::= expr(B) TIMES expr(C). { A.value = B.value * C.value;
+ A.n = B.n+1 + C.n+1;
+
+ }
+expr(A) ::= expr(B) DIVIDE expr(C). {
+
+ if(C.value != 0){
+ A.value = B.value / C.value;
+ A.n = B.n+1 + C.n+1;
+ }else{
+ std::cout << "divide by zero" << std::endl;
+ }
+} /* end of DIVIDE */
+expr(A) ::= NUM(B). { A.value = B.value; A.n = B.n+1; }
+
+
Added: test-suite/trunk/MultiSource/Applications/lemon/example_COPYING
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/example_COPYING?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/example_COPYING (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/example_COPYING Wed Mar 26 12:03:57 2008
@@ -0,0 +1,298 @@
+Copyright (C) 2003 Mike Chirico mmc mchirico at users.sourceforge.net
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
Added: test-suite/trunk/MultiSource/Applications/lemon/lemon.c
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/lemon.c?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/lemon.c (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/lemon.c Wed Mar 26 12:03:57 2008
@@ -0,0 +1,4867 @@
+/*
+** This file contains all sources (including headers) to the LEMON
+** LALR(1) parser generator. The sources have been combined into a
+** single file to make it easy to include LEMON in the source tree
+** and Makefile of another program.
+**
+** The author of this program disclaims copyright.
+*/
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifndef __WIN32__
+# if defined(_WIN32) || defined(WIN32)
+# define __WIN32__
+# endif
+#endif
+
+#ifdef __WIN32__
+extern int access();
+#else
+#include <unistd.h>
+#endif
+
+/* #define PRIVATE static */
+#define PRIVATE
+
+#ifdef TEST
+#define MAXRHS 5 /* Set low to exercise exception code */
+#else
+#define MAXRHS 1000
+#endif
+
+static char *msort(char*,char**,int(*)(const char*,const char*));
+
+static struct action *Action_new(void);
+static struct action *Action_sort(struct action *);
+
+/********** From the file "build.h" ************************************/
+void FindRulePrecedences();
+void FindFirstSets();
+void FindStates();
+void FindLinks();
+void FindFollowSets();
+void FindActions();
+
+/********* From the file "configlist.h" *********************************/
+void Configlist_init(/* void */);
+struct config *Configlist_add(/* struct rule *, int */);
+struct config *Configlist_addbasis(/* struct rule *, int */);
+void Configlist_closure(/* void */);
+void Configlist_sort(/* void */);
+void Configlist_sortbasis(/* void */);
+struct config *Configlist_return(/* void */);
+struct config *Configlist_basis(/* void */);
+void Configlist_eat(/* struct config * */);
+void Configlist_reset(/* void */);
+
+/********* From the file "error.h" ***************************************/
+void ErrorMsg(const char *, int,const char *, ...);
+
+/****** From the file "option.h" ******************************************/
+struct s_options {
+ enum { OPT_FLAG=1, OPT_INT, OPT_DBL, OPT_STR,
+ OPT_FFLAG, OPT_FINT, OPT_FDBL, OPT_FSTR} type;
+ char *label;
+ char *arg;
+ char *message;
+};
+int OptInit(/* char**,struct s_options*,FILE* */);
+int OptNArgs(/* void */);
+char *OptArg(/* int */);
+void OptErr(/* int */);
+void OptPrint(/* void */);
+
+/******** From the file "parse.h" *****************************************/
+void Parse(/* struct lemon *lemp */);
+
+/********* From the file "plink.h" ***************************************/
+struct plink *Plink_new(/* void */);
+void Plink_add(/* struct plink **, struct config * */);
+void Plink_copy(/* struct plink **, struct plink * */);
+void Plink_delete(/* struct plink * */);
+
+/********** From the file "report.h" *************************************/
+void Reprint(/* struct lemon * */);
+void ReportOutput(/* struct lemon * */);
+void ReportTable(/* struct lemon * */);
+void ReportHeader(/* struct lemon * */);
+void CompressTables(/* struct lemon * */);
+void ResortStates(/* struct lemon * */);
+
+/********** From the file "set.h" ****************************************/
+void SetSize(/* int N */); /* All sets will be of size N */
+char *SetNew(/* void */); /* A new set for element 0..N */
+void SetFree(/* char* */); /* Deallocate a set */
+
+int SetAdd(/* char*,int */); /* Add element to a set */
+int SetUnion(/* char *A,char *B */); /* A <- A U B, thru element N */
+
+#define SetFind(X,Y) (X[Y]) /* True if Y is in set X */
+
+/********** From the file "struct.h" *************************************/
+/*
+** Principal data structures for the LEMON parser generator.
+*/
+
+typedef enum {LEMON_FALSE=0, LEMON_TRUE} Boolean;
+
+/* Symbols (terminals and nonterminals) of the grammar are stored
+** in the following: */
+struct symbol {
+ char *name; /* Name of the symbol */
+ int index; /* Index number for this symbol */
+ enum {
+ TERMINAL,
+ NONTERMINAL,
+ MULTITERMINAL
+ } type; /* Symbols are all either TERMINALS or NTs */
+ struct rule *rule; /* Linked list of rules of this (if an NT) */
+ struct symbol *fallback; /* fallback token in case this token doesn't parse */
+ int prec; /* Precedence if defined (-1 otherwise) */
+ enum e_assoc {
+ LEFT,
+ RIGHT,
+ NONE,
+ UNK
+ } assoc; /* Associativity if predecence is defined */
+ char *firstset; /* First-set for all rules of this symbol */
+ Boolean lambda; /* True if NT and can generate an empty string */
+ int useCnt; /* Number of times used */
+ char *destructor; /* Code which executes whenever this symbol is
+ ** popped from the stack during error processing */
+ int destructorln; /* Line number of destructor code */
+ char *datatype; /* The data type of information held by this
+ ** object. Only used if type==NONTERMINAL */
+ int dtnum; /* The data type number. In the parser, the value
+ ** stack is a union. The .yy%d element of this
+ ** union is the correct data type for this object */
+ /* The following fields are used by MULTITERMINALs only */
+ int nsubsym; /* Number of constituent symbols in the MULTI */
+ struct symbol **subsym; /* Array of constituent symbols */
+};
+
+/* Each production rule in the grammar is stored in the following
+** structure. */
+struct rule {
+ struct symbol *lhs; /* Left-hand side of the rule */
+ char *lhsalias; /* Alias for the LHS (NULL if none) */
+ int lhsStart; /* True if left-hand side is the start symbol */
+ int ruleline; /* Line number for the rule */
+ int nrhs; /* Number of RHS symbols */
+ struct symbol **rhs; /* The RHS symbols */
+ char **rhsalias; /* An alias for each RHS symbol (NULL if none) */
+ int line; /* Line number at which code begins */
+ char *code; /* The code executed when this rule is reduced */
+ struct symbol *precsym; /* Precedence symbol for this rule */
+ int index; /* An index number for this rule */
+ Boolean canReduce; /* True if this rule is ever reduced */
+ struct rule *nextlhs; /* Next rule with the same LHS */
+ struct rule *next; /* Next rule in the global list */
+};
+
+/* A configuration is a production rule of the grammar together with
+** a mark (dot) showing how much of that rule has been processed so far.
+** Configurations also contain a follow-set which is a list of terminal
+** symbols which are allowed to immediately follow the end of the rule.
+** Every configuration is recorded as an instance of the following: */
+struct config {
+ struct rule *rp; /* The rule upon which the configuration is based */
+ int dot; /* The parse point */
+ char *fws; /* Follow-set for this configuration only */
+ struct plink *fplp; /* Follow-set forward propagation links */
+ struct plink *bplp; /* Follow-set backwards propagation links */
+ struct state *stp; /* Pointer to state which contains this */
+ enum {
+ COMPLETE, /* The status is used during followset and */
+ INCOMPLETE /* shift computations */
+ } status;
+ struct config *next; /* Next configuration in the state */
+ struct config *bp; /* The next basis configuration */
+};
+
+/* Every shift or reduce operation is stored as one of the following */
+struct action {
+ struct symbol *sp; /* The look-ahead symbol */
+ enum e_action {
+ SHIFT,
+ ACCEPT,
+ REDUCE,
+ ERROR,
+ SSCONFLICT, /* A shift/shift conflict */
+ SRCONFLICT, /* Was a reduce, but part of a conflict */
+ RRCONFLICT, /* Was a reduce, but part of a conflict */
+ SH_RESOLVED, /* Was a shift. Precedence resolved conflict */
+ RD_RESOLVED, /* Was reduce. Precedence resolved conflict */
+ NOT_USED /* Deleted by compression */
+ } type;
+ union {
+ struct state *stp; /* The new state, if a shift */
+ struct rule *rp; /* The rule, if a reduce */
+ } x;
+ struct action *next; /* Next action for this state */
+ struct action *collide; /* Next action with the same hash */
+};
+
+/* Each state of the generated parser's finite state machine
+** is encoded as an instance of the following structure. */
+struct state {
+ struct config *bp; /* The basis configurations for this state */
+ struct config *cfp; /* All configurations in this set */
+ int statenum; /* Sequencial number for this state */
+ struct action *ap; /* Array of actions for this state */
+ int nTknAct, nNtAct; /* Number of actions on terminals and nonterminals */
+ int iTknOfst, iNtOfst; /* yy_action[] offset for terminals and nonterms */
+ int iDflt; /* Default action */
+};
+#define NO_OFFSET (-2147483647)
+
+/* A followset propagation link indicates that the contents of one
+** configuration followset should be propagated to another whenever
+** the first changes. */
+struct plink {
+ struct config *cfp; /* The configuration to which linked */
+ struct plink *next; /* The next propagate link */
+};
+
+/* The state vector for the entire parser generator is recorded as
+** follows. (LEMON uses no global variables and makes little use of
+** static variables. Fields in the following structure can be thought
+** of as begin global variables in the program.) */
+struct lemon {
+ struct state **sorted; /* Table of states sorted by state number */
+ struct rule *rule; /* List of all rules */
+ int nstate; /* Number of states */
+ int nrule; /* Number of rules */
+ int nsymbol; /* Number of terminal and nonterminal symbols */
+ int nterminal; /* Number of terminal symbols */
+ struct symbol **symbols; /* Sorted array of pointers to symbols */
+ int errorcnt; /* Number of errors */
+ struct symbol *errsym; /* The error symbol */
+ struct symbol *wildcard; /* Token that matches anything */
+ char *name; /* Name of the generated parser */
+ char *arg; /* Declaration of the 3th argument to parser */
+ char *tokentype; /* Type of terminal symbols in the parser stack */
+ char *vartype; /* The default type of non-terminal symbols */
+ char *start; /* Name of the start symbol for the grammar */
+ char *stacksize; /* Size of the parser stack */
+ char *include; /* Code to put at the start of the C file */
+ int includeln; /* Line number for start of include code */
+ char *error; /* Code to execute when an error is seen */
+ int errorln; /* Line number for start of error code */
+ char *overflow; /* Code to execute on a stack overflow */
+ int overflowln; /* Line number for start of overflow code */
+ char *failure; /* Code to execute on parser failure */
+ int failureln; /* Line number for start of failure code */
+ char *accept; /* Code to execute when the parser excepts */
+ int acceptln; /* Line number for the start of accept code */
+ char *extracode; /* Code appended to the generated file */
+ int extracodeln; /* Line number for the start of the extra code */
+ char *tokendest; /* Code to execute to destroy token data */
+ int tokendestln; /* Line number for token destroyer code */
+ char *vardest; /* Code for the default non-terminal destructor */
+ int vardestln; /* Line number for default non-term destructor code*/
+ char *filename; /* Name of the input file */
+ char *outname; /* Name of the current output file */
+ char *tokenprefix; /* A prefix added to token names in the .h file */
+ int nconflict; /* Number of parsing conflicts */
+ int tablesize; /* Size of the parse tables */
+ int basisflag; /* Print only basis configurations */
+ int has_fallback; /* True if any %fallback is seen in the grammer */
+ char *argv0; /* Name of the program */
+};
+
+#define MemoryCheck(X) if((X)==0){ \
+ extern void memory_error(); \
+ memory_error(); \
+}
+
+/**************** From the file "table.h" *********************************/
+/*
+** All code in this file has been automatically generated
+** from a specification in the file
+** "table.q"
+** by the associative array code building program "aagen".
+** Do not edit this file! Instead, edit the specification
+** file, then rerun aagen.
+*/
+/*
+** Code for processing tables in the LEMON parser generator.
+*/
+
+/* Routines for handling a strings */
+
+char *Strsafe();
+
+void Strsafe_init(/* void */);
+int Strsafe_insert(/* char * */);
+char *Strsafe_find(/* char * */);
+
+/* Routines for handling symbols of the grammar */
+
+struct symbol *Symbol_new();
+int Symbolcmpp(/* struct symbol **, struct symbol ** */);
+void Symbol_init(/* void */);
+int Symbol_insert(/* struct symbol *, char * */);
+struct symbol *Symbol_find(/* char * */);
+struct symbol *Symbol_Nth(/* int */);
+int Symbol_count(/* */);
+struct symbol **Symbol_arrayof(/* */);
+
+/* Routines to manage the state table */
+
+int Configcmp(/* struct config *, struct config * */);
+struct state *State_new();
+void State_init(/* void */);
+int State_insert(/* struct state *, struct config * */);
+struct state *State_find(/* struct config * */);
+struct state **State_arrayof(/* */);
+
+/* Routines used for efficiency in Configlist_add */
+
+void Configtable_init(/* void */);
+int Configtable_insert(/* struct config * */);
+struct config *Configtable_find(/* struct config * */);
+void Configtable_clear(/* int(*)(struct config *) */);
+/****************** From the file "action.c" *******************************/
+/*
+** Routines processing parser actions in the LEMON parser generator.
+*/
+
+/* Allocate a new parser action */
+static struct action *Action_new(void){
+ static struct action *freelist = 0;
+ struct action *new;
+
+ if( freelist==0 ){
+ int i;
+ int amt = 100;
+ freelist = (struct action *)calloc(amt, sizeof(struct action));
+ if( freelist==0 ){
+ fprintf(stderr,"Unable to allocate memory for a new parser action.");
+ exit(1);
+ }
+ for(i=0; i<amt-1; i++) freelist[i].next = &freelist[i+1];
+ freelist[amt-1].next = 0;
+ }
+ new = freelist;
+ freelist = freelist->next;
+ return new;
+}
+
+/* Compare two actions for sorting purposes. Return negative, zero, or
+** positive if the first action is less than, equal to, or greater than
+** the first
+*/
+static int actioncmp(
+ struct action *ap1,
+ struct action *ap2
+){
+ int rc;
+ rc = ap1->sp->index - ap2->sp->index;
+ if( rc==0 ){
+ rc = (int)ap1->type - (int)ap2->type;
+ }
+ if( rc==0 && ap1->type==REDUCE ){
+ rc = ap1->x.rp->index - ap2->x.rp->index;
+ }
+ return rc;
+}
+
+/* Sort parser actions */
+static struct action *Action_sort(
+ struct action *ap
+){
+ ap = (struct action *)msort((char *)ap,(char **)&ap->next,
+ (int(*)(const char*,const char*))actioncmp);
+ return ap;
+}
+
+void Action_add(app,type,sp,arg)
+struct action **app;
+enum e_action type;
+struct symbol *sp;
+char *arg;
+{
+ struct action *new;
+ new = Action_new();
+ new->next = *app;
+ *app = new;
+ new->type = type;
+ new->sp = sp;
+ if( type==SHIFT ){
+ new->x.stp = (struct state *)arg;
+ }else{
+ new->x.rp = (struct rule *)arg;
+ }
+}
+/********************** New code to implement the "acttab" module ***********/
+/*
+** This module implements routines use to construct the yy_action[] table.
+*/
+
+/*
+** The state of the yy_action table under construction is an instance of
+** the following structure
+*/
+typedef struct acttab acttab;
+struct acttab {
+ int nAction; /* Number of used slots in aAction[] */
+ int nActionAlloc; /* Slots allocated for aAction[] */
+ struct {
+ int lookahead; /* Value of the lookahead token */
+ int action; /* Action to take on the given lookahead */
+ } *aAction, /* The yy_action[] table under construction */
+ *aLookahead; /* A single new transaction set */
+ int mnLookahead; /* Minimum aLookahead[].lookahead */
+ int mnAction; /* Action associated with mnLookahead */
+ int mxLookahead; /* Maximum aLookahead[].lookahead */
+ int nLookahead; /* Used slots in aLookahead[] */
+ int nLookaheadAlloc; /* Slots allocated in aLookahead[] */
+};
+
+/* Return the number of entries in the yy_action table */
+#define acttab_size(X) ((X)->nAction)
+
+/* The value for the N-th entry in yy_action */
+#define acttab_yyaction(X,N) ((X)->aAction[N].action)
+
+/* The value for the N-th entry in yy_lookahead */
+#define acttab_yylookahead(X,N) ((X)->aAction[N].lookahead)
+
+/* Free all memory associated with the given acttab */
+void acttab_free(acttab *p){
+ free( p->aAction );
+ free( p->aLookahead );
+ free( p );
+}
+
+/* Allocate a new acttab structure */
+acttab *acttab_alloc(void){
+ acttab *p = calloc( 1, sizeof(*p) );
+ if( p==0 ){
+ fprintf(stderr,"Unable to allocate memory for a new acttab.");
+ exit(1);
+ }
+ memset(p, 0, sizeof(*p));
+ return p;
+}
+
+/* Add a new action to the current transaction set
+*/
+void acttab_action(acttab *p, int lookahead, int action){
+ if( p->nLookahead>=p->nLookaheadAlloc ){
+ p->nLookaheadAlloc += 25;
+ p->aLookahead = realloc( p->aLookahead,
+ sizeof(p->aLookahead[0])*p->nLookaheadAlloc );
+ if( p->aLookahead==0 ){
+ fprintf(stderr,"malloc failed\n");
+ exit(1);
+ }
+ }
+ if( p->nLookahead==0 ){
+ p->mxLookahead = lookahead;
+ p->mnLookahead = lookahead;
+ p->mnAction = action;
+ }else{
+ if( p->mxLookahead<lookahead ) p->mxLookahead = lookahead;
+ if( p->mnLookahead>lookahead ){
+ p->mnLookahead = lookahead;
+ p->mnAction = action;
+ }
+ }
+ p->aLookahead[p->nLookahead].lookahead = lookahead;
+ p->aLookahead[p->nLookahead].action = action;
+ p->nLookahead++;
+}
+
+/*
+** Add the transaction set built up with prior calls to acttab_action()
+** into the current action table. Then reset the transaction set back
+** to an empty set in preparation for a new round of acttab_action() calls.
+**
+** Return the offset into the action table of the new transaction.
+*/
+int acttab_insert(acttab *p){
+ int i, j, k, n;
+ assert( p->nLookahead>0 );
+
+ /* Make sure we have enough space to hold the expanded action table
+ ** in the worst case. The worst case occurs if the transaction set
+ ** must be appended to the current action table
+ */
+ n = p->mxLookahead + 1;
+ if( p->nAction + n >= p->nActionAlloc ){
+ int oldAlloc = p->nActionAlloc;
+ p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20;
+ p->aAction = realloc( p->aAction,
+ sizeof(p->aAction[0])*p->nActionAlloc);
+ if( p->aAction==0 ){
+ fprintf(stderr,"malloc failed\n");
+ exit(1);
+ }
+ for(i=oldAlloc; i<p->nActionAlloc; i++){
+ p->aAction[i].lookahead = -1;
+ p->aAction[i].action = -1;
+ }
+ }
+
+ /* Scan the existing action table looking for an offset where we can
+ ** insert the current transaction set. Fall out of the loop when that
+ ** offset is found. In the worst case, we fall out of the loop when
+ ** i reaches p->nAction, which means we append the new transaction set.
+ **
+ ** i is the index in p->aAction[] where p->mnLookahead is inserted.
+ */
+ for(i=0; i<p->nAction+p->mnLookahead; i++){
+ if( p->aAction[i].lookahead<0 ){
+ for(j=0; j<p->nLookahead; j++){
+ k = p->aLookahead[j].lookahead - p->mnLookahead + i;
+ if( k<0 ) break;
+ if( p->aAction[k].lookahead>=0 ) break;
+ }
+ if( j<p->nLookahead ) continue;
+ for(j=0; j<p->nAction; j++){
+ if( p->aAction[j].lookahead==j+p->mnLookahead-i ) break;
+ }
+ if( j==p->nAction ){
+ break; /* Fits in empty slots */
+ }
+ }else if( p->aAction[i].lookahead==p->mnLookahead ){
+ if( p->aAction[i].action!=p->mnAction ) continue;
+ for(j=0; j<p->nLookahead; j++){
+ k = p->aLookahead[j].lookahead - p->mnLookahead + i;
+ if( k<0 || k>=p->nAction ) break;
+ if( p->aLookahead[j].lookahead!=p->aAction[k].lookahead ) break;
+ if( p->aLookahead[j].action!=p->aAction[k].action ) break;
+ }
+ if( j<p->nLookahead ) continue;
+ n = 0;
+ for(j=0; j<p->nAction; j++){
+ if( p->aAction[j].lookahead<0 ) continue;
+ if( p->aAction[j].lookahead==j+p->mnLookahead-i ) n++;
+ }
+ if( n==p->nLookahead ){
+ break; /* Same as a prior transaction set */
+ }
+ }
+ }
+ /* Insert transaction set at index i. */
+ for(j=0; j<p->nLookahead; j++){
+ k = p->aLookahead[j].lookahead - p->mnLookahead + i;
+ p->aAction[k] = p->aLookahead[j];
+ if( k>=p->nAction ) p->nAction = k+1;
+ }
+ p->nLookahead = 0;
+
+ /* Return the offset that is added to the lookahead in order to get the
+ ** index into yy_action of the action */
+ return i - p->mnLookahead;
+}
+
+/********************** From the file "build.c" *****************************/
+/*
+** Routines to construction the finite state machine for the LEMON
+** parser generator.
+*/
+
+/* Find a precedence symbol of every rule in the grammar.
+**
+** Those rules which have a precedence symbol coded in the input
+** grammar using the "[symbol]" construct will already have the
+** rp->precsym field filled. Other rules take as their precedence
+** symbol the first RHS symbol with a defined precedence. If there
+** are not RHS symbols with a defined precedence, the precedence
+** symbol field is left blank.
+*/
+void FindRulePrecedences(xp)
+struct lemon *xp;
+{
+ struct rule *rp;
+ for(rp=xp->rule; rp; rp=rp->next){
+ if( rp->precsym==0 ){
+ int i, j;
+ for(i=0; i<rp->nrhs && rp->precsym==0; i++){
+ struct symbol *sp = rp->rhs[i];
+ if( sp->type==MULTITERMINAL ){
+ for(j=0; j<sp->nsubsym; j++){
+ if( sp->subsym[j]->prec>=0 ){
+ rp->precsym = sp->subsym[j];
+ break;
+ }
+ }
+ }else if( sp->prec>=0 ){
+ rp->precsym = rp->rhs[i];
+ }
+ }
+ }
+ }
+ return;
+}
+
+/* Find all nonterminals which will generate the empty string.
+** Then go back and compute the first sets of every nonterminal.
+** The first set is the set of all terminal symbols which can begin
+** a string generated by that nonterminal.
+*/
+void FindFirstSets(lemp)
+struct lemon *lemp;
+{
+ int i, j;
+ struct rule *rp;
+ int progress;
+
+ for(i=0; i<lemp->nsymbol; i++){
+ lemp->symbols[i]->lambda = LEMON_FALSE;
+ }
+ for(i=lemp->nterminal; i<lemp->nsymbol; i++){
+ lemp->symbols[i]->firstset = SetNew();
+ }
+
+ /* First compute all lambdas */
+ do{
+ progress = 0;
+ for(rp=lemp->rule; rp; rp=rp->next){
+ if( rp->lhs->lambda ) continue;
+ for(i=0; i<rp->nrhs; i++){
+ struct symbol *sp = rp->rhs[i];
+ if( sp->type!=TERMINAL || sp->lambda==LEMON_FALSE ) break;
+ }
+ if( i==rp->nrhs ){
+ rp->lhs->lambda = LEMON_TRUE;
+ progress = 1;
+ }
+ }
+ }while( progress );
+
+ /* Now compute all first sets */
+ do{
+ struct symbol *s1, *s2;
+ progress = 0;
+ for(rp=lemp->rule; rp; rp=rp->next){
+ s1 = rp->lhs;
+ for(i=0; i<rp->nrhs; i++){
+ s2 = rp->rhs[i];
+ if( s2->type==TERMINAL ){
+ progress += SetAdd(s1->firstset,s2->index);
+ break;
+ }else if( s2->type==MULTITERMINAL ){
+ for(j=0; j<s2->nsubsym; j++){
+ progress += SetAdd(s1->firstset,s2->subsym[j]->index);
+ }
+ break;
+ }else if( s1==s2 ){
+ if( s1->lambda==LEMON_FALSE ) break;
+ }else{
+ progress += SetUnion(s1->firstset,s2->firstset);
+ if( s2->lambda==LEMON_FALSE ) break;
+ }
+ }
+ }
+ }while( progress );
+ return;
+}
+
+/* Compute all LR(0) states for the grammar. Links
+** are added to between some states so that the LR(1) follow sets
+** can be computed later.
+*/
+PRIVATE struct state *getstate(/* struct lemon * */); /* forward reference */
+void FindStates(lemp)
+struct lemon *lemp;
+{
+ struct symbol *sp;
+ struct rule *rp;
+
+ Configlist_init();
+
+ /* Find the start symbol */
+ if( lemp->start ){
+ sp = Symbol_find(lemp->start);
+ if( sp==0 ){
+ ErrorMsg(lemp->filename,0,
+"The specified start symbol \"%s\" is not \
+in a nonterminal of the grammar. \"%s\" will be used as the start \
+symbol instead.",lemp->start,lemp->rule->lhs->name);
+ lemp->errorcnt++;
+ sp = lemp->rule->lhs;
+ }
+ }else{
+ sp = lemp->rule->lhs;
+ }
+
+ /* Make sure the start symbol doesn't occur on the right-hand side of
+ ** any rule. Report an error if it does. (YACC would generate a new
+ ** start symbol in this case.) */
+ for(rp=lemp->rule; rp; rp=rp->next){
+ int i;
+ for(i=0; i<rp->nrhs; i++){
+ if( rp->rhs[i]==sp ){ /* FIX ME: Deal with multiterminals */
+ ErrorMsg(lemp->filename,0,
+"The start symbol \"%s\" occurs on the \
+right-hand side of a rule. This will result in a parser which \
+does not work properly.",sp->name);
+ lemp->errorcnt++;
+ }
+ }
+ }
+
+ /* The basis configuration set for the first state
+ ** is all rules which have the start symbol as their
+ ** left-hand side */
+ for(rp=sp->rule; rp; rp=rp->nextlhs){
+ struct config *newcfp;
+ rp->lhsStart = 1;
+ newcfp = Configlist_addbasis(rp,0);
+ SetAdd(newcfp->fws,0);
+ }
+
+ /* Compute the first state. All other states will be
+ ** computed automatically during the computation of the first one.
+ ** The returned pointer to the first state is not used. */
+ (void)getstate(lemp);
+ return;
+}
+
+/* Return a pointer to a state which is described by the configuration
+** list which has been built from calls to Configlist_add.
+*/
+PRIVATE void buildshifts(/* struct lemon *, struct state * */); /* Forwd ref */
+PRIVATE struct state *getstate(lemp)
+struct lemon *lemp;
+{
+ struct config *cfp, *bp;
+ struct state *stp;
+
+ /* Extract the sorted basis of the new state. The basis was constructed
+ ** by prior calls to "Configlist_addbasis()". */
+ Configlist_sortbasis();
+ bp = Configlist_basis();
+
+ /* Get a state with the same basis */
+ stp = State_find(bp);
+ if( stp ){
+ /* A state with the same basis already exists! Copy all the follow-set
+ ** propagation links from the state under construction into the
+ ** preexisting state, then return a pointer to the preexisting state */
+ struct config *x, *y;
+ for(x=bp, y=stp->bp; x && y; x=x->bp, y=y->bp){
+ Plink_copy(&y->bplp,x->bplp);
+ Plink_delete(x->fplp);
+ x->fplp = x->bplp = 0;
+ }
+ cfp = Configlist_return();
+ Configlist_eat(cfp);
+ }else{
+ /* This really is a new state. Construct all the details */
+ Configlist_closure(lemp); /* Compute the configuration closure */
+ Configlist_sort(); /* Sort the configuration closure */
+ cfp = Configlist_return(); /* Get a pointer to the config list */
+ stp = State_new(); /* A new state structure */
+ MemoryCheck(stp);
+ stp->bp = bp; /* Remember the configuration basis */
+ stp->cfp = cfp; /* Remember the configuration closure */
+ stp->statenum = lemp->nstate++; /* Every state gets a sequence number */
+ stp->ap = 0; /* No actions, yet. */
+ State_insert(stp,stp->bp); /* Add to the state table */
+ buildshifts(lemp,stp); /* Recursively compute successor states */
+ }
+ return stp;
+}
+
+/*
+** Return true if two symbols are the same.
+*/
+int same_symbol(a,b)
+struct symbol *a;
+struct symbol *b;
+{
+ int i;
+ if( a==b ) return 1;
+ if( a->type!=MULTITERMINAL ) return 0;
+ if( b->type!=MULTITERMINAL ) return 0;
+ if( a->nsubsym!=b->nsubsym ) return 0;
+ for(i=0; i<a->nsubsym; i++){
+ if( a->subsym[i]!=b->subsym[i] ) return 0;
+ }
+ return 1;
+}
+
+/* Construct all successor states to the given state. A "successor"
+** state is any state which can be reached by a shift action.
+*/
+PRIVATE void buildshifts(lemp,stp)
+struct lemon *lemp;
+struct state *stp; /* The state from which successors are computed */
+{
+ struct config *cfp; /* For looping thru the config closure of "stp" */
+ struct config *bcfp; /* For the inner loop on config closure of "stp" */
+ struct config *new; /* */
+ struct symbol *sp; /* Symbol following the dot in configuration "cfp" */
+ struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */
+ struct state *newstp; /* A pointer to a successor state */
+
+ /* Each configuration becomes complete after it contibutes to a successor
+ ** state. Initially, all configurations are incomplete */
+ for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE;
+
+ /* Loop through all configurations of the state "stp" */
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){
+ if( cfp->status==COMPLETE ) continue; /* Already used by inner loop */
+ if( cfp->dot>=cfp->rp->nrhs ) continue; /* Can't shift this config */
+ Configlist_reset(); /* Reset the new config set */
+ sp = cfp->rp->rhs[cfp->dot]; /* Symbol after the dot */
+
+ /* For every configuration in the state "stp" which has the symbol "sp"
+ ** following its dot, add the same configuration to the basis set under
+ ** construction but with the dot shifted one symbol to the right. */
+ for(bcfp=cfp; bcfp; bcfp=bcfp->next){
+ if( bcfp->status==COMPLETE ) continue; /* Already used */
+ if( bcfp->dot>=bcfp->rp->nrhs ) continue; /* Can't shift this one */
+ bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */
+ if( !same_symbol(bsp,sp) ) continue; /* Must be same as for "cfp" */
+ bcfp->status = COMPLETE; /* Mark this config as used */
+ new = Configlist_addbasis(bcfp->rp,bcfp->dot+1);
+ Plink_add(&new->bplp,bcfp);
+ }
+
+ /* Get a pointer to the state described by the basis configuration set
+ ** constructed in the preceding loop */
+ newstp = getstate(lemp);
+
+ /* The state "newstp" is reached from the state "stp" by a shift action
+ ** on the symbol "sp" */
+ if( sp->type==MULTITERMINAL ){
+ int i;
+ for(i=0; i<sp->nsubsym; i++){
+ Action_add(&stp->ap,SHIFT,sp->subsym[i],(char*)newstp);
+ }
+ }else{
+ Action_add(&stp->ap,SHIFT,sp,(char *)newstp);
+ }
+ }
+}
+
+/*
+** Construct the propagation links
+*/
+void FindLinks(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct config *cfp, *other;
+ struct state *stp;
+ struct plink *plp;
+
+ /* Housekeeping detail:
+ ** Add to every propagate link a pointer back to the state to
+ ** which the link is attached. */
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){
+ cfp->stp = stp;
+ }
+ }
+
+ /* Convert all backlinks into forward links. Only the forward
+ ** links are used in the follow-set computation. */
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){
+ for(plp=cfp->bplp; plp; plp=plp->next){
+ other = plp->cfp;
+ Plink_add(&other->fplp,cfp);
+ }
+ }
+ }
+}
+
+/* Compute all followsets.
+**
+** A followset is the set of all symbols which can come immediately
+** after a configuration.
+*/
+void FindFollowSets(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct config *cfp;
+ struct plink *plp;
+ int progress;
+ int change;
+
+ for(i=0; i<lemp->nstate; i++){
+ for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){
+ cfp->status = INCOMPLETE;
+ }
+ }
+
+ do{
+ progress = 0;
+ for(i=0; i<lemp->nstate; i++){
+ for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){
+ if( cfp->status==COMPLETE ) continue;
+ for(plp=cfp->fplp; plp; plp=plp->next){
+ change = SetUnion(plp->cfp->fws,cfp->fws);
+ if( change ){
+ plp->cfp->status = INCOMPLETE;
+ progress = 1;
+ }
+ }
+ cfp->status = COMPLETE;
+ }
+ }
+ }while( progress );
+}
+
+static int resolve_conflict();
+
+/* Compute the reduce actions, and resolve conflicts.
+*/
+void FindActions(lemp)
+struct lemon *lemp;
+{
+ int i,j;
+ struct config *cfp;
+ struct state *stp;
+ struct symbol *sp;
+ struct rule *rp;
+
+ /* Add all of the reduce actions
+ ** A reduce action is added for each element of the followset of
+ ** a configuration which has its dot at the extreme right.
+ */
+ for(i=0; i<lemp->nstate; i++){ /* Loop over all states */
+ stp = lemp->sorted[i];
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){ /* Loop over all configurations */
+ if( cfp->rp->nrhs==cfp->dot ){ /* Is dot at extreme right? */
+ for(j=0; j<lemp->nterminal; j++){
+ if( SetFind(cfp->fws,j) ){
+ /* Add a reduce action to the state "stp" which will reduce by the
+ ** rule "cfp->rp" if the lookahead symbol is "lemp->symbols[j]" */
+ Action_add(&stp->ap,REDUCE,lemp->symbols[j],(char *)cfp->rp);
+ }
+ }
+ }
+ }
+ }
+
+ /* Add the accepting token */
+ if( lemp->start ){
+ sp = Symbol_find(lemp->start);
+ if( sp==0 ) sp = lemp->rule->lhs;
+ }else{
+ sp = lemp->rule->lhs;
+ }
+ /* Add to the first state (which is always the starting state of the
+ ** finite state machine) an action to ACCEPT if the lookahead is the
+ ** start nonterminal. */
+ Action_add(&lemp->sorted[0]->ap,ACCEPT,sp,0);
+
+ /* Resolve conflicts */
+ for(i=0; i<lemp->nstate; i++){
+ struct action *ap, *nap;
+ struct state *stp;
+ stp = lemp->sorted[i];
+ /* assert( stp->ap ); */
+ stp->ap = Action_sort(stp->ap);
+ for(ap=stp->ap; ap && ap->next; ap=ap->next){
+ for(nap=ap->next; nap && nap->sp==ap->sp; nap=nap->next){
+ /* The two actions "ap" and "nap" have the same lookahead.
+ ** Figure out which one should be used */
+ lemp->nconflict += resolve_conflict(ap,nap,lemp->errsym);
+ }
+ }
+ }
+
+ /* Report an error for each rule that can never be reduced. */
+ for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = LEMON_FALSE;
+ for(i=0; i<lemp->nstate; i++){
+ struct action *ap;
+ for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){
+ if( ap->type==REDUCE ) ap->x.rp->canReduce = LEMON_TRUE;
+ }
+ }
+ for(rp=lemp->rule; rp; rp=rp->next){
+ if( rp->canReduce ) continue;
+ ErrorMsg(lemp->filename,rp->ruleline,"This rule can not be reduced.\n");
+ lemp->errorcnt++;
+ }
+}
+
+/* Resolve a conflict between the two given actions. If the
+** conflict can't be resolve, return non-zero.
+**
+** NO LONGER TRUE:
+** To resolve a conflict, first look to see if either action
+** is on an error rule. In that case, take the action which
+** is not associated with the error rule. If neither or both
+** actions are associated with an error rule, then try to
+** use precedence to resolve the conflict.
+**
+** If either action is a SHIFT, then it must be apx. This
+** function won't work if apx->type==REDUCE and apy->type==SHIFT.
+*/
+static int resolve_conflict(apx,apy,errsym)
+struct action *apx;
+struct action *apy;
+struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */
+{
+ struct symbol *spx, *spy;
+ int errcnt = 0;
+ assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */
+ if( apx->type==SHIFT && apy->type==SHIFT ){
+ apy->type = SSCONFLICT;
+ errcnt++;
+ }
+ if( apx->type==SHIFT && apy->type==REDUCE ){
+ spx = apx->sp;
+ spy = apy->x.rp->precsym;
+ if( spy==0 || spx->prec<0 || spy->prec<0 ){
+ /* Not enough precedence information. */
+ apy->type = SRCONFLICT;
+ errcnt++;
+ }else if( spx->prec>spy->prec ){ /* Lower precedence wins */
+ apy->type = RD_RESOLVED;
+ }else if( spx->prec<spy->prec ){
+ apx->type = SH_RESOLVED;
+ }else if( spx->prec==spy->prec && spx->assoc==RIGHT ){ /* Use operator */
+ apy->type = RD_RESOLVED; /* associativity */
+ }else if( spx->prec==spy->prec && spx->assoc==LEFT ){ /* to break tie */
+ apx->type = SH_RESOLVED;
+ }else{
+ assert( spx->prec==spy->prec && spx->assoc==NONE );
+ apy->type = SRCONFLICT;
+ errcnt++;
+ }
+ }else if( apx->type==REDUCE && apy->type==REDUCE ){
+ spx = apx->x.rp->precsym;
+ spy = apy->x.rp->precsym;
+ if( spx==0 || spy==0 || spx->prec<0 ||
+ spy->prec<0 || spx->prec==spy->prec ){
+ apy->type = RRCONFLICT;
+ errcnt++;
+ }else if( spx->prec>spy->prec ){
+ apy->type = RD_RESOLVED;
+ }else if( spx->prec<spy->prec ){
+ apx->type = RD_RESOLVED;
+ }
+ }else{
+ assert(
+ apx->type==SH_RESOLVED ||
+ apx->type==RD_RESOLVED ||
+ apx->type==SSCONFLICT ||
+ apx->type==SRCONFLICT ||
+ apx->type==RRCONFLICT ||
+ apy->type==SH_RESOLVED ||
+ apy->type==RD_RESOLVED ||
+ apy->type==SSCONFLICT ||
+ apy->type==SRCONFLICT ||
+ apy->type==RRCONFLICT
+ );
+ /* The REDUCE/SHIFT case cannot happen because SHIFTs come before
+ ** REDUCEs on the list. If we reach this point it must be because
+ ** the parser conflict had already been resolved. */
+ }
+ return errcnt;
+}
+/********************* From the file "configlist.c" *************************/
+/*
+** Routines to processing a configuration list and building a state
+** in the LEMON parser generator.
+*/
+
+static struct config *freelist = 0; /* List of free configurations */
+static struct config *current = 0; /* Top of list of configurations */
+static struct config **currentend = 0; /* Last on list of configs */
+static struct config *basis = 0; /* Top of list of basis configs */
+static struct config **basisend = 0; /* End of list of basis configs */
+
+/* Return a pointer to a new configuration */
+PRIVATE struct config *newconfig(){
+ struct config *new;
+ if( freelist==0 ){
+ int i;
+ int amt = 3;
+ freelist = (struct config *)calloc( amt, sizeof(struct config) );
+ if( freelist==0 ){
+ fprintf(stderr,"Unable to allocate memory for a new configuration.");
+ exit(1);
+ }
+ for(i=0; i<amt-1; i++) freelist[i].next = &freelist[i+1];
+ freelist[amt-1].next = 0;
+ }
+ new = freelist;
+ freelist = freelist->next;
+ return new;
+}
+
+/* The configuration "old" is no longer used */
+PRIVATE void deleteconfig(old)
+struct config *old;
+{
+ old->next = freelist;
+ freelist = old;
+}
+
+/* Initialized the configuration list builder */
+void Configlist_init(){
+ current = 0;
+ currentend = ¤t;
+ basis = 0;
+ basisend = &basis;
+ Configtable_init();
+ return;
+}
+
+/* Initialized the configuration list builder */
+void Configlist_reset(){
+ current = 0;
+ currentend = ¤t;
+ basis = 0;
+ basisend = &basis;
+ Configtable_clear(0);
+ return;
+}
+
+/* Add another configuration to the configuration list */
+struct config *Configlist_add(rp,dot)
+struct rule *rp; /* The rule */
+int dot; /* Index into the RHS of the rule where the dot goes */
+{
+ struct config *cfp, model;
+
+ assert( currentend!=0 );
+ model.rp = rp;
+ model.dot = dot;
+ cfp = Configtable_find(&model);
+ if( cfp==0 ){
+ cfp = newconfig();
+ cfp->rp = rp;
+ cfp->dot = dot;
+ cfp->fws = SetNew();
+ cfp->stp = 0;
+ cfp->fplp = cfp->bplp = 0;
+ cfp->next = 0;
+ cfp->bp = 0;
+ *currentend = cfp;
+ currentend = &cfp->next;
+ Configtable_insert(cfp);
+ }
+ return cfp;
+}
+
+/* Add a basis configuration to the configuration list */
+struct config *Configlist_addbasis(rp,dot)
+struct rule *rp;
+int dot;
+{
+ struct config *cfp, model;
+
+ assert( basisend!=0 );
+ assert( currentend!=0 );
+ model.rp = rp;
+ model.dot = dot;
+ cfp = Configtable_find(&model);
+ if( cfp==0 ){
+ cfp = newconfig();
+ cfp->rp = rp;
+ cfp->dot = dot;
+ cfp->fws = SetNew();
+ cfp->stp = 0;
+ cfp->fplp = cfp->bplp = 0;
+ cfp->next = 0;
+ cfp->bp = 0;
+ *currentend = cfp;
+ currentend = &cfp->next;
+ *basisend = cfp;
+ basisend = &cfp->bp;
+ Configtable_insert(cfp);
+ }
+ return cfp;
+}
+
+/* Compute the closure of the configuration list */
+void Configlist_closure(lemp)
+struct lemon *lemp;
+{
+ struct config *cfp, *newcfp;
+ struct rule *rp, *newrp;
+ struct symbol *sp, *xsp;
+ int i, dot;
+
+ assert( currentend!=0 );
+ for(cfp=current; cfp; cfp=cfp->next){
+ rp = cfp->rp;
+ dot = cfp->dot;
+ if( dot>=rp->nrhs ) continue;
+ sp = rp->rhs[dot];
+ if( sp->type==NONTERMINAL ){
+ if( sp->rule==0 && sp!=lemp->errsym ){
+ ErrorMsg(lemp->filename,rp->line,"Nonterminal \"%s\" has no rules.",
+ sp->name);
+ lemp->errorcnt++;
+ }
+ for(newrp=sp->rule; newrp; newrp=newrp->nextlhs){
+ newcfp = Configlist_add(newrp,0);
+ for(i=dot+1; i<rp->nrhs; i++){
+ xsp = rp->rhs[i];
+ if( xsp->type==TERMINAL ){
+ SetAdd(newcfp->fws,xsp->index);
+ break;
+ }else if( xsp->type==MULTITERMINAL ){
+ int k;
+ for(k=0; k<xsp->nsubsym; k++){
+ SetAdd(newcfp->fws, xsp->subsym[k]->index);
+ }
+ break;
+ }else{
+ SetUnion(newcfp->fws,xsp->firstset);
+ if( xsp->lambda==LEMON_FALSE ) break;
+ }
+ }
+ if( i==rp->nrhs ) Plink_add(&cfp->fplp,newcfp);
+ }
+ }
+ }
+ return;
+}
+
+/* Sort the configuration list */
+void Configlist_sort(){
+ current = (struct config *)msort((char *)current,(char **)&(current->next),Configcmp);
+ currentend = 0;
+ return;
+}
+
+/* Sort the basis configuration list */
+void Configlist_sortbasis(){
+ basis = (struct config *)msort((char *)current,(char **)&(current->bp),Configcmp);
+ basisend = 0;
+ return;
+}
+
+/* Return a pointer to the head of the configuration list and
+** reset the list */
+struct config *Configlist_return(){
+ struct config *old;
+ old = current;
+ current = 0;
+ currentend = 0;
+ return old;
+}
+
+/* Return a pointer to the head of the configuration list and
+** reset the list */
+struct config *Configlist_basis(){
+ struct config *old;
+ old = basis;
+ basis = 0;
+ basisend = 0;
+ return old;
+}
+
+/* Free all elements of the given configuration list */
+void Configlist_eat(cfp)
+struct config *cfp;
+{
+ struct config *nextcfp;
+ for(; cfp; cfp=nextcfp){
+ nextcfp = cfp->next;
+ assert( cfp->fplp==0 );
+ assert( cfp->bplp==0 );
+ if( cfp->fws ) SetFree(cfp->fws);
+ deleteconfig(cfp);
+ }
+ return;
+}
+/***************** From the file "error.c" *********************************/
+/*
+** Code for printing error message.
+*/
+
+/* Find a good place to break "msg" so that its length is at least "min"
+** but no more than "max". Make the point as close to max as possible.
+*/
+static int findbreak(msg,min,max)
+char *msg;
+int min;
+int max;
+{
+ int i,spot;
+ char c;
+ for(i=spot=min; i<=max; i++){
+ c = msg[i];
+ if( c=='\t' ) msg[i] = ' ';
+ if( c=='\n' ){ msg[i] = ' '; spot = i; break; }
+ if( c==0 ){ spot = i; break; }
+ if( c=='-' && i<max-1 ) spot = i+1;
+ if( c==' ' ) spot = i;
+ }
+ return spot;
+}
+
+/*
+** The error message is split across multiple lines if necessary. The
+** splits occur at a space, if there is a space available near the end
+** of the line.
+*/
+#define ERRMSGSIZE 10000 /* Hope this is big enough. No way to error check */
+#define LINEWIDTH 79 /* Max width of any output line */
+#define PREFIXLIMIT 30 /* Max width of the prefix on each line */
+void ErrorMsg(const char *filename, int lineno, const char *format, ...){
+ char errmsg[ERRMSGSIZE];
+ char prefix[PREFIXLIMIT+10];
+ int errmsgsize;
+ int prefixsize;
+ int availablewidth;
+ va_list ap;
+ int end, restart, base;
+
+ va_start(ap, format);
+ /* Prepare a prefix to be prepended to every output line */
+ if( lineno>0 ){
+ sprintf(prefix,"%.*s:%d: ",PREFIXLIMIT-10,filename,lineno);
+ }else{
+ sprintf(prefix,"%.*s: ",PREFIXLIMIT-10,filename);
+ }
+ prefixsize = strlen(prefix);
+ availablewidth = LINEWIDTH - prefixsize;
+
+ /* Generate the error message */
+ vsprintf(errmsg,format,ap);
+ va_end(ap);
+ errmsgsize = strlen(errmsg);
+ /* Remove trailing '\n's from the error message. */
+ while( errmsgsize>0 && errmsg[errmsgsize-1]=='\n' ){
+ errmsg[--errmsgsize] = 0;
+ }
+
+ /* Print the error message */
+ base = 0;
+ while( errmsg[base]!=0 ){
+ end = restart = findbreak(&errmsg[base],0,availablewidth);
+ restart += base;
+ while( errmsg[restart]==' ' ) restart++;
+ fprintf(stdout,"%s%.*s\n",prefix,end,&errmsg[base]);
+ base = restart;
+ }
+}
+/**************** From the file "main.c" ************************************/
+/*
+** Main program file for the LEMON parser generator.
+*/
+
+/* Report an out-of-memory condition and abort. This function
+** is used mostly by the "MemoryCheck" macro in struct.h
+*/
+void memory_error(){
+ fprintf(stderr,"Out of memory. Aborting...\n");
+ exit(1);
+}
+
+static int nDefine = 0; /* Number of -D options on the command line */
+static char **azDefine = 0; /* Name of the -D macros */
+
+/* This routine is called with the argument to each -D command-line option.
+** Add the macro defined to the azDefine array.
+*/
+static void handle_D_option(char *z){
+ char **paz;
+ nDefine++;
+ azDefine = realloc(azDefine, sizeof(azDefine[0])*nDefine);
+ if( azDefine==0 ){
+ fprintf(stderr,"out of memory\n");
+ exit(1);
+ }
+ paz = &azDefine[nDefine-1];
+ *paz = malloc( strlen(z)+1 );
+ if( *paz==0 ){
+ fprintf(stderr,"out of memory\n");
+ exit(1);
+ }
+ strcpy(*paz, z);
+ for(z=*paz; *z && *z!='='; z++){}
+ *z = 0;
+}
+
+
+/* The main program. Parse the command line and do it... */
+int lemon_main(int argc,char **argv)
+{
+ static int version = 0;
+ static int rpflag = 0;
+ static int basisflag = 0;
+ static int compress = 0;
+ static int quiet = 0;
+ static int statistics = 0;
+ static int mhflag = 0;
+ static struct s_options options[] = {
+ {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."},
+ {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."},
+ {OPT_FSTR, "D", (char*)handle_D_option, "Define an %ifdef macro."},
+ {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."},
+ {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file"},
+ {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."},
+ {OPT_FLAG, "s", (char*)&statistics,
+ "Print parser stats to standard output."},
+ {OPT_FLAG, "x", (char*)&version, "Print the version number."},
+ {OPT_FLAG,0,0,0}
+ };
+ int i;
+ struct lemon lem;
+
+ OptInit(argv,options,stderr);
+ if( version ){
+ printf("Lemon version 1.0\n");
+ exit(0);
+ }
+ if( OptNArgs()!=1 ){
+ fprintf(stderr,"Exactly one filename argument is required.\n");
+ exit(1);
+ }
+ memset(&lem, 0, sizeof(lem));
+ lem.errorcnt = 0;
+
+ /* Initialize the machine */
+ Strsafe_init();
+ Symbol_init();
+ State_init();
+ lem.argv0 = argv[0];
+ lem.filename = OptArg(0);
+ lem.basisflag = basisflag;
+ Symbol_new("$");
+ lem.errsym = Symbol_new("error");
+ lem.errsym->useCnt = 0;
+
+ /* Parse the input file */
+ Parse(&lem);
+ if( lem.errorcnt ) exit(lem.errorcnt);
+ if( lem.nrule==0 ){
+ fprintf(stderr,"Empty grammar.\n");
+ exit(1);
+ }
+
+ /* Count and index the symbols of the grammar */
+ lem.nsymbol = Symbol_count();
+ Symbol_new("{default}");
+ lem.symbols = Symbol_arrayof();
+ for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i;
+ qsort(lem.symbols,lem.nsymbol+1,sizeof(struct symbol*),
+ (int(*)())Symbolcmpp);
+ for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i;
+ for(i=1; isupper(lem.symbols[i]->name[0]); i++);
+ lem.nterminal = i;
+
+ /* Generate a reprint of the grammar, if requested on the command line */
+ if( rpflag ){
+ Reprint(&lem);
+ }else{
+ /* Initialize the size for all follow and first sets */
+ SetSize(lem.nterminal+1);
+
+ /* Find the precedence for every production rule (that has one) */
+ FindRulePrecedences(&lem);
+
+ /* Compute the lambda-nonterminals and the first-sets for every
+ ** nonterminal */
+ FindFirstSets(&lem);
+
+ /* Compute all LR(0) states. Also record follow-set propagation
+ ** links so that the follow-set can be computed later */
+ lem.nstate = 0;
+ FindStates(&lem);
+ lem.sorted = State_arrayof();
+
+ /* Tie up loose ends on the propagation links */
+ FindLinks(&lem);
+
+ /* Compute the follow set of every reducible configuration */
+ FindFollowSets(&lem);
+
+ /* Compute the action tables */
+ FindActions(&lem);
+
+ /* Compress the action tables */
+ if( compress==0 ) CompressTables(&lem);
+
+ /* Reorder and renumber the states so that states with fewer choices
+ ** occur at the end. */
+ ResortStates(&lem);
+
+ /* Generate a report of the parser generated. (the "y.output" file) */
+ if( !quiet ) ReportOutput(&lem);
+
+ /* Generate the source code for the parser */
+ ReportTable(&lem, mhflag);
+
+ /* Produce a header file for use by the scanner. (This step is
+ ** omitted if the "-m" option is used because makeheaders will
+ ** generate the file for us.) */
+ if( !mhflag ) ReportHeader(&lem);
+ }
+ if( statistics ){
+ printf("Parser statistics: %d terminals, %d nonterminals, %d rules\n",
+ lem.nterminal, lem.nsymbol - lem.nterminal, lem.nrule);
+ printf(" %d states, %d parser table entries, %d conflicts\n",
+ lem.nstate, lem.tablesize, lem.nconflict);
+ }
+ if( lem.nconflict ){
+ fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict);
+ }
+ exit(lem.errorcnt + lem.nconflict);
+ return (lem.errorcnt + lem.nconflict);
+}
+/******************** From the file "msort.c" *******************************/
+/*
+** A generic merge-sort program.
+**
+** USAGE:
+** Let "ptr" be a pointer to some structure which is at the head of
+** a null-terminated list. Then to sort the list call:
+**
+** ptr = msort(ptr,&(ptr->next),cmpfnc);
+**
+** In the above, "cmpfnc" is a pointer to a function which compares
+** two instances of the structure and returns an integer, as in
+** strcmp. The second argument is a pointer to the pointer to the
+** second element of the linked list. This address is used to compute
+** the offset to the "next" field within the structure. The offset to
+** the "next" field must be constant for all structures in the list.
+**
+** The function returns a new pointer which is the head of the list
+** after sorting.
+**
+** ALGORITHM:
+** Merge-sort.
+*/
+
+/*
+** Return a pointer to the next structure in the linked list.
+*/
+#define NEXT(A) (*(char**)(((unsigned long)A)+offset))
+
+/*
+** Inputs:
+** a: A sorted, null-terminated linked list. (May be null).
+** b: A sorted, null-terminated linked list. (May be null).
+** cmp: A pointer to the comparison function.
+** offset: Offset in the structure to the "next" field.
+**
+** Return Value:
+** A pointer to the head of a sorted list containing the elements
+** of both a and b.
+**
+** Side effects:
+** The "next" pointers for elements in the lists a and b are
+** changed.
+*/
+static char *merge(
+ char *a,
+ char *b,
+ int (*cmp)(const char*,const char*),
+ int offset
+){
+ char *ptr, *head;
+
+ if( a==0 ){
+ head = b;
+ }else if( b==0 ){
+ head = a;
+ }else{
+ if( (*cmp)(a,b)<0 ){
+ ptr = a;
+ a = NEXT(a);
+ }else{
+ ptr = b;
+ b = NEXT(b);
+ }
+ head = ptr;
+ while( a && b ){
+ if( (*cmp)(a,b)<0 ){
+ NEXT(ptr) = a;
+ ptr = a;
+ a = NEXT(a);
+ }else{
+ NEXT(ptr) = b;
+ ptr = b;
+ b = NEXT(b);
+ }
+ }
+ if( a ) NEXT(ptr) = a;
+ else NEXT(ptr) = b;
+ }
+ return head;
+}
+
+/*
+** Inputs:
+** list: Pointer to a singly-linked list of structures.
+** next: Pointer to pointer to the second element of the list.
+** cmp: A comparison function.
+**
+** Return Value:
+** A pointer to the head of a sorted list containing the elements
+** orginally in list.
+**
+** Side effects:
+** The "next" pointers for elements in list are changed.
+*/
+#define LISTSIZE 30
+static char *msort(
+ char *list,
+ char **next,
+ int (*cmp)(const char*,const char*)
+){
+ unsigned long offset;
+ char *ep;
+ char *set[LISTSIZE];
+ int i;
+ offset = (unsigned long)next - (unsigned long)list;
+ for(i=0; i<LISTSIZE; i++) set[i] = 0;
+ while( list ){
+ ep = list;
+ list = NEXT(list);
+ NEXT(ep) = 0;
+ for(i=0; i<LISTSIZE-1 && set[i]!=0; i++){
+ ep = merge(ep,set[i],cmp,offset);
+ set[i] = 0;
+ }
+ set[i] = ep;
+ }
+ ep = 0;
+ for(i=0; i<LISTSIZE; i++) if( set[i] ) ep = merge(ep,set[i],cmp,offset);
+ return ep;
+}
+/************************ From the file "option.c" **************************/
+static char **argv;
+static struct s_options *op;
+static FILE *errstream;
+
+#define ISOPT(X) ((X)[0]=='-'||(X)[0]=='+'||strchr((X),'=')!=0)
+
+/*
+** Print the command line with a carrot pointing to the k-th character
+** of the n-th field.
+*/
+static void errline(n,k,err)
+int n;
+int k;
+FILE *err;
+{
+ int spcnt, i;
+ if( argv[0] ) fprintf(err,"%s",argv[0]);
+ spcnt = strlen(argv[0]) + 1;
+ for(i=1; i<n && argv[i]; i++){
+ fprintf(err," %s",argv[i]);
+ spcnt += strlen(argv[i])+1;
+ }
+ spcnt += k;
+ for(; argv[i]; i++) fprintf(err," %s",argv[i]);
+ if( spcnt<20 ){
+ fprintf(err,"\n%*s^-- here\n",spcnt,"");
+ }else{
+ fprintf(err,"\n%*shere --^\n",spcnt-7,"");
+ }
+}
+
+/*
+** Return the index of the N-th non-switch argument. Return -1
+** if N is out of range.
+*/
+static int argindex(n)
+int n;
+{
+ int i;
+ int dashdash = 0;
+ if( argv!=0 && *argv!=0 ){
+ for(i=1; argv[i]; i++){
+ if( dashdash || !ISOPT(argv[i]) ){
+ if( n==0 ) return i;
+ n--;
+ }
+ if( strcmp(argv[i],"--")==0 ) dashdash = 1;
+ }
+ }
+ return -1;
+}
+
+static char emsg[] = "Command line syntax error: ";
+
+/*
+** Process a flag command line argument.
+*/
+static int handleflags(i,err)
+int i;
+FILE *err;
+{
+ int v;
+ int errcnt = 0;
+ int j;
+ for(j=0; op[j].label; j++){
+ if( strncmp(&argv[i][1],op[j].label,strlen(op[j].label))==0 ) break;
+ }
+ v = argv[i][0]=='-' ? 1 : 0;
+ if( op[j].label==0 ){
+ if( err ){
+ fprintf(err,"%sundefined option.\n",emsg);
+ errline(i,1,err);
+ }
+ errcnt++;
+ }else if( op[j].type==OPT_FLAG ){
+ *((int*)op[j].arg) = v;
+ }else if( op[j].type==OPT_FFLAG ){
+ (*(void(*)())(op[j].arg))(v);
+ }else if( op[j].type==OPT_FSTR ){
+ (*(void(*)())(op[j].arg))(&argv[i][2]);
+ }else{
+ if( err ){
+ fprintf(err,"%smissing argument on switch.\n",emsg);
+ errline(i,1,err);
+ }
+ errcnt++;
+ }
+ return errcnt;
+}
+
+/*
+** Process a command line switch which has an argument.
+*/
+static int handleswitch(i,err)
+int i;
+FILE *err;
+{
+ int lv = 0;
+ double dv = 0.0;
+ char *sv = 0, *end;
+ char *cp;
+ int j;
+ int errcnt = 0;
+ cp = strchr(argv[i],'=');
+ assert( cp!=0 );
+ *cp = 0;
+ for(j=0; op[j].label; j++){
+ if( strcmp(argv[i],op[j].label)==0 ) break;
+ }
+ *cp = '=';
+ if( op[j].label==0 ){
+ if( err ){
+ fprintf(err,"%sundefined option.\n",emsg);
+ errline(i,0,err);
+ }
+ errcnt++;
+ }else{
+ cp++;
+ switch( op[j].type ){
+ case OPT_FLAG:
+ case OPT_FFLAG:
+ if( err ){
+ fprintf(err,"%soption requires an argument.\n",emsg);
+ errline(i,0,err);
+ }
+ errcnt++;
+ break;
+ case OPT_DBL:
+ case OPT_FDBL:
+ dv = strtod(cp,&end);
+ if( *end ){
+ if( err ){
+ fprintf(err,"%sillegal character in floating-point argument.\n",emsg);
+ errline(i,((unsigned long)end)-(unsigned long)argv[i],err);
+ }
+ errcnt++;
+ }
+ break;
+ case OPT_INT:
+ case OPT_FINT:
+ lv = strtol(cp,&end,0);
+ if( *end ){
+ if( err ){
+ fprintf(err,"%sillegal character in integer argument.\n",emsg);
+ errline(i,((unsigned long)end)-(unsigned long)argv[i],err);
+ }
+ errcnt++;
+ }
+ break;
+ case OPT_STR:
+ case OPT_FSTR:
+ sv = cp;
+ break;
+ }
+ switch( op[j].type ){
+ case OPT_FLAG:
+ case OPT_FFLAG:
+ break;
+ case OPT_DBL:
+ *(double*)(op[j].arg) = dv;
+ break;
+ case OPT_FDBL:
+ (*(void(*)())(op[j].arg))(dv);
+ break;
+ case OPT_INT:
+ *(int*)(op[j].arg) = lv;
+ break;
+ case OPT_FINT:
+ (*(void(*)())(op[j].arg))((int)lv);
+ break;
+ case OPT_STR:
+ *(char**)(op[j].arg) = sv;
+ break;
+ case OPT_FSTR:
+ (*(void(*)())(op[j].arg))(sv);
+ break;
+ }
+ }
+ return errcnt;
+}
+
+int OptInit(a,o,err)
+char **a;
+struct s_options *o;
+FILE *err;
+{
+ int errcnt = 0;
+ argv = a;
+ op = o;
+ errstream = err;
+ if( argv && *argv && op ){
+ int i;
+ for(i=1; argv[i]; i++){
+ if( argv[i][0]=='+' || argv[i][0]=='-' ){
+ errcnt += handleflags(i,err);
+ }else if( strchr(argv[i],'=') ){
+ errcnt += handleswitch(i,err);
+ }
+ }
+ }
+ if( errcnt>0 ){
+ fprintf(err,"Valid command line options for \"%s\" are:\n",*a);
+ OptPrint();
+ exit(1);
+ }
+ return 0;
+}
+
+int OptNArgs(){
+ int cnt = 0;
+ int dashdash = 0;
+ int i;
+ if( argv!=0 && argv[0]!=0 ){
+ for(i=1; argv[i]; i++){
+ if( dashdash || !ISOPT(argv[i]) ) cnt++;
+ if( strcmp(argv[i],"--")==0 ) dashdash = 1;
+ }
+ }
+ return cnt;
+}
+
+char *OptArg(n)
+int n;
+{
+ int i;
+ i = argindex(n);
+ return i>=0 ? argv[i] : 0;
+}
+
+void OptErr(n)
+int n;
+{
+ int i;
+ i = argindex(n);
+ if( i>=0 ) errline(i,0,errstream);
+}
+
+void OptPrint(){
+ int i;
+ int max, len;
+ max = 0;
+ for(i=0; op[i].label; i++){
+ len = strlen(op[i].label) + 1;
+ switch( op[i].type ){
+ case OPT_FLAG:
+ case OPT_FFLAG:
+ break;
+ case OPT_INT:
+ case OPT_FINT:
+ len += 9; /* length of "<integer>" */
+ break;
+ case OPT_DBL:
+ case OPT_FDBL:
+ len += 6; /* length of "<real>" */
+ break;
+ case OPT_STR:
+ case OPT_FSTR:
+ len += 8; /* length of "<string>" */
+ break;
+ }
+ if( len>max ) max = len;
+ }
+ for(i=0; op[i].label; i++){
+ switch( op[i].type ){
+ case OPT_FLAG:
+ case OPT_FFLAG:
+ fprintf(errstream," -%-*s %s\n",max,op[i].label,op[i].message);
+ break;
+ case OPT_INT:
+ case OPT_FINT:
+ fprintf(errstream," %s=<integer>%*s %s\n",op[i].label,
+ (int)(max-strlen(op[i].label)-9),"",op[i].message);
+ break;
+ case OPT_DBL:
+ case OPT_FDBL:
+ fprintf(errstream," %s=<real>%*s %s\n",op[i].label,
+ (int)(max-strlen(op[i].label)-6),"",op[i].message);
+ break;
+ case OPT_STR:
+ case OPT_FSTR:
+ fprintf(errstream," %s=<string>%*s %s\n",op[i].label,
+ (int)(max-strlen(op[i].label)-8),"",op[i].message);
+ break;
+ }
+ }
+}
+/*********************** From the file "parse.c" ****************************/
+/*
+** Input file parser for the LEMON parser generator.
+*/
+
+/* The state of the parser */
+struct pstate {
+ char *filename; /* Name of the input file */
+ int tokenlineno; /* Linenumber at which current token starts */
+ int errorcnt; /* Number of errors so far */
+ char *tokenstart; /* Text of current token */
+ struct lemon *gp; /* Global state vector */
+ enum e_state {
+ INITIALIZE,
+ WAITING_FOR_DECL_OR_RULE,
+ WAITING_FOR_DECL_KEYWORD,
+ WAITING_FOR_DECL_ARG,
+ WAITING_FOR_PRECEDENCE_SYMBOL,
+ WAITING_FOR_ARROW,
+ IN_RHS,
+ LHS_ALIAS_1,
+ LHS_ALIAS_2,
+ LHS_ALIAS_3,
+ RHS_ALIAS_1,
+ RHS_ALIAS_2,
+ PRECEDENCE_MARK_1,
+ PRECEDENCE_MARK_2,
+ RESYNC_AFTER_RULE_ERROR,
+ RESYNC_AFTER_DECL_ERROR,
+ WAITING_FOR_DESTRUCTOR_SYMBOL,
+ WAITING_FOR_DATATYPE_SYMBOL,
+ WAITING_FOR_FALLBACK_ID,
+ WAITING_FOR_WILDCARD_ID
+ } state; /* The state of the parser */
+ struct symbol *fallback; /* The fallback token */
+ struct symbol *lhs; /* Left-hand side of current rule */
+ char *lhsalias; /* Alias for the LHS */
+ int nrhs; /* Number of right-hand side symbols seen */
+ struct symbol *rhs[MAXRHS]; /* RHS symbols */
+ char *alias[MAXRHS]; /* Aliases for each RHS symbol (or NULL) */
+ struct rule *prevrule; /* Previous rule parsed */
+ char *declkeyword; /* Keyword of a declaration */
+ char **declargslot; /* Where the declaration argument should be put */
+ int *decllnslot; /* Where the declaration linenumber is put */
+ enum e_assoc declassoc; /* Assign this association to decl arguments */
+ int preccounter; /* Assign this precedence to decl arguments */
+ struct rule *firstrule; /* Pointer to first rule in the grammar */
+ struct rule *lastrule; /* Pointer to the most recently parsed rule */
+};
+
+/* Parse a single token */
+static void parseonetoken(psp)
+struct pstate *psp;
+{
+ char *x;
+ x = Strsafe(psp->tokenstart); /* Save the token permanently */
+#if 0
+ printf("%s:%d: Token=[%s] state=%d\n",psp->filename,psp->tokenlineno,
+ x,psp->state);
+#endif
+ switch( psp->state ){
+ case INITIALIZE:
+ psp->prevrule = 0;
+ psp->preccounter = 0;
+ psp->firstrule = psp->lastrule = 0;
+ psp->gp->nrule = 0;
+ /* Fall thru to next case */
+ case WAITING_FOR_DECL_OR_RULE:
+ if( x[0]=='%' ){
+ psp->state = WAITING_FOR_DECL_KEYWORD;
+ }else if( islower(x[0]) ){
+ psp->lhs = Symbol_new(x);
+ psp->nrhs = 0;
+ psp->lhsalias = 0;
+ psp->state = WAITING_FOR_ARROW;
+ }else if( x[0]=='{' ){
+ if( psp->prevrule==0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+"There is not prior rule opon which to attach the code \
+fragment which begins on this line.");
+ psp->errorcnt++;
+ }else if( psp->prevrule->code!=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+"Code fragment beginning on this line is not the first \
+to follow the previous rule.");
+ psp->errorcnt++;
+ }else{
+ psp->prevrule->line = psp->tokenlineno;
+ psp->prevrule->code = &x[1];
+ }
+ }else if( x[0]=='[' ){
+ psp->state = PRECEDENCE_MARK_1;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Token \"%s\" should be either \"%%\" or a nonterminal name.",
+ x);
+ psp->errorcnt++;
+ }
+ break;
+ case PRECEDENCE_MARK_1:
+ if( !isupper(x[0]) ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "The precedence symbol must be a terminal.");
+ psp->errorcnt++;
+ }else if( psp->prevrule==0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "There is no prior rule to assign precedence \"[%s]\".",x);
+ psp->errorcnt++;
+ }else if( psp->prevrule->precsym!=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+"Precedence mark on this line is not the first \
+to follow the previous rule.");
+ psp->errorcnt++;
+ }else{
+ psp->prevrule->precsym = Symbol_new(x);
+ }
+ psp->state = PRECEDENCE_MARK_2;
+ break;
+ case PRECEDENCE_MARK_2:
+ if( x[0]!=']' ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \"]\" on precedence mark.");
+ psp->errorcnt++;
+ }
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ break;
+ case WAITING_FOR_ARROW:
+ if( x[0]==':' && x[1]==':' && x[2]=='=' ){
+ psp->state = IN_RHS;
+ }else if( x[0]=='(' ){
+ psp->state = LHS_ALIAS_1;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Expected to see a \":\" following the LHS symbol \"%s\".",
+ psp->lhs->name);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case LHS_ALIAS_1:
+ if( isalpha(x[0]) ){
+ psp->lhsalias = x;
+ psp->state = LHS_ALIAS_2;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "\"%s\" is not a valid alias for the LHS \"%s\"\n",
+ x,psp->lhs->name);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case LHS_ALIAS_2:
+ if( x[0]==')' ){
+ psp->state = LHS_ALIAS_3;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case LHS_ALIAS_3:
+ if( x[0]==':' && x[1]==':' && x[2]=='=' ){
+ psp->state = IN_RHS;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \"->\" following: \"%s(%s)\".",
+ psp->lhs->name,psp->lhsalias);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case IN_RHS:
+ if( x[0]=='.' ){
+ struct rule *rp;
+ rp = (struct rule *)calloc( sizeof(struct rule) +
+ sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs, 1);
+ if( rp==0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Can't allocate enough memory for this rule.");
+ psp->errorcnt++;
+ psp->prevrule = 0;
+ }else{
+ int i;
+ rp->ruleline = psp->tokenlineno;
+ rp->rhs = (struct symbol**)&rp[1];
+ rp->rhsalias = (char**)&(rp->rhs[psp->nrhs]);
+ for(i=0; i<psp->nrhs; i++){
+ rp->rhs[i] = psp->rhs[i];
+ rp->rhsalias[i] = psp->alias[i];
+ }
+ rp->lhs = psp->lhs;
+ rp->lhsalias = psp->lhsalias;
+ rp->nrhs = psp->nrhs;
+ rp->code = 0;
+ rp->precsym = 0;
+ rp->index = psp->gp->nrule++;
+ rp->nextlhs = rp->lhs->rule;
+ rp->lhs->rule = rp;
+ rp->next = 0;
+ if( psp->firstrule==0 ){
+ psp->firstrule = psp->lastrule = rp;
+ }else{
+ psp->lastrule->next = rp;
+ psp->lastrule = rp;
+ }
+ psp->prevrule = rp;
+ }
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else if( isalpha(x[0]) ){
+ if( psp->nrhs>=MAXRHS ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Too many symbols on RHS of rule beginning at \"%s\".",
+ x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }else{
+ psp->rhs[psp->nrhs] = Symbol_new(x);
+ psp->alias[psp->nrhs] = 0;
+ psp->nrhs++;
+ }
+ }else if( (x[0]=='|' || x[0]=='/') && psp->nrhs>0 ){
+ struct symbol *msp = psp->rhs[psp->nrhs-1];
+ if( msp->type!=MULTITERMINAL ){
+ struct symbol *origsp = msp;
+ msp = calloc(1,sizeof(*msp));
+ memset(msp, 0, sizeof(*msp));
+ msp->type = MULTITERMINAL;
+ msp->nsubsym = 1;
+ msp->subsym = calloc(1,sizeof(struct symbol*));
+ msp->subsym[0] = origsp;
+ msp->name = origsp->name;
+ psp->rhs[psp->nrhs-1] = msp;
+ }
+ msp->nsubsym++;
+ msp->subsym = realloc(msp->subsym, sizeof(struct symbol*)*msp->nsubsym);
+ msp->subsym[msp->nsubsym-1] = Symbol_new(&x[1]);
+ if( islower(x[1]) || islower(msp->subsym[0]->name[0]) ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Cannot form a compound containing a non-terminal");
+ psp->errorcnt++;
+ }
+ }else if( x[0]=='(' && psp->nrhs>0 ){
+ psp->state = RHS_ALIAS_1;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Illegal character on RHS of rule: \"%s\".",x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case RHS_ALIAS_1:
+ if( isalpha(x[0]) ){
+ psp->alias[psp->nrhs-1] = x;
+ psp->state = RHS_ALIAS_2;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n",
+ x,psp->rhs[psp->nrhs-1]->name);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case RHS_ALIAS_2:
+ if( x[0]==')' ){
+ psp->state = IN_RHS;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case WAITING_FOR_DECL_KEYWORD:
+ if( isalpha(x[0]) ){
+ psp->declkeyword = x;
+ psp->declargslot = 0;
+ psp->decllnslot = 0;
+ psp->state = WAITING_FOR_DECL_ARG;
+ if( strcmp(x,"name")==0 ){
+ psp->declargslot = &(psp->gp->name);
+ }else if( strcmp(x,"include")==0 ){
+ psp->declargslot = &(psp->gp->include);
+ psp->decllnslot = &psp->gp->includeln;
+ }else if( strcmp(x,"code")==0 ){
+ psp->declargslot = &(psp->gp->extracode);
+ psp->decllnslot = &psp->gp->extracodeln;
+ }else if( strcmp(x,"token_destructor")==0 ){
+ psp->declargslot = &psp->gp->tokendest;
+ psp->decllnslot = &psp->gp->tokendestln;
+ }else if( strcmp(x,"default_destructor")==0 ){
+ psp->declargslot = &psp->gp->vardest;
+ psp->decllnslot = &psp->gp->vardestln;
+ }else if( strcmp(x,"token_prefix")==0 ){
+ psp->declargslot = &psp->gp->tokenprefix;
+ }else if( strcmp(x,"syntax_error")==0 ){
+ psp->declargslot = &(psp->gp->error);
+ psp->decllnslot = &psp->gp->errorln;
+ }else if( strcmp(x,"parse_accept")==0 ){
+ psp->declargslot = &(psp->gp->accept);
+ psp->decllnslot = &psp->gp->acceptln;
+ }else if( strcmp(x,"parse_failure")==0 ){
+ psp->declargslot = &(psp->gp->failure);
+ psp->decllnslot = &psp->gp->failureln;
+ }else if( strcmp(x,"stack_overflow")==0 ){
+ psp->declargslot = &(psp->gp->overflow);
+ psp->decllnslot = &psp->gp->overflowln;
+ }else if( strcmp(x,"extra_argument")==0 ){
+ psp->declargslot = &(psp->gp->arg);
+ }else if( strcmp(x,"token_type")==0 ){
+ psp->declargslot = &(psp->gp->tokentype);
+ }else if( strcmp(x,"default_type")==0 ){
+ psp->declargslot = &(psp->gp->vartype);
+ }else if( strcmp(x,"stack_size")==0 ){
+ psp->declargslot = &(psp->gp->stacksize);
+ }else if( strcmp(x,"start_symbol")==0 ){
+ psp->declargslot = &(psp->gp->start);
+ }else if( strcmp(x,"left")==0 ){
+ psp->preccounter++;
+ psp->declassoc = LEFT;
+ psp->state = WAITING_FOR_PRECEDENCE_SYMBOL;
+ }else if( strcmp(x,"right")==0 ){
+ psp->preccounter++;
+ psp->declassoc = RIGHT;
+ psp->state = WAITING_FOR_PRECEDENCE_SYMBOL;
+ }else if( strcmp(x,"nonassoc")==0 ){
+ psp->preccounter++;
+ psp->declassoc = NONE;
+ psp->state = WAITING_FOR_PRECEDENCE_SYMBOL;
+ }else if( strcmp(x,"destructor")==0 ){
+ psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL;
+ }else if( strcmp(x,"type")==0 ){
+ psp->state = WAITING_FOR_DATATYPE_SYMBOL;
+ }else if( strcmp(x,"fallback")==0 ){
+ psp->fallback = 0;
+ psp->state = WAITING_FOR_FALLBACK_ID;
+ }else if( strcmp(x,"wildcard")==0 ){
+ psp->state = WAITING_FOR_WILDCARD_ID;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Unknown declaration keyword: \"%%%s\".",x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Illegal declaration keyword: \"%s\".",x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }
+ break;
+ case WAITING_FOR_DESTRUCTOR_SYMBOL:
+ if( !isalpha(x[0]) ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Symbol name missing after %destructor keyword");
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else{
+ struct symbol *sp = Symbol_new(x);
+ psp->declargslot = &sp->destructor;
+ psp->decllnslot = &sp->destructorln;
+ psp->state = WAITING_FOR_DECL_ARG;
+ }
+ break;
+ case WAITING_FOR_DATATYPE_SYMBOL:
+ if( !isalpha(x[0]) ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Symbol name missing after %destructor keyword");
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else{
+ struct symbol *sp = Symbol_new(x);
+ psp->declargslot = &sp->datatype;
+ psp->decllnslot = 0;
+ psp->state = WAITING_FOR_DECL_ARG;
+ }
+ break;
+ case WAITING_FOR_PRECEDENCE_SYMBOL:
+ if( x[0]=='.' ){
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else if( isupper(x[0]) ){
+ struct symbol *sp;
+ sp = Symbol_new(x);
+ if( sp->prec>=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Symbol \"%s\" has already be given a precedence.",x);
+ psp->errorcnt++;
+ }else{
+ sp->prec = psp->preccounter;
+ sp->assoc = psp->declassoc;
+ }
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Can't assign a precedence to \"%s\".",x);
+ psp->errorcnt++;
+ }
+ break;
+ case WAITING_FOR_DECL_ARG:
+ if( (x[0]=='{' || x[0]=='\"' || isalnum(x[0])) ){
+ if( *(psp->declargslot)!=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "The argument \"%s\" to declaration \"%%%s\" is not the first.",
+ x[0]=='\"' ? &x[1] : x,psp->declkeyword);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else{
+ *(psp->declargslot) = (x[0]=='\"' || x[0]=='{') ? &x[1] : x;
+ if( psp->decllnslot ) *psp->decllnslot = psp->tokenlineno;
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Illegal argument to %%%s: %s",psp->declkeyword,x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }
+ break;
+ case WAITING_FOR_FALLBACK_ID:
+ if( x[0]=='.' ){
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else if( !isupper(x[0]) ){
+ ErrorMsg(psp->filename, psp->tokenlineno,
+ "%%fallback argument \"%s\" should be a token", x);
+ psp->errorcnt++;
+ }else{
+ struct symbol *sp = Symbol_new(x);
+ if( psp->fallback==0 ){
+ psp->fallback = sp;
+ }else if( sp->fallback ){
+ ErrorMsg(psp->filename, psp->tokenlineno,
+ "More than one fallback assigned to token %s", x);
+ psp->errorcnt++;
+ }else{
+ sp->fallback = psp->fallback;
+ psp->gp->has_fallback = 1;
+ }
+ }
+ break;
+ case WAITING_FOR_WILDCARD_ID:
+ if( x[0]=='.' ){
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else if( !isupper(x[0]) ){
+ ErrorMsg(psp->filename, psp->tokenlineno,
+ "%%wildcard argument \"%s\" should be a token", x);
+ psp->errorcnt++;
+ }else{
+ struct symbol *sp = Symbol_new(x);
+ if( psp->gp->wildcard==0 ){
+ psp->gp->wildcard = sp;
+ }else{
+ ErrorMsg(psp->filename, psp->tokenlineno,
+ "Extra wildcard to token: %s", x);
+ psp->errorcnt++;
+ }
+ }
+ break;
+ case RESYNC_AFTER_RULE_ERROR:
+/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE;
+** break; */
+ case RESYNC_AFTER_DECL_ERROR:
+ if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE;
+ if( x[0]=='%' ) psp->state = WAITING_FOR_DECL_KEYWORD;
+ break;
+ }
+}
+
+/* Run the proprocessor over the input file text. The global variables
+** azDefine[0] through azDefine[nDefine-1] contains the names of all defined
+** macros. This routine looks for "%ifdef" and "%ifndef" and "%endif" and
+** comments them out. Text in between is also commented out as appropriate.
+*/
+static void preprocess_input(char *z){
+ int i, j, k, n;
+ int exclude = 0;
+ int start = 0;
+ int lineno = 1;
+ int start_lineno = 1;
+ for(i=0; z[i]; i++){
+ if( z[i]=='\n' ) lineno++;
+ if( z[i]!='%' || (i>0 && z[i-1]!='\n') ) continue;
+ if( strncmp(&z[i],"%endif",6)==0 && isspace(z[i+6]) ){
+ if( exclude ){
+ exclude--;
+ if( exclude==0 ){
+ for(j=start; j<i; j++) if( z[j]!='\n' ) z[j] = ' ';
+ }
+ }
+ for(j=i; z[j] && z[j]!='\n'; j++) z[j] = ' ';
+ }else if( (strncmp(&z[i],"%ifdef",6)==0 && isspace(z[i+6]))
+ || (strncmp(&z[i],"%ifndef",7)==0 && isspace(z[i+7])) ){
+ if( exclude ){
+ exclude++;
+ }else{
+ for(j=i+7; isspace(z[j]); j++){}
+ for(n=0; z[j+n] && !isspace(z[j+n]); n++){}
+ exclude = 1;
+ for(k=0; k<nDefine; k++){
+ if( strncmp(azDefine[k],&z[j],n)==0 && strlen(azDefine[k])==n ){
+ exclude = 0;
+ break;
+ }
+ }
+ if( z[i+3]=='n' ) exclude = !exclude;
+ if( exclude ){
+ start = i;
+ start_lineno = lineno;
+ }
+ }
+ for(j=i; z[j] && z[j]!='\n'; j++) z[j] = ' ';
+ }
+ }
+ if( exclude ){
+ fprintf(stderr,"unterminated %%ifdef starting on line %d\n", start_lineno);
+ exit(1);
+ }
+}
+
+/* In spite of its name, this function is really a scanner. It read
+** in the entire input file (all at once) then tokenizes it. Each
+** token is passed to the function "parseonetoken" which builds all
+** the appropriate data structures in the global state vector "gp".
+*/
+void Parse(gp)
+struct lemon *gp;
+{
+ struct pstate ps;
+ FILE *fp;
+ char *filebuf;
+ int filesize;
+ int lineno;
+ int c;
+ char *cp, *nextcp;
+ int startline = 0;
+
+ memset(&ps, '\0', sizeof(ps));
+ ps.gp = gp;
+ ps.filename = gp->filename;
+ ps.errorcnt = 0;
+ ps.state = INITIALIZE;
+
+ /* Begin by reading the input file */
+ fp = fopen(ps.filename,"rb");
+ if( fp==0 ){
+ ErrorMsg(ps.filename,0,"Can't open this file for reading.");
+ gp->errorcnt++;
+ return;
+ }
+ fseek(fp,0,2);
+ filesize = ftell(fp);
+ rewind(fp);
+ filebuf = (char *)malloc( filesize+1 );
+ if( filebuf==0 ){
+ ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.",
+ filesize+1);
+ gp->errorcnt++;
+ return;
+ }
+ if( fread(filebuf,1,filesize,fp)!=filesize ){
+ ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.",
+ filesize);
+ free(filebuf);
+ gp->errorcnt++;
+ return;
+ }
+ fclose(fp);
+ filebuf[filesize] = 0;
+
+ /* Make an initial pass through the file to handle %ifdef and %ifndef */
+ preprocess_input(filebuf);
+
+ /* Now scan the text of the input file */
+ lineno = 1;
+ for(cp=filebuf; (c= *cp)!=0; ){
+ if( c=='\n' ) lineno++; /* Keep track of the line number */
+ if( isspace(c) ){ cp++; continue; } /* Skip all white space */
+ if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments */
+ cp+=2;
+ while( (c= *cp)!=0 && c!='\n' ) cp++;
+ continue;
+ }
+ if( c=='/' && cp[1]=='*' ){ /* Skip C style comments */
+ cp+=2;
+ while( (c= *cp)!=0 && (c!='/' || cp[-1]!='*') ){
+ if( c=='\n' ) lineno++;
+ cp++;
+ }
+ if( c ) cp++;
+ continue;
+ }
+ ps.tokenstart = cp; /* Mark the beginning of the token */
+ ps.tokenlineno = lineno; /* Linenumber on which token begins */
+ if( c=='\"' ){ /* String literals */
+ cp++;
+ while( (c= *cp)!=0 && c!='\"' ){
+ if( c=='\n' ) lineno++;
+ cp++;
+ }
+ if( c==0 ){
+ ErrorMsg(ps.filename,startline,
+"String starting on this line is not terminated before the end of the file.");
+ ps.errorcnt++;
+ nextcp = cp;
+ }else{
+ nextcp = cp+1;
+ }
+ }else if( c=='{' ){ /* A block of C code */
+ int level;
+ cp++;
+ for(level=1; (c= *cp)!=0 && (level>1 || c!='}'); cp++){
+ if( c=='\n' ) lineno++;
+ else if( c=='{' ) level++;
+ else if( c=='}' ) level--;
+ else if( c=='/' && cp[1]=='*' ){ /* Skip comments */
+ int prevc;
+ cp = &cp[2];
+ prevc = 0;
+ while( (c= *cp)!=0 && (c!='/' || prevc!='*') ){
+ if( c=='\n' ) lineno++;
+ prevc = c;
+ cp++;
+ }
+ }else if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments too */
+ cp = &cp[2];
+ while( (c= *cp)!=0 && c!='\n' ) cp++;
+ if( c ) lineno++;
+ }else if( c=='\'' || c=='\"' ){ /* String a character literals */
+ int startchar, prevc;
+ startchar = c;
+ prevc = 0;
+ for(cp++; (c= *cp)!=0 && (c!=startchar || prevc=='\\'); cp++){
+ if( c=='\n' ) lineno++;
+ if( prevc=='\\' ) prevc = 0;
+ else prevc = c;
+ }
+ }
+ }
+ if( c==0 ){
+ ErrorMsg(ps.filename,ps.tokenlineno,
+"C code starting on this line is not terminated before the end of the file.");
+ ps.errorcnt++;
+ nextcp = cp;
+ }else{
+ nextcp = cp+1;
+ }
+ }else if( isalnum(c) ){ /* Identifiers */
+ while( (c= *cp)!=0 && (isalnum(c) || c=='_') ) cp++;
+ nextcp = cp;
+ }else if( c==':' && cp[1]==':' && cp[2]=='=' ){ /* The operator "::=" */
+ cp += 3;
+ nextcp = cp;
+ }else if( (c=='/' || c=='|') && isalpha(cp[1]) ){
+ cp += 2;
+ while( (c = *cp)!=0 && (isalnum(c) || c=='_') ) cp++;
+ nextcp = cp;
+ }else{ /* All other (one character) operators */
+ cp++;
+ nextcp = cp;
+ }
+ c = *cp;
+ *cp = 0; /* Null terminate the token */
+ parseonetoken(&ps); /* Parse the token */
+ *cp = c; /* Restore the buffer */
+ cp = nextcp;
+ }
+ free(filebuf); /* Release the buffer after parsing */
+ gp->rule = ps.firstrule;
+ gp->errorcnt = ps.errorcnt;
+}
+/*************************** From the file "plink.c" *********************/
+/*
+** Routines processing configuration follow-set propagation links
+** in the LEMON parser generator.
+*/
+static struct plink *plink_freelist = 0;
+
+/* Allocate a new plink */
+struct plink *Plink_new(){
+ struct plink *new;
+
+ if( plink_freelist==0 ){
+ int i;
+ int amt = 100;
+ plink_freelist = (struct plink *)calloc( amt, sizeof(struct plink) );
+ if( plink_freelist==0 ){
+ fprintf(stderr,
+ "Unable to allocate memory for a new follow-set propagation link.\n");
+ exit(1);
+ }
+ for(i=0; i<amt-1; i++) plink_freelist[i].next = &plink_freelist[i+1];
+ plink_freelist[amt-1].next = 0;
+ }
+ new = plink_freelist;
+ plink_freelist = plink_freelist->next;
+ return new;
+}
+
+/* Add a plink to a plink list */
+void Plink_add(plpp,cfp)
+struct plink **plpp;
+struct config *cfp;
+{
+ struct plink *new;
+ new = Plink_new();
+ new->next = *plpp;
+ *plpp = new;
+ new->cfp = cfp;
+}
+
+/* Transfer every plink on the list "from" to the list "to" */
+void Plink_copy(to,from)
+struct plink **to;
+struct plink *from;
+{
+ struct plink *nextpl;
+ while( from ){
+ nextpl = from->next;
+ from->next = *to;
+ *to = from;
+ from = nextpl;
+ }
+}
+
+/* Delete every plink on the list */
+void Plink_delete(plp)
+struct plink *plp;
+{
+ struct plink *nextpl;
+
+ while( plp ){
+ nextpl = plp->next;
+ plp->next = plink_freelist;
+ plink_freelist = plp;
+ plp = nextpl;
+ }
+}
+/*********************** From the file "report.c" **************************/
+/*
+** Procedures for generating reports and tables in the LEMON parser generator.
+*/
+
+/* Generate a filename with the given suffix. Space to hold the
+** name comes from malloc() and must be freed by the calling
+** function.
+*/
+PRIVATE char *file_makename(lemp,suffix)
+struct lemon *lemp;
+char *suffix;
+{
+ char *name;
+ char *cp;
+
+ name = malloc( strlen(lemp->filename) + strlen(suffix) + 5 );
+ if( name==0 ){
+ fprintf(stderr,"Can't allocate space for a filename.\n");
+ exit(1);
+ }
+ strcpy(name,lemp->filename);
+ cp = strrchr(name,'.');
+ if( cp ) *cp = 0;
+ strcat(name,suffix);
+ return name;
+}
+
+/* Open a file with a name based on the name of the input file,
+** but with a different (specified) suffix, and return a pointer
+** to the stream */
+PRIVATE FILE *file_open(lemp,suffix,mode)
+struct lemon *lemp;
+char *suffix;
+char *mode;
+{
+ FILE *fp;
+
+ if( lemp->outname ) free(lemp->outname);
+ lemp->outname = file_makename(lemp, suffix);
+ /* LLVM LOCAL begin */
+#if 0
+ fp = fopen(lemp->outname,mode);
+#else
+ if(*mode == 'r') return NULL;
+ fp = stdout;
+#endif
+ /* LLVM LOCAL end */
+ if( fp==0 && *mode=='w' ){
+ fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname);
+ lemp->errorcnt++;
+ return 0;
+ }
+ return fp;
+}
+
+/* Duplicate the input file without comments and without actions
+** on rules */
+void Reprint(lemp)
+struct lemon *lemp;
+{
+ struct rule *rp;
+ struct symbol *sp;
+ int i, j, maxlen, len, ncolumns, skip;
+ printf("// Reprint of input file \"%s\".\n// Symbols:\n",lemp->filename);
+ maxlen = 10;
+ for(i=0; i<lemp->nsymbol; i++){
+ sp = lemp->symbols[i];
+ len = strlen(sp->name);
+ if( len>maxlen ) maxlen = len;
+ }
+ ncolumns = 76/(maxlen+5);
+ if( ncolumns<1 ) ncolumns = 1;
+ skip = (lemp->nsymbol + ncolumns - 1)/ncolumns;
+ for(i=0; i<skip; i++){
+ printf("//");
+ for(j=i; j<lemp->nsymbol; j+=skip){
+ sp = lemp->symbols[j];
+ assert( sp->index==j );
+ printf(" %3d %-*.*s",j,maxlen,maxlen,sp->name);
+ }
+ printf("\n");
+ }
+ for(rp=lemp->rule; rp; rp=rp->next){
+ printf("%s",rp->lhs->name);
+ /* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */
+ printf(" ::=");
+ for(i=0; i<rp->nrhs; i++){
+ sp = rp->rhs[i];
+ printf(" %s", sp->name);
+ if( sp->type==MULTITERMINAL ){
+ for(j=1; j<sp->nsubsym; j++){
+ printf("|%s", sp->subsym[j]->name);
+ }
+ }
+ /* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */
+ }
+ printf(".");
+ if( rp->precsym ) printf(" [%s]",rp->precsym->name);
+ /* if( rp->code ) printf("\n %s",rp->code); */
+ printf("\n");
+ }
+}
+
+void ConfigPrint(fp,cfp)
+FILE *fp;
+struct config *cfp;
+{
+ struct rule *rp;
+ struct symbol *sp;
+ int i, j;
+ rp = cfp->rp;
+ fprintf(fp,"%s ::=",rp->lhs->name);
+ for(i=0; i<=rp->nrhs; i++){
+ if( i==cfp->dot ) fprintf(fp," *");
+ if( i==rp->nrhs ) break;
+ sp = rp->rhs[i];
+ fprintf(fp," %s", sp->name);
+ if( sp->type==MULTITERMINAL ){
+ for(j=1; j<sp->nsubsym; j++){
+ fprintf(fp,"|%s",sp->subsym[j]->name);
+ }
+ }
+ }
+}
+
+/* #define TEST */
+#if 0
+/* Print a set */
+PRIVATE void SetPrint(out,set,lemp)
+FILE *out;
+char *set;
+struct lemon *lemp;
+{
+ int i;
+ char *spacer;
+ spacer = "";
+ fprintf(out,"%12s[","");
+ for(i=0; i<lemp->nterminal; i++){
+ if( SetFind(set,i) ){
+ fprintf(out,"%s%s",spacer,lemp->symbols[i]->name);
+ spacer = " ";
+ }
+ }
+ fprintf(out,"]\n");
+}
+
+/* Print a plink chain */
+PRIVATE void PlinkPrint(out,plp,tag)
+FILE *out;
+struct plink *plp;
+char *tag;
+{
+ while( plp ){
+ fprintf(out,"%12s%s (state %2d) ","",tag,plp->cfp->stp->statenum);
+ ConfigPrint(out,plp->cfp);
+ fprintf(out,"\n");
+ plp = plp->next;
+ }
+}
+#endif
+
+/* Print an action to the given file descriptor. Return FALSE if
+** nothing was actually printed.
+*/
+int PrintAction(struct action *ap, FILE *fp, int indent){
+ int result = 1;
+ switch( ap->type ){
+ case SHIFT:
+ fprintf(fp,"%*s shift %d",indent,ap->sp->name,ap->x.stp->statenum);
+ break;
+ case REDUCE:
+ fprintf(fp,"%*s reduce %d",indent,ap->sp->name,ap->x.rp->index);
+ break;
+ case ACCEPT:
+ fprintf(fp,"%*s accept",indent,ap->sp->name);
+ break;
+ case ERROR:
+ fprintf(fp,"%*s error",indent,ap->sp->name);
+ break;
+ case SRCONFLICT:
+ case RRCONFLICT:
+ fprintf(fp,"%*s reduce %-3d ** Parsing conflict **",
+ indent,ap->sp->name,ap->x.rp->index);
+ break;
+ case SSCONFLICT:
+ fprintf(fp,"%*s shift %d ** Parsing conflict **",
+ indent,ap->sp->name,ap->x.stp->statenum);
+ break;
+ case SH_RESOLVED:
+ case RD_RESOLVED:
+ case NOT_USED:
+ result = 0;
+ break;
+ }
+ return result;
+}
+
+/* Generate the "y.output" log file */
+void ReportOutput(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct state *stp;
+ struct config *cfp;
+ struct action *ap;
+ FILE *fp;
+
+ fp = file_open(lemp,".out","wb");
+ if( fp==0 ) return;
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ fprintf(fp,"State %d:\n",stp->statenum);
+ if( lemp->basisflag ) cfp=stp->bp;
+ else cfp=stp->cfp;
+ while( cfp ){
+ char buf[20];
+ if( cfp->dot==cfp->rp->nrhs ){
+ sprintf(buf,"(%d)",cfp->rp->index);
+ fprintf(fp," %5s ",buf);
+ }else{
+ fprintf(fp," ");
+ }
+ ConfigPrint(fp,cfp);
+ fprintf(fp,"\n");
+#if 0
+ SetPrint(fp,cfp->fws,lemp);
+ PlinkPrint(fp,cfp->fplp,"To ");
+ PlinkPrint(fp,cfp->bplp,"From");
+#endif
+ if( lemp->basisflag ) cfp=cfp->bp;
+ else cfp=cfp->next;
+ }
+ fprintf(fp,"\n");
+ for(ap=stp->ap; ap; ap=ap->next){
+ if( PrintAction(ap,fp,30) ) fprintf(fp,"\n");
+ }
+ fprintf(fp,"\n");
+ }
+ fprintf(fp, "----------------------------------------------------\n");
+ fprintf(fp, "Symbols:\n");
+ for(i=0; i<lemp->nsymbol; i++){
+ int j;
+ struct symbol *sp;
+
+ sp = lemp->symbols[i];
+ fprintf(fp, " %3d: %s", i, sp->name);
+ if( sp->type==NONTERMINAL ){
+ fprintf(fp, ":");
+ if( sp->lambda ){
+ fprintf(fp, " <lambda>");
+ }
+ for(j=0; j<lemp->nterminal; j++){
+ if( sp->firstset && SetFind(sp->firstset, j) ){
+ fprintf(fp, " %s", lemp->symbols[j]->name);
+ }
+ }
+ }
+ fprintf(fp, "\n");
+ }
+#if 0
+ fclose(fp);
+#endif
+ return;
+}
+
+/* Search for the file "name" which is in the same directory as
+** the exacutable */
+PRIVATE char *pathsearch(argv0,name,modemask)
+char *argv0;
+char *name;
+int modemask;
+{
+ char *pathlist;
+ char *path,*cp;
+ char c;
+
+#ifdef __WIN32__
+ cp = strrchr(argv0,'\\');
+#else
+ cp = strrchr(argv0,'/');
+#endif
+ if( cp ){
+ c = *cp;
+ *cp = 0;
+ path = (char *)malloc( strlen(argv0) + strlen(name) + 2 );
+ if( path ) sprintf(path,"%s/%s",argv0,name);
+ *cp = c;
+ }else{
+ extern char *getenv();
+ pathlist = getenv("PATH");
+ if( pathlist==0 ) pathlist = ".:/bin:/usr/bin";
+ path = (char *)malloc( strlen(pathlist)+strlen(name)+2 );
+ if( path!=0 ){
+ while( *pathlist ){
+ cp = strchr(pathlist,':');
+ if( cp==0 ) cp = &pathlist[strlen(pathlist)];
+ c = *cp;
+ *cp = 0;
+ sprintf(path,"%s/%s",pathlist,name);
+ *cp = c;
+ if( c==0 ) pathlist = "";
+ else pathlist = &cp[1];
+ if( access(path,modemask)==0 ) break;
+ }
+ }
+ }
+ return path;
+}
+
+/* Given an action, compute the integer value for that action
+** which is to be put in the action table of the generated machine.
+** Return negative if no action should be generated.
+*/
+PRIVATE int compute_action(lemp,ap)
+struct lemon *lemp;
+struct action *ap;
+{
+ int act;
+ switch( ap->type ){
+ case SHIFT: act = ap->x.stp->statenum; break;
+ case REDUCE: act = ap->x.rp->index + lemp->nstate; break;
+ case ERROR: act = lemp->nstate + lemp->nrule; break;
+ case ACCEPT: act = lemp->nstate + lemp->nrule + 1; break;
+ default: act = -1; break;
+ }
+ return act;
+}
+
+#define LINESIZE 1000
+/* The next cluster of routines are for reading the template file
+** and writing the results to the generated parser */
+/* The first function transfers data from "in" to "out" until
+** a line is seen which begins with "%%". The line number is
+** tracked.
+**
+** if name!=0, then any word that begin with "Parse" is changed to
+** begin with *name instead.
+*/
+PRIVATE void tplt_xfer(name,in,out,lineno)
+char *name;
+FILE *in;
+FILE *out;
+int *lineno;
+{
+ int i, iStart;
+ char line[LINESIZE];
+ while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){
+ (*lineno)++;
+ iStart = 0;
+ if( name ){
+ for(i=0; line[i]; i++){
+ if( line[i]=='P' && strncmp(&line[i],"Parse",5)==0
+ && (i==0 || !isalpha(line[i-1]))
+ ){
+ if( i>iStart ) fprintf(out,"%.*s",i-iStart,&line[iStart]);
+ fprintf(out,"%s",name);
+ i += 4;
+ iStart = i+1;
+ }
+ }
+ }
+ fprintf(out,"%s",&line[iStart]);
+ }
+}
+
+/* The next function finds the template file and opens it, returning
+** a pointer to the opened file. */
+PRIVATE FILE *tplt_open(lemp)
+struct lemon *lemp;
+{
+ static char templatename[] = "lempar.c";
+ char buf[1000];
+ FILE *in;
+ char *tpltname;
+ char *cp;
+
+ cp = strrchr(lemp->filename,'.');
+ if( cp ){
+ sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename);
+ }else{
+ sprintf(buf,"%s.lt",lemp->filename);
+ }
+ if( access(buf,004)==0 ){
+ tpltname = buf;
+ }else if( access(templatename,004)==0 ){
+ tpltname = templatename;
+ }else{
+ tpltname = pathsearch(lemp->argv0,templatename,0);
+ }
+ if( tpltname==0 ){
+ fprintf(stderr,"Can't find the parser driver template file \"%s\".\n",
+ templatename);
+ lemp->errorcnt++;
+ return 0;
+ }
+ in = fopen(tpltname,"rb");
+ if( in==0 ){
+ fprintf(stderr,"Can't open the template file \"%s\".\n",templatename);
+ lemp->errorcnt++;
+ return 0;
+ }
+ return in;
+}
+
+/* Print a #line directive line to the output file. */
+PRIVATE void tplt_linedir(out,lineno,filename)
+FILE *out;
+int lineno;
+char *filename;
+{
+ fprintf(out,"#line %d \"",lineno);
+ while( *filename ){
+ if( *filename == '\\' ) putc('\\',out);
+ putc(*filename,out);
+ filename++;
+ }
+ fprintf(out,"\"\n");
+}
+
+/* Print a string to the file and keep the linenumber up to date */
+PRIVATE void tplt_print(out,lemp,str,strln,lineno)
+FILE *out;
+struct lemon *lemp;
+char *str;
+int strln;
+int *lineno;
+{
+ if( str==0 ) return;
+ tplt_linedir(out,strln,lemp->filename);
+ (*lineno)++;
+ while( *str ){
+ if( *str=='\n' ) (*lineno)++;
+ putc(*str,out);
+ str++;
+ }
+ if( str[-1]!='\n' ){
+ putc('\n',out);
+ (*lineno)++;
+ }
+ tplt_linedir(out,*lineno+2,lemp->outname);
+ (*lineno)+=2;
+ return;
+}
+
+/*
+** The following routine emits code for the destructor for the
+** symbol sp
+*/
+void emit_destructor_code(out,sp,lemp,lineno)
+FILE *out;
+struct symbol *sp;
+struct lemon *lemp;
+int *lineno;
+{
+ char *cp = 0;
+
+ int linecnt = 0;
+ if( sp->type==TERMINAL ){
+ cp = lemp->tokendest;
+ if( cp==0 ) return;
+ tplt_linedir(out,lemp->tokendestln,lemp->filename);
+ fprintf(out,"{");
+ }else if( sp->destructor ){
+ cp = sp->destructor;
+ tplt_linedir(out,sp->destructorln,lemp->filename);
+ fprintf(out,"{");
+ }else if( lemp->vardest ){
+ cp = lemp->vardest;
+ if( cp==0 ) return;
+ tplt_linedir(out,lemp->vardestln,lemp->filename);
+ fprintf(out,"{");
+ }else{
+ assert( 0 ); /* Cannot happen */
+ }
+ for(; *cp; cp++){
+ if( *cp=='$' && cp[1]=='$' ){
+ fprintf(out,"(yypminor->yy%d)",sp->dtnum);
+ cp++;
+ continue;
+ }
+ if( *cp=='\n' ) linecnt++;
+ fputc(*cp,out);
+ }
+ (*lineno) += 3 + linecnt;
+ fprintf(out,"}\n");
+ tplt_linedir(out,*lineno,lemp->outname);
+ return;
+}
+
+/*
+** Return TRUE (non-zero) if the given symbol has a destructor.
+*/
+int has_destructor(sp, lemp)
+struct symbol *sp;
+struct lemon *lemp;
+{
+ int ret;
+ if( sp->type==TERMINAL ){
+ ret = lemp->tokendest!=0;
+ }else{
+ ret = lemp->vardest!=0 || sp->destructor!=0;
+ }
+ return ret;
+}
+
+/*
+** Append text to a dynamically allocated string. If zText is 0 then
+** reset the string to be empty again. Always return the complete text
+** of the string (which is overwritten with each call).
+**
+** n bytes of zText are stored. If n==0 then all of zText up to the first
+** \000 terminator is stored. zText can contain up to two instances of
+** %d. The values of p1 and p2 are written into the first and second
+** %d.
+**
+** If n==-1, then the previous character is overwritten.
+*/
+PRIVATE char *append_str(char *zText, int n, int p1, int p2){
+ static char *z = 0;
+ static int alloced = 0;
+ static int used = 0;
+ int c;
+ char zInt[40];
+
+ if( zText==0 ){
+ used = 0;
+ return z;
+ }
+ if( n<=0 ){
+ if( n<0 ){
+ used += n;
+ assert( used>=0 );
+ }
+ n = strlen(zText);
+ }
+ if( n+sizeof(zInt)*2+used >= alloced ){
+ alloced = n + sizeof(zInt)*2 + used + 200;
+ z = realloc(z, alloced);
+ }
+ if( z==0 ) return "";
+ while( n-- > 0 ){
+ c = *(zText++);
+ if( c=='%' && n>0 && zText[0]=='d' ){
+ sprintf(zInt, "%d", p1);
+ p1 = p2;
+ strcpy(&z[used], zInt);
+ used += strlen(&z[used]);
+ zText++;
+ n--;
+ }else{
+ z[used++] = c;
+ }
+ }
+ z[used] = 0;
+ return z;
+}
+
+/*
+** zCode is a string that is the action associated with a rule. Expand
+** the symbols in this string so that the refer to elements of the parser
+** stack.
+*/
+PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){
+ char *cp, *xp;
+ int i;
+ char lhsused = 0; /* True if the LHS element has been used */
+ char used[MAXRHS]; /* True for each RHS element which is used */
+
+ for(i=0; i<rp->nrhs; i++) used[i] = 0;
+ lhsused = 0;
+
+ if( rp->code==0 ){
+ rp->code = "\n";
+ rp->line = rp->ruleline;
+ }
+
+ append_str(0,0,0,0);
+ for(cp=rp->code; *cp; cp++){
+ if( isalpha(*cp) && (cp==rp->code || (!isalnum(cp[-1]) && cp[-1]!='_')) ){
+ char saved;
+ for(xp= &cp[1]; isalnum(*xp) || *xp=='_'; xp++);
+ saved = *xp;
+ *xp = 0;
+ if( rp->lhsalias && strcmp(cp,rp->lhsalias)==0 ){
+ append_str("yygotominor.yy%d",0,rp->lhs->dtnum,0);
+ cp = xp;
+ lhsused = 1;
+ }else{
+ for(i=0; i<rp->nrhs; i++){
+ if( rp->rhsalias[i] && strcmp(cp,rp->rhsalias[i])==0 ){
+ if( cp!=rp->code && cp[-1]=='@' ){
+ /* If the argument is of the form @X then substituted
+ ** the token number of X, not the value of X */
+ append_str("yymsp[%d].major",-1,i-rp->nrhs+1,0);
+ }else{
+ struct symbol *sp = rp->rhs[i];
+ int dtnum;
+ if( sp->type==MULTITERMINAL ){
+ dtnum = sp->subsym[0]->dtnum;
+ }else{
+ dtnum = sp->dtnum;
+ }
+ append_str("yymsp[%d].minor.yy%d",0,i-rp->nrhs+1, dtnum);
+ }
+ cp = xp;
+ used[i] = 1;
+ break;
+ }
+ }
+ }
+ *xp = saved;
+ }
+ append_str(cp, 1, 0, 0);
+ } /* End loop */
+
+ /* Check to make sure the LHS has been used */
+ if( rp->lhsalias && !lhsused ){
+ ErrorMsg(lemp->filename,rp->ruleline,
+ "Label \"%s\" for \"%s(%s)\" is never used.",
+ rp->lhsalias,rp->lhs->name,rp->lhsalias);
+ lemp->errorcnt++;
+ }
+
+ /* Generate destructor code for RHS symbols which are not used in the
+ ** reduce code */
+ for(i=0; i<rp->nrhs; i++){
+ if( rp->rhsalias[i] && !used[i] ){
+ ErrorMsg(lemp->filename,rp->ruleline,
+ "Label %s for \"%s(%s)\" is never used.",
+ rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]);
+ lemp->errorcnt++;
+ }else if( rp->rhsalias[i]==0 ){
+ if( has_destructor(rp->rhs[i],lemp) ){
+ append_str(" yy_destructor(%d,&yymsp[%d].minor);\n", 0,
+ rp->rhs[i]->index,i-rp->nrhs+1);
+ }else{
+ /* No destructor defined for this term */
+ }
+ }
+ }
+ if( rp->code ){
+ cp = append_str(0,0,0,0);
+ rp->code = Strsafe(cp?cp:"");
+ }
+}
+
+/*
+** Generate code which executes when the rule "rp" is reduced. Write
+** the code to "out". Make sure lineno stays up-to-date.
+*/
+PRIVATE void emit_code(out,rp,lemp,lineno)
+FILE *out;
+struct rule *rp;
+struct lemon *lemp;
+int *lineno;
+{
+ char *cp;
+ int linecnt = 0;
+
+ /* Generate code to do the reduce action */
+ if( rp->code ){
+ tplt_linedir(out,rp->line,lemp->filename);
+ fprintf(out,"{%s",rp->code);
+ for(cp=rp->code; *cp; cp++){
+ if( *cp=='\n' ) linecnt++;
+ } /* End loop */
+ (*lineno) += 3 + linecnt;
+ fprintf(out,"}\n");
+ tplt_linedir(out,*lineno,lemp->outname);
+ } /* End if( rp->code ) */
+
+ return;
+}
+
+/*
+** Print the definition of the union used for the parser's data stack.
+** This union contains fields for every possible data type for tokens
+** and nonterminals. In the process of computing and printing this
+** union, also set the ".dtnum" field of every terminal and nonterminal
+** symbol.
+*/
+void print_stack_union(out,lemp,plineno,mhflag)
+FILE *out; /* The output stream */
+struct lemon *lemp; /* The main info structure for this parser */
+int *plineno; /* Pointer to the line number */
+int mhflag; /* True if generating makeheaders output */
+{
+ int lineno = *plineno; /* The line number of the output */
+ char **types; /* A hash table of datatypes */
+ int arraysize; /* Size of the "types" array */
+ int maxdtlength; /* Maximum length of any ".datatype" field. */
+ char *stddt; /* Standardized name for a datatype */
+ int i,j; /* Loop counters */
+ int hash; /* For hashing the name of a type */
+ char *name; /* Name of the parser */
+
+ /* Allocate and initialize types[] and allocate stddt[] */
+ arraysize = lemp->nsymbol * 2;
+ types = (char**)calloc( arraysize, sizeof(char*) );
+ for(i=0; i<arraysize; i++) types[i] = 0;
+ maxdtlength = 0;
+ if( lemp->vartype ){
+ maxdtlength = strlen(lemp->vartype);
+ }
+ for(i=0; i<lemp->nsymbol; i++){
+ int len;
+ struct symbol *sp = lemp->symbols[i];
+ if( sp->datatype==0 ) continue;
+ len = strlen(sp->datatype);
+ if( len>maxdtlength ) maxdtlength = len;
+ }
+ stddt = (char*)malloc( maxdtlength*2 + 1 );
+ if( types==0 || stddt==0 ){
+ fprintf(stderr,"Out of memory.\n");
+ exit(1);
+ }
+
+ /* Build a hash table of datatypes. The ".dtnum" field of each symbol
+ ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is
+ ** used for terminal symbols. If there is no %default_type defined then
+ ** 0 is also used as the .dtnum value for nonterminals which do not specify
+ ** a datatype using the %type directive.
+ */
+ for(i=0; i<lemp->nsymbol; i++){
+ struct symbol *sp = lemp->symbols[i];
+ char *cp;
+ if( sp==lemp->errsym ){
+ sp->dtnum = arraysize+1;
+ continue;
+ }
+ if( sp->type!=NONTERMINAL || (sp->datatype==0 && lemp->vartype==0) ){
+ sp->dtnum = 0;
+ continue;
+ }
+ cp = sp->datatype;
+ if( cp==0 ) cp = lemp->vartype;
+ j = 0;
+ while( isspace(*cp) ) cp++;
+ while( *cp ) stddt[j++] = *cp++;
+ while( j>0 && isspace(stddt[j-1]) ) j--;
+ stddt[j] = 0;
+ hash = 0;
+ for(j=0; stddt[j]; j++){
+ hash = hash*53 + stddt[j];
+ }
+ hash = (hash & 0x7fffffff)%arraysize;
+ while( types[hash] ){
+ if( strcmp(types[hash],stddt)==0 ){
+ sp->dtnum = hash + 1;
+ break;
+ }
+ hash++;
+ if( hash>=arraysize ) hash = 0;
+ }
+ if( types[hash]==0 ){
+ sp->dtnum = hash + 1;
+ types[hash] = (char*)malloc( strlen(stddt)+1 );
+ if( types[hash]==0 ){
+ fprintf(stderr,"Out of memory.\n");
+ exit(1);
+ }
+ strcpy(types[hash],stddt);
+ }
+ }
+
+ /* Print out the definition of YYTOKENTYPE and YYMINORTYPE */
+ name = lemp->name ? lemp->name : "Parse";
+ lineno = *plineno;
+ if( mhflag ){ fprintf(out,"#if INTERFACE\n"); lineno++; }
+ fprintf(out,"#define %sTOKENTYPE %s\n",name,
+ lemp->tokentype?lemp->tokentype:"void*"); lineno++;
+ if( mhflag ){ fprintf(out,"#endif\n"); lineno++; }
+ fprintf(out,"typedef union {\n"); lineno++;
+ fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++;
+ for(i=0; i<arraysize; i++){
+ if( types[i]==0 ) continue;
+ fprintf(out," %s yy%d;\n",types[i],i+1); lineno++;
+ free(types[i]);
+ }
+ if( lemp->errsym->useCnt ){
+ fprintf(out," int yy%d;\n",lemp->errsym->dtnum); lineno++;
+ }
+ free(stddt);
+ free(types);
+ fprintf(out,"} YYMINORTYPE;\n"); lineno++;
+ *plineno = lineno;
+}
+
+/*
+** Return the name of a C datatype able to represent values between
+** lwr and upr, inclusive.
+*/
+static const char *minimum_size_type(int lwr, int upr){
+ if( lwr>=0 ){
+ if( upr<=255 ){
+ return "unsigned char";
+ }else if( upr<65535 ){
+ return "unsigned short int";
+ }else{
+ return "unsigned int";
+ }
+ }else if( lwr>=-127 && upr<=127 ){
+ return "signed char";
+ }else if( lwr>=-32767 && upr<32767 ){
+ return "short";
+ }else{
+ return "int";
+ }
+}
+
+/*
+** Each state contains a set of token transaction and a set of
+** nonterminal transactions. Each of these sets makes an instance
+** of the following structure. An array of these structures is used
+** to order the creation of entries in the yy_action[] table.
+*/
+struct axset {
+ struct state *stp; /* A pointer to a state */
+ int isTkn; /* True to use tokens. False for non-terminals */
+ int nAction; /* Number of actions */
+};
+
+/*
+** Compare to axset structures for sorting purposes
+*/
+static int axset_compare(const void *a, const void *b){
+ struct axset *p1 = (struct axset*)a;
+ struct axset *p2 = (struct axset*)b;
+ return p2->nAction - p1->nAction;
+}
+
+/*
+** Write text on "out" that describes the rule "rp".
+*/
+static void writeRuleText(FILE *out, struct rule *rp){
+ int j;
+ fprintf(out,"%s ::=", rp->lhs->name);
+ for(j=0; j<rp->nrhs; j++){
+ struct symbol *sp = rp->rhs[j];
+ fprintf(out," %s", sp->name);
+ if( sp->type==MULTITERMINAL ){
+ int k;
+ for(k=1; k<sp->nsubsym; k++){
+ fprintf(out,"|%s",sp->subsym[k]->name);
+ }
+ }
+ }
+}
+
+
+/* Generate C source code for the parser */
+void ReportTable(lemp, mhflag)
+struct lemon *lemp;
+int mhflag; /* Output in makeheaders format if true */
+{
+ FILE *out, *in;
+ char line[LINESIZE];
+ int lineno;
+ struct state *stp;
+ struct action *ap;
+ struct rule *rp;
+ struct acttab *pActtab;
+ int i, j, n;
+ char *name;
+ int mnTknOfst, mxTknOfst;
+ int mnNtOfst, mxNtOfst;
+ struct axset *ax;
+
+ in = tplt_open(lemp);
+ if( in==0 ) return;
+ out = file_open(lemp,".c","wb");
+ if( out==0 ){
+ fclose(in);
+ return;
+ }
+ lineno = 1;
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the include code, if any */
+ tplt_print(out,lemp,lemp->include,lemp->includeln,&lineno);
+ if( mhflag ){
+ char *name = file_makename(lemp, ".h");
+ fprintf(out,"#include \"%s\"\n", name); lineno++;
+ free(name);
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate #defines for all tokens */
+ if( mhflag ){
+ char *prefix;
+ fprintf(out,"#if INTERFACE\n"); lineno++;
+ if( lemp->tokenprefix ) prefix = lemp->tokenprefix;
+ else prefix = "";
+ for(i=1; i<lemp->nterminal; i++){
+ fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ lineno++;
+ }
+ fprintf(out,"#endif\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the defines */
+ fprintf(out,"#define YYCODETYPE %s\n",
+ minimum_size_type(0, lemp->nsymbol+5)); lineno++;
+ fprintf(out,"#define YYNOCODE %d\n",lemp->nsymbol+1); lineno++;
+ fprintf(out,"#define YYACTIONTYPE %s\n",
+ minimum_size_type(0, lemp->nstate+lemp->nrule+5)); lineno++;
+ if( lemp->wildcard ){
+ fprintf(out,"#define YYWILDCARD %d\n",
+ lemp->wildcard->index); lineno++;
+ }
+ print_stack_union(out,lemp,&lineno,mhflag);
+ fprintf(out, "#ifndef YYSTACKDEPTH\n"); lineno++;
+ if( lemp->stacksize ){
+ fprintf(out,"#define YYSTACKDEPTH %s\n",lemp->stacksize); lineno++;
+ }else{
+ fprintf(out,"#define YYSTACKDEPTH 100\n"); lineno++;
+ }
+ fprintf(out, "#endif\n"); lineno++;
+ if( mhflag ){
+ fprintf(out,"#if INTERFACE\n"); lineno++;
+ }
+ name = lemp->name ? lemp->name : "Parse";
+ if( lemp->arg && lemp->arg[0] ){
+ int i;
+ i = strlen(lemp->arg);
+ while( i>=1 && isspace(lemp->arg[i-1]) ) i--;
+ while( i>=1 && (isalnum(lemp->arg[i-1]) || lemp->arg[i-1]=='_') ) i--;
+ fprintf(out,"#define %sARG_SDECL %s;\n",name,lemp->arg); lineno++;
+ fprintf(out,"#define %sARG_PDECL ,%s\n",name,lemp->arg); lineno++;
+ fprintf(out,"#define %sARG_FETCH %s = yypParser->%s\n",
+ name,lemp->arg,&lemp->arg[i]); lineno++;
+ fprintf(out,"#define %sARG_STORE yypParser->%s = %s\n",
+ name,&lemp->arg[i],&lemp->arg[i]); lineno++;
+ }else{
+ fprintf(out,"#define %sARG_SDECL\n",name); lineno++;
+ fprintf(out,"#define %sARG_PDECL\n",name); lineno++;
+ fprintf(out,"#define %sARG_FETCH\n",name); lineno++;
+ fprintf(out,"#define %sARG_STORE\n",name); lineno++;
+ }
+ if( mhflag ){
+ fprintf(out,"#endif\n"); lineno++;
+ }
+ fprintf(out,"#define YYNSTATE %d\n",lemp->nstate); lineno++;
+ fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++;
+ if( lemp->errsym->useCnt ){
+ fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++;
+ fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++;
+ }
+ if( lemp->has_fallback ){
+ fprintf(out,"#define YYFALLBACK 1\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the action table and its associates:
+ **
+ ** yy_action[] A single table containing all actions.
+ ** yy_lookahead[] A table containing the lookahead for each entry in
+ ** yy_action. Used to detect hash collisions.
+ ** yy_shift_ofst[] For each state, the offset into yy_action for
+ ** shifting terminals.
+ ** yy_reduce_ofst[] For each state, the offset into yy_action for
+ ** shifting non-terminals after a reduce.
+ ** yy_default[] Default action for each state.
+ */
+
+ /* Compute the actions on all states and count them up */
+ ax = calloc(lemp->nstate*2, sizeof(ax[0]));
+ if( ax==0 ){
+ fprintf(stderr,"malloc failed\n");
+ exit(1);
+ }
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ ax[i*2].stp = stp;
+ ax[i*2].isTkn = 1;
+ ax[i*2].nAction = stp->nTknAct;
+ ax[i*2+1].stp = stp;
+ ax[i*2+1].isTkn = 0;
+ ax[i*2+1].nAction = stp->nNtAct;
+ }
+ mxTknOfst = mnTknOfst = 0;
+ mxNtOfst = mnNtOfst = 0;
+
+ /* Compute the action table. In order to try to keep the size of the
+ ** action table to a minimum, the heuristic of placing the largest action
+ ** sets first is used.
+ */
+ qsort(ax, lemp->nstate*2, sizeof(ax[0]), axset_compare);
+ pActtab = acttab_alloc();
+ for(i=0; i<lemp->nstate*2 && ax[i].nAction>0; i++){
+ stp = ax[i].stp;
+ if( ax[i].isTkn ){
+ for(ap=stp->ap; ap; ap=ap->next){
+ int action;
+ if( ap->sp->index>=lemp->nterminal ) continue;
+ action = compute_action(lemp, ap);
+ if( action<0 ) continue;
+ acttab_action(pActtab, ap->sp->index, action);
+ }
+ stp->iTknOfst = acttab_insert(pActtab);
+ if( stp->iTknOfst<mnTknOfst ) mnTknOfst = stp->iTknOfst;
+ if( stp->iTknOfst>mxTknOfst ) mxTknOfst = stp->iTknOfst;
+ }else{
+ for(ap=stp->ap; ap; ap=ap->next){
+ int action;
+ if( ap->sp->index<lemp->nterminal ) continue;
+ if( ap->sp->index==lemp->nsymbol ) continue;
+ action = compute_action(lemp, ap);
+ if( action<0 ) continue;
+ acttab_action(pActtab, ap->sp->index, action);
+ }
+ stp->iNtOfst = acttab_insert(pActtab);
+ if( stp->iNtOfst<mnNtOfst ) mnNtOfst = stp->iNtOfst;
+ if( stp->iNtOfst>mxNtOfst ) mxNtOfst = stp->iNtOfst;
+ }
+ }
+ free(ax);
+
+ /* Output the yy_action table */
+ fprintf(out,"static const YYACTIONTYPE yy_action[] = {\n"); lineno++;
+ n = acttab_size(pActtab);
+ for(i=j=0; i<n; i++){
+ int action = acttab_yyaction(pActtab, i);
+ if( action<0 ) action = lemp->nstate + lemp->nrule + 2;
+ if( j==0 ) fprintf(out," /* %5d */ ", i);
+ fprintf(out, " %4d,", action);
+ if( j==9 || i==n-1 ){
+ fprintf(out, "\n"); lineno++;
+ j = 0;
+ }else{
+ j++;
+ }
+ }
+ fprintf(out, "};\n"); lineno++;
+
+ /* Output the yy_lookahead table */
+ fprintf(out,"static const YYCODETYPE yy_lookahead[] = {\n"); lineno++;
+ for(i=j=0; i<n; i++){
+ int la = acttab_yylookahead(pActtab, i);
+ if( la<0 ) la = lemp->nsymbol;
+ if( j==0 ) fprintf(out," /* %5d */ ", i);
+ fprintf(out, " %4d,", la);
+ if( j==9 || i==n-1 ){
+ fprintf(out, "\n"); lineno++;
+ j = 0;
+ }else{
+ j++;
+ }
+ }
+ fprintf(out, "};\n"); lineno++;
+
+ /* Output the yy_shift_ofst[] table */
+ fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", mnTknOfst-1); lineno++;
+ n = lemp->nstate;
+ while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--;
+ fprintf(out, "#define YY_SHIFT_MAX %d\n", n-1); lineno++;
+ fprintf(out, "static const %s yy_shift_ofst[] = {\n",
+ minimum_size_type(mnTknOfst-1, mxTknOfst)); lineno++;
+ for(i=j=0; i<n; i++){
+ int ofst;
+ stp = lemp->sorted[i];
+ ofst = stp->iTknOfst;
+ if( ofst==NO_OFFSET ) ofst = mnTknOfst - 1;
+ if( j==0 ) fprintf(out," /* %5d */ ", i);
+ fprintf(out, " %4d,", ofst);
+ if( j==9 || i==n-1 ){
+ fprintf(out, "\n"); lineno++;
+ j = 0;
+ }else{
+ j++;
+ }
+ }
+ fprintf(out, "};\n"); lineno++;
+
+ /* Output the yy_reduce_ofst[] table */
+ fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++;
+ n = lemp->nstate;
+ while( n>0 && lemp->sorted[n-1]->iNtOfst==NO_OFFSET ) n--;
+ fprintf(out, "#define YY_REDUCE_MAX %d\n", n-1); lineno++;
+ fprintf(out, "static const %s yy_reduce_ofst[] = {\n",
+ minimum_size_type(mnNtOfst-1, mxNtOfst)); lineno++;
+ for(i=j=0; i<n; i++){
+ int ofst;
+ stp = lemp->sorted[i];
+ ofst = stp->iNtOfst;
+ if( ofst==NO_OFFSET ) ofst = mnNtOfst - 1;
+ if( j==0 ) fprintf(out," /* %5d */ ", i);
+ fprintf(out, " %4d,", ofst);
+ if( j==9 || i==n-1 ){
+ fprintf(out, "\n"); lineno++;
+ j = 0;
+ }else{
+ j++;
+ }
+ }
+ fprintf(out, "};\n"); lineno++;
+
+ /* Output the default action table */
+ fprintf(out, "static const YYACTIONTYPE yy_default[] = {\n"); lineno++;
+ n = lemp->nstate;
+ for(i=j=0; i<n; i++){
+ stp = lemp->sorted[i];
+ if( j==0 ) fprintf(out," /* %5d */ ", i);
+ fprintf(out, " %4d,", stp->iDflt);
+ if( j==9 || i==n-1 ){
+ fprintf(out, "\n"); lineno++;
+ j = 0;
+ }else{
+ j++;
+ }
+ }
+ fprintf(out, "};\n"); lineno++;
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the table of fallback tokens.
+ */
+ if( lemp->has_fallback ){
+ for(i=0; i<lemp->nterminal; i++){
+ struct symbol *p = lemp->symbols[i];
+ if( p->fallback==0 ){
+ fprintf(out, " 0, /* %10s => nothing */\n", p->name);
+ }else{
+ fprintf(out, " %3d, /* %10s => %s */\n", p->fallback->index,
+ p->name, p->fallback->name);
+ }
+ lineno++;
+ }
+ }
+ tplt_xfer(lemp->name, in, out, &lineno);
+
+ /* Generate a table containing the symbolic name of every symbol
+ */
+ for(i=0; i<lemp->nsymbol; i++){
+ sprintf(line,"\"%s\",",lemp->symbols[i]->name);
+ fprintf(out," %-15s",line);
+ if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; }
+ }
+ if( (i&3)!=0 ){ fprintf(out,"\n"); lineno++; }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate a table containing a text string that describes every
+ ** rule in the rule set of the grammer. This information is used
+ ** when tracing REDUCE actions.
+ */
+ for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){
+ assert( rp->index==i );
+ fprintf(out," /* %3d */ \"", i);
+ writeRuleText(out, rp);
+ fprintf(out,"\",\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes every time a symbol is popped from
+ ** the stack while processing errors or while destroying the parser.
+ ** (In other words, generate the %destructor actions)
+ */
+ if( lemp->tokendest ){
+ for(i=0; i<lemp->nsymbol; i++){
+ struct symbol *sp = lemp->symbols[i];
+ if( sp==0 || sp->type!=TERMINAL ) continue;
+ fprintf(out," case %d: /* %s */\n",
+ sp->index, sp->name); lineno++;
+ }
+ for(i=0; i<lemp->nsymbol && lemp->symbols[i]->type!=TERMINAL; i++);
+ if( i<lemp->nsymbol ){
+ emit_destructor_code(out,lemp->symbols[i],lemp,&lineno);
+ fprintf(out," break;\n"); lineno++;
+ }
+ }
+ if( lemp->vardest ){
+ struct symbol *dflt_sp = 0;
+ for(i=0; i<lemp->nsymbol; i++){
+ struct symbol *sp = lemp->symbols[i];
+ if( sp==0 || sp->type==TERMINAL ||
+ sp->index<=0 || sp->destructor!=0 ) continue;
+ fprintf(out," case %d: /* %s */\n",
+ sp->index, sp->name); lineno++;
+ dflt_sp = sp;
+ }
+ if( dflt_sp!=0 ){
+ emit_destructor_code(out,dflt_sp,lemp,&lineno);
+ fprintf(out," break;\n"); lineno++;
+ }
+ }
+ for(i=0; i<lemp->nsymbol; i++){
+ struct symbol *sp = lemp->symbols[i];
+ if( sp==0 || sp->type==TERMINAL || sp->destructor==0 ) continue;
+ fprintf(out," case %d: /* %s */\n",
+ sp->index, sp->name); lineno++;
+
+ /* Combine duplicate destructors into a single case */
+ for(j=i+1; j<lemp->nsymbol; j++){
+ struct symbol *sp2 = lemp->symbols[j];
+ if( sp2 && sp2->type!=TERMINAL && sp2->destructor
+ && sp2->dtnum==sp->dtnum
+ && strcmp(sp->destructor,sp2->destructor)==0 ){
+ fprintf(out," case %d: /* %s */\n",
+ sp2->index, sp2->name); lineno++;
+ sp2->destructor = 0;
+ }
+ }
+
+ emit_destructor_code(out,lemp->symbols[i],lemp,&lineno);
+ fprintf(out," break;\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes whenever the parser stack overflows */
+ tplt_print(out,lemp,lemp->overflow,lemp->overflowln,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the table of rule information
+ **
+ ** Note: This code depends on the fact that rules are number
+ ** sequentually beginning with 0.
+ */
+ for(rp=lemp->rule; rp; rp=rp->next){
+ fprintf(out," { %d, %d },\n",rp->lhs->index,rp->nrhs); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which execution during each REDUCE action */
+ for(rp=lemp->rule; rp; rp=rp->next){
+ translate_code(lemp, rp);
+ }
+ for(rp=lemp->rule; rp; rp=rp->next){
+ struct rule *rp2;
+ if( rp->code==0 ) continue;
+ fprintf(out," case %d: /* ", rp->index);
+ writeRuleText(out, rp);
+ fprintf(out, " */\n"); lineno++;
+ for(rp2=rp->next; rp2; rp2=rp2->next){
+ if( rp2->code==rp->code ){
+ fprintf(out," case %d: /* ", rp2->index);
+ writeRuleText(out, rp2);
+ fprintf(out," */\n"); lineno++;
+ rp2->code = 0;
+ }
+ }
+ emit_code(out,rp,lemp,&lineno);
+ fprintf(out," break;\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes if a parse fails */
+ tplt_print(out,lemp,lemp->failure,lemp->failureln,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes when a syntax error occurs */
+ tplt_print(out,lemp,lemp->error,lemp->errorln,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes when the parser accepts its input */
+ tplt_print(out,lemp,lemp->accept,lemp->acceptln,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Append any addition code the user desires */
+ tplt_print(out,lemp,lemp->extracode,lemp->extracodeln,&lineno);
+
+ fclose(in);
+#if 0
+ fclose(out);
+#endif
+ return;
+}
+
+/* Generate a header file for the parser */
+void ReportHeader(lemp)
+struct lemon *lemp;
+{
+ FILE *out, *in;
+ char *prefix;
+ char line[LINESIZE];
+ char pattern[LINESIZE];
+ int i;
+
+ if( lemp->tokenprefix ) prefix = lemp->tokenprefix;
+ else prefix = "";
+ in = file_open(lemp,".h","rb");
+ if( in ){
+ for(i=1; i<lemp->nterminal && fgets(line,LINESIZE,in); i++){
+ sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ if( strcmp(line,pattern) ) break;
+ }
+ fclose(in);
+ if( i==lemp->nterminal ){
+ /* No change in the file. Don't rewrite it. */
+ return;
+ }
+ }
+ out = file_open(lemp,".h","wb");
+ if( out ){
+ for(i=1; i<lemp->nterminal; i++){
+ fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ }
+#if 0
+ fclose(out);
+#endif
+ }
+ return;
+}
+
+/* Reduce the size of the action tables, if possible, by making use
+** of defaults.
+**
+** In this version, we take the most frequent REDUCE action and make
+** it the default. Except, there is no default if the wildcard token
+** is a possible look-ahead.
+*/
+void CompressTables(lemp)
+struct lemon *lemp;
+{
+ struct state *stp;
+ struct action *ap, *ap2;
+ struct rule *rp, *rp2, *rbest;
+ int nbest, n;
+ int i;
+ int usesWildcard;
+
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ nbest = 0;
+ rbest = 0;
+ usesWildcard = 0;
+
+ for(ap=stp->ap; ap; ap=ap->next){
+ if( ap->type==SHIFT && ap->sp==lemp->wildcard ){
+ usesWildcard = 1;
+ }
+ if( ap->type!=REDUCE ) continue;
+ rp = ap->x.rp;
+ if( rp->lhsStart ) continue;
+ if( rp==rbest ) continue;
+ n = 1;
+ for(ap2=ap->next; ap2; ap2=ap2->next){
+ if( ap2->type!=REDUCE ) continue;
+ rp2 = ap2->x.rp;
+ if( rp2==rbest ) continue;
+ if( rp2==rp ) n++;
+ }
+ if( n>nbest ){
+ nbest = n;
+ rbest = rp;
+ }
+ }
+
+ /* Do not make a default if the number of rules to default
+ ** is not at least 1 or if the wildcard token is a possible
+ ** lookahead.
+ */
+ if( nbest<1 || usesWildcard ) continue;
+
+
+ /* Combine matching REDUCE actions into a single default */
+ for(ap=stp->ap; ap; ap=ap->next){
+ if( ap->type==REDUCE && ap->x.rp==rbest ) break;
+ }
+ assert( ap );
+ ap->sp = Symbol_new("{default}");
+ for(ap=ap->next; ap; ap=ap->next){
+ if( ap->type==REDUCE && ap->x.rp==rbest ) ap->type = NOT_USED;
+ }
+ stp->ap = Action_sort(stp->ap);
+ }
+}
+
+
+/*
+** Compare two states for sorting purposes. The smaller state is the
+** one with the most non-terminal actions. If they have the same number
+** of non-terminal actions, then the smaller is the one with the most
+** token actions.
+*/
+static int stateResortCompare(const void *a, const void *b){
+ const struct state *pA = *(const struct state**)a;
+ const struct state *pB = *(const struct state**)b;
+ int n;
+
+ n = pB->nNtAct - pA->nNtAct;
+ if( n==0 ){
+ n = pB->nTknAct - pA->nTknAct;
+ }
+ return n;
+}
+
+
+/*
+** Renumber and resort states so that states with fewer choices
+** occur at the end. Except, keep state 0 as the first state.
+*/
+void ResortStates(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct state *stp;
+ struct action *ap;
+
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ stp->nTknAct = stp->nNtAct = 0;
+ stp->iDflt = lemp->nstate + lemp->nrule;
+ stp->iTknOfst = NO_OFFSET;
+ stp->iNtOfst = NO_OFFSET;
+ for(ap=stp->ap; ap; ap=ap->next){
+ if( compute_action(lemp,ap)>=0 ){
+ if( ap->sp->index<lemp->nterminal ){
+ stp->nTknAct++;
+ }else if( ap->sp->index<lemp->nsymbol ){
+ stp->nNtAct++;
+ }else{
+ stp->iDflt = compute_action(lemp, ap);
+ }
+ }
+ }
+ }
+ qsort(&lemp->sorted[1], lemp->nstate-1, sizeof(lemp->sorted[0]),
+ stateResortCompare);
+ for(i=0; i<lemp->nstate; i++){
+ lemp->sorted[i]->statenum = i;
+ }
+}
+
+
+/***************** From the file "set.c" ************************************/
+/*
+** Set manipulation routines for the LEMON parser generator.
+*/
+
+static int size = 0;
+
+/* Set the set size */
+void SetSize(n)
+int n;
+{
+ size = n+1;
+}
+
+/* Allocate a new set */
+char *SetNew(){
+ char *s;
+ s = (char*)calloc( size, 1);
+ if( s==0 ){
+ extern void memory_error();
+ memory_error();
+ }
+ return s;
+}
+
+/* Deallocate a set */
+void SetFree(s)
+char *s;
+{
+ free(s);
+}
+
+/* Add a new element to the set. Return TRUE if the element was added
+** and FALSE if it was already there. */
+int SetAdd(s,e)
+char *s;
+int e;
+{
+ int rv;
+ assert( e>=0 && e<size );
+ rv = s[e];
+ s[e] = 1;
+ return !rv;
+}
+
+/* Add every element of s2 to s1. Return TRUE if s1 changes. */
+int SetUnion(s1,s2)
+char *s1;
+char *s2;
+{
+ int i, progress;
+ progress = 0;
+ for(i=0; i<size; i++){
+ if( s2[i]==0 ) continue;
+ if( s1[i]==0 ){
+ progress = 1;
+ s1[i] = 1;
+ }
+ }
+ return progress;
+}
+/********************** From the file "table.c" ****************************/
+/*
+** All code in this file has been automatically generated
+** from a specification in the file
+** "table.q"
+** by the associative array code building program "aagen".
+** Do not edit this file! Instead, edit the specification
+** file, then rerun aagen.
+*/
+/*
+** Code for processing tables in the LEMON parser generator.
+*/
+
+PRIVATE int strhash(x)
+char *x;
+{
+ int h = 0;
+ while( *x) h = h*13 + *(x++);
+ return h;
+}
+
+/* Works like strdup, sort of. Save a string in malloced memory, but
+** keep strings in a table so that the same string is not in more
+** than one place.
+*/
+char *Strsafe(y)
+char *y;
+{
+ char *z;
+
+ if( y==0 ) return 0;
+ z = Strsafe_find(y);
+ if( z==0 && (z=malloc( strlen(y)+1 ))!=0 ){
+ strcpy(z,y);
+ Strsafe_insert(z);
+ }
+ MemoryCheck(z);
+ return z;
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x1".
+*/
+struct s_x1 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x1node *tbl; /* The data stored here */
+ struct s_x1node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x1".
+*/
+typedef struct s_x1node {
+ char *data; /* The data */
+ struct s_x1node *next; /* Next entry with the same hash */
+ struct s_x1node **from; /* Previous link */
+} x1node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x1 *x1a;
+
+/* Allocate a new associative array */
+void Strsafe_init(){
+ if( x1a ) return;
+ x1a = (struct s_x1*)malloc( sizeof(struct s_x1) );
+ if( x1a ){
+ x1a->size = 1024;
+ x1a->count = 0;
+ x1a->tbl = (x1node*)malloc(
+ (sizeof(x1node) + sizeof(x1node*))*1024 );
+ if( x1a->tbl==0 ){
+ free(x1a);
+ x1a = 0;
+ }else{
+ int i;
+ x1a->ht = (x1node**)&(x1a->tbl[1024]);
+ for(i=0; i<1024; i++) x1a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int Strsafe_insert(data)
+char *data;
+{
+ x1node *np;
+ int h;
+ int ph;
+
+ if( x1a==0 ) return 0;
+ ph = strhash(data);
+ h = ph & (x1a->size-1);
+ np = x1a->ht[h];
+ while( np ){
+ if( strcmp(np->data,data)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x1a->count>=x1a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x1 array;
+ array.size = size = x1a->size*2;
+ array.count = x1a->count;
+ array.tbl = (x1node*)malloc(
+ (sizeof(x1node) + sizeof(x1node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x1node**)&(array.tbl[size]);
+ for(i=0; i<size; i++) array.ht[i] = 0;
+ for(i=0; i<x1a->count; i++){
+ x1node *oldnp, *newnp;
+ oldnp = &(x1a->tbl[i]);
+ h = strhash(oldnp->data) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x1a->tbl);
+ *x1a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x1a->size-1);
+ np = &(x1a->tbl[x1a->count++]);
+ np->data = data;
+ if( x1a->ht[h] ) x1a->ht[h]->from = &(np->next);
+ np->next = x1a->ht[h];
+ x1a->ht[h] = np;
+ np->from = &(x1a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+char *Strsafe_find(key)
+char *key;
+{
+ int h;
+ x1node *np;
+
+ if( x1a==0 ) return 0;
+ h = strhash(key) & (x1a->size-1);
+ np = x1a->ht[h];
+ while( np ){
+ if( strcmp(np->data,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Return a pointer to the (terminal or nonterminal) symbol "x".
+** Create a new symbol if this is the first time "x" has been seen.
+*/
+struct symbol *Symbol_new(x)
+char *x;
+{
+ struct symbol *sp;
+
+ sp = Symbol_find(x);
+ if( sp==0 ){
+ sp = (struct symbol *)calloc(1, sizeof(struct symbol) );
+ MemoryCheck(sp);
+ sp->name = Strsafe(x);
+ sp->type = isupper(*x) ? TERMINAL : NONTERMINAL;
+ sp->rule = 0;
+ sp->fallback = 0;
+ sp->prec = -1;
+ sp->assoc = UNK;
+ sp->firstset = 0;
+ sp->lambda = LEMON_FALSE;
+ sp->destructor = 0;
+ sp->datatype = 0;
+ sp->useCnt = 0;
+ Symbol_insert(sp,sp->name);
+ }
+ sp->useCnt++;
+ return sp;
+}
+
+/* Compare two symbols for working purposes
+**
+** Symbols that begin with upper case letters (terminals or tokens)
+** must sort before symbols that begin with lower case letters
+** (non-terminals). Other than that, the order does not matter.
+**
+** We find experimentally that leaving the symbols in their original
+** order (the order they appeared in the grammar file) gives the
+** smallest parser tables in SQLite.
+*/
+int Symbolcmpp(struct symbol **a, struct symbol **b){
+ int i1 = (**a).index + 10000000*((**a).name[0]>'Z');
+ int i2 = (**b).index + 10000000*((**b).name[0]>'Z');
+ return i1-i2;
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x2".
+*/
+struct s_x2 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x2node *tbl; /* The data stored here */
+ struct s_x2node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x2".
+*/
+typedef struct s_x2node {
+ struct symbol *data; /* The data */
+ char *key; /* The key */
+ struct s_x2node *next; /* Next entry with the same hash */
+ struct s_x2node **from; /* Previous link */
+} x2node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x2 *x2a;
+
+/* Allocate a new associative array */
+void Symbol_init(){
+ if( x2a ) return;
+ x2a = (struct s_x2*)malloc( sizeof(struct s_x2) );
+ if( x2a ){
+ x2a->size = 128;
+ x2a->count = 0;
+ x2a->tbl = (x2node*)malloc(
+ (sizeof(x2node) + sizeof(x2node*))*128 );
+ if( x2a->tbl==0 ){
+ free(x2a);
+ x2a = 0;
+ }else{
+ int i;
+ x2a->ht = (x2node**)&(x2a->tbl[128]);
+ for(i=0; i<128; i++) x2a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int Symbol_insert(data,key)
+struct symbol *data;
+char *key;
+{
+ x2node *np;
+ int h;
+ int ph;
+
+ if( x2a==0 ) return 0;
+ ph = strhash(key);
+ h = ph & (x2a->size-1);
+ np = x2a->ht[h];
+ while( np ){
+ if( strcmp(np->key,key)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x2a->count>=x2a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x2 array;
+ array.size = size = x2a->size*2;
+ array.count = x2a->count;
+ array.tbl = (x2node*)malloc(
+ (sizeof(x2node) + sizeof(x2node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x2node**)&(array.tbl[size]);
+ for(i=0; i<size; i++) array.ht[i] = 0;
+ for(i=0; i<x2a->count; i++){
+ x2node *oldnp, *newnp;
+ oldnp = &(x2a->tbl[i]);
+ h = strhash(oldnp->key) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->key = oldnp->key;
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x2a->tbl);
+ *x2a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x2a->size-1);
+ np = &(x2a->tbl[x2a->count++]);
+ np->key = key;
+ np->data = data;
+ if( x2a->ht[h] ) x2a->ht[h]->from = &(np->next);
+ np->next = x2a->ht[h];
+ x2a->ht[h] = np;
+ np->from = &(x2a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+struct symbol *Symbol_find(key)
+char *key;
+{
+ int h;
+ x2node *np;
+
+ if( x2a==0 ) return 0;
+ h = strhash(key) & (x2a->size-1);
+ np = x2a->ht[h];
+ while( np ){
+ if( strcmp(np->key,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Return the n-th data. Return NULL if n is out of range. */
+struct symbol *Symbol_Nth(n)
+int n;
+{
+ struct symbol *data;
+ if( x2a && n>0 && n<=x2a->count ){
+ data = x2a->tbl[n-1].data;
+ }else{
+ data = 0;
+ }
+ return data;
+}
+
+/* Return the size of the array */
+int Symbol_count()
+{
+ return x2a ? x2a->count : 0;
+}
+
+/* Return an array of pointers to all data in the table.
+** The array is obtained from malloc. Return NULL if memory allocation
+** problems, or if the array is empty. */
+struct symbol **Symbol_arrayof()
+{
+ struct symbol **array;
+ int i,size;
+ if( x2a==0 ) return 0;
+ size = x2a->count;
+ array = (struct symbol **)calloc(size, sizeof(struct symbol *));
+ if( array ){
+ for(i=0; i<size; i++) array[i] = x2a->tbl[i].data;
+ }
+ return array;
+}
+
+/* Compare two configurations */
+int Configcmp(a,b)
+struct config *a;
+struct config *b;
+{
+ int x;
+ x = a->rp->index - b->rp->index;
+ if( x==0 ) x = a->dot - b->dot;
+ return x;
+}
+
+/* Compare two states */
+PRIVATE int statecmp(a,b)
+struct config *a;
+struct config *b;
+{
+ int rc;
+ for(rc=0; rc==0 && a && b; a=a->bp, b=b->bp){
+ rc = a->rp->index - b->rp->index;
+ if( rc==0 ) rc = a->dot - b->dot;
+ }
+ if( rc==0 ){
+ if( a ) rc = 1;
+ if( b ) rc = -1;
+ }
+ return rc;
+}
+
+/* Hash a state */
+PRIVATE int statehash(a)
+struct config *a;
+{
+ int h=0;
+ while( a ){
+ h = h*571 + a->rp->index*37 + a->dot;
+ a = a->bp;
+ }
+ return h;
+}
+
+/* Allocate a new state structure */
+struct state *State_new()
+{
+ struct state *new;
+ new = (struct state *)calloc(1, sizeof(struct state) );
+ MemoryCheck(new);
+ return new;
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x3".
+*/
+struct s_x3 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x3node *tbl; /* The data stored here */
+ struct s_x3node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x3".
+*/
+typedef struct s_x3node {
+ struct state *data; /* The data */
+ struct config *key; /* The key */
+ struct s_x3node *next; /* Next entry with the same hash */
+ struct s_x3node **from; /* Previous link */
+} x3node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x3 *x3a;
+
+/* Allocate a new associative array */
+void State_init(){
+ if( x3a ) return;
+ x3a = (struct s_x3*)malloc( sizeof(struct s_x3) );
+ if( x3a ){
+ x3a->size = 128;
+ x3a->count = 0;
+ x3a->tbl = (x3node*)malloc(
+ (sizeof(x3node) + sizeof(x3node*))*128 );
+ if( x3a->tbl==0 ){
+ free(x3a);
+ x3a = 0;
+ }else{
+ int i;
+ x3a->ht = (x3node**)&(x3a->tbl[128]);
+ for(i=0; i<128; i++) x3a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int State_insert(data,key)
+struct state *data;
+struct config *key;
+{
+ x3node *np;
+ int h;
+ int ph;
+
+ if( x3a==0 ) return 0;
+ ph = statehash(key);
+ h = ph & (x3a->size-1);
+ np = x3a->ht[h];
+ while( np ){
+ if( statecmp(np->key,key)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x3a->count>=x3a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x3 array;
+ array.size = size = x3a->size*2;
+ array.count = x3a->count;
+ array.tbl = (x3node*)malloc(
+ (sizeof(x3node) + sizeof(x3node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x3node**)&(array.tbl[size]);
+ for(i=0; i<size; i++) array.ht[i] = 0;
+ for(i=0; i<x3a->count; i++){
+ x3node *oldnp, *newnp;
+ oldnp = &(x3a->tbl[i]);
+ h = statehash(oldnp->key) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->key = oldnp->key;
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x3a->tbl);
+ *x3a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x3a->size-1);
+ np = &(x3a->tbl[x3a->count++]);
+ np->key = key;
+ np->data = data;
+ if( x3a->ht[h] ) x3a->ht[h]->from = &(np->next);
+ np->next = x3a->ht[h];
+ x3a->ht[h] = np;
+ np->from = &(x3a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+struct state *State_find(key)
+struct config *key;
+{
+ int h;
+ x3node *np;
+
+ if( x3a==0 ) return 0;
+ h = statehash(key) & (x3a->size-1);
+ np = x3a->ht[h];
+ while( np ){
+ if( statecmp(np->key,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Return an array of pointers to all data in the table.
+** The array is obtained from malloc. Return NULL if memory allocation
+** problems, or if the array is empty. */
+struct state **State_arrayof()
+{
+ struct state **array;
+ int i,size;
+ if( x3a==0 ) return 0;
+ size = x3a->count;
+ array = (struct state **)malloc( sizeof(struct state *)*size );
+ if( array ){
+ for(i=0; i<size; i++) array[i] = x3a->tbl[i].data;
+ }
+ return array;
+}
+
+/* Hash a configuration */
+PRIVATE int confighash(a)
+struct config *a;
+{
+ int h=0;
+ h = h*571 + a->rp->index*37 + a->dot;
+ return h;
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x4".
+*/
+struct s_x4 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x4node *tbl; /* The data stored here */
+ struct s_x4node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x4".
+*/
+typedef struct s_x4node {
+ struct config *data; /* The data */
+ struct s_x4node *next; /* Next entry with the same hash */
+ struct s_x4node **from; /* Previous link */
+} x4node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x4 *x4a;
+
+/* Allocate a new associative array */
+void Configtable_init(){
+ if( x4a ) return;
+ x4a = (struct s_x4*)malloc( sizeof(struct s_x4) );
+ if( x4a ){
+ x4a->size = 64;
+ x4a->count = 0;
+ x4a->tbl = (x4node*)malloc(
+ (sizeof(x4node) + sizeof(x4node*))*64 );
+ if( x4a->tbl==0 ){
+ free(x4a);
+ x4a = 0;
+ }else{
+ int i;
+ x4a->ht = (x4node**)&(x4a->tbl[64]);
+ for(i=0; i<64; i++) x4a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int Configtable_insert(data)
+struct config *data;
+{
+ x4node *np;
+ int h;
+ int ph;
+
+ if( x4a==0 ) return 0;
+ ph = confighash(data);
+ h = ph & (x4a->size-1);
+ np = x4a->ht[h];
+ while( np ){
+ if( Configcmp(np->data,data)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x4a->count>=x4a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x4 array;
+ array.size = size = x4a->size*2;
+ array.count = x4a->count;
+ array.tbl = (x4node*)malloc(
+ (sizeof(x4node) + sizeof(x4node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x4node**)&(array.tbl[size]);
+ for(i=0; i<size; i++) array.ht[i] = 0;
+ for(i=0; i<x4a->count; i++){
+ x4node *oldnp, *newnp;
+ oldnp = &(x4a->tbl[i]);
+ h = confighash(oldnp->data) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x4a->tbl);
+ *x4a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x4a->size-1);
+ np = &(x4a->tbl[x4a->count++]);
+ np->data = data;
+ if( x4a->ht[h] ) x4a->ht[h]->from = &(np->next);
+ np->next = x4a->ht[h];
+ x4a->ht[h] = np;
+ np->from = &(x4a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+struct config *Configtable_find(key)
+struct config *key;
+{
+ int h;
+ x4node *np;
+
+ if( x4a==0 ) return 0;
+ h = confighash(key) & (x4a->size-1);
+ np = x4a->ht[h];
+ while( np ){
+ if( Configcmp(np->data,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Remove all data from the table. Pass each data to the function "f"
+** as it is removed. ("f" may be null to avoid this step.) */
+void Configtable_clear(f)
+int(*f)(/* struct config * */);
+{
+ int i;
+ if( x4a==0 || x4a->count==0 ) return;
+ if( f ) for(i=0; i<x4a->count; i++) (*f)(x4a->tbl[i].data);
+ for(i=0; i<x4a->size; i++) x4a->ht[i] = 0;
+ x4a->count = 0;
+ return;
+}
+
+/* LLVM LOCAL begin */
+
+#include <unistd.h>
+#include <sys/wait.h>
+#include <errno.h>
+/* llvm-test supports only running program once,
+ * we need to run it multiple times, because it only accepts
+ * one input a time, and has a global state */
+int main(int argc, char **argv)
+{
+ int j,i = 0;
+ for(j=0;j<20;j++) {
+ /* test finishes too fast, run more times to get
+ * meaningful timings */
+ for(i=1;i<argc;i++) {
+ int status;
+ pid_t p = fork();
+ if(p == 0) {
+ char *argv_child[] = {"lemon-child","-s",argv[i],NULL};
+ /* child */
+ fprintf(stdout,"Processing %s\n",argv[i]);
+ exit( lemon_main(3, argv_child) );
+ }
+ while(wait(&status) == -1 && errno == EINTR) {}
+ if(status) {
+ fprintf(stderr,"Error while running on: %s\n",argv[i]);
+ }
+ }
+ }
+ return 0;
+}
+
+/* LLVM LOCAL end */
Added: test-suite/trunk/MultiSource/Applications/lemon/lempar.c
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/lempar.c?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/lempar.c (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/lempar.c Wed Mar 26 12:03:57 2008
@@ -0,0 +1,769 @@
+/* Driver template for the LEMON parser generator.
+** The author disclaims copyright to this source code.
+*/
+/* First off, code is include which follows the "include" declaration
+** in the input file. */
+#include <stdio.h>
+%%
+/* Next is all token values, in a form suitable for use by makeheaders.
+** This section will be null unless lemon is run with the -m switch.
+*/
+/*
+** These constants (all generated automatically by the parser generator)
+** specify the various kinds of tokens (terminals) that the parser
+** understands.
+**
+** Each symbol here is a terminal symbol in the grammar.
+*/
+%%
+/* Make sure the INTERFACE macro is defined.
+*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/* The next thing included is series of defines which control
+** various aspects of the generated parser.
+** YYCODETYPE is the data type used for storing terminal
+** and nonterminal numbers. "unsigned char" is
+** used if there are fewer than 250 terminals
+** and nonterminals. "int" is used otherwise.
+** YYNOCODE is a number of type YYCODETYPE which corresponds
+** to no legal terminal or nonterminal number. This
+** number is used to fill in empty slots of the hash
+** table.
+** YYFALLBACK If defined, this indicates that one or more tokens
+** have fall-back values which should be used if the
+** original value of the token will not parse.
+** YYACTIONTYPE is the data type used for storing terminal
+** and nonterminal numbers. "unsigned char" is
+** used if there are fewer than 250 rules and
+** states combined. "int" is used otherwise.
+** ParseTOKENTYPE is the data type used for minor tokens given
+** directly to the parser from the tokenizer.
+** YYMINORTYPE is the data type used for all minor tokens.
+** This is typically a union of many types, one of
+** which is ParseTOKENTYPE. The entry in the union
+** for base tokens is called "yy0".
+** YYSTACKDEPTH is the maximum depth of the parser's stack. If
+** zero the stack is dynamically sized using realloc()
+** ParseARG_SDECL A static variable declaration for the %extra_argument
+** ParseARG_PDECL A parameter declaration for the %extra_argument
+** ParseARG_STORE Code to store %extra_argument into yypParser
+** ParseARG_FETCH Code to extract %extra_argument from yypParser
+** YYNSTATE the combined number of states.
+** YYNRULE the number of rules in the grammar
+** YYERRORSYMBOL is the code number of the error symbol. If not
+** defined, then do no error processing.
+*/
+%%
+#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
+#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1)
+#define YY_ERROR_ACTION (YYNSTATE+YYNRULE)
+
+/* Next are that 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 < YYNSTATE Shift N. That is, push the lookahead
+** token onto the stack and goto state N.
+**
+** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE.
+**
+** N == YYNSTATE+YYNRULE A syntax error has occurred.
+**
+** N == YYNSTATE+YYNRULE+1 The parser accepts its input.
+**
+** N == YYNSTATE+YYNRULE+2 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
+**
+** yy_action[ yy_shift_ofst[S] + X ]
+**
+** If the index value yy_shift_ofst[S]+X is out of range or if the value
+** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S]
+** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table
+** and that yy_default[S] should be used instead.
+**
+** The formula above is 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.
+*/
+%%
+#define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0]))
+
+/* The next table maps tokens into fallback tokens. If a construct
+** like the following:
+**
+** %fallback ID X Y Z.
+**
+** appears in the grammer, 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.
+*/
+#ifdef YYFALLBACK
+static const YYCODETYPE yyFallback[] = {
+%%
+};
+#endif /* 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.
+*/
+struct yyStackEntry {
+ int stateno; /* The state-number */
+ int major; /* The major token value. This is the code
+ ** number for the token at this stack level */
+ YYMINORTYPE minor; /* The user-supplied minor token value. This
+ ** is the value of the token */
+};
+typedef struct yyStackEntry yyStackEntry;
+
+/* The state of the parser is completely contained in an instance of
+** the following structure */
+struct yyParser {
+ int yyidx; /* Index of top element in stack */
+ int yyerrcnt; /* Shifts left before out of the error */
+ ParseARG_SDECL /* A place to hold %extra_argument */
+#if YYSTACKDEPTH<=0
+ int yystksz; /* Current side of the stack */
+ yyStackEntry *yystack; /* The parser's stack */
+#else
+ yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */
+#endif
+};
+typedef struct yyParser yyParser;
+
+#ifndef NDEBUG
+#include <stdio.h>
+static FILE *yyTraceFILE = 0;
+static char *yyTracePrompt = 0;
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/*
+** 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 FILE* 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. If NULL, then tracing is
+** turned off.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void ParseTrace(FILE *TraceFILE, char *zTracePrompt){
+ yyTraceFILE = TraceFILE;
+ yyTracePrompt = zTracePrompt;
+ if( yyTraceFILE==0 ) yyTracePrompt = 0;
+ else if( yyTracePrompt==0 ) yyTraceFILE = 0;
+}
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing shifts, the names of all terminals and nonterminals
+** are required. The following table supplies these names */
+static const char *const yyTokenName[] = {
+%%
+};
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing reduce actions, the names of all rules are required.
+*/
+static const char *const yyRuleName[] = {
+%%
+};
+#endif /* NDEBUG */
+
+
+#if YYSTACKDEPTH<=0
+/*
+** Try to increase the size of the parser stack.
+*/
+static void yyGrowStack(yyParser *p){
+ int newSize;
+ yyStackEntry *pNew;
+
+ newSize = p->yystksz*2 + 100;
+ pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
+ if( pNew ){
+ p->yystack = pNew;
+ p->yystksz = newSize;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sStack grows to %d entries!\n",
+ yyTracePrompt, p->yystksz);
+ }
+#endif
+ }
+}
+#endif
+
+/*
+** This function allocates a new parser.
+** The only argument is a pointer to a function which works like
+** malloc.
+**
+** Inputs:
+** A pointer to the function used to allocate memory.
+**
+** Outputs:
+** A pointer to a parser. This pointer is used in subsequent calls
+** to Parse and ParseFree.
+*/
+void *ParseAlloc(void *(*mallocProc)(size_t)){
+ yyParser *pParser;
+ pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) );
+ if( pParser ){
+ pParser->yyidx = -1;
+#if YYSTACKDEPTH<=0
+ yyGrowStack(pParser);
+#endif
+ }
+ return pParser;
+}
+
+/* The following function deletes the 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.
+*/
+static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){
+ 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.
+ */
+%%
+ 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.
+**
+** Return the major token number for the symbol popped.
+*/
+static int yy_pop_parser_stack(yyParser *pParser){
+ YYCODETYPE yymajor;
+ yyStackEntry *yytos = &pParser->yystack[pParser->yyidx];
+
+ if( pParser->yyidx<0 ) return 0;
+#ifndef NDEBUG
+ if( yyTraceFILE && pParser->yyidx>=0 ){
+ fprintf(yyTraceFILE,"%sPopping %s\n",
+ yyTracePrompt,
+ yyTokenName[yytos->major]);
+ }
+#endif
+ yymajor = yytos->major;
+ yy_destructor( yymajor, &yytos->minor);
+ pParser->yyidx--;
+ return yymajor;
+}
+
+/*
+** Deallocate and destroy a parser. Destructors are all called for
+** all stack elements before shutting the parser down.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser. This should be a pointer
+** obtained from ParseAlloc.
+** <li> A pointer to a function used to reclaim memory obtained
+** from malloc.
+** </ul>
+*/
+void ParseFree(
+ void *p, /* The parser to be deleted */
+ void (*freeProc)(void*) /* Function used to reclaim memory */
+){
+ yyParser *pParser = (yyParser*)p;
+ if( pParser==0 ) return;
+ while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser);
+#if YYSTACKDEPTH<=0
+ free(pParser->yystack);
+#endif
+ (*freeProc)((void*)pParser);
+}
+
+/*
+** Find the appropriate action for a parser given the terminal
+** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead. If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_shift_action(
+ yyParser *pParser, /* The parser */
+ YYCODETYPE iLookAhead /* The look-ahead token */
+){
+ int i;
+ int stateno = pParser->yystack[pParser->yyidx].stateno;
+
+ if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){
+ return yy_default[stateno];
+ }
+ assert( iLookAhead!=YYNOCODE );
+ i += iLookAhead;
+ if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
+ if( iLookAhead>0 ){
+#ifdef YYFALLBACK
+ int iFallback; /* Fallback token */
+ if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
+ && (iFallback = yyFallback[iLookAhead])!=0 ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
+ yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
+ }
+#endif
+ return yy_find_shift_action(pParser, iFallback);
+ }
+#endif
+#ifdef YYWILDCARD
+ {
+ int j = i - iLookAhead + YYWILDCARD;
+ if( j>=0 && j<YY_SZ_ACTTAB && yy_lookahead[j]==YYWILDCARD ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
+ yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]);
+ }
+#endif /* NDEBUG */
+ return yy_action[j];
+ }
+ }
+#endif /* YYWILDCARD */
+ }
+ return yy_default[stateno];
+ }else{
+ return yy_action[i];
+ }
+}
+
+/*
+** Find the appropriate action for a parser given the non-terminal
+** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead. If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_reduce_action(
+ int stateno, /* Current state number */
+ YYCODETYPE iLookAhead /* The look-ahead token */
+){
+ int i;
+ assert( stateno<=YY_REDUCE_MAX );
+ i = yy_reduce_ofst[stateno];
+ assert( i!=YY_REDUCE_USE_DFLT );
+ assert( iLookAhead!=YYNOCODE );
+ i += iLookAhead;
+ assert( i>=0 && i<YY_SZ_ACTTAB );
+ assert( yy_lookahead[i]==iLookAhead );
+ return yy_action[i];
+}
+
+/*
+** The following routine is called if the stack overflows.
+*/
+static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){
+ ParseARG_FETCH;
+ yypParser->yyidx--;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will execute if the parser
+ ** stack every overflows */
+%%
+ ParseARG_STORE; /* Suppress warning about unused %extra_argument var */
+}
+
+/*
+** Perform a shift action.
+*/
+static void yy_shift(
+ yyParser *yypParser, /* The parser to be shifted */
+ int yyNewState, /* The new state to shift in */
+ int yyMajor, /* The major token to shift in */
+ YYMINORTYPE *yypMinor /* Pointer ot the minor token to shift in */
+){
+ yyStackEntry *yytos;
+ yypParser->yyidx++;
+#if YYSTACKDEPTH>0
+ if( yypParser->yyidx>=YYSTACKDEPTH ){
+ yyStackOverflow(yypParser, yypMinor);
+ return;
+ }
+#else
+ if( yypParser->yyidx>=yypParser->yystksz ){
+ yyGrowStack(yypParser);
+ if( yypParser->yyidx>=yypParser->yystksz ){
+ yyStackOverflow(yypParser, yypMinor);
+ return;
+ }
+ }
+#endif
+ yytos = &yypParser->yystack[yypParser->yyidx];
+ yytos->stateno = yyNewState;
+ yytos->major = yyMajor;
+ yytos->minor = *yypMinor;
+#ifndef NDEBUG
+ if( yyTraceFILE && yypParser->yyidx>0 ){
+ int i;
+ fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
+ fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
+ for(i=1; i<=yypParser->yyidx; i++)
+ fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
+ fprintf(yyTraceFILE,"\n");
+ }
+#endif
+}
+
+/* The following table contains information about every rule that
+** is used during the reduce.
+*/
+static const struct {
+ YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
+ unsigned char nrhs; /* Number of right-hand side symbols in the rule */
+} yyRuleInfo[] = {
+%%
+};
+
+static void yy_accept(yyParser*); /* Forward Declaration */
+
+/*
+** Perform a reduce action and the shift that must immediately
+** follow the reduce.
+*/
+static void yy_reduce(
+ yyParser *yypParser, /* The parser */
+ int yyruleno /* Number of the rule by which to reduce */
+){
+ int yygoto; /* The next state */
+ int yyact; /* The next action */
+ YYMINORTYPE yygotominor; /* The LHS of the rule reduced */
+ yyStackEntry *yymsp; /* The top of the parser's stack */
+ int yysize; /* Amount to pop the stack */
+ ParseARG_FETCH;
+ yymsp = &yypParser->yystack[yypParser->yyidx];
+#ifndef NDEBUG
+ if( yyTraceFILE && yyruleno>=0
+ && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
+ fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt,
+ yyRuleName[yyruleno]);
+ }
+#endif /* NDEBUG */
+
+ /* Silence complaints from purify about yygotominor being uninitialized
+ ** in some cases when it is copied into the stack after the following
+ ** switch. yygotominor is uninitialized when a rule reduces that does
+ ** not set the value of its left-hand side nonterminal. Leaving the
+ ** value of the nonterminal uninitialized is utterly harmless as long
+ ** as the value is never used. So really the only thing this code
+ ** accomplishes is to quieten purify.
+ **
+ ** 2007-01-16: The wireshark project (www.wireshark.org) reports that
+ ** without this code, their parser segfaults. I'm not sure what there
+ ** parser is doing to make this happen. This is the second bug report
+ ** from wireshark this week. Clearly they are stressing Lemon in ways
+ ** that it has not been previously stressed... (SQLite ticket #2172)
+ */
+ memset(&yygotominor, 0, sizeof(yygotominor));
+
+
+ switch( yyruleno ){
+ /* Beginning here are the reduction cases. A typical example
+ ** follows:
+ ** case 0:
+ ** #line <lineno> <grammarfile>
+ ** { ... } // User supplied code
+ ** #line <lineno> <thisfile>
+ ** break;
+ */
+%%
+ };
+ yygoto = yyRuleInfo[yyruleno].lhs;
+ yysize = yyRuleInfo[yyruleno].nrhs;
+ yypParser->yyidx -= yysize;
+ yyact = yy_find_reduce_action(yymsp[-yysize].stateno,yygoto);
+ if( yyact < YYNSTATE ){
+#ifdef NDEBUG
+ /* If we are not debugging and the reduce action popped at least
+ ** one element off the stack, then we can push the new element back
+ ** onto the stack here, and skip the stack overflow test in yy_shift().
+ ** That gives a significant speed improvement. */
+ if( yysize ){
+ yypParser->yyidx++;
+ yymsp -= yysize-1;
+ yymsp->stateno = yyact;
+ yymsp->major = yygoto;
+ yymsp->minor = yygotominor;
+ }else
+#endif
+ {
+ yy_shift(yypParser,yyact,yygoto,&yygotominor);
+ }
+ }else{
+ assert( yyact == YYNSTATE + YYNRULE + 1 );
+ yy_accept(yypParser);
+ }
+}
+
+/*
+** The following code executes when the parse fails
+*/
+static void yy_parse_failed(
+ yyParser *yypParser /* The parser */
+){
+ ParseARG_FETCH;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser fails */
+%%
+ ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/*
+** The following code executes when a syntax error first occurs.
+*/
+static void yy_syntax_error(
+ yyParser *yypParser, /* The parser */
+ int yymajor, /* The major type of the error token */
+ YYMINORTYPE yyminor /* The minor type of the error token */
+){
+ ParseARG_FETCH;
+#define TOKEN (yyminor.yy0)
+%%
+ ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/*
+** The following is executed when the parser accepts
+*/
+static void yy_accept(
+ yyParser *yypParser /* The parser */
+){
+ ParseARG_FETCH;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser accepts */
+%%
+ ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/* 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.
+*/
+void Parse(
+ void *yyp, /* The parser */
+ int yymajor, /* The major token code number */
+ ParseTOKENTYPE yyminor /* The value for the token */
+ ParseARG_PDECL /* Optional %extra_argument parameter */
+){
+ YYMINORTYPE yyminorunion;
+ int yyact; /* The parser action. */
+ int yyendofinput; /* True if we are at the end of input */
+#ifdef YYERRORSYMBOL
+ int yyerrorhit = 0; /* True if yymajor has invoked an error */
+#endif
+ yyParser *yypParser; /* The parser */
+
+ /* (re)initialize the parser, if necessary */
+ yypParser = (yyParser*)yyp;
+ if( yypParser->yyidx<0 ){
+#if YYSTACKDEPTH<=0
+ if( yypParser->yystksz <=0 ){
+ memset(&yyminorunion, 0, sizeof(yyminorunion));
+ yyStackOverflow(yypParser, &yyminorunion);
+ return;
+ }
+#endif
+ yypParser->yyidx = 0;
+ yypParser->yyerrcnt = -1;
+ yypParser->yystack[0].stateno = 0;
+ yypParser->yystack[0].major = 0;
+ }
+ yyminorunion.yy0 = yyminor;
+ yyendofinput = (yymajor==0);
+ ParseARG_STORE;
+
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
+ }
+#endif
+
+ do{
+ yyact = yy_find_shift_action(yypParser,yymajor);
+ if( yyact<YYNSTATE ){
+ assert( !yyendofinput ); /* Impossible to shift the $ token */
+ yy_shift(yypParser,yyact,yymajor,&yyminorunion);
+ yypParser->yyerrcnt--;
+ yymajor = YYNOCODE;
+ }else if( yyact < YYNSTATE + YYNRULE ){
+ yy_reduce(yypParser,yyact-YYNSTATE);
+ }else{
+ assert( yyact == YY_ERROR_ACTION );
+#ifdef YYERRORSYMBOL
+ int yymx;
+#endif
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
+ }
+#endif
+#ifdef 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( yypParser->yyerrcnt<0 ){
+ yy_syntax_error(yypParser,yymajor,yyminorunion);
+ }
+ yymx = yypParser->yystack[yypParser->yyidx].major;
+ if( yymx==YYERRORSYMBOL || yyerrorhit ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sDiscard input token %s\n",
+ yyTracePrompt,yyTokenName[yymajor]);
+ }
+#endif
+ yy_destructor(yymajor,&yyminorunion);
+ yymajor = YYNOCODE;
+ }else{
+ while(
+ yypParser->yyidx >= 0 &&
+ yymx != YYERRORSYMBOL &&
+ (yyact = yy_find_reduce_action(
+ yypParser->yystack[yypParser->yyidx].stateno,
+ YYERRORSYMBOL)) >= YYNSTATE
+ ){
+ yy_pop_parser_stack(yypParser);
+ }
+ if( yypParser->yyidx < 0 || yymajor==0 ){
+ yy_destructor(yymajor,&yyminorunion);
+ yy_parse_failed(yypParser);
+ yymajor = YYNOCODE;
+ }else if( yymx!=YYERRORSYMBOL ){
+ YYMINORTYPE u2;
+ u2.YYERRSYMDT = 0;
+ yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
+ }
+ }
+ yypParser->yyerrcnt = 3;
+ yyerrorhit = 1;
+#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( yypParser->yyerrcnt<=0 ){
+ yy_syntax_error(yypParser,yymajor,yyminorunion);
+ }
+ yypParser->yyerrcnt = 3;
+ yy_destructor(yymajor,&yyminorunion);
+ if( yyendofinput ){
+ yy_parse_failed(yypParser);
+ }
+ yymajor = YYNOCODE;
+#endif
+ }
+ }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
+ return;
+}
Added: test-suite/trunk/MultiSource/Applications/lemon/lighttpd_COPYING
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/lighttpd_COPYING?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/lighttpd_COPYING (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/lighttpd_COPYING Wed Mar 26 12:03:57 2008
@@ -0,0 +1,31 @@
+
+
+Copyright (c) 2004, Jan Kneschke, incremental
+ All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+- Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+- Neither the name of the 'incremental' nor the names of its contributors may
+ be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+
Added: test-suite/trunk/MultiSource/Applications/lemon/lighttpd_configparser.y
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/lighttpd_configparser.y?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/lighttpd_configparser.y (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/lighttpd_configparser.y Wed Mar 26 12:03:57 2008
@@ -0,0 +1,564 @@
+%token_prefix TK_
+%extra_argument {config_t *ctx}
+%name configparser
+
+%include {
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "configfile.h"
+#include "buffer.h"
+#include "array.h"
+
+static void configparser_push(config_t *ctx, data_config *dc, int isnew) {
+ if (isnew) {
+ dc->context_ndx = ctx->all_configs->used;
+ assert(dc->context_ndx > ctx->current->context_ndx);
+ array_insert_unique(ctx->all_configs, (data_unset *)dc);
+ dc->parent = ctx->current;
+ array_insert_unique(dc->parent->childs, (data_unset *)dc);
+ }
+ array_insert_unique(ctx->configs_stack, (data_unset *)ctx->current);
+ ctx->current = dc;
+}
+
+static data_config *configparser_pop(config_t *ctx) {
+ data_config *old = ctx->current;
+ ctx->current = (data_config *) array_pop(ctx->configs_stack);
+ return old;
+}
+
+/* return a copied variable */
+static data_unset *configparser_get_variable(config_t *ctx, const buffer *key) {
+ data_unset *du;
+ data_config *dc;
+
+#if 0
+ fprintf(stderr, "get var %s\n", key->ptr);
+#endif
+ for (dc = ctx->current; dc; dc = dc->parent) {
+#if 0
+ fprintf(stderr, "get var on block: %s\n", dc->key->ptr);
+ array_print(dc->value, 0);
+#endif
+ if (NULL != (du = array_get_element(dc->value, key->ptr))) {
+ return du->copy(du);
+ }
+ }
+ return NULL;
+}
+
+/* op1 is to be eat/return by this function if success, op1->key is not cared
+ op2 is left untouch, unreferenced
+ */
+data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) {
+ /* type mismatch */
+ if (op1->type != op2->type) {
+ if (op1->type == TYPE_STRING && op2->type == TYPE_INTEGER) {
+ data_string *ds = (data_string *)op1;
+ buffer_append_long(ds->value, ((data_integer*)op2)->value);
+ return op1;
+ } else if (op1->type == TYPE_INTEGER && op2->type == TYPE_STRING) {
+ data_string *ds = data_string_init();
+ buffer_append_long(ds->value, ((data_integer*)op1)->value);
+ buffer_append_string_buffer(ds->value, ((data_string*)op2)->value);
+ op1->free(op1);
+ return (data_unset *)ds;
+ } else {
+ fprintf(stderr, "data type mismatch, cannot be merge\n");
+ return NULL;
+ }
+ }
+
+ switch (op1->type) {
+ case TYPE_STRING:
+ buffer_append_string_buffer(((data_string *)op1)->value, ((data_string *)op2)->value);
+ break;
+ case TYPE_INTEGER:
+ ((data_integer *)op1)->value += ((data_integer *)op2)->value;
+ break;
+ case TYPE_ARRAY: {
+ array *dst = ((data_array *)op1)->value;
+ array *src = ((data_array *)op2)->value;
+ data_unset *du;
+ size_t i;
+
+ for (i = 0; i < src->used; i ++) {
+ du = (data_unset *)src->data[i];
+ if (du) {
+ array_insert_unique(dst, du->copy(du));
+ }
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+ return op1;
+}
+
+}
+
+%parse_failure {
+ ctx->ok = 0;
+}
+
+input ::= metalines.
+metalines ::= metalines metaline.
+metalines ::= .
+metaline ::= varline.
+metaline ::= global.
+metaline ::= condlines(A) EOL. { A = NULL; }
+metaline ::= include.
+metaline ::= include_shell.
+metaline ::= EOL.
+
+%type value {data_unset *}
+%type expression {data_unset *}
+%type aelement {data_unset *}
+%type condline {data_config *}
+%type condlines {data_config *}
+%type global {data_config *}
+%type aelements {array *}
+%type array {array *}
+%type key {buffer *}
+%type stringop {buffer *}
+
+%type cond {config_cond_t }
+
+%destructor value { $$->free($$); }
+%destructor expression { $$->free($$); }
+%destructor aelement { $$->free($$); }
+%destructor aelements { array_free($$); }
+%destructor array { array_free($$); }
+%destructor key { buffer_free($$); }
+%destructor stringop { buffer_free($$); }
+
+%token_type {buffer *}
+%token_destructor { buffer_free($$); }
+
+varline ::= key(A) ASSIGN expression(B). {
+ if (ctx->ok) {
+ buffer_copy_string_buffer(B->key, A);
+ if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) {
+ fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n",
+ ctx->current->context_ndx,
+ ctx->current->key->ptr, A->ptr);
+ ctx->ok = 0;
+ } else if (NULL == array_get_element(ctx->current->value, B->key->ptr)) {
+ array_insert_unique(ctx->current->value, B);
+ B = NULL;
+ } else {
+ fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n",
+ ctx->current->context_ndx,
+ ctx->current->key->ptr, B->key->ptr);
+ ctx->ok = 0;
+ B->free(B);
+ B = NULL;
+ }
+ }
+ buffer_free(A);
+ A = NULL;
+}
+
+varline ::= key(A) APPEND expression(B). {
+ array *vars = ctx->current->value;
+ data_unset *du;
+
+ if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) {
+ fprintf(stderr, "Appending env variable is not supported in conditional %d %s: %s\n",
+ ctx->current->context_ndx,
+ ctx->current->key->ptr, A->ptr);
+ ctx->ok = 0;
+ } else if (NULL != (du = array_get_element(vars, A->ptr))) {
+ /* exists in current block */
+ du = configparser_merge_data(du, B);
+ if (NULL == du) {
+ ctx->ok = 0;
+ }
+ else {
+ buffer_copy_string_buffer(du->key, A);
+ array_replace(vars, du);
+ }
+ B->free(B);
+ } else if (NULL != (du = configparser_get_variable(ctx, A))) {
+ du = configparser_merge_data(du, B);
+ if (NULL == du) {
+ ctx->ok = 0;
+ du->free(du);
+ }
+ else {
+ buffer_copy_string_buffer(du->key, A);
+ array_insert_unique(ctx->current->value, du);
+ }
+ B->free(B);
+ } else {
+ buffer_copy_string_buffer(B->key, A);
+ array_insert_unique(ctx->current->value, B);
+ }
+ buffer_free(A);
+ A = NULL;
+ B = NULL;
+}
+
+key(A) ::= LKEY(B). {
+ if (strchr(B->ptr, '.') == NULL) {
+ A = buffer_init_string("var.");
+ buffer_append_string_buffer(A, B);
+ buffer_free(B);
+ B = NULL;
+ } else {
+ A = B;
+ B = NULL;
+ }
+}
+
+expression(A) ::= expression(B) PLUS value(C). {
+ A = configparser_merge_data(B, C);
+ if (NULL == A) {
+ ctx->ok = 0;
+ }
+ B = NULL;
+ C->free(C);
+ C = NULL;
+}
+
+expression(A) ::= value(B). {
+ A = B;
+ B = NULL;
+}
+
+value(A) ::= key(B). {
+ A = NULL;
+ if (strncmp(B->ptr, "env.", sizeof("env.") - 1) == 0) {
+ char *env;
+
+ if (NULL != (env = getenv(B->ptr + 4))) {
+ data_string *ds;
+ ds = data_string_init();
+ buffer_append_string(ds->value, env);
+ A = (data_unset *)ds;
+ }
+ else {
+ fprintf(stderr, "Undefined env variable: %s\n", B->ptr + 4);
+ ctx->ok = 0;
+ }
+ } else if (NULL == (A = configparser_get_variable(ctx, B))) {
+ fprintf(stderr, "Undefined config variable: %s\n", B->ptr);
+ ctx->ok = 0;
+ }
+ if (!A) {
+ /* make a dummy so it won't crash */
+ A = (data_unset *)data_string_init();
+ }
+ buffer_free(B);
+ B = NULL;
+}
+
+value(A) ::= STRING(B). {
+ A = (data_unset *)data_string_init();
+ buffer_copy_string_buffer(((data_string *)(A))->value, B);
+ buffer_free(B);
+ B = NULL;
+}
+
+value(A) ::= INTEGER(B). {
+ A = (data_unset *)data_integer_init();
+ ((data_integer *)(A))->value = strtol(B->ptr, NULL, 10);
+ buffer_free(B);
+ B = NULL;
+}
+value(A) ::= array(B). {
+ A = (data_unset *)data_array_init();
+ array_free(((data_array *)(A))->value);
+ ((data_array *)(A))->value = B;
+ B = NULL;
+}
+array(A) ::= LPARAN RPARAN. {
+ A = array_init();
+}
+array(A) ::= LPARAN aelements(B) RPARAN. {
+ A = B;
+ B = NULL;
+}
+
+aelements(A) ::= aelements(C) COMMA aelement(B). {
+ if (buffer_is_empty(B->key) ||
+ NULL == array_get_element(C, B->key->ptr)) {
+ array_insert_unique(C, B);
+ B = NULL;
+ } else {
+ fprintf(stderr, "Duplicate array-key: %s\n",
+ B->key->ptr);
+ ctx->ok = 0;
+ B->free(B);
+ B = NULL;
+ }
+
+ A = C;
+ C = NULL;
+}
+
+aelements(A) ::= aelements(C) COMMA. {
+ A = C;
+ C = NULL;
+}
+
+aelements(A) ::= aelement(B). {
+ A = array_init();
+ array_insert_unique(A, B);
+ B = NULL;
+}
+
+aelement(A) ::= expression(B). {
+ A = B;
+ B = NULL;
+}
+aelement(A) ::= stringop(B) ARRAY_ASSIGN expression(C). {
+ buffer_copy_string_buffer(C->key, B);
+ buffer_free(B);
+ B = NULL;
+
+ A = C;
+ C = NULL;
+}
+
+eols ::= EOL.
+eols ::= .
+
+globalstart ::= GLOBAL. {
+ data_config *dc;
+ dc = (data_config *)array_get_element(ctx->srv->config_context, "global");
+ assert(dc);
+ configparser_push(ctx, dc, 0);
+}
+
+global(A) ::= globalstart LCURLY metalines RCURLY. {
+ data_config *cur;
+
+ cur = ctx->current;
+ configparser_pop(ctx);
+
+ assert(cur && ctx->current);
+
+ A = cur;
+}
+
+condlines(A) ::= condlines(B) eols ELSE condline(C). {
+ assert(B->context_ndx < C->context_ndx);
+ C->prev = B;
+ B->next = C;
+ A = C;
+ B = NULL;
+ C = NULL;
+}
+
+condlines(A) ::= condline(B). {
+ A = B;
+ B = NULL;
+}
+
+condline(A) ::= context LCURLY metalines RCURLY. {
+ data_config *cur;
+
+ cur = ctx->current;
+ configparser_pop(ctx);
+
+ assert(cur && ctx->current);
+
+ A = cur;
+}
+
+context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expression(D). {
+ data_config *dc;
+ buffer *b, *rvalue, *op;
+
+ if (ctx->ok && D->type != TYPE_STRING) {
+ fprintf(stderr, "rvalue must be string");
+ ctx->ok = 0;
+ }
+
+ switch(E) {
+ case CONFIG_COND_NE:
+ op = buffer_init_string("!=");
+ break;
+ case CONFIG_COND_EQ:
+ op = buffer_init_string("==");
+ break;
+ case CONFIG_COND_NOMATCH:
+ op = buffer_init_string("!~");
+ break;
+ case CONFIG_COND_MATCH:
+ op = buffer_init_string("=~");
+ break;
+ default:
+ assert(0);
+ return;
+ }
+
+ b = buffer_init();
+ buffer_copy_string_buffer(b, ctx->current->key);
+ buffer_append_string(b, "/");
+ buffer_append_string_buffer(b, B);
+ buffer_append_string_buffer(b, C);
+ buffer_append_string_buffer(b, op);
+ rvalue = ((data_string*)D)->value;
+ buffer_append_string_buffer(b, rvalue);
+
+ if (NULL != (dc = (data_config *)array_get_element(ctx->all_configs, b->ptr))) {
+ configparser_push(ctx, dc, 0);
+ } else {
+ struct {
+ comp_key_t comp;
+ char *comp_key;
+ size_t len;
+ } comps[] = {
+ { COMP_SERVER_SOCKET, CONST_STR_LEN("SERVER[\"socket\"]" ) },
+ { COMP_HTTP_URL, CONST_STR_LEN("HTTP[\"url\"]" ) },
+ { COMP_HTTP_HOST, CONST_STR_LEN("HTTP[\"host\"]" ) },
+ { COMP_HTTP_REFERER, CONST_STR_LEN("HTTP[\"referer\"]" ) },
+ { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"useragent\"]" ) },
+ { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"user-agent\"]" ) },
+ { COMP_HTTP_COOKIE, CONST_STR_LEN("HTTP[\"cookie\"]" ) },
+ { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remoteip\"]" ) },
+ { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remote-ip\"]" ) },
+ { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"querystring\"]") },
+ { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"query-string\"]") },
+ { COMP_HTTP_REQUEST_METHOD, CONST_STR_LEN("HTTP[\"request-method\"]") },
+ { COMP_HTTP_SCHEME, CONST_STR_LEN("HTTP[\"scheme\"]" ) },
+ { COMP_UNSET, NULL, 0 },
+ };
+ size_t i;
+
+ dc = data_config_init();
+
+ buffer_copy_string_buffer(dc->key, b);
+ buffer_copy_string_buffer(dc->op, op);
+ buffer_copy_string_buffer(dc->comp_key, B);
+ buffer_append_string_len(dc->comp_key, CONST_STR_LEN("[\""));
+ buffer_append_string_buffer(dc->comp_key, C);
+ buffer_append_string_len(dc->comp_key, CONST_STR_LEN("\"]"));
+ dc->cond = E;
+
+ for (i = 0; comps[i].comp_key; i ++) {
+ if (buffer_is_equal_string(
+ dc->comp_key, comps[i].comp_key, comps[i].len)) {
+ dc->comp = comps[i].comp;
+ break;
+ }
+ }
+ if (COMP_UNSET == dc->comp) {
+ fprintf(stderr, "error comp_key %s", dc->comp_key->ptr);
+ ctx->ok = 0;
+ }
+
+ switch(E) {
+ case CONFIG_COND_NE:
+ case CONFIG_COND_EQ:
+ dc->string = buffer_init_buffer(rvalue);
+ break;
+ case CONFIG_COND_NOMATCH:
+ case CONFIG_COND_MATCH: {
+#ifdef HAVE_PCRE_H
+ const char *errptr;
+ int erroff;
+
+ if (NULL == (dc->regex =
+ pcre_compile(rvalue->ptr, 0, &errptr, &erroff, NULL))) {
+ dc->string = buffer_init_string(errptr);
+ dc->cond = CONFIG_COND_UNSET;
+
+ fprintf(stderr, "parsing regex failed: %s -> %s at offset %d\n",
+ rvalue->ptr, errptr, erroff);
+
+ ctx->ok = 0;
+ } else if (NULL == (dc->regex_study =
+ pcre_study(dc->regex, 0, &errptr)) &&
+ errptr != NULL) {
+ fprintf(stderr, "studying regex failed: %s -> %s\n",
+ rvalue->ptr, errptr);
+ ctx->ok = 0;
+ } else {
+ dc->string = buffer_init_buffer(rvalue);
+ }
+#else
+ fprintf(stderr, "can't handle '$%s[%s] =~ ...' as you compiled without pcre support. \n"
+ "(perhaps just a missing pcre-devel package ?) \n",
+ B->ptr, C->ptr);
+ ctx->ok = 0;
+#endif
+ break;
+ }
+
+ default:
+ fprintf(stderr, "unknown condition for $%s[%s]\n",
+ B->ptr, C->ptr);
+ ctx->ok = 0;
+ break;
+ }
+
+ configparser_push(ctx, dc, 1);
+ }
+
+ buffer_free(b);
+ buffer_free(op);
+ buffer_free(B);
+ B = NULL;
+ buffer_free(C);
+ C = NULL;
+ D->free(D);
+ D = NULL;
+}
+cond(A) ::= EQ. {
+ A = CONFIG_COND_EQ;
+}
+cond(A) ::= MATCH. {
+ A = CONFIG_COND_MATCH;
+}
+cond(A) ::= NE. {
+ A = CONFIG_COND_NE;
+}
+cond(A) ::= NOMATCH. {
+ A = CONFIG_COND_NOMATCH;
+}
+
+stringop(A) ::= expression(B). {
+ A = NULL;
+ if (ctx->ok) {
+ if (B->type == TYPE_STRING) {
+ A = buffer_init_buffer(((data_string*)B)->value);
+ } else if (B->type == TYPE_INTEGER) {
+ A = buffer_init();
+ buffer_copy_long(A, ((data_integer *)B)->value);
+ } else {
+ fprintf(stderr, "operand must be string");
+ ctx->ok = 0;
+ }
+ }
+ B->free(B);
+ B = NULL;
+}
+
+include ::= INCLUDE stringop(A). {
+ if (ctx->ok) {
+ if (0 != config_parse_file(ctx->srv, ctx, A->ptr)) {
+ ctx->ok = 0;
+ }
+ buffer_free(A);
+ A = NULL;
+ }
+}
+
+include_shell ::= INCLUDE_SHELL stringop(A). {
+ if (ctx->ok) {
+ if (0 != config_parse_cmd(ctx->srv, ctx, A->ptr)) {
+ ctx->ok = 0;
+ }
+ buffer_free(A);
+ A = NULL;
+ }
+}
Added: test-suite/trunk/MultiSource/Applications/lemon/lighttpd_mod_ssi_exprparser.y
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/lighttpd_mod_ssi_exprparser.y?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/lighttpd_mod_ssi_exprparser.y (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/lighttpd_mod_ssi_exprparser.y Wed Mar 26 12:03:57 2008
@@ -0,0 +1,121 @@
+%token_prefix TK_
+%token_type {buffer *}
+%extra_argument {ssi_ctx_t *ctx}
+%name ssiexprparser
+
+%include {
+#include <assert.h>
+#include <string.h>
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "mod_ssi_expr.h"
+#include "buffer.h"
+}
+
+%parse_failure {
+ ctx->ok = 0;
+}
+
+%type expr { ssi_val_t * }
+%type value { buffer * }
+%type exprline { ssi_val_t * }
+%type cond { int }
+%token_destructor { buffer_free($$); }
+
+%left AND.
+%left OR.
+%nonassoc EQ NE GT GE LT LE.
+%right NOT.
+
+input ::= exprline(B). {
+ ctx->val.bo = ssi_val_tobool(B);
+ ctx->val.type = SSI_TYPE_BOOL;
+
+ ssi_val_free(B);
+}
+
+exprline(A) ::= expr(B) cond(C) expr(D). {
+ int cmp;
+
+ if (B->type == SSI_TYPE_STRING &&
+ D->type == SSI_TYPE_STRING) {
+ cmp = strcmp(B->str->ptr, D->str->ptr);
+ } else {
+ cmp = ssi_val_tobool(B) - ssi_val_tobool(D);
+ }
+
+ A = B;
+
+ switch(C) {
+ case SSI_COND_EQ: A->bo = (cmp == 0) ? 1 : 0; break;
+ case SSI_COND_NE: A->bo = (cmp != 0) ? 1 : 0; break;
+ case SSI_COND_GE: A->bo = (cmp >= 0) ? 1 : 0; break;
+ case SSI_COND_GT: A->bo = (cmp > 0) ? 1 : 0; break;
+ case SSI_COND_LE: A->bo = (cmp <= 0) ? 1 : 0; break;
+ case SSI_COND_LT: A->bo = (cmp < 0) ? 1 : 0; break;
+ }
+
+ A->type = SSI_TYPE_BOOL;
+
+ ssi_val_free(D);
+}
+exprline(A) ::= expr(B). {
+ A = B;
+}
+expr(A) ::= expr(B) AND expr(C). {
+ int e;
+
+ e = ssi_val_tobool(B) && ssi_val_tobool(C);
+
+ A = B;
+ A->bo = e;
+ A->type = SSI_TYPE_BOOL;
+ ssi_val_free(C);
+}
+
+expr(A) ::= expr(B) OR expr(C). {
+ int e;
+
+ e = ssi_val_tobool(B) || ssi_val_tobool(C);
+
+ A = B;
+ A->bo = e;
+ A->type = SSI_TYPE_BOOL;
+ ssi_val_free(C);
+}
+
+expr(A) ::= NOT expr(B). {
+ int e;
+
+ e = !ssi_val_tobool(B);
+
+ A = B;
+ A->bo = e;
+ A->type = SSI_TYPE_BOOL;
+}
+expr(A) ::= LPARAN exprline(B) RPARAN. {
+ A = B;
+}
+
+expr(A) ::= value(B). {
+ A = ssi_val_init();
+ A->str = B;
+ A->type = SSI_TYPE_STRING;
+}
+
+value(A) ::= VALUE(B). {
+ A = buffer_init_string(B->ptr);
+}
+
+value(A) ::= value(B) VALUE(C). {
+ A = B;
+ buffer_append_string_buffer(A, C);
+}
+
+cond(A) ::= EQ. { A = SSI_COND_EQ; }
+cond(A) ::= NE. { A = SSI_COND_NE; }
+cond(A) ::= LE. { A = SSI_COND_LE; }
+cond(A) ::= GE. { A = SSI_COND_GE; }
+cond(A) ::= LT. { A = SSI_COND_LT; }
+cond(A) ::= GT. { A = SSI_COND_GT; }
Added: test-suite/trunk/MultiSource/Applications/lemon/parse.y
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/parse.y?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/parse.y (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/parse.y Wed Mar 26 12:03:57 2008
@@ -0,0 +1,1114 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains SQLite's grammar for SQL. Process this file
+** using the lemon parser generator to generate C code that runs
+** the parser. Lemon will also generate a header file containing
+** numeric codes for all of the tokens.
+**
+** @(#) $Id: parse.y,v 1.240 2008/01/22 23:37:10 drh Exp $
+*/
+
+// All token codes are small integers with #defines that begin with "TK_"
+%token_prefix TK_
+
+// The type of the data attached to each token is Token. This is also the
+// default type for non-terminals.
+//
+%token_type {Token}
+%default_type {Token}
+
+// The generated parser function takes a 4th argument as follows:
+%extra_argument {Parse *pParse}
+
+// This code runs whenever there is a syntax error
+//
+%syntax_error {
+ assert( TOKEN.z[0] ); /* The tokenizer always gives us a token */
+ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
+ pParse->parseError = 1;
+}
+%stack_overflow {
+ sqlite3ErrorMsg(pParse, "parser stack overflow");
+ pParse->parseError = 1;
+}
+
+// The name of the generated procedure that implements the parser
+// is as follows:
+%name sqlite3Parser
+
+// The following text is included near the beginning of the C source
+// code file that implements the parser.
+//
+%include {
+#include "sqliteInt.h"
+
+/*
+** An instance of this structure holds information about the
+** LIMIT clause of a SELECT statement.
+*/
+struct LimitVal {
+ Expr *pLimit; /* The LIMIT expression. NULL if there is no limit */
+ Expr *pOffset; /* The OFFSET expression. NULL if there is none */
+};
+
+/*
+** An instance of this structure is used to store the LIKE,
+** GLOB, NOT LIKE, and NOT GLOB operators.
+*/
+struct LikeOp {
+ Token eOperator; /* "like" or "glob" or "regexp" */
+ int not; /* True if the NOT keyword is present */
+};
+
+/*
+** An instance of the following structure describes the event of a
+** TRIGGER. "a" is the event type, one of TK_UPDATE, TK_INSERT,
+** TK_DELETE, or TK_INSTEAD. If the event is of the form
+**
+** UPDATE ON (a,b,c)
+**
+** Then the "b" IdList records the list "a,b,c".
+*/
+struct TrigEvent { int a; IdList * b; };
+
+/*
+** An instance of this structure holds the ATTACH key and the key type.
+*/
+struct AttachKey { int type; Token key; };
+
+} // end %include
+
+// Input is a single SQL command
+input ::= cmdlist.
+cmdlist ::= cmdlist ecmd.
+cmdlist ::= ecmd.
+cmdx ::= cmd. { sqlite3FinishCoding(pParse); }
+ecmd ::= SEMI.
+ecmd ::= explain cmdx SEMI.
+explain ::= . { sqlite3BeginParse(pParse, 0); }
+%ifndef SQLITE_OMIT_EXPLAIN
+explain ::= EXPLAIN. { sqlite3BeginParse(pParse, 1); }
+explain ::= EXPLAIN QUERY PLAN. { sqlite3BeginParse(pParse, 2); }
+%endif SQLITE_OMIT_EXPLAIN
+
+///////////////////// Begin and end transactions. ////////////////////////////
+//
+
+cmd ::= BEGIN transtype(Y) trans_opt. {sqlite3BeginTransaction(pParse, Y);}
+trans_opt ::= .
+trans_opt ::= TRANSACTION.
+trans_opt ::= TRANSACTION nm.
+%type transtype {int}
+transtype(A) ::= . {A = TK_DEFERRED;}
+transtype(A) ::= DEFERRED(X). {A = @X;}
+transtype(A) ::= IMMEDIATE(X). {A = @X;}
+transtype(A) ::= EXCLUSIVE(X). {A = @X;}
+cmd ::= COMMIT trans_opt. {sqlite3CommitTransaction(pParse);}
+cmd ::= END trans_opt. {sqlite3CommitTransaction(pParse);}
+cmd ::= ROLLBACK trans_opt. {sqlite3RollbackTransaction(pParse);}
+
+///////////////////// The CREATE TABLE statement ////////////////////////////
+//
+cmd ::= create_table create_table_args.
+create_table ::= CREATE temp(T) TABLE ifnotexists(E) nm(Y) dbnm(Z). {
+ sqlite3StartTable(pParse,&Y,&Z,T,0,0,E);
+}
+%type ifnotexists {int}
+ifnotexists(A) ::= . {A = 0;}
+ifnotexists(A) ::= IF NOT EXISTS. {A = 1;}
+%type temp {int}
+%ifndef SQLITE_OMIT_TEMPDB
+temp(A) ::= TEMP. {A = 1;}
+%endif SQLITE_OMIT_TEMPDB
+temp(A) ::= . {A = 0;}
+create_table_args ::= LP columnlist conslist_opt(X) RP(Y). {
+ sqlite3EndTable(pParse,&X,&Y,0);
+}
+create_table_args ::= AS select(S). {
+ sqlite3EndTable(pParse,0,0,S);
+ sqlite3SelectDelete(S);
+}
+columnlist ::= columnlist COMMA column.
+columnlist ::= column.
+
+// A "column" is a complete description of a single column in a
+// CREATE TABLE statement. This includes the column name, its
+// datatype, and other keywords such as PRIMARY KEY, UNIQUE, REFERENCES,
+// NOT NULL and so forth.
+//
+column(A) ::= columnid(X) type carglist. {
+ A.z = X.z;
+ A.n = (pParse->sLastToken.z-X.z) + pParse->sLastToken.n;
+}
+columnid(A) ::= nm(X). {
+ sqlite3AddColumn(pParse,&X);
+ A = X;
+}
+
+
+// An IDENTIFIER can be a generic identifier, or one of several
+// keywords. Any non-standard keyword can also be an identifier.
+//
+%type id {Token}
+id(A) ::= ID(X). {A = X;}
+
+// The following directive causes tokens ABORT, AFTER, ASC, etc. to
+// fallback to ID if they will not parse as their original value.
+// This obviates the need for the "id" nonterminal.
+//
+%fallback ID
+ ABORT AFTER ANALYZE ASC ATTACH BEFORE BEGIN CASCADE CAST CONFLICT
+ DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR
+ IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH PLAN
+ QUERY KEY OF OFFSET PRAGMA RAISE REPLACE RESTRICT ROW
+ TEMP TRIGGER VACUUM VIEW VIRTUAL
+%ifdef SQLITE_OMIT_COMPOUND_SELECT
+ EXCEPT INTERSECT UNION
+%endif SQLITE_OMIT_COMPOUND_SELECT
+ REINDEX RENAME CTIME_KW IF
+ .
+%wildcard ANY.
+
+// Define operator precedence early so that this is the first occurance
+// of the operator tokens in the grammer. Keeping the operators together
+// causes them to be assigned integer values that are close together,
+// which keeps parser tables smaller.
+//
+// The token values assigned to these symbols is determined by the order
+// in which lemon first sees them. It must be the case that ISNULL/NOTNULL,
+// NE/EQ, GT/LE, and GE/LT are separated by only a single value. See
+// the sqlite3ExprIfFalse() routine for additional information on this
+// constraint.
+//
+%left OR.
+%left AND.
+%right NOT.
+%left IS MATCH LIKE_KW BETWEEN IN ISNULL NOTNULL NE EQ.
+%left GT LE LT GE.
+%right ESCAPE.
+%left BITAND BITOR LSHIFT RSHIFT.
+%left PLUS MINUS.
+%left STAR SLASH REM.
+%left CONCAT.
+%left COLLATE.
+%right UMINUS UPLUS BITNOT.
+
+// And "ids" is an identifer-or-string.
+//
+%type ids {Token}
+ids(A) ::= ID|STRING(X). {A = X;}
+
+// The name of a column or table can be any of the following:
+//
+%type nm {Token}
+nm(A) ::= ID(X). {A = X;}
+nm(A) ::= STRING(X). {A = X;}
+nm(A) ::= JOIN_KW(X). {A = X;}
+
+// A typetoken is really one or more tokens that form a type name such
+// as can be found after the column name in a CREATE TABLE statement.
+// Multiple tokens are concatenated to form the value of the typetoken.
+//
+%type typetoken {Token}
+type ::= .
+type ::= typetoken(X). {sqlite3AddColumnType(pParse,&X);}
+typetoken(A) ::= typename(X). {A = X;}
+typetoken(A) ::= typename(X) LP signed RP(Y). {
+ A.z = X.z;
+ A.n = &Y.z[Y.n] - X.z;
+}
+typetoken(A) ::= typename(X) LP signed COMMA signed RP(Y). {
+ A.z = X.z;
+ A.n = &Y.z[Y.n] - X.z;
+}
+%type typename {Token}
+typename(A) ::= ids(X). {A = X;}
+typename(A) ::= typename(X) ids(Y). {A.z=X.z; A.n=Y.n+(Y.z-X.z);}
+signed ::= plus_num.
+signed ::= minus_num.
+
+// "carglist" is a list of additional constraints that come after the
+// column name and column type in a CREATE TABLE statement.
+//
+carglist ::= carglist carg.
+carglist ::= .
+carg ::= CONSTRAINT nm ccons.
+carg ::= ccons.
+ccons ::= DEFAULT term(X). {sqlite3AddDefaultValue(pParse,X);}
+ccons ::= DEFAULT LP expr(X) RP. {sqlite3AddDefaultValue(pParse,X);}
+ccons ::= DEFAULT PLUS term(X). {sqlite3AddDefaultValue(pParse,X);}
+ccons ::= DEFAULT MINUS term(X). {
+ Expr *p = sqlite3PExpr(pParse, TK_UMINUS, X, 0, 0);
+ sqlite3AddDefaultValue(pParse,p);
+}
+ccons ::= DEFAULT id(X). {
+ Expr *p = sqlite3PExpr(pParse, TK_STRING, 0, 0, &X);
+ sqlite3AddDefaultValue(pParse,p);
+}
+
+// In addition to the type name, we also care about the primary key and
+// UNIQUE constraints.
+//
+ccons ::= NULL onconf.
+ccons ::= NOT NULL onconf(R). {sqlite3AddNotNull(pParse, R);}
+ccons ::= PRIMARY KEY sortorder(Z) onconf(R) autoinc(I).
+ {sqlite3AddPrimaryKey(pParse,0,R,I,Z);}
+ccons ::= UNIQUE onconf(R). {sqlite3CreateIndex(pParse,0,0,0,0,R,0,0,0,0);}
+ccons ::= CHECK LP expr(X) RP. {sqlite3AddCheckConstraint(pParse,X);}
+ccons ::= REFERENCES nm(T) idxlist_opt(TA) refargs(R).
+ {sqlite3CreateForeignKey(pParse,0,&T,TA,R);}
+ccons ::= defer_subclause(D). {sqlite3DeferForeignKey(pParse,D);}
+ccons ::= COLLATE ids(C). {sqlite3AddCollateType(pParse, &C);}
+
+// The optional AUTOINCREMENT keyword
+%type autoinc {int}
+autoinc(X) ::= . {X = 0;}
+autoinc(X) ::= AUTOINCR. {X = 1;}
+
+// The next group of rules parses the arguments to a REFERENCES clause
+// that determine if the referential integrity checking is deferred or
+// or immediate and which determine what action to take if a ref-integ
+// check fails.
+//
+%type refargs {int}
+refargs(A) ::= . { A = OE_Restrict * 0x010101; }
+refargs(A) ::= refargs(X) refarg(Y). { A = (X & Y.mask) | Y.value; }
+%type refarg {struct {int value; int mask;}}
+refarg(A) ::= MATCH nm. { A.value = 0; A.mask = 0x000000; }
+refarg(A) ::= ON DELETE refact(X). { A.value = X; A.mask = 0x0000ff; }
+refarg(A) ::= ON UPDATE refact(X). { A.value = X<<8; A.mask = 0x00ff00; }
+refarg(A) ::= ON INSERT refact(X). { A.value = X<<16; A.mask = 0xff0000; }
+%type refact {int}
+refact(A) ::= SET NULL. { A = OE_SetNull; }
+refact(A) ::= SET DEFAULT. { A = OE_SetDflt; }
+refact(A) ::= CASCADE. { A = OE_Cascade; }
+refact(A) ::= RESTRICT. { A = OE_Restrict; }
+%type defer_subclause {int}
+defer_subclause(A) ::= NOT DEFERRABLE init_deferred_pred_opt(X). {A = X;}
+defer_subclause(A) ::= DEFERRABLE init_deferred_pred_opt(X). {A = X;}
+%type init_deferred_pred_opt {int}
+init_deferred_pred_opt(A) ::= . {A = 0;}
+init_deferred_pred_opt(A) ::= INITIALLY DEFERRED. {A = 1;}
+init_deferred_pred_opt(A) ::= INITIALLY IMMEDIATE. {A = 0;}
+
+// For the time being, the only constraint we care about is the primary
+// key and UNIQUE. Both create indices.
+//
+conslist_opt(A) ::= . {A.n = 0; A.z = 0;}
+conslist_opt(A) ::= COMMA(X) conslist. {A = X;}
+conslist ::= conslist COMMA tcons.
+conslist ::= conslist tcons.
+conslist ::= tcons.
+tcons ::= CONSTRAINT nm.
+tcons ::= PRIMARY KEY LP idxlist(X) autoinc(I) RP onconf(R).
+ {sqlite3AddPrimaryKey(pParse,X,R,I,0);}
+tcons ::= UNIQUE LP idxlist(X) RP onconf(R).
+ {sqlite3CreateIndex(pParse,0,0,0,X,R,0,0,0,0);}
+tcons ::= CHECK LP expr(E) RP onconf. {sqlite3AddCheckConstraint(pParse,E);}
+tcons ::= FOREIGN KEY LP idxlist(FA) RP
+ REFERENCES nm(T) idxlist_opt(TA) refargs(R) defer_subclause_opt(D). {
+ sqlite3CreateForeignKey(pParse, FA, &T, TA, R);
+ sqlite3DeferForeignKey(pParse, D);
+}
+%type defer_subclause_opt {int}
+defer_subclause_opt(A) ::= . {A = 0;}
+defer_subclause_opt(A) ::= defer_subclause(X). {A = X;}
+
+// The following is a non-standard extension that allows us to declare the
+// default behavior when there is a constraint conflict.
+//
+%type onconf {int}
+%type orconf {int}
+%type resolvetype {int}
+onconf(A) ::= . {A = OE_Default;}
+onconf(A) ::= ON CONFLICT resolvetype(X). {A = X;}
+orconf(A) ::= . {A = OE_Default;}
+orconf(A) ::= OR resolvetype(X). {A = X;}
+resolvetype(A) ::= raisetype(X). {A = X;}
+resolvetype(A) ::= IGNORE. {A = OE_Ignore;}
+resolvetype(A) ::= REPLACE. {A = OE_Replace;}
+
+////////////////////////// The DROP TABLE /////////////////////////////////////
+//
+cmd ::= DROP TABLE ifexists(E) fullname(X). {
+ sqlite3DropTable(pParse, X, 0, E);
+}
+%type ifexists {int}
+ifexists(A) ::= IF EXISTS. {A = 1;}
+ifexists(A) ::= . {A = 0;}
+
+///////////////////// The CREATE VIEW statement /////////////////////////////
+//
+%ifndef SQLITE_OMIT_VIEW
+cmd ::= CREATE(X) temp(T) VIEW ifnotexists(E) nm(Y) dbnm(Z) AS select(S). {
+ sqlite3CreateView(pParse, &X, &Y, &Z, S, T, E);
+}
+cmd ::= DROP VIEW ifexists(E) fullname(X). {
+ sqlite3DropTable(pParse, X, 1, E);
+}
+%endif SQLITE_OMIT_VIEW
+
+//////////////////////// The SELECT statement /////////////////////////////////
+//
+cmd ::= select(X). {
+ SelectDest dest = {SRT_Callback, 0, 0};
+ sqlite3Select(pParse, X, &dest, 0, 0, 0, 0);
+ sqlite3SelectDelete(X);
+}
+
+%type select {Select*}
+%destructor select {sqlite3SelectDelete($$);}
+%type oneselect {Select*}
+%destructor oneselect {sqlite3SelectDelete($$);}
+
+select(A) ::= oneselect(X). {A = X;}
+%ifndef SQLITE_OMIT_COMPOUND_SELECT
+select(A) ::= select(X) multiselect_op(Y) oneselect(Z). {
+ if( Z ){
+ Z->op = Y;
+ Z->pPrior = X;
+ }else{
+ sqlite3SelectDelete(X);
+ }
+ A = Z;
+}
+%type multiselect_op {int}
+multiselect_op(A) ::= UNION(OP). {A = @OP;}
+multiselect_op(A) ::= UNION ALL. {A = TK_ALL;}
+multiselect_op(A) ::= EXCEPT|INTERSECT(OP). {A = @OP;}
+%endif SQLITE_OMIT_COMPOUND_SELECT
+oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y)
+ groupby_opt(P) having_opt(Q) orderby_opt(Z) limit_opt(L). {
+ A = sqlite3SelectNew(pParse,W,X,Y,P,Q,Z,D,L.pLimit,L.pOffset);
+}
+
+// The "distinct" nonterminal is true (1) if the DISTINCT keyword is
+// present and false (0) if it is not.
+//
+%type distinct {int}
+distinct(A) ::= DISTINCT. {A = 1;}
+distinct(A) ::= ALL. {A = 0;}
+distinct(A) ::= . {A = 0;}
+
+// selcollist is a list of expressions that are to become the return
+// values of the SELECT statement. The "*" in statements like
+// "SELECT * FROM ..." is encoded as a special expression with an
+// opcode of TK_ALL.
+//
+%type selcollist {ExprList*}
+%destructor selcollist {sqlite3ExprListDelete($$);}
+%type sclp {ExprList*}
+%destructor sclp {sqlite3ExprListDelete($$);}
+sclp(A) ::= selcollist(X) COMMA. {A = X;}
+sclp(A) ::= . {A = 0;}
+selcollist(A) ::= sclp(P) expr(X) as(Y). {
+ A = sqlite3ExprListAppend(pParse,P,X,Y.n?&Y:0);
+}
+selcollist(A) ::= sclp(P) STAR. {
+ Expr *p = sqlite3PExpr(pParse, TK_ALL, 0, 0, 0);
+ A = sqlite3ExprListAppend(pParse, P, p, 0);
+}
+selcollist(A) ::= sclp(P) nm(X) DOT STAR. {
+ Expr *pRight = sqlite3PExpr(pParse, TK_ALL, 0, 0, 0);
+ Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);
+ Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
+ A = sqlite3ExprListAppend(pParse,P, pDot, 0);
+}
+
+// An option "AS <id>" phrase that can follow one of the expressions that
+// define the result set, or one of the tables in the FROM clause.
+//
+%type as {Token}
+as(X) ::= AS nm(Y). {X = Y;}
+as(X) ::= ids(Y). {X = Y;}
+as(X) ::= . {X.n = 0;}
+
+
+%type seltablist {SrcList*}
+%destructor seltablist {sqlite3SrcListDelete($$);}
+%type stl_prefix {SrcList*}
+%destructor stl_prefix {sqlite3SrcListDelete($$);}
+%type from {SrcList*}
+%destructor from {sqlite3SrcListDelete($$);}
+
+// A complete FROM clause.
+//
+from(A) ::= . {A = sqlite3DbMallocZero(pParse->db, sizeof(*A));}
+from(A) ::= FROM seltablist(X). {
+ A = X;
+ sqlite3SrcListShiftJoinType(A);
+}
+
+// "seltablist" is a "Select Table List" - the content of the FROM clause
+// in a SELECT statement. "stl_prefix" is a prefix of this list.
+//
+stl_prefix(A) ::= seltablist(X) joinop(Y). {
+ A = X;
+ if( A && A->nSrc>0 ) A->a[A->nSrc-1].jointype = Y;
+}
+stl_prefix(A) ::= . {A = 0;}
+seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) on_opt(N) using_opt(U). {
+ A = sqlite3SrcListAppendFromTerm(pParse,X,&Y,&D,&Z,0,N,U);
+}
+%ifndef SQLITE_OMIT_SUBQUERY
+ seltablist(A) ::= stl_prefix(X) LP seltablist_paren(S) RP
+ as(Z) on_opt(N) using_opt(U). {
+ A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,S,N,U);
+ }
+
+ // A seltablist_paren nonterminal represents anything in a FROM that
+ // is contained inside parentheses. This can be either a subquery or
+ // a grouping of table and subqueries.
+ //
+ %type seltablist_paren {Select*}
+ %destructor seltablist_paren {sqlite3SelectDelete($$);}
+ seltablist_paren(A) ::= select(S). {A = S;}
+ seltablist_paren(A) ::= seltablist(F). {
+ sqlite3SrcListShiftJoinType(F);
+ A = sqlite3SelectNew(pParse,0,F,0,0,0,0,0,0,0);
+ }
+%endif SQLITE_OMIT_SUBQUERY
+
+%type dbnm {Token}
+dbnm(A) ::= . {A.z=0; A.n=0;}
+dbnm(A) ::= DOT nm(X). {A = X;}
+
+%type fullname {SrcList*}
+%destructor fullname {sqlite3SrcListDelete($$);}
+fullname(A) ::= nm(X) dbnm(Y). {A = sqlite3SrcListAppend(pParse->db,0,&X,&Y);}
+
+%type joinop {int}
+%type joinop2 {int}
+joinop(X) ::= COMMA|JOIN. { X = JT_INNER; }
+joinop(X) ::= JOIN_KW(A) JOIN. { X = sqlite3JoinType(pParse,&A,0,0); }
+joinop(X) ::= JOIN_KW(A) nm(B) JOIN. { X = sqlite3JoinType(pParse,&A,&B,0); }
+joinop(X) ::= JOIN_KW(A) nm(B) nm(C) JOIN.
+ { X = sqlite3JoinType(pParse,&A,&B,&C); }
+
+%type on_opt {Expr*}
+%destructor on_opt {sqlite3ExprDelete($$);}
+on_opt(N) ::= ON expr(E). {N = E;}
+on_opt(N) ::= . {N = 0;}
+
+%type using_opt {IdList*}
+%destructor using_opt {sqlite3IdListDelete($$);}
+using_opt(U) ::= USING LP inscollist(L) RP. {U = L;}
+using_opt(U) ::= . {U = 0;}
+
+
+%type orderby_opt {ExprList*}
+%destructor orderby_opt {sqlite3ExprListDelete($$);}
+%type sortlist {ExprList*}
+%destructor sortlist {sqlite3ExprListDelete($$);}
+%type sortitem {Expr*}
+%destructor sortitem {sqlite3ExprDelete($$);}
+
+orderby_opt(A) ::= . {A = 0;}
+orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;}
+sortlist(A) ::= sortlist(X) COMMA sortitem(Y) sortorder(Z). {
+ A = sqlite3ExprListAppend(pParse,X,Y,0);
+ if( A ) A->a[A->nExpr-1].sortOrder = Z;
+}
+sortlist(A) ::= sortitem(Y) sortorder(Z). {
+ A = sqlite3ExprListAppend(pParse,0,Y,0);
+ if( A && A->a ) A->a[0].sortOrder = Z;
+}
+sortitem(A) ::= expr(X). {A = X;}
+
+%type sortorder {int}
+
+sortorder(A) ::= ASC. {A = SQLITE_SO_ASC;}
+sortorder(A) ::= DESC. {A = SQLITE_SO_DESC;}
+sortorder(A) ::= . {A = SQLITE_SO_ASC;}
+
+%type groupby_opt {ExprList*}
+%destructor groupby_opt {sqlite3ExprListDelete($$);}
+groupby_opt(A) ::= . {A = 0;}
+groupby_opt(A) ::= GROUP BY nexprlist(X). {A = X;}
+
+%type having_opt {Expr*}
+%destructor having_opt {sqlite3ExprDelete($$);}
+having_opt(A) ::= . {A = 0;}
+having_opt(A) ::= HAVING expr(X). {A = X;}
+
+%type limit_opt {struct LimitVal}
+
+// The destructor for limit_opt will never fire in the current grammar.
+// The limit_opt non-terminal only occurs at the end of a single production
+// rule for SELECT statements. As soon as the rule that create the
+// limit_opt non-terminal reduces, the SELECT statement rule will also
+// reduce. So there is never a limit_opt non-terminal on the stack
+// except as a transient. So there is never anything to destroy.
+//
+//%destructor limit_opt {
+// sqlite3ExprDelete($$.pLimit);
+// sqlite3ExprDelete($$.pOffset);
+//}
+limit_opt(A) ::= . {A.pLimit = 0; A.pOffset = 0;}
+limit_opt(A) ::= LIMIT expr(X). {A.pLimit = X; A.pOffset = 0;}
+limit_opt(A) ::= LIMIT expr(X) OFFSET expr(Y).
+ {A.pLimit = X; A.pOffset = Y;}
+limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
+ {A.pOffset = X; A.pLimit = Y;}
+
+/////////////////////////// The DELETE statement /////////////////////////////
+//
+cmd ::= DELETE FROM fullname(X) where_opt(Y). {sqlite3DeleteFrom(pParse,X,Y);}
+
+%type where_opt {Expr*}
+%destructor where_opt {sqlite3ExprDelete($$);}
+
+where_opt(A) ::= . {A = 0;}
+where_opt(A) ::= WHERE expr(X). {A = X;}
+
+////////////////////////// The UPDATE command ////////////////////////////////
+//
+cmd ::= UPDATE orconf(R) fullname(X) SET setlist(Y) where_opt(Z). {
+ sqlite3ExprListCheckLength(pParse,Y,SQLITE_MAX_COLUMN,"set list");
+ sqlite3Update(pParse,X,Y,Z,R);
+}
+
+%type setlist {ExprList*}
+%destructor setlist {sqlite3ExprListDelete($$);}
+
+setlist(A) ::= setlist(Z) COMMA nm(X) EQ expr(Y).
+ {A = sqlite3ExprListAppend(pParse,Z,Y,&X);}
+setlist(A) ::= nm(X) EQ expr(Y).
+ {A = sqlite3ExprListAppend(pParse,0,Y,&X);}
+
+////////////////////////// The INSERT command /////////////////////////////////
+//
+cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F)
+ VALUES LP itemlist(Y) RP.
+ {sqlite3Insert(pParse, X, Y, 0, F, R);}
+cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S).
+ {sqlite3Insert(pParse, X, 0, S, F, R);}
+cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES.
+ {sqlite3Insert(pParse, X, 0, 0, F, R);}
+
+%type insert_cmd {int}
+insert_cmd(A) ::= INSERT orconf(R). {A = R;}
+insert_cmd(A) ::= REPLACE. {A = OE_Replace;}
+
+
+%type itemlist {ExprList*}
+%destructor itemlist {sqlite3ExprListDelete($$);}
+
+itemlist(A) ::= itemlist(X) COMMA expr(Y).
+ {A = sqlite3ExprListAppend(pParse,X,Y,0);}
+itemlist(A) ::= expr(X).
+ {A = sqlite3ExprListAppend(pParse,0,X,0);}
+
+%type inscollist_opt {IdList*}
+%destructor inscollist_opt {sqlite3IdListDelete($$);}
+%type inscollist {IdList*}
+%destructor inscollist {sqlite3IdListDelete($$);}
+
+inscollist_opt(A) ::= . {A = 0;}
+inscollist_opt(A) ::= LP inscollist(X) RP. {A = X;}
+inscollist(A) ::= inscollist(X) COMMA nm(Y).
+ {A = sqlite3IdListAppend(pParse->db,X,&Y);}
+inscollist(A) ::= nm(Y).
+ {A = sqlite3IdListAppend(pParse->db,0,&Y);}
+
+/////////////////////////// Expression Processing /////////////////////////////
+//
+
+%type expr {Expr*}
+%destructor expr {sqlite3ExprDelete($$);}
+%type term {Expr*}
+%destructor term {sqlite3ExprDelete($$);}
+
+expr(A) ::= term(X). {A = X;}
+expr(A) ::= LP(B) expr(X) RP(E). {A = X; sqlite3ExprSpan(A,&B,&E); }
+term(A) ::= NULL(X). {A = sqlite3PExpr(pParse, @X, 0, 0, &X);}
+expr(A) ::= ID(X). {A = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);}
+expr(A) ::= JOIN_KW(X). {A = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);}
+expr(A) ::= nm(X) DOT nm(Y). {
+ Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);
+ Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &Y);
+ A = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0);
+}
+expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). {
+ Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);
+ Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &Y);
+ Expr *temp3 = sqlite3PExpr(pParse, TK_ID, 0, 0, &Z);
+ Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3, 0);
+ A = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0);
+}
+term(A) ::= INTEGER|FLOAT|BLOB(X). {A = sqlite3PExpr(pParse, @X, 0, 0, &X);}
+term(A) ::= STRING(X). {A = sqlite3PExpr(pParse, @X, 0, 0, &X);}
+expr(A) ::= REGISTER(X). {A = sqlite3RegisterExpr(pParse, &X);}
+expr(A) ::= VARIABLE(X). {
+ Token *pToken = &X;
+ Expr *pExpr = A = sqlite3PExpr(pParse, TK_VARIABLE, 0, 0, pToken);
+ sqlite3ExprAssignVarNumber(pParse, pExpr);
+}
+expr(A) ::= expr(E) COLLATE ids(C). {
+ A = sqlite3ExprSetColl(pParse, E, &C);
+}
+%ifndef SQLITE_OMIT_CAST
+expr(A) ::= CAST(X) LP expr(E) AS typetoken(T) RP(Y). {
+ A = sqlite3PExpr(pParse, TK_CAST, E, 0, &T);
+ sqlite3ExprSpan(A,&X,&Y);
+}
+%endif SQLITE_OMIT_CAST
+expr(A) ::= ID(X) LP distinct(D) exprlist(Y) RP(E). {
+ if( Y && Y->nExpr>SQLITE_MAX_FUNCTION_ARG ){
+ sqlite3ErrorMsg(pParse, "too many arguments on function %T", &X);
+ }
+ A = sqlite3ExprFunction(pParse, Y, &X);
+ sqlite3ExprSpan(A,&X,&E);
+ if( D && A ){
+ A->flags |= EP_Distinct;
+ }
+}
+expr(A) ::= ID(X) LP STAR RP(E). {
+ A = sqlite3ExprFunction(pParse, 0, &X);
+ sqlite3ExprSpan(A,&X,&E);
+}
+term(A) ::= CTIME_KW(OP). {
+ /* The CURRENT_TIME, CURRENT_DATE, and CURRENT_TIMESTAMP values are
+ ** treated as functions that return constants */
+ A = sqlite3ExprFunction(pParse, 0,&OP);
+ if( A ){
+ A->op = TK_CONST_FUNC;
+ A->span = OP;
+ }
+}
+expr(A) ::= expr(X) AND(OP) expr(Y). {A = sqlite3PExpr(pParse, at OP,X,Y,0);}
+expr(A) ::= expr(X) OR(OP) expr(Y). {A = sqlite3PExpr(pParse, at OP,X,Y,0);}
+expr(A) ::= expr(X) LT|GT|GE|LE(OP) expr(Y).
+ {A = sqlite3PExpr(pParse, at OP,X,Y,0);}
+expr(A) ::= expr(X) EQ|NE(OP) expr(Y). {A = sqlite3PExpr(pParse, at OP,X,Y,0);}
+expr(A) ::= expr(X) BITAND|BITOR|LSHIFT|RSHIFT(OP) expr(Y).
+ {A = sqlite3PExpr(pParse, at OP,X,Y,0);}
+expr(A) ::= expr(X) PLUS|MINUS(OP) expr(Y).{A = sqlite3PExpr(pParse, at OP,X,Y,0);}
+expr(A) ::= expr(X) STAR|SLASH|REM(OP) expr(Y).
+ {A = sqlite3PExpr(pParse, at OP,X,Y,0);}
+expr(A) ::= expr(X) CONCAT(OP) expr(Y). {A = sqlite3PExpr(pParse, at OP,X,Y,0);}
+%type likeop {struct LikeOp}
+likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.not = 0;}
+likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.not = 1;}
+likeop(A) ::= MATCH(X). {A.eOperator = X; A.not = 0;}
+likeop(A) ::= NOT MATCH(X). {A.eOperator = X; A.not = 1;}
+%type escape {Expr*}
+%destructor escape {sqlite3ExprDelete($$);}
+escape(X) ::= ESCAPE expr(A). [ESCAPE] {X = A;}
+escape(X) ::= . [ESCAPE] {X = 0;}
+expr(A) ::= expr(X) likeop(OP) expr(Y) escape(E). [LIKE_KW] {
+ ExprList *pList;
+ pList = sqlite3ExprListAppend(pParse,0, Y, 0);
+ pList = sqlite3ExprListAppend(pParse,pList, X, 0);
+ if( E ){
+ pList = sqlite3ExprListAppend(pParse,pList, E, 0);
+ }
+ A = sqlite3ExprFunction(pParse, pList, &OP.eOperator);
+ if( OP.not ) A = sqlite3PExpr(pParse, TK_NOT, A, 0, 0);
+ sqlite3ExprSpan(A, &X->span, &Y->span);
+ if( A ) A->flags |= EP_InfixFunc;
+}
+
+expr(A) ::= expr(X) ISNULL|NOTNULL(E). {
+ A = sqlite3PExpr(pParse, @E, X, 0, 0);
+ sqlite3ExprSpan(A,&X->span,&E);
+}
+expr(A) ::= expr(X) IS NULL(E). {
+ A = sqlite3PExpr(pParse, TK_ISNULL, X, 0, 0);
+ sqlite3ExprSpan(A,&X->span,&E);
+}
+expr(A) ::= expr(X) NOT NULL(E). {
+ A = sqlite3PExpr(pParse, TK_NOTNULL, X, 0, 0);
+ sqlite3ExprSpan(A,&X->span,&E);
+}
+expr(A) ::= expr(X) IS NOT NULL(E). {
+ A = sqlite3PExpr(pParse, TK_NOTNULL, X, 0, 0);
+ sqlite3ExprSpan(A,&X->span,&E);
+}
+expr(A) ::= NOT(B) expr(X). {
+ A = sqlite3PExpr(pParse, @B, X, 0, 0);
+ sqlite3ExprSpan(A,&B,&X->span);
+}
+expr(A) ::= BITNOT(B) expr(X). {
+ A = sqlite3PExpr(pParse, @B, X, 0, 0);
+ sqlite3ExprSpan(A,&B,&X->span);
+}
+expr(A) ::= MINUS(B) expr(X). [UMINUS] {
+ A = sqlite3PExpr(pParse, TK_UMINUS, X, 0, 0);
+ sqlite3ExprSpan(A,&B,&X->span);
+}
+expr(A) ::= PLUS(B) expr(X). [UPLUS] {
+ A = sqlite3PExpr(pParse, TK_UPLUS, X, 0, 0);
+ sqlite3ExprSpan(A,&B,&X->span);
+}
+%type between_op {int}
+between_op(A) ::= BETWEEN. {A = 0;}
+between_op(A) ::= NOT BETWEEN. {A = 1;}
+expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
+ ExprList *pList = sqlite3ExprListAppend(pParse,0, X, 0);
+ pList = sqlite3ExprListAppend(pParse,pList, Y, 0);
+ A = sqlite3PExpr(pParse, TK_BETWEEN, W, 0, 0);
+ if( A ){
+ A->pList = pList;
+ }else{
+ sqlite3ExprListDelete(pList);
+ }
+ if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0, 0);
+ sqlite3ExprSpan(A,&W->span,&Y->span);
+}
+%ifndef SQLITE_OMIT_SUBQUERY
+ %type in_op {int}
+ in_op(A) ::= IN. {A = 0;}
+ in_op(A) ::= NOT IN. {A = 1;}
+ expr(A) ::= expr(X) in_op(N) LP exprlist(Y) RP(E). [IN] {
+ A = sqlite3PExpr(pParse, TK_IN, X, 0, 0);
+ if( A ){
+ A->pList = Y;
+ sqlite3ExprSetHeight(A);
+ }else{
+ sqlite3ExprListDelete(Y);
+ }
+ if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0, 0);
+ sqlite3ExprSpan(A,&X->span,&E);
+ }
+ expr(A) ::= LP(B) select(X) RP(E). {
+ A = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0);
+ if( A ){
+ A->pSelect = X;
+ sqlite3ExprSetHeight(A);
+ }else{
+ sqlite3SelectDelete(X);
+ }
+ sqlite3ExprSpan(A,&B,&E);
+ }
+ expr(A) ::= expr(X) in_op(N) LP select(Y) RP(E). [IN] {
+ A = sqlite3PExpr(pParse, TK_IN, X, 0, 0);
+ if( A ){
+ A->pSelect = Y;
+ sqlite3ExprSetHeight(A);
+ }else{
+ sqlite3SelectDelete(Y);
+ }
+ if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0, 0);
+ sqlite3ExprSpan(A,&X->span,&E);
+ }
+ expr(A) ::= expr(X) in_op(N) nm(Y) dbnm(Z). [IN] {
+ SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&Y,&Z);
+ A = sqlite3PExpr(pParse, TK_IN, X, 0, 0);
+ if( A ){
+ A->pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
+ sqlite3ExprSetHeight(A);
+ }else{
+ sqlite3SrcListDelete(pSrc);
+ }
+ if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0, 0);
+ sqlite3ExprSpan(A,&X->span,Z.z?&Z:&Y);
+ }
+ expr(A) ::= EXISTS(B) LP select(Y) RP(E). {
+ Expr *p = A = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0);
+ if( p ){
+ p->pSelect = Y;
+ sqlite3ExprSpan(p,&B,&E);
+ sqlite3ExprSetHeight(A);
+ }else{
+ sqlite3SelectDelete(Y);
+ }
+ }
+%endif SQLITE_OMIT_SUBQUERY
+
+/* CASE expressions */
+expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). {
+ A = sqlite3PExpr(pParse, TK_CASE, X, Z, 0);
+ if( A ){
+ A->pList = Y;
+ sqlite3ExprSetHeight(A);
+ }else{
+ sqlite3ExprListDelete(Y);
+ }
+ sqlite3ExprSpan(A, &C, &E);
+}
+%type case_exprlist {ExprList*}
+%destructor case_exprlist {sqlite3ExprListDelete($$);}
+case_exprlist(A) ::= case_exprlist(X) WHEN expr(Y) THEN expr(Z). {
+ A = sqlite3ExprListAppend(pParse,X, Y, 0);
+ A = sqlite3ExprListAppend(pParse,A, Z, 0);
+}
+case_exprlist(A) ::= WHEN expr(Y) THEN expr(Z). {
+ A = sqlite3ExprListAppend(pParse,0, Y, 0);
+ A = sqlite3ExprListAppend(pParse,A, Z, 0);
+}
+%type case_else {Expr*}
+%destructor case_else {sqlite3ExprDelete($$);}
+case_else(A) ::= ELSE expr(X). {A = X;}
+case_else(A) ::= . {A = 0;}
+%type case_operand {Expr*}
+%destructor case_operand {sqlite3ExprDelete($$);}
+case_operand(A) ::= expr(X). {A = X;}
+case_operand(A) ::= . {A = 0;}
+
+%type exprlist {ExprList*}
+%destructor exprlist {sqlite3ExprListDelete($$);}
+%type nexprlist {ExprList*}
+%destructor nexprlist {sqlite3ExprListDelete($$);}
+
+exprlist(A) ::= nexprlist(X). {A = X;}
+exprlist(A) ::= . {A = 0;}
+nexprlist(A) ::= nexprlist(X) COMMA expr(Y).
+ {A = sqlite3ExprListAppend(pParse,X,Y,0);}
+nexprlist(A) ::= expr(Y).
+ {A = sqlite3ExprListAppend(pParse,0,Y,0);}
+
+
+///////////////////////////// The CREATE INDEX command ///////////////////////
+//
+cmd ::= CREATE(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) dbnm(D)
+ ON nm(Y) LP idxlist(Z) RP(E). {
+ sqlite3CreateIndex(pParse, &X, &D,
+ sqlite3SrcListAppend(pParse->db,0,&Y,0), Z, U,
+ &S, &E, SQLITE_SO_ASC, NE);
+}
+
+%type uniqueflag {int}
+uniqueflag(A) ::= UNIQUE. {A = OE_Abort;}
+uniqueflag(A) ::= . {A = OE_None;}
+
+%type idxlist {ExprList*}
+%destructor idxlist {sqlite3ExprListDelete($$);}
+%type idxlist_opt {ExprList*}
+%destructor idxlist_opt {sqlite3ExprListDelete($$);}
+%type idxitem {Token}
+
+idxlist_opt(A) ::= . {A = 0;}
+idxlist_opt(A) ::= LP idxlist(X) RP. {A = X;}
+idxlist(A) ::= idxlist(X) COMMA idxitem(Y) collate(C) sortorder(Z). {
+ Expr *p = 0;
+ if( C.n>0 ){
+ p = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0);
+ sqlite3ExprSetColl(pParse, p, &C);
+ }
+ A = sqlite3ExprListAppend(pParse,X, p, &Y);
+ sqlite3ExprListCheckLength(pParse, A, SQLITE_MAX_COLUMN, "index");
+ if( A ) A->a[A->nExpr-1].sortOrder = Z;
+}
+idxlist(A) ::= idxitem(Y) collate(C) sortorder(Z). {
+ Expr *p = 0;
+ if( C.n>0 ){
+ p = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0);
+ sqlite3ExprSetColl(pParse, p, &C);
+ }
+ A = sqlite3ExprListAppend(pParse,0, p, &Y);
+ sqlite3ExprListCheckLength(pParse, A, SQLITE_MAX_COLUMN, "index");
+ if( A ) A->a[A->nExpr-1].sortOrder = Z;
+}
+idxitem(A) ::= nm(X). {A = X;}
+
+%type collate {Token}
+collate(C) ::= . {C.z = 0; C.n = 0;}
+collate(C) ::= COLLATE ids(X). {C = X;}
+
+
+///////////////////////////// The DROP INDEX command /////////////////////////
+//
+cmd ::= DROP INDEX ifexists(E) fullname(X). {sqlite3DropIndex(pParse, X, E);}
+
+///////////////////////////// The VACUUM command /////////////////////////////
+//
+%ifndef SQLITE_OMIT_VACUUM
+%ifndef SQLITE_OMIT_ATTACH
+cmd ::= VACUUM. {sqlite3Vacuum(pParse);}
+cmd ::= VACUUM nm. {sqlite3Vacuum(pParse);}
+%endif SQLITE_OMIT_ATTACH
+%endif SQLITE_OMIT_VACUUM
+
+///////////////////////////// The PRAGMA command /////////////////////////////
+//
+%ifndef SQLITE_OMIT_PRAGMA
+cmd ::= PRAGMA nm(X) dbnm(Z) EQ nmnum(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
+cmd ::= PRAGMA nm(X) dbnm(Z) EQ ON(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
+cmd ::= PRAGMA nm(X) dbnm(Z) EQ minus_num(Y). {
+ sqlite3Pragma(pParse,&X,&Z,&Y,1);
+}
+cmd ::= PRAGMA nm(X) dbnm(Z) LP nmnum(Y) RP. {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
+cmd ::= PRAGMA nm(X) dbnm(Z). {sqlite3Pragma(pParse,&X,&Z,0,0);}
+nmnum(A) ::= plus_num(X). {A = X;}
+nmnum(A) ::= nm(X). {A = X;}
+%endif SQLITE_OMIT_PRAGMA
+plus_num(A) ::= plus_opt number(X). {A = X;}
+minus_num(A) ::= MINUS number(X). {A = X;}
+number(A) ::= INTEGER|FLOAT(X). {A = X;}
+plus_opt ::= PLUS.
+plus_opt ::= .
+
+//////////////////////////// The CREATE TRIGGER command /////////////////////
+
+%ifndef SQLITE_OMIT_TRIGGER
+
+cmd ::= CREATE trigger_decl(A) BEGIN trigger_cmd_list(S) END(Z). {
+ Token all;
+ all.z = A.z;
+ all.n = (Z.z - A.z) + Z.n;
+ sqlite3FinishTrigger(pParse, S, &all);
+}
+
+trigger_decl(A) ::= temp(T) TRIGGER ifnotexists(NOERR) nm(B) dbnm(Z)
+ trigger_time(C) trigger_event(D)
+ ON fullname(E) foreach_clause when_clause(G). {
+ sqlite3BeginTrigger(pParse, &B, &Z, C, D.a, D.b, E, G, T, NOERR);
+ A = (Z.n==0?B:Z);
+}
+
+%type trigger_time {int}
+trigger_time(A) ::= BEFORE. { A = TK_BEFORE; }
+trigger_time(A) ::= AFTER. { A = TK_AFTER; }
+trigger_time(A) ::= INSTEAD OF. { A = TK_INSTEAD;}
+trigger_time(A) ::= . { A = TK_BEFORE; }
+
+%type trigger_event {struct TrigEvent}
+%destructor trigger_event {sqlite3IdListDelete($$.b);}
+trigger_event(A) ::= DELETE|INSERT(OP). {A.a = @OP; A.b = 0;}
+trigger_event(A) ::= UPDATE(OP). {A.a = @OP; A.b = 0;}
+trigger_event(A) ::= UPDATE OF inscollist(X). {A.a = TK_UPDATE; A.b = X;}
+
+foreach_clause ::= .
+foreach_clause ::= FOR EACH ROW.
+
+%type when_clause {Expr*}
+%destructor when_clause {sqlite3ExprDelete($$);}
+when_clause(A) ::= . { A = 0; }
+when_clause(A) ::= WHEN expr(X). { A = X; }
+
+%type trigger_cmd_list {TriggerStep*}
+%destructor trigger_cmd_list {sqlite3DeleteTriggerStep($$);}
+trigger_cmd_list(A) ::= trigger_cmd_list(Y) trigger_cmd(X) SEMI. {
+ if( Y ){
+ Y->pLast->pNext = X;
+ }else{
+ Y = X;
+ }
+ Y->pLast = X;
+ A = Y;
+}
+trigger_cmd_list(A) ::= . { A = 0; }
+
+%type trigger_cmd {TriggerStep*}
+%destructor trigger_cmd {sqlite3DeleteTriggerStep($$);}
+// UPDATE
+trigger_cmd(A) ::= UPDATE orconf(R) nm(X) SET setlist(Y) where_opt(Z).
+ { A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); }
+
+// INSERT
+trigger_cmd(A) ::= insert_cmd(R) INTO nm(X) inscollist_opt(F)
+ VALUES LP itemlist(Y) RP.
+ {A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y, 0, R);}
+
+trigger_cmd(A) ::= insert_cmd(R) INTO nm(X) inscollist_opt(F) select(S).
+ {A = sqlite3TriggerInsertStep(pParse->db, &X, F, 0, S, R);}
+
+// DELETE
+trigger_cmd(A) ::= DELETE FROM nm(X) where_opt(Y).
+ {A = sqlite3TriggerDeleteStep(pParse->db, &X, Y);}
+
+// SELECT
+trigger_cmd(A) ::= select(X). {A = sqlite3TriggerSelectStep(pParse->db, X); }
+
+// The special RAISE expression that may occur in trigger programs
+expr(A) ::= RAISE(X) LP IGNORE RP(Y). {
+ A = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0);
+ if( A ){
+ A->iColumn = OE_Ignore;
+ sqlite3ExprSpan(A, &X, &Y);
+ }
+}
+expr(A) ::= RAISE(X) LP raisetype(T) COMMA nm(Z) RP(Y). {
+ A = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &Z);
+ if( A ) {
+ A->iColumn = T;
+ sqlite3ExprSpan(A, &X, &Y);
+ }
+}
+%endif !SQLITE_OMIT_TRIGGER
+
+%type raisetype {int}
+raisetype(A) ::= ROLLBACK. {A = OE_Rollback;}
+raisetype(A) ::= ABORT. {A = OE_Abort;}
+raisetype(A) ::= FAIL. {A = OE_Fail;}
+
+
+//////////////////////// DROP TRIGGER statement //////////////////////////////
+%ifndef SQLITE_OMIT_TRIGGER
+cmd ::= DROP TRIGGER ifexists(NOERR) fullname(X). {
+ sqlite3DropTrigger(pParse,X,NOERR);
+}
+%endif !SQLITE_OMIT_TRIGGER
+
+//////////////////////// ATTACH DATABASE file AS name /////////////////////////
+%ifndef SQLITE_OMIT_ATTACH
+cmd ::= ATTACH database_kw_opt expr(F) AS expr(D) key_opt(K). {
+ sqlite3Attach(pParse, F, D, K);
+}
+cmd ::= DETACH database_kw_opt expr(D). {
+ sqlite3Detach(pParse, D);
+}
+
+%type key_opt {Expr*}
+%destructor key_opt {sqlite3ExprDelete($$);}
+key_opt(A) ::= . { A = 0; }
+key_opt(A) ::= KEY expr(X). { A = X; }
+
+database_kw_opt ::= DATABASE.
+database_kw_opt ::= .
+%endif SQLITE_OMIT_ATTACH
+
+////////////////////////// REINDEX collation //////////////////////////////////
+%ifndef SQLITE_OMIT_REINDEX
+cmd ::= REINDEX. {sqlite3Reindex(pParse, 0, 0);}
+cmd ::= REINDEX nm(X) dbnm(Y). {sqlite3Reindex(pParse, &X, &Y);}
+%endif SQLITE_OMIT_REINDEX
+
+/////////////////////////////////// ANALYZE ///////////////////////////////////
+%ifndef SQLITE_OMIT_ANALYZE
+cmd ::= ANALYZE. {sqlite3Analyze(pParse, 0, 0);}
+cmd ::= ANALYZE nm(X) dbnm(Y). {sqlite3Analyze(pParse, &X, &Y);}
+%endif
+
+//////////////////////// ALTER TABLE table ... ////////////////////////////////
+%ifndef SQLITE_OMIT_ALTERTABLE
+cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). {
+ sqlite3AlterRenameTable(pParse,X,&Z);
+}
+cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column(Y). {
+ sqlite3AlterFinishAddColumn(pParse, &Y);
+}
+add_column_fullname ::= fullname(X). {
+ sqlite3AlterBeginAddColumn(pParse, X);
+}
+kwcolumn_opt ::= .
+kwcolumn_opt ::= COLUMNKW.
+%endif SQLITE_OMIT_ALTERTABLE
+
+//////////////////////// CREATE VIRTUAL TABLE ... /////////////////////////////
+%ifndef SQLITE_OMIT_VIRTUALTABLE
+cmd ::= create_vtab. {sqlite3VtabFinishParse(pParse,0);}
+cmd ::= create_vtab LP vtabarglist RP(X). {sqlite3VtabFinishParse(pParse,&X);}
+create_vtab ::= CREATE VIRTUAL TABLE nm(X) dbnm(Y) USING nm(Z). {
+ sqlite3VtabBeginParse(pParse, &X, &Y, &Z);
+}
+vtabarglist ::= vtabarg.
+vtabarglist ::= vtabarglist COMMA vtabarg.
+vtabarg ::= . {sqlite3VtabArgInit(pParse);}
+vtabarg ::= vtabarg vtabargtoken.
+vtabargtoken ::= ANY(X). {sqlite3VtabArgExtend(pParse,&X);}
+vtabargtoken ::= lp anylist RP(X). {sqlite3VtabArgExtend(pParse,&X);}
+lp ::= LP(X). {sqlite3VtabArgExtend(pParse,&X);}
+anylist ::= .
+anylist ::= anylist ANY(X). {sqlite3VtabArgExtend(pParse,&X);}
+%endif SQLITE_OMIT_VIRTUALTABLE
Added: test-suite/trunk/MultiSource/Applications/lemon/wireshark_COPYING
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/wireshark_COPYING?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/wireshark_COPYING (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/wireshark_COPYING Wed Mar 26 12:03:57 2008
@@ -0,0 +1,378 @@
+This text consists of two parts:
+
+Part I: Some remarks regarding the license given in
+Part II: The actual license that covers Wireshark.
+
+When in doubt: Part II is the legally binding part, Part I is just
+there to make it easier for people that are not familiar with the
+GPLv2.
+
+
+------------------------------------------------------------------------
+Part I:
+
+Wireshark is distributed under the GNU GPLv2. There are no restrictions
+on its use. There are restrictions on its distribution in source or
+binary form.
+
+Most parts of Wireshark are covered by a "GPL version 2 or later" license.
+Some files are covered by different licenses that are compatible with
+the GPLv2.
+
+As a notable exception the pidl utility at tools/pidl is covered by a
+"GPL version 3 or later" license. Note that only the tool itself
+is covered by this license, not the source code generated by it. The
+pidl authors do not consider generated code a derived work of pidl.
+
+Parts of Wireshark can be built and distributed as libraries. These
+parts are still covered by the GPL, and NOT by the Lesser General Public
+License or any other license.
+
+If you integrate all or part of Wireshark into your own application, then
+that application must be released under a license compatible with the GPL.
+
+The full text of the GNU GPL follows.
+
+------------------------------------------------------------------------
+Part II:
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Added: test-suite/trunk/MultiSource/Applications/lemon/wireshark_dtd_grammar.lemon
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/wireshark_dtd_grammar.lemon?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/wireshark_dtd_grammar.lemon (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/wireshark_dtd_grammar.lemon Wed Mar 26 12:03:57 2008
@@ -0,0 +1,217 @@
+%include {
+
+/* dtd_parser.lemon
+* XML dissector for wireshark
+* XML's DTD grammar
+*
+* Copyright 2005, Luis E. Garcia Ontanon <luis.ontanon at gmail.com>
+*
+* $Id: dtd_grammar.lemon 20443 2007-01-15 20:14:00Z lego $
+*
+* Wireshark - Network traffic analyzer
+* By Gerald Combs <gerald at wireshark.org>
+* Copyright 1998 Gerald Combs
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <stdio.h>
+#include <glib.h>
+#include "dtd.h"
+#include "dtd_parse.h"
+
+static dtd_named_list_t* dtd_named_list_new(gchar* name, GPtrArray* list) {
+ dtd_named_list_t* nl = g_malloc(sizeof(dtd_named_list_t));
+
+ nl->name = name;
+ nl->list = list;
+
+ return nl;
+}
+
+static GPtrArray* g_ptr_array_join(GPtrArray* a, GPtrArray* b){
+
+ while(b->len > 0) {
+ g_ptr_array_add(a,g_ptr_array_remove_index_fast(b,0));
+ }
+
+ g_ptr_array_free(b,TRUE);
+
+ return a;
+}
+
+}
+
+%name DtdParse
+
+%extra_argument { dtd_build_data_t *bd }
+
+%token_destructor {
+ if ($$) {
+ if ($$->text) g_free($$->text);
+ if ($$->location) g_free($$->location);
+ g_free($$);
+ }
+}
+
+%syntax_error {
+ if (!TOKEN)
+ g_string_sprintfa(bd->error,"syntax error at end of file");
+ else
+ g_string_sprintfa(bd->error,"syntax error in %s at or before '%s': \n", TOKEN->location,TOKEN->text);
+}
+
+%parse_failure {
+ g_string_sprintfa(bd->error,"DTD parsing failure\n");
+}
+
+%token_prefix TOKEN_
+
+%token_type { dtd_token_data_t* }
+
+dtd ::= doctype.
+dtd ::= dtd_parts.
+
+doctype ::= TAG_START DOCTYPE_KW NAME(Name) OPEN_BRACKET dtd_parts CLOSE_BRACKET TAG_STOP. {
+ dtd_named_list_t* root;
+ GPtrArray* root_elems = g_ptr_array_new();
+ guint i;
+
+ if(! bd->proto_name) {
+ bd->proto_name = Name->text;
+ }
+
+ if(bd->proto_root)
+ g_free(bd->proto_root);
+
+ bd->proto_root = Name->text;
+
+ g_strdown(bd->proto_name);
+
+ for( i = 0; i< bd->elements->len; i++) {
+ dtd_named_list_t* el = g_ptr_array_index(bd->elements,i);
+
+ g_ptr_array_add(root_elems,g_strdup(el->name));
+ }
+
+ root = dtd_named_list_new(g_strdup(Name->text),root_elems);
+
+ g_ptr_array_add(bd->elements,root);
+
+ g_free(Name->location);
+ g_free(Name);
+
+}
+
+dtd_parts ::= dtd_parts element(Element). { g_ptr_array_add(bd->elements,Element); }
+dtd_parts ::= dtd_parts attlist(Attlist). { g_ptr_array_add(bd->attributes,Attlist); }
+dtd_parts ::= element(Element). { g_ptr_array_add(bd->elements,Element); }
+dtd_parts ::= attlist(Attlist). { g_ptr_array_add(bd->attributes,Attlist); }
+
+%type attlist { dtd_named_list_t* }
+attlist(A) ::= TAG_START ATTLIST_KW NAME(B) attrib_list(TheList) TAG_STOP. {
+ g_strdown(B->text);
+ A = dtd_named_list_new(B->text,TheList);
+ g_free(B->location);
+ g_free(B);
+}
+
+%type element { dtd_named_list_t* }
+element(A) ::= TAG_START ELEMENT_KW NAME(B) sub_elements(C) TAG_STOP. {
+ g_strdown(B->text);
+ A = dtd_named_list_new(B->text,C);
+ g_free(B->location);
+ g_free(B);
+}
+
+%type attrib_list { GPtrArray* }
+attrib_list(A) ::= attrib_list(B) attrib(C). { g_ptr_array_add(B,C); A = B; }
+attrib_list(A) ::= attrib(B). { A = g_ptr_array_new(); g_ptr_array_add(A,B); }
+
+%type attrib { gchar* }
+attrib(A) ::= NAME(B) att_type att_default. {
+ A = B->text;
+ g_strdown(A);
+ g_free(B->location);
+ g_free(B);
+}
+
+att_type ::= ATT_TYPE.
+att_type ::= enumeration.
+
+att_default ::= ATT_DEF.
+att_default ::= ATT_DEF_WITH_VALUE QUOTED.
+att_default ::= QUOTED.
+att_default ::= IMPLIED_KW.
+att_default ::= REQUIRED_KW.
+
+enumeration ::= OPEN_PARENS enum_list CLOSE_PARENS.
+
+enum_list ::= enum_list PIPE enum_item.
+enum_list ::= enum_item.
+enum_list ::= enumeration.
+enum_list ::= enum_list PIPE enumeration.
+
+enum_item ::= NAME.
+enum_item ::= QUOTED.
+
+
+%type sub_elements { GPtrArray* }
+sub_elements(A) ::= sub_elements(B) STAR. {A=B;}
+sub_elements(A) ::= sub_elements(B) PLUS. {A=B;}
+sub_elements(A) ::= sub_elements(B) QUESTION. {A=B;}
+sub_elements(A) ::= OPEN_PARENS ELEM_DATA CLOSE_PARENS. { A = g_ptr_array_new(); }
+sub_elements(A) ::= OPEN_PARENS element_list(B) COMMA ELEM_DATA CLOSE_PARENS. { A = B; }
+sub_elements(A) ::= OPEN_PARENS element_list(B) PIPE ELEM_DATA CLOSE_PARENS. { A = B; }
+sub_elements(A) ::= OPEN_PARENS element_list(B) CLOSE_PARENS. { A = B; }
+sub_elements(A) ::= EMPTY_KW. { A = g_ptr_array_new(); }
+
+%type element_list { GPtrArray* }
+element_list(A) ::= element_list(B) COMMA element_child(C). { g_ptr_array_add(B,C); A = B; }
+element_list(A) ::= element_list(B) PIPE element_child(C). { g_ptr_array_add(B,C); A = B; }
+element_list(A) ::= element_child(B). { A = g_ptr_array_new(); g_ptr_array_add(A,B); }
+element_list(A) ::= sub_elements(B). { A = B; }
+element_list(A) ::= element_list(B) COMMA sub_elements(C). { A = g_ptr_array_join(B,C); }
+element_list(A) ::= element_list(B) PIPE sub_elements(C). { A = g_ptr_array_join(B,C); }
+
+%type element_child { gchar* }
+element_child(A) ::= NAME(B). {
+ A = B->text;
+ g_strdown(A);
+ g_free(B->location);
+ g_free(B);
+}
+
+element_child(A) ::= NAME(B) STAR. {
+ A = B->text;
+ g_strdown(A);
+ g_free(B->location);
+ g_free(B);
+}
+
+element_child(A) ::= NAME(B) QUESTION. {
+ A = B->text;
+ g_strdown(A);
+ g_free(B->location);
+ g_free(B);
+}
+
+element_child(A) ::= NAME(B) PLUS. {
+ A = B->text;
+ g_strdown(A);
+ g_free(B->location);
+ g_free(B);
+}
+
Added: test-suite/trunk/MultiSource/Applications/lemon/wireshark_grammar.lemon
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/wireshark_grammar.lemon?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/wireshark_grammar.lemon (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/wireshark_grammar.lemon Wed Mar 26 12:03:57 2008
@@ -0,0 +1,294 @@
+/* $Id: grammar.lemon 21321 2007-04-03 19:08:00Z lego $ */
+
+%include {
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dfilter-int.h"
+#include "syntax-tree.h"
+#include "sttype-range.h"
+#include "sttype-test.h"
+#include "sttype-function.h"
+#include "drange.h"
+
+#include "grammar.h"
+
+#ifdef _WIN32
+#pragma warning(disable:4671)
+#endif
+
+/* End of C code */
+}
+
+/* Parser Information */
+%name Dfilter
+%token_prefix TOKEN_
+%extra_argument {dfwork_t *dfw}
+
+/* Terminal and Non-Terminal types and destructors */
+%token_type {stnode_t*}
+%token_destructor {stnode_free($$);}
+
+%type sentence {stnode_t*}
+
+
+%type expr {stnode_t*}
+%destructor expr {stnode_free($$);}
+
+%type entity {stnode_t*}
+%destructor entity {stnode_free($$);}
+
+%type relation_test {stnode_t*}
+%destructor relation_test {stnode_free($$);}
+
+%type logical_test {stnode_t*}
+%destructor logical_test {stnode_free($$);}
+
+%type rel_op2 {test_op_t}
+
+%type range {stnode_t*}
+%destructor range {stnode_free($$);}
+
+%type drnode {drange_node*}
+%destructor drnode {drange_node_free($$);}
+
+%type drnode_list {GSList*}
+%destructor drnode_list {drange_node_free_list($$);}
+
+%type funcparams {GSList*}
+%destructor funcparams {st_funcparams_free($$);}
+
+/* This is called as soon as a syntax error happens. After that,
+any "error" symbols are shifted, if possible. */
+%syntax_error {
+
+ header_field_info *hfinfo;
+
+ if (!TOKEN) {
+ dfilter_fail("Unexpected end of filter string.");
+ return;
+ }
+
+ switch(stnode_type_id(TOKEN)) {
+ case STTYPE_UNINITIALIZED:
+ dfilter_fail("Syntax error.");
+ break;
+ case STTYPE_TEST:
+ dfilter_fail("Syntax error, TEST.");
+ break;
+ case STTYPE_STRING:
+ dfilter_fail("The string \"%s\" was unexpected in this context.",
+ stnode_data(TOKEN));
+ break;
+ case STTYPE_UNPARSED:
+ dfilter_fail("\"%s\" was unexpected in this context.",
+ stnode_data(TOKEN));
+ break;
+ case STTYPE_INTEGER:
+ dfilter_fail("The integer %d was unexpected in this context.",
+ stnode_value(TOKEN));
+ break;
+ case STTYPE_FIELD:
+ hfinfo = stnode_data(TOKEN);
+ dfilter_fail("Syntax error near \"%s\".", hfinfo->abbrev);
+ break;
+ case STTYPE_FUNCTION:
+ dfilter_fail("The function s was unexpected in this context.");
+ break;
+
+ /* These aren't handed to use as terminal tokens from
+ the scanner, so was can assert that we'll never
+ see them here. */
+ case STTYPE_NUM_TYPES:
+ case STTYPE_RANGE:
+ case STTYPE_FVALUE:
+ g_assert_not_reached();
+ break;
+ }
+}
+
+/* When a parse fails, mark an error. This occurs after
+the above syntax_error code and after the parser fails to
+use error recovery, shifting an "error" symbol and successfully
+shifting 3 more symbols. */
+%parse_failure {
+ dfw->syntax_error = TRUE;
+}
+
+/* ----------------- The grammar -------------- */
+
+/* Associativity */
+%left TEST_AND.
+%left TEST_OR.
+%nonassoc TEST_EQ TEST_NE TEST_LT TEST_LE TEST_GT TEST_GE TEST_CONTAINS TEST_MATCHES TEST_BITWISE_AND.
+%right TEST_NOT.
+
+/* Top-level targets */
+sentence ::= expr(X). { dfw->st_root = X; }
+sentence ::= . { dfw->st_root = NULL; }
+
+expr(X) ::= relation_test(R). { X = R; }
+expr(X) ::= logical_test(L). { X = L; }
+
+
+/* Logical tests */
+logical_test(T) ::= expr(E) TEST_AND expr(F).
+{
+ T = stnode_new(STTYPE_TEST, NULL);
+ sttype_test_set2(T, TEST_OP_AND, E, F);
+}
+
+logical_test(T) ::= expr(E) TEST_OR expr(F).
+{
+ T = stnode_new(STTYPE_TEST, NULL);
+ sttype_test_set2(T, TEST_OP_OR, E, F);
+}
+
+logical_test(T) ::= TEST_NOT expr(E).
+{
+ T = stnode_new(STTYPE_TEST, NULL);
+ sttype_test_set1(T, TEST_OP_NOT, E);
+}
+
+logical_test(T) ::= entity(E).
+{
+ T = stnode_new(STTYPE_TEST, NULL);
+ sttype_test_set1(T, TEST_OP_EXISTS, E);
+}
+
+
+
+/* Entities, or things that can be compared/tested/checked */
+entity(E) ::= FIELD(F). { E = F; }
+entity(E) ::= STRING(S). { E = S; }
+entity(E) ::= UNPARSED(U). { E = U; }
+entity(E) ::= range(R). { E = R; }
+
+
+/* Ranges */
+range(R) ::= FIELD(F) LBRACKET drnode_list(L) RBRACKET.
+{
+ R = stnode_new(STTYPE_RANGE, NULL);
+ sttype_range_set(R, F, L);
+
+ /* Delete the list, but not the drange_nodes that
+ * the list contains. */
+ g_slist_free(L);
+}
+
+drnode_list(L) ::= drnode(D).
+{
+ L = g_slist_append(NULL, D);
+}
+
+drnode_list(L) ::= drnode_list(P) COMMA drnode(D).
+{
+ L = g_slist_append(P, D);
+}
+
+/* x:y is offset:length */
+drnode(D) ::= INTEGER(X) COLON INTEGER(Y).
+{
+ D = drange_node_new();
+ drange_node_set_start_offset(D, stnode_value(X));
+ drange_node_set_length(D, stnode_value(Y));
+
+ stnode_free(X);
+ stnode_free(Y);
+}
+
+/* x-y == offset:offset */
+drnode(D) ::= INTEGER(X) HYPHEN INTEGER(Y).
+{
+ D = drange_node_new();
+ drange_node_set_start_offset(D, stnode_value(X));
+ drange_node_set_end_offset(D, stnode_value(Y));
+
+ stnode_free(X);
+ stnode_free(Y);
+}
+
+
+/* :y == from start to offset */
+drnode(D) ::= COLON INTEGER(Y).
+{
+ D = drange_node_new();
+ drange_node_set_start_offset(D, 0);
+ drange_node_set_length(D, stnode_value(Y));
+
+ stnode_free(Y);
+}
+
+/* x: from offset to end */
+drnode(D) ::= INTEGER(X) COLON.
+{
+ D = drange_node_new();
+ drange_node_set_start_offset(D, stnode_value(X));
+ drange_node_set_to_the_end(D);
+
+ stnode_free(X);
+}
+
+/* x == x:1 */
+drnode(D) ::= INTEGER(X).
+{
+ D = drange_node_new();
+ drange_node_set_start_offset(D, stnode_value(X));
+ drange_node_set_length(D, 1);
+
+ stnode_free(X);
+}
+
+
+
+/* Relational tests */
+relation_test(T) ::= entity(E) rel_op2(O) entity(F).
+{
+ T = stnode_new(STTYPE_TEST, NULL);
+ sttype_test_set2(T, O, E, F);
+}
+
+rel_op2(O) ::= TEST_EQ. { O = TEST_OP_EQ; }
+rel_op2(O) ::= TEST_NE. { O = TEST_OP_NE; }
+rel_op2(O) ::= TEST_GT. { O = TEST_OP_GT; }
+rel_op2(O) ::= TEST_GE. { O = TEST_OP_GE; }
+rel_op2(O) ::= TEST_LT. { O = TEST_OP_LT; }
+rel_op2(O) ::= TEST_LE. { O = TEST_OP_LE; }
+rel_op2(O) ::= TEST_BITWISE_AND. { O = TEST_OP_BITWISE_AND; }
+rel_op2(O) ::= TEST_CONTAINS. { O = TEST_OP_CONTAINS; }
+rel_op2(O) ::= TEST_MATCHES. { O = TEST_OP_MATCHES; }
+
+
+/* Functions */
+
+/* A function can have one or more parameters */
+entity(E) ::= FUNCTION(F) LPAREN funcparams(P) RPAREN.
+{
+ E = F;
+ sttype_function_set_params(E, P);
+}
+
+/* A function can have zero parameters. */
+entity(E) ::= FUNCTION(F) LPAREN RPAREN.
+{
+ E = F;
+}
+
+funcparams(P) ::= entity(E).
+{
+ P = g_slist_append(NULL, E);
+}
+
+funcparams(P) ::= funcparams(L) COMMA entity(E).
+{
+ P = g_slist_append(L, E);
+}
+
+
+/* Any expression inside parens is simply that expression */
+expr(X) ::= LPAREN expr(Y) RPAREN.
+{
+ X = Y;
+}
+
Added: test-suite/trunk/MultiSource/Applications/lemon/wireshark_mate_grammar.lemon
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/wireshark_mate_grammar.lemon?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/wireshark_mate_grammar.lemon (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/wireshark_mate_grammar.lemon Wed Mar 26 12:03:57 2008
@@ -0,0 +1,719 @@
+%include {
+
+/* mate_grammar.lemon
+* MATE's configuration language grammar
+*
+* Copyright 2005, Luis E. Garcia Ontanon <luis.ontanon at gmail.com>
+*
+* $Id: mate_grammar.lemon 23221 2007-10-17 21:25:16Z morriss $
+*
+* Wireshark - Network traffic analyzer
+* By Gerald Combs <gerald at wireshark.org>
+* Copyright 1998 Gerald Combs
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "mate.h"
+#include "mate_grammar.h"
+#include <epan/ws_strsplit.h>
+#include <wiretap/file_util.h>
+
+#define DUMMY void*
+
+typedef struct _extraction {
+ gchar* as;
+ header_field_info* hfi;
+ struct _extraction* next;
+ struct _extraction* last;
+} extraction_t;
+
+typedef struct _pdu_criteria_t {
+ AVPL* criterium_avpl;
+ avpl_match_mode criterium_match_mode;
+ accept_mode_t criterium_accept_mode;
+} pdu_criteria_t;
+
+typedef struct _gop_options {
+ gop_tree_mode_t pdu_tree_mode;
+ gboolean drop_unassigned;
+ gboolean show_times;
+ float expiration;
+ float idle_timeout;
+ float lifetime;
+ AVPL* start;
+ AVPL* stop;
+ AVPL* extras;
+} gop_options_t;
+
+typedef struct _gog_statements {
+ float expiration;
+ gop_tree_mode_t gop_tree_mode;
+ GPtrArray* transform_list;
+ AVPL* extras;
+ LoAL* current_gogkeys;
+} gog_statement_t;
+
+typedef struct _transf_match_t {
+ avpl_match_mode match_mode;
+ AVPL* avpl;
+} transf_match_t;
+
+typedef struct _transf_action_t {
+ avpl_replace_mode replace_mode;
+ AVPL* avpl;
+} transf_action_t;
+
+static void configuration_error(mate_config* mc, const gchar* fmt, ...) {
+ static gchar error_buffer[256];
+ const gchar* incl;
+ gint i;
+ mate_config_frame* current_frame;
+ va_list list;
+
+ va_start( list, fmt );
+ g_vsnprintf(error_buffer,sizeof(error_buffer),fmt,list);
+ va_end( list );
+
+ i = (gint) mc->config_stack->len;
+
+ while (i--) {
+
+ if (i>0) {
+ incl = "\n included from: ";
+ } else {
+ incl = " ";
+ }
+
+ current_frame = g_ptr_array_index(mc->config_stack,(guint)i);
+
+ g_string_sprintfa(mc->config_error,"%s%s at line %u",incl, current_frame->filename, current_frame->linenum);
+ }
+
+ g_string_sprintfa(mc->config_error,": %s\n",error_buffer);
+
+ THROW(MateConfigError);
+
+}
+
+static AVPL_Transf* new_transform_elem(AVPL* match, AVPL* replace, avpl_match_mode match_mode, avpl_replace_mode replace_mode) {
+ AVPL_Transf* t = g_malloc(sizeof(AVPL_Transf));
+
+ t->name = NULL;
+ t->match = match;
+ t->replace = replace;
+ t->match_mode = match_mode;
+ t->replace_mode = replace_mode;
+
+ t->map = NULL;
+ t->next = NULL;
+
+ return t;
+}
+
+static gchar* recolonize(mate_config* mc, gchar* s) {
+ GString* str = g_string_new("");
+ gchar** vec;
+ gchar* r;
+ guint i,v;
+ gchar c;
+
+ vec = g_strsplit(s,":",0);
+
+ for (i = 0; vec[i]; i++) {
+ g_strdown(vec[i]);
+
+ v = 0;
+ switch ( strlen(vec[i]) ) {
+ case 2:
+ c = vec[i][1];
+ vec[i][1] = vec[i][0];
+ vec[i][0] = c;
+ if (vec[i][0] >= '0' && vec[i][0] <= '9') {
+ v += (vec[i][1] - '0' )*16;
+ } else {
+ v += (vec[i][1] - 'a' + 10)*16;
+ }
+ case 1:
+ if (vec[i][0] >= '0' && vec[i][0] <= '9') {
+ v += (vec[i][0] - '0' );
+ } else {
+ v += (vec[i][0] - 'a' + 10);
+ }
+ case 0:
+ break;
+ default:
+ configuration_error(mc,"bad token %s",s);
+ }
+
+ g_string_sprintfa(str,":%.2X",v);
+ }
+
+ g_strfreev(vec);
+
+ g_string_erase(str,0,1);
+
+ r = str->str;
+
+ g_string_free(str,FALSE);
+
+ return r;
+}
+
+}
+
+%name MateParser
+
+%token_prefix TOKEN_
+
+%token_type { gchar* }
+%token_destructor { if ($$) g_free($$); }
+
+%extra_argument { mate_config* mc }
+
+%syntax_error {
+ configuration_error(mc,"Syntax Error before %s",yyminor);
+}
+
+%parse_failure {
+ configuration_error(mc,"Parse Error");
+}
+
+%type transform_decl { AVPL_Transf* }
+%type transform_body { AVPL_Transf* }
+%type transform_statements { AVPL_Transf* }
+%type transform_statement { AVPL_Transf* }
+%type transform_match { transf_match_t* }
+%type transform_action { transf_action_t* }
+%type match_mode { avpl_match_mode }
+%type action_mode { avpl_replace_mode }
+
+%type gop_name { gchar* }
+%type time_value { float }
+%type pdu_name { gchar* }
+%type gop_tree_mode { gop_tree_mode_t }
+%type true_false { gboolean }
+
+%type criteria_statement { pdu_criteria_t* }
+%type accept_mode { accept_mode_t }
+%type pdu_drop_unassigned_statement { gboolean }
+%type discard_pdu_data_statement { gboolean }
+%type last_extracted_statement { gboolean }
+
+%type extraction_statement {extraction_t*}
+%type extraction_statements {extraction_t*}
+
+%type gop_options { gop_options_t* }
+
+%type gop_start_statement { AVPL* }
+%type gop_stop_statement { AVPL* }
+%type extra_statement { AVPL* }
+%type gop_drop_unassigned_statement { gboolean }
+%type show_goptree_statement { gop_tree_mode_t }
+%type show_times_statement { gboolean }
+%type gop_expiration_statement { float }
+%type idle_timeout_statement { float }
+%type lifetime_statement { float }
+
+%type gog_statements { gog_statement_t* }
+%type gog_expiration_statement { float }
+%type gog_goptree_statement { gop_tree_mode_t }
+%type gog_key_statements { LoAL* }
+%type gog_key_statement { AVPL* }
+%type transform_list_statement { GPtrArray* }
+%type transform { AVPL_Transf* }
+%type gop_tree_type { gop_tree_mode_t }
+
+%type payload_statement { GPtrArray* }
+%type proto_stack { GPtrArray* }
+%type field { header_field_info* }
+%type transform_list { GPtrArray* }
+%type avpl { AVPL* }
+%type avps { AVPL* }
+%type avp { AVP* }
+%type value { gchar* }
+%type avp_oneoff { gchar* }
+
+
+mate_config ::= decls.
+
+decls ::= decls decl.
+decls ::= .
+
+decl ::= pdu_decl.
+decl ::= gop_decl.
+decl ::= gog_decl.
+decl ::= transform_decl.
+decl ::= defaults_decl.
+decl ::= debug_decl.
+decl ::= DONE_KW SEMICOLON.
+
+/************* DEBUG
+*/
+
+debug_decl ::= DEBUG_KW OPEN_BRACE dbgfile_default dbglevel_default pdu_dbglevel_default gop_dbglevel_default gog_dbglevel_default CLOSE_BRACE SEMICOLON.
+
+dbgfile_default ::= FILENAME_KW QUOTED(Filename) SEMICOLON. { mc->dbg_facility = eth_fopen(Filename,"w"); if (mc->dbg_facility == NULL) report_open_failure(Filename,errno,TRUE); }
+dbgfile_default ::= FILENAME_KW NAME(Filename) SEMICOLON. { mc->dbg_facility = eth_fopen(Filename,"w"); if (mc->dbg_facility == NULL) report_open_failure(Filename,errno,TRUE); }
+dbgfile_default ::= .
+
+dbglevel_default ::= LEVEL_KW INTEGER(LevelString) SEMICOLON. { mc->dbg_lvl = (int) strtol(LevelString,NULL,10); }
+dbglevel_default ::= .
+
+pdu_dbglevel_default ::= PDU_KW LEVEL_KW INTEGER(LevelString) SEMICOLON. { mc->dbg_pdu_lvl = (int) strtol(LevelString,NULL,10); }
+pdu_dbglevel_default ::= .
+
+gop_dbglevel_default ::= GOP_KW LEVEL_KW INTEGER(LevelString) SEMICOLON. { mc->dbg_gop_lvl = (int) strtol(LevelString,NULL,10); }
+gop_dbglevel_default ::= .
+
+gog_dbglevel_default ::= GOG_KW LEVEL_KW INTEGER(LevelString) SEMICOLON. { mc->dbg_gog_lvl = (int) strtol(LevelString,NULL,10); }
+gog_dbglevel_default ::= .
+
+
+/************* DEFAULTS
+*/
+
+defaults_decl ::= DEFAULT_KW OPEN_BRACE pdu_defaults gop_defaults gog_defaults CLOSE_BRACE SEMICOLON.
+
+pdu_defaults ::= PDU_KW OPEN_BRACE pdu_last_extracted_default pdu_drop_unassigned_default pdu_discard_default CLOSE_BRACE SEMICOLON.
+pdu_defaults ::= .
+
+pdu_last_extracted_default ::= LAST_EXTRACTED_KW true_false(Flag) SEMICOLON. { mc->defaults.pdu.last_extracted = Flag; }
+pdu_last_extracted_default ::= .
+
+pdu_drop_unassigned_default ::= DROP_UNASSIGNED_KW true_false(Flag) SEMICOLON. { mc->defaults.pdu.drop_unassigned = Flag; }
+pdu_drop_unassigned_default ::= .
+
+pdu_discard_default ::= DISCARD_PDU_DATA_KW true_false(Flag) SEMICOLON. { mc->defaults.pdu.discard = Flag; }
+pdu_discard_default ::= .
+
+gop_defaults ::= GOP_KW OPEN_BRACE gop_expiration_default gop_idle_timeout_default gop_lifetime_default gop_drop_unassigned_default gop_tree_mode_default gop_show_times_default CLOSE_BRACE SEMICOLON.
+gop_defaults ::= .
+
+gop_expiration_default ::= EXPIRATION_KW time_value(B) SEMICOLON. { mc->defaults.gop.expiration = B; }
+gop_expiration_default ::= .
+
+gop_idle_timeout_default ::= IDLE_TIMEOUT_KW time_value(B) SEMICOLON. { mc->defaults.gop.idle_timeout = B; }
+gop_idle_timeout_default ::= .
+
+gop_lifetime_default ::= LIFETIME_KW time_value(B) SEMICOLON. { mc->defaults.gop.lifetime = B; }
+gop_lifetime_default ::= .
+
+gop_drop_unassigned_default ::= DROP_UNASSIGNED_KW true_false(B) SEMICOLON. { mc->defaults.gop.drop_unassigned = B; }
+gop_drop_unassigned_default ::= .
+
+gop_tree_mode_default ::= SHOW_TREE_KW gop_tree_mode(B) SEMICOLON. { mc->defaults.gop.pdu_tree_mode = B; }
+gop_tree_mode_default ::= .
+
+gop_show_times_default ::= SHOW_TIMES_KW true_false(B) SEMICOLON. { mc->defaults.gop.show_times = B; }
+gop_show_times_default ::= .
+
+gog_defaults ::= GOG_KW OPEN_BRACE gog_expiration_default gop_tree_mode_default gog_goptree_default CLOSE_BRACE SEMICOLON.
+gog_defaults ::= .
+
+gog_expiration_default ::= EXPIRATION_KW time_value(B) SEMICOLON. { mc->defaults.gop.expiration = B; }
+gog_expiration_default ::= .
+
+gog_goptree_default ::= GOP_TREE_KW gop_tree_type(B) SEMICOLON. { mc->defaults.gog.gop_tree_mode = B; }
+gog_goptree_default ::= .
+
+
+/******************************************* TRANSFORM
+*/
+
+transform_decl(A) ::= TRANSFORM_KW NAME(B) transform_body(C) SEMICOLON. {
+ AVPL_Transf* c;
+
+ if ( g_hash_table_lookup(mc->transfs,B) ) {
+ configuration_error(mc,"A transformation called '%s' exists already",B);
+ }
+
+ for ( c = C; c; c = c->next )
+ c->name = g_strdup(B);
+
+ g_hash_table_insert(mc->transfs,C->name,C);
+
+ A = NULL;
+}
+
+transform_body(A) ::= OPEN_BRACE transform_statements(B) CLOSE_BRACE. { A = B; }
+
+transform_statements(A) ::= transform_statements(C) transform_statement(B). {
+ AVPL_Transf* c;
+
+ for ( c = C; c->next; c = c->next ) ;
+ c->next = B;
+ A = C;
+}
+
+transform_statements(A) ::= transform_statement(B). { A = B; }
+
+transform_statement(A) ::= transform_match(Match) transform_action(Action) SEMICOLON. {
+ A = new_transform_elem(Match->avpl,Action->avpl,Match->match_mode,Action->replace_mode);
+}
+
+transform_match(A) ::= MATCH_KW match_mode(Mode) avpl(Avpl). {
+ A = g_malloc(sizeof(transf_match_t));
+ A->match_mode = Mode;
+ A->avpl = Avpl;
+}
+
+transform_match(A) ::= . {
+ A = g_malloc(sizeof(transf_match_t));
+ A->match_mode = AVPL_STRICT;
+ A->avpl = new_avpl("");
+
+}
+
+transform_action(A) ::= . {
+ A = g_malloc(sizeof(transf_action_t));
+ A->replace_mode = AVPL_INSERT;
+ A->avpl = new_avpl("");
+}
+transform_action(A) ::= action_mode(Mode) avpl(Avpl). {
+ A = g_malloc(sizeof(transf_action_t));
+ A->replace_mode = Mode;
+ A->avpl = Avpl;
+}
+
+match_mode(A) ::= . { A = AVPL_STRICT; }
+match_mode(A) ::= STRICT_KW. { A = AVPL_STRICT; }
+match_mode(A) ::= EVERY_KW. { A = AVPL_EVERY; }
+match_mode(A) ::= LOOSE_KW. { A = AVPL_LOOSE; }
+
+action_mode(A) ::= REPLACE_KW. { A = AVPL_REPLACE; }
+action_mode(A) ::= INSERT_KW. { A = AVPL_INSERT; }
+action_mode(A) ::= . { A = AVPL_INSERT; }
+
+/******************************************* PDU
+*/
+
+pdu_decl ::=
+ PDU_KW NAME(Name) PROTO_KW field(Field) TRANSPORT_KW proto_stack(Stack)
+ OPEN_BRACE
+ payload_statement(Payload)
+ extraction_statements(Extraction)
+ transform_list_statement(Transform)
+ criteria_statement(Criteria)
+ pdu_drop_unassigned_statement(DropUnassigned)
+ discard_pdu_data_statement(DistcardPduData)
+ last_extracted_statement(LastExtracted)
+ CLOSE_BRACE SEMICOLON.
+{
+
+ mate_cfg_pdu* cfg = new_pducfg(Name);
+ extraction_t *extraction, *next_extraction;
+ GPtrArray* transport_stack = g_ptr_array_new();
+ int i;
+
+ if (! cfg ) configuration_error(mc,"could not create Pdu %s.",Name);
+
+ cfg->hfid_proto = Field->id;
+
+ cfg->last_extracted = LastExtracted;
+ cfg->discard = DistcardPduData;
+ cfg->drop_unassigned = DropUnassigned;
+
+ g_string_sprintfa(mc->protos_filter,"||%s",Field->abbrev);
+
+ /* flip the transport_stack */
+ for (i = Stack->len - 1; Stack->len; i--) {
+ g_ptr_array_add(transport_stack,g_ptr_array_remove_index(Stack,i));
+ }
+
+ g_ptr_array_free(Stack,FALSE);
+
+ cfg->transport_ranges = transport_stack;
+ cfg->payload_ranges = Payload;
+
+ if (Criteria) {
+ cfg->criterium = Criteria->criterium_avpl;
+ cfg->criterium_match_mode = Criteria->criterium_match_mode;
+ cfg->criterium_accept_mode = Criteria->criterium_accept_mode;
+ }
+
+ cfg->transforms = Transform;
+
+ for (extraction = Extraction; extraction; extraction = next_extraction) {
+ next_extraction = extraction->next;
+
+ if ( ! add_hfid(extraction->hfi, extraction->as, cfg->hfids_attr) ) {
+ configuration_error(mc,"MATE: failed to create extraction rule '%s'",extraction->as);
+ }
+
+ g_free(extraction);
+ }
+}
+
+payload_statement(A) ::= . { A = NULL; }
+payload_statement(A) ::= PAYLOAD_KW proto_stack(B) SEMICOLON. { A = B; }
+
+criteria_statement(A) ::= . { A = NULL; }
+criteria_statement(A) ::= CRITERIA_KW accept_mode(B) match_mode(C) avpl(D) SEMICOLON. {
+ A = g_malloc(sizeof(pdu_criteria_t));
+ A->criterium_avpl = D;
+ A->criterium_match_mode = C;
+ A->criterium_accept_mode = B;
+}
+
+accept_mode(A) ::= . { A = ACCEPT_MODE; }
+accept_mode(A) ::= ACCEPT_KW. { A = ACCEPT_MODE; }
+accept_mode(A) ::= REJECT_KW. { A = REJECT_MODE; }
+
+extraction_statements(A) ::= extraction_statements(B) extraction_statement(C). { A = B; A->last = A->last->next = C; }
+extraction_statements(A) ::= extraction_statement(B). { A = B; A->last = A; }
+
+extraction_statement(A) ::= EXTRACT_KW NAME(NAME) FROM_KW field(FIELD) SEMICOLON. {
+ A = g_malloc(sizeof(extraction_t));
+ A->as = NAME;
+ A->hfi = FIELD;
+ A->next = A->last = NULL;
+}
+
+
+pdu_drop_unassigned_statement(A) ::= DROP_UNASSIGNED_KW true_false(B) SEMICOLON. { A = B; }
+pdu_drop_unassigned_statement(A) ::= . { A = mc->defaults.pdu.drop_unassigned; }
+
+discard_pdu_data_statement(A) ::= DISCARD_PDU_DATA_KW true_false(B) SEMICOLON. { A = B; }
+discard_pdu_data_statement(A) ::= . { A = mc->defaults.pdu.discard; }
+
+last_extracted_statement(A) ::= LAST_PDU_KW true_false(B) SEMICOLON. { A = B; }
+last_extracted_statement(A) ::= . { A = mc->defaults.pdu.last_extracted; }
+
+proto_stack(A) ::= proto_stack(B) SLASH field(C). {
+ int* hfidp = g_malloc(sizeof(int));
+
+ g_string_sprintfa(mc->fields_filter,"||%s",C->abbrev);
+
+ *hfidp = C->id;
+ g_ptr_array_add(B,hfidp);
+ A = B;
+}
+
+proto_stack(A) ::= field(B). {
+ int* hfidp = g_malloc(sizeof(int));
+ *hfidp = B->id;
+
+ g_string_sprintfa(mc->fields_filter,"||%s",B->abbrev);
+
+ A = g_ptr_array_new();
+ g_ptr_array_add(A,hfidp);
+}
+
+field(A) ::= NAME(B). {
+ A = proto_registrar_get_byname(B);
+}
+
+/******************************************* GOP
+*/
+
+gop_decl(A) ::= GOP_KW NAME(Name) ON_KW pdu_name(PduName) MATCH_KW avpl(Key) OPEN_BRACE
+ gop_start_statement(Start)
+ gop_stop_statement(Stop)
+ extra_statement(Extra)
+ transform_list_statement(Transform)
+ gop_expiration_statement(Expiration)
+ idle_timeout_statement(IdleTimeout)
+ lifetime_statement(Lifetime)
+ gop_drop_unassigned_statement(DropUnassigned)
+ show_goptree_statement(TreeMode)
+ show_times_statement(ShowTimes)
+ CLOSE_BRACE SEMICOLON. {
+ mate_cfg_gop* cfg;
+
+ if (g_hash_table_lookup(mc->gopcfgs,Name)) configuration_error(mc,"A Gop Named '%s' exists already.",Name);
+ if (g_hash_table_lookup(mc->gops_by_pduname,PduName) ) configuration_error(mc,"Gop for Pdu '%s' exists already",PduName);
+
+ cfg = new_gopcfg(Name);
+ g_hash_table_insert(mc->gops_by_pduname,PduName,cfg);
+ g_hash_table_insert(mc->gopcfgs,cfg->name,cfg);
+
+ cfg->on_pdu = PduName;
+ cfg->key = Key;
+ cfg->drop_unassigned = DropUnassigned;
+ cfg->show_times = ShowTimes;
+ cfg->pdu_tree_mode = TreeMode;
+ cfg->expiration = Expiration;
+ cfg->idle_timeout = IdleTimeout;
+ cfg->lifetime = Lifetime;
+ cfg->start = Start;
+ cfg->stop = Stop;
+ cfg->transforms = Transform;
+
+ merge_avpl(cfg->extra,Extra,TRUE);
+ delete_avpl(Extra,TRUE);
+}
+
+gop_drop_unassigned_statement(A) ::= DROP_UNASSIGNED_KW true_false(B) SEMICOLON. { A = B; }
+gop_drop_unassigned_statement(A) ::= . { A = mc->defaults.gop.drop_unassigned; }
+
+gop_start_statement(A) ::= START_KW avpl(B) SEMICOLON. { A = B; }
+gop_start_statement(A) ::= . { A = NULL; }
+
+gop_stop_statement(A) ::= STOP_KW avpl(B) SEMICOLON. { A = B; }
+gop_stop_statement(A) ::= . { A = NULL; }
+
+show_goptree_statement(A) ::= SHOW_TREE_KW gop_tree_mode(B) SEMICOLON. { A = B; }
+show_goptree_statement(A) ::= . { A = mc->defaults.gop.pdu_tree_mode; }
+
+show_times_statement(A) ::= SHOW_TIMES_KW true_false(B) SEMICOLON. { A = B; }
+show_times_statement(A) ::= . { A = mc->defaults.gop.show_times; }
+
+gop_expiration_statement(A) ::= EXPIRATION_KW time_value(B) SEMICOLON. { A = B; }
+gop_expiration_statement(A) ::= . { A = mc->defaults.gop.lifetime; }
+
+idle_timeout_statement(A) ::= IDLE_TIMEOUT_KW time_value(B) SEMICOLON. { A = B; }
+idle_timeout_statement(A) ::= . { A = mc->defaults.gop.lifetime; }
+
+lifetime_statement(A) ::= LIFETIME_KW time_value(B) SEMICOLON. { A = B; }
+lifetime_statement(A) ::= . { A = mc->defaults.gop.lifetime; }
+
+gop_tree_mode(A) ::= NO_TREE_KW. { A = GOP_NO_TREE; }
+gop_tree_mode(A) ::= PDU_TREE_KW. { A = GOP_PDU_TREE; }
+gop_tree_mode(A) ::= FRAME_TREE_KW. { A = GOP_FRAME_TREE; }
+gop_tree_mode(A) ::= BASIC_TREE_KW. { A = GOP_BASIC_PDU_TREE; }
+
+true_false(A) ::= TRUE_KW. { A = TRUE; }
+true_false(A) ::= FALSE_KW. { A = FALSE; }
+
+pdu_name(A) ::= NAME(B). {
+ mate_cfg_pdu* c;
+ if (( c = g_hash_table_lookup(mc->pducfgs,B) )) {
+ A = c->name;
+ } else {
+ configuration_error(mc,"No such Pdu: '%s'",B);
+ }
+}
+
+
+time_value(A) ::= FLOATING(B). {
+ A = (float) strtod(B,NULL);
+}
+
+time_value(A) ::= INTEGER(B). {
+ A = (float) strtod(B,NULL);
+}
+
+/************* GOG
+*/
+
+gog_decl ::= GOG_KW NAME(Name) OPEN_BRACE
+ gog_key_statements(Keys)
+ extra_statement(Extra)
+ transform_list_statement(Transforms)
+ gog_expiration_statement(Expiration)
+ gog_goptree_statement(Tree)
+ CLOSE_BRACE SEMICOLON. {
+ mate_cfg_gog* cfg = NULL;
+
+ if ( g_hash_table_lookup(mc->gogcfgs,Name) ) {
+ configuration_error(mc,"Gog '%s' exists already ",Name);
+ }
+
+ cfg = new_gogcfg(Name);
+
+ cfg->expiration = Expiration;
+ cfg->gop_tree_mode = Tree;
+ cfg->transforms = Transforms;
+ cfg->keys = Keys;
+
+ merge_avpl(cfg->extra,Extra,TRUE);
+ delete_avpl(Extra,TRUE);
+}
+
+gog_goptree_statement(A) ::= GOP_TREE_KW gop_tree_type(B) SEMICOLON. { A = B; }
+gog_goptree_statement(A) ::= . { A = mc->defaults.gog.gop_tree_mode; }
+
+gog_expiration_statement(A) ::= EXPIRATION_KW time_value(B) SEMICOLON. { A = B; }
+gog_expiration_statement(A) ::= . { A = mc->defaults.gog.expiration; }
+
+gop_tree_type(A) ::= NULL_TREE_KW. { A = GOP_NULL_TREE; }
+gop_tree_type(A) ::= FULL_TREE_KW. { A = GOP_FULL_TREE; }
+gop_tree_type(A) ::= BASIC_TREE_KW. { A = GOP_BASIC_TREE; }
+
+gog_key_statements(A) ::= gog_key_statements(B) gog_key_statement(C). {
+ loal_append(B,C);
+ A = B;
+}
+
+gog_key_statements(A) ::= gog_key_statement(B). {
+ A = new_loal("");
+ loal_append(A,B);
+}
+
+
+gog_key_statement(A) ::= MEMBER_KW gop_name(B) avpl(C) SEMICOLON. {
+ rename_avpl(C,B);
+ A = C;
+}
+
+gop_name(A) ::= NAME(B). {
+ mate_cfg_gop* c;
+ if (( c = g_hash_table_lookup(mc->gopcfgs,B) )) {
+ A = c->name;
+ } else {
+ configuration_error(mc,"No Gop called '%s' has been already declared",B);
+ }
+}
+/******************************************** GENERAL
+*/
+
+
+extra_statement(A) ::= EXTRA_KW avpl(B) SEMICOLON. { A = B; }
+extra_statement(A) ::= . { A = new_avpl(""); }
+
+transform_list_statement(A) ::= TRANSFORM_KW transform_list(B) SEMICOLON. { A = B; }
+transform_list_statement(A) ::= . { A = g_ptr_array_new(); }
+
+transform_list(A) ::= transform_list(B) COMMA transform(C). {
+ A = B;
+ g_ptr_array_add(B,C);
+}
+
+transform_list(A) ::= transform(B). {
+ A = g_ptr_array_new();
+ g_ptr_array_add(A,B);
+}
+
+transform(A) ::= NAME(B). {
+ AVPL_Transf* t;
+
+ if (( t = g_hash_table_lookup(mc->transfs,B) )) {
+ A = t;
+ } else {
+ configuration_error(mc,"There's no such Transformation: %s",B);
+ }
+}
+
+avpl(A) ::= OPEN_PARENS avps(B) CLOSE_PARENS. { A = B; }
+avpl(A) ::= OPEN_PARENS CLOSE_PARENS. { A = new_avpl(""); }
+
+avps(A) ::= avps(B) COMMA avp(C). { A = B; if ( ! insert_avp(B,C) ) delete_avp(C); }
+avps(A) ::= avp(B). { A = new_avpl(""); if ( ! insert_avp(A,B) ) delete_avp(B); }
+
+avp(A) ::= NAME(B) AVP_OPERATOR(C) value(D). { A = new_avp(B,D,*C); }
+avp(A) ::= NAME(B). { A = new_avp(B,"",'?'); }
+avp(A) ::= NAME(B) OPEN_BRACE avp_oneoff(C) CLOSE_BRACE. { A = new_avp(B,C,'|'); }
+
+avp_oneoff(A) ::= avp_oneoff(B) PIPE value(C). { A = g_strdup_printf("%s|%s",B,C); }
+avp_oneoff(A) ::= value(B). { A = g_strdup(B); }
+
+value(A) ::= QUOTED(B). { A = g_strdup(B); }
+value(A) ::= NAME(B). { A = g_strdup(B); }
+value(A) ::= FLOATING(B). { A = g_strdup(B); }
+value(A) ::= INTEGER(B). { A = g_strdup(B); }
+value(A) ::= DOTED_IP(B). { A = g_strdup(B); }
+value(A) ::= COLONIZED(B). { A = recolonize(mc,B); }
+
Added: test-suite/trunk/MultiSource/Applications/lemon/xapian_COPYING
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/xapian_COPYING?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/xapian_COPYING (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/xapian_COPYING Wed Mar 26 12:03:57 2008
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Added: test-suite/trunk/MultiSource/Applications/lemon/xapian_queryparser.lemony
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/lemon/xapian_queryparser.lemony?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/lemon/xapian_queryparser.lemony (added)
+++ test-suite/trunk/MultiSource/Applications/lemon/xapian_queryparser.lemony Wed Mar 26 12:03:57 2008
@@ -0,0 +1,1763 @@
+%include {
+/* queryparser.lemony: build a Xapian::Query object from a user query string.
+ *
+ * Copyright (C) 2004,2005,2006,2007 Olly Betts
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#include <config.h>
+
+#include "omassert.h"
+#include "queryparser_internal.h"
+#include <xapian/error.h>
+#include <xapian/unicode.h>
+#include "stringutils.h"
+
+// Include the list of token values lemon generates.
+#include "queryparser_token.h"
+
+#include <algorithm>
+#include <list>
+#include <string>
+
+#include <string.h>
+
+using namespace std;
+
+using namespace Xapian;
+
+inline bool
+U_isupper(unsigned ch) {
+ return (ch < 128 && C_isupper((unsigned char)ch));
+}
+
+inline bool
+U_isdigit(unsigned ch) {
+ return (ch < 128 && C_isdigit((unsigned char)ch));
+}
+
+inline bool
+U_isalpha(unsigned ch) {
+ return (ch < 128 && C_isalpha((unsigned char)ch));
+}
+
+using Xapian::Unicode::is_whitespace;
+
+inline bool
+is_not_whitespace(unsigned ch) {
+ return !is_whitespace(ch);
+}
+
+using Xapian::Unicode::is_wordchar;
+
+inline bool
+is_not_wordchar(unsigned ch) {
+ return !is_wordchar(ch);
+}
+
+inline bool
+is_digit(unsigned ch) {
+ return (Unicode::get_category(ch) == Unicode::DECIMAL_DIGIT_NUMBER);
+}
+
+// FIXME: we used to keep trailing "-" (e.g. Cl-) but it's of dubious utility
+// and there's the risk of hyphens getting stuck onto the end of terms...
+inline bool
+is_suffix(unsigned ch) {
+ return ch == '+' || ch == '#';
+}
+
+inline bool
+prefix_needs_colon(const string & prefix, unsigned ch)
+{
+ if (!U_isupper(ch)) return false;
+ string::size_type len = prefix.length();
+ return (len > 1 && prefix[len - 1] != ':');
+}
+
+using Unicode::is_currency;
+
+/// A structure identifying a group of filter terms.
+struct filter_group_id {
+ /** The prefix of the filter terms.
+ * This is used for boolean filter terms.
+ */
+ list<string> prefixes;
+
+ /** The value number of the filter terms.
+ * This is used for value range terms.
+ */
+ Xapian::valueno valno;
+
+ /// Make a new filter_group_id for boolean filter terms.
+ explicit filter_group_id(const list<string> & prefixes_)
+ : prefixes(prefixes_), valno(Xapian::BAD_VALUENO) {}
+
+ /// Make a new filter_group_id for value range terms.
+ explicit filter_group_id(Xapian::valueno valno_)
+ : prefixes(), valno(valno_) {}
+
+ /// Compare to another filter_group_id.
+ bool operator<(const filter_group_id & other) const {
+ if (prefixes != other.prefixes) {
+ return prefixes < other.prefixes;
+ }
+ return valno < other.valno;
+ }
+};
+
+/** Class used to pass information about a token from lexer to parser.
+ *
+ * Generally an instance of this class carries term information, but it can be
+ * used for the start or end of a value range, with some operators (e.g. the
+ * distance in NEAR/3 or ADJ/3, etc).
+ */
+class Term {
+ State * state;
+
+ public:
+ string name;
+ list<string> prefixes;
+ string unstemmed;
+ QueryParser::stem_strategy stem;
+ termpos pos;
+
+ Term(const string &name_, termpos pos_) : name(name_), stem(QueryParser::STEM_NONE), pos(pos_) { }
+ Term(const string &name_) : name(name_), stem(QueryParser::STEM_NONE), pos(0) { }
+ Term(const string &name_, const list<string> &prefixes_)
+ : name(name_), prefixes(prefixes_), stem(QueryParser::STEM_NONE), pos(0) { }
+ Term(termpos pos_) : stem(QueryParser::STEM_NONE), pos(pos_) { }
+ Term(State * state_, const string &name_, const list<string> &prefixes_,
+ const string &unstemmed_,
+ QueryParser::stem_strategy stem_ = QueryParser::STEM_NONE,
+ termpos pos_ = 0)
+ : state(state_), name(name_), prefixes(prefixes_), unstemmed(unstemmed_),
+ stem(stem_), pos(pos_) { }
+
+ std::string make_term(const string & prefix) const;
+
+ void need_positions() {
+ if (stem == QueryParser::STEM_SOME) stem = QueryParser::STEM_NONE;
+ }
+
+ termpos get_termpos() const { return pos; }
+
+ filter_group_id get_filter_group_id() const { return filter_group_id(prefixes); }
+
+ Query * as_wildcarded_query(State * state) const;
+
+ Query * as_partial_query(State * state_) const;
+
+ Query get_query() const;
+
+ Query get_query_with_synonyms() const;
+
+ Query get_query_with_auto_synonyms() const;
+};
+
+/// Parser State shared between the lexer and the parser.
+class State {
+ QueryParser::Internal * qpi;
+
+ public:
+ Query query;
+ const char * error;
+ unsigned flags;
+
+ State(QueryParser::Internal * qpi_, unsigned flags_)
+ : qpi(qpi_), error(NULL), flags(flags_) { }
+
+ string stem_term(const string &term) {
+ return qpi->stemmer(term);
+ }
+
+ void add_to_stoplist(const Term * term) {
+ qpi->stoplist.push_back(term->name);
+ }
+
+ void add_to_unstem(const string & term, const string & unstemmed) {
+ qpi->unstem.insert(make_pair(term, unstemmed));
+ }
+
+ valueno value_range(Query & q, Term *a, Term *b) {
+ string start = a->name;
+ string end = b->name;
+ Xapian::valueno valno = Xapian::BAD_VALUENO;
+ list<ValueRangeProcessor *>::const_iterator i;
+ for (i = qpi->valrangeprocs.begin(); i != qpi->valrangeprocs.end(); ++i) {
+ valno = (**i)(start, end);
+ if (valno != Xapian::BAD_VALUENO) {
+ delete a;
+ delete b;
+ q = Query(Query::OP_VALUE_RANGE, valno, start, end);
+ return valno;
+ }
+ }
+ // FIXME: Do we want to report an error for this? If not we need
+ // to perform the above check in the tokeniser and if none of the
+ // ValueRangeProcessor classes like the range, we rollback to
+ // parsing the query without treating this as a range. Needs
+ // more thought and probably a look at queries users actually
+ // enter.
+ error = "Unknown range operation";
+ return valno;
+ }
+
+ Query::op default_op() const { return qpi->default_op; }
+
+ bool is_stopword(const Term *term) const {
+ return qpi->stopper && (*qpi->stopper)(term->name);
+ }
+
+ Database get_database() const {
+ return qpi->db;
+ }
+};
+
+string
+Term::make_term(const string & prefix) const
+{
+ string term;
+ if (stem == QueryParser::STEM_SOME) term += 'Z';
+ if (!prefix.empty()) {
+ term += prefix;
+ if (prefix_needs_colon(prefix, name[0])) term += ':';
+ }
+ if (stem != QueryParser::STEM_NONE) {
+ term += state->stem_term(name);
+ } else {
+ term += name;
+ }
+
+ if (!unstemmed.empty())
+ state->add_to_unstem(term, unstemmed);
+ return term;
+}
+
+Query
+Term::get_query_with_synonyms() const
+{
+ Query q = get_query();
+
+ // Handle single-word synonyms with each prefix.
+ list<string>::const_iterator piter;
+ for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
+ // First try the unstemmed term:
+ string term;
+ if (!piter->empty()) {
+ term += *piter;
+ if (prefix_needs_colon(*piter, name[0])) term += ':';
+ }
+ term += name;
+
+ Xapian::Database db = state->get_database();
+ Xapian::TermIterator syn = db.synonyms_begin(term);
+ Xapian::TermIterator end = db.synonyms_end(term);
+ if (syn == end && stem != QueryParser::STEM_NONE) {
+ // If that has no synonyms, try the stemmed form:
+ term = 'Z';
+ if (!piter->empty()) {
+ term += *piter;
+ if (prefix_needs_colon(*piter, name[0])) term += ':';
+ }
+ term += state->stem_term(name);
+ syn = db.synonyms_begin(term);
+ end = db.synonyms_end(term);
+ }
+ while (syn != end) {
+ q = Query(Query::OP_OR, q, Query(*syn, 1, pos));
+ ++syn;
+ }
+ }
+ return q;
+}
+
+Query
+Term::get_query_with_auto_synonyms() const
+{
+ if (state->flags & QueryParser::FLAG_AUTO_SYNONYMS)
+ return get_query_with_synonyms();
+
+ return get_query();
+}
+
+static void
+add_to_query(Query *& q, Query::op op, Query * term)
+{
+ Assert(term);
+ if (q) {
+ *q = Query(op, *q, *term);
+ delete term;
+ } else {
+ q = term;
+ }
+}
+
+static void
+add_to_query(Query *& q, Query::op op, const Query & term)
+{
+ if (q) {
+ *q = Query(op, *q, term);
+ } else {
+ q = new Query(term);
+ }
+}
+
+Query
+Term::get_query() const
+{
+ Assert(prefixes.size() >= 1);
+ list<string>::const_iterator piter = prefixes.begin();
+ Query q(make_term(*piter), 1, pos);
+ while (++piter != prefixes.end()) {
+ q = Query(Query::OP_OR, q, Query(make_term(*piter), 1, pos));
+ }
+ return q;
+}
+
+Query *
+Term::as_wildcarded_query(State * state_) const
+{
+ Database db = state_->get_database();
+ Query * q = new Query;
+ list<string>::const_iterator piter;
+ for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
+ string root = *piter;
+ root += name;
+ TermIterator t = db.allterms_begin(root);
+ while (t != db.allterms_end(root)) {
+ add_to_query(q, Query::OP_OR, Query(*t, 1, pos));
+ ++t;
+ }
+ }
+ delete this;
+ return q;
+}
+
+Query *
+Term::as_partial_query(State * state_) const
+{
+ Database db = state_->get_database();
+ Query * q = new Query;
+ list<string>::const_iterator piter;
+ for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
+ string root = *piter;
+ root += name;
+ TermIterator t = db.allterms_begin(root);
+ while (t != db.allterms_end(root)) {
+ add_to_query(q, Query::OP_OR, Query(*t, 1, pos));
+ ++t;
+ }
+ // Add the term, as it would normally be handled, as an alternative.
+ add_to_query(q, Query::OP_OR, Query(make_term(*piter), 1, pos));
+ }
+ delete this;
+ return q;
+}
+
+inline bool
+is_phrase_generator(unsigned ch)
+{
+ // These characters generate a phrase search.
+ // Ordered mostly by frequency of calls to this function done when
+ // running queryparsertest.
+ return (ch && ch < 128 && strchr(".-/:\\@", ch) != NULL);
+}
+
+inline bool
+is_stem_preventer(unsigned ch)
+{
+ return (ch && ch < 128 && strchr("(/\\@<>=*[{\"", ch) != NULL);
+}
+
+inline bool
+should_stem(const std::string & term)
+{
+ const unsigned int SHOULD_STEM_MASK =
+ (1 << Unicode::LOWERCASE_LETTER) |
+ (1 << Unicode::TITLECASE_LETTER) |
+ (1 << Unicode::MODIFIER_LETTER) |
+ (1 << Unicode::OTHER_LETTER);
+ Utf8Iterator u(term);
+ return ((SHOULD_STEM_MASK >> Unicode::get_category(*u)) & 1);
+}
+
+inline unsigned check_infix(unsigned ch) {
+ if (ch == '\'' || ch == '&' || ch == 0xb7 || ch == 0x5f4 || ch == 0x2027) {
+ // Unicode includes all these except '&' in it's word boundary rules,
+ // as well as 0x2019 (which we handle below) and ':' (for Swedish
+ // apparently, but we ignore this for now as it's problematic in
+ // real world cases).
+ return ch;
+ }
+ // 0x2019 is Unicode apostrophe and single closing quote.
+ // 0x201b is Unicode single opening quote with the tail rising.
+ if (ch == 0x2019 || ch == 0x201b) return '\'';
+ return 0;
+}
+
+inline unsigned check_infix_digit(unsigned ch) {
+ // This list of characters comes from Unicode's word identifying algorithm.
+ switch (ch) {
+ case ',':
+ case '.':
+ case ';':
+ case 0x037e: // GREEK QUESTION MARK
+ case 0x0589: // ARMENIAN FULL STOP
+ case 0x060D: // ARABIC DATE SEPARATOR
+ case 0x07F8: // NKO COMMA
+ case 0x2044: // FRACTION SLASH
+ case 0xFE10: // PRESENTATION FORM FOR VERTICAL COMMA
+ case 0xFE13: // PRESENTATION FORM FOR VERTICAL COLON
+ case 0xFE14: // PRESENTATION FORM FOR VERTICAL SEMICOLON
+ return ch;
+ }
+ return 0;
+}
+
+struct yyParser;
+
+// Prototype the functions lemon generates.
+static yyParser *ParseAlloc();
+static void ParseFree(yyParser *);
+static void Parse(yyParser *, int, Term *, State *);
+
+void
+QueryParser::Internal::add_prefix(const string &field, const string &prefix,
+ bool filter)
+{
+ map<string, PrefixInfo>::iterator p = prefixmap.find(field);
+ if (p == prefixmap.end()) {
+ prefixmap.insert(make_pair(field, PrefixInfo(filter, prefix)));
+ } else {
+ // Check that this is the same type of filter as the existing one(s).
+ if (p->second.filter != filter) {
+ throw Xapian::InvalidOperationError("Can't use add_prefix() and add_bool_prefix() on the same field name");
+ }
+ p->second.prefixes.push_back(prefix);
+ }
+}
+
+string
+QueryParser::Internal::parse_term(Utf8Iterator &it, const Utf8Iterator &end,
+ bool &was_acronym)
+{
+ string term;
+ // Look for initials separated by '.' (e.g. P.T.O., U.N.C.L.E).
+ // Don't worry if there's a trailing '.' or not.
+ if (U_isupper(*it)) {
+ string t;
+ Utf8Iterator p = it;
+ do {
+ Unicode::append_utf8(t, *p++);
+ } while (p != end && *p == '.' && ++p != end && U_isupper(*p));
+ // One letter does not make an acronym! If we handled a single
+ // uppercase letter here, we wouldn't catch M&S below.
+ if (t.length() > 1) {
+ // Check there's not a (lower case) letter or digit
+ // immediately after it.
+ // FIXME: should I.B.M..P.T.O be a range search?
+ if (p == end || !is_wordchar(*p)) {
+ it = p;
+ swap(term, t);
+ }
+ }
+ }
+ was_acronym = !term.empty();
+
+ if (term.empty()) {
+ unsigned prevch = *it;
+ Unicode::append_utf8(term, prevch);
+ while (++it != end) {
+ unsigned ch = *it;
+ if (!is_wordchar(ch)) {
+ // Treat a single embedded '&' or "'" or similar as a word
+ // character (e.g. AT&T, Fred's). Also, normalise
+ // apostrophes to ASCII apostrophe.
+ Utf8Iterator p = it;
+ ++p;
+ if (p == end || !is_wordchar(*p)) break;
+ unsigned nextch = *p;
+ if (is_digit(prevch) &&
+ is_digit(nextch)) {
+ ch = check_infix_digit(ch);
+ } else {
+ ch = check_infix(ch);
+ }
+ if (!ch) break;
+ }
+ Unicode::append_utf8(term, ch);
+ prevch = ch;
+ }
+ if (it != end && is_suffix(*it)) {
+ string suff_term = term;
+ Utf8Iterator p = it;
+ // Keep trailing + (e.g. C++, Na+) or # (e.g. C#).
+ do {
+ if (suff_term.size() - term.size() == 3) {
+ suff_term.resize(0);
+ break;
+ }
+ suff_term += *p;
+ } while (is_suffix(*++p));
+ if (!suff_term.empty() && (p == end || !is_wordchar(*p))) {
+ // If the suffixed term doesn't exist, check that the
+ // non-suffixed term does. This also takes care of
+ // the case when QueryParser::set_database() hasn't
+ // been called.
+ bool use_suff_term = false;
+ string lc = Unicode::tolower(suff_term);
+ if (db.term_exists(lc)) {
+ use_suff_term = true;
+ } else {
+ lc = Unicode::tolower(term);
+ if (!db.term_exists(lc)) use_suff_term = true;
+ }
+ if (use_suff_term) {
+ term = suff_term;
+ it = p;
+ }
+ }
+ }
+ }
+ return term;
+}
+
+Query
+QueryParser::Internal::parse_query(const string &qs, unsigned flags,
+ const string &default_prefix)
+{
+ yyParser * pParser = ParseAlloc();
+
+ // Set value_ranges if we may have to handle value ranges in the query.
+ bool value_ranges;
+ value_ranges = !valrangeprocs.empty() && (qs.find("..") != string::npos);
+
+ termpos term_pos = 1;
+ Utf8Iterator it(qs), end;
+
+ State state(this, flags);
+
+ // To successfully apply more than one spelling correction to a query
+ // string, we must keep track of the offset due to previous corrections.
+ int correction_offset = 0;
+ corrected_query.resize(0);
+
+ // Stack of prefixes, used for phrases and subexpressions.
+ list<const PrefixInfo *> prefix_stack;
+
+ // If default_prefix is specified, use it. Otherwise, use any list
+ // that has been set for the empty prefix.
+ const PrefixInfo def_pfx(false, default_prefix);
+ {
+ const PrefixInfo * default_prefixinfo = &def_pfx;
+ if (default_prefix.empty()) {
+ map<string, PrefixInfo>::const_iterator f = prefixmap.find("");
+ if (f != prefixmap.end()) default_prefixinfo = &(f->second);
+ }
+
+ // We always have the current prefix on the top of the stack.
+ prefix_stack.push_back(default_prefixinfo);
+ }
+
+ unsigned newprev = ' ';
+main_lex_loop:
+ enum {
+ DEFAULT, IN_QUOTES, IN_PREFIXED_QUOTES, IN_PHRASED_TERM, IN_GROUP
+ } mode = DEFAULT;
+ while (it != end) {
+ bool last_was_operator = false;
+ if (false) {
+just_had_operator:
+ if (it == end) break;
+ last_was_operator = true;
+ mode = DEFAULT;
+ }
+ if (mode == IN_PHRASED_TERM) mode = DEFAULT;
+ if (is_whitespace(*it)) {
+ newprev = ' ';
+ ++it;
+ it = find_if(it, end, is_not_whitespace);
+ if (it == end) break;
+ }
+
+ if ((mode == DEFAULT || mode == IN_GROUP) && value_ranges) {
+ // Scan forward to see if this could be the "start of range"
+ // token. Sadly this has O(n^2) tendencies, though at least
+ // "n" is the number of words in a query which is likely to
+ // remain fairly small. FIXME: can we tokenise more elegantly?
+ Utf8Iterator p = it;
+ unsigned ch = 0;
+ while (p != end) {
+ if (ch == '.' && *p == '.') {
+ ++p;
+ if (p == end || *p <= ' ' || *p == ')') break;
+
+ string r;
+ do {
+ Unicode::append_utf8(r, *it++);
+ } while (it != p);
+ // Trim off the trailing "..".
+ r.resize(r.size() - 2);
+ Parse(pParser, RANGE_START, new Term(r), &state);
+ r.resize(0);
+ // Allow any character except whitespace and ')' in a
+ // RANGE_END. Or should we be consistent with RANGE_START?
+ do {
+ Unicode::append_utf8(r, *p++);
+ } while (p != end && *p > ' ' && *p != ')');
+ Parse(pParser, RANGE_END, new Term(r), &state);
+ it = p;
+ goto main_lex_loop;
+ }
+ ch = *p;
+ if (!(is_wordchar(ch) || is_currency(ch) ||
+ (ch < 128 && strchr("%,-./:@", ch)))) break;
+ ++p;
+ }
+ }
+
+ if (!is_wordchar(*it)) {
+ unsigned prev = newprev;
+ unsigned ch = *it++;
+ newprev = ch;
+ // Drop out of IN_GROUP mode.
+ if (mode == IN_GROUP) mode = DEFAULT;
+ switch (ch) {
+ case '"': // Quoted phrase.
+ if (mode == DEFAULT) {
+ // Skip whitespace.
+ it = find_if(it, end, is_not_whitespace);
+ if (it == end) {
+ // Ignore an unmatched " at the end of the query to
+ // avoid generating an empty pair of QUOTEs which will
+ // cause a parse error.
+ goto done;
+ }
+ if (*it == '"') {
+ // Ignore empty "" (but only if we're not already
+ // IN_QUOTES as we don't merge two adjacent quoted
+ // phrases!)
+ newprev = *it++;
+ break;
+ }
+ }
+ if (flags & QueryParser::FLAG_PHRASE) {
+ Parse(pParser, QUOTE, NULL, &state);
+ if (mode == DEFAULT) {
+ mode = IN_QUOTES;
+ } else {
+ // Remove the prefix we pushed for this phrase.
+ if (mode == IN_PREFIXED_QUOTES)
+ prefix_stack.pop_back();
+ mode = DEFAULT;
+ }
+ }
+ break;
+
+ case '+': case '-': // Loved or hated term/phrase/subexpression.
+ // Ignore + or - at the end of the query string.
+ if (it == end) goto done;
+ if (prev > ' ' && prev != '(') {
+ // Or if not after whitespace or an open bracket.
+ break;
+ }
+ if (is_whitespace(*it) || *it == '+' || *it == '-') {
+ // Ignore + or - followed by a space, or further + or -.
+ // Postfix + (such as in C++ and H+) is handled as part of
+ // the term lexing code in parse_term().
+ newprev = *it++;
+ break;
+ }
+ if (mode == DEFAULT && (flags & FLAG_LOVEHATE)) {
+ Parse(pParser, (ch == '+' ? LOVE : HATE), NULL, &state);
+ goto just_had_operator;
+ }
+ // Need to prevent the term after a LOVE or HATE starting a
+ // term group...
+ break;
+
+ case '(': // Bracketed subexpression.
+ // Skip whitespace.
+ it = find_if(it, end, is_not_whitespace);
+ // Ignore ( at the end of the query string.
+ if (it == end) goto done;
+ if (prev > ' ' && strchr("()+-", prev) == NULL) {
+ // Or if not after whitespace or a bracket or '+' or '-'.
+ break;
+ }
+ if (*it == ')') {
+ // Ignore empty ().
+ newprev = *it++;
+ break;
+ }
+ if (mode == DEFAULT && (flags & FLAG_BOOLEAN)) {
+ prefix_stack.push_back(prefix_stack.back());
+ Parse(pParser, BRA, NULL, &state);
+ }
+ break;
+
+ case ')': // End of bracketed subexpression.
+ if (mode == DEFAULT && (flags & FLAG_BOOLEAN)) {
+ // Remove the prefix we pushed for the corresponding BRA.
+ // If brackets are unmatched, it's a syntax error, but
+ // that's no excuse to SEGV!
+ if (prefix_stack.size() > 1) prefix_stack.pop_back();
+ Parse(pParser, KET, NULL, &state);
+ }
+ break;
+
+ case '~': // Synonym expansion.
+ // Ignore at the end of the query string.
+ if (it == end) goto done;
+ if (prev > ' ' && prev != '+' && prev != '-' && prev != '(') {
+ // Or if not after whitespace, +, -, or an open bracket.
+ break;
+ }
+ if (!is_wordchar(*it)) {
+ // Ignore if not followed by a word character.
+ break;
+ }
+ if (mode == DEFAULT && (flags & FLAG_SYNONYM)) {
+ Parse(pParser, SYNONYM, NULL, &state);
+ goto just_had_operator;
+ }
+ break;
+ }
+ // Skip any other characters.
+ continue;
+ }
+
+ Assert(is_wordchar(*it));
+
+ size_t term_start_index = it.raw() - qs.data();
+
+ newprev = 'A'; // Any letter will do...
+
+ // A term, a prefix, or a boolean operator.
+ const PrefixInfo * prefixinfo = NULL;
+ if ((mode == DEFAULT || mode == IN_GROUP) && !prefixmap.empty()) {
+ // Check for a fieldname prefix (e.g. title:historical).
+ Utf8Iterator p = find_if(it, end, is_not_wordchar);
+ if (p != end && *p == ':' && ++p != end && *p > ' ' && *p != ')') {
+ string field;
+ p = it;
+ while (*p != ':')
+ Unicode::append_utf8(field, *p++);
+ map<string, PrefixInfo>::const_iterator f;
+ f = prefixmap.find(field);
+ if (f != prefixmap.end()) {
+ // Special handling for prefixed fields, depending on the
+ // type of the prefix.
+ unsigned ch = *++p;
+ prefixinfo = &(f->second);
+
+ if (prefixinfo->filter) {
+ // Drop out of IN_GROUP if we're in it.
+ mode = DEFAULT;
+ // Can't boolean filter prefix a subexpression or
+ // phrase; just use anything following the prefix
+ // until the next space or ')' as part of the boolean
+ // filter term.
+ it = p;
+ string name;
+ while (it != end && *it > ' ' && *it != ')')
+ Unicode::append_utf8(name, *it++);
+ // Build the unstemmed form in field.
+ field += ':';
+ field += name;
+ const list<string> & prefixes = prefixinfo->prefixes;
+ Term * token = new Term(&state, name, prefixes, field);
+ Parse(pParser, BOOLEAN_FILTER, token, &state);
+ continue;
+ }
+
+ if (ch == '"' && (flags & FLAG_PHRASE)) {
+ // Prefixed phrase, e.g.: subject:"space flight"
+ mode = IN_PREFIXED_QUOTES;
+ Parse(pParser, QUOTE, NULL, &state);
+ it = p;
+ newprev = ch;
+ ++it;
+ prefix_stack.push_back(prefixinfo);
+ continue;
+ }
+
+ if (ch == '(' && (flags & FLAG_BOOLEAN)) {
+ // Prefixed subexpression, e.g.: title:(fast NEAR food)
+ mode = DEFAULT;
+ Parse(pParser, BRA, NULL, &state);
+ it = p;
+ newprev = ch;
+ ++it;
+ prefix_stack.push_back(prefixinfo);
+ continue;
+ }
+
+ if (is_wordchar(ch)) {
+ // Prefixed term.
+ it = p;
+ } else {
+ // It looks like a prefix but isn't, so parse it as
+ // text instead.
+ prefixinfo = NULL;
+ }
+ }
+ }
+ }
+
+phrased_term:
+ bool was_acronym;
+ string term = parse_term(it, end, was_acronym);
+
+ // Boolean operators.
+ if ((mode == DEFAULT || mode == IN_GROUP) &&
+ (flags & FLAG_BOOLEAN) &&
+ // Don't want to interpret A.N.D. as an AND operator.
+ !was_acronym &&
+ !prefixinfo &&
+ term.size() >= 2 && term.size() <= 4 && U_isalpha(term[0])) {
+
+ string op = term;
+ if (flags & FLAG_BOOLEAN_ANY_CASE) {
+ for (string::iterator i = op.begin(); i != op.end(); ++i) {
+ *i = C_toupper(*i);
+ }
+ }
+ if (op.size() == 3) {
+ if (op == "AND") {
+ Parse(pParser, AND, NULL, &state);
+ goto just_had_operator;
+ }
+ if (op == "NOT") {
+ Parse(pParser, NOT, NULL, &state);
+ goto just_had_operator;
+ }
+ if (op == "XOR") {
+ Parse(pParser, XOR, NULL, &state);
+ goto just_had_operator;
+ }
+ if (op == "ADJ") {
+ if (it != end && *it == '/') {
+ size_t width = 0;
+ Utf8Iterator p = it;
+ while (++p != end && U_isdigit(*p)) {
+ width = (width * 10) + (*p - '0');
+ }
+ if (width && (p == end || is_whitespace(*p))) {
+ it = p;
+ Parse(pParser, ADJ, new Term(width), &state);
+ goto just_had_operator;
+ }
+ }
+
+ Parse(pParser, ADJ, NULL, &state);
+ goto just_had_operator;
+ }
+ } else if (op.size() == 2) {
+ if (op == "OR") {
+ Parse(pParser, OR, NULL, &state);
+ goto just_had_operator;
+ }
+ } else if (op.size() == 4) {
+ if (op == "NEAR") {
+ if (it != end && *it == '/') {
+ size_t width = 0;
+ Utf8Iterator p = it;
+ while (++p != end && U_isdigit(*p)) {
+ width = (width * 10) + (*p - '0');
+ }
+ if (width && (p == end || is_whitespace(*p))) {
+ it = p;
+ Parse(pParser, NEAR, new Term(width), &state);
+ goto just_had_operator;
+ }
+ }
+
+ Parse(pParser, NEAR, NULL, &state);
+ goto just_had_operator;
+ }
+ }
+ }
+
+ // If no prefix is set, use the default one.
+ if (!prefixinfo) prefixinfo = prefix_stack.back();
+
+ Assert(!prefixinfo->filter);
+
+ {
+ string unstemmed_term(term);
+ term = Unicode::tolower(term);
+
+ // Reuse stem_strategy - STEM_SOME here means "stem terms except
+ // when used with positional operators".
+ stem_strategy stem_term = stem_action;
+ if (stem_term != STEM_NONE) {
+ if (!stemmer.internal.get()) {
+ // No stemmer is set.
+ stem_term = STEM_NONE;
+ } else if (stem_term == STEM_SOME) {
+ if (!should_stem(unstemmed_term) ||
+ (it != end && is_stem_preventer(*it))) {
+ // Don't stem this particular term.
+ stem_term = STEM_NONE;
+ }
+ }
+ }
+
+ Term * term_obj = new Term(&state, term, prefixinfo->prefixes,
+ unstemmed_term, stem_term, term_pos++);
+
+ // Check spelling, if we're a normal term, and any of the prefixes
+ // are empty.
+ if ((flags & FLAG_SPELLING_CORRECTION) && !was_acronym) {
+ list<string>::const_iterator prefixiter;
+ for (prefixiter = prefixinfo->prefixes.begin();
+ prefixiter != prefixinfo->prefixes.end();
+ ++prefixiter) {
+ if (!prefixiter->empty())
+ continue;
+ if (!db.term_exists(term)) {
+ string suggestion = db.get_spelling_suggestion(term);
+ if (!suggestion.empty()) {
+ if (corrected_query.empty()) corrected_query = qs;
+ size_t term_end_index = it.raw() - qs.data();
+ size_t n = term_end_index - term_start_index;
+ size_t pos = term_start_index + correction_offset;
+ corrected_query.replace(pos, n, suggestion);
+ correction_offset += suggestion.size();
+ correction_offset -= n;
+ }
+ }
+ break;
+ }
+ }
+
+ if (mode == IN_PHRASED_TERM) {
+ Parse(pParser, PHR_TERM, term_obj, &state);
+ } else {
+ if (mode == DEFAULT || mode == IN_GROUP) {
+ if (it != end) {
+ if ((flags & FLAG_WILDCARD) && *it == '*') {
+ Utf8Iterator p(it);
+ ++p;
+ if (p == end || !is_wordchar(*p)) {
+ it = p;
+ // Wildcard at end of term (also known as
+ // "right truncation").
+ Parse(pParser, WILD_TERM, term_obj, &state);
+ continue;
+ }
+ }
+ } else {
+ if (flags & FLAG_PARTIAL) {
+ // Final term of a partial match query, with no
+ // following characters - treat as a wildcard.
+ Parse(pParser, PARTIAL_TERM, term_obj, &state);
+ continue;
+ }
+ }
+ }
+
+ // See if the next token will be PHR_TERM - if so, this one
+ // needs to be TERM not GROUP_TERM.
+ if (mode == IN_GROUP && is_phrase_generator(*it)) {
+ // FIXME: can we clean this up?
+ Utf8Iterator p = it;
+ do {
+ ++p;
+ } while (p != end && is_phrase_generator(*p));
+ // Don't generate a phrase unless the phrase generators are
+ // immediately followed by another term.
+ if (p != end && is_wordchar(*p)) {
+ mode = DEFAULT;
+ }
+ }
+
+ Parse(pParser, (mode == IN_GROUP ? GROUP_TERM : TERM),
+ term_obj, &state);
+ if (mode != DEFAULT && mode != IN_GROUP) continue;
+ }
+ }
+
+ if (it == end) break;
+
+ if (is_phrase_generator(*it)) {
+ // Skip multiple phrase generators.
+ do {
+ ++it;
+ } while (it != end && is_phrase_generator(*it));
+ // Don't generate a phrase unless the phrase generators are
+ // immediately followed by another term.
+ if (it != end && is_wordchar(*it)) {
+ mode = IN_PHRASED_TERM;
+ term_start_index = it.raw() - qs.data();
+ goto phrased_term;
+ }
+ } else if (mode == DEFAULT || mode == IN_GROUP) {
+ mode = DEFAULT;
+ if (!last_was_operator && is_whitespace(*it)) {
+ newprev = ' ';
+ // Skip multiple whitespace.
+ do {
+ ++it;
+ } while (it != end && is_whitespace(*it));
+ // Don't generate a group unless the terms are only separated
+ // by whitespace.
+ if (it != end && is_wordchar(*it)) {
+ mode = IN_GROUP;
+ }
+ }
+ }
+ }
+done:
+ // Implicitly close any unclosed quotes...
+ if (mode == IN_QUOTES || mode == IN_PREFIXED_QUOTES)
+ Parse(pParser, QUOTE, NULL, &state);
+ Parse(pParser, 0, NULL, &state);
+ ParseFree(pParser);
+
+ errmsg = state.error;
+ return state.query;
+}
+
+struct ProbQuery {
+ Query * query;
+ Query * love;
+ Query * hate;
+ // filter is a map from prefix to a query for that prefix. Queries with
+ // the same prefix are combined with OR, and the results of this are
+ // combined with AND to get the full filter.
+ map<filter_group_id, Query> filter;
+
+ ProbQuery() : query(0), love(0), hate(0) { }
+ ~ProbQuery() {
+ delete query;
+ delete love;
+ delete hate;
+ }
+
+ Query merge_filters() const {
+ map<filter_group_id, Query>::const_iterator i = filter.begin();
+ Assert(i != filter.end());
+ Query q = i->second;
+ while (++i != filter.end()) {
+ q = Query(Query::OP_AND, q, i->second);
+ }
+ return q;
+ }
+};
+
+class TermGroup {
+ list<Term *> terms;
+
+ public:
+ TermGroup() { }
+
+ /// Add a Term object to this TermGroup object.
+ void add_term(Term * term) {
+ terms.push_back(term);
+ }
+
+ /// Convert to a Xapian::Query * using default_op.
+ Query * as_group(State *state) const;
+
+ /** Provide a way to explicitly delete an object of this class. The
+ * destructor is protected to prevent auto-variables of this type.
+ */
+ void destroy() { delete this; }
+
+ protected:
+ /** Protected destructor, so an auto-variable of this type is a
+ * compile-time error - you must allocate this object with new.
+ */
+ ~TermGroup() {
+ list<Term*>::const_iterator i;
+ for (i = terms.begin(); i != terms.end(); ++i) {
+ delete *i;
+ }
+ }
+};
+
+Query *
+TermGroup::as_group(State *state) const
+{
+ Query * query = NULL;
+ Query::op default_op = state->default_op();
+ if (state->flags & QueryParser::FLAG_AUTO_MULTIWORD_SYNONYMS) {
+ // Check for multi-word synonyms.
+ Database db = state->get_database();
+
+ string key;
+ list<Term*>::const_iterator begin = terms.begin();
+ list<Term*>::const_iterator i = begin;
+ while (i != terms.end()) {
+ key.resize(0);
+ while (i != terms.end()) {
+ if (!key.empty()) key += ' ';
+ key += (*i)->name;
+ ++i;
+ }
+ // Greedily try to match as many consecutive words as possible.
+ TermIterator syn, end;
+ while (true) {
+ syn = db.synonyms_begin(key);
+ end = db.synonyms_end(key);
+ if (syn != end) break;
+ if (--i == begin) break;
+ key.resize(key.size() - (*i)->name.size() - 1);
+ }
+ if (i == begin) {
+ // No multi-synonym matches.
+ if (state->is_stopword(*i)) {
+ state->add_to_stoplist(*i);
+ } else {
+ add_to_query(query, default_op,
+ (*i)->get_query_with_auto_synonyms());
+ }
+ begin = ++i;
+ continue;
+ }
+
+ Query * q = NULL;
+ list<Term*>::const_iterator j;
+ for (j = begin; j != i; ++j) {
+ if (state->is_stopword(*j)) {
+ state->add_to_stoplist(*j);
+ } else {
+ add_to_query(q, default_op, (*j)->get_query());
+ }
+ }
+
+ // Use the position of the first term for the synonyms.
+ Xapian::termpos pos = (*begin)->pos;
+ begin = i;
+ while (syn != end) {
+ add_to_query(q, Query::OP_OR, Query(*syn, 1, pos));
+ ++syn;
+ }
+ add_to_query(query, default_op, q);
+ }
+ } else {
+ list<Term*>::const_iterator i;
+ for (i = terms.begin(); i != terms.end(); ++i) {
+ if (state->is_stopword(*i)) {
+ state->add_to_stoplist(*i);
+ } else {
+ add_to_query(query, default_op,
+ (*i)->get_query_with_auto_synonyms());
+ }
+ }
+ }
+ delete this;
+ return query;
+}
+
+class TermList {
+ list<Term *> terms;
+ size_t window;
+
+ /** Keep track of whether the terms added all have the same list of
+ * prefixes. If so, we'll build a set of phrases, one using each prefix.
+ * This works around the limitation that a phrase cannot have multiple
+ * components which are "OR" combinations of terms, but is also probably
+ * what users expect: ie, if a user specifies a phrase in a field, and that
+ * field maps to multiple prefixes, the user probably wants a phrase
+ * returned with all terms having one of those prefixes, rather than a
+ * phrase comprised of terms with differing prefixes.
+ */
+ bool uniform_prefixes;
+
+ /** The list of prefixes of the terms added.
+ * This will be empty if the terms have different prefixes.
+ */
+ list<string> prefixes;
+
+ public:
+ TermList() : window(0), uniform_prefixes(true) { }
+
+ /// Add an unstemmed Term object to this TermList object.
+ void add_positional_term(Term * term) {
+ if (terms.empty()) {
+ prefixes = term->prefixes;
+ } else if (uniform_prefixes && prefixes != term->prefixes) {
+ prefixes.clear();
+ uniform_prefixes = false;
+ }
+ term->need_positions();
+ terms.push_back(term);
+ }
+
+ void adjust_window(size_t alternative_window) {
+ if (alternative_window > window) window = alternative_window;
+ }
+
+ /// Convert to a query using the given operator and window size.
+ Query * as_opwindow_query(Query::op op, Xapian::termcount w_delta) const {
+ Query * q = NULL;
+ // Call terms.size() just once since std::list::size() may be O(n).
+ size_t n_terms = terms.size();
+ Xapian::termcount w = w_delta + terms.size();
+ if (uniform_prefixes) {
+ list<string>::const_iterator piter;
+ for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
+ vector<Query> subqs;
+ subqs.reserve(n_terms);
+ list<Term *>::const_iterator titer;
+ for (titer = terms.begin(); titer != terms.end(); ++titer) {
+ Term * t = *titer;
+ subqs.push_back(Query(t->make_term(*piter), 1, t->pos));
+ }
+ add_to_query(q, Query::OP_OR,
+ Query(op, subqs.begin(), subqs.end(), w));
+ }
+ } else {
+ vector<Query> subqs;
+ subqs.reserve(n_terms);
+ list<Term *>::const_iterator titer;
+ for (titer = terms.begin(); titer != terms.end(); ++titer) {
+ subqs.push_back((*titer)->get_query());
+ }
+ q = new Query(op, subqs.begin(), subqs.end(), w);
+ }
+
+ delete this;
+ return q;
+ }
+
+ /// Convert to a Xapian::Query * using adjacent OP_PHRASE.
+ Query * as_phrase_query() const {
+ return as_opwindow_query(Query::OP_PHRASE, 0);
+ }
+
+ /// Convert to a Xapian::Query * using OP_NEAR.
+ Query * as_near_query() const {
+ // The common meaning of 'a NEAR b' is "a within 10 terms of b", which
+ // means a window size of 11. For more than 2 terms, we just add one
+ // to the window size for each extra term.
+ size_t w = window;
+ if (w == 0) w = 10;
+ return as_opwindow_query(Query::OP_NEAR, w - 1);
+ }
+
+ /// Convert to a Xapian::Query * using OP_PHRASE to implement ADJ.
+ Query * as_adj_query() const {
+ // The common meaning of 'a ADJ b' is "a at most 10 terms before b",
+ // which means a window size of 11. For more than 2 terms, we just add
+ // one to the window size for each extra term.
+ size_t w = window;
+ if (w == 0) w = 10;
+ return as_opwindow_query(Query::OP_PHRASE, w - 1);
+ }
+
+ /** Provide a way to explicitly delete an object of this class. The
+ * destructor is protected to prevent auto-variables of this type.
+ */
+ void destroy() { delete this; }
+
+ protected:
+ /** Protected destructor, so an auto-variable of this type is a
+ * compile-time error - you must allocate this object with new.
+ */
+ ~TermList() {
+ list<Term *>::const_iterator t;
+ for (t = terms.begin(); t != terms.end(); ++t) {
+ delete *t;
+ }
+ }
+};
+
+// Helper macro for converting a boolean operation into a Xapian::Query.
+#define BOOL_OP_TO_QUERY(E, A, OP, B, OP_TXT) \
+ do {\
+ if (!A || !B) {\
+ state->error = "Syntax: <expression> "OP_TXT" <expression>";\
+ yy_parse_failed(yypParser);\
+ return;\
+ }\
+ E = new Query(OP, *A, *B);\
+ delete A;\
+ delete B;\
+ } while (0)
+
+}
+
+%token_type {Term *}
+%token_destructor {delete $$;}
+
+%extra_argument {State * state}
+
+%parse_failure {
+ // If we've not already set an error message, set a default one.
+ if (!state->error) state->error = "parse error";
+}
+
+// Operators, grouped in order of increasing precedence:
+%nonassoc ERROR.
+%left OR.
+%left XOR.
+%left AND NOT.
+%left NEAR ADJ.
+%left LOVE HATE SYNONYM.
+
+// Destructors for terminal symbols:
+
+// TERM is a query term, including prefix (if any).
+%destructor TERM {delete $$;}
+
+// GROUP_TERM is a query term which follows a TERM or another GROUP_TERM and
+// is only separated by whitespace characters.
+%destructor GROUP_TERM {delete $$;}
+
+// PHR_TERM is a query term which follows a TERM or another PHR_TERM and is
+// separated only by one or more phrase generator characters (hyphen and
+// apostrophe are common examples - see is_phrase_generator() for the list
+// of all punctuation which does this).
+%destructor PHR_TERM {delete $$;}
+
+// WILD_TERM is like a TERM, but has a trailing wildcard which needs to be
+// expanded.
+%destructor WILD_TERM {delete $$;}
+
+// PARTIAL_TERM is like a TERM, but it's at the end of the query string and
+// we're doing "search as you type". It expands to something like WILD_TERM
+// OR stemmed_form.
+%destructor PARTIAL_TERM {delete $$;}
+
+// BOOLEAN_FILTER is a query term with a prefix registered using
+// add_bool_prefix(). It's added to the query using an OP_FILTER operator,
+// (or OP_AND_NOT if it's negated) e.g. site:xapian.org or -site:xapian.org
+%destructor BOOLEAN_FILTER {delete $$;}
+
+// Grammar rules:
+
+// query - The whole query - just an expr or nothing.
+
+// query non-terminal doesn't need a type, so just give a dummy one.
+%type query {int}
+
+query ::= expr(E). {
+ // Save the parsed query in the State structure so we can return it.
+ if (E) {
+ state->query = *E;
+ delete E;
+ } else {
+ state->query = Query();
+ }
+}
+
+query ::= . {
+ // Handle a query string with no terms in.
+ state->query = Query();
+}
+
+// expr - A query expression.
+
+%type expr {Query *}
+%destructor expr {delete $$;}
+
+expr(E) ::= prob_expr(P).
+ { E = P; }
+
+expr(E) ::= bool_arg(A) AND bool_arg(B).
+ { BOOL_OP_TO_QUERY(E, A, Query::OP_AND, B, "AND"); }
+
+expr(E) ::= bool_arg(A) NOT bool_arg(B). {
+ // 'NOT foo' -> '<alldocuments> NOT foo'
+ if (!A && (state->flags & QueryParser::FLAG_PURE_NOT)) {
+ A = new Query("", 1, 0);
+ }
+ BOOL_OP_TO_QUERY(E, A, Query::OP_AND_NOT, B, "NOT");
+}
+
+expr(E) ::= bool_arg(A) AND NOT bool_arg(B). [NOT]
+ { BOOL_OP_TO_QUERY(E, A, Query::OP_AND_NOT, B, "AND NOT"); }
+
+expr(E) ::= bool_arg(A) OR bool_arg(B).
+ { BOOL_OP_TO_QUERY(E, A, Query::OP_OR, B, "OR"); }
+
+expr(E) ::= bool_arg(A) XOR bool_arg(B).
+ { BOOL_OP_TO_QUERY(E, A, Query::OP_XOR, B, "XOR"); }
+
+// bool_arg - an argument to a boolean operator such as AND or OR.
+
+%type bool_arg {Query *}
+%destructor bool_arg {delete $$;}
+
+bool_arg(A) ::= expr(E). { A = E; }
+
+bool_arg(A) ::= . [ERROR] {
+ // Set the argument to NULL, which enables the bool_arg-using rules in
+ // expr above to report uses of AND, OR, etc which don't have two
+ // arguments.
+ A = NULL;
+}
+
+// prob_expr - a single compound term, or a prob.
+
+%type prob_expr {Query *}
+%destructor prob_expr {delete $$;}
+
+prob_expr(E) ::= prob(P). {
+ E = P->query;
+ P->query = NULL;
+ // Handle any "+ terms".
+ if (P->love) {
+ if (P->love->empty()) {
+ // +<nothing>.
+ delete E;
+ E = P->love;
+ } else if (E) {
+ swap(E, P->love);
+ add_to_query(E, Query::OP_AND_MAYBE, P->love);
+ } else {
+ E = P->love;
+ }
+ P->love = NULL;
+ }
+ // Handle any boolean filters.
+ if (!P->filter.empty()) {
+ if (E) {
+ add_to_query(E, Query::OP_FILTER, P->merge_filters());
+ } else {
+ // Make the query a boolean one.
+ E = new Query(Query::OP_SCALE_WEIGHT, P->merge_filters(), 0.0);
+ }
+ }
+ // Handle any "- terms".
+ if (P->hate && !P->hate->empty()) {
+ if (!E) {
+ // Can't just hate!
+ yy_parse_failed(yypParser);
+ return;
+ }
+ *E = Query(Query::OP_AND_NOT, *E, *P->hate);
+ }
+ // FIXME what if E && E->empty() (all terms are stopwords)?
+ delete P;
+}
+
+prob_expr(E) ::= term(T). {
+ E = T;
+}
+
+// prob - a probabilistic sub-expression consisting of stop_terms, "+" terms,
+// "-" terms, boolean filters, and/or value ranges.
+//
+// Note: stop_term can also be several other things other than a simple term!
+
+%type prob {ProbQuery *}
+%destructor prob {delete $$;}
+
+prob(P) ::= RANGE_START(A) RANGE_END(B). {
+ Query range;
+ Xapian::valueno valno = state->value_range(range, A, B);
+ if (valno == BAD_VALUENO) {
+ yy_parse_failed(yypParser);
+ return;
+ }
+ P = new ProbQuery;
+ P->filter[filter_group_id(valno)] = range;
+}
+
+prob(P) ::= stop_prob(Q) RANGE_START(A) RANGE_END(B). {
+ Query range;
+ Xapian::valueno valno = state->value_range(range, A, B);
+ if (valno == BAD_VALUENO) {
+ yy_parse_failed(yypParser);
+ return;
+ }
+ P = Q;
+ Query & q = P->filter[filter_group_id(valno)];
+ q = Query(Query::OP_OR, q, range);
+}
+
+prob(P) ::= stop_term(T) stop_term(U). {
+ P = new ProbQuery;
+ P->query = T;
+ if (U) add_to_query(P->query, state->default_op(), U);
+}
+
+prob(P) ::= prob(Q) stop_term(T). {
+ P = Q;
+ // If T is a stopword, there's nothing to do here.
+ if (T) add_to_query(P->query, state->default_op(), T);
+}
+
+prob(P) ::= LOVE term(T). {
+ P = new ProbQuery;
+ if (state->default_op() == Query::OP_AND) {
+ P->query = T;
+ } else {
+ P->love = T;
+ }
+}
+
+prob(P) ::= stop_prob(Q) LOVE term(T). {
+ P = Q;
+ if (state->default_op() == Query::OP_AND) {
+ /* The default op is AND, so we just put loved terms into the query
+ * (in this case the only effect of love is to ignore the stopword
+ * list). */
+ add_to_query(P->query, Query::OP_AND, T);
+ } else {
+ add_to_query(P->love, Query::OP_AND, T);
+ }
+}
+
+prob(P) ::= HATE term(T). {
+ P = new ProbQuery;
+ P->hate = T;
+}
+
+prob(P) ::= stop_prob(Q) HATE term(T). {
+ P = Q;
+ add_to_query(P->hate, Query::OP_OR, T);
+}
+
+prob(P) ::= HATE BOOLEAN_FILTER(T). {
+ P = new ProbQuery;
+ P->hate = new Query(T->get_query());
+ delete T;
+}
+
+prob(P) ::= stop_prob(Q) HATE BOOLEAN_FILTER(T). {
+ P = Q;
+ add_to_query(P->hate, Query::OP_OR, T->get_query());
+ delete T;
+}
+
+prob(P) ::= BOOLEAN_FILTER(T). {
+ P = new ProbQuery;
+ P->filter[T->get_filter_group_id()] = T->get_query();
+ delete T;
+}
+
+prob(P) ::= stop_prob(Q) BOOLEAN_FILTER(T). {
+ P = Q;
+ // We OR filters with the same prefix...
+ Query & q = P->filter[T->get_filter_group_id()];
+ q = Query(Query::OP_OR, q, T->get_query());
+ delete T;
+}
+
+prob(P) ::= LOVE BOOLEAN_FILTER(T). {
+ // LOVE BOOLEAN_FILTER(T) is just the same as BOOLEAN_FILTER
+ P = new ProbQuery;
+ P->filter[T->get_filter_group_id()] = T->get_query();
+ delete T;
+}
+
+prob(P) ::= stop_prob(Q) LOVE BOOLEAN_FILTER(T). {
+ // LOVE BOOLEAN_FILTER(T) is just the same as BOOLEAN_FILTER
+ P = Q;
+ // We OR filters with the same prefix...
+ Query & q = P->filter[T->get_filter_group_id()];
+ q = Query(Query::OP_OR, q, T->get_query());
+ delete T;
+}
+
+// stop_prob - A prob or a stop_term.
+
+%type stop_prob {ProbQuery *}
+%destructor stop_prob {delete $$;}
+
+stop_prob(P) ::= prob(Q).
+ { P = Q; }
+
+stop_prob(P) ::= stop_term(T). {
+ P = new ProbQuery;
+ P->query = T;
+}
+
+// stop_term - A term which should be checked against the stopword list,
+// or a compound_term.
+//
+// If a term is loved, hated, or in a phrase, we don't want to consult the
+// stopword list, so stop_term isn't used there (instead term is).
+
+%type stop_term {Query *}
+%destructor stop_term {delete $$;}
+
+stop_term(T) ::= TERM(U). {
+ if (state->is_stopword(U)) {
+ T = NULL;
+ state->add_to_stoplist(U);
+ } else {
+ T = new Query(U->get_query_with_auto_synonyms());
+ }
+ delete U;
+}
+
+stop_term(T) ::= compound_term(U). {
+ T = U;
+}
+
+// term - A term or a compound_term.
+
+%type term {Query *}
+%destructor term {delete $$;}
+
+term(T) ::= TERM(U). {
+ T = new Query(U->get_query_with_auto_synonyms());
+ delete U;
+}
+
+term(T) ::= compound_term(U). {
+ T = U;
+}
+
+// compound_term - A WILD_TERM, a quoted phrase (with or without prefix), a
+// phrased_term, group, near_expr, adj_expr, or a bracketed subexpression (with
+// or without prefix).
+
+%type compound_term {Query *}
+%destructor compound_term {delete $$;}
+
+compound_term(T) ::= WILD_TERM(U).
+ { T = U->as_wildcarded_query(state); }
+
+compound_term(T) ::= PARTIAL_TERM(U).
+ { T = U->as_partial_query(state); }
+
+compound_term(T) ::= QUOTE phrase(P) QUOTE.
+ { T = P->as_phrase_query(); }
+
+compound_term(T) ::= phrased_term(P).
+ { T = P->as_phrase_query(); }
+
+compound_term(T) ::= group(P). {
+ T = P->as_group(state);
+}
+
+compound_term(T) ::= near_expr(P).
+ { T = P->as_near_query(); }
+
+compound_term(T) ::= adj_expr(P).
+ { T = P->as_adj_query(); }
+
+compound_term(T) ::= BRA expr(E) KET.
+ { T = E; }
+
+compound_term(T) ::= SYNONYM TERM(U). {
+ T = new Query(U->get_query_with_synonyms());
+ delete U;
+}
+
+// phrase - The "inside the quotes" part of a double-quoted phrase.
+
+%type phrase {TermList *}
+
+%destructor phrase {$$->destroy();}
+
+phrase(P) ::= TERM(T). {
+ P = new TermList;
+ P->add_positional_term(T);
+}
+
+phrase(P) ::= phrase(Q) TERM(T). {
+ P = Q;
+ P->add_positional_term(T);
+}
+
+// phrased_term - A phrased term works like a single term, but is actually
+// 2 or more terms linked together into a phrase by punctuation. There must be
+// at least 2 terms in order to be able to have punctuation between the terms!
+
+%type phrased_term {TermList *}
+%destructor phrased_term {$$->destroy();}
+
+phrased_term(P) ::= TERM(T) PHR_TERM(U). {
+ P = new TermList;
+ P->add_positional_term(T);
+ P->add_positional_term(U);
+}
+
+phrased_term(P) ::= phrased_term(Q) PHR_TERM(T). {
+ P = Q;
+ P->add_positional_term(T);
+}
+
+// group - A group of terms separated only by whitespace - candidates for
+// multi-term synonyms.
+
+%type group {TermGroup *}
+%destructor group {$$->destroy();}
+
+group(P) ::= TERM(T) GROUP_TERM(U). {
+ P = new TermGroup;
+ P->add_term(T);
+ P->add_term(U);
+}
+
+group(P) ::= group(Q) GROUP_TERM(T). {
+ P = Q;
+ P->add_term(T);
+}
+
+// near_expr - 2 or more terms with NEAR in between. There must be at least 2
+// terms in order for there to be any NEAR operators!
+
+%type near_expr {TermList *}
+%destructor near_expr {$$->destroy();}
+
+near_expr(P) ::= TERM(T) NEAR(N) TERM(U). {
+ P = new TermList;
+ P->add_positional_term(T);
+ P->add_positional_term(U);
+ if (N) {
+ P->adjust_window(N->get_termpos());
+ delete N;
+ }
+}
+
+near_expr(P) ::= near_expr(Q) NEAR(N) TERM(T). {
+ P = Q;
+ P->add_positional_term(T);
+ if (N) {
+ P->adjust_window(N->get_termpos());
+ delete N;
+ }
+}
+
+// adj_expr - 2 or more terms with ADJ in between. There must be at least 2
+// terms in order for there to be any ADJ operators!
+
+%type adj_expr {TermList *}
+%destructor adj_expr {$$->destroy();}
+
+adj_expr(P) ::= TERM(T) ADJ(N) TERM(U). {
+ P = new TermList;
+ P->add_positional_term(T);
+ P->add_positional_term(U);
+ if (N) {
+ P->adjust_window(N->get_termpos());
+ delete N;
+ }
+}
+
+adj_expr(P) ::= adj_expr(Q) ADJ(N) TERM(T). {
+ P = Q;
+ P->add_positional_term(T);
+ if (N) {
+ P->adjust_window(N->get_termpos());
+ delete N;
+ }
+}
+
+// Select yacc syntax highlighting in vim editor: vim: syntax=yacc
+// (lemon syntax colouring isn't supplied by default; yacc does an OK job).
Added: test-suite/trunk/MultiSource/Applications/sqlite3/Makefile
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/sqlite3/Makefile?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/sqlite3/Makefile (added)
+++ test-suite/trunk/MultiSource/Applications/sqlite3/Makefile Wed Mar 26 12:03:57 2008
@@ -0,0 +1,26 @@
+LEVEL = ../../../
+#RUN_OPTIONS =
+
+Source = sqlite3.c shell.c
+
+PROG = sqlite3
+CPPFLAGS += -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_DLFCN_H=0 -DHAVE_FDATASYNC=0 -DHAVE_USLEEP=0 -DHAVE_LOCALTIME_R=0 -DHAVE_GMTIME_R=0 -DHAVE_READLINE=0 -I. -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION=1
+LDFLAGS =
+STDIN_FILENAME = commands
+RUN_OPTIONS = :memory: >/dev/null
+
+include $(LEVEL)/Makefile.config
+
+include ../../Makefile.multisrc
+#compare debug output
+DIFFPROG := $(PROGDIR)/DiffOutput.sh "diff "
+
+# the input files are generated from a tcl script
+# it needs to be generated before sqlite is run
+sqlite3.c: test15.sql
+
+test15.sql: speedtest.tcl
+ $(TCLSH) speedtest.tcl
+
+clean::
+ rm -f test*.sql
Added: test-suite/trunk/MultiSource/Applications/sqlite3/README
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/sqlite3/README?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/sqlite3/README (added)
+++ test-suite/trunk/MultiSource/Applications/sqlite3/README Wed Mar 26 12:03:57 2008
@@ -0,0 +1,34 @@
+This directory contains source code to
+
+ SQLite: An Embeddable SQL Database Engine
+
+To compile the project, first create a directory in which to place
+the build products. It is recommended, but not required, that the
+build directory be separate from the source directory. Cd into the
+build directory and then from the build directory run the configure
+script found at the root of the source tree. Then run "make".
+
+For example:
+
+ tar xzf sqlite.tar.gz ;# Unpack the source tree into "sqlite"
+ mkdir bld ;# Build will occur in a sibling directory
+ cd bld ;# Change to the build directory
+ ../sqlite/configure ;# Run the configure script
+ make ;# Run the makefile.
+ make install ;# (Optional) Install the build products
+
+The configure script uses autoconf 2.61 and libtool. If the configure
+script does not work out for you, there is a generic makefile named
+"Makefile.linux-gcc" in the top directory of the source tree that you
+can copy and edit to suit your needs. Comments on the generic makefile
+show what changes are needed.
+
+The linux binaries on the website are created using the generic makefile,
+not the configure script.
+The windows binaries on the website are created using MinGW32 configured
+as a cross-compiler running under Linux. For details, see the ./publish.sh
+script at the top-level of the source tree.
+
+Contacts:
+
+ http://www.sqlite.org/
Added: test-suite/trunk/MultiSource/Applications/sqlite3/VERSION
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/sqlite3/VERSION?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/sqlite3/VERSION (added)
+++ test-suite/trunk/MultiSource/Applications/sqlite3/VERSION Wed Mar 26 12:03:57 2008
@@ -0,0 +1 @@
+3.5.7
Added: test-suite/trunk/MultiSource/Applications/sqlite3/commands
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/sqlite3/commands?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/sqlite3/commands (added)
+++ test-suite/trunk/MultiSource/Applications/sqlite3/commands Wed Mar 26 12:03:57 2008
@@ -0,0 +1,15 @@
+.read test1.sql
+.read test2.sql
+.read test3.sql
+.read test4.sql
+.read test5.sql
+.read test6.sql
+.read test7.sql
+.read test8.sql
+.read test9.sql
+.read test10.sql
+.read test11.sql
+.read test12.sql
+.read test13.sql
+.read test14.sql
+.read test15.sql
Added: test-suite/trunk/MultiSource/Applications/sqlite3/copyright-release.html
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/sqlite3/copyright-release.html?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/sqlite3/copyright-release.html (added)
+++ test-suite/trunk/MultiSource/Applications/sqlite3/copyright-release.html Wed Mar 26 12:03:57 2008
@@ -0,0 +1,109 @@
+<html>
+<body bgcolor="white">
+<h1 align="center">
+Copyright Release for<br>
+Contributions To SQLite
+</h1>
+
+<p>
+SQLite is software that implements an embeddable SQL database engine.
+SQLite is available for free download from http://www.sqlite.org/.
+The principal author and maintainer of SQLite has disclaimed all
+copyright interest in his contributions to SQLite
+and thus released his contributions into the public domain.
+In order to keep the SQLite software unencumbered by copyright
+claims, the principal author asks others who may from time to
+time contribute changes and enhancements to likewise disclaim
+their own individual copyright interest.
+</p>
+
+<p>
+Because the SQLite software found at http://www.sqlite.org/ is in the
+public domain, anyone is free to download the SQLite software
+from that website, make changes to the software, use, distribute,
+or sell the modified software, under either the original name or
+under some new name, without any need to obtain permission, pay
+royalties, acknowledge the original source of the software, or
+in any other way compensate, identify, or notify the original authors.
+Nobody is in any way compelled to contribute their SQLite changes and
+enhancements back to the SQLite website. This document concerns
+only changes and enhancements to SQLite that are intentionally and
+deliberately contributed back to the SQLite website.
+</p>
+
+<p>
+For the purposes of this document, "SQLite software" shall mean any
+computer source code, documentation, makefiles, test scripts, or
+other information that is published on the SQLite website,
+http://www.sqlite.org/. Precompiled binaries are excluded from
+the definition of "SQLite software" in this document because the
+process of compiling the software may introduce information from
+outside sources which is not properly a part of SQLite.
+</p>
+
+<p>
+The header comments on the SQLite source files exhort the reader to
+share freely and to never take more than one gives.
+In the spirit of that exhortation I make the following declarations:
+</p>
+
+<ol>
+<li><p>
+I dedicate to the public domain
+any and all copyright interest in the SQLite software that
+was publicly available on the SQLite website (http://www.sqlite.org/) prior
+to the date of the signature below and any changes or enhancements to
+the SQLite software
+that I may cause to be published on that website in the future.
+I make this dedication for the benefit of the public at large and
+to the detriment of my heirs and successors. I intend this
+dedication to be an overt act of relinquishment in perpetuity of
+all present and future rights to the SQLite software under copyright
+law.
+</p></li>
+
+<li><p>
+To the best of my knowledge and belief, the changes and enhancements that
+I have contributed to SQLite are either originally written by me
+or are derived from prior works which I have verified are also
+in the public domain and are not subject to claims of copyright
+by other parties.
+</p></li>
+
+<li><p>
+To the best of my knowledge and belief, no individual, business, organization,
+government, or other entity has any copyright interest
+in the SQLite software as it existed on the
+SQLite website as of the date on the signature line below.
+</p></li>
+
+<li><p>
+I agree never to publish any additional information
+to the SQLite website (by CVS, email, scp, FTP, or any other means) unless
+that information is an original work of authorship by me or is derived from
+prior published versions of SQLite.
+I agree never to copy and paste code into the SQLite code base from
+other sources.
+I agree never to publish on the SQLite website any information that
+would violate a law or breach a contract.
+</p></li>
+</ol>
+
+<p>
+<table width="100%" cellpadding="0" cellspacing="0">
+<tr>
+<td width="60%" valign="top">
+Signature:
+<p> </p>
+<p> </p>
+<p> </p>
+</td><td valign="top" align="left">
+Date:
+</td></tr>
+<td colspan=2>
+Name (printed):
+</td>
+</tr>
+</table>
+</body>
+</html>
Added: test-suite/trunk/MultiSource/Applications/sqlite3/shell.c
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/sqlite3/shell.c?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/sqlite3/shell.c (added)
+++ test-suite/trunk/MultiSource/Applications/sqlite3/shell.c Wed Mar 26 12:03:57 2008
@@ -0,0 +1,2087 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains code to implement the "sqlite" command line
+** utility for accessing SQLite databases.
+**
+** $Id: shell.c,v 1.174 2008/01/21 16:22:46 drh Exp $
+*/
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include "sqlite3.h"
+#include <ctype.h>
+#include <stdarg.h>
+
+#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__)
+# include <signal.h>
+# include <pwd.h>
+# include <unistd.h>
+# include <sys/types.h>
+#endif
+
+#ifdef __OS2__
+# include <unistd.h>
+#endif
+
+#if defined(HAVE_READLINE) && HAVE_READLINE==1
+# include <readline/readline.h>
+# include <readline/history.h>
+#else
+# define readline(p) local_getline(p,stdin)
+# define add_history(X)
+# define read_history(X)
+# define write_history(X)
+# define stifle_history(X)
+#endif
+
+#if defined(_WIN32) || defined(WIN32)
+# include <io.h>
+#else
+/* Make sure isatty() has a prototype.
+*/
+extern int isatty();
+#endif
+
+#if defined(_WIN32_WCE)
+/* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty()
+ * thus we always assume that we have a console. That can be
+ * overridden with the -batch command line option.
+ */
+#define isatty(x) 1
+#endif
+
+#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__)
+#include <sys/time.h>
+#include <sys/resource.h>
+
+/* Saved resource information for the beginning of an operation */
+static struct rusage sBegin;
+
+/* True if the timer is enabled */
+static int enableTimer = 0;
+
+/*
+** Begin timing an operation
+*/
+static void beginTimer(void){
+ if( enableTimer ){
+ getrusage(RUSAGE_SELF, &sBegin);
+ }
+}
+
+/* Return the difference of two time_structs in microseconds */
+static int timeDiff(struct timeval *pStart, struct timeval *pEnd){
+ return (pEnd->tv_usec - pStart->tv_usec) +
+ 1000000*(pEnd->tv_sec - pStart->tv_sec);
+}
+
+/*
+** Print the timing results.
+*/
+static void endTimer(void){
+ if( enableTimer ){
+ struct rusage sEnd;
+ getrusage(RUSAGE_SELF, &sEnd);
+ printf("CPU Time: user %f sys %f\n",
+ 0.000001*timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
+ 0.000001*timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
+ }
+}
+#define BEGIN_TIMER beginTimer()
+#define END_TIMER endTimer()
+#define HAS_TIMER 1
+#else
+#define BEGIN_TIMER
+#define END_TIMER
+#define HAS_TIMER 0
+#endif
+
+
+/*
+** If the following flag is set, then command execution stops
+** at an error if we are not interactive.
+*/
+static int bail_on_error = 0;
+
+/*
+** Threat stdin as an interactive input if the following variable
+** is true. Otherwise, assume stdin is connected to a file or pipe.
+*/
+static int stdin_is_interactive = 1;
+
+/*
+** The following is the open SQLite database. We make a pointer
+** to this database a static variable so that it can be accessed
+** by the SIGINT handler to interrupt database processing.
+*/
+static sqlite3 *db = 0;
+
+/*
+** True if an interrupt (Control-C) has been received.
+*/
+static volatile int seenInterrupt = 0;
+
+/*
+** This is the name of our program. It is set in main(), used
+** in a number of other places, mostly for error messages.
+*/
+static char *Argv0;
+
+/*
+** Prompt strings. Initialized in main. Settable with
+** .prompt main continue
+*/
+static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/
+static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */
+
+/*
+** Write I/O traces to the following stream.
+*/
+#ifdef SQLITE_ENABLE_IOTRACE
+static FILE *iotrace = 0;
+#endif
+
+/*
+** This routine works like printf in that its first argument is a
+** format string and subsequent arguments are values to be substituted
+** in place of % fields. The result of formatting this string
+** is written to iotrace.
+*/
+#ifdef SQLITE_ENABLE_IOTRACE
+static void iotracePrintf(const char *zFormat, ...){
+ va_list ap;
+ char *z;
+ if( iotrace==0 ) return;
+ va_start(ap, zFormat);
+ z = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ fprintf(iotrace, "%s", z);
+ sqlite3_free(z);
+}
+#endif
+
+
+/*
+** Determines if a string is a number of not.
+*/
+static int isNumber(const char *z, int *realnum){
+ if( *z=='-' || *z=='+' ) z++;
+ if( !isdigit(*z) ){
+ return 0;
+ }
+ z++;
+ if( realnum ) *realnum = 0;
+ while( isdigit(*z) ){ z++; }
+ if( *z=='.' ){
+ z++;
+ if( !isdigit(*z) ) return 0;
+ while( isdigit(*z) ){ z++; }
+ if( realnum ) *realnum = 1;
+ }
+ if( *z=='e' || *z=='E' ){
+ z++;
+ if( *z=='+' || *z=='-' ) z++;
+ if( !isdigit(*z) ) return 0;
+ while( isdigit(*z) ){ z++; }
+ if( realnum ) *realnum = 1;
+ }
+ return *z==0;
+}
+
+/*
+** A global char* and an SQL function to access its current value
+** from within an SQL statement. This program used to use the
+** sqlite_exec_printf() API to substitue a string into an SQL statement.
+** The correct way to do this with sqlite3 is to use the bind API, but
+** since the shell is built around the callback paradigm it would be a lot
+** of work. Instead just use this hack, which is quite harmless.
+*/
+static const char *zShellStatic = 0;
+static void shellstaticFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ assert( 0==argc );
+ assert( zShellStatic );
+ sqlite3_result_text(context, zShellStatic, -1, SQLITE_STATIC);
+}
+
+
+/*
+** This routine reads a line of text from FILE in, stores
+** the text in memory obtained from malloc() and returns a pointer
+** to the text. NULL is returned at end of file, or if malloc()
+** fails.
+**
+** The interface is like "readline" but no command-line editing
+** is done.
+*/
+static char *local_getline(char *zPrompt, FILE *in){
+ char *zLine;
+ int nLine;
+ int n;
+ int eol;
+
+ if( zPrompt && *zPrompt ){
+ printf("%s",zPrompt);
+ fflush(stdout);
+ }
+ nLine = 100;
+ zLine = malloc( nLine );
+ if( zLine==0 ) return 0;
+ n = 0;
+ eol = 0;
+ while( !eol ){
+ if( n+100>nLine ){
+ nLine = nLine*2 + 100;
+ zLine = realloc(zLine, nLine);
+ if( zLine==0 ) return 0;
+ }
+ if( fgets(&zLine[n], nLine - n, in)==0 ){
+ if( n==0 ){
+ free(zLine);
+ return 0;
+ }
+ zLine[n] = 0;
+ eol = 1;
+ break;
+ }
+ while( zLine[n] ){ n++; }
+ if( n>0 && zLine[n-1]=='\n' ){
+ n--;
+ zLine[n] = 0;
+ eol = 1;
+ }
+ }
+ zLine = realloc( zLine, n+1 );
+ return zLine;
+}
+
+/*
+** Retrieve a single line of input text.
+**
+** zPrior is a string of prior text retrieved. If not the empty
+** string, then issue a continuation prompt.
+*/
+static char *one_input_line(const char *zPrior, FILE *in){
+ char *zPrompt;
+ char *zResult;
+ if( in!=0 ){
+ return local_getline(0, in);
+ }
+ if( zPrior && zPrior[0] ){
+ zPrompt = continuePrompt;
+ }else{
+ zPrompt = mainPrompt;
+ }
+ zResult = readline(zPrompt);
+#if defined(HAVE_READLINE) && HAVE_READLINE==1
+ if( zResult && *zResult ) add_history(zResult);
+#endif
+ return zResult;
+}
+
+struct previous_mode_data {
+ int valid; /* Is there legit data in here? */
+ int mode;
+ int showHeader;
+ int colWidth[100];
+};
+
+/*
+** An pointer to an instance of this structure is passed from
+** the main program to the callback. This is used to communicate
+** state and mode information.
+*/
+struct callback_data {
+ sqlite3 *db; /* The database */
+ int echoOn; /* True to echo input commands */
+ int cnt; /* Number of records displayed so far */
+ FILE *out; /* Write results here */
+ int mode; /* An output mode setting */
+ int writableSchema; /* True if PRAGMA writable_schema=ON */
+ int showHeader; /* True to show column names in List or Column mode */
+ char *zDestTable; /* Name of destination table when MODE_Insert */
+ char separator[20]; /* Separator character for MODE_List */
+ int colWidth[100]; /* Requested width of each column when in column mode*/
+ int actualWidth[100]; /* Actual width of each column */
+ char nullvalue[20]; /* The text to print when a NULL comes back from
+ ** the database */
+ struct previous_mode_data explainPrev;
+ /* Holds the mode information just before
+ ** .explain ON */
+ char outfile[FILENAME_MAX]; /* Filename for *out */
+ const char *zDbFilename; /* name of the database file */
+};
+
+/*
+** These are the allowed modes.
+*/
+#define MODE_Line 0 /* One column per line. Blank line between records */
+#define MODE_Column 1 /* One record per line in neat columns */
+#define MODE_List 2 /* One record per line with a separator */
+#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */
+#define MODE_Html 4 /* Generate an XHTML table */
+#define MODE_Insert 5 /* Generate SQL "insert" statements */
+#define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */
+#define MODE_Csv 7 /* Quote strings, numbers are plain */
+#define MODE_NUM_OF 8 /* The number of modes (not a mode itself) */
+#define MODE_Explain 9 /* Like MODE_Column, but do not truncate data */
+
+static const char *modeDescr[MODE_NUM_OF] = {
+ "line",
+ "column",
+ "list",
+ "semi",
+ "html",
+ "insert",
+ "tcl",
+ "csv",
+};
+
+/*
+** Number of elements in an array
+*/
+#define ArraySize(X) (sizeof(X)/sizeof(X[0]))
+
+/*
+** Output the given string as a quoted string using SQL quoting conventions.
+*/
+static void output_quoted_string(FILE *out, const char *z){
+ int i;
+ int nSingle = 0;
+ for(i=0; z[i]; i++){
+ if( z[i]=='\'' ) nSingle++;
+ }
+ if( nSingle==0 ){
+ fprintf(out,"'%s'",z);
+ }else{
+ fprintf(out,"'");
+ while( *z ){
+ for(i=0; z[i] && z[i]!='\''; i++){}
+ if( i==0 ){
+ fprintf(out,"''");
+ z++;
+ }else if( z[i]=='\'' ){
+ fprintf(out,"%.*s''",i,z);
+ z += i+1;
+ }else{
+ fprintf(out,"%s",z);
+ break;
+ }
+ }
+ fprintf(out,"'");
+ }
+}
+
+/*
+** Output the given string as a quoted according to C or TCL quoting rules.
+*/
+static void output_c_string(FILE *out, const char *z){
+ unsigned int c;
+ fputc('"', out);
+ while( (c = *(z++))!=0 ){
+ if( c=='\\' ){
+ fputc(c, out);
+ fputc(c, out);
+ }else if( c=='\t' ){
+ fputc('\\', out);
+ fputc('t', out);
+ }else if( c=='\n' ){
+ fputc('\\', out);
+ fputc('n', out);
+ }else if( c=='\r' ){
+ fputc('\\', out);
+ fputc('r', out);
+ }else if( !isprint(c) ){
+ fprintf(out, "\\%03o", c&0xff);
+ }else{
+ fputc(c, out);
+ }
+ }
+ fputc('"', out);
+}
+
+/*
+** Output the given string with characters that are special to
+** HTML escaped.
+*/
+static void output_html_string(FILE *out, const char *z){
+ int i;
+ while( *z ){
+ for(i=0; z[i] && z[i]!='<' && z[i]!='&'; i++){}
+ if( i>0 ){
+ fprintf(out,"%.*s",i,z);
+ }
+ if( z[i]=='<' ){
+ fprintf(out,"<");
+ }else if( z[i]=='&' ){
+ fprintf(out,"&");
+ }else{
+ break;
+ }
+ z += i + 1;
+ }
+}
+
+/*
+** If a field contains any character identified by a 1 in the following
+** array, then the string must be quoted for CSV.
+*/
+static const char needCsvQuote[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+/*
+** Output a single term of CSV. Actually, p->separator is used for
+** the separator, which may or may not be a comma. p->nullvalue is
+** the null value. Strings are quoted using ANSI-C rules. Numbers
+** appear outside of quotes.
+*/
+static void output_csv(struct callback_data *p, const char *z, int bSep){
+ FILE *out = p->out;
+ if( z==0 ){
+ fprintf(out,"%s",p->nullvalue);
+ }else{
+ int i;
+ int nSep = strlen(p->separator);
+ for(i=0; z[i]; i++){
+ if( needCsvQuote[((unsigned char*)z)[i]]
+ || (z[i]==p->separator[0] &&
+ (nSep==1 || memcmp(z, p->separator, nSep)==0)) ){
+ i = 0;
+ break;
+ }
+ }
+ if( i==0 ){
+ putc('"', out);
+ for(i=0; z[i]; i++){
+ if( z[i]=='"' ) putc('"', out);
+ putc(z[i], out);
+ }
+ putc('"', out);
+ }else{
+ fprintf(out, "%s", z);
+ }
+ }
+ if( bSep ){
+ fprintf(p->out, "%s", p->separator);
+ }
+}
+
+#ifdef SIGINT
+/*
+** This routine runs when the user presses Ctrl-C
+*/
+static void interrupt_handler(int NotUsed){
+ seenInterrupt = 1;
+ if( db ) sqlite3_interrupt(db);
+}
+#endif
+
+/*
+** This is the callback routine that the SQLite library
+** invokes for each row of a query result.
+*/
+static int callback(void *pArg, int nArg, char **azArg, char **azCol){
+ int i;
+ struct callback_data *p = (struct callback_data*)pArg;
+ switch( p->mode ){
+ case MODE_Line: {
+ int w = 5;
+ if( azArg==0 ) break;
+ for(i=0; i<nArg; i++){
+ int len = strlen(azCol[i] ? azCol[i] : "");
+ if( len>w ) w = len;
+ }
+ if( p->cnt++>0 ) fprintf(p->out,"\n");
+ for(i=0; i<nArg; i++){
+ fprintf(p->out,"%*s = %s\n", w, azCol[i],
+ azArg[i] ? azArg[i] : p->nullvalue);
+ }
+ break;
+ }
+ case MODE_Explain:
+ case MODE_Column: {
+ if( p->cnt++==0 ){
+ for(i=0; i<nArg; i++){
+ int w, n;
+ if( i<ArraySize(p->colWidth) ){
+ w = p->colWidth[i];
+ }else{
+ w = 0;
+ }
+ if( w<=0 ){
+ w = strlen(azCol[i] ? azCol[i] : "");
+ if( w<10 ) w = 10;
+ n = strlen(azArg && azArg[i] ? azArg[i] : p->nullvalue);
+ if( w<n ) w = n;
+ }
+ if( i<ArraySize(p->actualWidth) ){
+ p->actualWidth[i] = w;
+ }
+ if( p->showHeader ){
+ fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " ");
+ }
+ }
+ if( p->showHeader ){
+ for(i=0; i<nArg; i++){
+ int w;
+ if( i<ArraySize(p->actualWidth) ){
+ w = p->actualWidth[i];
+ }else{
+ w = 10;
+ }
+ fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------"
+ "----------------------------------------------------------",
+ i==nArg-1 ? "\n": " ");
+ }
+ }
+ }
+ if( azArg==0 ) break;
+ for(i=0; i<nArg; i++){
+ int w;
+ if( i<ArraySize(p->actualWidth) ){
+ w = p->actualWidth[i];
+ }else{
+ w = 10;
+ }
+ if( p->mode==MODE_Explain && azArg[i] && strlen(azArg[i])>w ){
+ w = strlen(azArg[i]);
+ }
+ fprintf(p->out,"%-*.*s%s",w,w,
+ azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " ");
+ }
+ break;
+ }
+ case MODE_Semi:
+ case MODE_List: {
+ if( p->cnt++==0 && p->showHeader ){
+ for(i=0; i<nArg; i++){
+ fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator);
+ }
+ }
+ if( azArg==0 ) break;
+ for(i=0; i<nArg; i++){
+ char *z = azArg[i];
+ if( z==0 ) z = p->nullvalue;
+ fprintf(p->out, "%s", z);
+ if( i<nArg-1 ){
+ fprintf(p->out, "%s", p->separator);
+ }else if( p->mode==MODE_Semi ){
+ fprintf(p->out, ";\n");
+ }else{
+ fprintf(p->out, "\n");
+ }
+ }
+ break;
+ }
+ case MODE_Html: {
+ if( p->cnt++==0 && p->showHeader ){
+ fprintf(p->out,"<TR>");
+ for(i=0; i<nArg; i++){
+ fprintf(p->out,"<TH>%s</TH>",azCol[i]);
+ }
+ fprintf(p->out,"</TR>\n");
+ }
+ if( azArg==0 ) break;
+ fprintf(p->out,"<TR>");
+ for(i=0; i<nArg; i++){
+ fprintf(p->out,"<TD>");
+ output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue);
+ fprintf(p->out,"</TD>\n");
+ }
+ fprintf(p->out,"</TR>\n");
+ break;
+ }
+ case MODE_Tcl: {
+ if( p->cnt++==0 && p->showHeader ){
+ for(i=0; i<nArg; i++){
+ output_c_string(p->out,azCol[i] ? azCol[i] : "");
+ fprintf(p->out, "%s", p->separator);
+ }
+ fprintf(p->out,"\n");
+ }
+ if( azArg==0 ) break;
+ for(i=0; i<nArg; i++){
+ output_c_string(p->out, azArg[i] ? azArg[i] : p->nullvalue);
+ fprintf(p->out, "%s", p->separator);
+ }
+ fprintf(p->out,"\n");
+ break;
+ }
+ case MODE_Csv: {
+ if( p->cnt++==0 && p->showHeader ){
+ for(i=0; i<nArg; i++){
+ output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1);
+ }
+ fprintf(p->out,"\n");
+ }
+ if( azArg==0 ) break;
+ for(i=0; i<nArg; i++){
+ output_csv(p, azArg[i], i<nArg-1);
+ }
+ fprintf(p->out,"\n");
+ break;
+ }
+ case MODE_Insert: {
+ if( azArg==0 ) break;
+ fprintf(p->out,"INSERT INTO %s VALUES(",p->zDestTable);
+ for(i=0; i<nArg; i++){
+ char *zSep = i>0 ? ",": "";
+ if( azArg[i]==0 ){
+ fprintf(p->out,"%sNULL",zSep);
+ }else if( isNumber(azArg[i], 0) ){
+ fprintf(p->out,"%s%s",zSep, azArg[i]);
+ }else{
+ if( zSep[0] ) fprintf(p->out,"%s",zSep);
+ output_quoted_string(p->out, azArg[i]);
+ }
+ }
+ fprintf(p->out,");\n");
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+** Set the destination table field of the callback_data structure to
+** the name of the table given. Escape any quote characters in the
+** table name.
+*/
+static void set_table_name(struct callback_data *p, const char *zName){
+ int i, n;
+ int needQuote;
+ char *z;
+
+ if( p->zDestTable ){
+ free(p->zDestTable);
+ p->zDestTable = 0;
+ }
+ if( zName==0 ) return;
+ needQuote = !isalpha((unsigned char)*zName) && *zName!='_';
+ for(i=n=0; zName[i]; i++, n++){
+ if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ){
+ needQuote = 1;
+ if( zName[i]=='\'' ) n++;
+ }
+ }
+ if( needQuote ) n += 2;
+ z = p->zDestTable = malloc( n+1 );
+ if( z==0 ){
+ fprintf(stderr,"Out of memory!\n");
+ exit(1);
+ }
+ n = 0;
+ if( needQuote ) z[n++] = '\'';
+ for(i=0; zName[i]; i++){
+ z[n++] = zName[i];
+ if( zName[i]=='\'' ) z[n++] = '\'';
+ }
+ if( needQuote ) z[n++] = '\'';
+ z[n] = 0;
+}
+
+/* zIn is either a pointer to a NULL-terminated string in memory obtained
+** from malloc(), or a NULL pointer. The string pointed to by zAppend is
+** added to zIn, and the result returned in memory obtained from malloc().
+** zIn, if it was not NULL, is freed.
+**
+** If the third argument, quote, is not '\0', then it is used as a
+** quote character for zAppend.
+*/
+static char *appendText(char *zIn, char const *zAppend, char quote){
+ int len;
+ int i;
+ int nAppend = strlen(zAppend);
+ int nIn = (zIn?strlen(zIn):0);
+
+ len = nAppend+nIn+1;
+ if( quote ){
+ len += 2;
+ for(i=0; i<nAppend; i++){
+ if( zAppend[i]==quote ) len++;
+ }
+ }
+
+ zIn = (char *)realloc(zIn, len);
+ if( !zIn ){
+ return 0;
+ }
+
+ if( quote ){
+ char *zCsr = &zIn[nIn];
+ *zCsr++ = quote;
+ for(i=0; i<nAppend; i++){
+ *zCsr++ = zAppend[i];
+ if( zAppend[i]==quote ) *zCsr++ = quote;
+ }
+ *zCsr++ = quote;
+ *zCsr++ = '\0';
+ assert( (zCsr-zIn)==len );
+ }else{
+ memcpy(&zIn[nIn], zAppend, nAppend);
+ zIn[len-1] = '\0';
+ }
+
+ return zIn;
+}
+
+
+/*
+** Execute a query statement that has a single result column. Print
+** that result column on a line by itself with a semicolon terminator.
+**
+** This is used, for example, to show the schema of the database by
+** querying the SQLITE_MASTER table.
+*/
+static int run_table_dump_query(FILE *out, sqlite3 *db, const char *zSelect){
+ sqlite3_stmt *pSelect;
+ int rc;
+ rc = sqlite3_prepare(db, zSelect, -1, &pSelect, 0);
+ if( rc!=SQLITE_OK || !pSelect ){
+ return rc;
+ }
+ rc = sqlite3_step(pSelect);
+ while( rc==SQLITE_ROW ){
+ fprintf(out, "%s;\n", sqlite3_column_text(pSelect, 0));
+ rc = sqlite3_step(pSelect);
+ }
+ return sqlite3_finalize(pSelect);
+}
+
+
+/*
+** This is a different callback routine used for dumping the database.
+** Each row received by this callback consists of a table name,
+** the table type ("index" or "table") and SQL to create the table.
+** This routine should print text sufficient to recreate the table.
+*/
+static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
+ int rc;
+ const char *zTable;
+ const char *zType;
+ const char *zSql;
+ struct callback_data *p = (struct callback_data *)pArg;
+
+ if( nArg!=3 ) return 1;
+ zTable = azArg[0];
+ zType = azArg[1];
+ zSql = azArg[2];
+
+ if( strcmp(zTable, "sqlite_sequence")==0 ){
+ fprintf(p->out, "DELETE FROM sqlite_sequence;\n");
+ }else if( strcmp(zTable, "sqlite_stat1")==0 ){
+ fprintf(p->out, "ANALYZE sqlite_master;\n");
+ }else if( strncmp(zTable, "sqlite_", 7)==0 ){
+ return 0;
+ }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
+ char *zIns;
+ if( !p->writableSchema ){
+ fprintf(p->out, "PRAGMA writable_schema=ON;\n");
+ p->writableSchema = 1;
+ }
+ zIns = sqlite3_mprintf(
+ "INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)"
+ "VALUES('table','%q','%q',0,'%q');",
+ zTable, zTable, zSql);
+ fprintf(p->out, "%s\n", zIns);
+ sqlite3_free(zIns);
+ return 0;
+ }else{
+ fprintf(p->out, "%s;\n", zSql);
+ }
+
+ if( strcmp(zType, "table")==0 ){
+ sqlite3_stmt *pTableInfo = 0;
+ char *zSelect = 0;
+ char *zTableInfo = 0;
+ char *zTmp = 0;
+
+ zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0);
+ zTableInfo = appendText(zTableInfo, zTable, '"');
+ zTableInfo = appendText(zTableInfo, ");", 0);
+
+ rc = sqlite3_prepare(p->db, zTableInfo, -1, &pTableInfo, 0);
+ if( zTableInfo ) free(zTableInfo);
+ if( rc!=SQLITE_OK || !pTableInfo ){
+ return 1;
+ }
+
+ zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0);
+ zTmp = appendText(zTmp, zTable, '"');
+ if( zTmp ){
+ zSelect = appendText(zSelect, zTmp, '\'');
+ }
+ zSelect = appendText(zSelect, " || ' VALUES(' || ", 0);
+ rc = sqlite3_step(pTableInfo);
+ while( rc==SQLITE_ROW ){
+ const char *zText = (const char *)sqlite3_column_text(pTableInfo, 1);
+ zSelect = appendText(zSelect, "quote(", 0);
+ zSelect = appendText(zSelect, zText, '"');
+ rc = sqlite3_step(pTableInfo);
+ if( rc==SQLITE_ROW ){
+ zSelect = appendText(zSelect, ") || ',' || ", 0);
+ }else{
+ zSelect = appendText(zSelect, ") ", 0);
+ }
+ }
+ rc = sqlite3_finalize(pTableInfo);
+ if( rc!=SQLITE_OK ){
+ if( zSelect ) free(zSelect);
+ return 1;
+ }
+ zSelect = appendText(zSelect, "|| ')' FROM ", 0);
+ zSelect = appendText(zSelect, zTable, '"');
+
+ rc = run_table_dump_query(p->out, p->db, zSelect);
+ if( rc==SQLITE_CORRUPT ){
+ zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0);
+ rc = run_table_dump_query(p->out, p->db, zSelect);
+ }
+ if( zSelect ) free(zSelect);
+ }
+ return 0;
+}
+
+/*
+** Run zQuery. Use dump_callback() as the callback routine so that
+** the contents of the query are output as SQL statements.
+**
+** If we get a SQLITE_CORRUPT error, rerun the query after appending
+** "ORDER BY rowid DESC" to the end.
+*/
+static int run_schema_dump_query(
+ struct callback_data *p,
+ const char *zQuery,
+ char **pzErrMsg
+){
+ int rc;
+ rc = sqlite3_exec(p->db, zQuery, dump_callback, p, pzErrMsg);
+ if( rc==SQLITE_CORRUPT ){
+ char *zQ2;
+ int len = strlen(zQuery);
+ if( pzErrMsg ) sqlite3_free(*pzErrMsg);
+ zQ2 = malloc( len+100 );
+ if( zQ2==0 ) return rc;
+ sqlite3_snprintf(sizeof(zQ2), zQ2, "%s ORDER BY rowid DESC", zQuery);
+ rc = sqlite3_exec(p->db, zQ2, dump_callback, p, pzErrMsg);
+ free(zQ2);
+ }
+ return rc;
+}
+
+/*
+** Text of a help message
+*/
+static char zHelp[] =
+ ".bail ON|OFF Stop after hitting an error. Default OFF\n"
+ ".databases List names and files of attached databases\n"
+ ".dump ?TABLE? ... Dump the database in an SQL text format\n"
+ ".echo ON|OFF Turn command echo on or off\n"
+ ".exit Exit this program\n"
+ ".explain ON|OFF Turn output mode suitable for EXPLAIN on or off.\n"
+ ".header(s) ON|OFF Turn display of headers on or off\n"
+ ".help Show this message\n"
+ ".import FILE TABLE Import data from FILE into TABLE\n"
+ ".indices TABLE Show names of all indices on TABLE\n"
+#ifdef SQLITE_ENABLE_IOTRACE
+ ".iotrace FILE Enable I/O diagnostic logging to FILE\n"
+#endif
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+ ".load FILE ?ENTRY? Load an extension library\n"
+#endif
+ ".mode MODE ?TABLE? Set output mode where MODE is one of:\n"
+ " csv Comma-separated values\n"
+ " column Left-aligned columns. (See .width)\n"
+ " html HTML <table> code\n"
+ " insert SQL insert statements for TABLE\n"
+ " line One value per line\n"
+ " list Values delimited by .separator string\n"
+ " tabs Tab-separated values\n"
+ " tcl TCL list elements\n"
+ ".nullvalue STRING Print STRING in place of NULL values\n"
+ ".output FILENAME Send output to FILENAME\n"
+ ".output stdout Send output to the screen\n"
+ ".prompt MAIN CONTINUE Replace the standard prompts\n"
+ ".quit Exit this program\n"
+ ".read FILENAME Execute SQL in FILENAME\n"
+ ".schema ?TABLE? Show the CREATE statements\n"
+ ".separator STRING Change separator used by output mode and .import\n"
+ ".show Show the current values for various settings\n"
+ ".tables ?PATTERN? List names of tables matching a LIKE pattern\n"
+ ".timeout MS Try opening locked tables for MS milliseconds\n"
+#if HAS_TIMER
+ ".timer ON|OFF Turn the CPU timer measurement on or off\n"
+#endif
+ ".width NUM NUM ... Set column widths for \"column\" mode\n"
+;
+
+/* Forward reference */
+static int process_input(struct callback_data *p, FILE *in);
+
+/*
+** Make sure the database is open. If it is not, then open it. If
+** the database fails to open, print an error message and exit.
+*/
+static void open_db(struct callback_data *p){
+ if( p->db==0 ){
+ sqlite3_open(p->zDbFilename, &p->db);
+ db = p->db;
+ sqlite3_create_function(db, "shellstatic", 0, SQLITE_UTF8, 0,
+ shellstaticFunc, 0, 0);
+ if( SQLITE_OK!=sqlite3_errcode(db) ){
+ fprintf(stderr,"Unable to open database \"%s\": %s\n",
+ p->zDbFilename, sqlite3_errmsg(db));
+ exit(1);
+ }
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+ sqlite3_enable_load_extension(p->db, 1);
+#endif
+ }
+}
+
+/*
+** Do C-language style dequoting.
+**
+** \t -> tab
+** \n -> newline
+** \r -> carriage return
+** \NNN -> ascii character NNN in octal
+** \\ -> backslash
+*/
+static void resolve_backslashes(char *z){
+ int i, j, c;
+ for(i=j=0; (c = z[i])!=0; i++, j++){
+ if( c=='\\' ){
+ c = z[++i];
+ if( c=='n' ){
+ c = '\n';
+ }else if( c=='t' ){
+ c = '\t';
+ }else if( c=='r' ){
+ c = '\r';
+ }else if( c>='0' && c<='7' ){
+ c -= '0';
+ if( z[i+1]>='0' && z[i+1]<='7' ){
+ i++;
+ c = (c<<3) + z[i] - '0';
+ if( z[i+1]>='0' && z[i+1]<='7' ){
+ i++;
+ c = (c<<3) + z[i] - '0';
+ }
+ }
+ }
+ }
+ z[j] = c;
+ }
+ z[j] = 0;
+}
+
+/*
+** Interpret zArg as a boolean value. Return either 0 or 1.
+*/
+static int booleanValue(char *zArg){
+ int val = atoi(zArg);
+ int j;
+ for(j=0; zArg[j]; j++){
+ zArg[j] = tolower(zArg[j]);
+ }
+ if( strcmp(zArg,"on")==0 ){
+ val = 1;
+ }else if( strcmp(zArg,"yes")==0 ){
+ val = 1;
+ }
+ return val;
+}
+
+/*
+** If an input line begins with "." then invoke this routine to
+** process that line.
+**
+** Return 1 on error, 2 to exit, and 0 otherwise.
+*/
+static int do_meta_command(char *zLine, struct callback_data *p){
+ int i = 1;
+ int nArg = 0;
+ int n, c;
+ int rc = 0;
+ char *azArg[50];
+
+ /* Parse the input line into tokens.
+ */
+ while( zLine[i] && nArg<ArraySize(azArg) ){
+ while( isspace((unsigned char)zLine[i]) ){ i++; }
+ if( zLine[i]==0 ) break;
+ if( zLine[i]=='\'' || zLine[i]=='"' ){
+ int delim = zLine[i++];
+ azArg[nArg++] = &zLine[i];
+ while( zLine[i] && zLine[i]!=delim ){ i++; }
+ if( zLine[i]==delim ){
+ zLine[i++] = 0;
+ }
+ if( delim=='"' ) resolve_backslashes(azArg[nArg-1]);
+ }else{
+ azArg[nArg++] = &zLine[i];
+ while( zLine[i] && !isspace((unsigned char)zLine[i]) ){ i++; }
+ if( zLine[i] ) zLine[i++] = 0;
+ resolve_backslashes(azArg[nArg-1]);
+ }
+ }
+
+ /* Process the input line.
+ */
+ if( nArg==0 ) return rc;
+ n = strlen(azArg[0]);
+ c = azArg[0][0];
+ if( c=='b' && n>1 && strncmp(azArg[0], "bail", n)==0 && nArg>1 ){
+ bail_on_error = booleanValue(azArg[1]);
+ }else
+
+ if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){
+ struct callback_data data;
+ char *zErrMsg = 0;
+ open_db(p);
+ memcpy(&data, p, sizeof(data));
+ data.showHeader = 1;
+ data.mode = MODE_Column;
+ data.colWidth[0] = 3;
+ data.colWidth[1] = 15;
+ data.colWidth[2] = 58;
+ data.cnt = 0;
+ sqlite3_exec(p->db, "PRAGMA database_list; ", callback, &data, &zErrMsg);
+ if( zErrMsg ){
+ fprintf(stderr,"Error: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ }
+ }else
+
+ if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
+ char *zErrMsg = 0;
+ open_db(p);
+ fprintf(p->out, "BEGIN TRANSACTION;\n");
+ p->writableSchema = 0;
+ if( nArg==1 ){
+ run_schema_dump_query(p,
+ "SELECT name, type, sql FROM sqlite_master "
+ "WHERE sql NOT NULL AND type=='table'", 0
+ );
+ run_table_dump_query(p->out, p->db,
+ "SELECT sql FROM sqlite_master "
+ "WHERE sql NOT NULL AND type IN ('index','trigger','view')"
+ );
+ }else{
+ int i;
+ for(i=1; i<nArg; i++){
+ zShellStatic = azArg[i];
+ run_schema_dump_query(p,
+ "SELECT name, type, sql FROM sqlite_master "
+ "WHERE tbl_name LIKE shellstatic() AND type=='table'"
+ " AND sql NOT NULL", 0);
+ run_table_dump_query(p->out, p->db,
+ "SELECT sql FROM sqlite_master "
+ "WHERE sql NOT NULL"
+ " AND type IN ('index','trigger','view')"
+ " AND tbl_name LIKE shellstatic()"
+ );
+ zShellStatic = 0;
+ }
+ }
+ if( p->writableSchema ){
+ fprintf(p->out, "PRAGMA writable_schema=OFF;\n");
+ p->writableSchema = 0;
+ }
+ if( zErrMsg ){
+ fprintf(stderr,"Error: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ }else{
+ fprintf(p->out, "COMMIT;\n");
+ }
+ }else
+
+ if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 ){
+ p->echoOn = booleanValue(azArg[1]);
+ }else
+
+ if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
+ rc = 2;
+ }else
+
+ if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){
+ int val = nArg>=2 ? booleanValue(azArg[1]) : 1;
+ if(val == 1) {
+ if(!p->explainPrev.valid) {
+ p->explainPrev.valid = 1;
+ p->explainPrev.mode = p->mode;
+ p->explainPrev.showHeader = p->showHeader;
+ memcpy(p->explainPrev.colWidth,p->colWidth,sizeof(p->colWidth));
+ }
+ /* We could put this code under the !p->explainValid
+ ** condition so that it does not execute if we are already in
+ ** explain mode. However, always executing it allows us an easy
+ ** was to reset to explain mode in case the user previously
+ ** did an .explain followed by a .width, .mode or .header
+ ** command.
+ */
+ p->mode = MODE_Explain;
+ p->showHeader = 1;
+ memset(p->colWidth,0,ArraySize(p->colWidth));
+ p->colWidth[0] = 4; /* addr */
+ p->colWidth[1] = 13; /* opcode */
+ p->colWidth[2] = 4; /* P1 */
+ p->colWidth[3] = 4; /* P2 */
+ p->colWidth[4] = 4; /* P3 */
+ p->colWidth[5] = 13; /* P4 */
+ p->colWidth[6] = 2; /* P5 */
+ p->colWidth[7] = 13; /* Comment */
+ }else if (p->explainPrev.valid) {
+ p->explainPrev.valid = 0;
+ p->mode = p->explainPrev.mode;
+ p->showHeader = p->explainPrev.showHeader;
+ memcpy(p->colWidth,p->explainPrev.colWidth,sizeof(p->colWidth));
+ }
+ }else
+
+ if( c=='h' && (strncmp(azArg[0], "header", n)==0 ||
+ strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){
+ p->showHeader = booleanValue(azArg[1]);
+ }else
+
+ if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
+ fprintf(stderr,zHelp);
+ }else
+
+ if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg>=3 ){
+ char *zTable = azArg[2]; /* Insert data into this table */
+ char *zFile = azArg[1]; /* The file from which to extract data */
+ sqlite3_stmt *pStmt; /* A statement */
+ int rc; /* Result code */
+ int nCol; /* Number of columns in the table */
+ int nByte; /* Number of bytes in an SQL string */
+ int i, j; /* Loop counters */
+ int nSep; /* Number of bytes in p->separator[] */
+ char *zSql; /* An SQL statement */
+ char *zLine; /* A single line of input from the file */
+ char **azCol; /* zLine[] broken up into columns */
+ char *zCommit; /* How to commit changes */
+ FILE *in; /* The input file */
+ int lineno = 0; /* Line number of input file */
+
+ open_db(p);
+ nSep = strlen(p->separator);
+ if( nSep==0 ){
+ fprintf(stderr, "non-null separator required for import\n");
+ return 0;
+ }
+ zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable);
+ if( zSql==0 ) return 0;
+ nByte = strlen(zSql);
+ rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ if( rc ){
+ fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
+ nCol = 0;
+ rc = 1;
+ }else{
+ nCol = sqlite3_column_count(pStmt);
+ }
+ sqlite3_finalize(pStmt);
+ if( nCol==0 ) return 0;
+ zSql = malloc( nByte + 20 + nCol*2 );
+ if( zSql==0 ) return 0;
+ sqlite3_snprintf(nByte+20, zSql, "INSERT INTO '%q' VALUES(?", zTable);
+ j = strlen(zSql);
+ for(i=1; i<nCol; i++){
+ zSql[j++] = ',';
+ zSql[j++] = '?';
+ }
+ zSql[j++] = ')';
+ zSql[j] = 0;
+ rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
+ free(zSql);
+ if( rc ){
+ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
+ sqlite3_finalize(pStmt);
+ return 1;
+ }
+ in = fopen(zFile, "rb");
+ if( in==0 ){
+ fprintf(stderr, "cannot open file: %s\n", zFile);
+ sqlite3_finalize(pStmt);
+ return 0;
+ }
+ azCol = malloc( sizeof(azCol[0])*(nCol+1) );
+ if( azCol==0 ){
+ fclose(in);
+ return 0;
+ }
+ sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
+ zCommit = "COMMIT";
+ while( (zLine = local_getline(0, in))!=0 ){
+ char *z;
+ i = 0;
+ lineno++;
+ azCol[0] = zLine;
+ for(i=0, z=zLine; *z && *z!='\n' && *z!='\r'; z++){
+ if( *z==p->separator[0] && strncmp(z, p->separator, nSep)==0 ){
+ *z = 0;
+ i++;
+ if( i<nCol ){
+ azCol[i] = &z[nSep];
+ z += nSep-1;
+ }
+ }
+ }
+ *z = 0;
+ if( i+1!=nCol ){
+ fprintf(stderr,"%s line %d: expected %d columns of data but found %d\n",
+ zFile, lineno, nCol, i+1);
+ zCommit = "ROLLBACK";
+ break;
+ }
+ for(i=0; i<nCol; i++){
+ sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC);
+ }
+ sqlite3_step(pStmt);
+ rc = sqlite3_reset(pStmt);
+ free(zLine);
+ if( rc!=SQLITE_OK ){
+ fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
+ zCommit = "ROLLBACK";
+ rc = 1;
+ break;
+ }
+ }
+ free(azCol);
+ fclose(in);
+ sqlite3_finalize(pStmt);
+ sqlite3_exec(p->db, zCommit, 0, 0, 0);
+ }else
+
+ if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg>1 ){
+ struct callback_data data;
+ char *zErrMsg = 0;
+ open_db(p);
+ memcpy(&data, p, sizeof(data));
+ data.showHeader = 0;
+ data.mode = MODE_List;
+ zShellStatic = azArg[1];
+ sqlite3_exec(p->db,
+ "SELECT name FROM sqlite_master "
+ "WHERE type='index' AND tbl_name LIKE shellstatic() "
+ "UNION ALL "
+ "SELECT name FROM sqlite_temp_master "
+ "WHERE type='index' AND tbl_name LIKE shellstatic() "
+ "ORDER BY 1",
+ callback, &data, &zErrMsg
+ );
+ zShellStatic = 0;
+ if( zErrMsg ){
+ fprintf(stderr,"Error: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ }
+ }else
+
+#ifdef SQLITE_ENABLE_IOTRACE
+ if( c=='i' && strncmp(azArg[0], "iotrace", n)==0 ){
+ extern void (*sqlite3_io_trace)(const char*, ...);
+ if( iotrace && iotrace!=stdout ) fclose(iotrace);
+ iotrace = 0;
+ if( nArg<2 ){
+ sqlite3_io_trace = 0;
+ }else if( strcmp(azArg[1], "-")==0 ){
+ sqlite3_io_trace = iotracePrintf;
+ iotrace = stdout;
+ }else{
+ iotrace = fopen(azArg[1], "w");
+ if( iotrace==0 ){
+ fprintf(stderr, "cannot open \"%s\"\n", azArg[1]);
+ sqlite3_io_trace = 0;
+ }else{
+ sqlite3_io_trace = iotracePrintf;
+ }
+ }
+ }else
+#endif
+
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+ if( c=='l' && strncmp(azArg[0], "load", n)==0 && nArg>=2 ){
+ const char *zFile, *zProc;
+ char *zErrMsg = 0;
+ int rc;
+ zFile = azArg[1];
+ zProc = nArg>=3 ? azArg[2] : 0;
+ open_db(p);
+ rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg);
+ if( rc!=SQLITE_OK ){
+ fprintf(stderr, "%s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ rc = 1;
+ }
+ }else
+#endif
+
+ if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg>=2 ){
+ int n2 = strlen(azArg[1]);
+ if( strncmp(azArg[1],"line",n2)==0
+ ||
+ strncmp(azArg[1],"lines",n2)==0 ){
+ p->mode = MODE_Line;
+ }else if( strncmp(azArg[1],"column",n2)==0
+ ||
+ strncmp(azArg[1],"columns",n2)==0 ){
+ p->mode = MODE_Column;
+ }else if( strncmp(azArg[1],"list",n2)==0 ){
+ p->mode = MODE_List;
+ }else if( strncmp(azArg[1],"html",n2)==0 ){
+ p->mode = MODE_Html;
+ }else if( strncmp(azArg[1],"tcl",n2)==0 ){
+ p->mode = MODE_Tcl;
+ }else if( strncmp(azArg[1],"csv",n2)==0 ){
+ p->mode = MODE_Csv;
+ sqlite3_snprintf(sizeof(p->separator), p->separator, ",");
+ }else if( strncmp(azArg[1],"tabs",n2)==0 ){
+ p->mode = MODE_List;
+ sqlite3_snprintf(sizeof(p->separator), p->separator, "\t");
+ }else if( strncmp(azArg[1],"insert",n2)==0 ){
+ p->mode = MODE_Insert;
+ if( nArg>=3 ){
+ set_table_name(p, azArg[2]);
+ }else{
+ set_table_name(p, "table");
+ }
+ }else {
+ fprintf(stderr,"mode should be one of: "
+ "column csv html insert line list tabs tcl\n");
+ }
+ }else
+
+ if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 && nArg==2 ) {
+ sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue,
+ "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]);
+ }else
+
+ if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
+ if( p->out!=stdout ){
+ fclose(p->out);
+ }
+ if( strcmp(azArg[1],"stdout")==0 ){
+ p->out = stdout;
+ sqlite3_snprintf(sizeof(p->outfile), p->outfile, "stdout");
+ }else{
+ p->out = fopen(azArg[1], "wb");
+ if( p->out==0 ){
+ fprintf(stderr,"can't write to \"%s\"\n", azArg[1]);
+ p->out = stdout;
+ } else {
+ sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
+ }
+ }
+ }else
+
+ if( c=='p' && strncmp(azArg[0], "prompt", n)==0 && (nArg==2 || nArg==3)){
+ if( nArg >= 2) {
+ strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1);
+ }
+ if( nArg >= 3) {
+ strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1);
+ }
+ }else
+
+ if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){
+ rc = 2;
+ }else
+
+ if( c=='r' && strncmp(azArg[0], "read", n)==0 && nArg==2 ){
+ FILE *alt = fopen(azArg[1], "rb");
+ if( alt==0 ){
+ fprintf(stderr,"can't open \"%s\"\n", azArg[1]);
+ }else{
+ process_input(p, alt);
+ fclose(alt);
+ }
+ }else
+
+ if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){
+ struct callback_data data;
+ char *zErrMsg = 0;
+ open_db(p);
+ memcpy(&data, p, sizeof(data));
+ data.showHeader = 0;
+ data.mode = MODE_Semi;
+ if( nArg>1 ){
+ int i;
+ for(i=0; azArg[1][i]; i++) azArg[1][i] = tolower(azArg[1][i]);
+ if( strcmp(azArg[1],"sqlite_master")==0 ){
+ char *new_argv[2], *new_colv[2];
+ new_argv[0] = "CREATE TABLE sqlite_master (\n"
+ " type text,\n"
+ " name text,\n"
+ " tbl_name text,\n"
+ " rootpage integer,\n"
+ " sql text\n"
+ ")";
+ new_argv[1] = 0;
+ new_colv[0] = "sql";
+ new_colv[1] = 0;
+ callback(&data, 1, new_argv, new_colv);
+ }else if( strcmp(azArg[1],"sqlite_temp_master")==0 ){
+ char *new_argv[2], *new_colv[2];
+ new_argv[0] = "CREATE TEMP TABLE sqlite_temp_master (\n"
+ " type text,\n"
+ " name text,\n"
+ " tbl_name text,\n"
+ " rootpage integer,\n"
+ " sql text\n"
+ ")";
+ new_argv[1] = 0;
+ new_colv[0] = "sql";
+ new_colv[1] = 0;
+ callback(&data, 1, new_argv, new_colv);
+ }else{
+ zShellStatic = azArg[1];
+ sqlite3_exec(p->db,
+ "SELECT sql FROM "
+ " (SELECT * FROM sqlite_master UNION ALL"
+ " SELECT * FROM sqlite_temp_master) "
+ "WHERE tbl_name LIKE shellstatic() AND type!='meta' AND sql NOTNULL "
+ "ORDER BY substr(type,2,1), name",
+ callback, &data, &zErrMsg);
+ zShellStatic = 0;
+ }
+ }else{
+ sqlite3_exec(p->db,
+ "SELECT sql FROM "
+ " (SELECT * FROM sqlite_master UNION ALL"
+ " SELECT * FROM sqlite_temp_master) "
+ "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'"
+ "ORDER BY substr(type,2,1), name",
+ callback, &data, &zErrMsg
+ );
+ }
+ if( zErrMsg ){
+ fprintf(stderr,"Error: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ }
+ }else
+
+ if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
+ sqlite3_snprintf(sizeof(p->separator), p->separator,
+ "%.*s", (int)sizeof(p->separator)-1, azArg[1]);
+ }else
+
+ if( c=='s' && strncmp(azArg[0], "show", n)==0){
+ int i;
+ fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off");
+ fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off");
+ fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off");
+ fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]);
+ fprintf(p->out,"%9.9s: ", "nullvalue");
+ output_c_string(p->out, p->nullvalue);
+ fprintf(p->out, "\n");
+ fprintf(p->out,"%9.9s: %s\n","output",
+ strlen(p->outfile) ? p->outfile : "stdout");
+ fprintf(p->out,"%9.9s: ", "separator");
+ output_c_string(p->out, p->separator);
+ fprintf(p->out, "\n");
+ fprintf(p->out,"%9.9s: ","width");
+ for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) {
+ fprintf(p->out,"%d ",p->colWidth[i]);
+ }
+ fprintf(p->out,"\n");
+ }else
+
+ if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 ){
+ char **azResult;
+ int nRow, rc;
+ char *zErrMsg;
+ open_db(p);
+ if( nArg==1 ){
+ rc = sqlite3_get_table(p->db,
+ "SELECT name FROM sqlite_master "
+ "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'"
+ "UNION ALL "
+ "SELECT name FROM sqlite_temp_master "
+ "WHERE type IN ('table','view') "
+ "ORDER BY 1",
+ &azResult, &nRow, 0, &zErrMsg
+ );
+ }else{
+ zShellStatic = azArg[1];
+ rc = sqlite3_get_table(p->db,
+ "SELECT name FROM sqlite_master "
+ "WHERE type IN ('table','view') AND name LIKE '%'||shellstatic()||'%' "
+ "UNION ALL "
+ "SELECT name FROM sqlite_temp_master "
+ "WHERE type IN ('table','view') AND name LIKE '%'||shellstatic()||'%' "
+ "ORDER BY 1",
+ &azResult, &nRow, 0, &zErrMsg
+ );
+ zShellStatic = 0;
+ }
+ if( zErrMsg ){
+ fprintf(stderr,"Error: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ }
+ if( rc==SQLITE_OK ){
+ int len, maxlen = 0;
+ int i, j;
+ int nPrintCol, nPrintRow;
+ for(i=1; i<=nRow; i++){
+ if( azResult[i]==0 ) continue;
+ len = strlen(azResult[i]);
+ if( len>maxlen ) maxlen = len;
+ }
+ nPrintCol = 80/(maxlen+2);
+ if( nPrintCol<1 ) nPrintCol = 1;
+ nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
+ for(i=0; i<nPrintRow; i++){
+ for(j=i+1; j<=nRow; j+=nPrintRow){
+ char *zSp = j<=nPrintRow ? "" : " ";
+ printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : "");
+ }
+ printf("\n");
+ }
+ }else{
+ rc = 1;
+ }
+ sqlite3_free_table(azResult);
+ }else
+
+ if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg>=2 ){
+ open_db(p);
+ sqlite3_busy_timeout(p->db, atoi(azArg[1]));
+ }else
+
+#if HAS_TIMER
+ if( c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 && nArg>1 ){
+ enableTimer = booleanValue(azArg[1]);
+ }else
+#endif
+
+ if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
+ int j;
+ assert( nArg<=ArraySize(azArg) );
+ for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
+ p->colWidth[j-1] = atoi(azArg[j]);
+ }
+ }else
+
+
+ {
+ fprintf(stderr, "unknown command or invalid arguments: "
+ " \"%s\". Enter \".help\" for help\n", azArg[0]);
+ }
+
+ return rc;
+}
+
+/*
+** Return TRUE if a semicolon occurs anywhere in the first N characters
+** of string z[].
+*/
+static int _contains_semicolon(const char *z, int N){
+ int i;
+ for(i=0; i<N; i++){ if( z[i]==';' ) return 1; }
+ return 0;
+}
+
+/*
+** Test to see if a line consists entirely of whitespace.
+*/
+static int _all_whitespace(const char *z){
+ for(; *z; z++){
+ if( isspace(*(unsigned char*)z) ) continue;
+ if( *z=='/' && z[1]=='*' ){
+ z += 2;
+ while( *z && (*z!='*' || z[1]!='/') ){ z++; }
+ if( *z==0 ) return 0;
+ z++;
+ continue;
+ }
+ if( *z=='-' && z[1]=='-' ){
+ z += 2;
+ while( *z && *z!='\n' ){ z++; }
+ if( *z==0 ) return 1;
+ continue;
+ }
+ return 0;
+ }
+ return 1;
+}
+
+/*
+** Return TRUE if the line typed in is an SQL command terminator other
+** than a semi-colon. The SQL Server style "go" command is understood
+** as is the Oracle "/".
+*/
+static int _is_command_terminator(const char *zLine){
+ while( isspace(*(unsigned char*)zLine) ){ zLine++; };
+ if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ) return 1; /* Oracle */
+ if( tolower(zLine[0])=='g' && tolower(zLine[1])=='o'
+ && _all_whitespace(&zLine[2]) ){
+ return 1; /* SQL Server */
+ }
+ return 0;
+}
+
+/*
+** Read input from *in and process it. If *in==0 then input
+** is interactive - the user is typing it it. Otherwise, input
+** is coming from a file or device. A prompt is issued and history
+** is saved only if input is interactive. An interrupt signal will
+** cause this routine to exit immediately, unless input is interactive.
+**
+** Return the number of errors.
+*/
+static int process_input(struct callback_data *p, FILE *in){
+ char *zLine = 0;
+ char *zSql = 0;
+ int nSql = 0;
+ int nSqlPrior = 0;
+ char *zErrMsg;
+ int rc;
+ int errCnt = 0;
+ int lineno = 0;
+ int startline = 0;
+
+ while( errCnt==0 || !bail_on_error || (in==0 && stdin_is_interactive) ){
+ fflush(p->out);
+ free(zLine);
+ zLine = one_input_line(zSql, in);
+ if( zLine==0 ){
+ break; /* We have reached EOF */
+ }
+ if( seenInterrupt ){
+ if( in!=0 ) break;
+ seenInterrupt = 0;
+ }
+ lineno++;
+ if( p->echoOn ) printf("%s\n", zLine);
+ if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue;
+ if( zLine && zLine[0]=='.' && nSql==0 ){
+ rc = do_meta_command(zLine, p);
+ if( rc==2 ){
+ break;
+ }else if( rc ){
+ errCnt++;
+ }
+ continue;
+ }
+ if( _is_command_terminator(zLine) ){
+ memcpy(zLine,";",2);
+ }
+ nSqlPrior = nSql;
+ if( zSql==0 ){
+ int i;
+ for(i=0; zLine[i] && isspace((unsigned char)zLine[i]); i++){}
+ if( zLine[i]!=0 ){
+ nSql = strlen(zLine);
+ zSql = malloc( nSql+1 );
+ if( zSql==0 ){
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ memcpy(zSql, zLine, nSql+1);
+ startline = lineno;
+ }
+ }else{
+ int len = strlen(zLine);
+ zSql = realloc( zSql, nSql + len + 2 );
+ if( zSql==0 ){
+ fprintf(stderr,"%s: out of memory!\n", Argv0);
+ exit(1);
+ }
+ zSql[nSql++] = '\n';
+ memcpy(&zSql[nSql], zLine, len+1);
+ nSql += len;
+ }
+ if( zSql && _contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior)
+ && sqlite3_complete(zSql) ){
+ p->cnt = 0;
+ open_db(p);
+ BEGIN_TIMER;
+ rc = sqlite3_exec(p->db, zSql, callback, p, &zErrMsg);
+ END_TIMER;
+ if( rc || zErrMsg ){
+ char zPrefix[100];
+ if( in!=0 || !stdin_is_interactive ){
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix,
+ "SQL error near line %d:", startline);
+ }else{
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "SQL error:");
+ }
+ if( zErrMsg!=0 ){
+ printf("%s %s\n", zPrefix, zErrMsg);
+ sqlite3_free(zErrMsg);
+ zErrMsg = 0;
+ }else{
+ printf("%s %s\n", zPrefix, sqlite3_errmsg(p->db));
+ }
+ errCnt++;
+ }
+ free(zSql);
+ zSql = 0;
+ nSql = 0;
+ }
+ }
+ if( zSql ){
+ if( !_all_whitespace(zSql) ) printf("Incomplete SQL: %s\n", zSql);
+ free(zSql);
+ }
+ free(zLine);
+ return errCnt;
+}
+
+/*
+** Return a pathname which is the user's home directory. A
+** 0 return indicates an error of some kind. Space to hold the
+** resulting string is obtained from malloc(). The calling
+** function should free the result.
+*/
+static char *find_home_dir(void){
+ char *home_dir = NULL;
+
+#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(_WIN32_WCE)
+ struct passwd *pwent;
+ uid_t uid = getuid();
+ if( (pwent=getpwuid(uid)) != NULL) {
+ home_dir = pwent->pw_dir;
+ }
+#endif
+
+#if defined(_WIN32_WCE)
+ /* Windows CE (arm-wince-mingw32ce-gcc) does not provide getenv()
+ */
+ home_dir = strdup("/");
+#else
+
+#if defined(_WIN32) || defined(WIN32) || defined(__OS2__)
+ if (!home_dir) {
+ home_dir = getenv("USERPROFILE");
+ }
+#endif
+
+ if (!home_dir) {
+ home_dir = getenv("HOME");
+ }
+
+#if defined(_WIN32) || defined(WIN32) || defined(__OS2__)
+ if (!home_dir) {
+ char *zDrive, *zPath;
+ int n;
+ zDrive = getenv("HOMEDRIVE");
+ zPath = getenv("HOMEPATH");
+ if( zDrive && zPath ){
+ n = strlen(zDrive) + strlen(zPath) + 1;
+ home_dir = malloc( n );
+ if( home_dir==0 ) return 0;
+ sqlite3_snprintf(n, home_dir, "%s%s", zDrive, zPath);
+ return home_dir;
+ }
+ home_dir = "c:\\";
+ }
+#endif
+
+#endif /* !_WIN32_WCE */
+
+ if( home_dir ){
+ int n = strlen(home_dir) + 1;
+ char *z = malloc( n );
+ if( z ) memcpy(z, home_dir, n);
+ home_dir = z;
+ }
+
+ return home_dir;
+}
+
+/*
+** Read input from the file given by sqliterc_override. Or if that
+** parameter is NULL, take input from ~/.sqliterc
+*/
+static void process_sqliterc(
+ struct callback_data *p, /* Configuration data */
+ const char *sqliterc_override /* Name of config file. NULL to use default */
+){
+ char *home_dir = NULL;
+ const char *sqliterc = sqliterc_override;
+ char *zBuf = 0;
+ FILE *in = NULL;
+ int nBuf;
+
+ if (sqliterc == NULL) {
+ home_dir = find_home_dir();
+ if( home_dir==0 ){
+ fprintf(stderr,"%s: cannot locate your home directory!\n", Argv0);
+ return;
+ }
+ nBuf = strlen(home_dir) + 16;
+ zBuf = malloc( nBuf );
+ if( zBuf==0 ){
+ fprintf(stderr,"%s: out of memory!\n", Argv0);
+ exit(1);
+ }
+ sqlite3_snprintf(nBuf, zBuf,"%s/.sqliterc",home_dir);
+ free(home_dir);
+ sqliterc = (const char*)zBuf;
+ }
+ in = fopen(sqliterc,"rb");
+ if( in ){
+ if( stdin_is_interactive ){
+ printf("-- Loading resources from %s\n",sqliterc);
+ }
+ process_input(p,in);
+ fclose(in);
+ }
+ free(zBuf);
+ return;
+}
+
+/*
+** Show available command line options
+*/
+static const char zOptions[] =
+ " -init filename read/process named file\n"
+ " -echo print commands before execution\n"
+ " -[no]header turn headers on or off\n"
+ " -bail stop after hitting an error\n"
+ " -interactive force interactive I/O\n"
+ " -batch force batch I/O\n"
+ " -column set output mode to 'column'\n"
+ " -csv set output mode to 'csv'\n"
+ " -html set output mode to HTML\n"
+ " -line set output mode to 'line'\n"
+ " -list set output mode to 'list'\n"
+ " -separator 'x' set output field separator (|)\n"
+ " -nullvalue 'text' set text string for NULL values\n"
+ " -version show SQLite version\n"
+;
+static void usage(int showDetail){
+ fprintf(stderr,
+ "Usage: %s [OPTIONS] FILENAME [SQL]\n"
+ "FILENAME is the name of an SQLite database. A new database is created\n"
+ "if the file does not previously exist.\n", Argv0);
+ if( showDetail ){
+ fprintf(stderr, "OPTIONS include:\n%s", zOptions);
+ }else{
+ fprintf(stderr, "Use the -help option for additional information\n");
+ }
+ exit(1);
+}
+
+/*
+** Initialize the state information in data
+*/
+static void main_init(struct callback_data *data) {
+ memset(data, 0, sizeof(*data));
+ data->mode = MODE_List;
+ memcpy(data->separator,"|", 2);
+ data->showHeader = 0;
+ sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> ");
+ sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> ");
+}
+
+int main(int argc, char **argv){
+ char *zErrMsg = 0;
+ struct callback_data data;
+ const char *zInitFile = 0;
+ char *zFirstCmd = 0;
+ int i;
+ int rc = 0;
+
+ Argv0 = argv[0];
+ main_init(&data);
+ stdin_is_interactive = isatty(0);
+
+ /* Make sure we have a valid signal handler early, before anything
+ ** else is done.
+ */
+#ifdef SIGINT
+ signal(SIGINT, interrupt_handler);
+#endif
+
+ /* Do an initial pass through the command-line argument to locate
+ ** the name of the database file, the name of the initialization file,
+ ** and the first command to execute.
+ */
+ for(i=1; i<argc-1; i++){
+ char *z;
+ if( argv[i][0]!='-' ) break;
+ z = argv[i];
+ if( z[0]=='-' && z[1]=='-' ) z++;
+ if( strcmp(argv[i],"-separator")==0 || strcmp(argv[i],"-nullvalue")==0 ){
+ i++;
+ }else if( strcmp(argv[i],"-init")==0 ){
+ i++;
+ zInitFile = argv[i];
+ }
+ }
+ if( i<argc ){
+ data.zDbFilename = argv[i++];
+ }else{
+#ifndef SQLITE_OMIT_MEMORYDB
+ data.zDbFilename = ":memory:";
+#else
+ data.zDbFilename = 0;
+#endif
+ }
+ if( i<argc ){
+ zFirstCmd = argv[i++];
+ }
+ data.out = stdout;
+
+#ifdef SQLITE_OMIT_MEMORYDB
+ if( data.zDbFilename==0 ){
+ fprintf(stderr,"%s: no database filename specified\n", argv[0]);
+ exit(1);
+ }
+#endif
+
+ /* Go ahead and open the database file if it already exists. If the
+ ** file does not exist, delay opening it. This prevents empty database
+ ** files from being created if a user mistypes the database name argument
+ ** to the sqlite command-line tool.
+ */
+ if( access(data.zDbFilename, 0)==0 ){
+ open_db(&data);
+ }
+
+ /* Process the initialization file if there is one. If no -init option
+ ** is given on the command line, look for a file named ~/.sqliterc and
+ ** try to process it.
+ */
+ process_sqliterc(&data,zInitFile);
+
+ /* Make a second pass through the command-line argument and set
+ ** options. This second pass is delayed until after the initialization
+ ** file is processed so that the command-line arguments will override
+ ** settings in the initialization file.
+ */
+ for(i=1; i<argc && argv[i][0]=='-'; i++){
+ char *z = argv[i];
+ if( z[1]=='-' ){ z++; }
+ if( strcmp(z,"-init")==0 ){
+ i++;
+ }else if( strcmp(z,"-html")==0 ){
+ data.mode = MODE_Html;
+ }else if( strcmp(z,"-list")==0 ){
+ data.mode = MODE_List;
+ }else if( strcmp(z,"-line")==0 ){
+ data.mode = MODE_Line;
+ }else if( strcmp(z,"-column")==0 ){
+ data.mode = MODE_Column;
+ }else if( strcmp(z,"-csv")==0 ){
+ data.mode = MODE_Csv;
+ memcpy(data.separator,",",2);
+ }else if( strcmp(z,"-separator")==0 ){
+ i++;
+ sqlite3_snprintf(sizeof(data.separator), data.separator,
+ "%.*s",(int)sizeof(data.separator)-1,argv[i]);
+ }else if( strcmp(z,"-nullvalue")==0 ){
+ i++;
+ sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue,
+ "%.*s",(int)sizeof(data.nullvalue)-1,argv[i]);
+ }else if( strcmp(z,"-header")==0 ){
+ data.showHeader = 1;
+ }else if( strcmp(z,"-noheader")==0 ){
+ data.showHeader = 0;
+ }else if( strcmp(z,"-echo")==0 ){
+ data.echoOn = 1;
+ }else if( strcmp(z,"-bail")==0 ){
+ bail_on_error = 1;
+ }else if( strcmp(z,"-version")==0 ){
+ printf("%s\n", sqlite3_libversion());
+ return 0;
+ }else if( strcmp(z,"-interactive")==0 ){
+ stdin_is_interactive = 1;
+ }else if( strcmp(z,"-batch")==0 ){
+ stdin_is_interactive = 0;
+ }else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){
+ usage(1);
+ }else{
+ fprintf(stderr,"%s: unknown option: %s\n", Argv0, z);
+ fprintf(stderr,"Use -help for a list of options.\n");
+ return 1;
+ }
+ }
+
+ if( zFirstCmd ){
+ /* Run just the command that follows the database name
+ */
+ if( zFirstCmd[0]=='.' ){
+ do_meta_command(zFirstCmd, &data);
+ exit(0);
+ }else{
+ int rc;
+ open_db(&data);
+ rc = sqlite3_exec(data.db, zFirstCmd, callback, &data, &zErrMsg);
+ if( rc!=0 && zErrMsg!=0 ){
+ fprintf(stderr,"SQL error: %s\n", zErrMsg);
+ exit(1);
+ }
+ }
+ }else{
+ /* Run commands received from standard input
+ */
+ if( stdin_is_interactive ){
+ char *zHome;
+ char *zHistory = 0;
+ int nHistory;
+ printf(
+ "SQLite version %s\n"
+ "Enter \".help\" for instructions\n",
+ sqlite3_libversion()
+ );
+ zHome = find_home_dir();
+ if( zHome && (zHistory = malloc(nHistory = strlen(zHome)+20))!=0 ){
+ sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
+ }
+#if defined(HAVE_READLINE) && HAVE_READLINE==1
+ if( zHistory ) read_history(zHistory);
+#endif
+ rc = process_input(&data, 0);
+ if( zHistory ){
+ stifle_history(100);
+ write_history(zHistory);
+ free(zHistory);
+ }
+ free(zHome);
+ }else{
+ rc = process_input(&data, stdin);
+ }
+ }
+ set_table_name(&data, 0);
+ if( db ){
+ if( sqlite3_close(db)!=SQLITE_OK ){
+ fprintf(stderr,"error closing database: %s\n", sqlite3_errmsg(db));
+ }
+ }
+ return rc;
+}
Added: test-suite/trunk/MultiSource/Applications/sqlite3/speedtest.tcl
URL: http://llvm.org/viewvc/llvm-project/test-suite/trunk/MultiSource/Applications/sqlite3/speedtest.tcl?rev=48828&view=auto
==============================================================================
--- test-suite/trunk/MultiSource/Applications/sqlite3/speedtest.tcl (added)
+++ test-suite/trunk/MultiSource/Applications/sqlite3/speedtest.tcl Wed Mar 26 12:03:57 2008
@@ -0,0 +1,192 @@
+#!/usr/bin/tclsh
+#
+# Run this script using TCLSH to do a speed comparison between
+# various versions of SQLite and PostgreSQL and MySQL
+#
+
+# Run a test
+#
+set cnt 1
+proc runtest {title} {
+ global cnt
+ incr cnt
+}
+
+# Initialize the environment
+#
+expr srand(1)
+set ones {zero one two three four five six seven eight nine
+ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
+ eighteen nineteen}
+set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
+proc number_name {n} {
+ if {$n>=1000} {
+ set txt "[number_name [expr {$n/1000}]] thousand"
+ set n [expr {$n%1000}]
+ } else {
+ set txt {}
+ }
+ if {$n>=100} {
+ append txt " [lindex $::ones [expr {$n/100}]] hundred"
+ set n [expr {$n%100}]
+ }
+ if {$n>=20} {
+ append txt " [lindex $::tens [expr {$n/10}]]"
+ set n [expr {$n%10}]
+ }
+ if {$n>0} {
+ append txt " [lindex $::ones $n]"
+ }
+ set txt [string trim $txt]
+ if {$txt==""} {set txt zero}
+ return $txt
+}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd "CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100));"
+for {set i 1} {$i<=1000} {incr i} {
+ set r [expr {int(rand()*100000)}]
+ puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
+}
+close $fd
+runtest {1000 INSERTs}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+puts $fd "CREATE TABLE t2(a INTEGER, b INTEGER, c VARCHAR(100));"
+for {set i 1} {$i<=25000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ puts $fd "INSERT INTO t2 VALUES($i,$r,'[number_name $r]');"
+}
+puts $fd "COMMIT;"
+close $fd
+runtest {25000 INSERTs in a transaction}
+
+
+
+set fd [open test$cnt.sql w]
+for {set i 0} {$i<100} {incr i} {
+ set lwr [expr {$i*100}]
+ set upr [expr {($i+10)*100}]
+ puts $fd "SELECT count(*), avg(b) FROM t2 WHERE b>=$lwr AND b<$upr;"
+}
+close $fd
+runtest {100 SELECTs without an index}
+
+
+
+set fd [open test$cnt.sql w]
+for {set i 1} {$i<=100} {incr i} {
+ puts $fd "SELECT count(*), avg(b) FROM t2 WHERE c LIKE '%[number_name $i]%';"
+}
+close $fd
+runtest {100 SELECTs on a string comparison}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd {CREATE INDEX i2a ON t2(a);}
+puts $fd {CREATE INDEX i2b ON t2(b);}
+close $fd
+runtest {Creating an index}
+
+
+
+set fd [open test$cnt.sql w]
+for {set i 0} {$i<5000} {incr i} {
+ set lwr [expr {$i*100}]
+ set upr [expr {($i+1)*100}]
+ puts $fd "SELECT count(*), avg(b) FROM t2 WHERE b>=$lwr AND b<$upr;"
+}
+close $fd
+runtest {5000 SELECTs with an index}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+for {set i 0} {$i<1000} {incr i} {
+ set lwr [expr {$i*10}]
+ set upr [expr {($i+1)*10}]
+ puts $fd "UPDATE t1 SET b=b*2 WHERE a>=$lwr AND a<$upr;"
+}
+puts $fd "COMMIT;"
+close $fd
+runtest {1000 UPDATEs without an index}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+for {set i 1} {$i<=25000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ puts $fd "UPDATE t2 SET b=$r WHERE a=$i;"
+}
+puts $fd "COMMIT;"
+close $fd
+runtest {25000 UPDATEs with an index}
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+for {set i 1} {$i<=25000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ puts $fd "UPDATE t2 SET c='[number_name $r]' WHERE a=$i;"
+}
+puts $fd "COMMIT;"
+close $fd
+runtest {25000 text UPDATEs with an index}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+puts $fd "INSERT INTO t1 SELECT * FROM t2;"
+puts $fd "INSERT INTO t2 SELECT * FROM t1;"
+puts $fd "COMMIT;"
+close $fd
+runtest {INSERTs from a SELECT}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd {DELETE FROM t2 WHERE c LIKE '%fifty%';}
+close $fd
+runtest {DELETE without an index}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd {DELETE FROM t2 WHERE a>10 AND a<20000;}
+close $fd
+runtest {DELETE with an index}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd {INSERT INTO t2 SELECT * FROM t1;}
+close $fd
+runtest {A big INSERT after a big DELETE}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd {BEGIN;}
+puts $fd {DELETE FROM t1;}
+for {set i 1} {$i<=3000} {incr i} {
+ set r [expr {int(rand()*100000)}]
+ puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
+}
+puts $fd {COMMIT;}
+close $fd
+runtest {A big DELETE followed by many small INSERTs}
+
+set fd [open test$cnt.sql w]
+puts $fd {DROP TABLE t1;}
+puts $fd {DROP TABLE t2;}
+close $fd
+runtest {DROP TABLE}
More information about the llvm-commits
mailing list