summaryrefslogtreecommitdiff
path: root/test/racc/assets
diff options
context:
space:
mode:
authorHiroshi SHIBATA <hsbt@ruby-lang.org>2019-05-13 21:25:22 +0900
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2019-06-19 18:17:25 +0900
commit1a2546c2be839baa7d0a50dc056d4d6987d26852 (patch)
tree19fef5d8b8d96452a51ab68e8093ea895192ca27 /test/racc/assets
parentcbe06cd3501fdadd0e6e63094da2973484d70b0b (diff)
Backport racc-1.4.15 from upstream.
Diffstat (limited to 'test/racc/assets')
-rw-r--r--test/racc/assets/cadenza.y170
-rw-r--r--test/racc/assets/cast.y926
-rw-r--r--test/racc/assets/chk.y126
-rw-r--r--test/racc/assets/conf.y16
-rw-r--r--test/racc/assets/csspool.y729
-rw-r--r--test/racc/assets/digraph.y29
-rw-r--r--test/racc/assets/echk.y118
-rw-r--r--test/racc/assets/edtf.y583
-rw-r--r--test/racc/assets/err.y60
-rw-r--r--test/racc/assets/error_recovery.y35
-rw-r--r--test/racc/assets/expect.y7
-rw-r--r--test/racc/assets/firstline.y4
-rw-r--r--test/racc/assets/huia.y318
-rw-r--r--test/racc/assets/ichk.y102
-rw-r--r--test/racc/assets/intp.y546
-rw-r--r--test/racc/assets/journey.y47
-rw-r--r--test/racc/assets/liquor.y313
-rw-r--r--test/racc/assets/machete.y423
-rw-r--r--test/racc/assets/macruby.y2197
-rw-r--r--test/racc/assets/mailp.y437
-rw-r--r--test/racc/assets/mediacloth.y599
-rw-r--r--test/racc/assets/mof.y649
-rw-r--r--test/racc/assets/namae.y302
-rw-r--r--test/racc/assets/nasl.y626
-rw-r--r--test/racc/assets/newsyn.y25
-rw-r--r--test/racc/assets/noend.y4
-rw-r--r--test/racc/assets/nokogiri-css.y255
-rw-r--r--test/racc/assets/nonass.y41
-rw-r--r--test/racc/assets/normal.y27
-rw-r--r--test/racc/assets/norule.y4
-rw-r--r--test/racc/assets/nullbug1.y25
-rw-r--r--test/racc/assets/nullbug2.y15
-rw-r--r--test/racc/assets/opal.y1807
-rw-r--r--test/racc/assets/opt.y123
-rw-r--r--test/racc/assets/percent.y35
-rw-r--r--test/racc/assets/php_serialization.y98
-rw-r--r--test/racc/assets/recv.y97
-rw-r--r--test/racc/assets/riml.y665
-rw-r--r--test/racc/assets/rrconf.y14
-rw-r--r--test/racc/assets/ruby18.y1943
-rw-r--r--test/racc/assets/ruby19.y2174
-rw-r--r--test/racc/assets/ruby20.y2350
-rw-r--r--test/racc/assets/ruby21.y2359
-rw-r--r--test/racc/assets/ruby22.y2381
-rw-r--r--test/racc/assets/scan.y72
-rw-r--r--test/racc/assets/syntax.y50
-rw-r--r--test/racc/assets/tp_plus.y622
-rw-r--r--test/racc/assets/twowaysql.y278
-rw-r--r--test/racc/assets/unterm.y5
-rw-r--r--test/racc/assets/useless.y12
-rw-r--r--test/racc/assets/yyerr.y46
51 files changed, 24889 insertions, 0 deletions
diff --git a/test/racc/assets/cadenza.y b/test/racc/assets/cadenza.y
new file mode 100644
index 0000000000..1940ead225
--- /dev/null
+++ b/test/racc/assets/cadenza.y
@@ -0,0 +1,170 @@
+# This grammar is released under an MIT license
+# Author: William Howard (http://github.com/whoward)
+# Source: https://github.com/whoward/cadenza/blob/master/src/cadenza.y
+
+class Cadenza::RaccParser
+
+/* expect this many shift/reduce conflicts */
+expect 37
+
+rule
+ target
+ : document
+ | /* none */ { result = nil }
+ ;
+
+ parameter_list
+ : logical_expression { result = [val[0]] }
+ | parameter_list ',' logical_expression { result = val[0].push(val[2]) }
+ ;
+
+ /* this has a shift/reduce conflict but since Racc will shift in this case it is the correct behavior */
+ primary_expression
+ : IDENTIFIER { result = VariableNode.new(val[0].value) }
+ | IDENTIFIER parameter_list { result = VariableNode.new(val[0].value, val[1]) }
+ | INTEGER { result = ConstantNode.new(val[0].value) }
+ | REAL { result = ConstantNode.new(val[0].value) }
+ | STRING { result = ConstantNode.new(val[0].value) }
+ | '(' filtered_expression ')' { result = val[1] }
+ ;
+
+ multiplicative_expression
+ : primary_expression
+ | multiplicative_expression '*' primary_expression { result = OperationNode.new(val[0], "*", val[2]) }
+ | multiplicative_expression '/' primary_expression { result = OperationNode.new(val[0], "/", val[2]) }
+ ;
+
+ additive_expression
+ : multiplicative_expression
+ | additive_expression '+' multiplicative_expression { result = OperationNode.new(val[0], "+", val[2]) }
+ | additive_expression '-' multiplicative_expression { result = OperationNode.new(val[0], "-", val[2]) }
+ ;
+
+ boolean_expression
+ : additive_expression
+ | boolean_expression OP_EQ additive_expression { result = OperationNode.new(val[0], "==", val[2]) }
+ | boolean_expression OP_NEQ additive_expression { result = OperationNode.new(val[0], "!=", val[2]) }
+ | boolean_expression OP_LEQ additive_expression { result = OperationNode.new(val[0], "<=", val[2]) }
+ | boolean_expression OP_GEQ additive_expression { result = OperationNode.new(val[0], ">=", val[2]) }
+ | boolean_expression '>' additive_expression { result = OperationNode.new(val[0], ">", val[2]) }
+ | boolean_expression '<' additive_expression { result = OperationNode.new(val[0], "<", val[2]) }
+ ;
+
+ inverse_expression
+ : boolean_expression
+ | NOT boolean_expression { result = BooleanInverseNode.new(val[1]) }
+ ;
+
+ logical_expression
+ : inverse_expression
+ | logical_expression AND inverse_expression { result = OperationNode.new(val[0], "and", val[2]) }
+ | logical_expression OR inverse_expression { result = OperationNode.new(val[0], "or", val[2]) }
+ ;
+
+ filter
+ : IDENTIFIER { result = FilterNode.new(val[0].value) }
+ | IDENTIFIER ':' parameter_list { result = FilterNode.new(val[0].value, val[2]) }
+ ;
+
+ filter_list
+ : filter { result = [val[0]] }
+ | filter_list '|' filter { result = val[0].push(val[2]) }
+ ;
+
+ filtered_expression
+ : logical_expression
+ | logical_expression '|' filter_list { result = FilteredValueNode.new(val[0], val[2]) }
+ ;
+
+ inject_statement
+ : VAR_OPEN filtered_expression VAR_CLOSE { result = val[1] }
+ ;
+
+ if_tag
+ : STMT_OPEN IF logical_expression STMT_CLOSE { open_scope!; result = val[2] }
+ | STMT_OPEN UNLESS logical_expression STMT_CLOSE { open_scope!; result = BooleanInverseNode.new(val[2]) }
+ ;
+
+ else_tag
+ : STMT_OPEN ELSE STMT_CLOSE { result = close_scope!; open_scope! }
+ ;
+
+ end_if_tag
+ : STMT_OPEN ENDIF STMT_CLOSE { result = close_scope! }
+ | STMT_OPEN ENDUNLESS STMT_CLOSE { result = close_scope! }
+ ;
+
+ if_block
+ : if_tag end_if_tag { result = IfNode.new(val[0], val[1]) }
+ | if_tag document end_if_tag { result = IfNode.new(val[0], val[2]) }
+ | if_tag else_tag document end_if_tag { result = IfNode.new(val[0], val[1], val[3]) }
+ | if_tag document else_tag end_if_tag { result = IfNode.new(val[0], val[2], val[3]) }
+ | if_tag document else_tag document end_if_tag { result = IfNode.new(val[0], val[2], val[4]) }
+ ;
+
+ for_tag
+ : STMT_OPEN FOR IDENTIFIER IN filtered_expression STMT_CLOSE { open_scope!; result = [val[2].value, val[4]] }
+ ;
+
+ end_for_tag
+ : STMT_OPEN ENDFOR STMT_CLOSE { result = close_scope! }
+ ;
+
+ /* this has a shift/reduce conflict but since Racc will shift in this case it is the correct behavior */
+ for_block
+ : for_tag end_for_tag { result = ForNode.new(VariableNode.new(val[0].first), val[0].last, val[1]) }
+ | for_tag document end_for_tag { result = ForNode.new(VariableNode.new(val[0].first), val[0].last, val[2]) }
+ ;
+
+ block_tag
+ : STMT_OPEN BLOCK IDENTIFIER STMT_CLOSE { result = open_block_scope!(val[2].value) }
+ ;
+
+ end_block_tag
+ : STMT_OPEN ENDBLOCK STMT_CLOSE { result = close_block_scope! }
+ ;
+
+ /* this has a shift/reduce conflict but since Racc will shift in this case it is the correct behavior */
+ block_block
+ : block_tag end_block_tag { result = BlockNode.new(val[0], val[1]) }
+ | block_tag document end_block_tag { result = BlockNode.new(val[0], val[2]) }
+ ;
+
+ generic_block_tag
+ : STMT_OPEN IDENTIFIER STMT_CLOSE { open_scope!; result = [val[1].value, []] }
+ | STMT_OPEN IDENTIFIER parameter_list STMT_CLOSE { open_scope!; result = [val[1].value, val[2]] }
+ ;
+
+ end_generic_block_tag
+ : STMT_OPEN END STMT_CLOSE { result = close_scope! }
+ ;
+
+ generic_block
+ : generic_block_tag document end_generic_block_tag { result = GenericBlockNode.new(val[0].first, val[2], val[0].last) }
+ ;
+
+ extends_statement
+ : STMT_OPEN EXTENDS STRING STMT_CLOSE { result = val[2].value }
+ | STMT_OPEN EXTENDS IDENTIFIER STMT_CLOSE { result = VariableNode.new(val[2].value) }
+ ;
+
+ document_component
+ : TEXT_BLOCK { result = TextNode.new(val[0].value) }
+ | inject_statement
+ | if_block
+ | for_block
+ | generic_block
+ | block_block
+ ;
+
+ document
+ : document_component { push val[0] }
+ | document document_component { push val[1] }
+ | extends_statement { document.extends = val[0] }
+ | document extends_statement { document.extends = val[1] }
+ ;
+
+---- header ----
+# racc_parser.rb : generated by racc
+
+---- inner ----
diff --git a/test/racc/assets/cast.y b/test/racc/assets/cast.y
new file mode 100644
index 0000000000..d180c09e14
--- /dev/null
+++ b/test/racc/assets/cast.y
@@ -0,0 +1,926 @@
+# The MIT License
+#
+# Copyright (c) George Ogata
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+class C::Parser
+# shift/reduce conflict on "if (c) if (c) ; else ; else ;"
+expect 1
+rule
+
+# A.2.4 External definitions
+
+# Returns TranslationUnit
+translation_unit
+ : external_declaration {result = TranslationUnit.new_at(val[0].pos, NodeChain[val[0]])}
+ | translation_unit external_declaration {result = val[0]; result.entities << val[1]}
+
+# Returns Declaration|FunctionDef
+external_declaration
+ : function_definition {result = val[0]}
+ | declaration {result = val[0]}
+
+# Returns FunctionDef
+function_definition
+ : declaration_specifiers declarator declaration_list compound_statement {result = make_function_def(val[0][0], val[0][1], val[1], val[2], val[3])}
+ | declaration_specifiers declarator compound_statement {result = make_function_def(val[0][0], val[0][1], val[1], nil , val[2])}
+
+# Returns [Declaration]
+declaration_list
+ : declaration {result = [val[0]]}
+ | declaration_list declaration {result = val[0] << val[1]}
+
+# A.2.3 Statements
+
+# Returns Statement
+statement
+ : labeled_statement {result = val[0]}
+ | compound_statement {result = val[0]}
+ | expression_statement {result = val[0]}
+ | selection_statement {result = val[0]}
+ | iteration_statement {result = val[0]}
+ | jump_statement {result = val[0]}
+
+# Returns Statement
+labeled_statement
+ : identifier COLON statement {val[2].labels.unshift(PlainLabel.new_at(val[0].pos, val[0].val)); result = val[2]}
+ | CASE constant_expression COLON statement {val[3].labels.unshift(Case .new_at(val[0].pos, val[1] )); result = val[3]}
+ | DEFAULT COLON statement {val[2].labels.unshift(Default .new_at(val[0].pos )); result = val[2]}
+ # type names can also be used as labels
+ | typedef_name COLON statement {val[2].labels.unshift(PlainLabel.new_at(val[0].pos, val[0].name)); result = val[2]}
+
+# Returns Block
+compound_statement
+ : LBRACE block_item_list RBRACE {result = Block.new_at(val[0].pos, val[1])}
+ | LBRACE RBRACE {result = Block.new_at(val[0].pos )}
+
+# Returns NodeChain[Declaration|Statement]
+block_item_list
+ : block_item {result = NodeChain[val[0]]}
+ | block_item_list block_item {result = val[0] << val[1]}
+
+# Returns Declaration|Statement
+block_item
+ : declaration {result = val[0]}
+ | statement {result = val[0]}
+
+# Returns ExpressionStatement
+expression_statement
+ : expression SEMICOLON {result = ExpressionStatement.new_at(val[0].pos, val[0])}
+ | SEMICOLON {result = ExpressionStatement.new_at(val[0].pos )}
+
+# Returns Statement
+selection_statement
+ : IF LPAREN expression RPAREN statement {result = If .new_at(val[0].pos, val[2], val[4] )}
+ | IF LPAREN expression RPAREN statement ELSE statement {result = If .new_at(val[0].pos, val[2], val[4], val[6])}
+ | SWITCH LPAREN expression RPAREN statement {result = Switch.new_at(val[0].pos, val[2], val[4] )}
+
+# Returns Statement
+iteration_statement
+ : WHILE LPAREN expression RPAREN statement {result = While.new_at(val[0].pos, val[2], val[4] )}
+ | DO statement WHILE LPAREN expression RPAREN SEMICOLON {result = While.new_at(val[0].pos, val[4], val[1], :do => true )}
+ | FOR LPAREN expression SEMICOLON expression SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], val[4], val[6], val[8])}
+ | FOR LPAREN expression SEMICOLON expression SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], val[4], nil , val[7])}
+ | FOR LPAREN expression SEMICOLON SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , val[5], val[7])}
+ | FOR LPAREN expression SEMICOLON SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , nil , val[6])}
+ | FOR LPAREN SEMICOLON expression SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, nil , val[3], val[5], val[7])}
+ | FOR LPAREN SEMICOLON expression SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, nil , val[3], nil , val[6])}
+ | FOR LPAREN SEMICOLON SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, nil , nil , val[4], val[6])}
+ | FOR LPAREN SEMICOLON SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, nil , nil , nil , val[5])}
+ | FOR LPAREN declaration expression SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], val[3], val[5], val[7])}
+ | FOR LPAREN declaration expression SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], val[3], nil , val[6])}
+ | FOR LPAREN declaration SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , val[4], val[6])}
+ | FOR LPAREN declaration SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , nil , val[5])}
+
+# Returns Statement
+jump_statement
+ : GOTO identifier SEMICOLON {result = Goto .new_at(val[0].pos, val[1].val)}
+ | CONTINUE SEMICOLON {result = Continue.new_at(val[0].pos )}
+ | BREAK SEMICOLON {result = Break .new_at(val[0].pos )}
+ | RETURN expression SEMICOLON {result = Return .new_at(val[0].pos, val[1] )}
+ | RETURN SEMICOLON {result = Return .new_at(val[0].pos )}
+ # type names can also be used as labels
+ | GOTO typedef_name SEMICOLON {result = Goto .new_at(val[0].pos, val[1].name)}
+
+# A.2.2 Declarations
+
+# Returns Declaration
+declaration
+ : declaration_specifiers init_declarator_list SEMICOLON {result = make_declaration(val[0][0], val[0][1], val[1])}
+ | declaration_specifiers SEMICOLON {result = make_declaration(val[0][0], val[0][1], NodeArray[])}
+
+# Returns {Pos, [Symbol]}
+declaration_specifiers
+ : storage_class_specifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]}
+ | storage_class_specifier {result = [val[0][0], [val[0][1]]]}
+ | type_specifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]}
+ | type_specifier {result = [val[0][0], [val[0][1]]]}
+ | type_qualifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]}
+ | type_qualifier {result = [val[0][0], [val[0][1]]]}
+ | function_specifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]}
+ | function_specifier {result = [val[0][0], [val[0][1]]]}
+
+# Returns NodeArray[Declarator]
+init_declarator_list
+ : init_declarator {result = NodeArray[val[0]]}
+ | init_declarator_list COMMA init_declarator {result = val[0] << val[2]}
+
+# Returns Declarator
+init_declarator
+ : declarator {result = val[0]}
+ | declarator EQ initializer {val[0].init = val[2]; result = val[0]}
+
+# Returns [Pos, Symbol]
+storage_class_specifier
+ : TYPEDEF {result = [val[0].pos, :typedef ]}
+ | EXTERN {result = [val[0].pos, :extern ]}
+ | STATIC {result = [val[0].pos, :static ]}
+ | AUTO {result = [val[0].pos, :auto ]}
+ | REGISTER {result = [val[0].pos, :register]}
+
+# Returns [Pos, Type|Symbol]
+type_specifier
+ : VOID {result = [val[0].pos, :void ]}
+ | CHAR {result = [val[0].pos, :char ]}
+ | SHORT {result = [val[0].pos, :short ]}
+ | INT {result = [val[0].pos, :int ]}
+ | LONG {result = [val[0].pos, :long ]}
+ | FLOAT {result = [val[0].pos, :float ]}
+ | DOUBLE {result = [val[0].pos, :double ]}
+ | SIGNED {result = [val[0].pos, :signed ]}
+ | UNSIGNED {result = [val[0].pos, :unsigned ]}
+ | BOOL {result = [val[0].pos, :_Bool ]}
+ | COMPLEX {result = [val[0].pos, :_Complex ]}
+ | IMAGINARY {result = [val[0].pos, :_Imaginary]}
+ | struct_or_union_specifier {result = [val[0].pos, val[0] ]}
+ | enum_specifier {result = [val[0].pos, val[0] ]}
+ | typedef_name {result = [val[0].pos, val[0] ]}
+
+# Returns Struct|Union
+struct_or_union_specifier
+ : struct_or_union identifier LBRACE struct_declaration_list RBRACE {result = val[0][1].new_at(val[0][0], val[1].val, val[3])}
+ | struct_or_union LBRACE struct_declaration_list RBRACE {result = val[0][1].new_at(val[0][0], nil , val[2])}
+ | struct_or_union identifier {result = val[0][1].new_at(val[0][0], val[1].val, nil )}
+ # type names can also be used as struct identifiers
+ | struct_or_union typedef_name LBRACE struct_declaration_list RBRACE {result = val[0][1].new_at(val[0][0], val[1].name, val[3])}
+ | struct_or_union typedef_name {result = val[0][1].new_at(val[0][0], val[1].name, nil )}
+
+# Returns [Pos, Class]
+struct_or_union
+ : STRUCT {result = [val[0].pos, Struct]}
+ | UNION {result = [val[0].pos, Union ]}
+
+# Returns NodeArray[Declaration]
+struct_declaration_list
+ : struct_declaration {result = NodeArray[val[0]]}
+ | struct_declaration_list struct_declaration {val[0] << val[1]; result = val[0]}
+
+# Returns Declaration
+struct_declaration
+ : specifier_qualifier_list struct_declarator_list SEMICOLON {result = make_declaration(val[0][0], val[0][1], val[1])}
+
+# Returns {Pos, [Symbol]}
+specifier_qualifier_list
+ : type_specifier specifier_qualifier_list {val[1][1] << val[0][1]; result = val[1]}
+ | type_specifier {result = [val[0][0], [val[0][1]]]}
+ | type_qualifier specifier_qualifier_list {val[1][1] << val[0][1]; result = val[1]}
+ | type_qualifier {result = [val[0][0], [val[0][1]]]}
+
+# Returns NodeArray[Declarator]
+struct_declarator_list
+ : struct_declarator {result = NodeArray[val[0]]}
+ | struct_declarator_list COMMA struct_declarator {result = val[0] << val[2]}
+
+# Returns Declarator
+struct_declarator
+ : declarator {result = val[0]}
+ | declarator COLON constant_expression {result = val[0]; val[0].num_bits = val[2]}
+ | COLON constant_expression {result = Declarator.new_at(val[0].pos, :num_bits => val[1])}
+
+# Returns Enum
+enum_specifier
+ : ENUM identifier LBRACE enumerator_list RBRACE {result = Enum.new_at(val[0].pos, val[1].val, val[3])}
+ | ENUM LBRACE enumerator_list RBRACE {result = Enum.new_at(val[0].pos, nil , val[2])}
+ | ENUM identifier LBRACE enumerator_list COMMA RBRACE {result = Enum.new_at(val[0].pos, val[1].val, val[3])}
+ | ENUM LBRACE enumerator_list COMMA RBRACE {result = Enum.new_at(val[0].pos, nil , val[2])}
+ | ENUM identifier {result = Enum.new_at(val[0].pos, val[1].val, nil )}
+ # type names can also be used as enum names
+ | ENUM typedef_name LBRACE enumerator_list RBRACE {result = Enum.new_at(val[0].pos, val[1].name, val[3])}
+ | ENUM typedef_name LBRACE enumerator_list COMMA RBRACE {result = Enum.new_at(val[0].pos, val[1].name, val[3])}
+ | ENUM typedef_name {result = Enum.new_at(val[0].pos, val[1].name, nil )}
+
+# Returns NodeArray[Enumerator]
+enumerator_list
+ : enumerator {result = NodeArray[val[0]]}
+ | enumerator_list COMMA enumerator {result = val[0] << val[2]}
+
+# Returns Enumerator
+enumerator
+ : enumeration_constant {result = Enumerator.new_at(val[0].pos, val[0].val, nil )}
+ | enumeration_constant EQ constant_expression {result = Enumerator.new_at(val[0].pos, val[0].val, val[2])}
+
+# Returns [Pos, Symbol]
+type_qualifier
+ : CONST {result = [val[0].pos, :const ]}
+ | RESTRICT {result = [val[0].pos, :restrict]}
+ | VOLATILE {result = [val[0].pos, :volatile]}
+
+# Returns [Pos, Symbol]
+function_specifier
+ : INLINE {result = [val[0].pos, :inline]}
+
+# Returns Declarator
+declarator
+ : pointer direct_declarator {result = add_decl_type(val[1], val[0])}
+ | direct_declarator {result = val[0]}
+
+# Returns Declarator
+direct_declarator
+ : identifier {result = Declarator.new_at(val[0].pos, nil, val[0].val)}
+ | LPAREN declarator RPAREN {result = val[1]}
+ | direct_declarator LBRACKET type_qualifier_list assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
+ | direct_declarator LBRACKET type_qualifier_list RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
+ | direct_declarator LBRACKET assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos, nil, val[2]))}
+ | direct_declarator LBRACKET RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))}
+ | direct_declarator LBRACKET STATIC type_qualifier_list assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
+ | direct_declarator LBRACKET STATIC assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
+ | direct_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
+ | direct_declarator LBRACKET type_qualifier_list MUL RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
+ | direct_declarator LBRACKET MUL RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
+ | direct_declarator LPAREN parameter_type_list RPAREN {result = add_decl_type(val[0], Function.new_at(val[0].pos, nil, param_list(*val[2]), :var_args => val[2][1]))}
+ | direct_declarator LPAREN identifier_list RPAREN {result = add_decl_type(val[0], Function.new_at(val[0].pos, nil, val[2]))}
+ | direct_declarator LPAREN RPAREN {result = add_decl_type(val[0], Function.new_at(val[0].pos ))}
+
+# Returns Pointer
+pointer
+ : MUL type_qualifier_list {result = add_type_quals(Pointer.new_at(val[0].pos), val[1][1]) }
+ | MUL {result = Pointer.new_at(val[0].pos) }
+ | MUL type_qualifier_list pointer {p = add_type_quals(Pointer.new_at(val[0].pos), val[1][1]); val[2].direct_type = p; result = val[2]}
+ | MUL pointer {p = Pointer.new_at(val[0].pos) ; val[1].direct_type = p; result = val[1]}
+
+# Returns {Pos, [Symbol]}
+type_qualifier_list
+ : type_qualifier {result = [val[0][0], [val[0][1]]]}
+ | type_qualifier_list type_qualifier {val[0][1] << val[1][1]; result = val[0]}
+
+# Returns [NodeArray[Parameter], var_args?]
+parameter_type_list
+ : parameter_list {result = [val[0], false]}
+ | parameter_list COMMA ELLIPSIS {result = [val[0], true ]}
+
+# Returns NodeArray[Parameter]
+parameter_list
+ : parameter_declaration {result = NodeArray[val[0]]}
+ | parameter_list COMMA parameter_declaration {result = val[0] << val[2]}
+
+# Returns Parameter
+parameter_declaration
+ : declaration_specifiers declarator {ind_type = val[1].indirect_type and ind_type.detach
+ result = make_parameter(val[0][0], val[0][1], ind_type, val[1].name)}
+ | declaration_specifiers abstract_declarator {result = make_parameter(val[0][0], val[0][1], val[1] , nil )}
+ | declaration_specifiers {result = make_parameter(val[0][0], val[0][1], nil , nil )}
+
+# Returns NodeArray[Parameter]
+identifier_list
+ : identifier {result = NodeArray[Parameter.new_at(val[0].pos, nil, val[0].val)]}
+ | identifier_list COMMA identifier {result = val[0] << Parameter.new_at(val[2].pos, nil, val[2].val)}
+
+# Returns Type
+type_name
+ : specifier_qualifier_list abstract_declarator {val[1].direct_type = make_direct_type(val[0][0], val[0][1]); result = val[1]}
+ | specifier_qualifier_list {result = make_direct_type(val[0][0], val[0][1]) }
+
+# Returns Type
+abstract_declarator
+ : pointer {result = val[0]}
+ | pointer direct_abstract_declarator {val[1].direct_type = val[0]; result = val[1]}
+ | direct_abstract_declarator {result = val[0]}
+
+# Returns Type
+direct_abstract_declarator
+ : LPAREN abstract_declarator RPAREN {result = val[1]}
+ | direct_abstract_declarator LBRACKET assignment_expression RBRACKET {val[0].direct_type = Array.new_at(val[0].pos, nil, val[2]); result = val[0]}
+ | direct_abstract_declarator LBRACKET RBRACKET {val[0].direct_type = Array.new_at(val[0].pos, nil, nil ); result = val[0]}
+ | LBRACKET assignment_expression RBRACKET {result = Array.new_at(val[0].pos, nil, val[1])}
+ | LBRACKET RBRACKET {result = Array.new_at(val[0].pos )}
+ | direct_abstract_declarator LBRACKET MUL RBRACKET {val[0].direct_type = Array.new_at(val[0].pos); result = val[0]} # TODO
+ | LBRACKET MUL RBRACKET {result = Array.new_at(val[0].pos)} # TODO
+ | direct_abstract_declarator LPAREN parameter_type_list RPAREN {val[0].direct_type = Function.new_at(val[0].pos, nil, param_list(*val[2]), val[2][1]); result = val[0]}
+ | direct_abstract_declarator LPAREN RPAREN {val[0].direct_type = Function.new_at(val[0].pos ); result = val[0]}
+ | LPAREN parameter_type_list RPAREN {result = Function.new_at(val[0].pos, nil, param_list(*val[1]), val[1][1])}
+ | LPAREN RPAREN {result = Function.new_at(val[0].pos )}
+
+# Returns CustomType
+typedef_name
+ #: identifier -- insufficient since we must distinguish between type
+ # names and var names (otherwise we have a conflict)
+ : TYPENAME {result = CustomType.new_at(val[0].pos, val[0].val)}
+
+# Returns Expression
+initializer
+ : assignment_expression {result = val[0]}
+ | LBRACE initializer_list RBRACE {result = CompoundLiteral.new_at(val[0].pos, nil, val[1])}
+ | LBRACE initializer_list COMMA RBRACE {result = CompoundLiteral.new_at(val[0].pos, nil, val[1])}
+
+# Returns NodeArray[MemberInit]
+initializer_list
+ : designation initializer {result = NodeArray[MemberInit.new_at(val[0][0] , val[0][1], val[1])]}
+ | initializer {result = NodeArray[MemberInit.new_at(val[0].pos, nil , val[0])]}
+ | initializer_list COMMA designation initializer {result = val[0] << MemberInit.new_at(val[2][0] , val[2][1], val[3])}
+ | initializer_list COMMA initializer {result = val[0] << MemberInit.new_at(val[2].pos, nil , val[2])}
+
+# Returns {Pos, NodeArray[Expression|Token]}
+designation
+ : designator_list EQ {result = val[0]}
+
+# Returns {Pos, NodeArray[Expression|Token]}
+designator_list
+ : designator {result = val[0]; val[0][1] = NodeArray[val[0][1]]}
+ | designator_list designator {result = val[0]; val[0][1] << val[1][1]}
+
+# Returns {Pos, Expression|Member}
+designator
+ : LBRACKET constant_expression RBRACKET {result = [val[1].pos, val[1] ]}
+ | DOT identifier {result = [val[1].pos, Member.new_at(val[1].pos, val[1].val)]}
+
+# A.2.1 Expressions
+
+# Returns Expression
+primary_expression
+ : identifier {result = Variable.new_at(val[0].pos, val[0].val)}
+ | constant {result = val[0]}
+ | string_literal {result = val[0]}
+ # GCC EXTENSION: allow a compound statement in parentheses as an expression
+ | LPAREN expression RPAREN {result = val[1]}
+ | LPAREN compound_statement RPAREN {block_expressions_enabled? or parse_error val[0].pos, "compound statement found where expression expected"
+ result = BlockExpression.new(val[1]); result.pos = val[0].pos}
+
+# Returns Expression
+postfix_expression
+ : primary_expression {result = val[0]}
+ | postfix_expression LBRACKET expression RBRACKET {result = Index .new_at(val[0].pos, val[0], val[2])}
+ | postfix_expression LPAREN argument_expression_list RPAREN {result = Call .new_at(val[0].pos, val[0], val[2] )}
+ | postfix_expression LPAREN RPAREN {result = Call .new_at(val[0].pos, val[0], NodeArray[])}
+ | postfix_expression DOT identifier {result = Dot .new_at(val[0].pos, val[0], Member.new(val[2].val))}
+ | postfix_expression ARROW identifier {result = Arrow .new_at(val[0].pos, val[0], Member.new(val[2].val))}
+ | postfix_expression INC {result = PostInc .new_at(val[0].pos, val[0] )}
+ | postfix_expression DEC {result = PostDec .new_at(val[0].pos, val[0] )}
+ | LPAREN type_name RPAREN LBRACE initializer_list RBRACE {result = CompoundLiteral.new_at(val[0].pos, val[1], val[4])}
+ | LPAREN type_name RPAREN LBRACE initializer_list COMMA RBRACE {result = CompoundLiteral.new_at(val[0].pos, val[1], val[4])}
+
+# Returns [Expression|Type]
+argument_expression_list
+ : argument_expression {result = NodeArray[val[0]]}
+ | argument_expression_list COMMA argument_expression {result = val[0] << val[2]}
+
+# Returns Expression|Type -- EXTENSION: allow type names here too, to support some standard library macros (e.g., va_arg [7.15.1.1])
+argument_expression
+ : assignment_expression {result = val[0]}
+ | type_name {result = val[0]}
+
+# Returns Expression
+unary_expression
+ : postfix_expression {result = val[0]}
+ | INC unary_expression {result = PreInc.new_at(val[0].pos, val[1])}
+ | DEC unary_expression {result = PreDec.new_at(val[0].pos, val[1])}
+ | unary_operator cast_expression {result = val[0][0].new_at(val[0][1], val[1])}
+ | SIZEOF unary_expression {result = Sizeof.new_at(val[0].pos, val[1])}
+ | SIZEOF LPAREN type_name RPAREN {result = Sizeof.new_at(val[0].pos, val[2])}
+
+# Returns [Class, Pos]
+unary_operator
+ : AND {result = [Address , val[0].pos]}
+ | MUL {result = [Dereference, val[0].pos]}
+ | ADD {result = [Positive , val[0].pos]}
+ | SUB {result = [Negative , val[0].pos]}
+ | NOT {result = [BitNot , val[0].pos]}
+ | BANG {result = [Not , val[0].pos]}
+
+# Returns Expression
+cast_expression
+ : unary_expression {result = val[0]}
+ | LPAREN type_name RPAREN cast_expression {result = Cast.new_at(val[0].pos, val[1], val[3])}
+
+# Returns Expression
+multiplicative_expression
+ : cast_expression {result = val[0]}
+ | multiplicative_expression MUL cast_expression {result = Multiply.new_at(val[0].pos, val[0], val[2])}
+ | multiplicative_expression DIV cast_expression {result = Divide .new_at(val[0].pos, val[0], val[2])}
+ | multiplicative_expression MOD cast_expression {result = Mod .new_at(val[0].pos, val[0], val[2])}
+
+# Returns Expression
+additive_expression
+ : multiplicative_expression {result = val[0]}
+ | additive_expression ADD multiplicative_expression {result = Add .new_at(val[0].pos, val[0], val[2])}
+ | additive_expression SUB multiplicative_expression {result = Subtract.new_at(val[0].pos, val[0], val[2])}
+
+# Returns Expression
+shift_expression
+ : additive_expression {result = val[0]}
+ | shift_expression LSHIFT additive_expression {result = ShiftLeft .new_at(val[0].pos, val[0], val[2])}
+ | shift_expression RSHIFT additive_expression {result = ShiftRight.new_at(val[0].pos, val[0], val[2])}
+
+# Returns Expression
+relational_expression
+ : shift_expression {result = val[0]}
+ | relational_expression LT shift_expression {result = Less.new_at(val[0].pos, val[0], val[2])}
+ | relational_expression GT shift_expression {result = More.new_at(val[0].pos, val[0], val[2])}
+ | relational_expression LEQ shift_expression {result = LessOrEqual.new_at(val[0].pos, val[0], val[2])}
+ | relational_expression GEQ shift_expression {result = MoreOrEqual.new_at(val[0].pos, val[0], val[2])}
+
+# Returns Expression
+equality_expression
+ : relational_expression {result = val[0]}
+ | equality_expression EQEQ relational_expression {result = Equal .new_at(val[0].pos, val[0], val[2])}
+ | equality_expression NEQ relational_expression {result = NotEqual.new_at(val[0].pos, val[0], val[2])}
+
+# Returns Expression
+and_expression
+ : equality_expression {result = val[0]}
+ | and_expression AND equality_expression {result = BitAnd.new_at(val[0].pos, val[0], val[2])}
+
+# Returns Expression
+exclusive_or_expression
+ : and_expression {result = val[0]}
+ | exclusive_or_expression XOR and_expression {result = BitXor.new_at(val[0].pos, val[0], val[2])}
+
+# Returns Expression
+inclusive_or_expression
+ : exclusive_or_expression {result = val[0]}
+ | inclusive_or_expression OR exclusive_or_expression {result = BitOr.new_at(val[0].pos, val[0], val[2])}
+
+# Returns Expression
+logical_and_expression
+ : inclusive_or_expression {result = val[0]}
+ | logical_and_expression ANDAND inclusive_or_expression {result = And.new_at(val[0].pos, val[0], val[2])}
+
+# Returns Expression
+logical_or_expression
+ : logical_and_expression {result = val[0]}
+ | logical_or_expression OROR logical_and_expression {result = Or.new_at(val[0].pos, val[0], val[2])}
+
+# Returns Expression
+conditional_expression
+ : logical_or_expression {result = val[0]}
+ | logical_or_expression QUESTION expression COLON conditional_expression {result = Conditional.new_at(val[0].pos, val[0], val[2], val[4])}
+
+# Returns Expression
+assignment_expression
+ : conditional_expression {result = val[0]}
+ | unary_expression assignment_operator assignment_expression {result = val[1].new_at(val[0].pos, val[0], val[2])}
+
+# Returns Class
+assignment_operator
+ : EQ {result = Assign}
+ | MULEQ {result = MultiplyAssign}
+ | DIVEQ {result = DivideAssign}
+ | MODEQ {result = ModAssign}
+ | ADDEQ {result = AddAssign}
+ | SUBEQ {result = SubtractAssign}
+ | LSHIFTEQ {result = ShiftLeftAssign}
+ | RSHIFTEQ {result = ShiftRightAssign}
+ | ANDEQ {result = BitAndAssign}
+ | XOREQ {result = BitXorAssign}
+ | OREQ {result = BitOrAssign}
+
+# Returns Expression
+expression
+ : assignment_expression {result = val[0]}
+ | expression COMMA assignment_expression {
+ if val[0].is_a? Comma
+ if val[2].is_a? Comma
+ val[0].exprs.push(*val[2].exprs)
+ else
+ val[0].exprs << val[2]
+ end
+ result = val[0]
+ else
+ if val[2].is_a? Comma
+ val[2].exprs.unshift(val[0])
+ val[2].pos = val[0].pos
+ result = val[2]
+ else
+ result = Comma.new_at(val[0].pos, NodeArray[val[0], val[2]])
+ end
+ end
+ }
+
+# Returns Expression
+constant_expression
+ : conditional_expression {result = val[0]}
+
+# A.1.1 -- Lexical elements
+#
+# token
+# : keyword (raw string)
+# | identifier expanded below
+# | constant expanded below
+# | string_literal expanded below
+# | punctuator (raw string)
+#
+# preprocessing-token (skip)
+
+# Returns Token
+identifier
+ : ID {result = val[0]}
+
+# Returns Literal
+constant
+ : ICON {result = val[0].val; result.pos = val[0].pos}
+ | FCON {result = val[0].val; result.pos = val[0].pos}
+ #| enumeration_constant -- these are parsed as identifiers at all
+ # places the `constant' nonterminal appears
+ | CCON {result = val[0].val; result.pos = val[0].pos}
+
+# Returns Token
+enumeration_constant
+ : ID {result = val[0]}
+
+# Returns StringLiteral
+# Also handles string literal concatenation (6.4.5.4)
+string_literal
+ : string_literal SCON {val[0].val << val[1].val.val; result = val[0]}
+ | SCON { result = val[0].val; result.pos = val[0].pos }
+
+---- inner
+ # A.1.9 -- Preprocessing numbers -- skip
+ # A.1.8 -- Header names -- skip
+
+ # A.1.7 -- Puncuators -- we don't bother with {##,#,%:,%:%:} since
+ # we don't do preprocessing
+ @@punctuators = %r'\+\+|-[->]|&&|\|\||\.\.\.|(?:<<|>>|[<>=!*/%+\-&^|])=?|[\[\](){}.~?:;,]'
+ @@digraphs = %r'<[:%]|[:%]>'
+
+ # A.1.6 -- String Literals -- simple for us because we don't decode
+ # the string (and indeed accept some illegal strings)
+ @@string_literal = %r'L?"(?:[^\\]|\\.)*?"'m
+
+ # A.1.5 -- Constants
+ @@decimal_floating_constant = %r'(?:(?:\d*\.\d+|\d+\.)(?:e[-+]?\d+)?|\d+e[-+]?\d+)[fl]?'i
+ @@hexadecimal_floating_constant = %r'0x(?:(?:[0-9a-f]*\.[0-9a-f]+|[0-9a-f]+\.)|[0-9a-f]+)p[-+]?\d+[fl]?'i
+
+ @@integer_constant = %r'(?:[1-9][0-9]*|0x[0-9a-f]+|0[0-7]*)(?:ul?l?|ll?u?)?'i
+ @@floating_constant = %r'#{@@decimal_floating_constant}|#{@@hexadecimal_floating_constant}'
+ @@enumeration_constant = %r'[a-zA-Z_\\][a-zA-Z_\\0-9]*'
+ @@character_constant = %r"L?'(?:[^\\]|\\.)+?'"
+ # (note that as with string-literals, we accept some illegal
+ # character-constants)
+
+ # A.1.4 -- Universal character names -- skip
+
+ # A.1.3 -- Identifiers -- skip, since an identifier is lexically
+ # identical to an enumeration constant
+
+ # A.1.2 Keywords
+ keywords = %w'auto break case char const continue default do
+double else enum extern float for goto if inline int long register
+restrict return short signed sizeof static struct switch typedef union
+ unsigned void volatile while _Bool _Complex _Imaginary'
+ @@keywords = %r"#{keywords.join('|')}"
+
+ def initialize
+ @type_names = ::Set.new
+
+ @warning_proc = lambda{}
+ @pos = C::Node::Pos.new(nil, 1, 0)
+ end
+ def initialize_copy(x)
+ @pos = x.pos.dup
+ @type_names = x.type_names.dup
+ end
+ attr_accessor :pos, :type_names
+
+ def parse(str)
+ if str.respond_to? :read
+ str = str.read
+ end
+ @str = str
+ begin
+ prepare_lexer(str)
+ return do_parse
+ rescue ParseError => e
+ e.set_backtrace(caller)
+ raise
+ end
+ end
+
+ #
+ # Error handler, as used by racc.
+ #
+ def on_error(error_token_id, error_value, value_stack)
+ if error_value == '$'
+ parse_error @pos, "unexpected EOF"
+ else
+ parse_error(error_value.pos,
+ "parse error on #{token_to_str(error_token_id)} (#{error_value.val})")
+ end
+ end
+
+ def self.feature(name)
+ attr_writer "#{name}_enabled"
+ class_eval <<-EOS
+ def enable_#{name}
+ @#{name}_enabled = true
+ end
+ def #{name}_enabled?
+ @#{name}_enabled
+ end
+ EOS
+ end
+ private_class_method :feature
+
+ #
+ # Allow blocks in parentheses as expressions, as per the gcc
+ # extension. [http://rubyurl.com/iB7]
+ #
+ feature :block_expressions
+
+ private # ---------------------------------------------------------
+
+ class Token
+ attr_accessor :pos, :val
+ def initialize(pos, val)
+ @pos = pos
+ @val = val
+ end
+ end
+ def eat(str)
+ lines = str.split(/\r\n|[\r\n]/, -1)
+ if lines.length == 1
+ @pos.col_num += lines[0].length
+ else
+ @pos.line_num += lines.length - 1
+ @pos.col_num = lines[-1].length
+ end
+ end
+
+ #
+ # Make a Declaration from the given specs and declarators.
+ #
+ def make_declaration(pos, specs, declarators)
+ specs.all?{|x| x.is_a?(Symbol) || x.is_a?(Type)} or raise specs.map{|x| x.class}.inspect
+ decl = Declaration.new_at(pos, nil, declarators)
+
+ # set storage class
+ storage_classes = specs.find_all do |x|
+ [:typedef, :extern, :static, :auto, :register].include? x
+ end
+ # 6.7.1p2: at most, one storage-class specifier may be given in
+ # the declaration specifiers in a declaration
+ storage_classes.length <= 1 or
+ begin
+ if declarators.length == 0
+ for_name = ''
+ else
+ for_name = "for `#{declarators[0].name}'"
+ end
+ parse_error pos, "multiple or duplicate storage classes given #{for_name}'"
+ end
+ decl.storage = storage_classes[0]
+
+ # set type (specifiers, qualifiers)
+ decl.type = make_direct_type(pos, specs)
+
+ # set function specifiers
+ decl.inline = specs.include?(:inline)
+
+ # look for new type names
+ if decl.typedef?
+ decl.declarators.each do |d|
+ if d.name
+ @type_names << d.name
+ end
+ end
+ end
+
+ return decl
+ end
+
+ def make_function_def(pos, specs, func_declarator, decl_list, defn)
+ add_decl_type(func_declarator, make_direct_type(pos, specs))
+
+ # get types from decl_list if necessary
+ function = func_declarator.indirect_type
+ function.is_a? Function or
+ parse_error pos, "non function type for function `#{func_declarator.name}'"
+ params = function.params
+ if decl_list
+ params.all?{|p| p.type.nil?} or
+ parse_error pos, "both prototype and declaration list given for `#{func_declarator.name}'"
+ decl_list.each do |declaration|
+ declaration.declarators.each do |declarator|
+ param = params.find{|p| p.name == declarator.name} or
+ parse_error pos, "no parameter named #{declarator.name}"
+ if declarator.indirect_type
+ param.type = declarator.indirect_type
+ param.type.direct_type = declaration.type.dup
+ else
+ param.type = declaration.type.dup
+ end
+ end
+ end
+ params.all?{|p| p.type} or
+ begin
+ s = params.find_all{|p| p.type.nil?}.map{|p| "`#{p.name}'"}.join(' and ')
+ parse_error pos, "types missing for parameters #{s}"
+ end
+ end
+
+ fd = FunctionDef.new_at(pos,
+ function.detach,
+ func_declarator.name,
+ defn,
+ :no_prototype => !decl_list.nil?)
+
+ # set storage class
+ # 6.9.1p4: only extern or static allowed
+ specs.each do |s|
+ [:typedef, :auto, :register].include?(s) and
+ "`#{s}' illegal for function"
+ end
+ storage_classes = specs.find_all do |s|
+ s == :extern || s == :static
+ end
+ # 6.7.1p2: at most, one storage-class specifier may be given in
+ # the declaration specifiers in a declaration
+ storage_classes.length <= 1 or
+ "multiple or duplicate storage classes given for `#{func_declarator.name}'"
+ fd.storage = storage_classes[0] if storage_classes[0]
+
+ # set function specifiers
+ # 6.7.4p5 'inline' can be repeated
+ fd.inline = specs.include?(:inline)
+
+ return fd
+ end
+
+ #
+ # Make a direct type from the list of type specifiers and type
+ # qualifiers.
+ #
+ def make_direct_type(pos, specs)
+ specs_order = [:signed, :unsigned, :short, :long, :double, :void,
+ :char, :int, :float, :_Bool, :_Complex, :_Imaginary]
+
+ type_specs = specs.find_all do |x|
+ specs_order.include?(x) || !x.is_a?(Symbol)
+ end
+ type_specs.sort! do |a, b|
+ (specs_order.index(a)||100) <=> (specs_order.index(b)||100)
+ end
+
+ # set type specifiers
+ # 6.7.2p2: the specifier list should be one of these
+ type =
+ case type_specs
+ when [:void]
+ Void.new
+ when [:char]
+ Char.new
+ when [:signed, :char]
+ Char.new :signed => true
+ when [:unsigned, :char]
+ Char.new :signed => false
+ when [:short], [:signed, :short], [:short, :int],
+ [:signed, :short, :int]
+ Int.new :longness => -1
+ when [:unsigned, :short], [:unsigned, :short, :int]
+ Int.new :unsigned => true, :longness => -1
+ when [:int], [:signed], [:signed, :int]
+ Int.new
+ when [:unsigned], [:unsigned, :int]
+ Int.new :unsigned => true
+ when [:long], [:signed, :long], [:long, :int],
+ [:signed, :long, :int]
+ Int.new :longness => 1
+ when [:unsigned, :long], [:unsigned, :long, :int]
+ Int.new :longness => 1, :unsigned => true
+ when [:long, :long], [:signed, :long, :long],
+ [:long, :long, :int], [:signed, :long, :long, :int]
+ Int.new :longness => 2
+ when [:unsigned, :long, :long], [:unsigned, :long, :long, :int]
+ Int.new :longness => 2, :unsigned => true
+ when [:float]
+ Float.new
+ when [:double]
+ Float.new :longness => 1
+ when [:long, :double]
+ Float.new :longness => 2
+ when [:_Bool]
+ Bool.new
+ when [:float, :_Complex]
+ Complex.new
+ when [:double, :_Complex]
+ Complex.new :longness => 1
+ when [:long, :double, :_Complex]
+ Complex.new :longness => 2
+ when [:float, :_Imaginary]
+ Imaginary.new
+ when [:double, :_Imaginary]
+ Imaginary.new :longness => 1
+ when [:long, :double, :_Imaginary]
+ Imaginary.new :longness => 2
+ else
+ if type_specs.length == 1 &&
+ [CustomType, Struct, Union, Enum].any?{|c| type_specs[0].is_a? c}
+ type_specs[0]
+ else
+ if type_specs == []
+ parse_error pos, "no type specifiers given"
+ else
+ parse_error pos, "invalid type specifier combination: #{type_specs.join(' ')}"
+ end
+ end
+ end
+ type.pos ||= pos
+
+ # set type qualifiers
+ # 6.7.3p4: type qualifiers can be repeated
+ type.const = specs.any?{|x| x.equal? :const }
+ type.restrict = specs.any?{|x| x.equal? :restrict}
+ type.volatile = specs.any?{|x| x.equal? :volatile}
+
+ return type
+ end
+
+ def make_parameter(pos, specs, indirect_type, name)
+ type = indirect_type
+ if type
+ type.direct_type = make_direct_type(pos, specs)
+ else
+ type = make_direct_type(pos, specs)
+ end
+ [:typedef, :extern, :static, :auto, :inline].each do |sym|
+ specs.include? sym and
+ parse_error pos, "parameter `#{declarator.name}' declared `#{sym}'"
+ end
+ return Parameter.new_at(pos, type, name,
+ :register => specs.include?(:register))
+ end
+
+ def add_type_quals(type, quals)
+ type.const = quals.include?(:const )
+ type.restrict = quals.include?(:restrict)
+ type.volatile = quals.include?(:volatile)
+ return type
+ end
+
+ #
+ # Add te given type as the "most direct" type to the given
+ # declarator. Return the declarator.
+ #
+ def add_decl_type(declarator, type)
+ if declarator.indirect_type
+ declarator.indirect_type.direct_type = type
+ else
+ declarator.indirect_type = type
+ end
+ return declarator
+ end
+
+ def param_list(params, var_args)
+ if params.length == 1 &&
+ params[0].type.is_a?(Void) &&
+ params[0].name.nil?
+ return NodeArray[]
+ elsif params.empty?
+ return nil
+ else
+ return params
+ end
+ end
+
+ def parse_error(pos, str)
+ raise ParseError, "#{pos}: #{str}"
+ end
+
+---- header
+
+require 'set'
+
+# Error classes
+module C
+ class ParseError < StandardError; end
+end
+
+# Local variables:
+# mode: ruby
+# end:
diff --git a/test/racc/assets/chk.y b/test/racc/assets/chk.y
new file mode 100644
index 0000000000..7e0ee20f1e
--- /dev/null
+++ b/test/racc/assets/chk.y
@@ -0,0 +1,126 @@
+#
+# racc tester
+#
+
+class Calcp
+
+ prechigh
+ left '*' '/'
+ left '+' '-'
+ preclow
+
+ convert
+ NUMBER 'Number'
+ end
+
+rule
+
+ target : exp | /* none */ { result = 0 } ;
+
+ exp : exp '+' exp { result += val[2]; @plus = 'plus' }
+ | exp '-' exp { result -= val[2]; @str = "string test" }
+ | exp '*' exp { result *= val[2] }
+ | exp '/' exp { result /= val[2] }
+ | '(' { $emb = true } exp ')'
+ {
+ raise 'must not happen' unless $emb
+ result = val[2]
+ }
+ | '-' NUMBER { result = -val[1] }
+ | NUMBER
+ ;
+
+end
+
+----header
+
+class Number; end
+
+----inner
+
+ def parse( src )
+ $emb = false
+ @plus = nil
+ @str = nil
+ @src = src
+ result = do_parse
+ if @plus
+ raise 'string parse failed' unless @plus == 'plus'
+ end
+ if @str
+ raise 'string parse failed' unless @str == 'string test'
+ end
+ result
+ end
+
+ def next_token
+ @src.shift
+ end
+
+ def initialize
+ @yydebug = true
+ end
+
+----footer
+
+$parser = Calcp.new
+$test_number = 1
+
+def chk( src, ans )
+ result = $parser.parse(src)
+ raise "test #{$test_number} fail" unless result == ans
+ $test_number += 1
+end
+
+chk(
+ [ [Number, 9],
+ [false, false],
+ [false, false] ], 9
+)
+
+chk(
+ [ [Number, 5],
+ ['*', nil],
+ [Number, 1],
+ ['-', nil],
+ [Number, 1],
+ ['*', nil],
+ [Number, 8],
+ [false, false],
+ [false, false] ], -3
+)
+
+chk(
+ [ [Number, 5],
+ ['+', nil],
+ [Number, 2],
+ ['-', nil],
+ [Number, 5],
+ ['+', nil],
+ [Number, 2],
+ ['-', nil],
+ [Number, 5],
+ [false, false],
+ [false, false] ], -1
+)
+
+chk(
+ [ ['-', nil],
+ [Number, 4],
+ [false, false],
+ [false, false] ], -4
+)
+
+chk(
+ [ [Number, 7],
+ ['*', nil],
+ ['(', nil],
+ [Number, 4],
+ ['+', nil],
+ [Number, 3],
+ [')', nil],
+ ['-', nil],
+ [Number, 9],
+ [false, false],
+ [false, false] ], 40
+)
diff --git a/test/racc/assets/conf.y b/test/racc/assets/conf.y
new file mode 100644
index 0000000000..de9de71d28
--- /dev/null
+++ b/test/racc/assets/conf.y
@@ -0,0 +1,16 @@
+
+class A
+rule
+
+a: A c C expr;
+
+b: A B; # useless
+
+c: A;
+c: A;
+
+expr: expr '+' expr
+expr: expr '-' expr
+expr: NUMBER
+
+end
diff --git a/test/racc/assets/csspool.y b/test/racc/assets/csspool.y
new file mode 100644
index 0000000000..3d6af25d85
--- /dev/null
+++ b/test/racc/assets/csspool.y
@@ -0,0 +1,729 @@
+class CSSPool::CSS::Parser
+
+token CHARSET_SYM IMPORT_SYM STRING SEMI IDENT S COMMA LBRACE RBRACE STAR HASH
+token LSQUARE RSQUARE EQUAL INCLUDES DASHMATCH LPAREN RPAREN FUNCTION GREATER PLUS
+token SLASH NUMBER MINUS LENGTH PERCENTAGE ANGLE TIME FREQ URI
+token IMPORTANT_SYM MEDIA_SYM NOT ONLY AND NTH_PSEUDO_CLASS
+token DOCUMENT_QUERY_SYM FUNCTION_NO_QUOTE
+token TILDE
+token PREFIXMATCH SUFFIXMATCH SUBSTRINGMATCH
+token NOT_PSEUDO_CLASS
+token KEYFRAMES_SYM
+token MATCHES_PSEUDO_CLASS
+token NAMESPACE_SYM
+token MOZ_PSEUDO_ELEMENT
+token RESOLUTION
+token COLON
+token SUPPORTS_SYM
+token OR
+token VARIABLE_NAME
+token CALC_SYM
+token FONTFACE_SYM
+token UNICODE_RANGE
+token RATIO
+
+rule
+ document
+ : { @handler.start_document }
+ stylesheet
+ { @handler.end_document }
+ ;
+ stylesheet
+ : charset stylesheet
+ | import stylesheet
+ | namespace stylesheet
+ | charset
+ | import
+ | namespace
+ | body
+ |
+ ;
+ charset
+ : CHARSET_SYM STRING SEMI { @handler.charset interpret_string(val[1]), {} }
+ ;
+ import
+ : IMPORT_SYM import_location medium SEMI {
+ @handler.import_style val[2], val[1]
+ }
+ | IMPORT_SYM import_location SEMI {
+ @handler.import_style [], val[1]
+ }
+ ;
+ import_location
+ : import_location S
+ | STRING { result = Terms::String.new interpret_string val.first }
+ | URI { result = Terms::URI.new interpret_uri val.first }
+ ;
+ namespace
+ : NAMESPACE_SYM ident import_location SEMI {
+ @handler.namespace val[1], val[2]
+ }
+ | NAMESPACE_SYM import_location SEMI {
+ @handler.namespace nil, val[1]
+ }
+ ;
+ medium
+ : medium COMMA IDENT {
+ result = val[0] << MediaType.new(val[2])
+ }
+ | IDENT {
+ result = [MediaType.new(val[0])]
+ }
+ ;
+ media_query_list
+ : media_query { result = MediaQueryList.new([ val[0] ]) }
+ | media_query_list COMMA media_query { result = val[0] << val[2] }
+ | { result = MediaQueryList.new }
+ ;
+ media_query
+ : optional_only_or_not media_type optional_and_exprs { result = MediaQuery.new(val[0], val[1], val[2]) }
+ | media_expr optional_and_exprs { result = MediaQuery.new(nil, val[0], val[1]) }
+ ;
+ optional_only_or_not
+ : ONLY { result = :only }
+ | NOT { result = :not }
+ | { result = nil }
+ ;
+ media_type
+ : IDENT { result = MediaType.new(val[0]) }
+ ;
+ media_expr
+ : LPAREN optional_space IDENT optional_space RPAREN { result = MediaType.new(val[2]) }
+ | LPAREN optional_space IDENT optional_space COLON optional_space expr RPAREN { result = MediaFeature.new(val[2], val[6][0]) }
+ ;
+ optional_space
+ : S { result = val[0] }
+ | { result = nil }
+ ;
+ optional_and_exprs
+ : optional_and_exprs AND media_expr { result = val[0] << val[2] }
+ | { result = [] }
+ ;
+ resolution
+ : RESOLUTION {
+ unit = val.first.gsub(/[\s\d.]/, '')
+ number = numeric(val.first)
+ result = Terms::Resolution.new(number, unit)
+ }
+ ;
+ body
+ : ruleset body
+ | conditional_rule body
+ | keyframes_rule body
+ | fontface_rule body
+ | ruleset
+ | conditional_rule
+ | keyframes_rule
+ | fontface_rule
+ ;
+ conditional_rule
+ : media
+ | document_query
+ | supports
+ ;
+ body_in_media
+ : body
+ | empty_ruleset
+ ;
+ media
+ : start_media body_in_media RBRACE { @handler.end_media val.first }
+ ;
+ start_media
+ : MEDIA_SYM media_query_list LBRACE {
+ result = val[1]
+ @handler.start_media result
+ }
+ ;
+ document_query
+ : start_document_query body RBRACE { @handler.end_document_query(before_pos(val), after_pos(val)) }
+ | start_document_query RBRACE { @handler.end_document_query(before_pos(val), after_pos(val)) }
+ ;
+ start_document_query
+ : start_document_query_pos url_match_fns LBRACE {
+ @handler.start_document_query(val[1], after_pos(val))
+ }
+ ;
+ start_document_query_pos
+ : DOCUMENT_QUERY_SYM {
+ @handler.node_start_pos = before_pos(val)
+ }
+ ;
+ url_match_fns
+ : url_match_fn COMMA url_match_fns {
+ result = [val[0], val[2]].flatten
+ }
+ | url_match_fn {
+ result = val
+ }
+ ;
+ url_match_fn
+ : function_no_quote
+ | function
+ | uri
+ ;
+ supports
+ : start_supports body RBRACE { @handler.end_supports }
+ | start_supports RBRACE { @handler.end_supports }
+ ;
+ start_supports
+ : SUPPORTS_SYM supports_condition_root LBRACE {
+ @handler.start_supports val[1]
+ }
+ ;
+ supports_condition_root
+ : supports_negation { result = val.join('') }
+ | supports_conjunction_or_disjunction { result = val.join('') }
+ | supports_condition_in_parens { result = val.join('') }
+ ;
+ supports_condition
+ : supports_negation { result = val.join('') }
+ | supports_conjunction_or_disjunction { result = val.join('') }
+ | supports_condition_in_parens { result = val.join('') }
+ ;
+ supports_condition_in_parens
+ : LPAREN supports_condition RPAREN { result = val.join('') }
+ | supports_declaration_condition { result = val.join('') }
+ ;
+ supports_negation
+ : NOT supports_condition_in_parens { result = val.join('') }
+ ;
+ supports_conjunction_or_disjunction
+ : supports_conjunction
+ | supports_disjunction
+ ;
+ supports_conjunction
+ : supports_condition_in_parens AND supports_condition_in_parens { result = val.join('') }
+ | supports_conjunction_or_disjunction AND supports_condition_in_parens { result = val.join('') }
+ ;
+ supports_disjunction
+ : supports_condition_in_parens OR supports_condition_in_parens { result = val.join('') }
+ | supports_conjunction_or_disjunction OR supports_condition_in_parens { result = val.join('') }
+ ;
+ supports_declaration_condition
+ : LPAREN declaration_internal RPAREN { result = val.join('') }
+ | LPAREN S declaration_internal RPAREN { result = val.join('') }
+ ;
+ keyframes_rule
+ : start_keyframes_rule keyframes_blocks RBRACE
+ | start_keyframes_rule RBRACE
+ ;
+ start_keyframes_rule
+ : KEYFRAMES_SYM IDENT LBRACE {
+ @handler.start_keyframes_rule val[1]
+ }
+ ;
+ keyframes_blocks
+ : keyframes_block keyframes_blocks
+ | keyframes_block
+ ;
+ keyframes_block
+ : start_keyframes_block declarations RBRACE { @handler.end_keyframes_block }
+ | start_keyframes_block RBRACE { @handler.end_keyframes_block }
+ ;
+ start_keyframes_block
+ : keyframes_selectors LBRACE {
+ @handler.start_keyframes_block val[0]
+ }
+ ;
+ keyframes_selectors
+ | keyframes_selector COMMA keyframes_selectors {
+ result = val[0] + ', ' + val[2]
+ }
+ | keyframes_selector
+ ;
+ keyframes_selector
+ : IDENT
+ | PERCENTAGE { result = val[0].strip }
+ ;
+ fontface_rule
+ : start_fontface_rule declarations RBRACE { @handler.end_fontface_rule }
+ | start_fontface_rule RBRACE { @handler.end_fontface_rule }
+ ;
+ start_fontface_rule
+ : FONTFACE_SYM LBRACE {
+ @handler.start_fontface_rule
+ }
+ ;
+ ruleset
+ : start_selector declarations RBRACE {
+ @handler.end_selector val.first
+ }
+ | start_selector RBRACE {
+ @handler.end_selector val.first
+ }
+ ;
+ empty_ruleset
+ : optional_space {
+ start = @handler.start_selector([])
+ @handler.end_selector(start)
+ }
+ ;
+ start_selector
+ : S start_selector { result = val.last }
+ | selectors LBRACE {
+ @handler.start_selector val.first
+ }
+ ;
+ selectors
+ : selector COMMA selectors
+ {
+ sel = Selector.new(val.first, {})
+ result = [sel].concat(val[2])
+ }
+ | selector
+ {
+ result = [Selector.new(val.first, {})]
+ }
+ ;
+ selector
+ : simple_selector combinator selector
+ {
+ val.flatten!
+ val[2].combinator = val.delete_at 1
+ result = val
+ }
+ | simple_selector
+ ;
+ combinator
+ : S { result = :s }
+ | GREATER { result = :> }
+ | PLUS { result = :+ }
+ | TILDE { result = :~ }
+ ;
+ simple_selector
+ : element_name hcap {
+ selector = val.first
+ selector.additional_selectors = val.last
+ result = [selector]
+ }
+ | element_name { result = val }
+ | hcap
+ {
+ ss = Selectors::Simple.new nil, nil
+ ss.additional_selectors = val.flatten
+ result = [ss]
+ }
+ ;
+ simple_selectors
+ : simple_selector COMMA simple_selectors { result = [val[0], val[2]].flatten }
+ | simple_selector
+ ;
+ ident_with_namespace
+ : IDENT { result = [interpret_identifier(val[0]), nil] }
+ | IDENT '|' IDENT { result = [interpret_identifier(val[2]), interpret_identifier(val[0])] }
+ | '|' IDENT { result = [interpret_identifier(val[1]), nil] }
+ | STAR '|' IDENT { result = [interpret_identifier(val[2]), '*'] }
+ ;
+ element_name
+ : ident_with_namespace { result = Selectors::Type.new val.first[0], nil, val.first[1] }
+ | STAR { result = Selectors::Universal.new val.first }
+ | '|' STAR { result = Selectors::Universal.new val[1] }
+ | STAR '|' STAR { result = Selectors::Universal.new val[2], nil, val[0] }
+ | IDENT '|' STAR { result = Selectors::Universal.new val[2], nil, interpret_identifier(val[0]) }
+ ;
+ hcap
+ : hash { result = val }
+ | class { result = val }
+ | attrib { result = val }
+ | pseudo { result = val }
+ | hash hcap { result = val.flatten }
+ | class hcap { result = val.flatten }
+ | attrib hcap { result = val.flatten }
+ | pseudo hcap { result = val.flatten }
+ ;
+ hash
+ : HASH {
+ result = Selectors::Id.new interpret_identifier val.first.sub(/^#/, '')
+ }
+ class
+ : '.' IDENT {
+ result = Selectors::Class.new interpret_identifier val.last
+ }
+ ;
+ attrib
+ : LSQUARE ident_with_namespace EQUAL IDENT RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_identifier(val[3]),
+ Selectors::Attribute::EQUALS,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace EQUAL STRING RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_string(val[3]),
+ Selectors::Attribute::EQUALS,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace INCLUDES STRING RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_string(val[3]),
+ Selectors::Attribute::INCLUDES,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace INCLUDES IDENT RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_identifier(val[3]),
+ Selectors::Attribute::INCLUDES,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace DASHMATCH IDENT RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_identifier(val[3]),
+ Selectors::Attribute::DASHMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace DASHMATCH STRING RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_string(val[3]),
+ Selectors::Attribute::DASHMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace PREFIXMATCH IDENT RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_identifier(val[3]),
+ Selectors::Attribute::PREFIXMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace PREFIXMATCH STRING RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_string(val[3]),
+ Selectors::Attribute::PREFIXMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace SUFFIXMATCH IDENT RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_identifier(val[3]),
+ Selectors::Attribute::SUFFIXMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace SUFFIXMATCH STRING RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_string(val[3]),
+ Selectors::Attribute::SUFFIXMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace SUBSTRINGMATCH IDENT RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_identifier(val[3]),
+ Selectors::Attribute::SUBSTRINGMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace SUBSTRINGMATCH STRING RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_string(val[3]),
+ Selectors::Attribute::SUBSTRINGMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ nil,
+ Selectors::Attribute::SET,
+ val[1][1]
+ )
+ }
+ ;
+ pseudo
+ : COLON IDENT {
+ result = Selectors::pseudo interpret_identifier(val[1])
+ }
+ | COLON COLON IDENT {
+ result = Selectors::PseudoElement.new(
+ interpret_identifier(val[2])
+ )
+ }
+ | COLON FUNCTION RPAREN {
+ result = Selectors::PseudoClass.new(
+ interpret_identifier(val[1].sub(/\($/, '')),
+ ''
+ )
+ }
+ | COLON FUNCTION IDENT RPAREN {
+ result = Selectors::PseudoClass.new(
+ interpret_identifier(val[1].sub(/\($/, '')),
+ interpret_identifier(val[2])
+ )
+ }
+ | COLON NOT_PSEUDO_CLASS simple_selector RPAREN {
+ result = Selectors::PseudoClass.new(
+ 'not',
+ val[2].first.to_s
+ )
+ }
+ | COLON NTH_PSEUDO_CLASS {
+ result = Selectors::PseudoClass.new(
+ interpret_identifier(val[1].sub(/\(.*/, '')),
+ interpret_identifier(val[1].sub(/.*\(/, '').sub(/\).*/, ''))
+ )
+ }
+ | COLON MATCHES_PSEUDO_CLASS simple_selectors RPAREN {
+ result = Selectors::PseudoClass.new(
+ val[1].split('(').first.strip,
+ val[2].join(', ')
+ )
+ }
+ | COLON MOZ_PSEUDO_ELEMENT optional_space any_number_of_idents optional_space RPAREN {
+ result = Selectors::PseudoElement.new(
+ interpret_identifier(val[1].sub(/\($/, ''))
+ )
+ }
+ | COLON COLON MOZ_PSEUDO_ELEMENT optional_space any_number_of_idents optional_space RPAREN {
+ result = Selectors::PseudoElement.new(
+ interpret_identifier(val[2].sub(/\($/, ''))
+ )
+ }
+ ;
+ any_number_of_idents
+ :
+ | multiple_idents
+ ;
+ multiple_idents
+ : IDENT
+ | IDENT COMMA multiple_idents
+ ;
+ # declarations can be separated by one *or more* semicolons. semi-colons at the start or end of a ruleset are also allowed
+ one_or_more_semis
+ : SEMI
+ | SEMI one_or_more_semis
+ ;
+ declarations
+ : declaration one_or_more_semis declarations
+ | one_or_more_semis declarations
+ | declaration one_or_more_semis
+ | declaration
+ | one_or_more_semis
+ ;
+ declaration
+ : declaration_internal { @handler.property val.first }
+ ;
+ declaration_internal
+ : property COLON expr prio
+ { result = Declaration.new(val.first, val[2], val[3]) }
+ | property COLON S expr prio
+ { result = Declaration.new(val.first, val[3], val[4]) }
+ | property S COLON expr prio
+ { result = Declaration.new(val.first, val[3], val[4]) }
+ | property S COLON S expr prio
+ { result = Declaration.new(val.first, val[4], val[5]) }
+ ;
+ prio
+ : IMPORTANT_SYM { result = true }
+ | { result = false }
+ ;
+ property
+ : IDENT { result = interpret_identifier val[0] }
+ | STAR IDENT { result = interpret_identifier val.join }
+ | VARIABLE_NAME { result = interpret_identifier val[0] }
+ ;
+ operator
+ : COMMA
+ | SLASH
+ | EQUAL
+ ;
+ expr
+ : term operator expr {
+ result = [val.first, val.last].flatten
+ val.last.first.operator = val[1]
+ }
+ | term expr { result = val.flatten }
+ | term { result = val }
+ ;
+ term
+ : ident
+ | ratio
+ | numeric
+ | string
+ | uri
+ | hexcolor
+ | calc
+ | function
+ | resolution
+ | VARIABLE_NAME
+ | uranges
+ ;
+ function
+ : function S { result = val.first }
+ | FUNCTION expr RPAREN {
+ name = interpret_identifier val.first.sub(/\($/, '')
+ if name == 'rgb'
+ result = Terms::Rgb.new(*val[1])
+ else
+ result = Terms::Function.new name, val[1]
+ end
+ }
+ | FUNCTION RPAREN {
+ name = interpret_identifier val.first.sub(/\($/, '')
+ result = Terms::Function.new name
+ }
+ ;
+ function_no_quote
+ : function_no_quote S { result = val.first }
+ | FUNCTION_NO_QUOTE {
+ parts = val.first.split('(')
+ name = interpret_identifier parts.first
+ result = Terms::Function.new(name, [Terms::String.new(interpret_string_no_quote(parts.last))])
+ }
+ ;
+ uranges
+ : UNICODE_RANGE COMMA uranges
+ | UNICODE_RANGE
+ ;
+ calc
+ : CALC_SYM calc_sum RPAREN optional_space {
+ result = Terms::Math.new(val.first.split('(').first, val[1])
+ }
+ ;
+ # plus and minus are supposed to have whitespace around them, per http://dev.w3.org/csswg/css-values/#calc-syntax, but the numbers are eating trailing whitespace, so we inject it back in
+ calc_sum
+ : calc_product
+ | calc_product PLUS calc_sum { val.insert(1, ' '); result = val.join('') }
+ | calc_product MINUS calc_sum { val.insert(1, ' '); result = val.join('') }
+ ;
+ calc_product
+ : calc_value
+ | calc_value optional_space STAR calc_value { result = val.join('') }
+ | calc_value optional_space SLASH calc_value { result = val.join('') }
+ ;
+ calc_value
+ : numeric { result = val.join('') }
+ | function { result = val.join('') } # for var() variable references
+ | LPAREN calc_sum RPAREN { result = val.join('') }
+ ;
+ hexcolor
+ : hexcolor S { result = val.first }
+ | HASH { result = Terms::Hash.new val.first.sub(/^#/, '') }
+ ;
+ uri
+ : uri S { result = val.first }
+ | URI { result = Terms::URI.new interpret_uri val.first }
+ ;
+ string
+ : string S { result = val.first }
+ | STRING { result = Terms::String.new interpret_string val.first }
+ ;
+ numeric
+ : unary_operator numeric {
+ result = val[1]
+ val[1].unary_operator = val.first
+ }
+ | NUMBER {
+ result = Terms::Number.new numeric val.first
+ }
+ | PERCENTAGE {
+ result = Terms::Number.new numeric(val.first), nil, '%'
+ }
+ | LENGTH {
+ unit = val.first.gsub(/[\s\d.]/, '')
+ result = Terms::Number.new numeric(val.first), nil, unit
+ }
+ | ANGLE {
+ unit = val.first.gsub(/[\s\d.]/, '')
+ result = Terms::Number.new numeric(val.first), nil, unit
+ }
+ | TIME {
+ unit = val.first.gsub(/[\s\d.]/, '')
+ result = Terms::Number.new numeric(val.first), nil, unit
+ }
+ | FREQ {
+ unit = val.first.gsub(/[\s\d.]/, '')
+ result = Terms::Number.new numeric(val.first), nil, unit
+ }
+ ;
+ ratio
+ : RATIO {
+ result = Terms::Ratio.new(val[0], val[1])
+ }
+ ;
+ unary_operator
+ : MINUS { result = :minus }
+ | PLUS { result = :plus }
+ ;
+ ident
+ : ident S { result = val.first }
+ | IDENT { result = Terms::Ident.new interpret_identifier val.first }
+ ;
+
+---- inner
+
+def numeric thing
+ thing = thing.gsub(/[^\d.]/, '')
+ Integer(thing) rescue Float(thing)
+end
+
+def interpret_identifier s
+ interpret_escapes s
+end
+
+def interpret_uri s
+ interpret_escapes s.match(/^url\((.*)\)$/mui)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2]
+end
+
+def interpret_string_no_quote s
+ interpret_escapes s.match(/^(.*)\)$/mu)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2]
+end
+
+def interpret_string s
+ interpret_escapes s.match(/^(['"])((?:\\.|.)*)\1$/mu)[2]
+end
+
+def interpret_escapes s
+ token_exp = /\\(?:([0-9a-fA-F]{1,6}(?:\r\n|\s)?)|(.))/mu
+ return s.gsub(token_exp) do |escape_sequence|
+ if !$1.nil?
+ code = $1.chomp.to_i 16
+ code = 0xFFFD if code > 0x10FFFF
+ next [code].pack('U')
+ end
+ next '' if $2 == "\n"
+ next $2
+ end
+end
+
+# override racc's on_error so we can have context in our error messages
+def on_error(t, val, vstack)
+ errcontext = (@ss.pre_match[-10..-1] || @ss.pre_match) +
+ @ss.matched + @ss.post_match[0..9]
+ line_number = @ss.pre_match.lines.count
+ raise ParseError, sprintf("parse error on value %s (%s) " +
+ "on line %s around \"%s\"",
+ val.inspect, token_to_str(t) || '?',
+ line_number, errcontext)
+end
+
+def before_pos(val)
+ # don't include leading whitespace
+ return current_pos - val.last.length + val.last[/\A\s*/].size
+end
+
+def after_pos(val)
+ # don't include trailing whitespace
+ return current_pos - val.last[/\s*\z/].size
+end
+
+# charpos will work with multibyte strings but is not available until ruby 2
+def current_pos
+ @ss.respond_to?('charpos') ? @ss.charpos : @ss.pos
+end
diff --git a/test/racc/assets/digraph.y b/test/racc/assets/digraph.y
new file mode 100644
index 0000000000..17a034ee54
--- /dev/null
+++ b/test/racc/assets/digraph.y
@@ -0,0 +1,29 @@
+# ? detect digraph bug
+
+class P
+ token A B C D
+rule
+ target : a b c d
+ a : A
+ |
+ b : B
+ |
+ c : C
+ |
+ d : D
+ |
+end
+
+---- inner
+
+ def parse
+ do_parse
+ end
+
+ def next_token
+ [false, '$']
+ end
+
+---- footer
+
+P.new.parse
diff --git a/test/racc/assets/echk.y b/test/racc/assets/echk.y
new file mode 100644
index 0000000000..0fda2685aa
--- /dev/null
+++ b/test/racc/assets/echk.y
@@ -0,0 +1,118 @@
+#
+# racc tester
+#
+
+class Calcp
+
+ prechigh
+ left '*' '/'
+ left '+' '-'
+ preclow
+
+ convert
+ NUMBER 'Number'
+ end
+
+rule
+
+ target : exp | /* none */ { result = 0 } ;
+
+ exp : exp '+' exp { result += val[2]; a = 'plus' }
+ | exp '-' exp { result -= val[2]; "string test" }
+ | exp '*' exp { result *= val[2] }
+ | exp '/' exp { result /= val[2] }
+ | '(' { $emb = true } exp ')'
+ {
+ raise 'must not happen' unless $emb
+ result = val[2]
+ }
+ | '-' NUMBER { result = -val[1] }
+ | NUMBER
+ ;
+
+end
+
+----header
+
+class Number ; end
+
+----inner
+
+ def parse( src )
+ @src = src
+ do_parse
+ end
+
+ def next_token
+ @src.shift
+ end
+
+ def initialize
+ @yydebug = true
+ end
+
+----footer
+
+$parser = Calcp.new
+$tidx = 1
+
+def chk( src, ans )
+ ret = $parser.parse( src )
+ unless ret == ans then
+ bug! "test #{$tidx} fail"
+ end
+ $tidx += 1
+end
+
+chk(
+ [ [Number, 9],
+ [false, false],
+ [false, false] ], 9
+)
+
+chk(
+ [ [Number, 5],
+ ['*', nil],
+ [Number, 1],
+ ['-', nil],
+ [Number, 1],
+ ['*', nil],
+ [Number, 8],
+ [false, false],
+ [false, false] ], -3
+)
+
+chk(
+ [ [Number, 5],
+ ['+', nil],
+ [Number, 2],
+ ['-', nil],
+ [Number, 5],
+ ['+', nil],
+ [Number, 2],
+ ['-', nil],
+ [Number, 5],
+ [false, false],
+ [false, false] ], -1
+)
+
+chk(
+ [ ['-', nil],
+ [Number, 4],
+ [false, false],
+ [false, false] ], -4
+)
+
+chk(
+ [ [Number, 7],
+ ['*', nil],
+ ['(', nil],
+ [Number, 4],
+ ['+', nil],
+ [Number, 3],
+ [')', nil],
+ ['-', nil],
+ [Number, 9],
+ [false, false],
+ [false, false] ], 40
+)
diff --git a/test/racc/assets/edtf.y b/test/racc/assets/edtf.y
new file mode 100644
index 0000000000..4f5f6bb4fd
--- /dev/null
+++ b/test/racc/assets/edtf.y
@@ -0,0 +1,583 @@
+# -*- racc -*-
+
+# Copyright 2011 Sylvester Keil. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``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 HOLDER 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.
+#
+# The views and conclusions contained in the software and documentation are
+# those of the authors and should not be interpreted as representing official
+# policies, either expressed or implied, of the copyright holder.
+
+class EDTF::Parser
+
+token T Z E X U UNKNOWN OPEN LONGYEAR UNMATCHED DOTS UA PUA
+
+expect 0
+
+rule
+
+ edtf : level_0_expression
+ | level_1_expression
+ | level_2_expression
+ ;
+
+ # ---- Level 0 / ISO 8601 Rules ----
+
+ # NB: level 0 intervals are covered by the level 1 interval rules
+ level_0_expression : date
+ | date_time
+ ;
+
+ date : positive_date
+ | negative_date
+ ;
+
+ positive_date :
+ year { result = Date.new(val[0]).year_precision! }
+ | year_month { result = Date.new(*val.flatten).month_precision! }
+ | year_month_day { result = Date.new(*val.flatten).day_precision! }
+ ;
+
+ negative_date : '-' positive_date { result = -val[1] }
+
+
+ date_time : date T time {
+ result = DateTime.new(val[0].year, val[0].month, val[0].day, *val[2])
+ result.skip_timezone = (val[2].length == 3)
+ }
+
+ time : base_time
+ | base_time zone_offset { result = val.flatten }
+
+ base_time : hour ':' minute ':' second { result = val.values_at(0, 2, 4) }
+ | midnight
+
+ midnight : '2' '4' ':' '0' '0' ':' '0' '0' { result = [24, 0, 0] }
+
+ zone_offset : Z { result = 0 }
+ | '-' zone_offset_hour { result = -1 * val[1] }
+ | '+' positive_zone_offset { result = val[1] }
+ ;
+
+ positive_zone_offset : zone_offset_hour
+ | '0' '0' ':' '0' '0' { result = 0 }
+ ;
+
+
+ zone_offset_hour : d01_13 ':' minute { result = Rational(val[0] * 60 + val[2], 1440) }
+ | '1' '4' ':' '0' '0' { result = Rational(840, 1440) }
+ | '0' '0' ':' d01_59 { result = Rational(val[3], 1440) }
+ ;
+
+ year : digit digit digit digit {
+ result = val.zip([1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
+ }
+
+ month : d01_12
+ day : d01_31
+
+ year_month : year '-' month { result = [val[0], val[2]] }
+
+ # We raise an exception if there are two many days for the month, but
+ # do not consider leap years, as the EDTF BNF did not either.
+ # NB: an exception will be raised regardless, because the Ruby Date
+ # implementation calculates leap years.
+ year_month_day : year_month '-' day {
+ result = val[0] << val[2]
+ if result[2] > 31 || (result[2] > 30 && [2,4,6,9,11].include?(result[1])) || (result[2] > 29 && result[1] == 2)
+ raise ArgumentError, "invalid date (invalid days #{result[2]} for month #{result[1]})"
+ end
+ }
+
+ hour : d00_23
+ minute : d00_59
+ second : d00_59
+
+ # Completely covered by level_1_interval
+ # level_0_interval : date '/' date { result = Interval.new(val[0], val[1]) }
+
+
+ # ---- Level 1 Extension Rules ----
+
+ # NB: Uncertain/approximate Dates are covered by the Level 2 rules
+ level_1_expression : unspecified | level_1_interval | long_year_simple | season
+
+ # uncertain_or_approximate_date : date UA { result = uoa(val[0], val[1]) }
+
+ unspecified : unspecified_year
+ {
+ result = Date.new(val[0][0]).year_precision!
+ result.unspecified.year[2,2] = val[0][1]
+ }
+ | unspecified_month
+ | unspecified_day
+ | unspecified_day_and_month
+ ;
+
+ unspecified_year :
+ digit digit digit U
+ {
+ result = [val[0,3].zip([1000,100,10]).reduce(0) { |s,(a,b)| s += a * b }, [false,true]]
+ }
+ | digit digit U U
+ {
+ result = [val[0,2].zip([1000,100]).reduce(0) { |s,(a,b)| s += a * b }, [true, true]]
+ }
+
+ unspecified_month : year '-' U U {
+ result = Date.new(val[0]).unspecified!(:month)
+ result.precision = :month
+ }
+
+ unspecified_day : year_month '-' U U {
+ result = Date.new(*val[0]).unspecified!(:day)
+ }
+
+ unspecified_day_and_month : year '-' U U '-' U U {
+ result = Date.new(val[0]).unspecified!([:day,:month])
+ }
+
+
+ level_1_interval : level_1_start '/' level_1_end {
+ result = Interval.new(val[0], val[2])
+ }
+
+ level_1_start : date | partial_uncertain_or_approximate | unspecified | partial_unspecified | UNKNOWN
+
+ level_1_end : level_1_start | OPEN
+
+
+ long_year_simple :
+ LONGYEAR long_year
+ {
+ result = Date.new(val[1])
+ result.precision = :year
+ }
+ | LONGYEAR '-' long_year
+ {
+ result = Date.new(-1 * val[2])
+ result.precision = :year
+ }
+ ;
+
+ long_year :
+ positive_digit digit digit digit digit {
+ result = val.zip([10000,1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
+ }
+ | long_year digit { result = 10 * val[0] + val[1] }
+ ;
+
+
+ season : year '-' season_number ua {
+ result = Season.new(val[0], val[2])
+ val[3].each { |ua| result.send(ua) }
+ }
+
+ season_number : '2' '1' { result = 21 }
+ | '2' '2' { result = 22 }
+ | '2' '3' { result = 23 }
+ | '2' '4' { result = 24 }
+ ;
+
+
+ # ---- Level 2 Extension Rules ----
+
+ # NB: Level 2 Intervals are covered by the Level 1 Interval rules.
+ level_2_expression : season_qualified
+ | partial_uncertain_or_approximate
+ | partial_unspecified
+ | choice_list
+ | inclusive_list
+ | masked_precision
+ | date_and_calendar
+ | long_year_scientific
+ ;
+
+
+ season_qualified : season '^' { result = val[0]; result.qualifier = val[1] }
+
+
+ long_year_scientific :
+ long_year_simple E integer
+ {
+ result = Date.new(val[0].year * 10 ** val[2]).year_precision!
+ }
+ | LONGYEAR int1_4 E integer
+ {
+ result = Date.new(val[1] * 10 ** val[3]).year_precision!
+ }
+ | LONGYEAR '-' int1_4 E integer
+ {
+ result = Date.new(-1 * val[2] * 10 ** val[4]).year_precision!
+ }
+ ;
+
+
+ date_and_calendar : date '^' { result = val[0]; result.calendar = val[1] }
+
+
+ masked_precision :
+ digit digit digit X
+ {
+ d = val[0,3].zip([1000,100,10]).reduce(0) { |s,(a,b)| s += a * b }
+ result = EDTF::Decade.new(d)
+ }
+ | digit digit X X
+ {
+ d = val[0,2].zip([1000,100]).reduce(0) { |s,(a,b)| s += a * b }
+ result = EDTF::Century.new(d)
+ }
+ ;
+
+
+ choice_list : '[' list ']' { result = val[1].choice! }
+
+ inclusive_list : '{' list '}' { result = val[1] }
+
+ list : earlier { result = EDTF::Set.new(val[0]).earlier! }
+ | earlier ',' list_elements ',' later { result = EDTF::Set.new([val[0]] + val[2] + [val[4]]).earlier!.later! }
+ | earlier ',' list_elements { result = EDTF::Set.new([val[0]] + val[2]).earlier! }
+ | earlier ',' later { result = EDTF::Set.new([val[0]] + [val[2]]).earlier!.later! }
+ | list_elements ',' later { result = EDTF::Set.new(val[0] + [val[2]]).later! }
+ | list_elements { result = EDTF::Set.new(*val[0]) }
+ | later { result = EDTF::Set.new(val[0]).later! }
+ ;
+
+ list_elements : list_element { result = [val[0]].flatten }
+ | list_elements ',' list_element { result = val[0] + [val[2]].flatten }
+ ;
+
+ list_element : atomic
+ | consecutives
+ ;
+
+ atomic : date
+ | partial_uncertain_or_approximate
+ | unspecified
+ ;
+
+ earlier : DOTS date { result = val[1] }
+
+ later : year_month_day DOTS { result = Date.new(*val[0]).year_precision! }
+ | year_month DOTS { result = Date.new(*val[0]).month_precision! }
+ | year DOTS { result = Date.new(val[0]).year_precision! }
+ ;
+
+ consecutives : year_month_day DOTS year_month_day { result = (Date.new(val[0]).day_precision! .. Date.new(val[2]).day_precision!) }
+ | year_month DOTS year_month { result = (Date.new(val[0]).month_precision! .. Date.new(val[2]).month_precision!) }
+ | year DOTS year { result = (Date.new(val[0]).year_precision! .. Date.new(val[2]).year_precision!) }
+ ;
+
+ partial_unspecified :
+ unspecified_year '-' month '-' day
+ {
+ result = Date.new(val[0][0], val[2], val[4])
+ result.unspecified.year[2,2] = val[0][1]
+ }
+ | unspecified_year '-' U U '-' day
+ {
+ result = Date.new(val[0][0], 1, val[5])
+ result.unspecified.year[2,2] = val[0][1]
+ result.unspecified!(:month)
+ }
+ | unspecified_year '-' U U '-' U U
+ {
+ result = Date.new(val[0][0], 1, 1)
+ result.unspecified.year[2,2] = val[0][1]
+ result.unspecified!([:month, :day])
+ }
+ | unspecified_year '-' month '-' U U
+ {
+ result = Date.new(val[0][0], val[2], 1)
+ result.unspecified.year[2,2] = val[0][1]
+ result.unspecified!(:day)
+ }
+ | year '-' U U '-' day
+ {
+ result = Date.new(val[0], 1, val[5])
+ result.unspecified!(:month)
+ }
+ ;
+
+
+ partial_uncertain_or_approximate : pua_base
+ | '(' pua_base ')' UA { result = uoa(val[1], val[3]) }
+
+ pua_base :
+ pua_year { result = val[0].year_precision! }
+ | pua_year_month { result = val[0][0].month_precision! }
+ | pua_year_month_day { result = val[0].day_precision! }
+
+ pua_year : year UA { result = uoa(Date.new(val[0]), val[1], :year) }
+
+ pua_year_month :
+ pua_year '-' month ua {
+ result = [uoa(val[0].change(:month => val[2]), val[3], [:month, :year])]
+ }
+ | year '-' month UA {
+ result = [uoa(Date.new(val[0], val[2]), val[3], [:year, :month])]
+ }
+ | year '-(' month ')' UA {
+ result = [uoa(Date.new(val[0], val[2]), val[4], [:month]), true]
+ }
+ | pua_year '-(' month ')' UA {
+ result = [uoa(val[0].change(:month => val[2]), val[4], [:month]), true]
+ }
+ ;
+
+ pua_year_month_day :
+ pua_year_month '-' day ua {
+ result = uoa(val[0][0].change(:day => val[2]), val[3], val[0][1] ? [:day] : nil)
+ }
+ | pua_year_month '-(' day ')' UA {
+ result = uoa(val[0][0].change(:day => val[2]), val[4], [:day])
+ }
+ | year '-(' month ')' UA day ua {
+ result = uoa(uoa(Date.new(val[0], val[2], val[5]), val[4], :month), val[6], :day)
+ }
+ | year_month '-' day UA {
+ result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[3])
+ }
+ | year_month '-(' day ')' UA {
+ result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[4], [:day])
+ }
+ | year '-(' month '-' day ')' UA {
+ result = uoa(Date.new(val[0], val[2], val[4]), val[6], [:month, :day])
+ }
+ | year '-(' month '-(' day ')' UA ')' UA {
+ result = Date.new(val[0], val[2], val[4])
+ result = uoa(result, val[6], [:day])
+ result = uoa(result, val[8], [:month, :day])
+ }
+ | pua_year '-(' month '-' day ')' UA {
+ result = val[0].change(:month => val[2], :day => val[4])
+ result = uoa(result, val[6], [:month, :day])
+ }
+ | pua_year '-(' month '-(' day ')' UA ')' UA {
+ result = val[0].change(:month => val[2], :day => val[4])
+ result = uoa(result, val[6], [:day])
+ result = uoa(result, val[8], [:month, :day])
+ }
+ # | '(' pua_year '-(' month ')' UA ')' UA '-' day ua {
+ # result = val[1].change(:month => val[3], :day => val[9])
+ # result = uoa(result, val[5], [:month])
+ # result = [uoa(result, val[7], [:year]), true]
+ # }
+ ;
+
+ ua : { result = [] } | UA
+
+ # ---- Auxiliary Rules ----
+
+ digit : '0' { result = 0 }
+ | positive_digit
+ ;
+
+ positive_digit : '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
+
+ d01_12 : '0' positive_digit { result = val[1] }
+ | '1' '0' { result = 10 }
+ | '1' '1' { result = 11 }
+ | '1' '2' { result = 12 }
+ ;
+
+ d01_13 : d01_12
+ | '1' '3' { result = 13 }
+ ;
+
+ d01_23 : '0' positive_digit { result = val[1] }
+ | '1' digit { result = 10 + val[1] }
+ | '2' '0' { result = 20 }
+ | '2' '1' { result = 21 }
+ | '2' '2' { result = 22 }
+ | '2' '3' { result = 23 }
+ ;
+
+ d00_23 : '0' '0'
+ | d01_23
+ ;
+
+ d01_29 : d01_23
+ | '2' '4' { result = 24 }
+ | '2' '5' { result = 25 }
+ | '2' '6' { result = 26 }
+ | '2' '7' { result = 27 }
+ | '2' '8' { result = 28 }
+ | '2' '9' { result = 29 }
+ ;
+
+ d01_30 : d01_29
+ | '3' '0' { result = 30 }
+ ;
+
+ d01_31 : d01_30
+ | '3' '1' { result = 31 }
+ ;
+
+ d01_59 : d01_29
+ | '3' digit { result = 30 + val[1] }
+ | '4' digit { result = 40 + val[1] }
+ | '5' digit { result = 50 + val[1] }
+ ;
+
+ d00_59 : '0' '0'
+ | d01_59
+ ;
+
+ int1_4 : positive_digit { result = val[0] }
+ | positive_digit digit { result = 10 * val[0] + val[1] }
+ | positive_digit digit digit
+ {
+ result = val.zip([100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
+ }
+ | positive_digit digit digit digit
+ {
+ result = val.zip([1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
+ }
+ ;
+
+ integer : positive_digit { result = val[0] }
+ | integer digit { result = 10 * val[0] + val[1] }
+ ;
+
+
+
+---- header
+require 'strscan'
+
+---- inner
+
+ @defaults = {
+ :level => 2,
+ :debug => false
+ }.freeze
+
+ class << self; attr_reader :defaults; end
+
+ attr_reader :options
+
+ def initialize(options = {})
+ @options = Parser.defaults.merge(options)
+ end
+
+ def debug?
+ !!(options[:debug] || ENV['DEBUG'])
+ end
+
+ def parse(input)
+ parse!(input)
+ rescue => e
+ warn e.message if debug?
+ nil
+ end
+
+ def parse!(input)
+ @yydebug = debug?
+ @src = StringScanner.new(input)
+ do_parse
+ end
+
+ def on_error(tid, value, stack)
+ raise ArgumentError,
+ "failed to parse date: unexpected '#{value}' at #{stack.inspect}"
+ end
+
+ def apply_uncertainty(date, uncertainty, scope = nil)
+ uncertainty.each do |u|
+ scope.nil? ? date.send(u) : date.send(u, scope)
+ end
+ date
+ end
+
+ alias uoa apply_uncertainty
+
+ def next_token
+ case
+ when @src.eos?
+ nil
+ # when @src.scan(/\s+/)
+ # ignore whitespace
+ when @src.scan(/\(/)
+ ['(', @src.matched]
+ # when @src.scan(/\)\?~-/)
+ # [:PUA, [:uncertain!, :approximate!]]
+ # when @src.scan(/\)\?-/)
+ # [:PUA, [:uncertain!]]
+ # when @src.scan(/\)~-/)
+ # [:PUA, [:approximate!]]
+ when @src.scan(/\)/)
+ [')', @src.matched]
+ when @src.scan(/\[/)
+ ['[', @src.matched]
+ when @src.scan(/\]/)
+ [']', @src.matched]
+ when @src.scan(/\{/)
+ ['{', @src.matched]
+ when @src.scan(/\}/)
+ ['}', @src.matched]
+ when @src.scan(/T/)
+ [:T, @src.matched]
+ when @src.scan(/Z/)
+ [:Z, @src.matched]
+ when @src.scan(/\?~/)
+ [:UA, [:uncertain!, :approximate!]]
+ when @src.scan(/\?/)
+ [:UA, [:uncertain!]]
+ when @src.scan(/~/)
+ [:UA, [:approximate!]]
+ when @src.scan(/open/i)
+ [:OPEN, :open]
+ when @src.scan(/unkn?own/i) # matches 'unkown' typo too
+ [:UNKNOWN, :unknown]
+ when @src.scan(/u/)
+ [:U, @src.matched]
+ when @src.scan(/x/i)
+ [:X, @src.matched]
+ when @src.scan(/y/)
+ [:LONGYEAR, @src.matched]
+ when @src.scan(/e/)
+ [:E, @src.matched]
+ when @src.scan(/\+/)
+ ['+', @src.matched]
+ when @src.scan(/-\(/)
+ ['-(', @src.matched]
+ when @src.scan(/-/)
+ ['-', @src.matched]
+ when @src.scan(/:/)
+ [':', @src.matched]
+ when @src.scan(/\//)
+ ['/', @src.matched]
+ when @src.scan(/\s*\.\.\s*/)
+ [:DOTS, '..']
+ when @src.scan(/\s*,\s*/)
+ [',', ',']
+ when @src.scan(/\^\w+/)
+ ['^', @src.matched[1..-1]]
+ when @src.scan(/\d/)
+ [@src.matched, @src.matched.to_i]
+ else @src.scan(/./)
+ [:UNMATCHED, @src.rest]
+ end
+ end
+
+
+# -*- racc -*-
diff --git a/test/racc/assets/err.y b/test/racc/assets/err.y
new file mode 100644
index 0000000000..ae280957cc
--- /dev/null
+++ b/test/racc/assets/err.y
@@ -0,0 +1,60 @@
+
+class ErrTestp
+
+rule
+
+target: lines
+ ;
+
+lines: line
+ | lines line
+ ;
+
+line: A B C D E
+ | error E
+ ;
+
+end
+
+---- inner
+
+def initialize
+ @yydebug = false
+ @q = [
+ [:A, 'a'],
+ # [:B, 'b'],
+ [:C, 'c'],
+ [:D, 'd'],
+ [:E, 'e'],
+
+ [:A, 'a'],
+ [:B, 'b'],
+ [:C, 'c'],
+ [:D, 'd'],
+ [:E, 'e'],
+
+ [:A, 'a'],
+ [:B, 'b'],
+ # [:C, 'c'],
+ [:D, 'd'],
+ [:E, 'e'],
+ [false, nil]
+ ]
+end
+
+def next_token
+ @q.shift
+end
+
+def on_error( t, val, values )
+ $stderr.puts "error on token '#{val}'(#{t})"
+end
+
+def parse
+ do_parse
+end
+
+---- footer
+
+p = ErrTestp.new
+p.parse
diff --git a/test/racc/assets/error_recovery.y b/test/racc/assets/error_recovery.y
new file mode 100644
index 0000000000..1fd21ac7d0
--- /dev/null
+++ b/test/racc/assets/error_recovery.y
@@ -0,0 +1,35 @@
+# Regression test case for the bug discussed here:
+# https://github.com/whitequark/parser/issues/93
+# In short, a Racc-generated parser could go into an infinite loop when
+# attempting error recovery at EOF
+
+class InfiniteLoop
+
+rule
+
+ stmts: stmt
+ | error stmt
+
+ stmt: '%' stmt
+
+end
+
+---- inner
+
+ def parse
+ @errors = []
+ do_parse
+ end
+
+ def next_token
+ nil
+ end
+
+ def on_error(error_token, error_value, value_stack)
+ # oh my, an error
+ @errors << [error_token, error_value]
+ end
+
+---- footer
+
+InfiniteLoop.new.parse \ No newline at end of file
diff --git a/test/racc/assets/expect.y b/test/racc/assets/expect.y
new file mode 100644
index 0000000000..24c27443e2
--- /dev/null
+++ b/test/racc/assets/expect.y
@@ -0,0 +1,7 @@
+class E
+ expect 1
+rule
+ list: inlist inlist
+ inlist:
+ | A
+end
diff --git a/test/racc/assets/firstline.y b/test/racc/assets/firstline.y
new file mode 100644
index 0000000000..ab0692e543
--- /dev/null
+++ b/test/racc/assets/firstline.y
@@ -0,0 +1,4 @@
+class T
+rule
+ a: A B C
+end
diff --git a/test/racc/assets/huia.y b/test/racc/assets/huia.y
new file mode 100644
index 0000000000..de9d45150c
--- /dev/null
+++ b/test/racc/assets/huia.y
@@ -0,0 +1,318 @@
+# Copyright (c) 2014 James Harton
+#
+# MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+class Huia::Parser
+
+ token
+ IDENTIFIER EQUAL PLUS MINUS ASTERISK FWD_SLASH COLON FLOAT INTEGER STRING
+ EXPO INDENT OUTDENT OPAREN CPAREN DOT SIGNATURE NL EOF PIPE COMMA NIL TRUE
+ FALSE EQUALITY CALL SELF CONSTANT CHAR DOUBLE_TICK_STRING
+ DOUBLE_TICK_STRING_END INTERPOLATE_START INTERPOLATE_END BOX LSQUARE
+ RSQUARE FACES LFACE RFACE BANG TILDE RETURN NOT_EQUALITY OR AND GT LT
+ GTE LTE AT
+
+ prechigh
+ left EXPO
+ left BANG TILDE
+ left ASTERISK FWD_SLASH PERCENT
+ left PLUS MINUS
+
+ right EQUAL
+ preclow
+
+ rule
+ statements: statement
+ | statements statement { return scope }
+
+ statement: expr eol { return scope.append val[0] }
+ | expr { return scope.append val[0] }
+ | eol { return scope }
+
+ eol: NL | EOF
+ nlq: NL |
+
+ expr: literal
+ | grouped_expr
+ | binary_op
+ | unary_op
+ | method_call
+ | constant
+ | variable
+ | array
+ | hash
+ | return
+
+ return: return_expr
+ | return_nil
+ return_expr: RETURN expr { return n(:Return, val[1]) }
+ return_nil: RETURN { return n(:Return, n(:Nil)) }
+
+ array: empty_array
+ | array_list
+
+ empty_array: BOX { return n :Array }
+
+ array_list: LSQUARE array_items RSQUARE { return val[1] }
+ array_items: expr { return n :Array, [val[0]] }
+ | array_items COMMA expr { val[0].append(val[2]); return val[0] }
+
+ hash: empty_hash
+ | hash_list
+ empty_hash: FACES { return n :Hash }
+ hash_list: LFACE hash_items RFACE { return val[1] }
+ hash_items: hash_item { return n :Hash, val[0] }
+ | hash_items COMMA hash_item { val[0].append(val[2]); return val[0] }
+ hash_item: expr COLON expr { return n :HashItem, val[0], val[2] }
+
+ constant: CONSTANT { return constant val[0] }
+
+ indented: indented_w_stmts
+ | indented_w_expr
+ | indented_wo_stmts
+ indented_w_stmts: indent statements outdent { return val[0] }
+ indented_w_expr: indent expr outdent { return val[0].append(val[1]) }
+ indented_wo_stmts: indent outdent { return val[0] }
+ outdent: OUTDENT { return pop_scope }
+
+
+ indent_w_args: indent_pipe indent_args PIPE nlq INDENT { return val[0] }
+ indent_pipe: PIPE { return push_scope }
+ indent_wo_args: INDENT { return push_scope }
+ indent: indent_w_args
+ | indent_wo_args
+
+ indent_args: indent_arg
+ | indent_args COMMA indent_arg
+ indent_arg: arg_var { return scope.add_argument val[0] }
+ | arg_var EQUAL expr { return n :Assignment, val[0], val[2] }
+ arg_var: IDENTIFIER { return n :Variable, val[0] }
+
+ method_call: method_call_on_object
+ | method_call_on_self
+ | method_call_on_closure
+ method_call_on_object: expr DOT call_signature { return n :MethodCall, val[0], val[2] }
+ | expr DOT IDENTIFIER { return n :MethodCall, val[0], n(:CallSignature, val[2]) }
+ method_call_on_self: call_signature { return n :MethodCall, scope_instance, val[0] }
+
+ method_call_on_closure: AT call_signature { return n :MethodCall, this_closure, val[1] }
+ | AT IDENTIFIER { return n :MethodCall, this_closure, n(:CallSignature, val[1]) }
+
+ call_signature: call_arguments
+ | call_simple_name
+ call_simple_name: CALL { return n :CallSignature, val[0] }
+ call_argument: SIGNATURE call_passed_arg { return n :CallSignature, val[0], [val[1]] }
+ call_passed_arg: call_passed_simple
+ | call_passed_indented
+ call_passed_simple: expr
+ | expr NL
+ call_passed_indented: indented
+ | indented NL
+ call_arguments: call_argument { return val[0] }
+ | call_arguments call_argument { return val[0].concat_signature val[1] }
+
+ grouped_expr: OPAREN expr CPAREN { return n :Expression, val[1] }
+
+ variable: IDENTIFIER { return allocate_local val[0] }
+
+ binary_op: assignment
+ | addition
+ | subtraction
+ | multiplication
+ | division
+ | exponentiation
+ | modulo
+ | equality
+ | not_equality
+ | logical_or
+ | logical_and
+ | greater_than
+ | less_than
+ | greater_or_eq
+ | less_or_eq
+
+ assignment: IDENTIFIER EQUAL expr { return allocate_local_assignment val[0], val[2] }
+ addition: expr PLUS expr { return binary val[0], val[2], 'plus:' }
+ subtraction: expr MINUS expr { return binary val[0], val[2], 'minus:' }
+ multiplication: expr ASTERISK expr { return binary val[0], val[2], 'multiplyBy:' }
+ division: expr FWD_SLASH expr { return binary val[0], val[2], 'divideBy:' }
+ exponentiation: expr EXPO expr { return binary val[0], val[2], 'toThePowerOf:' }
+ modulo: expr PERCENT expr { return binary val[0], val[2], 'moduloOf:' }
+ equality: expr EQUALITY expr { return binary val[0], val[2], 'isEqualTo:' }
+ not_equality: expr NOT_EQUALITY expr { return binary val[0], val[2], 'isNotEqualTo:' }
+ logical_or: expr OR expr { return binary val[0], val[2], 'logicalOr:' }
+ logical_and: expr AND expr { return binary val[0], val[2], 'logicalAnd:' }
+ greater_than: expr GT expr { return binary val[0], val[2], 'isGreaterThan:' }
+ less_than: expr LT expr { return binary val[0], val[2], 'isLessThan:' }
+ greater_or_eq: expr GTE expr { return binary val[0], val[2], 'isGreaterOrEqualTo:' }
+ less_or_eq: expr LTE expr { return binary val[0], val[2], 'isLessOrEqualTo:' }
+
+ unary_op: unary_not
+ | unary_plus
+ | unary_minus
+ | unary_complement
+
+ unary_not: BANG expr { return unary val[1], 'unaryNot' }
+ unary_plus: PLUS expr { return unary val[1], 'unaryPlus' }
+ unary_minus: MINUS expr { return unary val[1], 'unaryMinus' }
+ unary_complement: TILDE expr { return unary val[1], 'unaryComplement' }
+
+ literal: integer
+ | float
+ | string
+ | nil
+ | true
+ | false
+ | self
+
+ float: FLOAT { return n :Float, val[0] }
+ integer: INTEGER { return n :Integer, val[0] }
+ nil: NIL { return n :Nil }
+ true: TRUE { return n :True }
+ false: FALSE { return n :False }
+ self: SELF { return n :Self }
+
+ string: STRING { return n :String, val[0] }
+ | interpolated_string
+ | empty_string
+
+ interpolated_string: DOUBLE_TICK_STRING interpolated_string_contents DOUBLE_TICK_STRING_END { return val[1] }
+ interpolation: INTERPOLATE_START expr INTERPOLATE_END { return val[1] }
+ interpolated_string_contents: interpolated_string_chunk { return n :InterpolatedString, val[0] }
+ | interpolated_string_contents interpolated_string_chunk { val[0].append(val[1]); return val[0] }
+ interpolated_string_chunk: chars { return val[0] }
+ | interpolation { return to_string(val[0]) }
+ empty_string: DOUBLE_TICK_STRING DOUBLE_TICK_STRING_END { return n :String, '' }
+
+ chars: CHAR { return n :String, val[0] }
+ | chars CHAR { val[0].append(val[1]); return val[0] }
+end
+
+---- inner
+
+attr_accessor :lexer, :scopes, :state
+
+def initialize lexer
+ @lexer = lexer
+ @state = []
+ @scopes = []
+ push_scope
+end
+
+def ast
+ @ast ||= do_parse
+ @scopes.first
+end
+
+def on_error t, val, vstack
+ line = lexer.line
+ col = lexer.column
+ message = "Unexpected #{token_to_str t} at #{lexer.filename} line #{line}:#{col}:\n\n"
+
+ start = line - 5 > 0 ? line - 5 : 0
+ i_size = line.to_s.size
+ (start..(start + 5)).each do |i|
+ message << sprintf("\t%#{i_size}d: %s\n", i, lexer.get_line(i))
+ message << "\t#{' ' * i_size} #{'-' * (col - 1)}^\n" if i == line
+ end
+
+ raise SyntaxError, message
+end
+
+def next_token
+ nt = lexer.next_computed_token
+ # just use a state stack for now, we'll have to do something
+ # more sophisticated soon.
+ if nt && nt.first == :state
+ if nt.last
+ state.push << nt.last
+ else
+ state.pop
+ end
+ next_token
+ else
+ nt
+ end
+end
+
+def push_scope
+ new_scope = Huia::AST::Scope.new scope
+ new_scope.file = lexer.filename
+ new_scope.line = lexer.line
+ new_scope.column = lexer.column
+ scopes.push new_scope
+ new_scope
+end
+
+def pop_scope
+ scopes.pop
+end
+
+def scope
+ scopes.last
+end
+
+def binary left, right, method
+ node(:MethodCall, left, node(:CallSignature, method, [right]))
+end
+
+def unary left, method
+ node(:MethodCall, left, node(:CallSignature, method))
+end
+
+def node type, *args
+ Huia::AST.const_get(type).new(*args).tap do |n|
+ n.file = lexer.filename
+ n.line = lexer.line
+ n.column = lexer.column
+ end
+end
+alias n node
+
+def allocate_local name
+ node(:Variable, name).tap do |n|
+ scope.allocate_local n
+ end
+end
+
+def allocate_local_assignment name, value
+ node(:Assignment, name, value).tap do |n|
+ scope.allocate_local n
+ end
+end
+
+def this_closure
+ allocate_local('@')
+end
+
+def scope_instance
+ node(:ScopeInstance, scope)
+end
+
+def constant name
+ return scope_instance if name == 'self'
+ node(:Constant, name)
+end
+
+def to_string expr
+ node(:MethodCall, expr, node(:CallSignature, 'toString'))
+end
diff --git a/test/racc/assets/ichk.y b/test/racc/assets/ichk.y
new file mode 100644
index 0000000000..1d359df83e
--- /dev/null
+++ b/test/racc/assets/ichk.y
@@ -0,0 +1,102 @@
+class Calculator
+
+ prechigh
+ left '*' '/'
+ left '+' '-'
+ preclow
+
+ convert
+ NUMBER 'Number'
+ end
+
+rule
+
+ target : exp
+ | /* none */ { result = 0 }
+
+ exp : exp '+' exp { result += val[2]; a = 'plus' }
+ | exp '-' exp { result -= val[2]; a = "string test" }
+ | exp '*' exp { result *= val[2] }
+ | exp '/' exp { result /= val[2] }
+ | '(' { $emb = true } exp ')'
+ {
+ raise 'must not happen' unless $emb
+ result = val[2]
+ }
+ | '-' NUMBER { result = -val[1] }
+ | NUMBER
+
+----header
+
+class Number
+end
+
+----inner
+
+ def initialize
+ @racc_debug_out = $stdout
+ @yydebug = false
+ end
+
+ def validate(expected, src)
+ result = parse(src)
+ unless result == expected
+ raise "test #{@test_number} fail"
+ end
+ @test_number += 1
+ end
+
+ def parse(src)
+ @src = src
+ @test_number = 1
+ yyparse self, :scan
+ end
+
+ def scan(&block)
+ @src.each(&block)
+ end
+
+----footer
+
+calc = Calculator.new
+
+calc.validate(9, [[Number, 9], nil])
+
+calc.validate(-3,
+ [[Number, 5],
+ ['*', '*'],
+ [Number, 1],
+ ['-', '*'],
+ [Number, 1],
+ ['*', '*'],
+ [Number, 8],
+ nil])
+
+calc.validate(-1,
+ [[Number, 5],
+ ['+', '+'],
+ [Number, 2],
+ ['-', '-'],
+ [Number, 5],
+ ['+', '+'],
+ [Number, 2],
+ ['-', '-'],
+ [Number, 5],
+ nil])
+
+calc.validate(-4,
+ [['-', 'UMINUS'],
+ [Number, 4],
+ nil])
+
+calc.validate(40,
+ [[Number, 7],
+ ['*', '*'],
+ ['(', '('],
+ [Number, 4],
+ ['+', '+'],
+ [Number, 3],
+ [')', ')'],
+ ['-', '-'],
+ [Number, 9],
+ nil])
diff --git a/test/racc/assets/intp.y b/test/racc/assets/intp.y
new file mode 100644
index 0000000000..24e547da61
--- /dev/null
+++ b/test/racc/assets/intp.y
@@ -0,0 +1,546 @@
+#
+# intp
+#
+
+class Intp::Parser
+
+prechigh
+ nonassoc UMINUS
+ left '*' '/'
+ left '+' '-'
+ nonassoc EQ
+preclow
+
+rule
+
+ program : stmt_list
+ {
+ result = RootNode.new( val[0] )
+ }
+
+ stmt_list :
+ {
+ result = []
+ }
+ | stmt_list stmt EOL
+ {
+ result.push val[1]
+ }
+ | stmt_list EOL
+
+ stmt : expr
+ | assign
+ | IDENT realprim
+ {
+ result = FuncallNode.new( @fname, val[0][0],
+ val[0][1], [val[1]] )
+ }
+ | if_stmt
+ | while_stmt
+ | defun
+
+ if_stmt : IF stmt THEN EOL stmt_list else_stmt END
+ {
+ result = IfNode.new( @fname, val[0][0],
+ val[1], val[4], val[5] )
+ }
+
+ else_stmt : ELSE EOL stmt_list
+ {
+ result = val[2]
+ }
+ |
+ {
+ result = nil
+ }
+
+ while_stmt: WHILE stmt DO EOL stmt_list END
+ {
+ result = WhileNode.new(@fname, val[0][0],
+ val[1], val[4])
+ }
+
+ defun : DEF IDENT param EOL stmt_list END
+ {
+ result = DefNode.new(@fname, val[0][0], val[1][1],
+ Function.new(@fname, val[0][0], val[2], val[4]))
+ }
+
+ param : '(' name_list ')'
+ {
+ result = val[1]
+ }
+ | '(' ')'
+ {
+ result = []
+ }
+ |
+ {
+ result = []
+ }
+
+ name_list : IDENT
+ {
+ result = [ val[0][1] ]
+ }
+ | name_list ',' IDENT
+ {
+ result.push val[2][1]
+ }
+
+ assign : IDENT '=' expr
+ {
+ result = AssignNode.new(@fname, val[0][0], val[0][1], val[2])
+ }
+
+ expr : expr '+' expr
+ {
+ result = FuncallNode.new(@fname, val[0].lineno, '+', [val[0], val[2]])
+ }
+ | expr '-' expr
+ {
+ result = FuncallNode.new(@fname, val[0].lineno, '-', [val[0], val[2]])
+ }
+ | expr '*' expr
+ {
+ result = FuncallNode.new(@fname, val[0].lineno, '*', [val[0], val[2]])
+ }
+ | expr '/' expr
+ {
+ result = FuncallNode.new(@fname, val[0].lineno,
+ '/', [val[0], val[2]])
+ }
+ | expr EQ expr
+ {
+ result = FuncallNode.new(@fname, val[0].lineno, '==', [val[0], val[2]])
+ }
+ | primary
+
+ primary : realprim
+ | '(' expr ')'
+ {
+ result = val[1]
+ }
+ | '-' expr =UMINUS
+ {
+ result = FuncallNode.new(@fname, val[0][0], '-@', [val[1]])
+ }
+
+ realprim : IDENT
+ {
+ result = VarRefNode.new(@fname, val[0][0],
+ val[0][1])
+ }
+ | NUMBER
+ {
+ result = LiteralNode.new(@fname, *val[0])
+ }
+ | STRING
+ {
+ result = StringNode.new(@fname, *val[0])
+ }
+ | TRUE
+ {
+ result = LiteralNode.new(@fname, *val[0])
+ }
+ | FALSE
+ {
+ result = LiteralNode.new(@fname, *val[0])
+ }
+ | NIL
+ {
+ result = LiteralNode.new(@fname, *val[0])
+ }
+ | funcall
+
+ funcall : IDENT '(' args ')'
+ {
+ result = FuncallNode.new(@fname, val[0][0], val[0][1], val[2])
+ }
+ | IDENT '(' ')'
+ {
+ result = FuncallNode.new(@fname, val[0][0], val[0][1], [])
+ }
+
+ args : expr
+ {
+ result = val
+ }
+ | args ',' expr
+ {
+ result.push val[2]
+ }
+
+end
+
+---- header
+#
+# intp/parser.rb
+#
+
+---- inner
+
+ def initialize
+ @scope = {}
+ end
+
+ RESERVED = {
+ 'if' => :IF,
+ 'else' => :ELSE,
+ 'while' => :WHILE,
+ 'then' => :THEN,
+ 'do' => :DO,
+ 'def' => :DEF,
+ 'true' => :TRUE,
+ 'false' => :FALSE,
+ 'nil' => :NIL,
+ 'end' => :END
+ }
+
+ RESERVED_V = {
+ 'true' => true,
+ 'false' => false,
+ 'nil' => nil
+ }
+
+ def parse(f, fname)
+ @q = []
+ @fname = fname
+ lineno = 1
+ f.each do |line|
+ line.strip!
+ until line.empty?
+ case line
+ when /\A\s+/, /\A\#.*/
+ ;
+ when /\A[a-zA-Z_]\w*/
+ word = $&
+ @q.push [(RESERVED[word] || :IDENT),
+ [lineno, RESERVED_V.key?(word) ? RESERVED_V[word] : word.intern]]
+ when /\A\d+/
+ @q.push [:NUMBER, [lineno, $&.to_i]]
+ when /\A"(?:[^"\\]+|\\.)*"/, /\A'(?:[^'\\]+|\\.)*'/
+ @q.push [:STRING, [lineno, eval($&)]]
+ when /\A==/
+ @q.push [:EQ, [lineno, '==']]
+ when /\A./
+ @q.push [$&, [lineno, $&]]
+ else
+ raise RuntimeError, 'must not happen'
+ end
+ line = $'
+ end
+ @q.push [:EOL, [lineno, nil]]
+ lineno += 1
+ end
+ @q.push [false, '$']
+ do_parse
+ end
+
+ def next_token
+ @q.shift
+ end
+
+ def on_error(t, v, values)
+ if v
+ line = v[0]
+ v = v[1]
+ else
+ line = 'last'
+ end
+ raise Racc::ParseError, "#{@fname}:#{line}: syntax error on #{v.inspect}"
+ end
+
+---- footer
+# intp/node.rb
+
+module Intp
+
+ class IntpError < StandardError; end
+ class IntpArgumentError < IntpError; end
+
+ class Core
+
+ def initialize
+ @ftab = {}
+ @obj = Object.new
+ @stack = []
+ @stack.push Frame.new '(toplevel)'
+ end
+
+ def frame
+ @stack[-1]
+ end
+
+ def define_function(fname, node)
+ raise IntpError, "function #{fname} defined twice" if @ftab.key?(fname)
+ @ftab[fname] = node
+ end
+
+ def call_function_or(fname, args)
+ call_intp_function_or(fname, args) {
+ call_ruby_toplevel_or(fname, args) {
+ yield
+ }
+ }
+ end
+
+ def call_intp_function_or(fname, args)
+ if func = @ftab[fname]
+ frame = Frame.new(fname)
+ @stack.push frame
+ func.call self, frame, args
+ @stack.pop
+ else
+ yield
+ end
+ end
+
+ def call_ruby_toplevel_or(fname, args)
+ if @obj.respond_to? fname, true
+ @obj.send fname, *args
+ else
+ yield
+ end
+ end
+
+ end
+
+ class Frame
+
+ def initialize(fname)
+ @fname = fname
+ @lvars = {}
+ end
+
+ attr :fname
+
+ def lvar?(name)
+ @lvars.key? name
+ end
+
+ def [](key)
+ @lvars[key]
+ end
+
+ def []=(key, val)
+ @lvars[key] = val
+ end
+
+ end
+
+
+ class Node
+
+ def initialize(fname, lineno)
+ @filename = fname
+ @lineno = lineno
+ end
+
+ attr_reader :filename
+ attr_reader :lineno
+
+ def exec_list(intp, nodes)
+ v = nil
+ nodes.each {|i| v = i.evaluate(intp) }
+ v
+ end
+
+ def intp_error!(msg)
+ raise IntpError, "in #{filename}:#{lineno}: #{msg}"
+ end
+
+ def inspect
+ "#{self.class.name}/#{lineno}"
+ end
+
+ end
+
+
+ class RootNode < Node
+
+ def initialize(tree)
+ super nil, nil
+ @tree = tree
+ end
+
+ def evaluate
+ exec_list Core.new, @tree
+ end
+
+ end
+
+
+ class DefNode < Node
+
+ def initialize(file, lineno, fname, func)
+ super file, lineno
+ @funcname = fname
+ @funcobj = func
+ end
+
+ def evaluate(intp)
+ intp.define_function @funcname, @funcobj
+ end
+
+ end
+
+ class FuncallNode < Node
+
+ def initialize(file, lineno, func, args)
+ super file, lineno
+ @funcname = func
+ @args = args
+ end
+
+ def evaluate(intp)
+ args = @args.map {|i| i.evaluate intp }
+ begin
+ intp.call_intp_function_or(@funcname, args) {
+ if args.empty? or not args[0].respond_to?(@funcname)
+ intp.call_ruby_toplevel_or(@funcname, args) {
+ intp_error! "undefined function #{@funcname.id2name}"
+ }
+ else
+ recv = args.shift
+ recv.send @funcname, *args
+ end
+ }
+ rescue IntpArgumentError, ArgumentError
+ intp_error! $!.message
+ end
+ end
+
+ end
+
+ class Function < Node
+
+ def initialize(file, lineno, params, body)
+ super file, lineno
+ @params = params
+ @body = body
+ end
+
+ def call(intp, frame, args)
+ unless args.size == @params.size
+ raise IntpArgumentError,
+ "wrong # of arg for #{frame.fname}() (#{args.size} for #{@params.size})"
+ end
+ args.each_with_index do |v,i|
+ frame[@params[i]] = v
+ end
+ exec_list intp, @body
+ end
+
+ end
+
+
+ class IfNode < Node
+
+ def initialize(fname, lineno, cond, tstmt, fstmt)
+ super fname, lineno
+ @condition = cond
+ @tstmt = tstmt
+ @fstmt = fstmt
+ end
+
+ def evaluate(intp)
+ if @condition.evaluate(intp)
+ exec_list intp, @tstmt
+ else
+ exec_list intp, @fstmt if @fstmt
+ end
+ end
+
+ end
+
+ class WhileNode < Node
+
+ def initialize(fname, lineno, cond, body)
+ super fname, lineno
+ @condition = cond
+ @body = body
+ end
+
+ def evaluate(intp)
+ while @condition.evaluate(intp)
+ exec_list intp, @body
+ end
+ end
+
+ end
+
+
+ class AssignNode < Node
+
+ def initialize(fname, lineno, vname, val)
+ super fname, lineno
+ @vname = vname
+ @val = val
+ end
+
+ def evaluate(intp)
+ intp.frame[@vname] = @val.evaluate(intp)
+ end
+
+ end
+
+ class VarRefNode < Node
+
+ def initialize(fname, lineno, vname)
+ super fname, lineno
+ @vname = vname
+ end
+
+ def evaluate(intp)
+ if intp.frame.lvar?(@vname)
+ intp.frame[@vname]
+ else
+ intp.call_function_or(@vname, []) {
+ intp_error! "unknown method or local variable #{@vname.id2name}"
+ }
+ end
+ end
+
+ end
+
+ class StringNode < Node
+
+ def initialize(fname, lineno, str)
+ super fname, lineno
+ @val = str
+ end
+
+ def evaluate(intp)
+ @val.dup
+ end
+
+ end
+
+ class LiteralNode < Node
+
+ def initialize(fname, lineno, val)
+ super fname, lineno
+ @val = val
+ end
+
+ def evaluate(intp)
+ @val
+ end
+
+ end
+
+end # module Intp
+
+begin
+ tree = nil
+ fname = 'src.intp'
+ File.open(fname) {|f|
+ tree = Intp::Parser.new.parse(f, fname)
+ }
+ tree.evaluate
+rescue Racc::ParseError, Intp::IntpError, Errno::ENOENT
+ raise ####
+ $stderr.puts "#{File.basename $0}: #{$!}"
+ exit 1
+end
diff --git a/test/racc/assets/journey.y b/test/racc/assets/journey.y
new file mode 100644
index 0000000000..c2640f3339
--- /dev/null
+++ b/test/racc/assets/journey.y
@@ -0,0 +1,47 @@
+class Journey::Parser
+
+token SLASH LITERAL SYMBOL LPAREN RPAREN DOT STAR OR
+
+rule
+ expressions
+ : expressions expression { result = Cat.new(val.first, val.last) }
+ | expression { result = val.first }
+ | or
+ ;
+ expression
+ : terminal
+ | group
+ | star
+ ;
+ group
+ : LPAREN expressions RPAREN { result = Group.new(val[1]) }
+ ;
+ or
+ : expressions OR expression { result = Or.new([val.first, val.last]) }
+ ;
+ star
+ : STAR { result = Star.new(Symbol.new(val.last)) }
+ ;
+ terminal
+ : symbol
+ | literal
+ | slash
+ | dot
+ ;
+ slash
+ : SLASH { result = Slash.new('/') }
+ ;
+ symbol
+ : SYMBOL { result = Symbol.new(val.first) }
+ ;
+ literal
+ : LITERAL { result = Literal.new(val.first) }
+ dot
+ : DOT { result = Dot.new(val.first) }
+ ;
+
+end
+
+---- header
+
+require 'journey/parser_extras'
diff --git a/test/racc/assets/liquor.y b/test/racc/assets/liquor.y
new file mode 100644
index 0000000000..8045a072a4
--- /dev/null
+++ b/test/racc/assets/liquor.y
@@ -0,0 +1,313 @@
+# Copyright (c) 2012-2013 Peter Zotov <whitequark@whitequark.org>
+# 2012 Yaroslav Markin <yaroslav@markin.net>
+# 2012 Nate Gadgibalaev <nat@xnsv.ru>
+#
+# MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+class Liquor::Parser
+ token comma dot endtag ident integer keyword lblock lblock2 lbracket
+ linterp lparen op_div op_eq op_gt op_geq op_lt op_leq op_minus
+ op_mod op_mul op_neq op_not op_plus pipe plaintext rblock
+ rbracket rinterp rparen string tag_ident
+
+ prechigh
+ left dot
+ nonassoc op_uminus op_not
+ left op_mul op_div op_mod
+ left op_plus op_minus
+ left op_eq op_neq op_lt op_leq op_gt op_geq
+ left op_and
+ left op_or
+ preclow
+
+ expect 15
+
+ start block
+
+rule
+ block: /* empty */
+ { result = [] }
+ | plaintext block
+ { result = [ val[0], *val[1] ] }
+ | interp block
+ { result = [ val[0], *val[1] ] }
+ | tag block
+ { result = [ val[0], *val[1] ] }
+
+ interp:
+ linterp expr rinterp
+ { result = [ :interp, retag(val), val[1] ] }
+ | linterp filter_chain rinterp
+ { result = [ :interp, retag(val), val[1] ] }
+
+ primary_expr:
+ ident
+ | lparen expr rparen
+ { result = [ val[1][0], retag(val), *val[1][2..-1] ] }
+
+ expr:
+ integer
+ | string
+ | tuple
+ | ident function_args
+ { result = [ :call, retag(val), val[0], val[1] ] }
+ | expr lbracket expr rbracket
+ { result = [ :index, retag(val), val[0], val[2] ] }
+ | expr dot ident function_args
+ { result = [ :external, retag(val), val[0], val[2], val[3] ] }
+ | expr dot ident
+ { result = [ :external, retag(val), val[0], val[2], nil ] }
+ | op_minus expr =op_uminus
+ { result = [ :uminus, retag(val), val[1] ] }
+ | op_not expr
+ { result = [ :not, retag(val), val[1] ] }
+ | expr op_mul expr
+ { result = [ :mul, retag(val), val[0], val[2] ] }
+ | expr op_div expr
+ { result = [ :div, retag(val), val[0], val[2] ] }
+ | expr op_mod expr
+ { result = [ :mod, retag(val), val[0], val[2] ] }
+ | expr op_plus expr
+ { result = [ :plus, retag(val), val[0], val[2] ] }
+ | expr op_minus expr
+ { result = [ :minus, retag(val), val[0], val[2] ] }
+ | expr op_eq expr
+ { result = [ :eq, retag(val), val[0], val[2] ] }
+ | expr op_neq expr
+ { result = [ :neq, retag(val), val[0], val[2] ] }
+ | expr op_lt expr
+ { result = [ :lt, retag(val), val[0], val[2] ] }
+ | expr op_leq expr
+ { result = [ :leq, retag(val), val[0], val[2] ] }
+ | expr op_gt expr
+ { result = [ :gt, retag(val), val[0], val[2] ] }
+ | expr op_geq expr
+ { result = [ :geq, retag(val), val[0], val[2] ] }
+ | expr op_and expr
+ { result = [ :and, retag(val), val[0], val[2] ] }
+ | expr op_or expr
+ { result = [ :or, retag(val), val[0], val[2] ] }
+ | primary_expr
+
+ tuple:
+ lbracket tuple_content rbracket
+ { result = [ :tuple, retag(val), val[1].compact ] }
+
+ tuple_content:
+ expr comma tuple_content
+ { result = [ val[0], *val[2] ] }
+ | expr
+ { result = [ val[0] ] }
+ | /* empty */
+ { result = [ ] }
+
+ function_args:
+ lparen function_args_inside rparen
+ { result = [ :args, retag(val), *val[1] ] }
+
+ function_args_inside:
+ expr function_keywords
+ { result = [ val[0], val[1][2] ] }
+ | function_keywords
+ { result = [ nil, val[0][2] ] }
+
+ function_keywords:
+ keyword expr function_keywords
+ { name = val[0][2].to_sym
+ tail = val[2][2]
+ loc = retag([ val[0], val[1] ])
+
+ if tail.include? name
+ @errors << SyntaxError.new("duplicate keyword argument `#{val[0][2]}'",
+ tail[name][1])
+ end
+
+ hash = {
+ name => [ val[1][0], loc, *val[1][2..-1] ]
+ }.merge(tail)
+
+ result = [ :keywords, retag([ loc, val[2] ]), hash ]
+ }
+ | /* empty */
+ { result = [ :keywords, nil, {} ] }
+
+ filter_chain:
+ expr pipe filter_chain_cont
+ { result = [ val[0], *val[2] ].
+ reduce { |tree, node| node[3][2] = tree; node }
+ }
+
+ filter_chain_cont:
+ filter_call pipe filter_chain_cont
+ { result = [ val[0], *val[2] ] }
+ | filter_call
+ { result = [ val[0] ] }
+
+ filter_call:
+ ident function_keywords
+ { ident_loc = val[0][1]
+ empty_args_loc = { line: ident_loc[:line],
+ start: ident_loc[:end] + 1,
+ end: ident_loc[:end] + 1, }
+ result = [ :call, val[0][1], val[0],
+ [ :args, val[1][1] || empty_args_loc, nil, val[1][2] ] ]
+ }
+
+ tag:
+ lblock ident expr tag_first_cont
+ { result = [ :tag, retag(val), val[1], val[2], *reduce_tag_args(val[3][2]) ] }
+ | lblock ident tag_first_cont
+ { result = [ :tag, retag(val), val[1], nil, *reduce_tag_args(val[2][2]) ] }
+
+ # Racc cannot do lookahead across rules. I had to add states
+ # explicitly to avoid S/R conflicts. You are not expected to
+ # understand this.
+
+ tag_first_cont:
+ rblock
+ { result = [ :cont, retag(val), [] ] }
+ | keyword tag_first_cont2
+ { result = [ :cont, retag(val), [ val[0], *val[1][2] ] ] }
+
+ tag_first_cont2:
+ rblock block lblock2 tag_next_cont
+ { result = [ :cont2, val[0][1], [ [:block, val[0][1], val[1] ], *val[3] ] ] }
+ | expr tag_first_cont
+ { result = [ :cont2, retag(val), [ val[0], *val[1][2] ] ] }
+
+ tag_next_cont:
+ endtag rblock
+ { result = [] }
+ | keyword tag_next_cont2
+ { result = [ val[0], *val[1] ] }
+
+ tag_next_cont2:
+ rblock block lblock2 tag_next_cont
+ { result = [ [:block, val[0][1], val[1] ], *val[3] ] }
+ | expr keyword tag_next_cont3
+ { result = [ val[0], val[1], *val[2] ] }
+
+ tag_next_cont3:
+ rblock block lblock2 tag_next_cont
+ { result = [ [:block, val[0][1], val[1] ], *val[3] ] }
+ | expr tag_next_cont
+ { result = [ val[0], *val[1] ] }
+
+---- inner
+ attr_reader :errors, :ast
+
+ def initialize(tags={})
+ super()
+
+ @errors = []
+ @ast = nil
+ @tags = tags
+ end
+
+ def success?
+ @errors.empty?
+ end
+
+ def parse(string, name='(code)')
+ @errors.clear
+ @name = name
+ @ast = nil
+
+ begin
+ @stream = Lexer.lex(string, @name, @tags)
+ @ast = do_parse
+ rescue Liquor::SyntaxError => e
+ @errors << e
+ end
+
+ success?
+ end
+
+ def next_token
+ tok = @stream.shift
+ [ tok[0], tok ] if tok
+ end
+
+ TOKEN_NAME_MAP = {
+ :comma => ',',
+ :dot => '.',
+ :lblock => '{%',
+ :rblock => '%}',
+ :linterp => '{{',
+ :rinterp => '}}',
+ :lbracket => '[',
+ :rbracket => ']',
+ :lparen => '(',
+ :rparen => ')',
+ :pipe => '|',
+ :op_not => '!',
+ :op_mul => '*',
+ :op_div => '/',
+ :op_mod => '%',
+ :op_plus => '+',
+ :op_minus => '-',
+ :op_eq => '==',
+ :op_neq => '!=',
+ :op_lt => '<',
+ :op_leq => '<=',
+ :op_gt => '>',
+ :op_geq => '>=',
+ :keyword => 'keyword argument name',
+ :kwarg => 'keyword argument',
+ :ident => 'identifier',
+ }
+
+ def on_error(error_token_id, error_token, value_stack)
+ if token_to_str(error_token_id) == "$end"
+ raise Liquor::SyntaxError.new("unexpected end of program", {
+ file: @name
+ })
+ else
+ type, (loc, value) = error_token
+ type = TOKEN_NAME_MAP[type] || type
+
+ raise Liquor::SyntaxError.new("unexpected token `#{type}'", loc)
+ end
+ end
+
+ def retag(nodes)
+ loc = nodes.map { |node| node[1] }.compact
+ first, *, last = loc
+ return first if last.nil?
+
+ {
+ file: first[:file],
+ line: first[:line],
+ start: first[:start],
+ end: last[:end],
+ }
+ end
+
+ def reduce_tag_args(list)
+ list.each_slice(2).reduce([]) { |args, (k, v)|
+ if v[0] == :block
+ args << [ :blockarg, retag([ k, v ]), k, v[2] || [] ]
+ else
+ args << [ :kwarg, retag([ k, v ]), k, v ]
+ end
+ }
+ end \ No newline at end of file
diff --git a/test/racc/assets/machete.y b/test/racc/assets/machete.y
new file mode 100644
index 0000000000..ea92d47a69
--- /dev/null
+++ b/test/racc/assets/machete.y
@@ -0,0 +1,423 @@
+# Copyright (c) 2011 SUSE
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+class Machete::Parser
+
+token NIL
+token TRUE
+token FALSE
+token INTEGER
+token SYMBOL
+token STRING
+token REGEXP
+token ANY
+token EVEN
+token ODD
+token METHOD_NAME
+token CLASS_NAME
+
+start expression
+
+rule
+
+expression : primary
+ | expression "|" primary {
+ result = if val[0].is_a?(ChoiceMatcher)
+ ChoiceMatcher.new(val[0].alternatives << val[2])
+ else
+ ChoiceMatcher.new([val[0], val[2]])
+ end
+ }
+
+primary : node
+ | array
+ | literal
+ | any
+
+node : CLASS_NAME {
+ result = NodeMatcher.new(val[0].to_sym)
+ }
+ | CLASS_NAME "<" attrs ">" {
+ result = NodeMatcher.new(val[0].to_sym, val[2])
+ }
+
+attrs : attr
+ | attrs "," attr { result = val[0].merge(val[2]) }
+
+attr : method_name "=" expression { result = { val[0].to_sym => val[2] } }
+ | method_name "^=" SYMBOL {
+ result = {
+ val[0].to_sym => SymbolRegexpMatcher.new(
+ Regexp.new("^" + Regexp.escape(symbol_value(val[2]).to_s))
+ )
+ }
+ }
+ | method_name "$=" SYMBOL {
+ result = {
+ val[0].to_sym => SymbolRegexpMatcher.new(
+ Regexp.new(Regexp.escape(symbol_value(val[2]).to_s) + "$")
+ )
+ }
+ }
+ | method_name "*=" SYMBOL {
+ result = {
+ val[0].to_sym => SymbolRegexpMatcher.new(
+ Regexp.new(Regexp.escape(symbol_value(val[2]).to_s))
+ )
+ }
+ }
+ | method_name "^=" STRING {
+ result = {
+ val[0].to_sym => StringRegexpMatcher.new(
+ Regexp.new("^" + Regexp.escape(string_value(val[2])))
+ )
+ }
+ }
+ | method_name "$=" STRING {
+ result = {
+ val[0].to_sym => StringRegexpMatcher.new(
+ Regexp.new(Regexp.escape(string_value(val[2])) + "$")
+ )
+ }
+ }
+ | method_name "*=" STRING {
+ result = {
+ val[0].to_sym => StringRegexpMatcher.new(
+ Regexp.new(Regexp.escape(string_value(val[2])))
+ )
+ }
+ }
+ | method_name "*=" REGEXP {
+ result = {
+ val[0].to_sym => IndifferentRegexpMatcher.new(
+ Regexp.new(regexp_value(val[2]))
+ )
+ }
+ }
+
+# Hack to overcome the fact that some tokens will lex as simple tokens, not
+# METHOD_NAME tokens, and that "reserved words" will lex as separate kinds of
+# tokens.
+method_name : METHOD_NAME
+ | NIL
+ | TRUE
+ | FALSE
+ | ANY
+ | EVEN
+ | ODD
+ | "*"
+ | "+"
+ | "<"
+ | ">"
+ | "^"
+ | "|"
+
+array : "[" items_opt "]" { result = ArrayMatcher.new(val[1]) }
+
+items_opt : /* empty */ { result = [] }
+ | items
+
+items : item { result = [val[0]] }
+ | items "," item { result = val[0] << val[2] }
+
+item : expression
+ | expression quantifier { result = Quantifier.new(val[0], *val[1]) }
+
+quantifier : "*" { result = [0, nil, 1] }
+ | "+" { result = [1, nil, 1] }
+ | "?" { result = [0, 1, 1] }
+ | "{" INTEGER "}" {
+ result = [integer_value(val[1]), integer_value(val[1]), 1]
+ }
+ | "{" INTEGER "," "}" {
+ result = [integer_value(val[1]), nil, 1]
+ }
+ | "{" "," INTEGER "}" {
+ result = [0, integer_value(val[2]), 1]
+ }
+ | "{" INTEGER "," INTEGER "}" {
+ result = [integer_value(val[1]), integer_value(val[3]), 1]
+ }
+ | "{" EVEN "}" { result = [0, nil, 2] }
+ | "{" ODD "}" { result = [1, nil, 2] }
+
+literal : NIL { result = LiteralMatcher.new(nil) }
+ | TRUE { result = LiteralMatcher.new(true) }
+ | FALSE { result = LiteralMatcher.new(false) }
+ | INTEGER { result = LiteralMatcher.new(integer_value(val[0])) }
+ | SYMBOL { result = LiteralMatcher.new(symbol_value(val[0])) }
+ | STRING { result = LiteralMatcher.new(string_value(val[0])) }
+ | REGEXP { result = LiteralMatcher.new(regexp_value(val[0])) }
+
+any : ANY { result = AnyMatcher.new }
+
+---- inner
+
+include Matchers
+
+class SyntaxError < StandardError; end
+
+def parse(input)
+ @input = input
+ @pos = 0
+
+ do_parse
+end
+
+private
+
+def integer_value(value)
+ if value =~ /^0[bB]/
+ value[2..-1].to_i(2)
+ elsif value =~ /^0[oO]/
+ value[2..-1].to_i(8)
+ elsif value =~ /^0[dD]/
+ value[2..-1].to_i(10)
+ elsif value =~ /^0[xX]/
+ value[2..-1].to_i(16)
+ elsif value =~ /^0/
+ value.to_i(8)
+ else
+ value.to_i
+ end
+end
+
+def symbol_value(value)
+ value[1..-1].to_sym
+end
+
+def string_value(value)
+ quote = value[0..0]
+ if quote == "'"
+ value[1..-2].gsub("\\\\", "\\").gsub("\\'", "'")
+ elsif quote == '"'
+ value[1..-2].
+ gsub("\\\\", "\\").
+ gsub('\\"', '"').
+ gsub("\\n", "\n").
+ gsub("\\t", "\t").
+ gsub("\\r", "\r").
+ gsub("\\f", "\f").
+ gsub("\\v", "\v").
+ gsub("\\a", "\a").
+ gsub("\\e", "\e").
+ gsub("\\b", "\b").
+ gsub("\\s", "\s").
+ gsub(/\\([0-7]{1,3})/) { $1.to_i(8).chr }.
+ gsub(/\\x([0-9a-fA-F]{1,2})/) { $1.to_i(16).chr }
+ else
+ raise "Unknown quote: #{quote.inspect}."
+ end
+end
+
+REGEXP_OPTIONS = {
+ 'i' => Regexp::IGNORECASE,
+ 'm' => Regexp::MULTILINE,
+ 'x' => Regexp::EXTENDED
+}
+
+def regexp_value(value)
+ /\A\/(.*)\/([imx]*)\z/ =~ value
+ pattern, options = $1, $2
+
+ Regexp.new(pattern, options.chars.map { |ch| REGEXP_OPTIONS[ch] }.inject(:|))
+end
+
+# "^" needs to be here because if it were among operators recognized by
+# METHOD_NAME, "^=" would be recognized as two tokens.
+SIMPLE_TOKENS = [
+ "|",
+ "<",
+ ">",
+ ",",
+ "=",
+ "^=",
+ "^",
+ "$=",
+ "[",
+ "]",
+ "*=",
+ "*",
+ "+",
+ "?",
+ "{",
+ "}"
+]
+
+COMPLEX_TOKENS = [
+ [:NIL, /^nil/],
+ [:TRUE, /^true/],
+ [:FALSE, /^false/],
+ # INTEGER needs to be before METHOD_NAME, otherwise e.g. "+1" would be
+ # recognized as two tokens.
+ [
+ :INTEGER,
+ /^
+ [+-]? # sign
+ (
+ 0[bB][01]+(_[01]+)* # binary (prefixed)
+ |
+ 0[oO][0-7]+(_[0-7]+)* # octal (prefixed)
+ |
+ 0[dD]\d+(_\d+)* # decimal (prefixed)
+ |
+ 0[xX][0-9a-fA-F]+(_[0-9a-fA-F]+)* # hexadecimal (prefixed)
+ |
+ 0[0-7]*(_[0-7]+)* # octal (unprefixed)
+ |
+ [1-9]\d*(_\d+)* # decimal (unprefixed)
+ )
+ /x
+ ],
+ [
+ :SYMBOL,
+ /^
+ :
+ (
+ # class name
+ [A-Z][a-zA-Z0-9_]*
+ |
+ # regular method name
+ [a-z_][a-zA-Z0-9_]*[?!=]?
+ |
+ # instance variable name
+ @[a-zA-Z_][a-zA-Z0-9_]*
+ |
+ # class variable name
+ @@[a-zA-Z_][a-zA-Z0-9_]*
+ |
+ # operator (sorted by length, then alphabetically)
+ (<=>|===|\[\]=|\*\*|\+@|-@|<<|<=|==|=~|>=|>>|\[\]|[%&*+\-\/<>^`|~])
+ )
+ /x
+ ],
+ [
+ :STRING,
+ /^
+ (
+ ' # sinqle-quoted string
+ (
+ \\[\\'] # escape
+ |
+ [^'] # regular character
+ )*
+ '
+ |
+ " # double-quoted string
+ (
+ \\ # escape
+ (
+ [\\"ntrfvaebs] # one-character escape
+ |
+ [0-7]{1,3} # octal number escape
+ |
+ x[0-9a-fA-F]{1,2} # hexadecimal number escape
+ )
+ |
+ [^"] # regular character
+ )*
+ "
+ )
+ /x
+ ],
+ [
+ :REGEXP,
+ /^
+ \/
+ (
+ \\ # escape
+ (
+ [\\\/ntrfvaebs\(\)\[\]\{\}\-\.\?\*\+\|\^\$] # one-character escape
+ |
+ [0-7]{2,3} # octal number escape
+ |
+ x[0-9a-fA-F]{1,2} # hexadecimal number escape
+ )
+ |
+ [^\/] # regular character
+ )*
+ \/
+ [imx]*
+ /x
+ ],
+ # ANY, EVEN and ODD need to be before METHOD_NAME, otherwise they would be
+ # recognized as method names.
+ [:ANY, /^any/],
+ [:EVEN, /^even/],
+ [:ODD, /^odd/],
+ # We exclude "*", "+", "<", ">", "^" and "|" from method names since they are
+ # lexed as simple tokens. This is because they have also other meanings in
+ # Machette patterns beside Ruby method names.
+ [
+ :METHOD_NAME,
+ /^
+ (
+ # regular name
+ [a-z_][a-zA-Z0-9_]*[?!=]?
+ |
+ # operator (sorted by length, then alphabetically)
+ (<=>|===|\[\]=|\*\*|\+@|-@|<<|<=|==|=~|>=|>>|\[\]|[%&\-\/`~])
+ )
+ /x
+ ],
+ [:CLASS_NAME, /^[A-Z][a-zA-Z0-9_]*/]
+]
+
+def next_token
+ skip_whitespace
+
+ return false if remaining_input.empty?
+
+ # Complex tokens need to be before simple tokens, otherwise e.g. "<<" would be
+ # recognized as two tokens.
+
+ COMPLEX_TOKENS.each do |type, regexp|
+ if remaining_input =~ regexp
+ @pos += $&.length
+ return [type, $&]
+ end
+ end
+
+ SIMPLE_TOKENS.each do |token|
+ if remaining_input[0...token.length] == token
+ @pos += token.length
+ return [token, token]
+ end
+ end
+
+ raise SyntaxError, "Unexpected character: #{remaining_input[0..0].inspect}."
+end
+
+def skip_whitespace
+ if remaining_input =~ /\A^[ \t\r\n]+/
+ @pos += $&.length
+ end
+end
+
+def remaining_input
+ @input[@pos..-1]
+end
+
+def on_error(error_token_id, error_value, value_stack)
+ raise SyntaxError, "Unexpected token: #{error_value.inspect}."
+end
diff --git a/test/racc/assets/macruby.y b/test/racc/assets/macruby.y
new file mode 100644
index 0000000000..5ede008308
--- /dev/null
+++ b/test/racc/assets/macruby.y
@@ -0,0 +1,2197 @@
+# Copyright (c) 2013 Peter Zotov <whitequark@whitequark.org>
+#
+# Parts of the source are derived from ruby_parser:
+# Copyright (c) Ryan Davis, seattle.rb
+#
+# MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+class Parser::MacRuby
+
+token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
+ kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT
+ kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kDO_LAMBDA kRETURN kYIELD kSUPER
+ kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD
+ kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__
+ k__FILE__ k__ENCODING__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT
+ tLABEL tCVAR tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT
+ tREGEXP_END tUPLUS tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ
+ tGEQ tLEQ tANDOP tOROP tMATCH tNMATCH tDOT tDOT2 tDOT3 tAREF
+ tASET tLSHFT tRSHFT tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN
+ tLPAREN2 tRPAREN tLPAREN_ARG tLBRACK tLBRACK2 tRBRACK tLBRACE
+ tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2 tTILDE tPERCENT tDIVIDE
+ tPLUS tMINUS tLT tGT tPIPE tBANG tCARET tLCURLY tRCURLY
+ tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tREGEXP_OPT
+ tWORDS_BEG tQWORDS_BEG tSTRING_DBEG tSTRING_DVAR tSTRING_END
+ tSTRING tSYMBOL tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG
+ tCHARACTER
+
+prechigh
+ right tBANG tTILDE tUPLUS
+ right tPOW
+ right tUMINUS_NUM tUMINUS
+ left tSTAR2 tDIVIDE tPERCENT
+ left tPLUS tMINUS
+ left tLSHFT tRSHFT
+ left tAMPER2
+ left tPIPE tCARET
+ left tGT tGEQ tLT tLEQ
+ nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
+ left tANDOP
+ left tOROP
+ nonassoc tDOT2 tDOT3
+ right tEH tCOLON
+ left kRESCUE_MOD
+ right tEQL tOP_ASGN
+ nonassoc kDEFINED
+ right kNOT
+ left kOR kAND
+ nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
+ nonassoc tLBRACE_ARG
+ nonassoc tLOWEST
+preclow
+
+rule
+
+ program: top_compstmt
+
+ top_compstmt: top_stmts opt_terms
+ {
+ result = @builder.compstmt(val[0])
+ }
+
+ top_stmts: # nothing
+ {
+ result = []
+ }
+ | top_stmt
+ {
+ result = [ val[0] ]
+ }
+ | top_stmts terms top_stmt
+ {
+ result = val[0] << val[2]
+ }
+ | error top_stmt
+ {
+ result = [ val[1] ]
+ }
+
+ top_stmt: stmt
+ | klBEGIN tLCURLY top_compstmt tRCURLY
+ {
+ result = @builder.preexe(val[0], val[1], val[2], val[3])
+ }
+
+ bodystmt: compstmt opt_rescue opt_else opt_ensure
+ {
+ rescue_bodies = val[1]
+ else_t, else_ = val[2]
+ ensure_t, ensure_ = val[3]
+
+ if rescue_bodies.empty? && !else_.nil?
+ diagnostic :warning, :useless_else, nil, else_t
+ end
+
+ result = @builder.begin_body(val[0],
+ rescue_bodies,
+ else_t, else_,
+ ensure_t, ensure_)
+ }
+
+ compstmt: stmts opt_terms
+ {
+ result = @builder.compstmt(val[0])
+ }
+
+ stmts: # nothing
+ {
+ result = []
+ }
+ | stmt
+ {
+ result = [ val[0] ]
+ }
+ | stmts terms stmt
+ {
+ result = val[0] << val[2]
+ }
+ | error stmt
+ {
+ result = [ val[1] ]
+ }
+
+ stmt: kALIAS fitem
+ {
+ @lexer.state = :expr_fname
+ }
+ fitem
+ {
+ result = @builder.alias(val[0], val[1], val[3])
+ }
+ | kALIAS tGVAR tGVAR
+ {
+ result = @builder.alias(val[0],
+ @builder.gvar(val[1]),
+ @builder.gvar(val[2]))
+ }
+ | kALIAS tGVAR tBACK_REF
+ {
+ result = @builder.alias(val[0],
+ @builder.gvar(val[1]),
+ @builder.back_ref(val[2]))
+ }
+ | kALIAS tGVAR tNTH_REF
+ {
+ diagnostic :error, :nth_ref_alias, nil, val[2]
+ }
+ | kUNDEF undef_list
+ {
+ result = @builder.undef_method(val[0], val[1])
+ }
+ | stmt kIF_MOD expr_value
+ {
+ result = @builder.condition_mod(val[0], nil,
+ val[1], val[2])
+ }
+ | stmt kUNLESS_MOD expr_value
+ {
+ result = @builder.condition_mod(nil, val[0],
+ val[1], val[2])
+ }
+ | stmt kWHILE_MOD expr_value
+ {
+ result = @builder.loop_mod(:while, val[0], val[1], val[2])
+ }
+ | stmt kUNTIL_MOD expr_value
+ {
+ result = @builder.loop_mod(:until, val[0], val[1], val[2])
+ }
+ | stmt kRESCUE_MOD stmt
+ {
+ rescue_body = @builder.rescue_body(val[1],
+ nil, nil, nil,
+ nil, val[2])
+
+ result = @builder.begin_body(val[0], [ rescue_body ])
+ }
+ | klEND tLCURLY compstmt tRCURLY
+ {
+ result = @builder.postexe(val[0], val[1], val[2], val[3])
+ }
+ | lhs tEQL command_call
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+ | mlhs tEQL command_call
+ {
+ result = @builder.multi_assign(val[0], val[1], val[2])
+ }
+ | var_lhs tOP_ASGN command_call
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.index(
+ val[0], val[1], val[2], val[3]),
+ val[4], val[5])
+ }
+ | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tDOT tCONSTANT tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | backref tOP_ASGN command_call
+ {
+ @builder.op_assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL mrhs
+ {
+ result = @builder.assign(val[0], val[1],
+ @builder.array(nil, val[2], nil))
+ }
+ | mlhs tEQL arg_value
+ {
+ result = @builder.multi_assign(val[0], val[1], val[2])
+ }
+ | mlhs tEQL mrhs
+ {
+ result = @builder.multi_assign(val[0], val[1],
+ @builder.array(nil, val[2], nil))
+ }
+ | expr
+
+ expr: command_call
+ | expr kAND expr
+ {
+ result = @builder.logical_op(:and, val[0], val[1], val[2])
+ }
+ | expr kOR expr
+ {
+ result = @builder.logical_op(:or, val[0], val[1], val[2])
+ }
+ | kNOT opt_nl expr
+ {
+ result = @builder.not_op(val[0], nil, val[2], nil)
+ }
+ | tBANG command_call
+ {
+ result = @builder.not_op(val[0], nil, val[1], nil)
+ }
+ | arg
+
+ expr_value: expr
+
+ command_call: command
+ | block_command
+
+ block_command: block_call
+ | block_call tDOT operation2 command_args
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ *val[3])
+ }
+ | block_call tCOLON2 operation2 command_args
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ *val[3])
+ }
+
+ cmd_brace_block: tLBRACE_ARG
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt tRCURLY
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ command: operation command_args =tLOWEST
+ {
+ result = @builder.call_method(nil, nil, val[0],
+ *val[1])
+ }
+ | operation command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(nil, nil, val[0],
+ *val[1])
+
+ begin_t, args, body, end_t = val[2]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | primary_value tDOT operation2 command_args =tLOWEST
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ *val[3])
+ }
+ | primary_value tDOT operation2 command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ *val[3])
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | primary_value tCOLON2 operation2 command_args =tLOWEST
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ *val[3])
+ }
+ | primary_value tCOLON2 operation2 command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ *val[3])
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | kSUPER command_args
+ {
+ result = @builder.keyword_cmd(:super, val[0],
+ *val[1])
+ }
+ | kYIELD command_args
+ {
+ result = @builder.keyword_cmd(:yield, val[0],
+ *val[1])
+ }
+ | kRETURN call_args
+ {
+ result = @builder.keyword_cmd(:return, val[0],
+ nil, val[1], nil)
+ }
+ | kBREAK call_args
+ {
+ result = @builder.keyword_cmd(:break, val[0],
+ nil, val[1], nil)
+ }
+ | kNEXT call_args
+ {
+ result = @builder.keyword_cmd(:next, val[0],
+ nil, val[1], nil)
+ }
+
+ mlhs: mlhs_basic
+ {
+ result = @builder.multi_lhs(nil, val[0], nil)
+ }
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+
+ mlhs_inner: mlhs_basic
+ {
+ result = @builder.multi_lhs(nil, val[0], nil)
+ }
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ mlhs_basic: mlhs_head
+ | mlhs_head mlhs_item
+ {
+ result = val[0].
+ push(val[1])
+ }
+ | mlhs_head tSTAR mlhs_node
+ {
+ result = val[0].
+ push(@builder.splat(val[1], val[2]))
+ }
+ | mlhs_head tSTAR mlhs_node tCOMMA mlhs_post
+ {
+ result = val[0].
+ push(@builder.splat(val[1], val[2])).
+ concat(val[4])
+ }
+ | mlhs_head tSTAR
+ {
+ result = val[0].
+ push(@builder.splat(val[1]))
+ }
+ | mlhs_head tSTAR tCOMMA mlhs_post
+ {
+ result = val[0].
+ push(@builder.splat(val[1])).
+ concat(val[3])
+ }
+ | tSTAR mlhs_node
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+ | tSTAR mlhs_node tCOMMA mlhs_post
+ {
+ result = [ @builder.splat(val[0], val[1]),
+ *val[3] ]
+ }
+ | tSTAR
+ {
+ result = [ @builder.splat(val[0]) ]
+ }
+ | tSTAR tCOMMA mlhs_post
+ {
+ result = [ @builder.splat(val[0]),
+ *val[2] ]
+ }
+
+ mlhs_item: mlhs_node
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+
+ mlhs_head: mlhs_item tCOMMA
+ {
+ result = [ val[0] ]
+ }
+ | mlhs_head mlhs_item tCOMMA
+ {
+ result = val[0] << val[1]
+ }
+
+ mlhs_post: mlhs_item
+ {
+ result = [ val[0] ]
+ }
+ | mlhs_post tCOMMA mlhs_item
+ {
+ result = val[0] << val[2]
+ }
+
+ mlhs_node: variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index_asgn(val[0], val[1], val[2], val[3])
+ }
+ | primary_value tDOT tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tDOT tCONSTANT
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_global(val[0], val[1]))
+ }
+ | backref
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ lhs: variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index_asgn(val[0], val[1], val[2], val[3])
+ }
+ | primary_value tDOT tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tDOT tCONSTANT
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_global(val[0], val[1]))
+ }
+ | backref
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ cname: tIDENTIFIER
+ {
+ diagnostic :error, :module_name_const, nil, val[0]
+ }
+ | tCONSTANT
+
+ cpath: tCOLON3 cname
+ {
+ result = @builder.const_global(val[0], val[1])
+ }
+ | cname
+ {
+ result = @builder.const(val[0])
+ }
+ | primary_value tCOLON2 cname
+ {
+ result = @builder.const_fetch(val[0], val[1], val[2])
+ }
+
+ fname: tIDENTIFIER | tCONSTANT | tFID
+ | op
+ | reswords
+
+ fsym: fname
+ {
+ result = @builder.symbol(val[0])
+ }
+ | symbol
+
+ fitem: fsym
+ | dsym
+
+ undef_list: fitem
+ {
+ result = [ val[0] ]
+ }
+ | undef_list tCOMMA
+ {
+ @lexer.state = :expr_fname
+ }
+ fitem
+ {
+ result = val[0] << val[3]
+ }
+
+ op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ
+ | tMATCH | tNMATCH | tGT | tGEQ | tLT | tLEQ
+ | tNEQ | tLSHFT | tRSHFT | tPLUS | tMINUS | tSTAR2
+ | tSTAR | tDIVIDE | tPERCENT | tPOW | tBANG | tTILDE
+ | tUPLUS | tUMINUS | tAREF | tASET | tBACK_REF2
+
+ reswords: k__LINE__ | k__FILE__ | k__ENCODING__ | klBEGIN | klEND
+ | kALIAS | kAND | kBEGIN | kBREAK | kCASE
+ | kCLASS | kDEF | kDEFINED | kDO | kELSE
+ | kELSIF | kEND | kENSURE | kFALSE | kFOR
+ | kIN | kMODULE | kNEXT | kNIL | kNOT
+ | kOR | kREDO | kRESCUE | kRETRY | kRETURN
+ | kSELF | kSUPER | kTHEN | kTRUE | kUNDEF
+ | kWHEN | kYIELD | kIF | kUNLESS | kWHILE
+ | kUNTIL
+
+ arg: lhs tEQL arg
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL arg kRESCUE_MOD arg
+ {
+ rescue_body = @builder.rescue_body(val[3],
+ nil, nil, nil,
+ nil, val[4])
+
+ rescue_ = @builder.begin_body(val[2], [ rescue_body ])
+
+ result = @builder.assign(val[0], val[1], rescue_)
+ }
+ | var_lhs tOP_ASGN arg
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | var_lhs tOP_ASGN arg kRESCUE_MOD arg
+ {
+ rescue_body = @builder.rescue_body(val[3],
+ nil, nil, nil,
+ nil, val[4])
+
+ rescue_ = @builder.begin_body(val[2], [ rescue_body ])
+
+ result = @builder.op_assign(val[0], val[1], rescue_)
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.index(
+ val[0], val[1], val[2], val[3]),
+ val[4], val[5])
+ }
+ | primary_value tDOT tIDENTIFIER tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tDOT tCONSTANT tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
+ {
+ diagnostic :error, :dynamic_const, nil, val[2], [ val[3] ]
+ }
+ | tCOLON3 tCONSTANT tOP_ASGN arg
+ {
+ diagnostic :error, :dynamic_const, nil, val[1], [ val[2] ]
+ }
+ | backref tOP_ASGN arg
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | arg tDOT2 arg
+ {
+ result = @builder.range_inclusive(val[0], val[1], val[2])
+ }
+ | arg tDOT3 arg
+ {
+ result = @builder.range_exclusive(val[0], val[1], val[2])
+ }
+ | arg tPLUS arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tMINUS arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tSTAR2 arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tDIVIDE arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tPERCENT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tPOW arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | tUMINUS_NUM tINTEGER tPOW arg
+ {
+ result = @builder.unary_op(val[0],
+ @builder.binary_op(
+ @builder.integer(val[1]),
+ val[2], val[3]))
+ }
+ | tUMINUS_NUM tFLOAT tPOW arg
+ {
+ result = @builder.unary_op(val[0],
+ @builder.binary_op(
+ @builder.float(val[1]),
+ val[2], val[3]))
+ }
+ | tUPLUS arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | tUMINUS arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | arg tPIPE arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tCARET arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tAMPER2 arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tCMP arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tGT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tGEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tLT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tLEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tEQQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tNEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tMATCH arg
+ {
+ result = @builder.match_op(val[0], val[1], val[2])
+ }
+ | arg tNMATCH arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | tBANG arg
+ {
+ result = @builder.not_op(val[0], nil, val[1], nil)
+ }
+ | tTILDE arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | arg tLSHFT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tRSHFT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tANDOP arg
+ {
+ result = @builder.logical_op(:and, val[0], val[1], val[2])
+ }
+ | arg tOROP arg
+ {
+ result = @builder.logical_op(:or, val[0], val[1], val[2])
+ }
+ | kDEFINED opt_nl arg
+ {
+ result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil)
+ }
+
+ | arg tEH arg opt_nl tCOLON arg
+ {
+ result = @builder.ternary(val[0], val[1],
+ val[2], val[4], val[5])
+ }
+ | primary
+
+ arg_value: arg
+
+ aref_args: none
+ | args trailer
+ | args tCOMMA assocs trailer
+ {
+ result = val[0] << @builder.associate(nil, val[2], nil)
+ }
+ | assocs trailer
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ }
+
+ paren_args: tLPAREN2 opt_call_args rparen
+ {
+ result = val
+ }
+
+ opt_paren_args: # nothing
+ {
+ result = [ nil, [], nil ]
+ }
+ | paren_args
+
+ opt_call_args: # nothing
+ {
+ result = []
+ }
+ | call_args
+
+ call_args: command
+ {
+ result = [ val[0] ]
+ }
+ | args opt_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | assocs opt_block_arg
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ result.concat(val[1])
+ }
+ | args tCOMMA assocs opt_block_arg
+ {
+ assocs = @builder.associate(nil, val[2], nil)
+ result = val[0] << assocs
+ result.concat(val[3])
+ }
+ | args tCOMMA assocs tCOMMA args opt_block_arg
+ {
+ val[2][-1] = @builder.objc_varargs(val[2][-1], val[4])
+ assocs = @builder.associate(nil, val[2], nil)
+ result = val[0] << assocs
+ result.concat(val[5])
+ }
+ | block_arg
+ {
+ result = [ val[0] ]
+ }
+
+ call_args2: arg_value tCOMMA args opt_block_arg
+ {
+ result = [ val[0], *val[2].concat(val[3]) ]
+ }
+ | arg_value tCOMMA block_arg
+ {
+ result = [ val[0], val[2] ]
+ }
+ | assocs opt_block_arg
+ {
+ result = [ @builder.associate(nil, val[0], nil),
+ *val[1] ]
+ }
+ | arg_value tCOMMA assocs opt_block_arg
+ {
+ result = [ val[0],
+ @builder.associate(nil, val[2], nil),
+ *val[3] ]
+ }
+ | arg_value tCOMMA args tCOMMA assocs opt_block_arg
+ {
+ result = [ val[0],
+ *val[2].
+ push(@builder.associate(nil, val[4], nil)).
+ concat(val[5]) ]
+ }
+ | block_arg
+ {
+ result = [ val[0] ]
+ }
+
+ command_args: {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.push(true)
+ }
+ open_args
+ {
+ @lexer.cmdarg = val[0]
+
+ result = val[1]
+ }
+
+ open_args: call_args
+ {
+ result = [ nil, val[0], nil ]
+ }
+ | tLPAREN_ARG
+ {
+ @lexer.state = :expr_endarg
+ }
+ rparen
+ {
+ result = [ val[0], [], val[2] ]
+ }
+ | tLPAREN_ARG call_args2
+ {
+ @lexer.state = :expr_endarg
+ }
+ rparen
+ {
+ result = [ val[0], val[1], val[3] ]
+ }
+
+ block_arg: tAMPER arg_value
+ {
+ result = @builder.block_pass(val[0], val[1])
+ }
+
+ opt_block_arg: tCOMMA block_arg
+ {
+ result = [ val[1] ]
+ }
+ | tCOMMA
+ {
+ result = []
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ args: arg_value
+ {
+ result = [ val[0] ]
+ }
+ | tSTAR arg_value
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+ | args tCOMMA arg_value
+ {
+ result = val[0] << val[2]
+ }
+ | args tCOMMA tSTAR arg_value
+ {
+ result = val[0] << @builder.splat(val[2], val[3])
+ }
+
+ mrhs: args tCOMMA arg_value
+ {
+ result = val[0] << val[2]
+ }
+ | args tCOMMA tSTAR arg_value
+ {
+ result = val[0] << @builder.splat(val[2], val[3])
+ }
+ | tSTAR arg_value
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+
+ primary: literal
+ | strings
+ | xstring
+ | regexp
+ | words
+ | qwords
+ | var_ref
+ | backref
+ | tFID
+ {
+ result = @builder.call_method(nil, nil, val[0])
+ }
+ | kBEGIN bodystmt kEND
+ {
+ result = @builder.begin_keyword(val[0], val[1], val[2])
+ }
+ | tLPAREN_ARG expr
+ {
+ @lexer.state = :expr_endarg
+ }
+ rparen
+ {
+ result = @builder.begin(val[0], val[1], val[3])
+ }
+ | tLPAREN compstmt tRPAREN
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.const_fetch(val[0], val[1], val[2])
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.const_global(val[0], val[1])
+ }
+ | tLBRACK aref_args tRBRACK
+ {
+ result = @builder.array(val[0], val[1], val[2])
+ }
+ | tLBRACE assoc_list tRCURLY
+ {
+ result = @builder.associate(val[0], val[1], val[2])
+ }
+ | kRETURN
+ {
+ result = @builder.keyword_cmd(:return, val[0])
+ }
+ | kYIELD tLPAREN2 call_args rparen
+ {
+ result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3])
+ }
+ | kYIELD tLPAREN2 rparen
+ {
+ result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2])
+ }
+ | kYIELD
+ {
+ result = @builder.keyword_cmd(:yield, val[0])
+ }
+ | kDEFINED opt_nl tLPAREN2 expr rparen
+ {
+ result = @builder.keyword_cmd(:defined?, val[0],
+ val[2], [ val[3] ], val[4])
+ }
+ | kNOT tLPAREN2 expr rparen
+ {
+ result = @builder.not_op(val[0], val[1], val[2], val[3])
+ }
+ | kNOT tLPAREN2 rparen
+ {
+ result = @builder.not_op(val[0], val[1], nil, val[2])
+ }
+ | operation brace_block
+ {
+ method_call = @builder.call_method(nil, nil, val[0])
+
+ begin_t, args, body, end_t = val[1]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | method_call
+ | method_call brace_block
+ {
+ begin_t, args, body, end_t = val[1]
+ result = @builder.block(val[0],
+ begin_t, args, body, end_t)
+ }
+ | tLAMBDA lambda
+ {
+ lambda_call = @builder.call_lambda(val[0])
+
+ args, (begin_t, body, end_t) = val[1]
+ result = @builder.block(lambda_call,
+ begin_t, args, body, end_t)
+ }
+ | kIF expr_value then compstmt if_tail kEND
+ {
+ else_t, else_ = val[4]
+ result = @builder.condition(val[0], val[1], val[2],
+ val[3], else_t,
+ else_, val[5])
+ }
+ | kUNLESS expr_value then compstmt opt_else kEND
+ {
+ else_t, else_ = val[4]
+ result = @builder.condition(val[0], val[1], val[2],
+ else_, else_t,
+ val[3], val[5])
+ }
+ | kWHILE
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.loop(:while, val[0], val[2], val[3],
+ val[5], val[6])
+ }
+ | kUNTIL
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.loop(:until, val[0], val[2], val[3],
+ val[5], val[6])
+ }
+ | kCASE expr_value opt_terms case_body kEND
+ {
+ *when_bodies, (else_t, else_body) = *val[3]
+
+ result = @builder.case(val[0], val[1],
+ when_bodies, else_t, else_body,
+ val[4])
+ }
+ | kCASE opt_terms case_body kEND
+ {
+ *when_bodies, (else_t, else_body) = *val[2]
+
+ result = @builder.case(val[0], nil,
+ when_bodies, else_t, else_body,
+ val[3])
+ }
+ | kFOR for_var kIN
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.for(val[0], val[1],
+ val[2], val[4],
+ val[5], val[7], val[8])
+ }
+ | kCLASS cpath superclass
+ {
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ if in_def?
+ diagnostic :error, :class_in_def, nil, val[0]
+ end
+
+ lt_t, superclass = val[2]
+ result = @builder.def_class(val[0], val[1],
+ lt_t, superclass,
+ val[4], val[5])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ }
+ | kCLASS tLSHFT expr term
+ {
+ result = @def_level
+ @def_level = 0
+
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ result = @builder.def_sclass(val[0], val[1], val[2],
+ val[5], val[6])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+
+ @def_level = val[4]
+ }
+ | kMODULE cpath
+ {
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ if in_def?
+ diagnostic :error, :module_in_def, nil, val[0]
+ end
+
+ result = @builder.def_module(val[0], val[1],
+ val[3], val[4])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ }
+ | kDEF fname
+ {
+ @def_level += 1
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ f_arglist bodystmt kEND
+ {
+ result = @builder.def_method(val[0], val[1],
+ val[3], val[4], val[5])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ @def_level -= 1
+ }
+ | kDEF singleton dot_or_colon
+ {
+ @lexer.state = :expr_fname
+ }
+ fname
+ {
+ @def_level += 1
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ f_arglist bodystmt kEND
+ {
+ result = @builder.def_singleton(val[0], val[1], val[2],
+ val[4], val[6], val[7], val[8])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ @def_level -= 1
+ }
+ | kBREAK
+ {
+ result = @builder.keyword_cmd(:break, val[0])
+ }
+ | kNEXT
+ {
+ result = @builder.keyword_cmd(:next, val[0])
+ }
+ | kREDO
+ {
+ result = @builder.keyword_cmd(:redo, val[0])
+ }
+ | kRETRY
+ {
+ result = @builder.keyword_cmd(:retry, val[0])
+ }
+
+ primary_value: primary
+
+ then: term
+ | kTHEN
+ | term kTHEN
+ {
+ result = val[1]
+ }
+
+ do: term
+ | kDO_COND
+
+ if_tail: opt_else
+ | kELSIF expr_value then compstmt if_tail
+ {
+ else_t, else_ = val[4]
+ result = [ val[0],
+ @builder.condition(val[0], val[1], val[2],
+ val[3], else_t,
+ else_, nil),
+ ]
+ }
+
+ opt_else: none
+ | kELSE compstmt
+ {
+ result = val
+ }
+
+ for_var: lhs
+ | mlhs
+
+ f_marg: f_norm_arg
+ | tLPAREN f_margs rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ f_marg_list: f_marg
+ {
+ result = [ val[0] ]
+ }
+ | f_marg_list tCOMMA f_marg
+ {
+ result = val[0] << val[2]
+ }
+
+ f_margs: f_marg_list
+ | f_marg_list tCOMMA tSTAR f_norm_arg
+ {
+ result = val[0].
+ push(@builder.objc_restarg(val[2], val[3]))
+ }
+ | f_marg_list tCOMMA tSTAR f_norm_arg tCOMMA f_marg_list
+ {
+ result = val[0].
+ push(@builder.objc_restarg(val[2], val[3])).
+ concat(val[5])
+ }
+ | f_marg_list tCOMMA tSTAR
+ {
+ result = val[0].
+ push(@builder.objc_restarg(val[2]))
+ }
+ | f_marg_list tCOMMA tSTAR tCOMMA f_marg_list
+ {
+ result = val[0].
+ push(@builder.objc_restarg(val[2])).
+ concat(val[4])
+ }
+ | tSTAR f_norm_arg
+ {
+ result = [ @builder.objc_restarg(val[0], val[1]) ]
+ }
+ | tSTAR f_norm_arg tCOMMA f_marg_list
+ {
+ result = [ @builder.objc_restarg(val[0], val[1]),
+ *val[3] ]
+ }
+ | tSTAR
+ {
+ result = [ @builder.objc_restarg(val[0]) ]
+ }
+ | tSTAR tCOMMA f_marg_list
+ {
+ result = [ @builder.objc_restarg(val[0]),
+ *val[2] ]
+ }
+
+ block_param: f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[6]).
+ concat(val[7])
+ }
+ | f_arg tCOMMA f_block_optarg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_block_optarg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA
+ | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg opt_f_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_block_optarg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_block_optarg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_block_optarg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_block_arg
+ {
+ result = [ val[0] ]
+ }
+
+ opt_block_param: # nothing
+ {
+ result = @builder.args(nil, [], nil)
+ }
+ | block_param_def
+ {
+ @lexer.state = :expr_value
+ }
+
+ block_param_def: tPIPE opt_bv_decl tPIPE
+ {
+ result = @builder.args(val[0], val[1], val[2])
+ }
+ | tOROP
+ {
+ result = @builder.args(val[0], [], val[0])
+ }
+ | tPIPE block_param opt_bv_decl tPIPE
+ {
+ result = @builder.args(val[0], val[1].concat(val[2]), val[3])
+ }
+
+ opt_bv_decl: # nothing
+ {
+ result = []
+ }
+ | tSEMI bv_decls
+ {
+ result = val[1]
+ }
+
+ bv_decls: bvar
+ {
+ result = [ val[0] ]
+ }
+ | bv_decls tCOMMA bvar
+ {
+ result = val[0] << val[2]
+ }
+
+ bvar: tIDENTIFIER
+ {
+ result = @builder.shadowarg(val[0])
+ }
+ | f_bad_arg
+
+ lambda: {
+ @static_env.extend_dynamic
+ }
+ f_larglist lambda_body
+ {
+ result = [ val[1], val[2] ]
+
+ @static_env.unextend
+ }
+
+ f_larglist: tLPAREN2 f_args opt_bv_decl rparen
+ {
+ result = @builder.args(val[0], val[1].concat(val[2]), val[3])
+ }
+ | f_args
+ {
+ result = @builder.args(nil, val[0], nil)
+ }
+
+ lambda_body: tLAMBEG compstmt tRCURLY
+ {
+ result = [ val[0], val[1], val[2] ]
+ }
+ | kDO_LAMBDA compstmt kEND
+ {
+ result = [ val[0], val[1], val[2] ]
+ }
+
+ do_block: kDO_BLOCK
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt kEND
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ block_call: command do_block
+ {
+ begin_t, block_args, body, end_t = val[1]
+ result = @builder.block(val[0],
+ begin_t, block_args, body, end_t)
+ }
+ | block_call tDOT operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | block_call tCOLON2 operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+
+ method_call: operation paren_args
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.call_method(nil, nil, val[0],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tDOT operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 operation2 paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 operation3
+ {
+ result = @builder.call_method(val[0], val[1], val[2])
+ }
+ | primary_value tDOT paren_args
+ {
+ lparen_t, args, rparen_t = val[2]
+ result = @builder.call_method(val[0], val[1], nil,
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 paren_args
+ {
+ lparen_t, args, rparen_t = val[2]
+ result = @builder.call_method(val[0], val[1], nil,
+ lparen_t, args, rparen_t)
+ }
+ | kSUPER paren_args
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.keyword_cmd(:super, val[0],
+ lparen_t, args, rparen_t)
+ }
+ | kSUPER
+ {
+ result = @builder.keyword_cmd(:zsuper, val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index(val[0], val[1], val[2], val[3])
+ }
+
+ brace_block: tLCURLY
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt tRCURLY
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+ | kDO
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt kEND
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ case_body: kWHEN args then compstmt cases
+ {
+ result = [ @builder.when(val[0], val[1], val[2], val[3]),
+ *val[4] ]
+ }
+
+ cases: opt_else
+ {
+ result = [ val[0] ]
+ }
+ | case_body
+
+ opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue
+ {
+ assoc_t, exc_var = val[2]
+
+ if val[1]
+ exc_list = @builder.array(nil, val[1], nil)
+ end
+
+ result = [ @builder.rescue_body(val[0],
+ exc_list, assoc_t, exc_var,
+ val[3], val[4]),
+ *val[5] ]
+ }
+ |
+ {
+ result = []
+ }
+
+ exc_list: arg_value
+ {
+ result = [ val[0] ]
+ }
+ | mrhs
+ | none
+
+ exc_var: tASSOC lhs
+ {
+ result = [ val[0], val[1] ]
+ }
+ | none
+
+ opt_ensure: kENSURE compstmt
+ {
+ result = [ val[0], val[1] ]
+ }
+ | none
+
+ literal: numeric
+ | symbol
+ | dsym
+
+ strings: string
+ {
+ result = @builder.string_compose(nil, val[0], nil)
+ }
+
+ string: string1
+ {
+ result = [ val[0] ]
+ }
+ | string string1
+ {
+ result = val[0] << val[1]
+ }
+
+ string1: tSTRING_BEG string_contents tSTRING_END
+ {
+ result = @builder.string_compose(val[0], val[1], val[2])
+ }
+ | tSTRING
+ {
+ result = @builder.string(val[0])
+ }
+ | tCHARACTER
+ {
+ result = @builder.character(val[0])
+ }
+
+ xstring: tXSTRING_BEG xstring_contents tSTRING_END
+ {
+ result = @builder.xstring_compose(val[0], val[1], val[2])
+ }
+
+ regexp: tREGEXP_BEG regexp_contents tSTRING_END tREGEXP_OPT
+ {
+ opts = @builder.regexp_options(val[3])
+ result = @builder.regexp_compose(val[0], val[1], val[2], opts)
+ }
+
+ words: tWORDS_BEG word_list tSTRING_END
+ {
+ result = @builder.words_compose(val[0], val[1], val[2])
+ }
+
+ word_list: # nothing
+ {
+ result = []
+ }
+ | word_list word tSPACE
+ {
+ result = val[0] << @builder.word(val[1])
+ }
+
+ word: string_content
+ {
+ result = [ val[0] ]
+ }
+ | word string_content
+ {
+ result = val[0] << val[1]
+ }
+
+ qwords: tQWORDS_BEG qword_list tSTRING_END
+ {
+ result = @builder.words_compose(val[0], val[1], val[2])
+ }
+
+ qword_list: # nothing
+ {
+ result = []
+ }
+ | qword_list tSTRING_CONTENT tSPACE
+ {
+ result = val[0] << @builder.string_internal(val[1])
+ }
+
+ string_contents: # nothing
+ {
+ result = []
+ }
+ | string_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+xstring_contents: # nothing
+ {
+ result = []
+ }
+ | xstring_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+regexp_contents: # nothing
+ {
+ result = []
+ }
+ | regexp_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+ string_content: tSTRING_CONTENT
+ {
+ result = @builder.string_internal(val[0])
+ }
+ | tSTRING_DVAR string_dvar
+ {
+ result = val[1]
+ }
+ | tSTRING_DBEG
+ {
+ @lexer.cond.push(false)
+ @lexer.cmdarg.push(false)
+ }
+ compstmt tRCURLY
+ {
+ @lexer.cond.lexpop
+ @lexer.cmdarg.lexpop
+
+ result = @builder.begin(val[0], val[2], val[3])
+ }
+
+ string_dvar: tGVAR
+ {
+ result = @builder.gvar(val[0])
+ }
+ | tIVAR
+ {
+ result = @builder.ivar(val[0])
+ }
+ | tCVAR
+ {
+ result = @builder.cvar(val[0])
+ }
+ | backref
+
+
+ symbol: tSYMBOL
+ {
+ result = @builder.symbol(val[0])
+ }
+
+ dsym: tSYMBEG xstring_contents tSTRING_END
+ {
+ result = @builder.symbol_compose(val[0], val[1], val[2])
+ }
+
+ numeric: tINTEGER
+ {
+ result = @builder.integer(val[0])
+ }
+ | tFLOAT
+ {
+ result = @builder.float(val[0])
+ }
+ | tUMINUS_NUM tINTEGER =tLOWEST
+ {
+ result = @builder.negate(val[0],
+ @builder.integer(val[1]))
+ }
+ | tUMINUS_NUM tFLOAT =tLOWEST
+ {
+ result = @builder.negate(val[0],
+ @builder.float(val[1]))
+ }
+
+ variable: tIDENTIFIER
+ {
+ result = @builder.ident(val[0])
+ }
+ | tIVAR
+ {
+ result = @builder.ivar(val[0])
+ }
+ | tGVAR
+ {
+ result = @builder.gvar(val[0])
+ }
+ | tCONSTANT
+ {
+ result = @builder.const(val[0])
+ }
+ | tCVAR
+ {
+ result = @builder.cvar(val[0])
+ }
+ | kNIL
+ {
+ result = @builder.nil(val[0])
+ }
+ | kSELF
+ {
+ result = @builder.self(val[0])
+ }
+ | kTRUE
+ {
+ result = @builder.true(val[0])
+ }
+ | kFALSE
+ {
+ result = @builder.false(val[0])
+ }
+ | k__FILE__
+ {
+ result = @builder.__FILE__(val[0])
+ }
+ | k__LINE__
+ {
+ result = @builder.__LINE__(val[0])
+ }
+ | k__ENCODING__
+ {
+ result = @builder.__ENCODING__(val[0])
+ }
+
+ var_ref: variable
+ {
+ result = @builder.accessible(val[0])
+ }
+
+ var_lhs: variable
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ backref: tNTH_REF
+ {
+ result = @builder.nth_ref(val[0])
+ }
+ | tBACK_REF
+ {
+ result = @builder.back_ref(val[0])
+ }
+
+ superclass: term
+ {
+ result = nil
+ }
+ | tLT expr_value term
+ {
+ result = [ val[0], val[1] ]
+ }
+ | error term
+ {
+ yyerrok
+ result = nil
+ }
+
+ f_arglist: tLPAREN2 f_args rparen
+ {
+ result = @builder.args(val[0], val[1], val[2])
+
+ @lexer.state = :expr_value
+ }
+ | f_args term
+ {
+ result = @builder.args(nil, val[0], nil)
+ }
+
+ f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[6]).
+ concat(val[7])
+ }
+ | f_arg tCOMMA f_optarg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_optarg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_optarg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_optarg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_optarg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_block_arg
+ {
+ result = [ val[0] ]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ f_bad_arg: tCONSTANT
+ {
+ diagnostic :error, :argument_const, nil, val[0]
+ }
+ | tIVAR
+ {
+ diagnostic :error, :argument_ivar, nil, val[0]
+ }
+ | tGVAR
+ {
+ diagnostic :error, :argument_gvar, nil, val[0]
+ }
+ | tCVAR
+ {
+ diagnostic :error, :argument_cvar, nil, val[0]
+ }
+
+ f_norm_arg: f_bad_arg
+ | tIDENTIFIER
+ {
+ @static_env.declare val[0][0]
+
+ result = @builder.arg(val[0])
+ }
+ | tIDENTIFIER tASSOC tIDENTIFIER
+ {
+ @static_env.declare val[2][0]
+
+ result = @builder.objc_kwarg(val[0], val[1], val[2])
+ }
+ | tLABEL tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = @builder.objc_kwarg(val[0], nil, val[1])
+ }
+
+ f_arg_item: f_norm_arg
+ | tLPAREN f_margs rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ f_arg: f_arg_item
+ {
+ result = [ val[0] ]
+ }
+ | f_arg tCOMMA f_arg_item
+ {
+ result = val[0] << val[2]
+ }
+
+ f_opt: tIDENTIFIER tEQL arg_value
+ {
+ @static_env.declare val[0][0]
+
+ result = @builder.optarg(val[0], val[1], val[2])
+ }
+
+ f_block_opt: tIDENTIFIER tEQL primary_value
+ {
+ @static_env.declare val[0][0]
+
+ result = @builder.optarg(val[0], val[1], val[2])
+ }
+
+ f_block_optarg: f_block_opt
+ {
+ result = [ val[0] ]
+ }
+ | f_block_optarg tCOMMA f_block_opt
+ {
+ result = val[0] << val[2]
+ }
+
+ f_optarg: f_opt
+ {
+ result = [ val[0] ]
+ }
+ | f_optarg tCOMMA f_opt
+ {
+ result = val[0] << val[2]
+ }
+
+ restarg_mark: tSTAR2 | tSTAR
+
+ f_rest_arg: restarg_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = [ @builder.restarg(val[0], val[1]) ]
+ }
+ | restarg_mark
+ {
+ result = [ @builder.restarg(val[0]) ]
+ }
+
+ blkarg_mark: tAMPER2 | tAMPER
+
+ f_block_arg: blkarg_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = @builder.blockarg(val[0], val[1])
+ }
+
+ opt_f_block_arg: tCOMMA f_block_arg
+ {
+ result = [ val[1] ]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ singleton: var_ref
+ | tLPAREN2 expr rparen
+ {
+ result = val[1]
+ }
+
+ assoc_list: # nothing
+ {
+ result = []
+ }
+ | assocs trailer
+
+ assocs: assoc
+ {
+ result = [ val[0] ]
+ }
+ | assocs tCOMMA assoc
+ {
+ result = val[0] << val[2]
+ }
+
+ assoc: arg_value tASSOC arg_value
+ {
+ result = @builder.pair(val[0], val[1], val[2])
+ }
+ | tLABEL arg_value
+ {
+ result = @builder.pair_keyword(val[0], val[1])
+ }
+
+ operation: tIDENTIFIER | tCONSTANT | tFID
+ operation2: tIDENTIFIER | tCONSTANT | tFID | op
+ operation3: tIDENTIFIER | tFID | op
+ dot_or_colon: tDOT | tCOLON2
+ opt_terms: | terms
+ opt_nl: | tNL
+ rparen: opt_nl tRPAREN
+ {
+ result = val[1]
+ }
+ rbracket: opt_nl tRBRACK
+ {
+ result = val[1]
+ }
+ trailer: | tNL | tCOMMA
+
+ term: tSEMI
+ {
+ yyerrok
+ }
+ | tNL
+
+ terms: term
+ | terms tSEMI
+
+ none: # nothing
+ {
+ result = nil
+ }
+end
+
+---- header
+
+require 'parser'
+
+Parser.check_for_encoding_support
+
+---- inner
+
+ def version
+ 19 # closest released match: v1_9_0_2
+ end
+
+ def default_encoding
+ Encoding::BINARY
+ end
diff --git a/test/racc/assets/mailp.y b/test/racc/assets/mailp.y
new file mode 100644
index 0000000000..da332a33ba
--- /dev/null
+++ b/test/racc/assets/mailp.y
@@ -0,0 +1,437 @@
+#
+# mailp for test
+#
+
+class Testp
+
+rule
+
+ content : DateH datetime { @field.date = val[1] }
+ | RecvH received
+ | RetpathH returnpath
+ | MaddrH addrs { @field.addrs.replace val[1] }
+ | SaddrH addr { @field.addr = val[1] }
+ | MmboxH mboxes { @field.addrs.replace val[1] }
+ | SmboxH mbox { @field.addr = val[1] }
+ | MsgidH msgid { @field.msgid = val[1] }
+ | KeyH keys { @field.keys.replace val[1] }
+ | EncH enc
+ | VersionH version
+ | CTypeH ctype
+ | CEncodingH cencode
+ | CDispositionH cdisp
+ | Mbox mbox
+ {
+ mb = val[1]
+ @field.phrase = mb.phrase
+ @field.setroute mb.route
+ @field.local = mb.local
+ @field.domain = mb.domain
+ }
+ | Spec spec
+ {
+ mb = val[1]
+ @field.local = mb.local
+ @field.domain = mb.domain
+ }
+ ;
+
+ datetime : day DIGIT ATOM DIGIT hour zone
+ # 0 1 2 3 4 5
+ # day month year
+ {
+ t = Time.gm( val[3].to_i, val[2], val[1].to_i, 0, 0, 0 )
+ result = (t + val[4] - val[5]).localtime
+ }
+ ;
+
+ day : /* none */
+ | ATOM ','
+ ;
+
+ hour : DIGIT ':' DIGIT
+ {
+ result = (result.to_i * 60 * 60) + (val[2].to_i * 60)
+ }
+ | DIGIT ':' DIGIT ':' DIGIT
+ {
+ result = (result.to_i * 60 * 60) +
+ (val[2].to_i * 60)
+ + val[4].to_i
+ }
+ ;
+
+ zone : ATOM
+ {
+ result = ::TMail.zonestr2i( val[0] ) * 60
+ }
+ ;
+
+ received : from by via with id for recvdatetime
+ ;
+
+ from : /* none */
+ | FROM domain
+ {
+ @field.from = Address.join( val[1] )
+ }
+ | FROM domain '@' domain
+ {
+ @field.from = Address.join( val[3] )
+ }
+ | FROM domain DOMLIT
+ {
+ @field.from = Address.join( val[1] )
+ }
+ ;
+
+ by : /* none */
+ | BY domain
+ {
+ @field.by = Address.join( val[1] )
+ }
+ ;
+
+ via : /* none */
+ | VIA ATOM
+ {
+ @field.via = val[1]
+ }
+ ;
+
+ with : /* none */
+ | WITH ATOM
+ {
+ @field.with.push val[1]
+ }
+ ;
+
+ id : /* none */
+ | ID msgid
+ {
+ @field.msgid = val[1]
+ }
+ | ID ATOM
+ {
+ @field.msgid = val[1]
+ }
+ ;
+
+ for : /* none */
+ | FOR addr
+ {
+ @field.for_ = val[1].address
+ }
+ ;
+
+ recvdatetime
+ : /* none */
+ | ';' datetime
+ {
+ @field.date = val[1]
+ }
+ ;
+
+ returnpath: '<' '>'
+ | routeaddr
+ {
+ @field.route.replace result.route
+ @field.addr = result.addr
+ }
+ ;
+
+ addrs : addr { result = val }
+ | addrs ',' addr { result.push val[2] }
+ ;
+
+ addr : mbox
+ | group
+ ;
+
+ mboxes : mbox
+ {
+ result = val
+ }
+ | mboxes ',' mbox
+ {
+ result.push val[2]
+ }
+ ;
+
+ mbox : spec
+ | routeaddr
+ | phrase routeaddr
+ {
+ val[1].phrase = HFdecoder.decode( result )
+ result = val[1]
+ }
+ ;
+
+ group : phrase ':' mboxes ';'
+ {
+ result = AddressGroup.new( result, val[2] )
+ }
+ # | phrase ':' ';' { result = AddressGroup.new( result ) }
+ ;
+
+ routeaddr : '<' route spec '>'
+ {
+ result = val[2]
+ result.route = val[1]
+ }
+ | '<' spec '>'
+ {
+ result = val[1]
+ }
+ ;
+
+ route : at_domains ':'
+ ;
+
+ at_domains: '@' domain { result = [ val[1] ] }
+ | at_domains ',' '@' domain { result.push val[3] }
+ ;
+
+ spec : local '@' domain { result = Address.new( val[0], val[2] ) }
+ | local { result = Address.new( result, nil ) }
+ ;
+
+ local : word { result = val }
+ | local '.' word { result.push val[2] }
+ ;
+
+ domain : domword { result = val }
+ | domain '.' domword { result.push val[2] }
+ ;
+
+ domword : atom
+ | DOMLIT
+ | DIGIT
+ ;
+
+ msgid : '<' spec '>'
+ {
+ val[1] = val[1].addr
+ result = val.join('')
+ }
+ ;
+
+ phrase : word
+ | phrase word { result << ' ' << val[1] }
+ ;
+
+ word : atom
+ | QUOTED
+ | DIGIT
+ ;
+
+ keys : phrase
+ | keys ',' phrase
+ ;
+
+ enc : word
+ {
+ @field.encrypter = val[0]
+ }
+ | word word
+ {
+ @field.encrypter = val[0]
+ @field.keyword = val[1]
+ }
+ ;
+
+ version : DIGIT '.' DIGIT
+ {
+ @field.major = val[0].to_i
+ @field.minor = val[2].to_i
+ }
+ ;
+
+ ctype : TOKEN '/' TOKEN params
+ {
+ @field.main = val[0]
+ @field.sub = val[2]
+ }
+ | TOKEN params
+ {
+ @field.main = val[0]
+ @field.sub = ''
+ }
+ ;
+
+ params : /* none */
+ | params ';' TOKEN '=' value
+ {
+ @field.params[ val[2].downcase ] = val[4]
+ }
+ ;
+
+ value : TOKEN
+ | QUOTED
+ ;
+
+ cencode : TOKEN
+ {
+ @field.encoding = val[0]
+ }
+ ;
+
+ cdisp : TOKEN disp_params
+ {
+ @field.disposition = val[0]
+ }
+ ;
+
+ disp_params
+ : /* none */
+ | disp_params ';' disp_param
+ ;
+
+ disp_param: /* none */
+ | TOKEN '=' value
+ {
+ @field.params[ val[0].downcase ] = val[2]
+ }
+ ;
+
+ atom : ATOM
+ | FROM
+ | BY
+ | VIA
+ | WITH
+ | ID
+ | FOR
+ ;
+
+end
+
+
+---- header
+#
+# mailp for test
+#
+
+require 'tmail/mails'
+
+
+module TMail
+
+---- inner
+
+ MAILP_DEBUG = false
+
+ def initialize
+ self.debug = MAILP_DEBUG
+ end
+
+ def debug=( flag )
+ @yydebug = flag && Racc_debug_parser
+ @scanner_debug = flag
+ end
+
+ def debug
+ @yydebug
+ end
+
+
+ def Mailp.parse( str, obj, ident )
+ new.parse( str, obj, ident )
+ end
+
+
+ NATIVE_ROUTINE = {
+ 'TMail::MsgidH' => :msgid_parse,
+ 'TMail::RefH' => :refs_parse
+ }
+
+ def parse( str, obj, ident )
+ return if /\A\s*\z/ === str
+
+ @field = obj
+
+ if mid = NATIVE_ROUTINE[ obj.type.name ] then
+ send mid, str
+ else
+ unless ident then
+ ident = obj.type.name.split('::')[-1].to_s
+ cmt = []
+ obj.comments.replace cmt
+ else
+ cmt = nil
+ end
+
+ @scanner = MailScanner.new( str, ident, cmt )
+ @scanner.debug = @scanner_debug
+ @first = [ ident.intern, ident ]
+ @pass_array = [nil, nil]
+
+ do_parse
+ end
+ end
+
+
+ private
+
+
+ def next_token
+ if @first then
+ ret = @first
+ @first = nil
+ ret
+ else
+ @scanner.scan @pass_array
+ end
+ end
+
+ def on_error( tok, val, vstack )
+ raise ParseError,
+ "\nparse error in '#{@field.name}' header, on token #{val.inspect}"
+ end
+
+
+
+ def refs_parse( str )
+ arr = []
+
+ while mdata = ::TMail::MSGID.match( str ) do
+ str = mdata.post_match
+
+ pre = mdata.pre_match
+ pre.strip!
+ proc_phrase pre, arr unless pre.empty?
+ arr.push mdata.to_s
+ end
+ str.strip!
+ proc_phrase str, arr if not pre or pre.empty?
+
+ @field.refs.replace arr
+ end
+
+ def proc_phrase( str, arr )
+ while mdata = /"([^\\]*(?:\\.[^"\\]*)*)"/.match( str ) do
+ str = mdata.post_match
+
+ pre = mdata.pre_match
+ pre.strip!
+ arr.push pre unless pre.empty?
+ arr.push mdata[1]
+ end
+ str.strip!
+ arr.push unless str.empty?
+ end
+
+
+ def msgid_parse( str )
+ if mdata = ::TMail::MSGID.match( str ) then
+ @field.msgid = mdata.to_s
+ else
+ raise ParseError, "wrong Message-ID format: #{str}"
+ end
+ end
+
+---- footer
+
+end # module TMail
+
+mp = TMail::Testp.new
+mp.parse
diff --git a/test/racc/assets/mediacloth.y b/test/racc/assets/mediacloth.y
new file mode 100644
index 0000000000..94cc411ea7
--- /dev/null
+++ b/test/racc/assets/mediacloth.y
@@ -0,0 +1,599 @@
+# Copyright (c) 2006 Pluron Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+# The parser for the MediaWiki language.
+#
+# Usage together with a lexer:
+# inputFile = File.new("data/input1", "r")
+# input = inputFile.read
+# parser = MediaWikiParser.new
+# parser.lexer = MediaWikiLexer.new
+# parser.parse(input)
+
+class MediaWikiParser
+
+token TEXT BOLD_START BOLD_END ITALIC_START ITALIC_END LINK_START LINK_END LINKSEP
+ INTLINK_START INTLINK_END INTLINKSEP RESOURCESEP CHAR_ENT
+ PRE_START PRE_END PREINDENT_START PREINDENT_END
+ SECTION_START SECTION_END HLINE SIGNATURE_NAME SIGNATURE_DATE SIGNATURE_FULL
+ PARA_START PARA_END UL_START UL_END OL_START OL_END LI_START LI_END
+ DL_START DL_END DT_START DT_END DD_START DD_END TAG_START TAG_END ATTR_NAME ATTR_VALUE
+ TABLE_START TABLE_END ROW_START ROW_END HEAD_START HEAD_END CELL_START CELL_END
+ KEYWORD TEMPLATE_START TEMPLATE_END CATEGORY PASTE_START PASTE_END
+
+
+rule
+
+wiki:
+ repeated_contents
+ {
+ @nodes.push WikiAST.new(0, @wiki_ast_length)
+ #@nodes.last.children.insert(0, val[0])
+ #puts val[0]
+ @nodes.last.children += val[0]
+ }
+ ;
+
+contents:
+ text
+ {
+ result = val[0]
+ }
+ | bulleted_list
+ {
+ result = val[0]
+ }
+ | numbered_list
+ {
+ result = val[0]
+ }
+ | dictionary_list
+ {
+ list = ListAST.new(@ast_index, @ast_length)
+ list.list_type = :Dictionary
+ list.children = val[0]
+ result = list
+ }
+ | preformatted
+ {
+ result = val[0]
+ }
+ | section
+ {
+ result = val[0]
+ }
+ | tag
+ {
+ result = val[0]
+ }
+ | template
+ {
+ result = val[0]
+ }
+ | KEYWORD
+ {
+ k = KeywordAST.new(@ast_index, @ast_length)
+ k.text = val[0]
+ result = k
+ }
+ | PARA_START para_contents PARA_END
+ {
+ p = ParagraphAST.new(@ast_index, @ast_length)
+ p.children = val[1]
+ result = p
+ }
+ | LINK_START link_contents LINK_END
+ {
+ l = LinkAST.new(@ast_index, @ast_length)
+ l.link_type = val[0]
+ l.url = val[1][0]
+ l.children += val[1][1..-1] if val[1].length > 1
+ result = l
+ }
+ | PASTE_START para_contents PASTE_END
+ {
+ p = PasteAST.new(@ast_index, @ast_length)
+ p.children = val[1]
+ result = p
+ }
+ | INTLINK_START TEXT RESOURCESEP TEXT reslink_repeated_contents INTLINK_END
+ {
+ l = ResourceLinkAST.new(@ast_index, @ast_length)
+ l.prefix = val[1]
+ l.locator = val[3]
+ l.children = val[4] unless val[4].nil? or val[4].empty?
+ result = l
+ }
+ | INTLINK_START TEXT intlink_repeated_contents INTLINK_END
+ {
+ l = InternalLinkAST.new(@ast_index, @ast_length)
+ l.locator = val[1]
+ l.children = val[2] unless val[2].nil? or val[2].empty?
+ result = l
+ }
+ | INTLINK_START CATEGORY TEXT cat_sort_contents INTLINK_END
+ {
+ l = CategoryAST.new(@ast_index, @ast_length)
+ l.locator = val[2]
+ l.sort_as = val[3]
+ result = l
+ }
+ | INTLINK_START RESOURCESEP CATEGORY TEXT intlink_repeated_contents INTLINK_END
+ {
+ l = CategoryLinkAST.new(@ast_index, @ast_length)
+ l.locator = val[3]
+ l.children = val[4] unless val[4].nil? or val[4].empty?
+ result = l
+ }
+ | table
+ ;
+
+para_contents:
+ {
+ result = nil
+ }
+ | repeated_contents
+ {
+ result = val[0]
+ }
+ ;
+
+tag:
+ TAG_START tag_attributes TAG_END
+ {
+ if val[0] != val[2]
+ raise Racc::ParseError.new("XHTML end tag #{val[2]} does not match start tag #{val[0]}")
+ end
+ elem = ElementAST.new(@ast_index, @ast_length)
+ elem.name = val[0]
+ elem.attributes = val[1]
+ result = elem
+ }
+ | TAG_START tag_attributes repeated_contents TAG_END
+ {
+ if val[0] != val[3]
+ raise Racc::ParseError.new("XHTML end tag #{val[3]} does not match start tag #{val[0]}")
+ end
+ elem = ElementAST.new(@ast_index, @ast_length)
+ elem.name = val[0]
+ elem.attributes = val[1]
+ elem.children += val[2]
+ result = elem
+ }
+ ;
+
+tag_attributes:
+ {
+ result = nil
+ }
+ | ATTR_NAME tag_attributes
+ {
+ attr_map = val[2] ? val[2] : {}
+ attr_map[val[0]] = true
+ result = attr_map
+ }
+ | ATTR_NAME ATTR_VALUE tag_attributes
+ {
+ attr_map = val[2] ? val[2] : {}
+ attr_map[val[0]] = val[1]
+ result = attr_map
+ }
+ ;
+
+
+link_contents:
+ TEXT
+ {
+ result = val
+ }
+ | TEXT LINKSEP link_repeated_contents
+ {
+ result = [val[0]]
+ result += val[2]
+ }
+ ;
+
+
+link_repeated_contents:
+ repeated_contents
+ {
+ result = val[0]
+ }
+ | repeated_contents LINKSEP link_repeated_contents
+ {
+ result = val[0]
+ result += val[2] if val[2]
+ }
+ ;
+
+
+intlink_repeated_contents:
+ {
+ result = nil
+ }
+ | INTLINKSEP repeated_contents
+ {
+ result = val[1]
+ }
+ ;
+
+cat_sort_contents:
+ {
+ result = nil
+ }
+ | INTLINKSEP TEXT
+ {
+ result = val[1]
+ }
+ ;
+
+reslink_repeated_contents:
+ {
+ result = nil
+ }
+ | INTLINKSEP reslink_repeated_contents
+ {
+ result = val[1]
+ }
+ | INTLINKSEP repeated_contents reslink_repeated_contents
+ {
+ i = InternalLinkItemAST.new(@ast_index, @ast_length)
+ i.children = val[1]
+ result = [i]
+ result += val[2] if val[2]
+ }
+ ;
+
+repeated_contents: contents
+ {
+ result = []
+ result << val[0]
+ }
+ | repeated_contents contents
+ {
+ result = []
+ result += val[0]
+ result << val[1]
+ }
+ ;
+
+text: element
+ {
+ p = TextAST.new(@ast_index, @ast_length)
+ p.formatting = val[0][0]
+ p.contents = val[0][1]
+ result = p
+ }
+ | formatted_element
+ {
+ result = val[0]
+ }
+ ;
+
+table:
+ TABLE_START table_contents TABLE_END
+ {
+ table = TableAST.new(@ast_index, @ast_length)
+ table.children = val[1] unless val[1].nil? or val[1].empty?
+ result = table
+ }
+ | TABLE_START TEXT table_contents TABLE_END
+ {
+ table = TableAST.new(@ast_index, @ast_length)
+ table.options = val[1]
+ table.children = val[2] unless val[2].nil? or val[2].empty?
+ result = table
+ }
+
+table_contents:
+ {
+ result = nil
+ }
+ | ROW_START row_contents ROW_END table_contents
+ {
+ row = TableRowAST.new(@ast_index, @ast_length)
+ row.children = val[1] unless val[1].nil? or val[1].empty?
+ result = [row]
+ result += val[3] unless val[3].nil? or val[3].empty?
+ }
+ | ROW_START TEXT row_contents ROW_END table_contents
+ {
+ row = TableRowAST.new(@ast_index, @ast_length)
+ row.children = val[2] unless val[2].nil? or val[2].empty?
+ row.options = val[1]
+ result = [row]
+ result += val[4] unless val[4].nil? or val[4].empty?
+ }
+
+row_contents:
+ {
+ result = nil
+ }
+ | HEAD_START HEAD_END row_contents
+ {
+ cell = TableCellAST.new(@ast_index, @ast_length)
+ cell.type = :head
+ result = [cell]
+ result += val[2] unless val[2].nil? or val[2].empty?
+ }
+ | HEAD_START repeated_contents HEAD_END row_contents
+ {
+ cell = TableCellAST.new(@ast_index, @ast_length)
+ cell.children = val[1] unless val[1].nil? or val[1].empty?
+ cell.type = :head
+ result = [cell]
+ result += val[3] unless val[3].nil? or val[3].empty?
+ }
+ | CELL_START CELL_END row_contents
+ {
+ cell = TableCellAST.new(@ast_index, @ast_length)
+ cell.type = :body
+ result = [cell]
+ result += val[2] unless val[2].nil? or val[2].empty?
+ }
+ | CELL_START repeated_contents CELL_END row_contents
+ {
+ if val[2] == 'attributes'
+ result = []
+ else
+ cell = TableCellAST.new(@ast_index, @ast_length)
+ cell.children = val[1] unless val[1].nil? or val[1].empty?
+ cell.type = :body
+ result = [cell]
+ end
+ result += val[3] unless val[3].nil? or val[3].empty?
+ if val[2] == 'attributes' and val[3] and val[3].first.class == TableCellAST
+ val[3].first.attributes = val[1]
+ end
+ result
+ }
+
+
+element:
+ TEXT
+ { return [:None, val[0]] }
+ | HLINE
+ { return [:HLine, val[0]] }
+ | CHAR_ENT
+ { return [:CharacterEntity, val[0]] }
+ | SIGNATURE_DATE
+ { return [:SignatureDate, val[0]] }
+ | SIGNATURE_NAME
+ { return [:SignatureName, val[0]] }
+ | SIGNATURE_FULL
+ { return [:SignatureFull, val[0]] }
+ ;
+
+formatted_element:
+ BOLD_START BOLD_END
+ {
+ result = FormattedAST.new(@ast_index, @ast_length)
+ result.formatting = :Bold
+ result
+ }
+ | ITALIC_START ITALIC_END
+ {
+ result = FormattedAST.new(@ast_index, @ast_length)
+ result.formatting = :Italic
+ result
+ }
+ | BOLD_START repeated_contents BOLD_END
+ {
+ p = FormattedAST.new(@ast_index, @ast_length)
+ p.formatting = :Bold
+ p.children += val[1]
+ result = p
+ }
+ | ITALIC_START repeated_contents ITALIC_END
+ {
+ p = FormattedAST.new(@ast_index, @ast_length)
+ p.formatting = :Italic
+ p.children += val[1]
+ result = p
+ }
+ ;
+
+bulleted_list: UL_START list_item list_contents UL_END
+ {
+ list = ListAST.new(@ast_index, @ast_length)
+ list.list_type = :Bulleted
+ list.children << val[1]
+ list.children += val[2]
+ result = list
+ }
+ ;
+
+numbered_list: OL_START list_item list_contents OL_END
+ {
+ list = ListAST.new(@ast_index, @ast_length)
+ list.list_type = :Numbered
+ list.children << val[1]
+ list.children += val[2]
+ result = list
+ }
+ ;
+
+list_contents:
+ { result = [] }
+ list_item list_contents
+ {
+ result << val[1]
+ result += val[2]
+ }
+ |
+ { result = [] }
+ ;
+
+list_item:
+ LI_START LI_END
+ {
+ result = ListItemAST.new(@ast_index, @ast_length)
+ }
+ | LI_START repeated_contents LI_END
+ {
+ li = ListItemAST.new(@ast_index, @ast_length)
+ li.children += val[1]
+ result = li
+ }
+ ;
+
+dictionary_list:
+ DL_START dictionary_term dictionary_contents DL_END
+ {
+ result = [val[1]]
+ result += val[2]
+ }
+ | DL_START dictionary_contents DL_END
+ {
+ result = val[1]
+ }
+ ;
+
+dictionary_term:
+ DT_START DT_END
+ {
+ result = ListTermAST.new(@ast_index, @ast_length)
+ }
+ | DT_START repeated_contents DT_END
+ {
+ term = ListTermAST.new(@ast_index, @ast_length)
+ term.children += val[1]
+ result = term
+ }
+
+dictionary_contents:
+ dictionary_definition dictionary_contents
+ {
+ result = [val[0]]
+ result += val[1] if val[1]
+ }
+ |
+ {
+ result = []
+ }
+
+dictionary_definition:
+ DD_START DD_END
+ {
+ result = ListDefinitionAST.new(@ast_index, @ast_length)
+ }
+ | DD_START repeated_contents DD_END
+ {
+ term = ListDefinitionAST.new(@ast_index, @ast_length)
+ term.children += val[1]
+ result = term
+ }
+
+preformatted: PRE_START repeated_contents PRE_END
+ {
+ p = PreformattedAST.new(@ast_index, @ast_length)
+ p.children += val[1]
+ result = p
+ }
+ | PREINDENT_START repeated_contents PREINDENT_END
+ {
+ p = PreformattedAST.new(@ast_index, @ast_length)
+ p.indented = true
+ p.children += val[1]
+ result = p
+ }
+ ;
+
+section: SECTION_START repeated_contents SECTION_END
+ { result = [val[1], val[0].length]
+ s = SectionAST.new(@ast_index, @ast_length)
+ s.children = val[1]
+ s.level = val[0].length
+ result = s
+ }
+ ;
+
+template: TEMPLATE_START TEXT template_parameters TEMPLATE_END
+ {
+ t = TemplateAST.new(@ast_index, @ast_length)
+ t.template_name = val[1]
+ t.children = val[2] unless val[2].nil? or val[2].empty?
+ result = t
+ }
+ ;
+
+template_parameters:
+ {
+ result = nil
+ }
+ | INTLINKSEP TEXT template_parameters
+ {
+ p = TemplateParameterAST.new(@ast_index, @ast_length)
+ p.parameter_value = val[1]
+ result = [p]
+ result += val[2] if val[2]
+ }
+ | INTLINKSEP template template_parameters
+ {
+ p = TemplateParameterAST.new(@ast_index, @ast_length)
+ p.children << val[1]
+ result = [p]
+ result += val[2] if val[2]
+ }
+ ;
+
+end
+
+---- header ----
+require 'mediacloth/mediawikiast'
+
+---- inner ----
+
+attr_accessor :lexer
+
+def initialize
+ @nodes = []
+ @context = []
+ @wiki_ast_length = 0
+ super
+end
+
+#Tokenizes input string and parses it.
+def parse(input)
+ @yydebug=true
+ lexer.tokenize(input)
+ do_parse
+ return @nodes.last
+end
+
+#Asks the lexer to return the next token.
+def next_token
+ token = @lexer.lex
+ if token[0].to_s.upcase.include? "_START"
+ @context << token[2..3]
+ elsif token[0].to_s.upcase.include? "_END"
+ @ast_index = @context.last[0]
+ @ast_length = token[2] + token[3] - @context.last[0]
+ @context.pop
+ else
+ @ast_index = token[2]
+ @ast_length = token[3]
+ end
+
+ @wiki_ast_length += token[3]
+
+ return token[0..1]
+end
diff --git a/test/racc/assets/mof.y b/test/racc/assets/mof.y
new file mode 100644
index 0000000000..1adc5ade14
--- /dev/null
+++ b/test/racc/assets/mof.y
@@ -0,0 +1,649 @@
+# Distributed under the Ruby license
+# See http://www.ruby-lang.org/en/LICENSE.txt for the full license text
+# Copyright (c) 2010 Klaus Kämpf <kkaempf@suse.de>
+
+/*
+ * According to appendix A of
+ * http://www.dmtf.org/standards/cim/cim_spec_v22
+ */
+
+class MOF::Parser
+ prechigh
+/* nonassoc UMINUS */
+ left '*' '/'
+ left '+' '-'
+ preclow
+
+ token PRAGMA INCLUDE IDENTIFIER CLASS ASSOCIATION INDICATION
+ AMENDED ENABLEOVERRIDE DISABLEOVERRIDE RESTRICTED TOSUBCLASS TOINSTANCE
+ TRANSLATABLE QUALIFIER SCOPE SCHEMA PROPERTY REFERENCE
+ METHOD PARAMETER FLAVOR INSTANCE
+ AS REF ANY OF
+ DT_VOID
+ DT_UINT8 DT_SINT8 DT_UINT16 DT_SINT16 DT_UINT32 DT_SINT32
+ DT_UINT64 DT_SINT64 DT_REAL32 DT_REAL64 DT_CHAR16 DT_STR
+ DT_BOOLEAN DT_DATETIME
+ positiveDecimalValue
+ stringValue
+ realValue
+ charValue
+ booleanValue
+ nullValue
+ binaryValue
+ octalValue
+ decimalValue
+ hexValue
+
+rule
+
+ /* Returns a Hash of filename and MofResult */
+ mofSpecification
+ : /* empty */
+ { result = Hash.new }
+ | mofProduction
+ { result = { @name => @result } }
+ | mofSpecification mofProduction
+ { result = val[0]
+ result[@name] = @result
+ }
+ ;
+
+ mofProduction
+ : compilerDirective
+ | classDeclaration
+ { #puts "Class '#{val[0].name}'"
+ @result.classes << val[0]
+ }
+ | qualifierDeclaration
+ { @result.qualifiers << val[0]
+ @qualifiers[val[0].name.downcase] = val[0]
+ }
+ | instanceDeclaration
+ { @result.instances << val[0] }
+ ;
+
+/***
+ * compilerDirective
+ *
+ */
+
+ compilerDirective
+ : "#" PRAGMA INCLUDE pragmaParameters_opt
+ { raise MOF::Helper::Error.new(@name,@lineno,@line,"Missing filename after '#pragma include'") unless val[3]
+ open val[3], :pragma
+ }
+ | "#" PRAGMA pragmaName pragmaParameters_opt
+ | "#" INCLUDE pragmaParameters_opt
+ { raise StyleError.new(@name,@lineno,@line,"Use '#pragma include' instead of '#include'") unless @style == :wmi
+ raise MOF::Helper::Error.new(@name,@lineno,@line,"Missing filename after '#include'") unless val[2]
+ open val[2], :pragma
+ }
+ ;
+
+ pragmaName
+ : IDENTIFIER
+ ;
+
+ pragmaParameters_opt
+ : /* empty */
+ { raise StyleError.new(@name,@lineno,@line,"#pragma parameter missing") unless @style == :wmi }
+ | "(" pragmaParameterValues ")"
+ { result = val[1] }
+ ;
+
+ pragmaParameterValues
+ : pragmaParameterValue
+ | pragmaParameterValues "," pragmaParameterValue
+ ;
+
+ pragmaParameterValue
+ : string
+ | integerValue
+ { raise StyleError.new(@name,@lineno,@line,"#pragma parameter missing") unless @style == :wmi }
+ | IDENTIFIER
+ ;
+
+/***
+ * classDeclaration
+ *
+ */
+
+ classDeclaration
+ : qualifierList_opt CLASS className alias_opt superClass_opt "{" classFeatures "}" ";"
+ { qualifiers = val[0]
+ features = val[6]
+ # FIXME: features must not include references
+ result = CIM::Class.new(val[2],qualifiers,val[3],val[4],features)
+ }
+ ;
+
+ classFeatures
+ : /* empty */
+ { result = [] }
+ | classFeatures classFeature
+ { result = val[0] << val[1] }
+ ;
+
+ classFeature
+ : propertyDeclaration
+ | methodDeclaration
+ | referenceDeclaration /* must have association qualifier */
+ ;
+
+
+ qualifierList_opt
+ : /* empty */
+ | qualifierList
+ { result = CIM::QualifierSet.new val[0] }
+ ;
+
+ qualifierList
+ : "[" qualifier qualifiers "]"
+ { result = val[2]
+ result.unshift val[1] if val[1] }
+ ;
+
+ qualifiers
+ : /* empty */
+ { result = [] }
+ | qualifiers "," qualifier
+ { result = val[0]
+ result << val[2] if val[2]
+ }
+ ;
+
+ qualifier
+ : qualifierName qualifierParameter_opt flavor_opt
+ { # Get qualifier decl
+ qualifier = case val[0]
+ when CIM::Qualifier then val[0].definition
+ when CIM::QualifierDeclaration then val[0]
+ when String then @qualifiers[val[0].downcase]
+ else
+ nil
+ end
+ raise MOF::Helper::Error.new(@name,@lineno,@line,"'#{val[0]}' is not a valid qualifier") unless qualifier
+ value = val[1]
+ raise MOF::Helper::Error.new(@name,@lineno,@line,"#{value.inspect} does not match qualifier type '#{qualifier.type}'") unless qualifier.type.matches?(value)||@style == :wmi
+ # Don't propagate a boolean 'false'
+ if qualifier.type == :boolean && value == false
+ result = nil
+ else
+ result = CIM::Qualifier.new(qualifier,value,val[2])
+ end
+ }
+ ;
+
+ flavor_opt
+ : /* empty */
+ | ":" flavor
+ { result = CIM::QualifierFlavors.new val[1] }
+ ;
+
+ qualifierParameter_opt
+ : /* empty */
+ | qualifierParameter
+ ;
+
+ qualifierParameter
+ : "(" constantValue ")"
+ { result = val[1] }
+ | arrayInitializer
+ ;
+
+ /* CIM::Flavors */
+ flavor
+ : AMENDED | ENABLEOVERRIDE | DISABLEOVERRIDE | RESTRICTED | TOSUBCLASS | TRANSLATABLE | TOINSTANCE
+ { case val[0].to_sym
+ when :amended, :toinstance
+ raise StyleError.new(@name,@lineno,@line,"'#{val[0]}' is not a valid flavor") unless @style == :wmi
+ end
+ }
+ ;
+
+ alias_opt
+ : /* empty */
+ | alias
+ ;
+
+ superClass_opt
+ : /* empty */
+ | superClass
+ ;
+
+ className
+ : IDENTIFIER /* must be <schema>_<classname> in CIM v2.x */
+ { raise ParseError.new("Class name must be prefixed by '<schema>_'") unless val[0].include?("_") || @style == :wmi }
+ ;
+
+ alias
+ : AS aliasIdentifier
+ { result = val[1] }
+ ;
+
+ aliasIdentifier
+ : "$" IDENTIFIER /* NO whitespace ! */
+ { result = val[1] }
+ ;
+
+ superClass
+ : ":" className
+ { result = val[1] }
+ ;
+
+
+ propertyDeclaration
+ : qualifierList_opt dataType propertyName array_opt defaultValue_opt ";"
+ { if val[3]
+ type = CIM::Array.new val[3],val[1]
+ else
+ type = val[1]
+ end
+ result = CIM::Property.new(type,val[2],val[0],val[4])
+ }
+ ;
+
+ referenceDeclaration
+ : qualifierList_opt objectRef referenceName array_opt defaultValue_opt ";"
+ { if val[4]
+ raise StyleError.new(@name,@lineno,@line,"Array not allowed in reference declaration") unless @style == :wmi
+ end
+ result = CIM::Reference.new(val[1],val[2],val[0],val[4]) }
+ ;
+
+ methodDeclaration
+ : qualifierList_opt dataType methodName "(" parameterList_opt ")" ";"
+ { result = CIM::Method.new(val[1],val[2],val[0],val[4]) }
+ ;
+
+ propertyName
+ : IDENTIFIER
+ | PROPERTY
+ { # tmplprov.mof has 'string Property;'
+ raise StyleError.new(@name,@lineno,@line,"Invalid keyword '#{val[0]}' used for property name") unless @style == :wmi
+ }
+ ;
+
+ referenceName
+ : IDENTIFIER
+ | INDICATION
+ { result = "Indication" }
+ ;
+
+ methodName
+ : IDENTIFIER
+ ;
+
+ dataType
+ : DT_UINT8
+ | DT_SINT8
+ | DT_UINT16
+ | DT_SINT16
+ | DT_UINT32
+ | DT_SINT32
+ | DT_UINT64
+ | DT_SINT64
+ | DT_REAL32
+ | DT_REAL64
+ | DT_CHAR16
+ | DT_STR
+ | DT_BOOLEAN
+ | DT_DATETIME
+ | DT_VOID
+ { raise StyleError.new(@name,@lineno,@line,"'void' is not a valid datatype") unless @style == :wmi }
+ ;
+
+ objectRef
+ : className
+ { # WMI uses class names as data types (without REF ?!)
+ raise StyleError.new(@name,@lineno,@line,"Expected 'ref' keyword after classname '#{val[0]}'") unless @style == :wmi
+ result = CIM::ReferenceType.new val[0]
+ }
+
+ | className REF
+ { result = CIM::ReferenceType.new val[0] }
+ ;
+
+ parameterList_opt
+ : /* empty */
+ | parameterList
+ ;
+
+ parameterList
+ : parameter parameters
+ { result = val[1].unshift val[0] }
+ ;
+
+ parameters
+ : /* empty */
+ { result = [] }
+ | parameters "," parameter
+ { result = val[0] << val[2] }
+ ;
+
+ parameter
+ : qualifierList_opt typespec parameterName array_opt parameterValue_opt
+ { if val[3]
+ type = CIM::Array.new val[3], val[1]
+ else
+ type = val[1]
+ end
+ result = CIM::Property.new(type,val[2],val[0])
+ }
+ ;
+
+ typespec
+ : dataType
+ | objectRef
+ ;
+
+ parameterName
+ : IDENTIFIER
+ ;
+
+ array_opt
+ : /* empty */
+ | array
+ ;
+
+ parameterValue_opt
+ : /* empty */
+ | defaultValue
+ { raise "Default parameter value not allowed in syntax style '{@style}'" unless @style == :wmi }
+ ;
+
+ array
+ : "[" positiveDecimalValue_opt "]"
+ { result = val[1] }
+ ;
+
+ positiveDecimalValue_opt
+ : /* empty */
+ { result = -1 }
+ | positiveDecimalValue
+ ;
+
+ defaultValue_opt
+ : /* empty */
+ | defaultValue
+ ;
+
+ defaultValue
+ : "=" initializer
+ { result = val[1] }
+ ;
+
+ initializer
+ : constantValue
+ | arrayInitializer
+ | referenceInitializer
+ ;
+
+ arrayInitializer
+ : "{" constantValues "}"
+ { result = val[1] }
+ ;
+
+ constantValues
+ : /* empty */
+ | constantValue
+ { result = [ val[0] ] }
+ | constantValues "," constantValue
+ { result = val[0] << val[2] }
+ ;
+
+ constantValue
+ : integerValue
+ | realValue
+ | charValue
+ | string
+ | booleanValue
+ | nullValue
+ | instance
+ { raise "Instance as property value not allowed in syntax style '{@style}'" unless @style == :wmi }
+ ;
+
+ integerValue
+ : binaryValue
+ | octalValue
+ | decimalValue
+ | positiveDecimalValue
+ | hexValue
+ ;
+
+ string
+ : stringValue
+ | string stringValue
+ { result = val[0] + val[1] }
+ ;
+
+ referenceInitializer
+ : objectHandle
+ | aliasIdentifier
+ ;
+
+ objectHandle
+ : namespace_opt modelPath
+ ;
+
+ namespace_opt
+ : /* empty */
+ | namespaceHandle ":"
+ ;
+
+ namespaceHandle
+ : IDENTIFIER
+ ;
+
+ /*
+ * Note
+ : structure depends on type of namespace
+ */
+
+ modelPath
+ : className "." keyValuePairList
+ ;
+
+ keyValuePairList
+ : keyValuePair keyValuePairs
+ ;
+
+ keyValuePairs
+ : /* empty */
+ | keyValuePairs "," keyValuePair
+ ;
+
+ keyValuePair
+ : keyname "=" initializer
+ ;
+
+ keyname
+ : propertyName | referenceName
+ ;
+
+/***
+ * qualifierDeclaration
+ *
+ */
+
+ qualifierDeclaration
+ /* 0 1 2 3 4 */
+ : QUALIFIER qualifierName qualifierType scope defaultFlavor_opt ";"
+ { result = CIM::QualifierDeclaration.new( val[1], val[2][0], val[2][1], val[3], val[4]) }
+ ;
+
+ defaultFlavor_opt
+ : /* empty */
+ | defaultFlavor
+ ;
+
+ qualifierName
+ : IDENTIFIER
+ | ASSOCIATION /* meta qualifier */
+ | INDICATION /* meta qualifier */
+ | REFERENCE /* Added in DSP0004 2.7.0 */
+ | SCHEMA
+ ;
+
+ /* [type, value] */
+ qualifierType
+ : ":" dataType array_opt defaultValue_opt
+ { type = val[2].nil? ? val[1] : CIM::Array.new(val[2],val[1])
+ result = [ type, val[3] ]
+ }
+ ;
+
+ scope
+ : "," SCOPE "(" metaElements ")"
+ { result = CIM::QualifierScopes.new(val[3]) }
+ ;
+
+ metaElements
+ : metaElement
+ { result = [ val[0] ] }
+ | metaElements "," metaElement
+ { result = val[0] << val[2] }
+ ;
+
+ metaElement
+ : SCHEMA
+ | CLASS
+ | ASSOCIATION
+ | INDICATION
+ | QUALIFIER
+ | PROPERTY
+ | REFERENCE
+ | METHOD
+ | PARAMETER
+ | ANY
+ ;
+
+ defaultFlavor
+ : "," FLAVOR "(" flavors ")"
+ { result = CIM::QualifierFlavors.new val[3] }
+ ;
+
+ flavors
+ : flavor
+ { result = [ val[0] ] }
+ | flavors "," flavor
+ { result = val[0] << val[2] }
+ ;
+
+/***
+ * instanceDeclaration
+ *
+ */
+
+ instanceDeclaration
+ : instance ";"
+ ;
+
+ instance
+ : qualifierList_opt INSTANCE OF className alias_opt "{" valueInitializers "}"
+ ;
+
+ valueInitializers
+ : valueInitializer
+ | valueInitializers valueInitializer
+ ;
+
+ valueInitializer
+ : qualifierList_opt keyname "=" initializer ";"
+ | qualifierList_opt keyname ";"
+ { raise "Instance property '#{val[1]} must have a value" unless @style == :wmi }
+ ;
+
+end # class Parser
+
+---- header ----
+
+# parser.rb - generated by racc
+
+require 'strscan'
+require 'rubygems'
+require 'cim'
+require File.join(File.dirname(__FILE__), 'result')
+require File.join(File.dirname(__FILE__), 'scanner')
+require File.join(File.dirname(__FILE__), 'helper')
+
+---- inner ----
+
+#
+# Initialize MOF::Parser
+# MOF::Parser.new options = {}
+#
+# options -> Hash of options
+# :debug -> boolean
+# :includes -> array of include dirs
+# :style -> :cim or :wmi
+#
+def initialize options = {}
+ @yydebug = options[:debug]
+ @includes = options[:includes] || []
+ @quiet = options[:quiet]
+ @style = options[:style] || :cim # default to style CIM v2.2 syntax
+
+ @lineno = 1
+ @file = nil
+ @iconv = nil
+ @eol = "\n"
+ @fname = nil
+ @fstack = []
+ @in_comment = false
+ @seen_files = []
+ @qualifiers = {}
+end
+
+#
+# Make options hash from argv
+#
+# returns [ files, options ]
+#
+
+ def self.argv_handler name, argv
+ files = []
+ options = { :namespace => "" }
+ while argv.size > 0
+ case opt = argv.shift
+ when "-h"
+ $stderr.puts "Ruby MOF compiler"
+ $stderr.puts "#{name} [-h] [-d] [-I <dir>] [<moffiles>]"
+ $stderr.puts "Compiles <moffile>"
+ $stderr.puts "\t-d debug"
+ $stderr.puts "\t-h this help"
+ $stderr.puts "\t-I <dir> include dir"
+ $stderr.puts "\t-f force"
+ $stderr.puts "\t-n <namespace>"
+ $stderr.puts "\t-o <output>"
+ $stderr.puts "\t-s <style> syntax style (wmi,cim)"
+ $stderr.puts "\t-q quiet"
+ $stderr.puts "\t<moffiles> file(s) to read (else use $stdin)"
+ exit 0
+ when "-f" then options[:force] = true
+ when "-s" then options[:style] = argv.shift.to_sym
+ when "-d" then options[:debug] = true
+ when "-q" then options[:quiet] = true
+ when "-I"
+ options[:includes] ||= []
+ dirname = argv.shift
+ unless File.directory?(dirname)
+ files << dirname
+ dirname = File.dirname(dirname)
+ end
+ options[:includes] << Pathname.new(dirname)
+ when "-n" then options[:namespace] = argv.shift
+ when "-o" then options[:output] = argv.shift
+ when /^-.+/
+ $stderr.puts "Undefined option #{opt}"
+ else
+ files << opt
+ end
+ end
+ [ files, options ]
+ end
+
+include Helper
+include Scanner
+
+---- footer ----
diff --git a/test/racc/assets/namae.y b/test/racc/assets/namae.y
new file mode 100644
index 0000000000..0378345fef
--- /dev/null
+++ b/test/racc/assets/namae.y
@@ -0,0 +1,302 @@
+# -*- ruby -*-
+# vi: set ft=ruby :
+
+# Copyright (C) 2012 President and Fellows of Harvard College
+# Copyright (C) 2013-2014 Sylvester Keil
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``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 HOLDER 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.
+#
+# The views and conclusions contained in the software and documentation are
+# those of the authors and should not be interpreted as representing official
+# policies, either expressed or implied, of the copyright holder.
+
+class Namae::Parser
+
+token COMMA UWORD LWORD PWORD NICK AND APPELLATION TITLE SUFFIX
+
+expect 0
+
+rule
+
+ names : { result = [] }
+ | name { result = [val[0]] }
+ | names AND name { result = val[0] << val[2] }
+
+ name : word { result = Name.new(:given => val[0]) }
+ | display_order
+ | honorific word { result = val[0].merge(:family => val[1]) }
+ | honorific display_order { result = val[1].merge(val[0]) }
+ | sort_order
+
+ honorific : APPELLATION { result = Name.new(:appellation => val[0]) }
+ | TITLE { result = Name.new(:title => val[0]) }
+
+ display_order : u_words word opt_suffices opt_titles
+ {
+ result = Name.new(:given => val[0], :family => val[1],
+ :suffix => val[2], :title => val[3])
+ }
+ | u_words NICK last opt_suffices opt_titles
+ {
+ result = Name.new(:given => val[0], :nick => val[1],
+ :family => val[2], :suffix => val[3], :title => val[4])
+ }
+ | u_words NICK von last opt_suffices opt_titles
+ {
+ result = Name.new(:given => val[0], :nick => val[1],
+ :particle => val[2], :family => val[3],
+ :suffix => val[4], :title => val[5])
+ }
+ | u_words von last
+ {
+ result = Name.new(:given => val[0], :particle => val[1],
+ :family => val[2])
+ }
+ | von last
+ {
+ result = Name.new(:particle => val[0], :family => val[1])
+ }
+
+ sort_order : last COMMA first
+ {
+ result = Name.new({ :family => val[0], :suffix => val[2][0],
+ :given => val[2][1] }, !!val[2][0])
+ }
+ | von last COMMA first
+ {
+ result = Name.new({ :particle => val[0], :family => val[1],
+ :suffix => val[3][0], :given => val[3][1] }, !!val[3][0])
+ }
+ | u_words von last COMMA first
+ {
+ result = Name.new({ :particle => val[0,2].join(' '), :family => val[2],
+ :suffix => val[4][0], :given => val[4][1] }, !!val[4][0])
+ }
+ ;
+
+ von : LWORD
+ | von LWORD { result = val.join(' ') }
+ | von u_words LWORD { result = val.join(' ') }
+
+ last : LWORD | u_words
+
+ first : opt_words { result = [nil,val[0]] }
+ | words opt_comma suffices { result = [val[2],val[0]] }
+ | suffices { result = [val[0],nil] }
+ | suffices COMMA words { result = [val[0],val[2]] }
+
+ u_words : u_word
+ | u_words u_word { result = val.join(' ') }
+
+ u_word : UWORD | PWORD
+
+ words : word
+ | words word { result = val.join(' ') }
+
+ opt_comma : /* empty */ | COMMA
+ opt_words : /* empty */ | words
+
+ word : LWORD | UWORD | PWORD
+
+ opt_suffices : /* empty */ | suffices
+
+ suffices : SUFFIX
+ | suffices SUFFIX { result = val.join(' ') }
+
+ opt_titles : /* empty */ | titles
+
+ titles : TITLE
+ | titles TITLE { result = val.join(' ') }
+
+---- header
+require 'singleton'
+require 'strscan'
+
+---- inner
+
+ include Singleton
+
+ attr_reader :options, :input
+
+ def initialize
+ @input, @options = StringScanner.new(''), {
+ :debug => false,
+ :prefer_comma_as_separator => false,
+ :comma => ',',
+ :stops => ',;',
+ :separator => /\s*(\band\b|\&|;)\s*/i,
+ :title => /\s*\b(sir|lord|count(ess)?|(gen|adm|col|maj|capt|cmdr|lt|sgt|cpl|pvt|prof|dr|md|ph\.?d)\.?)(\s+|$)/i,
+ :suffix => /\s*\b(JR|Jr|jr|SR|Sr|sr|[IVX]{2,})(\.|\b)/,
+ :appellation => /\s*\b((mrs?|ms|fr|hr)\.?|miss|herr|frau)(\s+|$)/i
+ }
+ end
+
+ def debug?
+ options[:debug] || ENV['DEBUG']
+ end
+
+ def separator
+ options[:separator]
+ end
+
+ def comma
+ options[:comma]
+ end
+
+ def stops
+ options[:stops]
+ end
+
+ def title
+ options[:title]
+ end
+
+ def suffix
+ options[:suffix]
+ end
+
+ def appellation
+ options[:appellation]
+ end
+
+ def prefer_comma_as_separator?
+ options[:prefer_comma_as_separator]
+ end
+
+ def parse(input)
+ parse!(input)
+ rescue => e
+ warn e.message if debug?
+ []
+ end
+
+ def parse!(string)
+ input.string = normalize(string)
+ reset
+ do_parse
+ end
+
+ def normalize(string)
+ string = string.strip
+ string
+ end
+
+ def reset
+ @commas, @words, @initials, @suffices, @yydebug = 0, 0, 0, 0, debug?
+ self
+ end
+
+ private
+
+ def stack
+ @vstack || @racc_vstack || []
+ end
+
+ def last_token
+ stack[-1]
+ end
+
+ def consume_separator
+ return next_token if seen_separator?
+ @commas, @words, @initials, @suffices = 0, 0, 0, 0
+ [:AND, :AND]
+ end
+
+ def consume_comma
+ @commas += 1
+ [:COMMA, :COMMA]
+ end
+
+ def consume_word(type, word)
+ @words += 1
+
+ case type
+ when :UWORD
+ @initials += 1 if word =~ /^[[:upper:]]+\b/
+ when :SUFFIX
+ @suffices += 1
+ end
+
+ [type, word]
+ end
+
+ def seen_separator?
+ !stack.empty? && last_token == :AND
+ end
+
+ def suffix?
+ !@suffices.zero? || will_see_suffix?
+ end
+
+ def will_see_suffix?
+ input.peek(8).to_s.strip.split(/\s+/)[0] =~ suffix
+ end
+
+ def will_see_initial?
+ input.peek(6).to_s.strip.split(/\s+/)[0] =~ /^[[:upper:]]+\b/
+ end
+
+ def seen_full_name?
+ prefer_comma_as_separator? && @words > 1 &&
+ (@initials > 0 || !will_see_initial?) && !will_see_suffix?
+ end
+
+ def next_token
+ case
+ when input.nil?, input.eos?
+ nil
+ when input.scan(separator)
+ consume_separator
+ when input.scan(/\s*#{comma}\s*/)
+ if @commas.zero? && !seen_full_name? || @commas == 1 && suffix?
+ consume_comma
+ else
+ consume_separator
+ end
+ when input.scan(/\s+/)
+ next_token
+ when input.scan(title)
+ consume_word(:TITLE, input.matched.strip)
+ when input.scan(suffix)
+ consume_word(:SUFFIX, input.matched.strip)
+ when input.scan(appellation)
+ [:APPELLATION, input.matched.strip]
+ when input.scan(/((\\\w+)?\{[^\}]*\})*[[:upper:]][^\s#{stops}]*/)
+ consume_word(:UWORD, input.matched)
+ when input.scan(/((\\\w+)?\{[^\}]*\})*[[:lower:]][^\s#{stops}]*/)
+ consume_word(:LWORD, input.matched)
+ when input.scan(/(\\\w+)?\{[^\}]*\}[^\s#{stops}]*/)
+ consume_word(:PWORD, input.matched)
+ when input.scan(/('[^'\n]+')|("[^"\n]+")/)
+ consume_word(:NICK, input.matched[1...-1])
+ else
+ raise ArgumentError,
+ "Failed to parse name #{input.string.inspect}: unmatched data at offset #{input.pos}"
+ end
+ end
+
+ def on_error(tid, value, stack)
+ raise ArgumentError,
+ "Failed to parse name: unexpected '#{value}' at #{stack.inspect}"
+ end
+
+# -*- racc -*-
diff --git a/test/racc/assets/nasl.y b/test/racc/assets/nasl.y
new file mode 100644
index 0000000000..e68dd699f8
--- /dev/null
+++ b/test/racc/assets/nasl.y
@@ -0,0 +1,626 @@
+################################################################################
+# Copyright (c) 2011-2014, Tenable Network Security
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# 2. 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.
+#
+# 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 HOLDER 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.
+################################################################################
+
+class Nasl::Grammar
+
+preclow
+ right ASS_EQ ADD_EQ SUB_EQ MUL_EQ DIV_EQ MOD_EQ SLL_EQ SRA_EQ SRL_EQ
+ left OR
+ left AND
+ left CMP_LT CMP_GT CMP_EQ CMP_NE CMP_GE CMP_LE SUBSTR_EQ SUBSTR_NE REGEX_EQ REGEX_NE
+ left BIT_OR
+ left BIT_XOR
+ left AMPERSAND
+ left BIT_SRA BIT_SRL BIT_SLL
+ left ADD SUB
+ left MUL DIV MOD
+ right NOT
+ right UMINUS BIT_NOT
+ right EXP
+ right INCR DECR
+prechigh
+
+# Tell the parser generator that we don't wish to use the result variable in the
+# action section of rules. Instead, the result of the rule will be the value of
+# evaluating the action block.
+options no_result_var
+
+# Tell the parser generator that we expect one shift/reduce conflict due to the
+# well-known dangling else problem. We could make the grammar solve this
+# problem, but this is how the NASL YACC file solves it, so we'll follow suit.
+expect 1
+
+rule
+ ##############################################################################
+ # Aggregate Statements
+ ##############################################################################
+
+ start : roots
+ { val[0] }
+ | /* Blank */
+ { [] }
+ ;
+
+ roots : root roots
+ { [val[0]] + val[1] }
+ | root
+ { [val[0]] }
+ ;
+
+ root : COMMENT export
+ { c(*val) }
+ | export
+ { val[0] }
+ | COMMENT function
+ { c(*val) }
+ | function
+ { val[0] }
+ | statement
+ { val[0] }
+ ;
+
+ statement : simple
+ { val[0] }
+ | compound
+ { val[0] }
+ ;
+
+ ##############################################################################
+ # Root Statements
+ ##############################################################################
+
+ export : EXPORT function
+ { n(:Export, *val) }
+ ;
+
+ function : FUNCTION ident LPAREN params RPAREN block
+ { n(:Function, *val) }
+ | FUNCTION ident LPAREN RPAREN block
+ { n(:Function, *val) }
+ ;
+
+ simple : assign
+ { val[0] }
+ | break
+ { val[0] }
+ | call
+ { val[0] }
+ | continue
+ { val[0] }
+ | decr
+ { val[0] }
+ | empty
+ { val[0] }
+ | COMMENT global
+ { c(*val) }
+ | global
+ { val[0] }
+ | import
+ { val[0] }
+ | include
+ { val[0] }
+ | incr
+ { val[0] }
+ | local
+ { val[0] }
+ | rep
+ { val[0] }
+ | return
+ { val[0] }
+ ;
+
+ compound : block
+ { val[0] }
+ | for
+ { val[0] }
+ | foreach
+ { val[0] }
+ | if
+ { val[0] }
+ | repeat
+ { val[0] }
+ | while
+ { val[0] }
+ ;
+
+ ##############################################################################
+ # Simple Statements
+ ##############################################################################
+
+ assign : assign_exp SEMICOLON
+ { val[0] }
+ ;
+
+ break : BREAK SEMICOLON
+ { n(:Break, *val) }
+ ;
+
+ call : call_exp SEMICOLON
+ { val[0] }
+ ;
+
+ continue : CONTINUE SEMICOLON
+ { n(:Continue, *val) }
+ ;
+
+ decr : decr_exp SEMICOLON
+ { val[0] }
+ ;
+
+ empty : SEMICOLON
+ { n(:Empty, *val) }
+ ;
+
+ global : GLOBAL var_decls SEMICOLON
+ { n(:Global, *val) }
+ ;
+
+ incr : incr_exp SEMICOLON
+ { val[0] }
+ ;
+
+ import : IMPORT LPAREN string RPAREN SEMICOLON
+ { n(:Import, *val) }
+ ;
+
+ include : INCLUDE LPAREN string RPAREN SEMICOLON
+ { n(:Include, *val) }
+ ;
+
+ local : LOCAL var_decls SEMICOLON
+ { n(:Local, *val) }
+ ;
+
+ rep : call_exp REP expr SEMICOLON
+ { n(:Repetition, *val[0..-1]) }
+ ;
+
+ return : RETURN expr SEMICOLON
+ { n(:Return, *val) }
+ | RETURN ref SEMICOLON
+ { n(:Return, *val) }
+ | RETURN SEMICOLON
+ { n(:Return, *val) }
+ ;
+
+ ##############################################################################
+ # Compound Statements
+ ##############################################################################
+
+ block : LBRACE statements RBRACE
+ { n(:Block, *val) }
+ | LBRACE RBRACE
+ { n(:Block, *val) }
+ ;
+
+ for : FOR LPAREN field SEMICOLON expr SEMICOLON field RPAREN statement
+ { n(:For, *val) }
+ ;
+
+ foreach : FOREACH ident LPAREN expr RPAREN statement
+ { n(:Foreach, val[0], val[1], val[3], val[5]) }
+ | FOREACH LPAREN ident IN expr RPAREN statement
+ { n(:Foreach, val[0], val[2], val[4], val[6]) }
+ ;
+
+ if : IF LPAREN expr RPAREN statement
+ { n(:If, *val) }
+ | IF LPAREN expr RPAREN statement ELSE statement
+ { n(:If, *val) }
+ ;
+
+ repeat : REPEAT statement UNTIL expr SEMICOLON
+ { n(:Repeat, *val) }
+ ;
+
+ while : WHILE LPAREN expr RPAREN statement
+ { n(:While, *val) }
+ ;
+
+ ##############################################################################
+ # Expressions
+ ##############################################################################
+
+ assign_exp : lval ASS_EQ expr
+ { n(:Assignment, *val) }
+ | lval ASS_EQ ref
+ { n(:Assignment, *val) }
+ | lval ADD_EQ expr
+ { n(:Assignment, *val) }
+ | lval SUB_EQ expr
+ { n(:Assignment, *val) }
+ | lval MUL_EQ expr
+ { n(:Assignment, *val) }
+ | lval DIV_EQ expr
+ { n(:Assignment, *val) }
+ | lval MOD_EQ expr
+ { n(:Assignment, *val) }
+ | lval SRL_EQ expr
+ { n(:Assignment, *val) }
+ | lval SRA_EQ expr
+ { n(:Assignment, *val) }
+ | lval SLL_EQ expr
+ { n(:Assignment, *val) }
+ ;
+
+ call_exp : lval LPAREN args RPAREN
+ { n(:Call, *val) }
+ | lval LPAREN RPAREN
+ { n(:Call, *val) }
+ ;
+
+ decr_exp : DECR lval
+ { n(:Decrement, val[0]) }
+ | lval DECR
+ { n(:Decrement, val[0]) }
+ ;
+
+ incr_exp : INCR lval
+ { n(:Increment, val[0]) }
+ | lval INCR
+ { n(:Increment, val[0]) }
+ ;
+
+ expr : LPAREN expr RPAREN
+ { n(:Expression, *val) }
+ | expr AND expr
+ { n(:Expression, *val) }
+ | NOT expr
+ { n(:Expression, *val) }
+ | expr OR expr
+ { n(:Expression, *val) }
+ | expr ADD expr
+ { n(:Expression, *val) }
+ | expr SUB expr
+ { n(:Expression, *val) }
+ | SUB expr =UMINUS
+ { n(:Expression, *val) }
+ | BIT_NOT expr
+ { n(:Expression, *val) }
+ | expr MUL expr
+ { n(:Expression, *val) }
+ | expr EXP expr
+ { n(:Expression, *val) }
+ | expr DIV expr
+ { n(:Expression, *val) }
+ | expr MOD expr
+ { n(:Expression, *val) }
+ | expr AMPERSAND expr
+ { n(:Expression, *val) }
+ | expr BIT_XOR expr
+ { n(:Expression, *val) }
+ | expr BIT_OR expr
+ { n(:Expression, *val) }
+ | expr BIT_SRA expr
+ { n(:Expression, *val) }
+ | expr BIT_SRL expr
+ { n(:Expression, *val) }
+ | expr BIT_SLL expr
+ { n(:Expression, *val) }
+ | incr_exp
+ { val[0] }
+ | decr_exp
+ { val[0] }
+ | expr SUBSTR_EQ expr
+ { n(:Expression, *val) }
+ | expr SUBSTR_NE expr
+ { n(:Expression, *val) }
+ | expr REGEX_EQ expr
+ { n(:Expression, *val) }
+ | expr REGEX_NE expr
+ { n(:Expression, *val) }
+ | expr CMP_LT expr
+ { n(:Expression, *val) }
+ | expr CMP_GT expr
+ { n(:Expression, *val) }
+ | expr CMP_EQ expr
+ { n(:Expression, *val) }
+ | expr CMP_NE expr
+ { n(:Expression, *val) }
+ | expr CMP_GE expr
+ { n(:Expression, *val) }
+ | expr CMP_LE expr
+ { n(:Expression, *val) }
+ | assign_exp
+ { val[0] }
+ | string
+ { val[0] }
+ | call_exp
+ { val[0] }
+ | lval
+ { val[0] }
+ | ip
+ { val[0] }
+ | int
+ { val[0] }
+ | undef
+ { val[0] }
+ | list_expr
+ { val[0] }
+ | array_expr
+ { val[0] }
+ ;
+
+ ##############################################################################
+ # Named Components
+ ##############################################################################
+
+ arg : ident COLON expr
+ { n(:Argument, *val) }
+ | ident COLON ref
+ { n(:Argument, *val) }
+ | expr
+ { n(:Argument, *val) }
+ | ref
+ { n(:Argument, *val) }
+ ;
+
+ kv_pair : string COLON expr
+ { n(:KeyValuePair, *val) }
+ | int COLON expr
+ { n(:KeyValuePair, *val) }
+ | ident COLON expr
+ { n(:KeyValuePair, *val) }
+ | string COLON ref
+ { n(:KeyValuePair, *val) }
+ | int COLON ref
+ { n(:KeyValuePair, *val) }
+ | ident COLON ref
+ { n(:KeyValuePair, *val) }
+ ;
+
+ kv_pairs : kv_pair COMMA kv_pairs
+ { [val[0]] + val[2] }
+ | kv_pair COMMA
+ { [val[0]] }
+ | kv_pair
+ { [val[0]] }
+ ;
+
+ lval : ident indexes
+ { n(:Lvalue, *val) }
+ | ident
+ { n(:Lvalue, *val) }
+ ;
+
+ ref : AT_SIGN ident
+ { n(:Reference, val[1]) }
+ ;
+
+ ##############################################################################
+ # Anonymous Components
+ ##############################################################################
+
+ args : arg COMMA args
+ { [val[0]] + val[2] }
+ | arg
+ { [val[0]] }
+ ;
+
+ array_expr : LBRACE kv_pairs RBRACE
+ { n(:Array, *val) }
+ | LBRACE RBRACE
+ { n(:Array, *val) }
+ ;
+
+ field : assign_exp
+ { val[0] }
+ | call_exp
+ { val[0] }
+ | decr_exp
+ { val[0] }
+ | incr_exp
+ { val[0] }
+ | /* Blank */
+ { nil }
+ ;
+
+ index : LBRACK expr RBRACK
+ { val[1] }
+ | PERIOD ident
+ { val[1] }
+ ;
+
+ indexes : index indexes
+ { [val[0]] + val[1] }
+ | index
+ { [val[0]] }
+ ;
+
+ list_elem : expr
+ { val[0] }
+ | ref
+ { val[0] }
+ ;
+
+ list_elems : list_elem COMMA list_elems
+ { [val[0]] + val[2] }
+ | list_elem
+ { [val[0]] }
+ ;
+
+ list_expr : LBRACK list_elems RBRACK
+ { n(:List, *val) }
+ | LBRACK RBRACK
+ { n(:List, *val) }
+ ;
+
+ param : AMPERSAND ident
+ { n(:Parameter, val[1], 'reference') }
+ | ident
+ { n(:Parameter, val[0], 'value') }
+ ;
+
+ params : param COMMA params
+ { [val[0]] + val[2] }
+ | param
+ { [val[0]] }
+ ;
+
+ statements : statement statements
+ { [val[0]] + val[1] }
+ | statement
+ { [val[0]] }
+ ;
+
+ var_decl : ident ASS_EQ expr
+ { n(:Assignment, *val) }
+ | ident ASS_EQ ref
+ { n(:Assignment, *val) }
+ | ident
+ { val[0] }
+ ;
+
+ var_decls : var_decl COMMA var_decls
+ { [val[0]] + val[2] }
+ | var_decl
+ { [val[0]] }
+ ;
+
+ ##############################################################################
+ # Literals
+ ##############################################################################
+
+ ident : IDENT
+ { n(:Identifier, *val) }
+ | REP
+ { n(:Identifier, *val) }
+ | IN
+ { n(:Identifier, *val) }
+ ;
+
+ int : INT_DEC
+ { n(:Integer, *val) }
+ | INT_HEX
+ { n(:Integer, *val) }
+ | INT_OCT
+ { n(:Integer, *val) }
+ | FALSE
+ { n(:Integer, *val) }
+ | TRUE
+ { n(:Integer, *val) }
+ ;
+
+ ip : int PERIOD int PERIOD int PERIOD int
+ { n(:Ip, *val) }
+
+ string : DATA
+ { n(:String, *val) }
+ | STRING
+ { n(:String, *val) }
+ ;
+
+ undef : UNDEF
+ { n(:Undefined, *val) }
+ ;
+end
+
+---- header ----
+
+require 'nasl/parser/tree'
+
+require 'nasl/parser/argument'
+require 'nasl/parser/array'
+require 'nasl/parser/assigment'
+require 'nasl/parser/block'
+require 'nasl/parser/break'
+require 'nasl/parser/call'
+require 'nasl/parser/comment'
+require 'nasl/parser/continue'
+require 'nasl/parser/decrement'
+require 'nasl/parser/empty'
+require 'nasl/parser/export'
+require 'nasl/parser/expression'
+require 'nasl/parser/for'
+require 'nasl/parser/foreach'
+require 'nasl/parser/function'
+require 'nasl/parser/global'
+require 'nasl/parser/identifier'
+require 'nasl/parser/if'
+require 'nasl/parser/import'
+require 'nasl/parser/include'
+require 'nasl/parser/increment'
+require 'nasl/parser/integer'
+require 'nasl/parser/ip'
+require 'nasl/parser/key_value_pair'
+require 'nasl/parser/list'
+require 'nasl/parser/local'
+require 'nasl/parser/lvalue'
+require 'nasl/parser/parameter'
+require 'nasl/parser/reference'
+require 'nasl/parser/repeat'
+require 'nasl/parser/repetition'
+require 'nasl/parser/return'
+require 'nasl/parser/string'
+require 'nasl/parser/undefined'
+require 'nasl/parser/while'
+
+---- inner ----
+
+def n(cls, *args)
+ begin
+ Nasl.const_get(cls).new(@tree, *args)
+ rescue
+ puts "An exception occurred during the creation of a #{cls} instance."
+ puts
+ puts "The arguments passed to the constructer were:"
+ puts args
+ puts
+ puts @tok.last.context
+ puts
+ raise
+ end
+end
+
+def c(*args)
+ n(:Comment, *args)
+ args[1]
+end
+
+def on_error(type, value, stack)
+ raise ParseException, "The language's grammar does not permit #{value.name} to appear here", value.context
+end
+
+def next_token
+ @tok = @tkz.get_token
+
+ if @first && @tok.first == :COMMENT
+ n(:Comment, @tok.last)
+ @tok = @tkz.get_token
+ end
+ @first = false
+
+ return @tok
+end
+
+def parse(env, code, path)
+ @first = true
+ @tree = Tree.new(env)
+ @tkz = Tokenizer.new(code, path)
+ @tree.concat(do_parse)
+end
+
+---- footer ----
diff --git a/test/racc/assets/newsyn.y b/test/racc/assets/newsyn.y
new file mode 100644
index 0000000000..5b670c966a
--- /dev/null
+++ b/test/racc/assets/newsyn.y
@@ -0,0 +1,25 @@
+
+class A
+
+ preclow
+ left preclow prechigh right left nonassoc token
+ right preclow prechigh right left nonassoc token
+ nonassoc preclow prechigh right left nonassoc token
+ prechigh
+
+ convert
+ left 'a'
+ right 'b'
+ preclow 'c'
+ nonassoc 'd'
+ preclow 'e'
+ prechigh 'f'
+ end
+
+rule
+
+ left: right nonassoc preclow prechigh
+
+ right: A B C
+
+end
diff --git a/test/racc/assets/noend.y b/test/racc/assets/noend.y
new file mode 100644
index 0000000000..5aa0670be0
--- /dev/null
+++ b/test/racc/assets/noend.y
@@ -0,0 +1,4 @@
+class MyParser
+rule
+input: A B C
+end
diff --git a/test/racc/assets/nokogiri-css.y b/test/racc/assets/nokogiri-css.y
new file mode 100644
index 0000000000..24dfbf3b1b
--- /dev/null
+++ b/test/racc/assets/nokogiri-css.y
@@ -0,0 +1,255 @@
+class Nokogiri::CSS::Parser
+
+token FUNCTION INCLUDES DASHMATCH LBRACE HASH PLUS GREATER S STRING IDENT
+token COMMA NUMBER PREFIXMATCH SUFFIXMATCH SUBSTRINGMATCH TILDE NOT_EQUAL
+token SLASH DOUBLESLASH NOT EQUAL RPAREN LSQUARE RSQUARE HAS
+
+rule
+ selector
+ : selector COMMA simple_selector_1toN {
+ result = [val.first, val.last].flatten
+ }
+ | prefixless_combinator_selector { result = val.flatten }
+ | optional_S simple_selector_1toN { result = [val.last].flatten }
+ ;
+ combinator
+ : PLUS { result = :DIRECT_ADJACENT_SELECTOR }
+ | GREATER { result = :CHILD_SELECTOR }
+ | TILDE { result = :FOLLOWING_SELECTOR }
+ | DOUBLESLASH { result = :DESCENDANT_SELECTOR }
+ | SLASH { result = :CHILD_SELECTOR }
+ ;
+ simple_selector
+ : element_name hcap_0toN {
+ result = if val[1].nil?
+ val.first
+ else
+ Node.new(:CONDITIONAL_SELECTOR, [val.first, val[1]])
+ end
+ }
+ | function
+ | function pseudo {
+ result = Node.new(:CONDITIONAL_SELECTOR, val)
+ }
+ | function attrib {
+ result = Node.new(:CONDITIONAL_SELECTOR, val)
+ }
+ | hcap_1toN {
+ result = Node.new(:CONDITIONAL_SELECTOR,
+ [Node.new(:ELEMENT_NAME, ['*']), val.first]
+ )
+ }
+ ;
+ prefixless_combinator_selector
+ : combinator simple_selector_1toN {
+ result = Node.new(val.first, [nil, val.last])
+ }
+ ;
+ simple_selector_1toN
+ : simple_selector combinator simple_selector_1toN {
+ result = Node.new(val[1], [val.first, val.last])
+ }
+ | simple_selector S simple_selector_1toN {
+ result = Node.new(:DESCENDANT_SELECTOR, [val.first, val.last])
+ }
+ | simple_selector
+ ;
+ class
+ : '.' IDENT { result = Node.new(:CLASS_CONDITION, [val[1]]) }
+ ;
+ element_name
+ : namespaced_ident
+ | '*' { result = Node.new(:ELEMENT_NAME, val) }
+ ;
+ namespaced_ident
+ : namespace '|' IDENT {
+ result = Node.new(:ELEMENT_NAME,
+ [[val.first, val.last].compact.join(':')]
+ )
+ }
+ | IDENT {
+ name = @namespaces.key?('xmlns') ? "xmlns:#{val.first}" : val.first
+ result = Node.new(:ELEMENT_NAME, [name])
+ }
+ ;
+ namespace
+ : IDENT { result = val[0] }
+ |
+ ;
+ attrib
+ : LSQUARE attrib_name attrib_val_0or1 RSQUARE {
+ result = Node.new(:ATTRIBUTE_CONDITION,
+ [val[1]] + (val[2] || [])
+ )
+ }
+ | LSQUARE function attrib_val_0or1 RSQUARE {
+ result = Node.new(:ATTRIBUTE_CONDITION,
+ [val[1]] + (val[2] || [])
+ )
+ }
+ | LSQUARE NUMBER RSQUARE {
+ # Non standard, but hpricot supports it.
+ result = Node.new(:PSEUDO_CLASS,
+ [Node.new(:FUNCTION, ['nth-child(', val[1]])]
+ )
+ }
+ ;
+ attrib_name
+ : namespace '|' IDENT {
+ result = Node.new(:ELEMENT_NAME,
+ [[val.first, val.last].compact.join(':')]
+ )
+ }
+ | IDENT {
+ # Default namespace is not applied to attributes.
+ # So we don't add prefix "xmlns:" as in namespaced_ident.
+ result = Node.new(:ELEMENT_NAME, [val.first])
+ }
+ ;
+ function
+ : FUNCTION RPAREN {
+ result = Node.new(:FUNCTION, [val.first.strip])
+ }
+ | FUNCTION expr RPAREN {
+ result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
+ }
+ | FUNCTION nth RPAREN {
+ result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
+ }
+ | NOT expr RPAREN {
+ result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
+ }
+ | HAS selector RPAREN {
+ result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
+ }
+ ;
+ expr
+ : NUMBER COMMA expr { result = [val.first, val.last] }
+ | STRING COMMA expr { result = [val.first, val.last] }
+ | IDENT COMMA expr { result = [val.first, val.last] }
+ | NUMBER
+ | STRING
+ | IDENT # even, odd
+ {
+ case val[0]
+ when 'even'
+ result = Node.new(:NTH, ['2','n','+','0'])
+ when 'odd'
+ result = Node.new(:NTH, ['2','n','+','1'])
+ when 'n'
+ result = Node.new(:NTH, ['1','n','+','0'])
+ else
+ # This is not CSS standard. It allows us to support this:
+ # assert_xpath("//a[foo(., @href)]", @parser.parse('a:foo(@href)'))
+ # assert_xpath("//a[foo(., @a, b)]", @parser.parse('a:foo(@a, b)'))
+ # assert_xpath("//a[foo(., a, 10)]", @parser.parse('a:foo(a, 10)'))
+ result = val
+ end
+ }
+ ;
+ nth
+ : NUMBER IDENT PLUS NUMBER # 5n+3 -5n+3
+ {
+ if val[1] == 'n'
+ result = Node.new(:NTH, val)
+ else
+ raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
+ end
+ }
+ | IDENT PLUS NUMBER { # n+3, -n+3
+ if val[0] == 'n'
+ val.unshift("1")
+ result = Node.new(:NTH, val)
+ elsif val[0] == '-n'
+ val[0] = 'n'
+ val.unshift("-1")
+ result = Node.new(:NTH, val)
+ else
+ raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
+ end
+ }
+ | NUMBER IDENT { # 5n, -5n, 10n-1
+ n = val[1]
+ if n[0, 2] == 'n-'
+ val[1] = 'n'
+ val << "-"
+ # b is contained in n as n is the string "n-b"
+ val << n[2, n.size]
+ result = Node.new(:NTH, val)
+ elsif n == 'n'
+ val << "+"
+ val << "0"
+ result = Node.new(:NTH, val)
+ else
+ raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
+ end
+ }
+ ;
+ pseudo
+ : ':' function {
+ result = Node.new(:PSEUDO_CLASS, [val[1]])
+ }
+ | ':' IDENT { result = Node.new(:PSEUDO_CLASS, [val[1]]) }
+ ;
+ hcap_0toN
+ : hcap_1toN
+ |
+ ;
+ hcap_1toN
+ : attribute_id hcap_1toN {
+ result = Node.new(:COMBINATOR, val)
+ }
+ | class hcap_1toN {
+ result = Node.new(:COMBINATOR, val)
+ }
+ | attrib hcap_1toN {
+ result = Node.new(:COMBINATOR, val)
+ }
+ | pseudo hcap_1toN {
+ result = Node.new(:COMBINATOR, val)
+ }
+ | negation hcap_1toN {
+ result = Node.new(:COMBINATOR, val)
+ }
+ | attribute_id
+ | class
+ | attrib
+ | pseudo
+ | negation
+ ;
+ attribute_id
+ : HASH { result = Node.new(:ID, val) }
+ ;
+ attrib_val_0or1
+ : eql_incl_dash IDENT { result = [val.first, val[1]] }
+ | eql_incl_dash STRING { result = [val.first, val[1]] }
+ |
+ ;
+ eql_incl_dash
+ : EQUAL { result = :equal }
+ | PREFIXMATCH { result = :prefix_match }
+ | SUFFIXMATCH { result = :suffix_match }
+ | SUBSTRINGMATCH { result = :substring_match }
+ | NOT_EQUAL { result = :not_equal }
+ | INCLUDES { result = :includes }
+ | DASHMATCH { result = :dash_match }
+ ;
+ negation
+ : NOT negation_arg RPAREN {
+ result = Node.new(:NOT, [val[1]])
+ }
+ ;
+ negation_arg
+ : element_name
+ | element_name hcap_1toN
+ | hcap_1toN
+ ;
+ optional_S
+ : S
+ |
+ ;
+end
+
+---- header
+
+require 'nokogiri/css/parser_extras'
diff --git a/test/racc/assets/nonass.y b/test/racc/assets/nonass.y
new file mode 100644
index 0000000000..b9a35a2626
--- /dev/null
+++ b/test/racc/assets/nonass.y
@@ -0,0 +1,41 @@
+#
+# nonassoc test
+#
+
+class P
+
+preclow
+ nonassoc N
+ left P
+prechigh
+
+rule
+
+target : exp
+exp : exp N exp
+ | exp P exp
+ | T
+
+end
+
+---- inner
+
+ def parse
+ @src = [[:T,'T'], [:N,'N'], [:T,'T'], [:N,'N'], [:T,'T']]
+ do_parse
+ end
+
+ def next_token
+ @src.shift
+ end
+
+---- footer
+
+begin
+ P.new.parse
+rescue ParseError
+ exit 0
+else
+ $stderr.puts 'parse error not raised: nonassoc not work'
+ exit 1
+end
diff --git a/test/racc/assets/normal.y b/test/racc/assets/normal.y
new file mode 100644
index 0000000000..96ae352c82
--- /dev/null
+++ b/test/racc/assets/normal.y
@@ -0,0 +1,27 @@
+
+class Testp
+
+ convert
+ A '2'
+ B '3'
+ end
+
+ prechigh
+ left B
+ preclow
+
+rule
+
+/* comment */
+ target: A B C nonterminal { action "string" == /regexp/o
+ 1 /= 3 }
+ ; # comment
+
+ nonterminal: A '+' B = A;
+
+/* end */
+end
+
+---- driver
+
+ # driver is old name
diff --git a/test/racc/assets/norule.y b/test/racc/assets/norule.y
new file mode 100644
index 0000000000..e50a4b3472
--- /dev/null
+++ b/test/racc/assets/norule.y
@@ -0,0 +1,4 @@
+
+class A
+rule
+end
diff --git a/test/racc/assets/nullbug1.y b/test/racc/assets/nullbug1.y
new file mode 100644
index 0000000000..4b267ba0ea
--- /dev/null
+++ b/test/racc/assets/nullbug1.y
@@ -0,0 +1,25 @@
+#
+# number of conflicts must be ZERO.
+#
+
+class T
+
+rule
+
+targ : dummy
+ | a b c
+
+dummy : V v
+
+V : E e
+ | F f
+ |
+ ;
+
+E :
+ ;
+
+F :
+ ;
+
+end
diff --git a/test/racc/assets/nullbug2.y b/test/racc/assets/nullbug2.y
new file mode 100644
index 0000000000..f09ca625e8
--- /dev/null
+++ b/test/racc/assets/nullbug2.y
@@ -0,0 +1,15 @@
+#
+# number of conflicts must be ZERO.
+#
+
+class A
+rule
+ targ: operation voidhead
+ | variable
+
+ voidhead : void B
+ void:
+
+ operation: A
+ variable : A
+end
diff --git a/test/racc/assets/opal.y b/test/racc/assets/opal.y
new file mode 100644
index 0000000000..ae6a5a6bdd
--- /dev/null
+++ b/test/racc/assets/opal.y
@@ -0,0 +1,1807 @@
+# Copyright (C) 2013 by Adam Beynon
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+class Opal::Parser
+
+token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
+ kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT
+ kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kDO_LAMBDA kRETURN kYIELD kSUPER
+ kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD
+ kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__
+ k__FILE__ k__ENCODING__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT
+ tLABEL tCVAR tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT
+ tREGEXP_END tUPLUS tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ tGEQ tLEQ tANDOP
+ tOROP tMATCH tNMATCH tJSDOT tDOT tDOT2 tDOT3 tAREF tASET tLSHFT tRSHFT
+ tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN tLPAREN2 tRPAREN tLPAREN_ARG
+ ARRAY_BEG tRBRACK tLBRACE tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2
+ tTILDE tPERCENT tDIVIDE tPLUS tMINUS tLT tGT tPIPE tBANG tCARET
+ tLCURLY tRCURLY tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG
+ tWORDS_BEG tAWORDS_BEG tSTRING_DBEG tSTRING_DVAR tSTRING_END tSTRING
+ tSYMBOL tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG
+ tLBRACK2 tLBRACK tJSLBRACK tDSTAR
+
+prechigh
+ right tBANG tTILDE tUPLUS
+ right tPOW
+ right tUMINUS_NUM tUMINUS
+ left tSTAR2 tDIVIDE tPERCENT
+ left tPLUS tMINUS
+ left tLSHFT tRSHFT
+ left tAMPER2
+ left tPIPE tCARET
+ left tGT tGEQ tLT tLEQ
+ nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
+ left tANDOP
+ left tOROP
+ nonassoc tDOT2 tDOT3
+ right tEH tCOLON
+ left kRESCUE_MOD
+ right tEQL tOP_ASGN
+ nonassoc kDEFINED
+ right kNOT
+ left kOR kAND
+ nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
+ nonassoc tLBRACE_ARG
+ nonassoc tLOWEST
+preclow
+
+rule
+
+ program: top_compstmt
+
+ top_compstmt: top_stmts opt_terms
+ {
+ result = new_compstmt val[0]
+ }
+
+ top_stmts: # none
+ {
+ result = new_block
+ }
+ | top_stmt
+ {
+ result = new_block val[0]
+ }
+ | top_stmts terms top_stmt
+ {
+ val[0] << val[2]
+ result = val[0]
+ }
+
+ top_stmt: stmt
+ | klBEGIN tLCURLY top_compstmt tRCURLY
+ {
+ result = val[2]
+ }
+
+ bodystmt: compstmt opt_rescue opt_else opt_ensure
+ {
+ result = new_body(val[0], val[1], val[2], val[3])
+ }
+
+ compstmt: stmts opt_terms
+ {
+ result = new_compstmt val[0]
+ }
+
+ stmts: # none
+ {
+ result = new_block
+ }
+ | stmt
+ {
+ result = new_block val[0]
+ }
+ | stmts terms stmt
+ {
+ val[0] << val[2]
+ result = val[0]
+ }
+
+ stmt: kALIAS fitem
+ {
+ lexer.lex_state = :expr_fname
+ }
+ fitem
+ {
+ result = new_alias(val[0], val[1], val[3])
+ }
+ | kALIAS tGVAR tGVAR
+ {
+ result = s(:valias, value(val[1]).to_sym, value(val[2]).to_sym)
+ }
+ | kALIAS tGVAR tBACK_REF
+ | kALIAS tGVAR tNTH_REF
+ {
+ result = s(:valias, value(val[1]).to_sym, value(val[2]).to_sym)
+ }
+ | kUNDEF undef_list
+ {
+ result = val[1]
+ }
+ | stmt kIF_MOD expr_value
+ {
+ result = new_if(val[1], val[2], val[0], nil)
+ }
+ | stmt kUNLESS_MOD expr_value
+ {
+ result = new_if(val[1], val[2], nil, val[0])
+ }
+ | stmt kWHILE_MOD expr_value
+ {
+ result = new_while(val[1], val[2], val[0])
+ }
+ | stmt kUNTIL_MOD expr_value
+ {
+ result = new_until(val[1], val[2], val[0])
+ }
+ | stmt kRESCUE_MOD stmt
+ {
+ result = new_rescue_mod(val[1], val[0], val[2])
+ }
+ | klEND tLCURLY compstmt tRCURLY
+ | lhs tEQL command_call
+ {
+ result = new_assign(val[0], val[1], val[2])
+ }
+ | mlhs tEQL command_call
+ {
+ result = s(:masgn, val[0], s(:to_ary, val[2]))
+ }
+ | var_lhs tOP_ASGN command_call
+ {
+ result = new_op_asgn val[1], val[0], val[2]
+ }
+ | primary_value tLBRACK2 aref_args tRBRACK tOP_ASGN command_call
+ | primary_value tJSLBRACK aref_args tRBRACK tOP_ASGN command_call
+ | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
+ {
+ result = s(:op_asgn2, val[0], op_to_setter(val[2]), value(val[3]).to_sym, val[4])
+ }
+ | primary_value tDOT tCONSTANT tOP_ASGN command_call
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
+ | backref tOP_ASGN command_call
+ | lhs tEQL mrhs
+ {
+ result = new_assign val[0], val[1], s(:svalue, val[2])
+ }
+ | mlhs tEQL arg_value
+ {
+ result = s(:masgn, val[0], s(:to_ary, val[2]))
+ }
+ | mlhs tEQL mrhs
+ {
+ result = s(:masgn, val[0], val[2])
+ }
+ | expr
+
+ expr: command_call
+ | expr kAND expr
+ {
+ result = s(:and, val[0], val[2])
+ }
+ | expr kOR expr
+ {
+ result = s(:or, val[0], val[2])
+ }
+ | kNOT expr
+ {
+ result = new_unary_call(['!', []], val[1])
+ }
+ | tBANG command_call
+ {
+ result = new_unary_call(val[0], val[1])
+ }
+ | arg
+
+ expr_value: expr
+
+ command_call: command
+ | block_command
+ | kRETURN call_args
+ {
+ result = new_return(val[0], val[1])
+ }
+ | kBREAK call_args
+ {
+ result = new_break(val[0], val[1])
+ }
+ | kNEXT call_args
+ {
+ result = new_next(val[0], val[1])
+ }
+
+ block_command: block_call
+ | block_call tJSDOT operation2 command_args
+ | block_call tDOT operation2 command_args
+ | block_call tCOLON2 operation2 command_args
+
+ cmd_brace_block: tLBRACE_ARG opt_block_var compstmt tRCURLY
+
+ command: operation command_args =tLOWEST
+ {
+ result = new_call(nil, val[0], val[1])
+ }
+ | operation command_args cmd_brace_block
+ | primary_value tJSDOT operation2 command_args =tLOWEST
+ {
+ result = new_js_call(val[0], val[2], val[3])
+ }
+ | primary_value tJSDOT operation2 command_args cmd_brace_block
+ | primary_value tDOT operation2 command_args =tLOWEST
+ {
+ result = new_call(val[0], val[2], val[3])
+ }
+ | primary_value tDOT operation2 command_args cmd_brace_block
+ | primary_value tCOLON2 operation2 command_args =tLOWEST
+ {
+ result = new_call(val[0], val[2], val[3])
+ }
+ | primary_value tCOLON2 operation2 command_args cmd_brace_block
+ | kSUPER command_args
+ {
+ result = new_super(val[0], val[1])
+ }
+ | kYIELD command_args
+ {
+ result = new_yield val[1]
+ }
+
+ mlhs: mlhs_basic
+ {
+ result = val[0]
+ }
+ | tLPAREN mlhs_entry tRPAREN
+ {
+ result = val[1]
+ }
+
+ mlhs_entry: mlhs_basic
+ {
+ result = val[0]
+ }
+ | tLPAREN mlhs_entry tRPAREN
+ {
+ result = val[1]
+ }
+
+ mlhs_basic: mlhs_head
+ {
+ result = val[0]
+ }
+ | mlhs_head mlhs_item
+ {
+ result = val[0] << val[1]
+ }
+ | mlhs_head tSTAR mlhs_node
+ {
+ result = val[0] << s(:splat, val[2])
+ }
+ | mlhs_head tSTAR mlhs_node tCOMMA mlhs_post
+ | mlhs_head tSTAR
+ {
+ result = val[0] << s(:splat)
+ }
+ | mlhs_head tSTAR tCOMMA mlhs_post
+ | tSTAR mlhs_node
+ {
+ result = s(:array, s(:splat, val[1]))
+ }
+ | tSTAR
+ {
+ result = s(:array, s(:splat))
+ }
+ | tSTAR tCOMMA mlhs_post
+
+ mlhs_item: mlhs_node
+ {
+ result = val[0]
+ }
+ | tLPAREN mlhs_entry tRPAREN
+ {
+ result = val[1]
+ }
+
+ mlhs_head: mlhs_item tCOMMA
+ {
+ result = s(:array, val[0])
+ }
+ | mlhs_head mlhs_item tCOMMA
+ {
+ result = val[0] << val[1]
+ }
+
+ mlhs_post: mlhs_item
+ | mlhs_post tCOMMA mlhs_item
+
+ mlhs_node: variable
+ {
+ result = new_assignable val[0]
+ }
+ | primary_value tLBRACK2 aref_args tRBRACK
+ {
+ args = val[2] ? val[2] : []
+ result = s(:attrasgn, val[0], :[]=, s(:arglist, *args))
+ }
+ | primary_value tDOT tIDENTIFIER
+ {
+ result = new_call val[0], val[2], []
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ | primary_value tDOT tCONSTANT
+ | primary_value tCOLON2 tCONSTANT
+ | tCOLON3 tCONSTANT
+ | backref
+
+ lhs: variable
+ {
+ result = new_assignable val[0]
+ }
+ | primary_value tJSLBRACK aref_args tRBRACK
+ {
+ result = new_js_attrasgn(val[0], val[2])
+ }
+ | primary_value tLBRACK2 aref_args tRBRACK
+ {
+ result = new_attrasgn(val[0], :[]=, val[2])
+ }
+ | primary_value tDOT tIDENTIFIER
+ {
+ result = new_attrasgn(val[0], op_to_setter(val[2]))
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ result = new_attrasgn(val[0], op_to_setter(val[2]))
+ }
+ | primary_value tDOT tCONSTANT
+ {
+ result = new_attrasgn(val[0], op_to_setter(val[2]))
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = new_colon2(val[0], val[1], val[2])
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = new_colon3(val[0], val[1])
+ }
+ | backref
+
+ cname: tCONSTANT
+
+ cpath: tCOLON3 cname
+ {
+ result = new_colon3(val[0], val[1])
+ }
+ | cname
+ {
+ result = new_const(val[0])
+ }
+ | primary_value tCOLON2 cname
+ {
+ result = new_colon2(val[0], val[1], val[2])
+ }
+
+ fname: tIDENTIFIER
+ | tCONSTANT
+ | tFID
+ | op
+ {
+ lexer.lex_state = :expr_end
+ result = val[0]
+ }
+ | reswords
+ {
+ lexer.lex_state = :expr_end
+ result = val[0]
+ }
+
+ fitem: fname
+ {
+ result = new_sym(val[0])
+ }
+ | symbol
+
+ undef_list: fitem
+ {
+ result = s(:undef, val[0])
+ }
+ | undef_list tCOMMA fitem
+ {
+ result = val[0] << val[2]
+ }
+
+ op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ
+ | tMATCH | tNMATCH | tGT | tGEQ | tLT | tLEQ
+ | tNEQ | tLSHFT | tRSHFT | tPLUS | tMINUS | tSTAR2
+ | tSTAR | tDIVIDE | tPERCENT | tPOW | tBANG | tTILDE
+ | tUPLUS | tUMINUS | tAREF | tASET | tBACK_REF2
+
+ reswords: k__LINE__ | k__FILE__ | klBEGIN | klEND | kALIAS | kAND
+ | kBEGIN | kBREAK | kCASE | kCLASS | kDEF | kDEFINED
+ | kDO | kELSE | kELSIF | kEND | kENSURE | kFALSE
+ | kFOR | kIN | kMODULE | kNEXT | kNIL | kNOT
+ | kOR | kREDO | kRESCUE | kRETRY | kRETURN | kSELF
+ | kSUPER | kTHEN | kTRUE | kUNDEF | kWHEN | kYIELD
+ | kIF_MOD | kUNLESS_MOD | kWHILE_MOD | kUNTIL_MOD | kRESCUE_MOD
+ | kIF | kWHILE | kUNTIL | kUNLESS
+
+ arg: lhs tEQL arg
+ {
+ result = new_assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL arg kRESCUE_MOD arg
+ {
+ result = new_assign val[0], val[1], s(:rescue_mod, val[2], val[4])
+ }
+ | var_lhs tOP_ASGN arg
+ {
+ result = new_op_asgn val[1], val[0], val[2]
+ }
+ | primary_value tLBRACK2 aref_args tRBRACK tOP_ASGN arg
+ {
+ result = new_op_asgn1(val[0], val[2], val[4], val[5])
+ }
+ | primary_value tJSLBRACK aref_args tRBRACK tOP_ASGN arg
+ {
+ raise ".JS[...] #{val[4]} is not supported"
+ }
+ | primary_value tDOT tIDENTIFIER tOP_ASGN arg
+ {
+ result = s(:op_asgn2, val[0], op_to_setter(val[2]), value(val[3]).to_sym, val[4])
+ }
+ | primary_value tDOT tCONSTANT tOP_ASGN arg
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
+ | tCOLON3 tCONSTANT tOP_ASGN arg
+ | backref tOP_ASGN arg
+ | arg tDOT2 arg
+ {
+ result = new_irange(val[0], val[1], val[2])
+ }
+ | arg tDOT3 arg
+ {
+ result = new_erange(val[0], val[1], val[2])
+ }
+ | arg tPLUS arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tMINUS arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tSTAR2 arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tDIVIDE arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tPERCENT arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tPOW arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | '-@NUM' tINTEGER tPOW arg
+ {
+ result = new_call new_binary_call(new_int(val[1]), val[2], val[3]), [:"-@", []], []
+ }
+ | '-@NUM' tFLOAT tPOW arg
+ {
+ result = new_call new_binary_call(new_float(val[1]), val[2], val[3]), [:"-@", []], []
+ }
+ | tUPLUS arg
+ {
+ result = new_call val[1], [:"+@", []], []
+ if [:int, :float].include? val[1].type
+ result = val[1]
+ end
+ }
+ | tUMINUS arg
+ {
+ result = new_call val[1], [:"-@", []], []
+ if val[1].type == :int
+ val[1][1] = -val[1][1]
+ result = val[1]
+ elsif val[1].type == :float
+ val[1][1] = -val[1][1].to_f
+ result = val[1]
+ end
+ }
+ | arg tPIPE arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tCARET arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tAMPER2 arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tCMP arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tGT arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tGEQ arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tLT arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tLEQ arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tEQ arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tEQQ arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tNEQ arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tMATCH arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tNMATCH arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | tBANG arg
+ {
+ result = new_unary_call(val[0], val[1])
+ }
+ | tTILDE arg
+ {
+ result = new_unary_call(val[0], val[1])
+ }
+ | arg tLSHFT arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tRSHFT arg
+ {
+ result = new_binary_call(val[0], val[1], val[2])
+ }
+ | arg tANDOP arg
+ {
+ result = new_and(val[0], val[1], val[2])
+ }
+ | arg tOROP arg
+ {
+ result = new_or(val[0], val[1], val[2])
+ }
+ | kDEFINED opt_nl arg
+ {
+ result = s(:defined, val[2])
+ }
+ | arg tEH arg tCOLON arg
+ {
+ result = new_if(val[1], val[0], val[2], val[4])
+ }
+ | primary
+
+ arg_value: arg
+
+ aref_args: none
+ {
+ result = nil
+ }
+ | command opt_nl
+ {
+ result = [val[0]]
+ }
+ | args trailer
+ {
+ result = val[0]
+ }
+ | args tCOMMA assocs trailer
+ {
+ val[0] << s(:hash, *val[2])
+ result = val[0]
+ }
+ | assocs trailer
+ {
+ result = [s(:hash, *val[0])]
+ }
+
+ paren_args: tLPAREN2 opt_call_args rparen
+ {
+ result = val[1]
+ }
+
+ rparen: opt_nl tRPAREN
+
+ opt_paren_args: none
+ {
+ result = []
+ }
+ | paren_args
+
+ opt_call_args: none
+ {
+ result = []
+ }
+ | call_args
+ | args tCOMMA
+ {
+ result = val[0]
+ }
+ | args tCOMMA assocs tCOMMA
+ {
+ result = val[0]
+ result << new_hash(nil, val[2], nil)
+ }
+ | assocs tCOMMA
+ {
+ result = [new_hash(nil, val[0], nil)]
+ }
+
+ call_args: command
+ {
+ result = [val[0]]
+ }
+ | args opt_block_arg
+ {
+ result = val[0]
+ add_block_pass val[0], val[1]
+ }
+ | assocs opt_block_arg
+ {
+ result = [new_hash(nil, val[0], nil)]
+ add_block_pass result, val[1]
+ }
+ | args tCOMMA assocs opt_block_arg
+ {
+ result = val[0]
+ result << new_hash(nil, val[2], nil)
+ result << val[3] if val[3]
+ }
+ | block_arg
+ {
+ result = []
+ add_block_pass result, val[0]
+ }
+
+ call_args2: arg_value tCOMMA args opt_block_arg
+ | block_arg
+
+ command_args: {
+ lexer.cmdarg_push 1
+ }
+ open_args
+ {
+ lexer.cmdarg_pop
+ result = val[1]
+ }
+
+ open_args: call_args
+ | tLPAREN_ARG tRPAREN
+ {
+ result = nil
+ }
+ | tLPAREN_ARG call_args2 tRPAREN
+ {
+ result = val[1]
+ }
+
+ block_arg: tAMPER arg_value
+ {
+ result = new_block_pass(val[0], val[1])
+ }
+
+ opt_block_arg: tCOMMA block_arg
+ {
+ result = val[1]
+ }
+ | # none
+ {
+ result = nil
+ }
+
+ args: arg_value
+ {
+ result = [val[0]]
+ }
+ | tSTAR arg_value
+ {
+ result = [new_splat(val[0], val[1])]
+ }
+ | args tCOMMA arg_value
+ {
+ result = val[0] << val[2]
+ }
+ | args tCOMMA tSTAR arg_value
+ {
+ result = val[0] << new_splat(val[2], val[3])
+ }
+
+ mrhs: args tCOMMA arg_value
+ {
+ val[0] << val[2]
+ result = s(:array, *val[0])
+ }
+ | args tCOMMA tSTAR arg_value
+ {
+ val[0] << s(:splat, val[3])
+ result = s(:array, *val[0])
+ }
+ | tSTAR arg_value
+ {
+ result = s(:splat, val[1])
+ }
+
+ primary: literal
+ | strings
+ | xstring
+ | regexp
+ | words
+ | awords
+ | var_ref
+ | backref
+ | tFID
+ | kBEGIN
+ {
+ result = lexer.line
+ }
+ bodystmt kEND
+ {
+ result = s(:begin, val[2])
+ }
+ | tLPAREN_ARG expr opt_nl tRPAREN
+ {
+ result = val[1]
+ }
+ | tLPAREN compstmt tRPAREN
+ {
+ result = new_paren(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = new_colon2(val[0], val[1], val[2])
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = new_colon3(val[0], val[1])
+ }
+ | primary_value tLBRACK2 aref_args tRBRACK
+ {
+ result = new_call val[0], [:[], []], val[2]
+ }
+ | primary_value tJSLBRACK aref_args tRBRACK
+ {
+ result = new_js_call val[0], [:[], []], val[2]
+ }
+ | tLBRACK aref_args tRBRACK
+ {
+ result = new_array(val[0], val[1], val[2])
+ }
+ | tLBRACE assoc_list tRCURLY
+ {
+ result = new_hash(val[0], val[1], val[2])
+ }
+ | kRETURN
+ {
+ result = new_return(val[0])
+ }
+ | kYIELD tLPAREN2 call_args tRPAREN
+ {
+ result = new_yield val[2]
+ }
+ | kYIELD tLPAREN2 tRPAREN
+ {
+ result = s(:yield)
+ }
+ | kYIELD
+ {
+ result = s(:yield)
+ }
+ | kDEFINED opt_nl tLPAREN2 expr tRPAREN
+ {
+ result = s(:defined, val[3])
+ }
+ | kNOT tLPAREN2 expr tRPAREN
+ {
+ result = new_unary_call(['!', []], val[2])
+ }
+ | kNOT tLPAREN2 tRPAREN
+ {
+ result = new_unary_call(['!', []], new_nil(val[0]))
+ }
+ | operation brace_block
+ {
+ result = new_call(nil, val[0], [])
+ result << val[1]
+ }
+ | method_call
+ | method_call brace_block
+ {
+ val[0] << val[1]
+ result = val[0]
+ }
+ | tLAMBDA lambda
+ {
+ result = val[1]
+ }
+ | kIF expr_value then compstmt if_tail kEND
+ {
+ result = new_if(val[0], val[1], val[3], val[4])
+ }
+ | kUNLESS expr_value then compstmt opt_else kEND
+ {
+ result = new_if(val[0], val[1], val[4], val[3])
+ }
+ | kWHILE
+ {
+ lexer.cond_push 1
+ result = lexer.line
+ }
+ expr_value do
+ {
+ lexer.cond_pop
+ }
+ compstmt kEND
+ {
+ result = s(:while, val[2], val[5])
+ }
+ | kUNTIL
+ {
+ lexer.cond_push 1
+ result = lexer.line
+ }
+ expr_value do
+ {
+ lexer.cond_pop
+ }
+ compstmt kEND
+ {
+ result = s(:until, val[2], val[5])
+ }
+ | kCASE expr_value opt_terms case_body kEND
+ {
+ result = s(:case, val[1], *val[3])
+ }
+ | kCASE opt_terms case_body kEND
+ {
+ result = s(:case, nil, *val[2])
+ }
+ | kCASE opt_terms kELSE compstmt kEND
+ {
+ result = s(:case, nil, val[3])
+ }
+ | kFOR for_var kIN
+ {
+ lexer.cond_push 1
+ result = lexer.line
+ }
+ expr_value do
+ {
+ lexer.cond_pop
+ }
+ compstmt kEND
+ {
+ result = s(:for, val[4], val[1], val[7])
+ }
+ | kCLASS cpath superclass
+ {
+ # ...
+ }
+ bodystmt kEND
+ {
+ result = new_class val[0], val[1], val[2], val[4], val[5]
+ }
+ | kCLASS tLSHFT
+ {
+ result = lexer.line
+ }
+ expr term
+ {
+ # ...
+ }
+ bodystmt kEND
+ {
+ result = new_sclass(val[0], val[3], val[6], val[7])
+ }
+ | kMODULE
+ {
+ result = lexer.line
+ }
+ cpath
+ {
+ # ...
+ }
+ bodystmt kEND
+ {
+ result = new_module(val[0], val[2], val[4], val[5])
+ }
+ | kDEF fname
+ {
+ push_scope
+ lexer.lex_state = :expr_endfn
+ }
+ f_arglist bodystmt kEND
+ {
+ result = new_def(val[0], nil, val[1], val[3], val[4], val[5])
+ pop_scope
+ }
+ | kDEF singleton dot_or_colon
+ {
+ lexer.lex_state = :expr_fname
+ }
+ fname
+ {
+ push_scope
+ lexer.lex_state = :expr_endfn
+ }
+ f_arglist bodystmt kEND
+ {
+ result = new_def(val[0], val[1], val[4], val[6], val[7], val[8])
+ pop_scope
+ }
+ | kBREAK
+ {
+ result = new_break(val[0])
+ }
+ | kNEXT
+ {
+ result = s(:next)
+ }
+ | kREDO
+ {
+ result = s(:redo)
+ }
+ | kRETRY
+
+ primary_value: primary
+
+ then: term
+ | tCOLON
+ | kTHEN
+ | term kTHEN
+
+ do: term
+ | tCOLON
+ | kDO_COND
+
+ lambda: f_larglist lambda_body
+ {
+ result = new_call nil, [:lambda, []], []
+ result << new_iter(val[0], val[1])
+ }
+
+ f_larglist: tLPAREN2 block_param tRPAREN
+ {
+ result = val[1]
+ }
+ | tLPAREN2 tRPAREN
+ {
+ result = nil
+ }
+ | block_param
+ | none
+
+ lambda_body: tLAMBEG compstmt tRCURLY
+ {
+ result = val[1]
+ }
+ | kDO_LAMBDA compstmt kEND
+ {
+ result = val[1]
+ }
+
+ if_tail: opt_else
+ {
+ result = val[0]
+ }
+ | kELSIF expr_value then compstmt if_tail
+ {
+ result = new_if(val[0], val[1], val[3], val[4])
+ }
+
+ opt_else: none
+ | kELSE compstmt
+ {
+ result = val[1]
+ }
+
+ f_block_optarg: f_block_opt
+ {
+ result = s(:block, val[0])
+ }
+ | f_block_optarg tCOMMA f_block_opt
+ {
+ val[0] << val[2]
+ result = val[0]
+ }
+
+ f_block_opt: tIDENTIFIER tEQL primary_value
+ {
+ result = new_assign(new_assignable(new_ident(
+ val[0])), val[1], val[2])
+ }
+
+ opt_block_var: none
+ | tPIPE tPIPE
+ {
+ result = nil
+ }
+ | tOROP
+ {
+ result = nil
+ }
+ | tPIPE block_param tPIPE
+ {
+ result = val[1]
+ }
+
+ block_args_tail: f_block_arg
+ {
+ result = val[0]
+ }
+
+opt_block_args_tail: tCOMMA block_args_tail
+ {
+ result = val[1]
+ }
+ | none
+ {
+ nil
+ }
+
+ block_param: f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
+ {
+ result = new_block_args(val[0], val[2], val[4], val[5])
+ }
+ | f_arg tCOMMA f_block_optarg opt_block_args_tail
+ {
+ result = new_block_args(val[0], val[2], nil, val[3])
+ }
+ | f_arg tCOMMA f_rest_arg opt_block_args_tail
+ {
+ result = new_block_args(val[0], nil, val[2], val[3])
+ }
+ | f_arg tCOMMA
+ {
+ result = new_block_args(val[0], nil, nil, nil)
+ }
+ | f_arg opt_block_args_tail
+ {
+ result = new_block_args(val[0], nil, nil, val[1])
+ }
+ | f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
+ {
+ result = new_block_args(nil, val[0], val[2], val[3])
+ }
+ | f_block_optarg opt_block_args_tail
+ {
+ result = new_block_args(nil, val[0], nil, val[1])
+ }
+ | f_rest_arg opt_block_args_tail
+ {
+ result = new_block_args(nil, nil, val[0], val[1])
+ }
+ | block_args_tail
+ {
+ result = new_block_args(nil, nil, nil, val[0])
+ }
+
+ do_block: kDO_BLOCK
+ {
+ push_scope :block
+ result = lexer.line
+ }
+ opt_block_var compstmt kEND
+ {
+ result = new_iter val[2], val[3]
+ pop_scope
+ }
+
+ block_call: command do_block
+ {
+ val[0] << val[1]
+ result = val[0]
+ }
+ | block_call tJSDOT operation2 opt_paren_args
+ | block_call tDOT operation2 opt_paren_args
+ | block_call tCOLON2 operation2 opt_paren_args
+
+ method_call: operation paren_args
+ {
+ result = new_call(nil, val[0], val[1])
+ }
+ | primary_value tDOT operation2 opt_paren_args
+ {
+ result = new_call(val[0], val[2], val[3])
+ }
+ | primary_value tJSDOT operation2 opt_paren_args
+ {
+ result = new_js_call(val[0], val[2], val[3])
+ }
+ | primary_value tDOT paren_args
+ {
+ result = new_call(val[0], [:call, []], val[2])
+ }
+ | primary_value tCOLON2 operation2 paren_args
+ {
+ result = new_call(val[0], val[2], val[3])
+ }
+ | primary_value tCOLON2 operation3
+ {
+ result = new_call(val[0], val[2])
+ }
+ | kSUPER paren_args
+ {
+ result = new_super(val[0], val[1])
+ }
+ | kSUPER
+ {
+ result = new_super(val[0], nil)
+ }
+
+ brace_block: tLCURLY
+ {
+ push_scope :block
+ result = lexer.line
+ }
+ opt_block_var compstmt tRCURLY
+ {
+ result = new_iter val[2], val[3]
+ pop_scope
+ }
+ | kDO
+ {
+ push_scope :block
+ result = lexer.line
+ }
+ opt_block_var compstmt kEND
+ {
+ result = new_iter val[2], val[3]
+ pop_scope
+ }
+
+ case_body: kWHEN
+ {
+ result = lexer.line
+ }
+ args then compstmt cases
+ {
+ part = s(:when, s(:array, *val[2]), val[4])
+ result = [part]
+ result.push(*val[5]) if val[5]
+ }
+
+ cases: opt_else
+ {
+ result = [val[0]]
+ }
+ | case_body
+
+ opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue
+ {
+ exc = val[1] || s(:array)
+ exc << new_assign(val[2], val[2], s(:gvar, '$!'.intern)) if val[2]
+ result = [s(:resbody, exc, val[4])]
+ result.push val[5].first if val[5]
+ }
+ | # none
+ {
+ result = nil
+ }
+
+ exc_list: arg_value
+ {
+ result = s(:array, val[0])
+ }
+ | mrhs
+ | none
+
+ exc_var: tASSOC lhs
+ {
+ result = val[1]
+ }
+ | none
+ {
+ result = nil
+ }
+
+ opt_ensure: kENSURE compstmt
+ {
+ result = val[1].nil? ? s(:nil) : val[1]
+ }
+ | none
+
+ literal: numeric
+ | symbol
+ | dsym
+
+ strings: string
+ {
+ result = new_str val[0]
+ }
+
+ string: string1
+ | string string1
+ {
+ result = str_append val[0], val[1]
+ }
+
+ string1: tSTRING_BEG string_contents tSTRING_END
+ {
+ result = val[1]
+ }
+ | tSTRING
+ {
+ result = s(:str, value(val[0]))
+ }
+
+ xstring: tXSTRING_BEG xstring_contents tSTRING_END
+ {
+ result = new_xstr(val[0], val[1], val[2])
+ }
+
+ regexp: tREGEXP_BEG xstring_contents tREGEXP_END
+ {
+ result = new_regexp val[1], val[2]
+ }
+
+ words: tWORDS_BEG tSPACE tSTRING_END
+ {
+ result = s(:array)
+ }
+ | tWORDS_BEG word_list tSTRING_END
+ {
+ result = val[1]
+ }
+
+ word_list: none
+ {
+ result = s(:array)
+ }
+ | word_list word tSPACE
+ {
+ part = val[1]
+ part = s(:dstr, "", val[1]) if part.type == :evstr
+ result = val[0] << part
+ }
+
+ word: string_content
+ {
+ result = val[0]
+ }
+ | word string_content
+ {
+ result = val[0].concat([val[1]])
+ }
+
+ awords: tAWORDS_BEG tSPACE tSTRING_END
+ {
+ result = s(:array)
+ }
+ | tAWORDS_BEG qword_list tSTRING_END
+ {
+ result = val[1]
+ }
+
+ qword_list: none
+ {
+ result = s(:array)
+ }
+ | qword_list tSTRING_CONTENT tSPACE
+ {
+ result = val[0] << s(:str, value(val[1]))
+ }
+
+ string_contents: none
+ {
+ result = nil
+ }
+ | string_contents string_content
+ {
+ result = str_append val[0], val[1]
+ }
+
+xstring_contents: none
+ {
+ result = nil
+ }
+ | xstring_contents string_content
+ {
+ result = str_append val[0], val[1]
+ }
+
+ string_content: tSTRING_CONTENT
+ {
+ result = new_str_content(val[0])
+ }
+ | tSTRING_DVAR
+ {
+ result = lexer.strterm
+ lexer.strterm = nil
+ }
+ string_dvar
+ {
+ lexer.strterm = val[1]
+ result = new_evstr(val[2])
+ }
+ | tSTRING_DBEG
+ {
+ lexer.cond_push 0
+ lexer.cmdarg_push 0
+ result = lexer.strterm
+ lexer.strterm = nil
+ lexer.lex_state = :expr_beg
+ }
+ compstmt tRCURLY
+ {
+ lexer.strterm = val[1]
+ lexer.cond_lexpop
+ lexer.cmdarg_lexpop
+ result = new_evstr(val[2])
+ }
+
+ string_dvar: tGVAR
+ {
+ result = new_gvar(val[0])
+ }
+ | tIVAR
+ {
+ result = new_ivar(val[0])
+ }
+ | tCVAR
+ {
+ result = new_cvar(val[0])
+ }
+ | backref
+
+
+ symbol: tSYMBEG sym
+ {
+ result = new_sym(val[1])
+ lexer.lex_state = :expr_end
+ }
+ | tSYMBOL
+ {
+ result = new_sym(val[0])
+ }
+
+ sym: fname
+ | tIVAR
+ | tGVAR
+ | tCVAR
+
+ dsym: tSYMBEG xstring_contents tSTRING_END
+ {
+ result = new_dsym val[1]
+ }
+
+ numeric: tINTEGER
+ {
+ result = new_int(val[0])
+ }
+ | tFLOAT
+ {
+ result = new_float(val[0])
+ }
+ | '-@NUM' tINTEGER =tLOWEST
+ {
+ result = negate_num(new_int(val[1]))
+ }
+ | '-@NUM' tFLOAT =tLOWEST
+ {
+ result = negate_num(new_float(val[1]))
+ }
+ | '+@NUM' tINTEGER =tLOWEST
+ {
+ result = new_int(val[1])
+ }
+ | '+@NUM' tFLOAT =tLOWEST
+ {
+ result = new_float(val[1])
+ }
+
+ variable: tIDENTIFIER
+ {
+ result = new_ident(val[0])
+ }
+ | tIVAR
+ {
+ result = new_ivar(val[0])
+ }
+ | tGVAR
+ {
+ result = new_gvar(val[0])
+ }
+ | tCONSTANT
+ {
+ result = new_const(val[0])
+ }
+ | tCVAR
+ {
+ result = new_cvar(val[0])
+ }
+ | kNIL
+ {
+ result = new_nil(val[0])
+ }
+ | kSELF
+ {
+ result = new_self(val[0])
+ }
+ | kTRUE
+ {
+ result = new_true(val[0])
+ }
+ | kFALSE
+ {
+ result = new_false(val[0])
+ }
+ | k__FILE__
+ {
+ result = new___FILE__(val[0])
+ }
+ | k__LINE__
+ {
+ result = new___LINE__(val[0])
+ }
+
+ var_ref: variable
+ {
+ result = new_var_ref(val[0])
+ }
+
+ var_lhs: variable
+ {
+ result = new_assignable val[0]
+ }
+
+ backref: tNTH_REF
+ {
+ result = s(:nth_ref, value(val[0]))
+ }
+ | tBACK_REF
+
+ superclass: term
+ {
+ result = nil
+ }
+ | tLT expr_value term
+ {
+ result = val[1]
+ }
+ | error term
+ {
+ result = nil
+ }
+
+ f_arglist: tLPAREN2 f_args opt_nl tRPAREN
+ {
+ result = val[1]
+ lexer.lex_state = :expr_beg
+ }
+ | f_args term
+ {
+ result = val[0]
+ lexer.lex_state = :expr_beg
+ }
+
+ kwrest_mark: tPOW
+ | tDSTAR
+
+ f_kwrest: kwrest_mark tIDENTIFIER
+ {
+ result = new_kwrestarg(val[1])
+ }
+ | kwrest_mark
+ {
+ result = new_kwrestarg()
+ }
+
+ f_label: tLABEL
+ {
+ result = new_sym(val[0])
+ }
+
+ f_kw: f_label arg_value
+ {
+ result = new_kwoptarg(val[0], val[1])
+ }
+ | f_label
+ {
+ result = new_kwarg(val[0])
+ }
+
+ f_kwarg: f_kw
+ {
+ result = [val[0]]
+ }
+ | f_kwarg tCOMMA f_kw
+ {
+ result = val[0]
+ result << val[2]
+ }
+
+ args_tail: f_kwarg tCOMMA f_kwrest opt_f_block_arg
+ {
+ result = new_args_tail(val[0], val[2], val[3])
+ }
+ | f_kwarg opt_f_block_arg
+ {
+ result = new_args_tail(val[0], nil, val[1])
+ }
+ | f_kwrest opt_f_block_arg
+ {
+ result = new_args_tail(nil, val[0], val[1])
+ }
+ | f_block_arg
+ {
+ result = new_args_tail(nil, nil, val[0])
+ }
+
+ opt_args_tail: tCOMMA args_tail
+ {
+ result = val[1]
+ }
+ | # none
+ {
+ result = new_args_tail(nil, nil, nil)
+ }
+
+ f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_args_tail
+ {
+ result = new_args(val[0], val[2], val[4], val[5])
+ }
+ | f_arg tCOMMA f_optarg opt_args_tail
+ {
+ result = new_args(val[0], val[2], nil, val[3])
+ }
+ | f_arg tCOMMA f_rest_arg opt_args_tail
+ {
+ result = new_args(val[0], nil, val[2], val[3])
+ }
+ | f_arg opt_args_tail
+ {
+ result = new_args(val[0], nil, nil, val[1])
+ }
+ | f_optarg tCOMMA f_rest_arg opt_args_tail
+ {
+ result = new_args(nil, val[0], val[2], val[3])
+ }
+ | f_optarg opt_args_tail
+ {
+ result = new_args(nil, val[0], nil, val[1])
+ }
+ | f_rest_arg opt_args_tail
+ {
+ result = new_args(nil, nil, val[0], val[1])
+ }
+ | args_tail
+ {
+ result = new_args(nil, nil, nil, val[0])
+ }
+ | # none
+ {
+ result = new_args(nil, nil, nil, nil)
+ }
+
+ f_norm_arg: f_bad_arg
+ | tIDENTIFIER
+ {
+ result = value(val[0]).to_sym
+ scope.add_local result
+ }
+
+ f_bad_arg: tCONSTANT
+ {
+ raise 'formal argument cannot be a constant'
+ }
+ | tIVAR
+ {
+ raise 'formal argument cannot be an instance variable'
+ }
+ | tCVAR
+ {
+ raise 'formal argument cannot be a class variable'
+ }
+ | tGVAR
+ {
+ raise 'formal argument cannot be a global variable'
+ }
+
+ f_arg_item: f_norm_arg
+ {
+ result = val[0]
+ }
+ | tLPAREN f_margs tRPAREN
+ {
+ result = val[1]
+ }
+
+ for_var: lhs
+ | mlhs
+
+ f_marg: f_norm_arg
+ {
+ result = s(:lasgn, val[0])
+ }
+ | tLPAREN f_margs tRPAREN
+
+ f_marg_list: f_marg
+ {
+ result = s(:array, val[0])
+ }
+ | f_marg_list tCOMMA f_marg
+ {
+ val[0] << val[2]
+ result = val[0]
+ }
+
+ f_margs: f_marg_list
+ | f_marg_list tCOMMA tSTAR f_norm_arg
+ | f_marg_list tCOMMA tSTAR
+ | tSTAR f_norm_arg
+ | tSTAR
+
+ f_arg: f_arg_item
+ {
+ result = [val[0]]
+ }
+ | f_arg tCOMMA f_arg_item
+ {
+ val[0] << val[2]
+ result = val[0]
+ }
+
+ f_opt: tIDENTIFIER tEQL arg_value
+ {
+ result = new_assign(new_assignable(new_ident(val[0])), val[1], val[2])
+ }
+
+ f_optarg: f_opt
+ {
+ result = s(:block, val[0])
+ }
+ | f_optarg tCOMMA f_opt
+ {
+ result = val[0]
+ val[0] << val[2]
+ }
+
+ restarg_mark: tSTAR2
+ | tSTAR
+
+ f_rest_arg: restarg_mark tIDENTIFIER
+ {
+ result = "*#{value(val[1])}".to_sym
+ }
+ | restarg_mark
+ {
+ result = :"*"
+ }
+
+ blkarg_mark: tAMPER2
+ | tAMPER
+
+ f_block_arg: blkarg_mark tIDENTIFIER
+ {
+ result = "&#{value(val[1])}".to_sym
+ }
+
+ opt_f_block_arg: tCOMMA f_block_arg
+ {
+ result = val[1]
+ }
+ | # none
+ {
+ result = nil
+ }
+
+ singleton: var_ref
+ {
+ result = val[0]
+ }
+ | tLPAREN2 expr opt_nl tRPAREN
+ {
+ result = val[1]
+ }
+
+ assoc_list: # none
+ {
+ result = []
+ }
+ | assocs trailer
+ {
+ result = val[0]
+ }
+
+ assocs: assoc
+ {
+ result = val[0]
+ }
+ | assocs tCOMMA assoc
+ {
+ result = val[0].push(*val[2])
+ }
+
+ assoc: arg_value tASSOC arg_value
+ {
+ result = [val[0], val[2]]
+ }
+ | tLABEL arg_value
+ {
+ result = [new_sym(val[0]), val[1]]
+ }
+
+ operation: tIDENTIFIER
+ | tCONSTANT
+ | tFID
+
+ operation2: tIDENTIFIER
+ | tCONSTANT
+ | tFID
+ | op
+
+ operation3: tIDENTIFIER
+ | tFID
+ | op
+
+ dot_or_colon: tDOT
+ | tCOLON2
+
+ opt_terms: # none
+ | terms
+
+ opt_nl: # none
+ | tNL
+
+ trailer: # none
+ | tNL
+ | tCOMMA
+
+ term: tSEMI
+ | tNL
+
+ terms: term
+ | terms tSEMI
+
+ none: # none
+ {
+ result = nil
+ }
+end
+
+---- inner
diff --git a/test/racc/assets/opt.y b/test/racc/assets/opt.y
new file mode 100644
index 0000000000..a011953a51
--- /dev/null
+++ b/test/racc/assets/opt.y
@@ -0,0 +1,123 @@
+#
+# check options working
+#
+
+class Calcp
+
+ prechigh
+ left '*' '/'
+ left '+' '-'
+ preclow
+
+ convert
+ NUMBER 'Number'
+ end
+
+ options no_omit_action_call no_result_var
+
+rule
+
+ target : exp | /* none */ { 0 } ;
+
+ exp : exp '+' exp { chk(val[0] + val[2]) }
+ | exp '-' exp { chk(val[0] - val[2]) }
+ | exp '*' exp { chk(val[0] * val[2]) }
+ | exp '/' exp { chk(val[0] / val[2]) }
+ | '(' { $emb = true } exp ')'
+ {
+ raise 'must not happen' unless $emb
+ val[2]
+ }
+ | '-' NUMBER { -val[1] }
+ | NUMBER
+ ;
+
+end
+
+----header
+
+class Number; end
+
+----inner
+
+ def parse( src )
+ @src = src
+ do_parse
+ end
+
+ def next_token
+ @src.shift
+ end
+
+ def initialize
+ @yydebug = true
+ end
+
+ def chk( i )
+ # p i
+ i
+ end
+
+----footer
+
+$parser = Calcp.new
+$test_number = 1
+
+def chk( src, ans )
+ result = $parser.parse(src)
+ raise "test #{$test_number} failed" unless result == ans
+ $test_number += 1
+end
+
+chk(
+ [ [Number, 9],
+ [false, false],
+ [false, false] ], 9
+)
+
+chk(
+ [ [Number, 5],
+ ['*', nil],
+ [Number, 1],
+ ['-', nil],
+ [Number, 1],
+ ['*', nil],
+ [Number, 8],
+ [false, false],
+ [false, false] ], -3
+)
+
+chk(
+ [ [Number, 5],
+ ['+', nil],
+ [Number, 2],
+ ['-', nil],
+ [Number, 5],
+ ['+', nil],
+ [Number, 2],
+ ['-', nil],
+ [Number, 5],
+ [false, false],
+ [false, false] ], -1
+)
+
+chk(
+ [ ['-', nil],
+ [Number, 4],
+ [false, false],
+ [false, false] ], -4
+)
+
+chk(
+ [ [Number, 7],
+ ['*', nil],
+ ['(', nil],
+ [Number, 4],
+ ['+', nil],
+ [Number, 3],
+ [')', nil],
+ ['-', nil],
+ [Number, 9],
+ [false, false],
+ [false, false] ], 40
+)
diff --git a/test/racc/assets/percent.y b/test/racc/assets/percent.y
new file mode 100644
index 0000000000..68d63583ca
--- /dev/null
+++ b/test/racc/assets/percent.y
@@ -0,0 +1,35 @@
+class ScannerChecker
+rule
+ target: A
+ {
+ i = 7
+ i %= 4
+ raise 'assert failed' unless i == 3
+ tmp = %-This is percent string.-
+ raise 'assert failed' unless tmp == 'This is percent string.'
+ a = 5; b = 3
+ assert_equal(2,(a%b)) #A
+ # assert_equal(2,(a %b)) # is %-string
+ assert_equal(2,(a% b)) #B
+ assert_equal(2,(a % b)) #C
+ }
+end
+
+---- inner ----
+
+ def parse
+ @q = [[:A, 'A'], [false, '$']]
+ do_parse
+ end
+
+ def next_token
+ @q.shift
+ end
+
+ def assert_equal( expect, real )
+ raise "expect #{expect.inspect} but #{real.inspect}" unless expect == real
+ end
+
+---- footer ----
+
+parser = ScannerChecker.new.parse
diff --git a/test/racc/assets/php_serialization.y b/test/racc/assets/php_serialization.y
new file mode 100644
index 0000000000..99f78f2081
--- /dev/null
+++ b/test/racc/assets/php_serialization.y
@@ -0,0 +1,98 @@
+# MIT License
+# See https://github.com/divoxx/ruby-php-serialization/blob/master/LICENSE.txt
+
+class PhpSerialization::Unserializer
+rule
+
+ data : null ';' { @object = val[0] }
+ | bool ';' { @object = val[0] }
+ | integer ';' { @object = val[0] }
+ | double ';' { @object = val[0] }
+ | string ';' { @object = val[0] }
+ | assoc_array { @object = val[0] }
+ | object { @object = val[0] }
+ ;
+
+ null : 'N' { result = nil }
+ ;
+
+ bool : 'b' ':' NUMBER { result = Integer(val[2]) > 0 }
+ ;
+
+ integer : 'i' ':' NUMBER { result = Integer(val[2]) }
+ ;
+
+ double : 'd' ':' NUMBER { result = Float(val[2]) }
+ ;
+
+ string : 's' ':' NUMBER ':' STRING { result = val[4] }
+ ;
+
+ object : 'O' ':' NUMBER ':' STRING ':' NUMBER ':' '{' attribute_list '}'
+ {
+ if eval("defined?(#{val[4]})")
+ result = Object.const_get(val[4]).new
+
+ val[9].each do |(attr_name, value)|
+ # Protected and private attributes will have a \0..\0 prefix
+ attr_name = attr_name.gsub(/\A\\0[^\\]+\\0/, '')
+ result.instance_variable_set("@#{attr_name}", value)
+ end
+ else
+ klass_name = val[4].gsub(/^Struct::/, '')
+ attr_names, values = [], []
+
+ val[9].each do |(attr_name, value)|
+ # Protected and private attributes will have a \0..\0 prefix
+ attr_names << attr_name.gsub(/\A\\0[^\\]+\\0/, '')
+ values << value
+ end
+
+ result = Struct.new(klass_name, *attr_names).new(*values)
+ result.instance_variable_set("@_php_class", klass_name)
+ end
+ }
+ ;
+
+ attribute_list : attribute_list attribute { result = val[0] << val[1] }
+ | { result = [] }
+ ;
+
+ attribute : data data { result = val }
+ ;
+
+ assoc_array : 'a' ':' NUMBER ':' '{' attribute_list '}'
+ {
+ # Checks if the keys are a sequence of integers
+ idx = -1
+ arr = val[5].all? { |(k,v)| k == (idx += 1) }
+
+ if arr
+ result = val[5].map { |(k,v)| v }
+ else
+ result = Hash[val[5]]
+ end
+ }
+ ;
+
+end
+
+---- header ----
+require 'php_serialization/tokenizer'
+
+---- inner ----
+ def initialize(tokenizer_klass = Tokenizer)
+ @tokenizer_klass = tokenizer_klass
+ end
+
+ def run(string)
+ @tokenizer = @tokenizer_klass.new(string)
+ yyparse(@tokenizer, :each)
+ return @object
+ ensure
+ @tokenizer = nil
+ end
+
+ def next_token
+ @tokenizer.next_token
+ end
diff --git a/test/racc/assets/recv.y b/test/racc/assets/recv.y
new file mode 100644
index 0000000000..0c672b3b6c
--- /dev/null
+++ b/test/racc/assets/recv.y
@@ -0,0 +1,97 @@
+# s/r 5, r/r 10
+class A
+rule
+
+ content: RecvH received
+ ;
+
+ datetime: day
+ ;
+
+ msgid: '<' spec '>';
+
+ day:
+ | ATOM ','
+ ;
+
+ received: recvitem_list recvdatetime
+ ;
+
+ recvitem_list:
+ | recvitem_list recvitem
+ ;
+
+ recvitem: by | via | with | for ;
+
+ by:
+ | BY domain
+ ;
+
+ via:
+ | VIA ATOM
+ ;
+
+ with: WITH ATOM
+ ;
+
+ for:
+ | FOR addr
+ ;
+
+ recvdatetime:
+ | ';' datetime
+ ;
+
+ addr: mbox | group ;
+
+ mboxes: mbox
+ | mboxes ',' mbox
+ ;
+
+ mbox: spec
+ | routeaddr
+ | phrase routeaddr
+ ;
+
+ group: phrase ':' mboxes ';'
+ ;
+
+ routeaddr: '<' route spec '>'
+ | '<' spec '>'
+ ;
+
+ route: at_domains ':' ;
+
+ at_domains: '@' domain
+ | at_domains ',' '@' domain
+ ;
+
+ spec: local '@' domain
+ | local
+ ;
+
+ local: word
+ | local '.' word
+ ;
+
+ domain: domword
+ | domain '.' domword
+ ;
+
+ domword: atom
+ | DOMLIT
+ | DIGIT
+ ;
+
+ phrase: word
+ | phrase word
+ ;
+
+ word: atom
+ | QUOTED
+ | DIGIT
+ ;
+
+ atom: ATOM | FROM | BY | VIA | WITH | ID | FOR ;
+
+end
diff --git a/test/racc/assets/riml.y b/test/racc/assets/riml.y
new file mode 100644
index 0000000000..1d99b0fdb8
--- /dev/null
+++ b/test/racc/assets/riml.y
@@ -0,0 +1,665 @@
+# Copyright (c) 2012-2014 by Luke Gruber
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+class Riml::Parser
+
+token IF ELSE ELSEIF THEN UNLESS END
+token WHILE UNTIL BREAK CONTINUE
+token TRY CATCH FINALLY
+token FOR IN
+token DEF DEF_BANG SPLAT_PARAM SPLAT_ARG CALL BUILTIN_COMMAND # such as echo "hi"
+token CLASS NEW DEFM DEFM_BANG SUPER
+token RIML_FILE_COMMAND RIML_CLASS_COMMAND
+token RETURN
+token NEWLINE
+token NUMBER
+token STRING_D STRING_S # single- and double-quoted
+token EX_LITERAL
+token REGEXP
+token TRUE FALSE
+token LET UNLET UNLET_BANG IDENTIFIER
+token DICT_VAL # like dict.key, 'key' is a DICT_VAL
+token SCOPE_MODIFIER SCOPE_MODIFIER_LITERAL SPECIAL_VAR_PREFIX
+token FINISH
+
+prechigh
+ right '!'
+ left '*' '/' '%'
+ left '+' '-' '.'
+ left '>' '>#' '>?' '<' '<#' '<?' '>=' '>=#' '>=?' '<=' '<=#' '<=?'
+ left '==' '==?' '==#' '=~' '=~?' '=~#' '!~' '!~?' '!~#' '!=' '!=?' '!=#'
+ left IS ISNOT
+ left '&&'
+ left '||'
+ right '?'
+ right '=' '+=' '-=' '.='
+ left ','
+ left IF UNLESS
+preclow
+
+# All rules
+rule
+
+ Root:
+ /* nothing */ { result = make_node(val) { |_| Riml::Nodes.new([]) } }
+ | Terminator { result = make_node(val) { |_| Riml::Nodes.new([]) } }
+ | Statements { result = val[0] }
+ ;
+
+ # any list of expressions
+ Statements:
+ Statement { result = make_node(val) { |v| Riml::Nodes.new([ v[0] ]) } }
+ | Statements Terminator Statement { result = val[0] << val[2] }
+ | Statements Terminator { result = val[0] }
+ | Terminator Statements { result = make_node(val) { |v| Riml::Nodes.new(v[1]) } }
+ ;
+
+ # All types of expressions in Riml
+ Statement:
+ ExplicitCall { result = val[0] }
+ | Def { result = val[0] }
+ | Return { result = val[0] }
+ | UnletVariable { result = val[0] }
+ | ExLiteral { result = val[0] }
+ | For { result = val[0] }
+ | While { result = val[0] }
+ | Until { result = val[0] }
+ | Try { result = val[0] }
+ | ClassDefinition { result = val[0] }
+ | LoopKeyword { result = val[0] }
+ | EndScript { result = val[0] }
+ | RimlFileCommand { result = val[0] }
+ | RimlClassCommand { result = val[0] }
+ | MultiAssign { result = val[0] }
+ | If { result = val[0] }
+ | Unless { result = val[0] }
+ | Expression { result = val[0] }
+ ;
+
+ Expression:
+ ExpressionWithoutDictLiteral { result = val[0] }
+ | Dictionary { result = val[0] }
+ | Dictionary DictGetWithDotLiteral { result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) } }
+ | BinaryOperator { result = val[0] }
+ | Ternary { result = val[0] }
+ | Assign { result = val[0] }
+ | Super { result = val[0] }
+ | '(' Expression ')' { result = make_node(val) { |v| Riml::WrapInParensNode.new(v[1]) } }
+ ;
+
+ ExpressionWithoutDictLiteral:
+ UnaryOperator { result = val[0] }
+ | DictGet { result = val[0] }
+ | ListOrDictGet { result = val[0] }
+ | AllVariableRetrieval { result = val[0] }
+ | LiteralWithoutDictLiteral { result = val[0] }
+ | Call { result = val[0] }
+ | ObjectInstantiation { result = val[0] }
+ | '(' ExpressionWithoutDictLiteral ')' { result = make_node(val) { |v| Riml::WrapInParensNode.new(v[1]) } }
+ ;
+
+ # for inside curly-brace variable names
+ PossibleStringValue:
+ String { result = val[0] }
+ | DictGet { result = val[0] }
+ | ListOrDictGet { result = val[0] }
+ | AllVariableRetrieval { result = val[0] }
+ | BinaryOperator { result = val[0] }
+ | Ternary { result = val[0] }
+ | Call { result = val[0] }
+ ;
+
+ Terminator:
+ NEWLINE { result = nil }
+ | ';' { result = nil }
+ ;
+
+ LiteralWithoutDictLiteral:
+ Number { result = val[0] }
+ | String { result = val[0] }
+ | Regexp { result = val[0] }
+ | List { result = val[0] }
+ | ScopeModifierLiteral { result = val[0] }
+ | TRUE { result = make_node(val) { |_| Riml::TrueNode.new } }
+ | FALSE { result = make_node(val) { |_| Riml::FalseNode.new } }
+ ;
+
+ Number:
+ NUMBER { result = make_node(val) { |v| Riml::NumberNode.new(v[0]) } }
+ ;
+
+ String:
+ STRING_S { result = make_node(val) { |v| Riml::StringNode.new(v[0], :s) } }
+ | STRING_D { result = make_node(val) { |v| Riml::StringNode.new(v[0], :d) } }
+ | String STRING_S { result = make_node(val) { |v| Riml::StringLiteralConcatNode.new(v[0], Riml::StringNode.new(v[1], :s)) } }
+ | String STRING_D { result = make_node(val) { |v| Riml::StringLiteralConcatNode.new(v[0], Riml::StringNode.new(v[1], :d)) } }
+ ;
+
+ Regexp:
+ REGEXP { result = make_node(val) { |v| Riml::RegexpNode.new(v[0]) } }
+ ;
+
+ ScopeModifierLiteral:
+ SCOPE_MODIFIER_LITERAL { result = make_node(val) { |v| Riml::ScopeModifierLiteralNode.new(v[0]) } }
+ ;
+
+ List:
+ ListLiteral { result = make_node(val) { |v| Riml::ListNode.new(v[0]) } }
+ ;
+
+ ListUnpack:
+ '[' ListItems ';' Expression ']' { result = make_node(val) { |v| Riml::ListUnpackNode.new(v[1] << v[3]) } }
+ ;
+
+ ListLiteral:
+ '[' ListItems ']' { result = val[1] }
+ | '[' ListItems ',' ']' { result = val[1] }
+ ;
+
+ ListItems:
+ /* nothing */ { result = [] }
+ | Expression { result = [val[0]] }
+ | ListItems ',' Expression { result = val[0] << val[2] }
+ ;
+
+ Dictionary:
+ DictionaryLiteral { result = make_node(val) { |v| Riml::DictionaryNode.new(v[0]) } }
+ ;
+
+ # {'key': 'value', 'key2': 'value2'}
+ # Save as [['key', 'value'], ['key2', 'value2']] because ruby-1.8.7 offers
+ # no guarantee for key-value pair ordering.
+ DictionaryLiteral:
+ '{' DictItems '}' { result = val[1] }
+ | '{' DictItems ',' '}' { result = val[1] }
+ ;
+
+ # [[key, value], [key, value]]
+ DictItems:
+ /* nothing */ { result = [] }
+ | DictItem { result = val }
+ | DictItems ',' DictItem { result = val[0] << val[2] }
+ ;
+
+ # [key, value]
+ DictItem:
+ Expression ':' Expression { result = [val[0], val[2]] }
+ ;
+
+ DictGet:
+ AllVariableRetrieval DictGetWithDot { result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) } }
+ | ListOrDictGet DictGetWithDot { result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) } }
+ | Call DictGetWithDot { result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) } }
+ | '(' Expression ')' DictGetWithDot { result = make_node(val) { |v| Riml::DictGetDotNode.new(Riml::WrapInParensNode.new(v[1]), v[3]) } }
+ ;
+
+ ListOrDictGet:
+ ExpressionWithoutDictLiteral ListOrDictGetWithBrackets { result = make_node(val) { |v| Riml::ListOrDictGetNode.new(v[0], v[1]) } }
+ | '(' Expression ')' ListOrDictGetWithBrackets { result = make_node(val) { |v| Riml::ListOrDictGetNode.new(Riml::WrapInParensNode.new(v[1]), v[3]) } }
+ ;
+
+ ListOrDictGetAssign:
+ ExpressionWithoutDictLiteral ListOrDictGetWithBrackets { result = make_node(val) { |v| Riml::ListOrDictGetNode.new(v[0], v[1]) } }
+ ;
+
+ ListOrDictGetWithBrackets:
+ '[' Expression ']' { result = [val[1]] }
+ | '[' SubList ']' { result = [val[1]] }
+ | ListOrDictGetWithBrackets '[' Expression ']' { result = val[0] << val[2] }
+ | ListOrDictGetWithBrackets '[' SubList ']' { result = val[0] << val[2] }
+ ;
+
+ SubList:
+ Expression ':' Expression { result = make_node(val) { |v| Riml::SublistNode.new([v[0], Riml::LiteralNode.new(' : '), v[2]]) } }
+ | Expression ':' { result = make_node(val) { |v| Riml::SublistNode.new([v[0], Riml::LiteralNode.new(' :')]) } }
+ | ':' Expression { result = make_node(val) { |v| Riml::SublistNode.new([Riml::LiteralNode.new(': '), v[1]]) } }
+ | ':' { result = make_node(val) { |_| Riml::SublistNode.new([Riml::LiteralNode.new(':')]) } }
+ ;
+
+ DictGetWithDot:
+ DICT_VAL { result = [val[0]] }
+ | DictGetWithDot DICT_VAL { result = val[0] << val[1] }
+ ;
+
+ DictGetWithDotLiteral:
+ '.' IDENTIFIER { result = [val[1]] }
+ | DictGetWithDotLiteral DICT_VAL { result = val[0] << val[1] }
+ ;
+
+ Call:
+ Scope DefCallIdentifier '(' ArgList ')' { result = make_node(val) { |v| Riml::CallNode.new(v[0], v[1], v[3]) } }
+ | DictGet '(' ArgList ')' { result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], v[2]) } }
+ | BUILTIN_COMMAND '(' ArgList ')' { result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], v[2]) } }
+ | BUILTIN_COMMAND ArgListWithoutNothing { result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], v[1]) } }
+ | BUILTIN_COMMAND NEWLINE { result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], []) } }
+ | CALL '(' ArgList ')' { result = make_node(val) { |v| Riml::ExplicitCallNode.new(nil, nil, v[2]) } }
+ ;
+
+ ObjectInstantiationCall:
+ Scope DefCallIdentifier '(' ArgList ')' { result = make_node(val) { |v| Riml::CallNode.new(v[0], v[1], v[3]) } }
+ | Scope DefCallIdentifier { result = make_node(val) { |v| Riml::CallNode.new(v[0], v[1], []) } }
+ ;
+
+ RimlFileCommand:
+ RIML_FILE_COMMAND '(' ArgList ')' { result = make_node(val) { |v| Riml::RimlFileCommandNode.new(nil, v[0], v[2]) } }
+ | RIML_FILE_COMMAND ArgList { result = make_node(val) { |v| Riml::RimlFileCommandNode.new(nil, v[0], v[1]) } }
+ ;
+
+ RimlClassCommand:
+ RIML_CLASS_COMMAND '(' ClassArgList ')' { result = make_node(val) { |v| Riml::RimlClassCommandNode.new(nil, v[0], v[2]) } }
+ | RIML_CLASS_COMMAND ClassArgList { result = make_node(val) { |v| Riml::RimlClassCommandNode.new(nil, v[0], v[1]) } }
+ ;
+
+ ClassArgList:
+ Scope IDENTIFIER { result = ["#{val[0]}#{val[1]}"] }
+ | String { result = val }
+ | ClassArgList ',' Scope IDENTIFIER { result = val[0].concat ["#{val[2]}#{val[3]}"] }
+ ;
+
+ ExplicitCall:
+ CALL Scope DefCallIdentifier '(' ArgList ')' { result = make_node(val) { |v| Riml::ExplicitCallNode.new(v[1], v[2], v[4]) } }
+ | CALL DictGet '(' ArgList ')' { result = make_node(val) { |v| Riml::ExplicitCallNode.new(nil, v[1], v[3]) } }
+ ;
+
+ Scope:
+ SCOPE_MODIFIER { result = val[0] }
+ | /* nothing */ { result = nil }
+ ;
+
+ # [SID, scope_modifier]
+ SIDAndScope:
+ Scope { result = [ nil, val[0] ] }
+ | '<' IDENTIFIER '>' Scope { result = [ make_node(val) { |v| Riml::SIDNode.new(v[1]) }, val[3] ] }
+ ;
+
+ ArgList:
+ /* nothing */ { result = [] }
+ | ArgListWithoutNothingWithSplat { result = val[0] }
+ ;
+
+ ArgListWithSplat:
+ /* nothing */ { result = [] }
+ | ArgListWithoutNothingWithSplat { result = val[0] }
+ ;
+
+ ArgListWithoutNothingWithSplat:
+ Expression { result = val }
+ | SPLAT_ARG Expression { result = [ make_node(val) { |v| Riml::SplatNode.new(v[1]) } ] }
+ | ArgListWithoutNothingWithSplat "," Expression { result = val[0] << val[2] }
+ | ArgListWithoutNothingWithSplat "," SPLAT_ARG Expression { result = val[0] << make_node(val) { |v| Riml::SplatNode.new(v[3]) } }
+ ;
+
+ ArgListWithoutNothing:
+ Expression { result = val }
+ | ArgListWithoutNothing "," Expression { result = val[0] << val[2] }
+ ;
+
+ BinaryOperator:
+ Expression '||' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '&&' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+
+ | Expression '==' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '==#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '==?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+
+ # added by riml
+ | Expression '===' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+
+ | Expression '!=' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '!=#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '!=?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+
+ | Expression '=~' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '=~#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '=~?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+
+ | Expression '!~' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '!~#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '!~?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+
+ | Expression '>' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '>#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '>?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+
+ | Expression '>=' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '>=#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '>=?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+
+ | Expression '<' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '<#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '<?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+
+ | Expression '<=' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '<=#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '<=?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+
+ | Expression '+' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '-' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '*' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '/' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '.' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression '%' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+
+ | Expression IS Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ | Expression ISNOT Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
+ ;
+
+ UnaryOperator:
+ '!' Expression { result = make_node(val) { |v| Riml::UnaryOperatorNode.new(val[0], [val[1]]) } }
+ | '+' Expression { result = make_node(val) { |v| Riml::UnaryOperatorNode.new(val[0], [val[1]]) } }
+ | '-' Expression { result = make_node(val) { |v| Riml::UnaryOperatorNode.new(val[0], [val[1]]) } }
+ ;
+
+ # ['=', LHS, RHS]
+ Assign:
+ LET AssignExpression { result = make_node(val) { |v| Riml::AssignNode.new(v[1][0], v[1][1], v[1][2]) } }
+ | AssignExpression { result = make_node(val) { |v| Riml::AssignNode.new(v[0][0], v[0][1], v[0][2]) } }
+ ;
+
+ MultiAssign:
+ Assign ',' Assign { result = make_node(val) { |v| Riml::MultiAssignNode.new([v[0], v[2]]) } }
+ | MultiAssign ',' Assign { val[0].assigns << val[2]; result = val[0] }
+ ;
+
+ # ['=', AssignLHS, Expression]
+ AssignExpression:
+ AssignLHS '=' Expression { result = [val[1], val[0], val[2]] }
+ | AssignLHS '+=' Expression { result = [val[1], val[0], val[2]] }
+ | AssignLHS '-=' Expression { result = [val[1], val[0], val[2]] }
+ | AssignLHS '.=' Expression { result = [val[1], val[0], val[2]] }
+ ;
+
+ AssignLHS:
+ AllVariableRetrieval { result = val[0] }
+ | List { result = val[0] }
+ | ListUnpack { result = val[0] }
+ | DictGet { result = val[0] }
+ | ListOrDictGetAssign { result = val[0] }
+ ;
+
+ # retrieving the value of a variable
+ VariableRetrieval:
+ SimpleVariableRetrieval { result = val[0] }
+ | SPECIAL_VAR_PREFIX IDENTIFIER { result = make_node(val) { |v| Riml::GetSpecialVariableNode.new(v[0], v[1]) } }
+ | ScopeModifierLiteral ListOrDictGetWithBrackets { result = make_node(val) { |v| Riml::GetVariableByScopeAndDictNameNode.new(v[0], v[1]) } }
+ ;
+
+ SimpleVariableRetrieval:
+ Scope IDENTIFIER { result = make_node(val) { |v| Riml::GetVariableNode.new(v[0], v[1]) } }
+ ;
+
+ AllVariableRetrieval:
+ VariableRetrieval { result = val[0] }
+ | Scope CurlyBraceName { result = make_node(val) { |v| Riml::GetCurlyBraceNameNode.new(v[0], v[1]) } }
+ ;
+
+ UnletVariable:
+ UNLET VariableRetrieval { result = make_node(val) { |v| Riml::UnletVariableNode.new('!', [ v[1] ]) } }
+ | UNLET_BANG VariableRetrieval { result = make_node(val) { |v| Riml::UnletVariableNode.new('!', [ v[1] ]) } }
+ | UnletVariable VariableRetrieval { result = val[0] << val[1] }
+ ;
+
+ CurlyBraceName:
+ CurlyBraceVarPart { result = make_node(val) { |v| Riml::CurlyBraceVariable.new([ v[0] ]) } }
+ | IDENTIFIER CurlyBraceName { result = make_node(val) { |v| Riml::CurlyBraceVariable.new([ Riml::CurlyBracePart.new(v[0]), v[1] ]) } }
+ | CurlyBraceName IDENTIFIER { result = val[0] << make_node(val) { |v| Riml::CurlyBracePart.new(v[1]) } }
+ | CurlyBraceName CurlyBraceVarPart { result = val[0] << val[1] }
+ ;
+
+ CurlyBraceVarPart:
+ '{' PossibleStringValue '}' { result = make_node(val) { |v| Riml::CurlyBracePart.new(v[1]) } }
+ | '{' PossibleStringValue CurlyBraceVarPart '}' { result = make_node(val) { |v| Riml::CurlyBracePart.new([v[1], v[2]]) } }
+ | '{' CurlyBraceVarPart PossibleStringValue '}' { result = make_node(val) { |v| Riml::CurlyBracePart.new([v[1], v[2]]) } }
+ ;
+
+ # Method definition
+ # [SID, scope_modifier, name, parameters, keyword, expressions]
+ Def:
+ FunctionType SIDAndScope DefCallIdentifier DefKeywords Block END { result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], [], v[3], v[4]) } }
+ | FunctionType SIDAndScope DefCallIdentifier '(' ParamList ')' DefKeywords Block END { result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], v[4], v[6], v[7]) } }
+ | FunctionType SIDAndScope DefCallIdentifier '(' SPLAT_PARAM ')' DefKeywords Block END { result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], [v[4]], v[6], v[7]) } }
+ | FunctionType SIDAndScope DefCallIdentifier '(' ParamList ',' SPLAT_PARAM ')' DefKeywords Block END { result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], v[4] << v[6], v[8], v[9]) } }
+ ;
+
+ FunctionType:
+ DEF { result = "DefNode" }
+ | DEF_BANG { result = "DefNode" }
+ | DEFM { result = "DefMethodNode" }
+ ;
+
+ DefCallIdentifier:
+ # use '' for first argument instead of nil in order to avoid a double scope-modifier
+ CurlyBraceName { result = make_node(val) { |v| Riml::GetCurlyBraceNameNode.new('', v[0]) } }
+ | IDENTIFIER { result = val[0] }
+ ;
+
+ # Example: 'range', 'dict' or 'abort' after function definition
+ DefKeywords:
+ IDENTIFIER { result = [val[0]] }
+ | DefKeywords IDENTIFIER { result = val[0] << val[1] }
+ | /* nothing */ { result = nil }
+ ;
+
+ ParamList:
+ /* nothing */ { result = [] }
+ | IDENTIFIER { result = val }
+ | DefaultParam { result = val }
+ | ParamList ',' IDENTIFIER { result = val[0] << val[2] }
+ | ParamList ',' DefaultParam { result = val[0] << val[2] }
+ ;
+
+ DefaultParam:
+ IDENTIFIER '=' Expression { result = make_node(val) { |v| Riml::DefaultParamNode.new(v[0], v[2]) } }
+ ;
+
+ Return:
+ RETURN Returnable { result = make_node(val) { |v| Riml::ReturnNode.new(v[1]) } }
+ | RETURN Returnable IF Expression { result = make_node(val) { |v| Riml::IfNode.new(v[3], Nodes.new([ReturnNode.new(v[1])])) } }
+ | RETURN Returnable UNLESS Expression { result = make_node(val) { |v| Riml::UnlessNode.new(v[3], Nodes.new([ReturnNode.new(v[1])])) } }
+ ;
+
+ Returnable:
+ /* nothing */ { result = nil }
+ | Expression { result = val[0] }
+ ;
+
+ EndScript:
+ FINISH { result = make_node(val) { |_| Riml::FinishNode.new } }
+ ;
+
+ # [expression, expressions]
+ If:
+ IF Expression IfBlock END { result = make_node(val) { |v| Riml::IfNode.new(v[1], v[2]) } }
+ | IF Expression THEN Expression END { result = make_node(val) { |v| Riml::IfNode.new(v[1], Riml::Nodes.new([v[3]])) } }
+ | Expression IF Expression { result = make_node(val) { |v| Riml::IfNode.new(v[2], Riml::Nodes.new([v[0]])) } }
+ ;
+
+ Unless:
+ UNLESS Expression IfBlock END { result = make_node(val) { |v| Riml::UnlessNode.new(v[1], v[2]) } }
+ | UNLESS Expression THEN Expression END { result = make_node(val) { |v| Riml::UnlessNode.new(v[1], Riml::Nodes.new([v[3]])) } }
+ | Expression UNLESS Expression { result = make_node(val) { |v| Riml::UnlessNode.new(v[2], Riml::Nodes.new([v[0]])) } }
+ ;
+
+ Ternary:
+ Expression '?' Expression ':' Expression { result = make_node(val) { |v| Riml::TernaryOperatorNode.new([v[0], v[2], v[4]]) } }
+ ;
+
+ While:
+ WHILE Expression Block END { result = make_node(val) { |v| Riml::WhileNode.new(v[1], v[2]) } }
+ ;
+
+ LoopKeyword:
+ BREAK { result = make_node(val) { |_| Riml::BreakNode.new } }
+ | CONTINUE { result = make_node(val) { |_| Riml::ContinueNode.new } }
+ ;
+
+ Until:
+ UNTIL Expression Block END { result = make_node(val) { |v| Riml::UntilNode.new(v[1], v[2]) } }
+ ;
+
+ For:
+ FOR SimpleVariableRetrieval IN Expression Block END { result = make_node(val) { |v| Riml::ForNode.new(v[1], v[3], v[4]) } }
+ | FOR List IN Expression Block END { result = make_node(val) { |v| Riml::ForNode.new(v[1], v[3], v[4]) } }
+ | FOR ListUnpack IN Expression Block END { result = make_node(val) { |v| Riml::ForNode.new(v[1], v[3], v[4]) } }
+ ;
+
+ Try:
+ TRY Block END { result = make_node(val) { |v| Riml::TryNode.new(v[1], nil, nil) } }
+ | TRY Block Catch END { result = make_node(val) { |v| Riml::TryNode.new(v[1], v[2], nil) } }
+ | TRY Block Catch FINALLY Block END { result = make_node(val) { |v| Riml::TryNode.new(v[1], v[2], v[4]) } }
+ ;
+
+ Catch:
+ /* nothing */ { result = nil }
+ | CATCH Block { result = [ make_node(val) { |v| Riml::CatchNode.new(nil, v[1]) } ] }
+ | CATCH Catchable Block { result = [ make_node(val) { |v| Riml::CatchNode.new(v[1], v[2]) } ] }
+ | Catch CATCH Block { result = val[0] << make_node(val) { |v| Riml::CatchNode.new(nil, v[2]) } }
+ | Catch CATCH Catchable Block { result = val[0] << make_node(val) { |v| Riml::CatchNode.new(v[2], v[3]) } }
+ ;
+
+ Catchable:
+ Regexp { result = val[0] }
+ | String { result = val[0] }
+ ;
+
+ # [expressions]
+ # expressions list could contain an ElseNode, which contains expressions
+ # itself
+ Block:
+ NEWLINE Statements { result = val[1] }
+ | NEWLINE { result = make_node(val) { |_| Riml::Nodes.new([]) } }
+ ;
+
+ IfBlock:
+ Block { result = val[0] }
+ | NEWLINE Statements ElseBlock { result = val[1] << val[2] }
+ | NEWLINE Statements ElseifBlock { result = val[1] << val[2] }
+ | NEWLINE Statements ElseifBlock ElseBlock { result = val[1] << val[2] << val[3] }
+ ;
+
+ ElseBlock:
+ ELSE NEWLINE Statements { result = make_node(val) { |v| Riml::ElseNode.new(v[2]) } }
+ ;
+
+ ElseifBlock:
+ ELSEIF Expression NEWLINE Statements { result = make_node(val) { |v| Riml::Nodes.new([Riml::ElseifNode.new(v[1], v[3])]) } }
+ | ElseifBlock ELSEIF Expression NEWLINE Statements { result = val[0] << make_node(val) { |v| Riml::ElseifNode.new(v[2], v[4]) } }
+ ;
+
+ ClassDefinition:
+ CLASS Scope IDENTIFIER Block END { result = make_node(val) { |v| Riml::ClassDefinitionNode.new(v[1], v[2], nil, v[3]) } }
+ | CLASS Scope IDENTIFIER '<' Scope IDENTIFIER Block END { result = make_node(val) { |v| Riml::ClassDefinitionNode.new(v[1], v[2], (v[4] || ClassDefinitionNode::DEFAULT_SCOPE_MODIFIER) + v[5], v[6]) } }
+ ;
+
+ ObjectInstantiation:
+ NEW ObjectInstantiationCall { result = make_node(val) { |v| Riml::ObjectInstantiationNode.new(v[1]) } }
+ ;
+
+ Super:
+ SUPER '(' ArgListWithSplat ')' { result = make_node(val) { |v| Riml::SuperNode.new(v[2], true) } }
+ | SUPER { result = make_node(val) { |_| Riml::SuperNode.new([], false) } }
+ ;
+
+ ExLiteral:
+ EX_LITERAL { result = make_node(val) { |v| Riml::ExLiteralNode.new(v[0]) } }
+ ;
+end
+
+---- header
+ require File.expand_path("../lexer", __FILE__)
+ require File.expand_path("../nodes", __FILE__)
+ require File.expand_path("../errors", __FILE__)
+ require File.expand_path("../ast_rewriter", __FILE__)
+---- inner
+ # This code will be put as-is in the parser class
+
+ attr_accessor :ast_rewriter
+ attr_writer :options
+
+ # The Parser and AST_Rewriter share this same hash of options
+ def options
+ @options ||= {}
+ end
+
+ def self.ast_cache
+ @ast_cache
+ end
+ @ast_cache = {}
+
+ # parses tokens or code into output nodes
+ def parse(object, ast_rewriter = Riml::AST_Rewriter.new, filename = nil, included = false)
+ if (ast = self.class.ast_cache[filename])
+ else
+ if tokens?(object)
+ @tokens = object
+ elsif code?(object)
+ @lexer = Riml::Lexer.new(object, filename, true)
+ end
+
+ begin
+ ast = do_parse
+ rescue Racc::ParseError => e
+ raise unless @lexer
+ if (invalid_token = @lexer.prev_token_is_keyword?)
+ warning = "#{invalid_token.inspect} is a keyword, and cannot " \
+ "be used as a variable name"
+ end
+ error_msg = e.message
+ error_msg << "\nWARNING: #{warning}" if warning
+ error = Riml::ParseError.new(error_msg, @lexer.filename, @lexer.lineno)
+ raise error
+ end
+ self.class.ast_cache[filename] = ast if filename
+ end
+ @ast_rewriter ||= ast_rewriter
+ return ast unless @ast_rewriter
+ @ast_rewriter.ast = ast.dup
+ @ast_rewriter.options ||= options
+ @ast_rewriter.rewrite(filename, included)
+ @ast_rewriter.ast
+ end
+
+ # get the next token from either the list of tokens provided, or
+ # the lexer getting the next token
+ def next_token
+ return @tokens.shift unless @lexer
+ token = @lexer.next_token
+ if token && @lexer.parser_info
+ @current_parser_info = token.pop
+ end
+ token
+ end
+
+ private
+
+ def tokens?(object)
+ Array === object
+ end
+
+ def code?(object)
+ String === object
+ end
+
+ def make_node(racc_val)
+ node = yield racc_val
+ node.parser_info = @current_parser_info
+ node
+ end
diff --git a/test/racc/assets/rrconf.y b/test/racc/assets/rrconf.y
new file mode 100644
index 0000000000..baf9249a77
--- /dev/null
+++ b/test/racc/assets/rrconf.y
@@ -0,0 +1,14 @@
+# 1 s/r conflict and 1 r/r conflict
+
+class A
+rule
+
+target: a
+
+a :
+ | a list
+
+list :
+ | list ITEM
+
+end
diff --git a/test/racc/assets/ruby18.y b/test/racc/assets/ruby18.y
new file mode 100644
index 0000000000..eceb253298
--- /dev/null
+++ b/test/racc/assets/ruby18.y
@@ -0,0 +1,1943 @@
+# Copyright (c) 2013 Peter Zotov <whitequark@whitequark.org>
+#
+# Parts of the source are derived from ruby_parser:
+# Copyright (c) Ryan Davis, seattle.rb
+#
+# MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+class Parser::Ruby18
+
+token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
+ kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT
+ kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kRETURN kYIELD kSUPER
+ kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD
+ kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__
+ k__FILE__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tNTH_REF
+ tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT tREGEXP_END tUPLUS
+ tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ tGEQ tLEQ tANDOP
+ tOROP tMATCH tNMATCH tDOT tDOT2 tDOT3 tAREF tASET tLSHFT tRSHFT
+ tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN tLPAREN2 tRPAREN tLPAREN_ARG
+ tLBRACK tLBRACK2 tRBRACK tLBRACE tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2
+ tTILDE tPERCENT tDIVIDE tPLUS tMINUS tLT tGT tPIPE tBANG tCARET
+ tLCURLY tRCURLY tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG
+ tWORDS_BEG tQWORDS_BEG tSTRING_DBEG tSTRING_DVAR tSTRING_END tSTRING
+ tSYMBOL tREGEXP_OPT tNL tEH tCOLON tCOMMA tSPACE tSEMI
+
+prechigh
+ right tBANG tTILDE tUPLUS
+ right tPOW
+ right tUMINUS_NUM tUMINUS
+ left tSTAR2 tDIVIDE tPERCENT
+ left tPLUS tMINUS
+ left tLSHFT tRSHFT
+ left tAMPER2
+ left tPIPE tCARET
+ left tGT tGEQ tLT tLEQ
+ nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
+ left tANDOP
+ left tOROP
+ nonassoc tDOT2 tDOT3
+ right tEH tCOLON
+ left kRESCUE_MOD
+ right tEQL tOP_ASGN
+ nonassoc kDEFINED
+ right kNOT
+ left kOR kAND
+ nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
+ nonassoc tLBRACE_ARG
+ nonassoc tLOWEST
+preclow
+
+rule
+
+ program: compstmt
+ {
+ result = val[0]
+ }
+
+ bodystmt: compstmt opt_rescue opt_else opt_ensure
+ {
+ rescue_bodies = val[1]
+ else_t, else_ = val[2]
+ ensure_t, ensure_ = val[3]
+
+ if rescue_bodies.empty? && !else_.nil?
+ diagnostic :warning, :useless_else, nil, else_t
+ end
+
+ result = @builder.begin_body(val[0],
+ rescue_bodies,
+ else_t, else_,
+ ensure_t, ensure_)
+ }
+
+ compstmt: stmts opt_terms
+ {
+ result = @builder.compstmt(val[0])
+ }
+
+ stmts: # nothing
+ {
+ result = []
+ }
+ | stmt
+ {
+ result = [ val[0] ]
+ }
+ | error stmt
+ {
+ result = [ val[1] ]
+ }
+ | stmts terms stmt
+ {
+ result = val[0] << val[2]
+ }
+
+ stmt: kALIAS fitem
+ {
+ @lexer.state = :expr_fname
+ }
+ fitem
+ {
+ result = @builder.alias(val[0], val[1], val[3])
+ }
+ | kALIAS tGVAR tGVAR
+ {
+ result = @builder.alias(val[0],
+ @builder.gvar(val[1]),
+ @builder.gvar(val[2]))
+ }
+ | kALIAS tGVAR tBACK_REF
+ {
+ result = @builder.alias(val[0],
+ @builder.gvar(val[1]),
+ @builder.back_ref(val[2]))
+ }
+ | kALIAS tGVAR tNTH_REF
+ {
+ diagnostic :error, :nth_ref_alias, nil, val[2]
+ }
+ | kUNDEF undef_list
+ {
+ result = @builder.undef_method(val[0], val[1])
+ }
+ | stmt kIF_MOD expr_value
+ {
+ result = @builder.condition_mod(val[0], nil,
+ val[1], val[2])
+ }
+ | stmt kUNLESS_MOD expr_value
+ {
+ result = @builder.condition_mod(nil, val[0],
+ val[1], val[2])
+ }
+ | stmt kWHILE_MOD expr_value
+ {
+ result = @builder.loop_mod(:while, val[0], val[1], val[2])
+ }
+ | stmt kUNTIL_MOD expr_value
+ {
+ result = @builder.loop_mod(:until, val[0], val[1], val[2])
+ }
+ | stmt kRESCUE_MOD stmt
+ {
+ rescue_body = @builder.rescue_body(val[1],
+ nil, nil, nil,
+ nil, val[2])
+
+ result = @builder.begin_body(val[0], [ rescue_body ])
+ }
+ | klBEGIN tLCURLY compstmt tRCURLY
+ {
+ if in_def?
+ diagnostic :error, :begin_in_method, nil, val[0]
+ end
+
+ result = @builder.preexe(val[0], val[1], val[2], val[3])
+ }
+ | klEND tLCURLY compstmt tRCURLY
+ {
+ result = @builder.postexe(val[0], val[1], val[2], val[3])
+ }
+ | lhs tEQL command_call
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+ | mlhs tEQL command_call
+ {
+ result = @builder.multi_assign(val[0], val[1], val[2])
+ }
+ | var_lhs tOP_ASGN command_call
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | primary_value tLBRACK2 aref_args tRBRACK tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.index(
+ val[0], val[1], val[2], val[3]),
+ val[4], val[5])
+ }
+ | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tDOT tCONSTANT tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | backref tOP_ASGN command_call
+ {
+ @builder.op_assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL mrhs
+ {
+ result = @builder.assign(val[0], val[1],
+ @builder.array(nil, val[2], nil))
+ }
+ | mlhs tEQL arg_value
+ {
+ result = @builder.multi_assign(val[0], val[1], val[2])
+ }
+ | mlhs tEQL mrhs
+ {
+ result = @builder.multi_assign(val[0], val[1],
+ @builder.array(nil, val[2], nil))
+ }
+ | expr
+
+ expr: command_call
+ | expr kAND expr
+ {
+ result = @builder.logical_op(:and, val[0], val[1], val[2])
+ }
+ | expr kOR expr
+ {
+ result = @builder.logical_op(:or, val[0], val[1], val[2])
+ }
+ | kNOT expr
+ {
+ result = @builder.not_op(val[0], nil, val[1], nil)
+ }
+ | tBANG command_call
+ {
+ result = @builder.not_op(val[0], nil, val[1], nil)
+ }
+ | arg
+
+ expr_value: expr
+
+ command_call: command
+ | block_command
+ | kRETURN call_args
+ {
+ result = @builder.keyword_cmd(:return, val[0],
+ nil, val[1], nil)
+ }
+ | kBREAK call_args
+ {
+ result = @builder.keyword_cmd(:break, val[0],
+ nil, val[1], nil)
+ }
+ | kNEXT call_args
+ {
+ result = @builder.keyword_cmd(:next, val[0],
+ nil, val[1], nil)
+ }
+
+ block_command: block_call
+ | block_call tDOT operation2 command_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | block_call tCOLON2 operation2 command_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+
+ cmd_brace_block: tLBRACE_ARG
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_var compstmt tRCURLY
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ command: operation command_args =tLOWEST
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.call_method(nil, nil, val[0],
+ lparen_t, args, rparen_t)
+ }
+ | operation command_args cmd_brace_block
+ {
+ lparen_t, args, rparen_t = val[1]
+ method_call = @builder.call_method(nil, nil, val[0],
+ lparen_t, args, rparen_t)
+
+ begin_t, block_args, body, end_t = val[2]
+ result = @builder.block(method_call,
+ begin_t, block_args, body, end_t)
+ }
+ | primary_value tDOT operation2 command_args =tLOWEST
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+
+ }
+ | primary_value tDOT operation2 command_args cmd_brace_block
+ {
+ lparen_t, args, rparen_t = val[3]
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+
+ begin_t, block_args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, block_args, body, end_t)
+ }
+ | primary_value tCOLON2 operation2 command_args =tLOWEST
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 operation2 command_args cmd_brace_block
+ {
+ lparen_t, args, rparen_t = val[3]
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+
+ begin_t, block_args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, block_args, body, end_t)
+ }
+ | kSUPER command_args
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.keyword_cmd(:super, val[0],
+ lparen_t, args, rparen_t)
+ }
+ | kYIELD command_args
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.keyword_cmd(:yield, val[0],
+ lparen_t, args, rparen_t)
+ }
+
+ mlhs: mlhs_basic
+ {
+ result = @builder.multi_lhs(nil, val[0], nil)
+ }
+ | tLPAREN mlhs_entry tRPAREN
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+
+ mlhs_entry: mlhs_basic
+ {
+ result = @builder.multi_lhs(nil, val[0], nil)
+ }
+ | tLPAREN mlhs_entry tRPAREN
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ mlhs_basic: mlhs_head
+ {
+ result = val[0]
+ }
+ | mlhs_head mlhs_item
+ {
+ result = val[0] << val[1]
+ }
+ | mlhs_head tSTAR mlhs_node
+ {
+ result = val[0] << @builder.splat(val[1], val[2])
+ }
+ | mlhs_head tSTAR
+ {
+ result = val[0] << @builder.splat(val[1])
+ }
+ | tSTAR mlhs_node
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+ | tSTAR
+ {
+ result = [ @builder.splat(val[0]) ]
+ }
+
+ mlhs_item: mlhs_node
+ | tLPAREN mlhs_entry tRPAREN
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+
+ mlhs_head: mlhs_item tCOMMA
+ {
+ result = [ val[0] ]
+ }
+ | mlhs_head mlhs_item tCOMMA
+ {
+ result = val[0] << val[1]
+ }
+
+ mlhs_node: variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | primary_value tLBRACK2 aref_args tRBRACK
+ {
+ result = @builder.index_asgn(val[0], val[1], val[2], val[3])
+ }
+ | primary_value tDOT tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tDOT tCONSTANT
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_global(val[0], val[1]))
+ }
+ | backref
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ lhs: variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | primary_value tLBRACK2 aref_args tRBRACK
+ {
+ result = @builder.index_asgn(val[0], val[1], val[2], val[3])
+ }
+ | primary_value tDOT tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tDOT tCONSTANT
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_global(val[0], val[1]))
+ }
+ | backref
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ cname: tIDENTIFIER
+ {
+ diagnostic :error, :module_name_const, nil, val[0]
+ }
+ | tCONSTANT
+
+ cpath: tCOLON3 cname
+ {
+ result = @builder.const_global(val[0], val[1])
+ }
+ | cname
+ {
+ result = @builder.const(val[0])
+ }
+ | primary_value tCOLON2 cname
+ {
+ result = @builder.const_fetch(val[0], val[1], val[2])
+ }
+
+ fname: tIDENTIFIER | tCONSTANT | tFID
+ | op
+ | reswords
+
+ fsym: fname
+ {
+ result = @builder.symbol(val[0])
+ }
+ | symbol
+
+ fitem: fsym
+ | dsym
+
+ undef_list: fitem
+ {
+ result = [ val[0] ]
+ }
+ | undef_list tCOMMA
+ {
+ @lexer.state = :expr_fname
+ }
+ fitem
+ {
+ result = val[0] << val[3]
+ }
+
+ op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ
+ | tMATCH | tGT | tGEQ | tLT | tLEQ | tLSHFT
+ | tRSHFT | tPLUS | tMINUS | tSTAR2 | tSTAR | tDIVIDE
+ | tPERCENT | tPOW | tTILDE | tUPLUS | tUMINUS | tAREF
+ | tASET | tBACK_REF2
+
+ reswords: k__LINE__ | k__FILE__ | klBEGIN | klEND | kALIAS | kAND
+ | kBEGIN | kBREAK | kCASE | kCLASS | kDEF | kDEFINED
+ | kDO | kELSE | kELSIF | kEND | kENSURE | kFALSE
+ | kFOR | kIN | kMODULE | kNEXT | kNIL | kNOT
+ | kOR | kREDO | kRESCUE | kRETRY | kRETURN | kSELF
+ | kSUPER | kTHEN | kTRUE | kUNDEF | kWHEN | kYIELD
+ | kIF | kUNLESS | kWHILE | kUNTIL
+
+ arg: lhs tEQL arg
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL arg kRESCUE_MOD arg
+ {
+ rescue_body = @builder.rescue_body(val[3],
+ nil, nil, nil,
+ nil, val[4])
+
+ rescue_ = @builder.begin_body(val[2], [ rescue_body ])
+
+ result = @builder.assign(val[0], val[1], rescue_)
+ }
+ | var_lhs tOP_ASGN arg
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | primary_value tLBRACK2 aref_args tRBRACK tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.index(
+ val[0], val[1], val[2], val[3]),
+ val[4], val[5])
+ }
+ | primary_value tDOT tIDENTIFIER tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tDOT tCONSTANT tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
+ {
+ diagnostic :error, :dynamic_const, nil, val[2], [ val[3] ]
+ }
+ | tCOLON3 tCONSTANT tOP_ASGN arg
+ {
+ diagnostic :error, :dynamic_const, nil, val[1], [ val[2] ]
+ }
+ | backref tOP_ASGN arg
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | arg tDOT2 arg
+ {
+ result = @builder.range_inclusive(val[0], val[1], val[2])
+ }
+ | arg tDOT3 arg
+ {
+ result = @builder.range_exclusive(val[0], val[1], val[2])
+ }
+ | arg tPLUS arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tMINUS arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tSTAR2 arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tDIVIDE arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tPERCENT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tPOW arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | tUMINUS_NUM tINTEGER tPOW arg
+ {
+ result = @builder.unary_op(val[0],
+ @builder.binary_op(
+ @builder.integer(val[1]),
+ val[2], val[3]))
+ }
+ | tUMINUS_NUM tFLOAT tPOW arg
+ {
+ result = @builder.unary_op(val[0],
+ @builder.binary_op(
+ @builder.float(val[1]),
+ val[2], val[3]))
+ }
+ | tUPLUS arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | tUMINUS arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | arg tPIPE arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tCARET arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tAMPER2 arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tCMP arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tGT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tGEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tLT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tLEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tEQQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tNEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tMATCH arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tNMATCH arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | tBANG arg
+ {
+ result = @builder.not_op(val[0], nil, val[1], nil)
+ }
+ | tTILDE arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | arg tLSHFT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tRSHFT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tANDOP arg
+ {
+ result = @builder.logical_op(:and, val[0], val[1], val[2])
+ }
+ | arg tOROP arg
+ {
+ result = @builder.logical_op(:or, val[0], val[1], val[2])
+ }
+ | kDEFINED opt_nl arg
+ {
+ result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil)
+ }
+ | arg tEH arg tCOLON arg
+ {
+ result = @builder.ternary(val[0], val[1],
+ val[2], val[3], val[4])
+ }
+ | primary
+
+ arg_value: arg
+
+ aref_args: none
+ | command opt_nl
+ {
+ result = [ val[0] ]
+ }
+ | args trailer
+ {
+ result = val[0]
+ }
+ | args tCOMMA tSTAR arg opt_nl
+ {
+ result = val[0] << @builder.splat(val[2], val[3])
+ }
+ | assocs trailer
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ }
+ | tSTAR arg opt_nl
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+
+ paren_args: tLPAREN2 none tRPAREN
+ {
+ result = [ val[0], [], val[2] ]
+ }
+ | tLPAREN2 call_args opt_nl tRPAREN
+ {
+ result = [ val[0], val[1], val[3] ]
+ }
+ | tLPAREN2 block_call opt_nl tRPAREN
+ {
+ result = [ val[0], [ val[1] ], val[3] ]
+ }
+ | tLPAREN2 args tCOMMA block_call opt_nl tRPAREN
+ {
+ result = [ val[0], val[1] << val[3], val[5] ]
+ }
+
+ opt_paren_args: # nothing
+ {
+ result = [ nil, [], nil ]
+ }
+ | paren_args
+
+ call_args: command
+ {
+ result = [ val[0] ]
+ }
+ | args opt_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | args tCOMMA tSTAR arg_value opt_block_arg
+ {
+ result = val[0].concat(
+ [ @builder.splat(val[2], val[3]),
+ *val[4] ])
+ }
+ | assocs opt_block_arg
+ {
+ result = [ @builder.associate(nil, val[0], nil),
+ *val[1] ]
+ }
+ | assocs tCOMMA tSTAR arg_value opt_block_arg
+ {
+ result = [ @builder.associate(nil, val[0], nil),
+ @builder.splat(val[2], val[3]),
+ *val[4] ]
+ }
+ | args tCOMMA assocs opt_block_arg
+ {
+ result = val[0].concat(
+ [ @builder.associate(nil, val[2], nil),
+ *val[3] ])
+ }
+ | args tCOMMA assocs tCOMMA tSTAR arg opt_block_arg
+ {
+ result = val[0].concat(
+ [ @builder.associate(nil, val[2], nil),
+ @builder.splat(val[4], val[5]),
+ *val[6] ])
+ }
+ | tSTAR arg_value opt_block_arg
+ {
+ result = [ @builder.splat(val[0], val[1]),
+ *val[2] ]
+ }
+ | block_arg
+ {
+ result = [ val[0] ]
+ }
+
+ call_args2: arg_value tCOMMA args opt_block_arg
+ {
+ result = [ val[0], *val[2].concat(val[3]) ]
+ }
+ | arg_value tCOMMA block_arg
+ {
+ result = [ val[0], val[2] ]
+ }
+ | arg_value tCOMMA tSTAR arg_value opt_block_arg
+ {
+ result = [ val[0],
+ @builder.splat(val[2], val[3]),
+ *val[4] ]
+ }
+ | arg_value tCOMMA args tCOMMA tSTAR arg_value opt_block_arg
+ {
+ result = [ val[0],
+ *val[2].
+ push(@builder.splat(val[4], val[5])).
+ concat(val[6]) ]
+ }
+ | assocs opt_block_arg
+ {
+ result = [ @builder.associate(nil, val[0], nil),
+ *val[1] ]
+ }
+ | assocs tCOMMA tSTAR arg_value opt_block_arg
+ {
+ result = [ @builder.associate(nil, val[0], nil),
+ @builder.splat(val[2], val[3]),
+ *val[4] ]
+ }
+ | arg_value tCOMMA assocs opt_block_arg
+ {
+ result = [ val[0],
+ @builder.associate(nil, val[2], nil),
+ *val[3] ]
+ }
+ | arg_value tCOMMA args tCOMMA assocs opt_block_arg
+ {
+ result = [ val[0],
+ *val[2].
+ push(@builder.associate(nil, val[4], nil)).
+ concat(val[5]) ]
+ }
+ | arg_value tCOMMA assocs tCOMMA tSTAR arg_value opt_block_arg
+ {
+ result = [ val[0],
+ @builder.associate(nil, val[2], nil),
+ @builder.splat(val[4], val[5]),
+ *val[6] ]
+ }
+ | arg_value tCOMMA args tCOMMA assocs tCOMMA tSTAR arg_value opt_block_arg
+ {
+ result = [ val[0],
+ *val[2].
+ push(@builder.associate(nil, val[4], nil)).
+ push(@builder.splat(val[6], val[7])).
+ concat(val[8]) ]
+ }
+ | tSTAR arg_value opt_block_arg
+ {
+ result = [ @builder.splat(val[0], val[1]),
+ *val[2] ]
+ }
+ | block_arg
+ {
+ result = [ val[0] ]
+ }
+
+ command_args: {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.push(true)
+ }
+ open_args
+ {
+ @lexer.cmdarg = val[0]
+
+ result = val[1]
+ }
+
+ open_args: call_args
+ {
+ result = [ nil, val[0], nil ]
+ }
+ | tLPAREN_ARG
+ {
+ @lexer.state = :expr_endarg
+ }
+ tRPAREN
+ {
+ result = [ val[0], [], val[2] ]
+ }
+ | tLPAREN_ARG call_args2
+ {
+ @lexer.state = :expr_endarg
+ }
+ tRPAREN
+ {
+ result = [ val[0], val[1], val[3] ]
+ }
+
+ block_arg: tAMPER arg_value
+ {
+ result = @builder.block_pass(val[0], val[1])
+ }
+
+ opt_block_arg: tCOMMA block_arg
+ {
+ result = [ val[1] ]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ args: arg_value
+ {
+ result = [ val[0] ]
+ }
+ | args tCOMMA arg_value
+ {
+ result = val[0] << val[2]
+ }
+
+ mrhs: args tCOMMA arg_value
+ {
+ result = val[0] << val[2]
+ }
+ | args tCOMMA tSTAR arg_value
+ {
+ result = val[0] << @builder.splat(val[2], val[3])
+ }
+ | tSTAR arg_value
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+
+ primary: literal
+ | strings
+ | xstring
+ | regexp
+ | words
+ | qwords
+ | var_ref
+ | backref
+ | tFID
+ {
+ result = @builder.call_method(nil, nil, val[0])
+ }
+ | kBEGIN bodystmt kEND
+ {
+ result = @builder.begin_keyword(val[0], val[1], val[2])
+ }
+ | tLPAREN_ARG expr
+ {
+ @lexer.state = :expr_endarg
+ }
+ opt_nl tRPAREN
+ {
+ result = @builder.begin(val[0], val[1], val[4])
+ }
+ | tLPAREN compstmt tRPAREN
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.const_fetch(val[0], val[1], val[2])
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.const_global(val[0], val[1])
+ }
+ | primary_value tLBRACK2 aref_args tRBRACK
+ {
+ result = @builder.index(val[0], val[1], val[2], val[3])
+ }
+ | tLBRACK aref_args tRBRACK
+ {
+ result = @builder.array(val[0], val[1], val[2])
+ }
+ | tLBRACE assoc_list tRCURLY
+ {
+ result = @builder.associate(val[0], val[1], val[2])
+ }
+ | kRETURN
+ {
+ result = @builder.keyword_cmd(:return, val[0])
+ }
+ | kYIELD tLPAREN2 call_args tRPAREN
+ {
+ result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3])
+ }
+ | kYIELD tLPAREN2 tRPAREN
+ {
+ result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2])
+ }
+ | kYIELD
+ {
+ result = @builder.keyword_cmd(:yield, val[0])
+ }
+ | kDEFINED opt_nl tLPAREN2 expr tRPAREN
+ {
+ result = @builder.keyword_cmd(:defined?, val[0],
+ val[2], [ val[3] ], val[4])
+ }
+ | operation brace_block
+ {
+ method_call = @builder.call_method(nil, nil, val[0])
+
+ begin_t, args, body, end_t = val[1]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | method_call
+ | method_call brace_block
+ {
+ begin_t, args, body, end_t = val[1]
+ result = @builder.block(val[0],
+ begin_t, args, body, end_t)
+ }
+ | kIF expr_value then compstmt if_tail kEND
+ {
+ else_t, else_ = val[4]
+ result = @builder.condition(val[0], val[1], val[2],
+ val[3], else_t,
+ else_, val[5])
+ }
+ | kUNLESS expr_value then compstmt opt_else kEND
+ {
+ else_t, else_ = val[4]
+ result = @builder.condition(val[0], val[1], val[2],
+ else_, else_t,
+ val[3], val[5])
+ }
+ | kWHILE
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.loop(:while, val[0], val[2], val[3],
+ val[5], val[6])
+ }
+ | kUNTIL
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.loop(:until, val[0], val[2], val[3],
+ val[5], val[6])
+ }
+ | kCASE expr_value opt_terms case_body kEND
+ {
+ when_bodies = val[3][0..-2]
+ else_t, else_body = val[3][-1]
+
+ result = @builder.case(val[0], val[1],
+ when_bodies, else_t, else_body,
+ val[4])
+ }
+ | kCASE opt_terms case_body kEND
+ {
+ when_bodies = val[2][0..-2]
+ else_t, else_body = val[2][-1]
+
+ result = @builder.case(val[0], nil,
+ when_bodies, else_t, else_body,
+ val[3])
+ }
+ | kCASE opt_terms kELSE compstmt kEND
+ {
+ result = @builder.case(val[0], nil,
+ [], val[2], val[3],
+ val[4])
+ }
+ | kFOR for_var kIN
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.for(val[0], val[1],
+ val[2], val[4],
+ val[5], val[7], val[8])
+ }
+ | kCLASS cpath superclass
+ {
+ @static_env.extend_static
+ }
+ bodystmt kEND
+ {
+ if in_def?
+ diagnostic :error, :class_in_def, nil, val[0]
+ end
+
+ lt_t, superclass = val[2]
+ result = @builder.def_class(val[0], val[1],
+ lt_t, superclass,
+ val[4], val[5])
+
+ @static_env.unextend
+ }
+ | kCLASS tLSHFT expr term
+ {
+ result = @def_level
+ @def_level = 0
+
+ @static_env.extend_static
+ }
+ bodystmt kEND
+ {
+ result = @builder.def_sclass(val[0], val[1], val[2],
+ val[5], val[6])
+
+ @static_env.unextend
+
+ @def_level = val[4]
+ }
+ | kMODULE cpath
+ {
+ @static_env.extend_static
+ }
+ bodystmt kEND
+ {
+ if in_def?
+ diagnostic :error, :module_in_def, nil, val[0]
+ end
+
+ result = @builder.def_module(val[0], val[1],
+ val[3], val[4])
+
+ @static_env.unextend
+ }
+ | kDEF fname
+ {
+ @def_level += 1
+ @static_env.extend_static
+ }
+ f_arglist bodystmt kEND
+ {
+ result = @builder.def_method(val[0], val[1],
+ val[3], val[4], val[5])
+
+ @static_env.unextend
+ @def_level -= 1
+ }
+ | kDEF singleton dot_or_colon
+ {
+ @lexer.state = :expr_fname
+ }
+ fname
+ {
+ @def_level += 1
+ @static_env.extend_static
+ }
+ f_arglist bodystmt kEND
+ {
+ result = @builder.def_singleton(val[0], val[1], val[2],
+ val[4], val[6], val[7], val[8])
+
+ @static_env.unextend
+ @def_level -= 1
+ }
+ | kBREAK
+ {
+ result = @builder.keyword_cmd(:break, val[0])
+ }
+ | kNEXT
+ {
+ result = @builder.keyword_cmd(:next, val[0])
+ }
+ | kREDO
+ {
+ result = @builder.keyword_cmd(:redo, val[0])
+ }
+ | kRETRY
+ {
+ result = @builder.keyword_cmd(:retry, val[0])
+ }
+
+ primary_value: primary
+
+ then: term
+ | tCOLON
+ | kTHEN
+ | term kTHEN
+ {
+ result = val[1]
+ }
+
+ do: term
+ | tCOLON
+ | kDO_COND
+
+ if_tail: opt_else
+ | kELSIF expr_value then compstmt if_tail
+ {
+ else_t, else_ = val[4]
+ result = [ val[0],
+ @builder.condition(val[0], val[1], val[2],
+ val[3], else_t,
+ else_, nil),
+ ]
+ }
+
+ opt_else: none
+ | kELSE compstmt
+ {
+ result = val
+ }
+
+ for_var: lhs
+ | mlhs
+
+ block_par: mlhs_item
+ {
+ result = [ @builder.arg_expr(val[0]) ]
+ }
+ | block_par tCOMMA mlhs_item
+ {
+ result = val[0] << @builder.arg_expr(val[2])
+ }
+
+ block_var: block_par
+ | block_par tCOMMA
+ | block_par tCOMMA tAMPER lhs
+ {
+ result = val[0].
+ push(@builder.blockarg_expr(val[2], val[3]))
+ }
+ | block_par tCOMMA tSTAR lhs tCOMMA tAMPER lhs
+ {
+ result = val[0].
+ push(@builder.restarg_expr(val[2], val[3])).
+ push(@builder.blockarg_expr(val[5], val[6]))
+ }
+ | block_par tCOMMA tSTAR tCOMMA tAMPER lhs
+ {
+ result = val[0].
+ push(@builder.restarg_expr(val[2])).
+ push(@builder.blockarg_expr(val[4], val[5]))
+ }
+ | block_par tCOMMA tSTAR lhs
+ {
+ result = val[0].
+ push(@builder.restarg_expr(val[2], val[3]))
+ }
+ | block_par tCOMMA tSTAR
+ {
+ result = val[0].
+ push(@builder.restarg_expr(val[2]))
+ }
+ | tSTAR lhs tCOMMA tAMPER lhs
+ {
+ result = [ @builder.restarg_expr(val[0], val[1]),
+ @builder.blockarg_expr(val[3], val[4]) ]
+ }
+ | tSTAR tCOMMA tAMPER lhs
+ {
+ result = [ @builder.restarg_expr(val[0]),
+ @builder.blockarg_expr(val[2], val[3]) ]
+ }
+ | tSTAR lhs
+ {
+ result = [ @builder.restarg_expr(val[0], val[1]) ]
+ }
+ | tSTAR
+ {
+ result = [ @builder.restarg_expr(val[0]) ]
+ }
+ | tAMPER lhs
+ {
+ result = [ @builder.blockarg_expr(val[0], val[1]) ]
+ }
+ ;
+
+ opt_block_var: # nothing
+ {
+ result = @builder.args(nil, [], nil)
+ }
+ | tPIPE tPIPE
+ {
+ result = @builder.args(val[0], [], val[1])
+ }
+ | tOROP
+ {
+ result = @builder.args(val[0], [], val[0])
+ }
+ | tPIPE block_var tPIPE
+ {
+ result = @builder.args(val[0], val[1], val[2], false)
+ }
+
+ do_block: kDO_BLOCK
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_var compstmt kEND
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ block_call: command do_block
+ {
+ begin_t, block_args, body, end_t = val[1]
+ result = @builder.block(val[0],
+ begin_t, block_args, body, end_t)
+ }
+ | block_call tDOT operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | block_call tCOLON2 operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+
+ method_call: operation paren_args
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.call_method(nil, nil, val[0],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tDOT operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 operation2 paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 operation3
+ {
+ result = @builder.call_method(val[0], val[1], val[2])
+ }
+ | kSUPER paren_args
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.keyword_cmd(:super, val[0],
+ lparen_t, args, rparen_t)
+ }
+ | kSUPER
+ {
+ result = @builder.keyword_cmd(:zsuper, val[0])
+ }
+
+ brace_block: tLCURLY
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_var compstmt tRCURLY
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+ | kDO
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_var compstmt kEND
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ case_body: kWHEN when_args then compstmt cases
+ {
+ result = [ @builder.when(val[0], val[1], val[2], val[3]),
+ *val[4] ]
+ }
+
+ when_args: args
+ | args tCOMMA tSTAR arg_value
+ {
+ result = val[0] << @builder.splat(val[2], val[3])
+ }
+ | tSTAR arg_value
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+
+ cases: opt_else
+ {
+ result = [ val[0] ]
+ }
+ | case_body
+
+ opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue
+ {
+ assoc_t, exc_var = val[2]
+
+ if val[1]
+ exc_list = @builder.array(nil, val[1], nil)
+ end
+
+ result = [ @builder.rescue_body(val[0],
+ exc_list, assoc_t, exc_var,
+ val[3], val[4]),
+ *val[5] ]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ exc_list: arg_value
+ {
+ result = [ val[0] ]
+ }
+ | mrhs
+ | none
+
+ exc_var: tASSOC lhs
+ {
+ result = [ val[0], val[1] ]
+ }
+ | none
+
+ opt_ensure: kENSURE compstmt
+ {
+ result = [ val[0], val[1] ]
+ }
+ | none
+
+ literal: numeric
+ | symbol
+ | dsym
+
+ strings: string
+ {
+ result = @builder.string_compose(nil, val[0], nil)
+ }
+
+ string: string1
+ {
+ result = [ val[0] ]
+ }
+ | string string1
+ {
+ result = val[0] << val[1]
+ }
+
+ string1: tSTRING_BEG string_contents tSTRING_END
+ {
+ result = @builder.string_compose(val[0], val[1], val[2])
+ }
+ | tSTRING
+ {
+ result = @builder.string(val[0])
+ }
+
+ xstring: tXSTRING_BEG xstring_contents tSTRING_END
+ {
+ result = @builder.xstring_compose(val[0], val[1], val[2])
+ }
+
+ regexp: tREGEXP_BEG xstring_contents tSTRING_END tREGEXP_OPT
+ {
+ opts = @builder.regexp_options(val[3])
+ result = @builder.regexp_compose(val[0], val[1], val[2], opts)
+ }
+
+ words: tWORDS_BEG word_list tSTRING_END
+ {
+ result = @builder.words_compose(val[0], val[1], val[2])
+ }
+
+ word_list: # nothing
+ {
+ result = []
+ }
+ | word_list word tSPACE
+ {
+ result = val[0] << @builder.word(val[1])
+ }
+
+ word: string_content
+ {
+ result = [ val[0] ]
+ }
+ | word string_content
+ {
+ result = val[0] << val[1]
+ }
+
+ qwords: tQWORDS_BEG qword_list tSTRING_END
+ {
+ result = @builder.words_compose(val[0], val[1], val[2])
+ }
+
+ qword_list: # nothing
+ {
+ result = []
+ }
+ | qword_list tSTRING_CONTENT tSPACE
+ {
+ result = val[0] << @builder.string_internal(val[1])
+ }
+
+ string_contents: # nothing
+ {
+ result = []
+ }
+ | string_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+xstring_contents: # nothing
+ {
+ result = []
+ }
+ | xstring_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+ string_content: tSTRING_CONTENT
+ {
+ result = @builder.string_internal(val[0])
+ }
+ | tSTRING_DVAR string_dvar
+ {
+ result = val[1]
+ }
+ | tSTRING_DBEG
+ {
+ @lexer.cond.push(false)
+ @lexer.cmdarg.push(false)
+ }
+ compstmt tRCURLY
+ {
+ @lexer.cond.lexpop
+ @lexer.cmdarg.lexpop
+
+ result = @builder.begin(val[0], val[2], val[3])
+ }
+
+ string_dvar: tGVAR
+ {
+ result = @builder.gvar(val[0])
+ }
+ | tIVAR
+ {
+ result = @builder.ivar(val[0])
+ }
+ | tCVAR
+ {
+ result = @builder.cvar(val[0])
+ }
+ | backref
+
+
+ symbol: tSYMBOL
+ {
+ result = @builder.symbol(val[0])
+ }
+
+ dsym: tSYMBEG xstring_contents tSTRING_END
+ {
+ result = @builder.symbol_compose(val[0], val[1], val[2])
+ }
+
+ numeric: tINTEGER
+ {
+ result = @builder.integer(val[0])
+ }
+ | tFLOAT
+ {
+ result = @builder.float(val[0])
+ }
+ | tUMINUS_NUM tINTEGER =tLOWEST
+ {
+ result = @builder.negate(val[0],
+ @builder.integer(val[1]))
+ }
+ | tUMINUS_NUM tFLOAT =tLOWEST
+ {
+ result = @builder.negate(val[0],
+ @builder.float(val[1]))
+ }
+
+ variable: tIDENTIFIER
+ {
+ result = @builder.ident(val[0])
+ }
+ | tIVAR
+ {
+ result = @builder.ivar(val[0])
+ }
+ | tGVAR
+ {
+ result = @builder.gvar(val[0])
+ }
+ | tCVAR
+ {
+ result = @builder.cvar(val[0])
+ }
+ | tCONSTANT
+ {
+ result = @builder.const(val[0])
+ }
+ | kNIL
+ {
+ result = @builder.nil(val[0])
+ }
+ | kSELF
+ {
+ result = @builder.self(val[0])
+ }
+ | kTRUE
+ {
+ result = @builder.true(val[0])
+ }
+ | kFALSE
+ {
+ result = @builder.false(val[0])
+ }
+ | k__FILE__
+ {
+ result = @builder.__FILE__(val[0])
+ }
+ | k__LINE__
+ {
+ result = @builder.__LINE__(val[0])
+ }
+
+ var_ref: variable
+ {
+ result = @builder.accessible(val[0])
+ }
+
+ var_lhs: variable
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ backref: tNTH_REF
+ {
+ result = @builder.nth_ref(val[0])
+ }
+ | tBACK_REF
+ {
+ result = @builder.back_ref(val[0])
+ }
+
+ superclass: term
+ {
+ result = nil
+ }
+ | tLT expr_value term
+ {
+ result = [ val[0], val[1] ]
+ }
+ | error term
+ {
+ yyerrok
+ result = nil
+ }
+
+ f_arglist: tLPAREN2 f_args opt_nl tRPAREN
+ {
+ result = @builder.args(val[0], val[1], val[3])
+
+ @lexer.state = :expr_beg
+ }
+ | f_args term
+ {
+ result = @builder.args(nil, val[0], nil)
+ }
+
+ f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_optarg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_optarg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_optarg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_block_arg
+ {
+ result = [ val[0] ]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ f_norm_arg: tCONSTANT
+ {
+ diagnostic :error, :argument_const, nil, val[0]
+ }
+ | tIVAR
+ {
+ diagnostic :error, :argument_ivar, nil, val[0]
+ }
+ | tGVAR
+ {
+ diagnostic :error, :argument_gvar, nil, val[0]
+ }
+ | tCVAR
+ {
+ diagnostic :error, :argument_cvar, nil, val[0]
+ }
+ | tIDENTIFIER
+ {
+ @static_env.declare val[0][0]
+
+ result = @builder.arg(val[0])
+ }
+
+ f_arg: f_norm_arg
+ {
+ result = [ val[0] ]
+ }
+ | f_arg tCOMMA f_norm_arg
+ {
+ result = val[0] << val[2]
+ }
+
+ f_opt: tIDENTIFIER tEQL arg_value
+ {
+ @static_env.declare val[0][0]
+
+ result = @builder.optarg(val[0], val[1], val[2])
+ }
+
+ f_optarg: f_opt
+ {
+ result = [ val[0] ]
+ }
+ | f_optarg tCOMMA f_opt
+ {
+ result = val[0] << val[2]
+ }
+
+ restarg_mark: tSTAR2 | tSTAR
+
+ f_rest_arg: restarg_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = [ @builder.restarg(val[0], val[1]) ]
+ }
+ | restarg_mark
+ {
+ result = [ @builder.restarg(val[0]) ]
+ }
+
+ blkarg_mark: tAMPER2 | tAMPER
+
+ f_block_arg: blkarg_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = @builder.blockarg(val[0], val[1])
+ }
+
+ opt_f_block_arg: tCOMMA f_block_arg
+ {
+ result = [ val[1] ]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ singleton: var_ref
+ | tLPAREN2 expr opt_nl tRPAREN
+ {
+ result = val[1]
+ }
+
+ assoc_list: # nothing
+ {
+ result = []
+ }
+ | assocs trailer
+ {
+ result = val[0]
+ }
+ | args trailer
+ {
+ result = @builder.pair_list_18(val[0])
+ }
+
+ assocs: assoc
+ {
+ result = [ val[0] ]
+ }
+ | assocs tCOMMA assoc
+ {
+ result = val[0] << val[2]
+ }
+
+ assoc: arg_value tASSOC arg_value
+ {
+ result = @builder.pair(val[0], val[1], val[2])
+ }
+
+ operation: tIDENTIFIER | tCONSTANT | tFID
+ operation2: tIDENTIFIER | tCONSTANT | tFID | op
+ operation3: tIDENTIFIER | tFID | op
+ dot_or_colon: tDOT | tCOLON2
+ opt_terms: | terms
+ opt_nl: | tNL
+ trailer: | tNL | tCOMMA
+
+ term: tSEMI
+ {
+ yyerrok
+ }
+ | tNL
+
+ terms: term
+ | terms tSEMI
+
+ none: # nothing
+ {
+ result = nil
+ }
+
+end
+
+---- header
+
+require 'parser'
+
+---- inner
+
+ def version
+ 18
+ end
+
+ def default_encoding
+ Encoding::BINARY if defined? Encoding
+ end
diff --git a/test/racc/assets/ruby19.y b/test/racc/assets/ruby19.y
new file mode 100644
index 0000000000..b405c952e7
--- /dev/null
+++ b/test/racc/assets/ruby19.y
@@ -0,0 +1,2174 @@
+# Copyright (c) 2013 Peter Zotov <whitequark@whitequark.org>
+#
+# Parts of the source are derived from ruby_parser:
+# Copyright (c) Ryan Davis, seattle.rb
+#
+# MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+class Parser::Ruby19
+
+token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
+ kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT
+ kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kDO_LAMBDA kRETURN kYIELD kSUPER
+ kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD
+ kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__
+ k__FILE__ k__ENCODING__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT
+ tLABEL tCVAR tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT
+ tREGEXP_END tUPLUS tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ
+ tGEQ tLEQ tANDOP tOROP tMATCH tNMATCH tDOT tDOT2 tDOT3 tAREF
+ tASET tLSHFT tRSHFT tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN
+ tLPAREN2 tRPAREN tLPAREN_ARG tLBRACK tLBRACK2 tRBRACK tLBRACE
+ tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2 tTILDE tPERCENT tDIVIDE
+ tPLUS tMINUS tLT tGT tPIPE tBANG tCARET tLCURLY tRCURLY
+ tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tREGEXP_OPT
+ tWORDS_BEG tQWORDS_BEG tSTRING_DBEG tSTRING_DVAR tSTRING_END
+ tSTRING tSYMBOL tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG
+ tCHARACTER
+
+prechigh
+ right tBANG tTILDE tUPLUS
+ right tPOW
+ right tUMINUS_NUM tUMINUS
+ left tSTAR2 tDIVIDE tPERCENT
+ left tPLUS tMINUS
+ left tLSHFT tRSHFT
+ left tAMPER2
+ left tPIPE tCARET
+ left tGT tGEQ tLT tLEQ
+ nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
+ left tANDOP
+ left tOROP
+ nonassoc tDOT2 tDOT3
+ right tEH tCOLON
+ left kRESCUE_MOD
+ right tEQL tOP_ASGN
+ nonassoc kDEFINED
+ right kNOT
+ left kOR kAND
+ nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
+ nonassoc tLBRACE_ARG
+ nonassoc tLOWEST
+preclow
+
+rule
+
+ program: top_compstmt
+
+ top_compstmt: top_stmts opt_terms
+ {
+ result = @builder.compstmt(val[0])
+ }
+
+ top_stmts: # nothing
+ {
+ result = []
+ }
+ | top_stmt
+ {
+ result = [ val[0] ]
+ }
+ | top_stmts terms top_stmt
+ {
+ result = val[0] << val[2]
+ }
+ | error top_stmt
+ {
+ result = [ val[1] ]
+ }
+
+ top_stmt: stmt
+ | klBEGIN tLCURLY top_compstmt tRCURLY
+ {
+ result = @builder.preexe(val[0], val[1], val[2], val[3])
+ }
+
+ bodystmt: compstmt opt_rescue opt_else opt_ensure
+ {
+ rescue_bodies = val[1]
+ else_t, else_ = val[2]
+ ensure_t, ensure_ = val[3]
+
+ if rescue_bodies.empty? && !else_.nil?
+ diagnostic :warning, :useless_else, nil, else_t
+ end
+
+ result = @builder.begin_body(val[0],
+ rescue_bodies,
+ else_t, else_,
+ ensure_t, ensure_)
+ }
+
+ compstmt: stmts opt_terms
+ {
+ result = @builder.compstmt(val[0])
+ }
+
+ stmts: # nothing
+ {
+ result = []
+ }
+ | stmt
+ {
+ result = [ val[0] ]
+ }
+ | stmts terms stmt
+ {
+ result = val[0] << val[2]
+ }
+ | error stmt
+ {
+ result = [ val[1] ]
+ }
+
+ stmt: kALIAS fitem
+ {
+ @lexer.state = :expr_fname
+ }
+ fitem
+ {
+ result = @builder.alias(val[0], val[1], val[3])
+ }
+ | kALIAS tGVAR tGVAR
+ {
+ result = @builder.alias(val[0],
+ @builder.gvar(val[1]),
+ @builder.gvar(val[2]))
+ }
+ | kALIAS tGVAR tBACK_REF
+ {
+ result = @builder.alias(val[0],
+ @builder.gvar(val[1]),
+ @builder.back_ref(val[2]))
+ }
+ | kALIAS tGVAR tNTH_REF
+ {
+ diagnostic :error, :nth_ref_alias, nil, val[2]
+ }
+ | kUNDEF undef_list
+ {
+ result = @builder.undef_method(val[0], val[1])
+ }
+ | stmt kIF_MOD expr_value
+ {
+ result = @builder.condition_mod(val[0], nil,
+ val[1], val[2])
+ }
+ | stmt kUNLESS_MOD expr_value
+ {
+ result = @builder.condition_mod(nil, val[0],
+ val[1], val[2])
+ }
+ | stmt kWHILE_MOD expr_value
+ {
+ result = @builder.loop_mod(:while, val[0], val[1], val[2])
+ }
+ | stmt kUNTIL_MOD expr_value
+ {
+ result = @builder.loop_mod(:until, val[0], val[1], val[2])
+ }
+ | stmt kRESCUE_MOD stmt
+ {
+ rescue_body = @builder.rescue_body(val[1],
+ nil, nil, nil,
+ nil, val[2])
+
+ result = @builder.begin_body(val[0], [ rescue_body ])
+ }
+ | klEND tLCURLY compstmt tRCURLY
+ {
+ result = @builder.postexe(val[0], val[1], val[2], val[3])
+ }
+ | command_asgn
+ | mlhs tEQL command_call
+ {
+ result = @builder.multi_assign(val[0], val[1], val[2])
+ }
+ | var_lhs tOP_ASGN command_call
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.index(
+ val[0], val[1], val[2], val[3]),
+ val[4], val[5])
+ }
+ | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tDOT tCONSTANT tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | backref tOP_ASGN command_call
+ {
+ @builder.op_assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL mrhs
+ {
+ result = @builder.assign(val[0], val[1],
+ @builder.array(nil, val[2], nil))
+ }
+ | mlhs tEQL arg_value
+ {
+ result = @builder.multi_assign(val[0], val[1], val[2])
+ }
+ | mlhs tEQL mrhs
+ {
+ result = @builder.multi_assign(val[0], val[1],
+ @builder.array(nil, val[2], nil))
+ }
+ | expr
+
+ command_asgn: lhs tEQL command_call
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL command_asgn
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+
+ expr: command_call
+ | expr kAND expr
+ {
+ result = @builder.logical_op(:and, val[0], val[1], val[2])
+ }
+ | expr kOR expr
+ {
+ result = @builder.logical_op(:or, val[0], val[1], val[2])
+ }
+ | kNOT opt_nl expr
+ {
+ result = @builder.not_op(val[0], nil, val[2], nil)
+ }
+ | tBANG command_call
+ {
+ result = @builder.not_op(val[0], nil, val[1], nil)
+ }
+ | arg
+
+ expr_value: expr
+
+ command_call: command
+ | block_command
+
+ block_command: block_call
+ | block_call tDOT operation2 command_args
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+ }
+ | block_call tCOLON2 operation2 command_args
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+ }
+
+ cmd_brace_block: tLBRACE_ARG
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt tRCURLY
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ command: operation command_args =tLOWEST
+ {
+ result = @builder.call_method(nil, nil, val[0],
+ nil, val[1], nil)
+ }
+ | operation command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(nil, nil, val[0],
+ nil, val[1], nil)
+
+ begin_t, args, body, end_t = val[2]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | primary_value tDOT operation2 command_args =tLOWEST
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+ }
+ | primary_value tDOT operation2 command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | primary_value tCOLON2 operation2 command_args =tLOWEST
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+ }
+ | primary_value tCOLON2 operation2 command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | kSUPER command_args
+ {
+ result = @builder.keyword_cmd(:super, val[0],
+ nil, val[1], nil)
+ }
+ | kYIELD command_args
+ {
+ result = @builder.keyword_cmd(:yield, val[0],
+ nil, val[1], nil)
+ }
+ | kRETURN call_args
+ {
+ result = @builder.keyword_cmd(:return, val[0],
+ nil, val[1], nil)
+ }
+ | kBREAK call_args
+ {
+ result = @builder.keyword_cmd(:break, val[0],
+ nil, val[1], nil)
+ }
+ | kNEXT call_args
+ {
+ result = @builder.keyword_cmd(:next, val[0],
+ nil, val[1], nil)
+ }
+
+ mlhs: mlhs_basic
+ {
+ result = @builder.multi_lhs(nil, val[0], nil)
+ }
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+
+ mlhs_inner: mlhs_basic
+ {
+ result = @builder.multi_lhs(nil, val[0], nil)
+ }
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ mlhs_basic: mlhs_head
+ | mlhs_head mlhs_item
+ {
+ result = val[0].
+ push(val[1])
+ }
+ | mlhs_head tSTAR mlhs_node
+ {
+ result = val[0].
+ push(@builder.splat(val[1], val[2]))
+ }
+ | mlhs_head tSTAR mlhs_node tCOMMA mlhs_post
+ {
+ result = val[0].
+ push(@builder.splat(val[1], val[2])).
+ concat(val[4])
+ }
+ | mlhs_head tSTAR
+ {
+ result = val[0].
+ push(@builder.splat(val[1]))
+ }
+ | mlhs_head tSTAR tCOMMA mlhs_post
+ {
+ result = val[0].
+ push(@builder.splat(val[1])).
+ concat(val[3])
+ }
+ | tSTAR mlhs_node
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+ | tSTAR mlhs_node tCOMMA mlhs_post
+ {
+ result = [ @builder.splat(val[0], val[1]),
+ *val[3] ]
+ }
+ | tSTAR
+ {
+ result = [ @builder.splat(val[0]) ]
+ }
+ | tSTAR tCOMMA mlhs_post
+ {
+ result = [ @builder.splat(val[0]),
+ *val[2] ]
+ }
+
+ mlhs_item: mlhs_node
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+
+ mlhs_head: mlhs_item tCOMMA
+ {
+ result = [ val[0] ]
+ }
+ | mlhs_head mlhs_item tCOMMA
+ {
+ result = val[0] << val[1]
+ }
+
+ mlhs_post: mlhs_item
+ {
+ result = [ val[0] ]
+ }
+ | mlhs_post tCOMMA mlhs_item
+ {
+ result = val[0] << val[2]
+ }
+
+ mlhs_node: user_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index_asgn(val[0], val[1], val[2], val[3])
+ }
+ | primary_value tDOT tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tDOT tCONSTANT
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_global(val[0], val[1]))
+ }
+ | backref
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ lhs: user_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index_asgn(val[0], val[1], val[2], val[3])
+ }
+ | primary_value tDOT tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tDOT tCONSTANT
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_global(val[0], val[1]))
+ }
+ | backref
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ cname: tIDENTIFIER
+ {
+ diagnostic :error, :module_name_const, nil, val[0]
+ }
+ | tCONSTANT
+
+ cpath: tCOLON3 cname
+ {
+ result = @builder.const_global(val[0], val[1])
+ }
+ | cname
+ {
+ result = @builder.const(val[0])
+ }
+ | primary_value tCOLON2 cname
+ {
+ result = @builder.const_fetch(val[0], val[1], val[2])
+ }
+
+ fname: tIDENTIFIER | tCONSTANT | tFID
+ | op
+ | reswords
+
+ fsym: fname
+ {
+ result = @builder.symbol(val[0])
+ }
+ | symbol
+
+ fitem: fsym
+ | dsym
+
+ undef_list: fitem
+ {
+ result = [ val[0] ]
+ }
+ | undef_list tCOMMA
+ {
+ @lexer.state = :expr_fname
+ }
+ fitem
+ {
+ result = val[0] << val[3]
+ }
+
+ op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ
+ | tMATCH | tNMATCH | tGT | tGEQ | tLT | tLEQ
+ | tNEQ | tLSHFT | tRSHFT | tPLUS | tMINUS | tSTAR2
+ | tSTAR | tDIVIDE | tPERCENT | tPOW | tBANG | tTILDE
+ | tUPLUS | tUMINUS | tAREF | tASET | tBACK_REF2
+
+ reswords: k__LINE__ | k__FILE__ | k__ENCODING__ | klBEGIN | klEND
+ | kALIAS | kAND | kBEGIN | kBREAK | kCASE
+ | kCLASS | kDEF | kDEFINED | kDO | kELSE
+ | kELSIF | kEND | kENSURE | kFALSE | kFOR
+ | kIN | kMODULE | kNEXT | kNIL | kNOT
+ | kOR | kREDO | kRESCUE | kRETRY | kRETURN
+ | kSELF | kSUPER | kTHEN | kTRUE | kUNDEF
+ | kWHEN | kYIELD | kIF | kUNLESS | kWHILE
+ | kUNTIL
+
+ arg: lhs tEQL arg
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL arg kRESCUE_MOD arg
+ {
+ rescue_body = @builder.rescue_body(val[3],
+ nil, nil, nil,
+ nil, val[4])
+
+ rescue_ = @builder.begin_body(val[2], [ rescue_body ])
+
+ result = @builder.assign(val[0], val[1], rescue_)
+ }
+ | var_lhs tOP_ASGN arg
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | var_lhs tOP_ASGN arg kRESCUE_MOD arg
+ {
+ rescue_body = @builder.rescue_body(val[3],
+ nil, nil, nil,
+ nil, val[4])
+
+ rescue_ = @builder.begin_body(val[2], [ rescue_body ])
+
+ result = @builder.op_assign(val[0], val[1], rescue_)
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.index(
+ val[0], val[1], val[2], val[3]),
+ val[4], val[5])
+ }
+ | primary_value tDOT tIDENTIFIER tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tDOT tCONSTANT tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
+ {
+ diagnostic :error, :dynamic_const, nil, val[2], [ val[3] ]
+ }
+ | tCOLON3 tCONSTANT tOP_ASGN arg
+ {
+ diagnostic :error, :dynamic_const, nil, val[1], [ val[2] ]
+ }
+ | backref tOP_ASGN arg
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | arg tDOT2 arg
+ {
+ result = @builder.range_inclusive(val[0], val[1], val[2])
+ }
+ | arg tDOT3 arg
+ {
+ result = @builder.range_exclusive(val[0], val[1], val[2])
+ }
+ | arg tPLUS arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tMINUS arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tSTAR2 arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tDIVIDE arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tPERCENT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tPOW arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | tUMINUS_NUM tINTEGER tPOW arg
+ {
+ result = @builder.unary_op(val[0],
+ @builder.binary_op(
+ @builder.integer(val[1]),
+ val[2], val[3]))
+ }
+ | tUMINUS_NUM tFLOAT tPOW arg
+ {
+ result = @builder.unary_op(val[0],
+ @builder.binary_op(
+ @builder.float(val[1]),
+ val[2], val[3]))
+ }
+ | tUPLUS arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | tUMINUS arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | arg tPIPE arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tCARET arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tAMPER2 arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tCMP arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tGT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tGEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tLT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tLEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tEQQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tNEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tMATCH arg
+ {
+ result = @builder.match_op(val[0], val[1], val[2])
+ }
+ | arg tNMATCH arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | tBANG arg
+ {
+ result = @builder.not_op(val[0], nil, val[1], nil)
+ }
+ | tTILDE arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | arg tLSHFT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tRSHFT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tANDOP arg
+ {
+ result = @builder.logical_op(:and, val[0], val[1], val[2])
+ }
+ | arg tOROP arg
+ {
+ result = @builder.logical_op(:or, val[0], val[1], val[2])
+ }
+ | kDEFINED opt_nl arg
+ {
+ result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil)
+ }
+
+ | arg tEH arg opt_nl tCOLON arg
+ {
+ result = @builder.ternary(val[0], val[1],
+ val[2], val[4], val[5])
+ }
+ | primary
+
+ arg_value: arg
+
+ aref_args: none
+ | args trailer
+ | args tCOMMA assocs trailer
+ {
+ result = val[0] << @builder.associate(nil, val[2], nil)
+ }
+ | assocs trailer
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ }
+
+ paren_args: tLPAREN2 opt_call_args rparen
+ {
+ result = val
+ }
+
+ opt_paren_args: # nothing
+ {
+ result = [ nil, [], nil ]
+ }
+ | paren_args
+
+ opt_call_args: # nothing
+ {
+ result = []
+ }
+ | call_args
+ | args tCOMMA
+ | args tCOMMA assocs tCOMMA
+ {
+ result = val[0] << @builder.associate(nil, val[2], nil)
+ }
+ | assocs tCOMMA
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ }
+
+ call_args: command
+ {
+ result = [ val[0] ]
+ }
+ | args opt_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | assocs opt_block_arg
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ result.concat(val[1])
+ }
+ | args tCOMMA assocs opt_block_arg
+ {
+ assocs = @builder.associate(nil, val[2], nil)
+ result = val[0] << assocs
+ result.concat(val[3])
+ }
+ | block_arg
+ {
+ result = [ val[0] ]
+ }
+
+ command_args: {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.push(true)
+ }
+ call_args
+ {
+ @lexer.cmdarg = val[0]
+
+ result = val[1]
+ }
+
+ block_arg: tAMPER arg_value
+ {
+ result = @builder.block_pass(val[0], val[1])
+ }
+
+ opt_block_arg: tCOMMA block_arg
+ {
+ result = [ val[1] ]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ args: arg_value
+ {
+ result = [ val[0] ]
+ }
+ | tSTAR arg_value
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+ | args tCOMMA arg_value
+ {
+ result = val[0] << val[2]
+ }
+ | args tCOMMA tSTAR arg_value
+ {
+ result = val[0] << @builder.splat(val[2], val[3])
+ }
+
+ mrhs: args tCOMMA arg_value
+ {
+ result = val[0] << val[2]
+ }
+ | args tCOMMA tSTAR arg_value
+ {
+ result = val[0] << @builder.splat(val[2], val[3])
+ }
+ | tSTAR arg_value
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+
+ primary: literal
+ | strings
+ | xstring
+ | regexp
+ | words
+ | qwords
+ | var_ref
+ | backref
+ | tFID
+ {
+ result = @builder.call_method(nil, nil, val[0])
+ }
+ | kBEGIN bodystmt kEND
+ {
+ result = @builder.begin_keyword(val[0], val[1], val[2])
+ }
+ | tLPAREN_ARG
+ {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.clear
+ }
+ expr
+ {
+ @lexer.state = :expr_endarg
+ }
+ opt_nl tRPAREN
+ {
+ @lexer.cmdarg = val[1]
+
+ result = @builder.begin(val[0], val[2], val[5])
+ }
+ | tLPAREN compstmt tRPAREN
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.const_fetch(val[0], val[1], val[2])
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.const_global(val[0], val[1])
+ }
+ | tLBRACK aref_args tRBRACK
+ {
+ result = @builder.array(val[0], val[1], val[2])
+ }
+ | tLBRACE assoc_list tRCURLY
+ {
+ result = @builder.associate(val[0], val[1], val[2])
+ }
+ | kRETURN
+ {
+ result = @builder.keyword_cmd(:return, val[0])
+ }
+ | kYIELD tLPAREN2 call_args rparen
+ {
+ result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3])
+ }
+ | kYIELD tLPAREN2 rparen
+ {
+ result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2])
+ }
+ | kYIELD
+ {
+ result = @builder.keyword_cmd(:yield, val[0])
+ }
+ | kDEFINED opt_nl tLPAREN2 expr rparen
+ {
+ result = @builder.keyword_cmd(:defined?, val[0],
+ val[2], [ val[3] ], val[4])
+ }
+ | kNOT tLPAREN2 expr rparen
+ {
+ result = @builder.not_op(val[0], val[1], val[2], val[3])
+ }
+ | kNOT tLPAREN2 rparen
+ {
+ result = @builder.not_op(val[0], val[1], nil, val[2])
+ }
+ | operation brace_block
+ {
+ method_call = @builder.call_method(nil, nil, val[0])
+
+ begin_t, args, body, end_t = val[1]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | method_call
+ | method_call brace_block
+ {
+ begin_t, args, body, end_t = val[1]
+ result = @builder.block(val[0],
+ begin_t, args, body, end_t)
+ }
+ | tLAMBDA lambda
+ {
+ lambda_call = @builder.call_lambda(val[0])
+
+ args, (begin_t, body, end_t) = val[1]
+ result = @builder.block(lambda_call,
+ begin_t, args, body, end_t)
+ }
+ | kIF expr_value then compstmt if_tail kEND
+ {
+ else_t, else_ = val[4]
+ result = @builder.condition(val[0], val[1], val[2],
+ val[3], else_t,
+ else_, val[5])
+ }
+ | kUNLESS expr_value then compstmt opt_else kEND
+ {
+ else_t, else_ = val[4]
+ result = @builder.condition(val[0], val[1], val[2],
+ else_, else_t,
+ val[3], val[5])
+ }
+ | kWHILE
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.loop(:while, val[0], val[2], val[3],
+ val[5], val[6])
+ }
+ | kUNTIL
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.loop(:until, val[0], val[2], val[3],
+ val[5], val[6])
+ }
+ | kCASE expr_value opt_terms case_body kEND
+ {
+ *when_bodies, (else_t, else_body) = *val[3]
+
+ result = @builder.case(val[0], val[1],
+ when_bodies, else_t, else_body,
+ val[4])
+ }
+ | kCASE opt_terms case_body kEND
+ {
+ *when_bodies, (else_t, else_body) = *val[2]
+
+ result = @builder.case(val[0], nil,
+ when_bodies, else_t, else_body,
+ val[3])
+ }
+ | kFOR for_var kIN
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.for(val[0], val[1],
+ val[2], val[4],
+ val[5], val[7], val[8])
+ }
+ | kCLASS cpath superclass
+ {
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ if in_def?
+ diagnostic :error, :class_in_def, nil, val[0]
+ end
+
+ lt_t, superclass = val[2]
+ result = @builder.def_class(val[0], val[1],
+ lt_t, superclass,
+ val[4], val[5])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ }
+ | kCLASS tLSHFT expr term
+ {
+ result = @def_level
+ @def_level = 0
+
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ result = @builder.def_sclass(val[0], val[1], val[2],
+ val[5], val[6])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+
+ @def_level = val[4]
+ }
+ | kMODULE cpath
+ {
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ if in_def?
+ diagnostic :error, :module_in_def, nil, val[0]
+ end
+
+ result = @builder.def_module(val[0], val[1],
+ val[3], val[4])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ }
+ | kDEF fname
+ {
+ @def_level += 1
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ f_arglist bodystmt kEND
+ {
+ result = @builder.def_method(val[0], val[1],
+ val[3], val[4], val[5])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ @def_level -= 1
+ }
+ | kDEF singleton dot_or_colon
+ {
+ @lexer.state = :expr_fname
+ }
+ fname
+ {
+ @def_level += 1
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ f_arglist bodystmt kEND
+ {
+ result = @builder.def_singleton(val[0], val[1], val[2],
+ val[4], val[6], val[7], val[8])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ @def_level -= 1
+ }
+ | kBREAK
+ {
+ result = @builder.keyword_cmd(:break, val[0])
+ }
+ | kNEXT
+ {
+ result = @builder.keyword_cmd(:next, val[0])
+ }
+ | kREDO
+ {
+ result = @builder.keyword_cmd(:redo, val[0])
+ }
+ | kRETRY
+ {
+ result = @builder.keyword_cmd(:retry, val[0])
+ }
+
+ primary_value: primary
+
+ then: term
+ | kTHEN
+ | term kTHEN
+ {
+ result = val[1]
+ }
+
+ do: term
+ | kDO_COND
+
+ if_tail: opt_else
+ | kELSIF expr_value then compstmt if_tail
+ {
+ else_t, else_ = val[4]
+ result = [ val[0],
+ @builder.condition(val[0], val[1], val[2],
+ val[3], else_t,
+ else_, nil),
+ ]
+ }
+
+ opt_else: none
+ | kELSE compstmt
+ {
+ result = val
+ }
+
+ for_var: lhs
+ | mlhs
+
+ f_marg: f_norm_arg
+ {
+ @static_env.declare val[0][0]
+
+ result = @builder.arg(val[0])
+ }
+ | tLPAREN f_margs rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ f_marg_list: f_marg
+ {
+ result = [ val[0] ]
+ }
+ | f_marg_list tCOMMA f_marg
+ {
+ result = val[0] << val[2]
+ }
+
+ f_margs: f_marg_list
+ | f_marg_list tCOMMA tSTAR f_norm_arg
+ {
+ @static_env.declare val[3][0]
+
+ result = val[0].
+ push(@builder.restarg(val[2], val[3]))
+ }
+ | f_marg_list tCOMMA tSTAR f_norm_arg tCOMMA f_marg_list
+ {
+ @static_env.declare val[3][0]
+
+ result = val[0].
+ push(@builder.restarg(val[2], val[3])).
+ concat(val[5])
+ }
+ | f_marg_list tCOMMA tSTAR
+ {
+ result = val[0].
+ push(@builder.restarg(val[2]))
+ }
+ | f_marg_list tCOMMA tSTAR tCOMMA f_marg_list
+ {
+ result = val[0].
+ push(@builder.restarg(val[2])).
+ concat(val[4])
+ }
+ | tSTAR f_norm_arg
+ {
+ @static_env.declare val[1][0]
+
+ result = [ @builder.restarg(val[0], val[1]) ]
+ }
+ | tSTAR f_norm_arg tCOMMA f_marg_list
+ {
+ @static_env.declare val[1][0]
+
+ result = [ @builder.restarg(val[0], val[1]),
+ *val[3] ]
+ }
+ | tSTAR
+ {
+ result = [ @builder.restarg(val[0]) ]
+ }
+ | tSTAR tCOMMA f_marg_list
+ {
+ result = [ @builder.restarg(val[0]),
+ *val[2] ]
+ }
+
+ block_param: f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[6]).
+ concat(val[7])
+ }
+ | f_arg tCOMMA f_block_optarg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_block_optarg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA
+ | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg opt_f_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_block_optarg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_block_optarg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_block_optarg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_block_arg
+ {
+ result = [ val[0] ]
+ }
+
+ opt_block_param: # nothing
+ {
+ result = @builder.args(nil, [], nil)
+ }
+ | block_param_def
+ {
+ @lexer.state = :expr_value
+ }
+
+ block_param_def: tPIPE opt_bv_decl tPIPE
+ {
+ result = @builder.args(val[0], val[1], val[2])
+ }
+ | tOROP
+ {
+ result = @builder.args(val[0], [], val[0])
+ }
+ | tPIPE block_param opt_bv_decl tPIPE
+ {
+ result = @builder.args(val[0], val[1].concat(val[2]), val[3])
+ }
+
+ opt_bv_decl: # nothing
+ {
+ result = []
+ }
+ | tSEMI bv_decls
+ {
+ result = val[1]
+ }
+
+ bv_decls: bvar
+ {
+ result = [ val[0] ]
+ }
+ | bv_decls tCOMMA bvar
+ {
+ result = val[0] << val[2]
+ }
+
+ bvar: tIDENTIFIER
+ {
+ result = @builder.shadowarg(val[0])
+ }
+ | f_bad_arg
+
+ lambda: {
+ @static_env.extend_dynamic
+ }
+ f_larglist lambda_body
+ {
+ result = [ val[1], val[2] ]
+
+ @static_env.unextend
+ }
+
+ f_larglist: tLPAREN2 f_args opt_bv_decl rparen
+ {
+ result = @builder.args(val[0], val[1].concat(val[2]), val[3])
+ }
+ | f_args
+ {
+ result = @builder.args(nil, val[0], nil)
+ }
+
+ lambda_body: tLAMBEG compstmt tRCURLY
+ {
+ result = [ val[0], val[1], val[2] ]
+ }
+ | kDO_LAMBDA compstmt kEND
+ {
+ result = [ val[0], val[1], val[2] ]
+ }
+
+ do_block: kDO_BLOCK
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt kEND
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ block_call: command do_block
+ {
+ begin_t, block_args, body, end_t = val[1]
+ result = @builder.block(val[0],
+ begin_t, block_args, body, end_t)
+ }
+ | block_call tDOT operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | block_call tCOLON2 operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+
+ method_call: operation paren_args
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.call_method(nil, nil, val[0],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tDOT operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 operation2 paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 operation3
+ {
+ result = @builder.call_method(val[0], val[1], val[2])
+ }
+ | primary_value tDOT paren_args
+ {
+ lparen_t, args, rparen_t = val[2]
+ result = @builder.call_method(val[0], val[1], nil,
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 paren_args
+ {
+ lparen_t, args, rparen_t = val[2]
+ result = @builder.call_method(val[0], val[1], nil,
+ lparen_t, args, rparen_t)
+ }
+ | kSUPER paren_args
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.keyword_cmd(:super, val[0],
+ lparen_t, args, rparen_t)
+ }
+ | kSUPER
+ {
+ result = @builder.keyword_cmd(:zsuper, val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index(val[0], val[1], val[2], val[3])
+ }
+
+ brace_block: tLCURLY
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt tRCURLY
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+ | kDO
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt kEND
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ case_body: kWHEN args then compstmt cases
+ {
+ result = [ @builder.when(val[0], val[1], val[2], val[3]),
+ *val[4] ]
+ }
+
+ cases: opt_else
+ {
+ result = [ val[0] ]
+ }
+ | case_body
+
+ opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue
+ {
+ assoc_t, exc_var = val[2]
+
+ if val[1]
+ exc_list = @builder.array(nil, val[1], nil)
+ end
+
+ result = [ @builder.rescue_body(val[0],
+ exc_list, assoc_t, exc_var,
+ val[3], val[4]),
+ *val[5] ]
+ }
+ |
+ {
+ result = []
+ }
+
+ exc_list: arg_value
+ {
+ result = [ val[0] ]
+ }
+ | mrhs
+ | none
+
+ exc_var: tASSOC lhs
+ {
+ result = [ val[0], val[1] ]
+ }
+ | none
+
+ opt_ensure: kENSURE compstmt
+ {
+ result = [ val[0], val[1] ]
+ }
+ | none
+
+ literal: numeric
+ | symbol
+ | dsym
+
+ strings: string
+ {
+ result = @builder.string_compose(nil, val[0], nil)
+ }
+
+ string: string1
+ {
+ result = [ val[0] ]
+ }
+ | string string1
+ {
+ result = val[0] << val[1]
+ }
+
+ string1: tSTRING_BEG string_contents tSTRING_END
+ {
+ result = @builder.string_compose(val[0], val[1], val[2])
+ }
+ | tSTRING
+ {
+ result = @builder.string(val[0])
+ }
+ | tCHARACTER
+ {
+ result = @builder.character(val[0])
+ }
+
+ xstring: tXSTRING_BEG xstring_contents tSTRING_END
+ {
+ result = @builder.xstring_compose(val[0], val[1], val[2])
+ }
+
+ regexp: tREGEXP_BEG regexp_contents tSTRING_END tREGEXP_OPT
+ {
+ opts = @builder.regexp_options(val[3])
+ result = @builder.regexp_compose(val[0], val[1], val[2], opts)
+ }
+
+ words: tWORDS_BEG word_list tSTRING_END
+ {
+ result = @builder.words_compose(val[0], val[1], val[2])
+ }
+
+ word_list: # nothing
+ {
+ result = []
+ }
+ | word_list word tSPACE
+ {
+ result = val[0] << @builder.word(val[1])
+ }
+
+ word: string_content
+ {
+ result = [ val[0] ]
+ }
+ | word string_content
+ {
+ result = val[0] << val[1]
+ }
+
+ qwords: tQWORDS_BEG qword_list tSTRING_END
+ {
+ result = @builder.words_compose(val[0], val[1], val[2])
+ }
+
+ qword_list: # nothing
+ {
+ result = []
+ }
+ | qword_list tSTRING_CONTENT tSPACE
+ {
+ result = val[0] << @builder.string_internal(val[1])
+ }
+
+ string_contents: # nothing
+ {
+ result = []
+ }
+ | string_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+xstring_contents: # nothing
+ {
+ result = []
+ }
+ | xstring_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+regexp_contents: # nothing
+ {
+ result = []
+ }
+ | regexp_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+ string_content: tSTRING_CONTENT
+ {
+ result = @builder.string_internal(val[0])
+ }
+ | tSTRING_DVAR string_dvar
+ {
+ result = val[1]
+ }
+ | tSTRING_DBEG
+ {
+ @lexer.cond.push(false)
+ @lexer.cmdarg.push(false)
+ }
+ compstmt tRCURLY
+ {
+ @lexer.cond.lexpop
+ @lexer.cmdarg.lexpop
+
+ result = @builder.begin(val[0], val[2], val[3])
+ }
+
+ string_dvar: tGVAR
+ {
+ result = @builder.gvar(val[0])
+ }
+ | tIVAR
+ {
+ result = @builder.ivar(val[0])
+ }
+ | tCVAR
+ {
+ result = @builder.cvar(val[0])
+ }
+ | backref
+
+
+ symbol: tSYMBOL
+ {
+ result = @builder.symbol(val[0])
+ }
+
+ dsym: tSYMBEG xstring_contents tSTRING_END
+ {
+ result = @builder.symbol_compose(val[0], val[1], val[2])
+ }
+
+ numeric: tINTEGER
+ {
+ result = @builder.integer(val[0])
+ }
+ | tFLOAT
+ {
+ result = @builder.float(val[0])
+ }
+ | tUMINUS_NUM tINTEGER =tLOWEST
+ {
+ result = @builder.negate(val[0],
+ @builder.integer(val[1]))
+ }
+ | tUMINUS_NUM tFLOAT =tLOWEST
+ {
+ result = @builder.negate(val[0],
+ @builder.float(val[1]))
+ }
+
+ user_variable: tIDENTIFIER
+ {
+ result = @builder.ident(val[0])
+ }
+ | tIVAR
+ {
+ result = @builder.ivar(val[0])
+ }
+ | tGVAR
+ {
+ result = @builder.gvar(val[0])
+ }
+ | tCONSTANT
+ {
+ result = @builder.const(val[0])
+ }
+ | tCVAR
+ {
+ result = @builder.cvar(val[0])
+ }
+
+keyword_variable: kNIL
+ {
+ result = @builder.nil(val[0])
+ }
+ | kSELF
+ {
+ result = @builder.self(val[0])
+ }
+ | kTRUE
+ {
+ result = @builder.true(val[0])
+ }
+ | kFALSE
+ {
+ result = @builder.false(val[0])
+ }
+ | k__FILE__
+ {
+ result = @builder.__FILE__(val[0])
+ }
+ | k__LINE__
+ {
+ result = @builder.__LINE__(val[0])
+ }
+ | k__ENCODING__
+ {
+ result = @builder.__ENCODING__(val[0])
+ }
+
+ var_ref: user_variable
+ {
+ result = @builder.accessible(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.accessible(val[0])
+ }
+
+ var_lhs: user_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ backref: tNTH_REF
+ {
+ result = @builder.nth_ref(val[0])
+ }
+ | tBACK_REF
+ {
+ result = @builder.back_ref(val[0])
+ }
+
+ superclass: term
+ {
+ result = nil
+ }
+ | tLT expr_value term
+ {
+ result = [ val[0], val[1] ]
+ }
+ | error term
+ {
+ yyerrok
+ result = nil
+ }
+
+ f_arglist: tLPAREN2 f_args rparen
+ {
+ result = @builder.args(val[0], val[1], val[2])
+
+ @lexer.state = :expr_value
+ }
+ | f_args term
+ {
+ result = @builder.args(nil, val[0], nil)
+ }
+
+ f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[6]).
+ concat(val[7])
+ }
+ | f_arg tCOMMA f_optarg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_optarg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_optarg tCOMMA f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_optarg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_optarg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_rest_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_rest_arg tCOMMA f_arg opt_f_block_arg
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_block_arg
+ {
+ result = [ val[0] ]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ f_bad_arg: tCONSTANT
+ {
+ diagnostic :error, :argument_const, nil, val[0]
+ }
+ | tIVAR
+ {
+ diagnostic :error, :argument_ivar, nil, val[0]
+ }
+ | tGVAR
+ {
+ diagnostic :error, :argument_gvar, nil, val[0]
+ }
+ | tCVAR
+ {
+ diagnostic :error, :argument_cvar, nil, val[0]
+ }
+
+ f_norm_arg: f_bad_arg
+ | tIDENTIFIER
+
+ f_arg_item: f_norm_arg
+ {
+ @static_env.declare val[0][0]
+
+ result = @builder.arg(val[0])
+ }
+ | tLPAREN f_margs rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ f_arg: f_arg_item
+ {
+ result = [ val[0] ]
+ }
+ | f_arg tCOMMA f_arg_item
+ {
+ result = val[0] << val[2]
+ }
+
+ f_opt: tIDENTIFIER tEQL arg_value
+ {
+ @static_env.declare val[0][0]
+
+ result = @builder.optarg(val[0], val[1], val[2])
+ }
+
+ f_block_opt: tIDENTIFIER tEQL primary_value
+ {
+ @static_env.declare val[0][0]
+
+ result = @builder.optarg(val[0], val[1], val[2])
+ }
+
+ f_block_optarg: f_block_opt
+ {
+ result = [ val[0] ]
+ }
+ | f_block_optarg tCOMMA f_block_opt
+ {
+ result = val[0] << val[2]
+ }
+
+ f_optarg: f_opt
+ {
+ result = [ val[0] ]
+ }
+ | f_optarg tCOMMA f_opt
+ {
+ result = val[0] << val[2]
+ }
+
+ restarg_mark: tSTAR2 | tSTAR
+
+ f_rest_arg: restarg_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = [ @builder.restarg(val[0], val[1]) ]
+ }
+ | restarg_mark
+ {
+ result = [ @builder.restarg(val[0]) ]
+ }
+
+ blkarg_mark: tAMPER2 | tAMPER
+
+ f_block_arg: blkarg_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = @builder.blockarg(val[0], val[1])
+ }
+
+ opt_f_block_arg: tCOMMA f_block_arg
+ {
+ result = [ val[1] ]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ singleton: var_ref
+ | tLPAREN2 expr rparen
+ {
+ result = val[1]
+ }
+
+ assoc_list: # nothing
+ {
+ result = []
+ }
+ | assocs trailer
+
+ assocs: assoc
+ {
+ result = [ val[0] ]
+ }
+ | assocs tCOMMA assoc
+ {
+ result = val[0] << val[2]
+ }
+
+ assoc: arg_value tASSOC arg_value
+ {
+ result = @builder.pair(val[0], val[1], val[2])
+ }
+ | tLABEL arg_value
+ {
+ result = @builder.pair_keyword(val[0], val[1])
+ }
+
+ operation: tIDENTIFIER | tCONSTANT | tFID
+ operation2: tIDENTIFIER | tCONSTANT | tFID | op
+ operation3: tIDENTIFIER | tFID | op
+ dot_or_colon: tDOT | tCOLON2
+ opt_terms: | terms
+ opt_nl: | tNL
+ rparen: opt_nl tRPAREN
+ {
+ result = val[1]
+ }
+ rbracket: opt_nl tRBRACK
+ {
+ result = val[1]
+ }
+ trailer: | tNL | tCOMMA
+
+ term: tSEMI
+ {
+ yyerrok
+ }
+ | tNL
+
+ terms: term
+ | terms tSEMI
+
+ none: # nothing
+ {
+ result = nil
+ }
+end
+
+---- header
+
+require 'parser'
+
+Parser.check_for_encoding_support
+
+---- inner
+
+ def version
+ 19
+ end
+
+ def default_encoding
+ Encoding::BINARY
+ end
diff --git a/test/racc/assets/ruby20.y b/test/racc/assets/ruby20.y
new file mode 100644
index 0000000000..6e07734778
--- /dev/null
+++ b/test/racc/assets/ruby20.y
@@ -0,0 +1,2350 @@
+# Copyright (c) 2013 Peter Zotov <whitequark@whitequark.org>
+#
+# Parts of the source are derived from ruby_parser:
+# Copyright (c) Ryan Davis, seattle.rb
+#
+# MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+class Parser::Ruby20
+
+token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
+ kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT
+ kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kDO_LAMBDA kRETURN kYIELD kSUPER
+ kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD
+ kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__
+ k__FILE__ k__ENCODING__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT
+ tLABEL tCVAR tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT
+ tREGEXP_END tUPLUS tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ
+ tGEQ tLEQ tANDOP tOROP tMATCH tNMATCH tDOT tDOT2 tDOT3 tAREF
+ tASET tLSHFT tRSHFT tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN
+ tLPAREN2 tRPAREN tLPAREN_ARG tLBRACK tLBRACK2 tRBRACK tLBRACE
+ tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2 tTILDE tPERCENT tDIVIDE
+ tDSTAR tPLUS tMINUS tLT tGT tPIPE tBANG tCARET tLCURLY tRCURLY
+ tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tREGEXP_OPT
+ tWORDS_BEG tQWORDS_BEG tSYMBOLS_BEG tQSYMBOLS_BEG tSTRING_DBEG
+ tSTRING_DVAR tSTRING_END tSTRING_DEND tSTRING tSYMBOL
+ tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG tCHARACTER
+
+prechigh
+ right tBANG tTILDE tUPLUS
+ right tPOW
+ right tUMINUS_NUM tUMINUS
+ left tSTAR2 tDIVIDE tPERCENT
+ left tPLUS tMINUS
+ left tLSHFT tRSHFT
+ left tAMPER2
+ left tPIPE tCARET
+ left tGT tGEQ tLT tLEQ
+ nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
+ left tANDOP
+ left tOROP
+ nonassoc tDOT2 tDOT3
+ right tEH tCOLON
+ left kRESCUE_MOD
+ right tEQL tOP_ASGN
+ nonassoc kDEFINED
+ right kNOT
+ left kOR kAND
+ nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
+ nonassoc tLBRACE_ARG
+ nonassoc tLOWEST
+preclow
+
+rule
+
+ program: top_compstmt
+
+ top_compstmt: top_stmts opt_terms
+ {
+ result = @builder.compstmt(val[0])
+ }
+
+ top_stmts: # nothing
+ {
+ result = []
+ }
+ | top_stmt
+ {
+ result = [ val[0] ]
+ }
+ | top_stmts terms top_stmt
+ {
+ result = val[0] << val[2]
+ }
+ | error top_stmt
+ {
+ result = [ val[1] ]
+ }
+
+ top_stmt: stmt
+ | klBEGIN tLCURLY top_compstmt tRCURLY
+ {
+ result = @builder.preexe(val[0], val[1], val[2], val[3])
+ }
+
+ bodystmt: compstmt opt_rescue opt_else opt_ensure
+ {
+ rescue_bodies = val[1]
+ else_t, else_ = val[2]
+ ensure_t, ensure_ = val[3]
+
+ if rescue_bodies.empty? && !else_.nil?
+ diagnostic :warning, :useless_else, nil, else_t
+ end
+
+ result = @builder.begin_body(val[0],
+ rescue_bodies,
+ else_t, else_,
+ ensure_t, ensure_)
+ }
+
+ compstmt: stmts opt_terms
+ {
+ result = @builder.compstmt(val[0])
+ }
+
+ stmts: # nothing
+ {
+ result = []
+ }
+ | stmt_or_begin
+ {
+ result = [ val[0] ]
+ }
+ | stmts terms stmt_or_begin
+ {
+ result = val[0] << val[2]
+ }
+ | error stmt
+ {
+ result = [ val[1] ]
+ }
+
+ stmt_or_begin: stmt
+ | klBEGIN tLCURLY top_compstmt tRCURLY
+ {
+ if in_def?
+ diagnostic :error, :begin_in_method, nil, val[0]
+ end
+
+ result = @builder.preexe(val[0], val[1], val[2], val[3])
+ }
+
+ stmt: kALIAS fitem
+ {
+ @lexer.state = :expr_fname
+ }
+ fitem
+ {
+ result = @builder.alias(val[0], val[1], val[3])
+ }
+ | kALIAS tGVAR tGVAR
+ {
+ result = @builder.alias(val[0],
+ @builder.gvar(val[1]),
+ @builder.gvar(val[2]))
+ }
+ | kALIAS tGVAR tBACK_REF
+ {
+ result = @builder.alias(val[0],
+ @builder.gvar(val[1]),
+ @builder.back_ref(val[2]))
+ }
+ | kALIAS tGVAR tNTH_REF
+ {
+ diagnostic :error, :nth_ref_alias, nil, val[2]
+ }
+ | kUNDEF undef_list
+ {
+ result = @builder.undef_method(val[0], val[1])
+ }
+ | stmt kIF_MOD expr_value
+ {
+ result = @builder.condition_mod(val[0], nil,
+ val[1], val[2])
+ }
+ | stmt kUNLESS_MOD expr_value
+ {
+ result = @builder.condition_mod(nil, val[0],
+ val[1], val[2])
+ }
+ | stmt kWHILE_MOD expr_value
+ {
+ result = @builder.loop_mod(:while, val[0], val[1], val[2])
+ }
+ | stmt kUNTIL_MOD expr_value
+ {
+ result = @builder.loop_mod(:until, val[0], val[1], val[2])
+ }
+ | stmt kRESCUE_MOD stmt
+ {
+ rescue_body = @builder.rescue_body(val[1],
+ nil, nil, nil,
+ nil, val[2])
+
+ result = @builder.begin_body(val[0], [ rescue_body ])
+ }
+ | klEND tLCURLY compstmt tRCURLY
+ {
+ result = @builder.postexe(val[0], val[1], val[2], val[3])
+ }
+ | command_asgn
+ | mlhs tEQL command_call
+ {
+ result = @builder.multi_assign(val[0], val[1], val[2])
+ }
+ | var_lhs tOP_ASGN command_call
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.index(
+ val[0], val[1], val[2], val[3]),
+ val[4], val[5])
+ }
+ | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tDOT tCONSTANT tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | backref tOP_ASGN command_call
+ {
+ @builder.op_assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL mrhs
+ {
+ result = @builder.assign(val[0], val[1],
+ @builder.array(nil, val[2], nil))
+ }
+ | mlhs tEQL arg_value
+ {
+ result = @builder.multi_assign(val[0], val[1], val[2])
+ }
+ | mlhs tEQL mrhs
+ {
+ result = @builder.multi_assign(val[0], val[1],
+ @builder.array(nil, val[2], nil))
+ }
+ | expr
+
+ command_asgn: lhs tEQL command_call
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL command_asgn
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+
+ expr: command_call
+ | expr kAND expr
+ {
+ result = @builder.logical_op(:and, val[0], val[1], val[2])
+ }
+ | expr kOR expr
+ {
+ result = @builder.logical_op(:or, val[0], val[1], val[2])
+ }
+ | kNOT opt_nl expr
+ {
+ result = @builder.not_op(val[0], nil, val[2], nil)
+ }
+ | tBANG command_call
+ {
+ result = @builder.not_op(val[0], nil, val[1], nil)
+ }
+ | arg
+
+ expr_value: expr
+
+ command_call: command
+ | block_command
+
+ block_command: block_call
+ | block_call dot_or_colon operation2 command_args
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+ }
+
+ cmd_brace_block: tLBRACE_ARG
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt tRCURLY
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ fcall: operation
+
+ command: fcall command_args =tLOWEST
+ {
+ result = @builder.call_method(nil, nil, val[0],
+ nil, val[1], nil)
+ }
+ | fcall command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(nil, nil, val[0],
+ nil, val[1], nil)
+
+ begin_t, args, body, end_t = val[2]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | primary_value tDOT operation2 command_args =tLOWEST
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+ }
+ | primary_value tDOT operation2 command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | primary_value tCOLON2 operation2 command_args =tLOWEST
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+ }
+ | primary_value tCOLON2 operation2 command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | kSUPER command_args
+ {
+ result = @builder.keyword_cmd(:super, val[0],
+ nil, val[1], nil)
+ }
+ | kYIELD command_args
+ {
+ result = @builder.keyword_cmd(:yield, val[0],
+ nil, val[1], nil)
+ }
+ | kRETURN call_args
+ {
+ result = @builder.keyword_cmd(:return, val[0],
+ nil, val[1], nil)
+ }
+ | kBREAK call_args
+ {
+ result = @builder.keyword_cmd(:break, val[0],
+ nil, val[1], nil)
+ }
+ | kNEXT call_args
+ {
+ result = @builder.keyword_cmd(:next, val[0],
+ nil, val[1], nil)
+ }
+
+ mlhs: mlhs_basic
+ {
+ result = @builder.multi_lhs(nil, val[0], nil)
+ }
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+
+ mlhs_inner: mlhs_basic
+ {
+ result = @builder.multi_lhs(nil, val[0], nil)
+ }
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ mlhs_basic: mlhs_head
+ | mlhs_head mlhs_item
+ {
+ result = val[0].
+ push(val[1])
+ }
+ | mlhs_head tSTAR mlhs_node
+ {
+ result = val[0].
+ push(@builder.splat(val[1], val[2]))
+ }
+ | mlhs_head tSTAR mlhs_node tCOMMA mlhs_post
+ {
+ result = val[0].
+ push(@builder.splat(val[1], val[2])).
+ concat(val[4])
+ }
+ | mlhs_head tSTAR
+ {
+ result = val[0].
+ push(@builder.splat(val[1]))
+ }
+ | mlhs_head tSTAR tCOMMA mlhs_post
+ {
+ result = val[0].
+ push(@builder.splat(val[1])).
+ concat(val[3])
+ }
+ | tSTAR mlhs_node
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+ | tSTAR mlhs_node tCOMMA mlhs_post
+ {
+ result = [ @builder.splat(val[0], val[1]),
+ *val[3] ]
+ }
+ | tSTAR
+ {
+ result = [ @builder.splat(val[0]) ]
+ }
+ | tSTAR tCOMMA mlhs_post
+ {
+ result = [ @builder.splat(val[0]),
+ *val[2] ]
+ }
+
+ mlhs_item: mlhs_node
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+
+ mlhs_head: mlhs_item tCOMMA
+ {
+ result = [ val[0] ]
+ }
+ | mlhs_head mlhs_item tCOMMA
+ {
+ result = val[0] << val[1]
+ }
+
+ mlhs_post: mlhs_item
+ {
+ result = [ val[0] ]
+ }
+ | mlhs_post tCOMMA mlhs_item
+ {
+ result = val[0] << val[2]
+ }
+
+ mlhs_node: user_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index_asgn(val[0], val[1], val[2], val[3])
+ }
+ | primary_value tDOT tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tDOT tCONSTANT
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_global(val[0], val[1]))
+ }
+ | backref
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ lhs: user_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index_asgn(val[0], val[1], val[2], val[3])
+ }
+ | primary_value tDOT tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tDOT tCONSTANT
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_global(val[0], val[1]))
+ }
+ | backref
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ cname: tIDENTIFIER
+ {
+ diagnostic :error, :module_name_const, nil, val[0]
+ }
+ | tCONSTANT
+
+ cpath: tCOLON3 cname
+ {
+ result = @builder.const_global(val[0], val[1])
+ }
+ | cname
+ {
+ result = @builder.const(val[0])
+ }
+ | primary_value tCOLON2 cname
+ {
+ result = @builder.const_fetch(val[0], val[1], val[2])
+ }
+
+ fname: tIDENTIFIER | tCONSTANT | tFID
+ | op
+ | reswords
+
+ fsym: fname
+ {
+ result = @builder.symbol(val[0])
+ }
+ | symbol
+
+ fitem: fsym
+ | dsym
+
+ undef_list: fitem
+ {
+ result = [ val[0] ]
+ }
+ | undef_list tCOMMA
+ {
+ @lexer.state = :expr_fname
+ }
+ fitem
+ {
+ result = val[0] << val[3]
+ }
+
+ op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ
+ | tMATCH | tNMATCH | tGT | tGEQ | tLT | tLEQ
+ | tNEQ | tLSHFT | tRSHFT | tPLUS | tMINUS | tSTAR2
+ | tSTAR | tDIVIDE | tPERCENT | tPOW | tBANG | tTILDE
+ | tUPLUS | tUMINUS | tAREF | tASET | tDSTAR | tBACK_REF2
+
+ reswords: k__LINE__ | k__FILE__ | k__ENCODING__ | klBEGIN | klEND
+ | kALIAS | kAND | kBEGIN | kBREAK | kCASE
+ | kCLASS | kDEF | kDEFINED | kDO | kELSE
+ | kELSIF | kEND | kENSURE | kFALSE | kFOR
+ | kIN | kMODULE | kNEXT | kNIL | kNOT
+ | kOR | kREDO | kRESCUE | kRETRY | kRETURN
+ | kSELF | kSUPER | kTHEN | kTRUE | kUNDEF
+ | kWHEN | kYIELD | kIF | kUNLESS | kWHILE
+ | kUNTIL
+
+ arg: lhs tEQL arg
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL arg kRESCUE_MOD arg
+ {
+ rescue_body = @builder.rescue_body(val[3],
+ nil, nil, nil,
+ nil, val[4])
+
+ rescue_ = @builder.begin_body(val[2], [ rescue_body ])
+
+ result = @builder.assign(val[0], val[1], rescue_)
+ }
+ | var_lhs tOP_ASGN arg
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | var_lhs tOP_ASGN arg kRESCUE_MOD arg
+ {
+ rescue_body = @builder.rescue_body(val[3],
+ nil, nil, nil,
+ nil, val[4])
+
+ rescue_ = @builder.begin_body(val[2], [ rescue_body ])
+
+ result = @builder.op_assign(val[0], val[1], rescue_)
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.index(
+ val[0], val[1], val[2], val[3]),
+ val[4], val[5])
+ }
+ | primary_value tDOT tIDENTIFIER tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tDOT tCONSTANT tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
+ {
+ const = @builder.const_op_assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ result = @builder.op_assign(const, val[3], val[4])
+ }
+ | tCOLON3 tCONSTANT tOP_ASGN arg
+ {
+ const = @builder.const_op_assignable(
+ @builder.const_global(val[0], val[1]))
+ result = @builder.op_assign(const, val[2], val[3])
+ }
+ | backref tOP_ASGN arg
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | arg tDOT2 arg
+ {
+ result = @builder.range_inclusive(val[0], val[1], val[2])
+ }
+ | arg tDOT3 arg
+ {
+ result = @builder.range_exclusive(val[0], val[1], val[2])
+ }
+ | arg tPLUS arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tMINUS arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tSTAR2 arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tDIVIDE arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tPERCENT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tPOW arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | tUMINUS_NUM tINTEGER tPOW arg
+ {
+ result = @builder.unary_op(val[0],
+ @builder.binary_op(
+ @builder.integer(val[1]),
+ val[2], val[3]))
+ }
+ | tUMINUS_NUM tFLOAT tPOW arg
+ {
+ result = @builder.unary_op(val[0],
+ @builder.binary_op(
+ @builder.float(val[1]),
+ val[2], val[3]))
+ }
+ | tUPLUS arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | tUMINUS arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | arg tPIPE arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tCARET arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tAMPER2 arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tCMP arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tGT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tGEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tLT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tLEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tEQQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tNEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tMATCH arg
+ {
+ result = @builder.match_op(val[0], val[1], val[2])
+ }
+ | arg tNMATCH arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | tBANG arg
+ {
+ result = @builder.not_op(val[0], nil, val[1], nil)
+ }
+ | tTILDE arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | arg tLSHFT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tRSHFT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tANDOP arg
+ {
+ result = @builder.logical_op(:and, val[0], val[1], val[2])
+ }
+ | arg tOROP arg
+ {
+ result = @builder.logical_op(:or, val[0], val[1], val[2])
+ }
+ | kDEFINED opt_nl arg
+ {
+ result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil)
+ }
+
+ | arg tEH arg opt_nl tCOLON arg
+ {
+ result = @builder.ternary(val[0], val[1],
+ val[2], val[4], val[5])
+ }
+ | primary
+
+ arg_value: arg
+
+ aref_args: none
+ | args trailer
+ | args tCOMMA assocs trailer
+ {
+ result = val[0] << @builder.associate(nil, val[2], nil)
+ }
+ | assocs trailer
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ }
+
+ paren_args: tLPAREN2 opt_call_args rparen
+ {
+ result = val
+ }
+
+ opt_paren_args: # nothing
+ {
+ result = [ nil, [], nil ]
+ }
+ | paren_args
+
+ opt_call_args: # nothing
+ {
+ result = []
+ }
+ | call_args
+ | args tCOMMA
+ | args tCOMMA assocs tCOMMA
+ {
+ result = val[0] << @builder.associate(nil, val[2], nil)
+ }
+ | assocs tCOMMA
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ }
+
+ call_args: command
+ {
+ result = [ val[0] ]
+ }
+ | args opt_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | assocs opt_block_arg
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ result.concat(val[1])
+ }
+ | args tCOMMA assocs opt_block_arg
+ {
+ assocs = @builder.associate(nil, val[2], nil)
+ result = val[0] << assocs
+ result.concat(val[3])
+ }
+ | block_arg
+ {
+ result = [ val[0] ]
+ }
+
+ command_args: {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.push(true)
+ }
+ call_args
+ {
+ @lexer.cmdarg = val[0]
+
+ result = val[1]
+ }
+
+ block_arg: tAMPER arg_value
+ {
+ result = @builder.block_pass(val[0], val[1])
+ }
+
+ opt_block_arg: tCOMMA block_arg
+ {
+ result = [ val[1] ]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ args: arg_value
+ {
+ result = [ val[0] ]
+ }
+ | tSTAR arg_value
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+ | args tCOMMA arg_value
+ {
+ result = val[0] << val[2]
+ }
+ | args tCOMMA tSTAR arg_value
+ {
+ result = val[0] << @builder.splat(val[2], val[3])
+ }
+
+ mrhs: args tCOMMA arg_value
+ {
+ result = val[0] << val[2]
+ }
+ | args tCOMMA tSTAR arg_value
+ {
+ result = val[0] << @builder.splat(val[2], val[3])
+ }
+ | tSTAR arg_value
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+
+ primary: literal
+ | strings
+ | xstring
+ | regexp
+ | words
+ | qwords
+ | symbols
+ | qsymbols
+ | var_ref
+ | backref
+ | tFID
+ {
+ result = @builder.call_method(nil, nil, val[0])
+ }
+ | kBEGIN
+ {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.clear
+ }
+ bodystmt kEND
+ {
+ @lexer.cmdarg = val[1]
+
+ result = @builder.begin_keyword(val[0], val[2], val[3])
+ }
+ | tLPAREN_ARG
+ {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.clear
+ }
+ expr
+ {
+ @lexer.state = :expr_endarg
+ }
+ opt_nl tRPAREN
+ {
+ @lexer.cmdarg = val[1]
+
+ result = @builder.begin(val[0], val[2], val[5])
+ }
+ | tLPAREN_ARG
+ {
+ @lexer.state = :expr_endarg
+ }
+ opt_nl tRPAREN
+ {
+ result = @builder.begin(val[0], nil, val[3])
+ }
+ | tLPAREN compstmt tRPAREN
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.const_fetch(val[0], val[1], val[2])
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.const_global(val[0], val[1])
+ }
+ | tLBRACK aref_args tRBRACK
+ {
+ result = @builder.array(val[0], val[1], val[2])
+ }
+ | tLBRACE assoc_list tRCURLY
+ {
+ result = @builder.associate(val[0], val[1], val[2])
+ }
+ | kRETURN
+ {
+ result = @builder.keyword_cmd(:return, val[0])
+ }
+ | kYIELD tLPAREN2 call_args rparen
+ {
+ result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3])
+ }
+ | kYIELD tLPAREN2 rparen
+ {
+ result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2])
+ }
+ | kYIELD
+ {
+ result = @builder.keyword_cmd(:yield, val[0])
+ }
+ | kDEFINED opt_nl tLPAREN2 expr rparen
+ {
+ result = @builder.keyword_cmd(:defined?, val[0],
+ val[2], [ val[3] ], val[4])
+ }
+ | kNOT tLPAREN2 expr rparen
+ {
+ result = @builder.not_op(val[0], val[1], val[2], val[3])
+ }
+ | kNOT tLPAREN2 rparen
+ {
+ result = @builder.not_op(val[0], val[1], nil, val[2])
+ }
+ | fcall brace_block
+ {
+ method_call = @builder.call_method(nil, nil, val[0])
+
+ begin_t, args, body, end_t = val[1]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | method_call
+ | method_call brace_block
+ {
+ begin_t, args, body, end_t = val[1]
+ result = @builder.block(val[0],
+ begin_t, args, body, end_t)
+ }
+ | tLAMBDA lambda
+ {
+ lambda_call = @builder.call_lambda(val[0])
+
+ args, (begin_t, body, end_t) = val[1]
+ result = @builder.block(lambda_call,
+ begin_t, args, body, end_t)
+ }
+ | kIF expr_value then compstmt if_tail kEND
+ {
+ else_t, else_ = val[4]
+ result = @builder.condition(val[0], val[1], val[2],
+ val[3], else_t,
+ else_, val[5])
+ }
+ | kUNLESS expr_value then compstmt opt_else kEND
+ {
+ else_t, else_ = val[4]
+ result = @builder.condition(val[0], val[1], val[2],
+ else_, else_t,
+ val[3], val[5])
+ }
+ | kWHILE
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.loop(:while, val[0], val[2], val[3],
+ val[5], val[6])
+ }
+ | kUNTIL
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.loop(:until, val[0], val[2], val[3],
+ val[5], val[6])
+ }
+ | kCASE expr_value opt_terms case_body kEND
+ {
+ *when_bodies, (else_t, else_body) = *val[3]
+
+ result = @builder.case(val[0], val[1],
+ when_bodies, else_t, else_body,
+ val[4])
+ }
+ | kCASE opt_terms case_body kEND
+ {
+ *when_bodies, (else_t, else_body) = *val[2]
+
+ result = @builder.case(val[0], nil,
+ when_bodies, else_t, else_body,
+ val[3])
+ }
+ | kFOR for_var kIN
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.for(val[0], val[1],
+ val[2], val[4],
+ val[5], val[7], val[8])
+ }
+ | kCLASS cpath superclass
+ {
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ if in_def?
+ diagnostic :error, :class_in_def, nil, val[0]
+ end
+
+ lt_t, superclass = val[2]
+ result = @builder.def_class(val[0], val[1],
+ lt_t, superclass,
+ val[4], val[5])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ }
+ | kCLASS tLSHFT expr term
+ {
+ result = @def_level
+ @def_level = 0
+
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ result = @builder.def_sclass(val[0], val[1], val[2],
+ val[5], val[6])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+
+ @def_level = val[4]
+ }
+ | kMODULE cpath
+ {
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ if in_def?
+ diagnostic :error, :module_in_def, nil, val[0]
+ end
+
+ result = @builder.def_module(val[0], val[1],
+ val[3], val[4])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ }
+ | kDEF fname
+ {
+ @def_level += 1
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ f_arglist bodystmt kEND
+ {
+ result = @builder.def_method(val[0], val[1],
+ val[3], val[4], val[5])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ @def_level -= 1
+ }
+ | kDEF singleton dot_or_colon
+ {
+ @lexer.state = :expr_fname
+ }
+ fname
+ {
+ @def_level += 1
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ f_arglist bodystmt kEND
+ {
+ result = @builder.def_singleton(val[0], val[1], val[2],
+ val[4], val[6], val[7], val[8])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ @def_level -= 1
+ }
+ | kBREAK
+ {
+ result = @builder.keyword_cmd(:break, val[0])
+ }
+ | kNEXT
+ {
+ result = @builder.keyword_cmd(:next, val[0])
+ }
+ | kREDO
+ {
+ result = @builder.keyword_cmd(:redo, val[0])
+ }
+ | kRETRY
+ {
+ result = @builder.keyword_cmd(:retry, val[0])
+ }
+
+ primary_value: primary
+
+ then: term
+ | kTHEN
+ | term kTHEN
+ {
+ result = val[1]
+ }
+
+ do: term
+ | kDO_COND
+
+ if_tail: opt_else
+ | kELSIF expr_value then compstmt if_tail
+ {
+ else_t, else_ = val[4]
+ result = [ val[0],
+ @builder.condition(val[0], val[1], val[2],
+ val[3], else_t,
+ else_, nil),
+ ]
+ }
+
+ opt_else: none
+ | kELSE compstmt
+ {
+ result = val
+ }
+
+ for_var: lhs
+ | mlhs
+
+ f_marg: f_norm_arg
+ {
+ @static_env.declare val[0][0]
+
+ result = @builder.arg(val[0])
+ }
+ | tLPAREN f_margs rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ f_marg_list: f_marg
+ {
+ result = [ val[0] ]
+ }
+ | f_marg_list tCOMMA f_marg
+ {
+ result = val[0] << val[2]
+ }
+
+ f_margs: f_marg_list
+ | f_marg_list tCOMMA tSTAR f_norm_arg
+ {
+ @static_env.declare val[3][0]
+
+ result = val[0].
+ push(@builder.restarg(val[2], val[3]))
+ }
+ | f_marg_list tCOMMA tSTAR f_norm_arg tCOMMA f_marg_list
+ {
+ @static_env.declare val[3][0]
+
+ result = val[0].
+ push(@builder.restarg(val[2], val[3])).
+ concat(val[5])
+ }
+ | f_marg_list tCOMMA tSTAR
+ {
+ result = val[0].
+ push(@builder.restarg(val[2]))
+ }
+ | f_marg_list tCOMMA tSTAR tCOMMA f_marg_list
+ {
+ result = val[0].
+ push(@builder.restarg(val[2])).
+ concat(val[4])
+ }
+ | tSTAR f_norm_arg
+ {
+ @static_env.declare val[1][0]
+
+ result = [ @builder.restarg(val[0], val[1]) ]
+ }
+ | tSTAR f_norm_arg tCOMMA f_marg_list
+ {
+ @static_env.declare val[1][0]
+
+ result = [ @builder.restarg(val[0], val[1]),
+ *val[3] ]
+ }
+ | tSTAR
+ {
+ result = [ @builder.restarg(val[0]) ]
+ }
+ | tSTAR tCOMMA f_marg_list
+ {
+ result = [ @builder.restarg(val[0]),
+ *val[2] ]
+ }
+
+ block_args_tail: f_block_kwarg tCOMMA f_kwrest opt_f_block_arg
+ {
+ result = val[0].concat(val[2]).concat(val[3])
+ }
+ | f_block_kwarg opt_f_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_kwrest opt_f_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_block_arg
+ {
+ result = [ val[0] ]
+ }
+
+opt_block_args_tail:
+ tCOMMA block_args_tail
+ {
+ result = val[1]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ block_param: f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[6]).
+ concat(val[7])
+ }
+ | f_arg tCOMMA f_block_optarg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_block_optarg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_rest_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA
+ | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg opt_block_args_tail
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_block_optarg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_block_optarg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_rest_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_rest_arg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | block_args_tail
+
+ opt_block_param: # nothing
+ {
+ result = @builder.args(nil, [], nil)
+ }
+ | block_param_def
+ {
+ @lexer.state = :expr_value
+ }
+
+ block_param_def: tPIPE opt_bv_decl tPIPE
+ {
+ result = @builder.args(val[0], val[1], val[2])
+ }
+ | tOROP
+ {
+ result = @builder.args(val[0], [], val[0])
+ }
+ | tPIPE block_param opt_bv_decl tPIPE
+ {
+ result = @builder.args(val[0], val[1].concat(val[2]), val[3])
+ }
+
+ opt_bv_decl: opt_nl
+ {
+ result = []
+ }
+ | opt_nl tSEMI bv_decls opt_nl
+ {
+ result = val[2]
+ }
+
+ bv_decls: bvar
+ {
+ result = [ val[0] ]
+ }
+ | bv_decls tCOMMA bvar
+ {
+ result = val[0] << val[2]
+ }
+
+ bvar: tIDENTIFIER
+ {
+ result = @builder.shadowarg(val[0])
+ }
+ | f_bad_arg
+
+ lambda: {
+ @static_env.extend_dynamic
+ }
+ f_larglist lambda_body
+ {
+ result = [ val[1], val[2] ]
+
+ @static_env.unextend
+ }
+
+ f_larglist: tLPAREN2 f_args opt_bv_decl tRPAREN
+ {
+ result = @builder.args(val[0], val[1].concat(val[2]), val[3])
+ }
+ | f_args
+ {
+ result = @builder.args(nil, val[0], nil)
+ }
+
+ lambda_body: tLAMBEG compstmt tRCURLY
+ {
+ result = [ val[0], val[1], val[2] ]
+ }
+ | kDO_LAMBDA compstmt kEND
+ {
+ result = [ val[0], val[1], val[2] ]
+ }
+
+ do_block: kDO_BLOCK
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt kEND
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ block_call: command do_block
+ {
+ begin_t, block_args, body, end_t = val[1]
+ result = @builder.block(val[0],
+ begin_t, block_args, body, end_t)
+ }
+ | block_call dot_or_colon operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | block_call dot_or_colon operation2 opt_paren_args brace_block
+ {
+ lparen_t, args, rparen_t = val[3]
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | block_call dot_or_colon operation2 command_args do_block
+ {
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+
+ method_call: fcall paren_args
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.call_method(nil, nil, val[0],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tDOT operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 operation2 paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 operation3
+ {
+ result = @builder.call_method(val[0], val[1], val[2])
+ }
+ | primary_value tDOT paren_args
+ {
+ lparen_t, args, rparen_t = val[2]
+ result = @builder.call_method(val[0], val[1], nil,
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 paren_args
+ {
+ lparen_t, args, rparen_t = val[2]
+ result = @builder.call_method(val[0], val[1], nil,
+ lparen_t, args, rparen_t)
+ }
+ | kSUPER paren_args
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.keyword_cmd(:super, val[0],
+ lparen_t, args, rparen_t)
+ }
+ | kSUPER
+ {
+ result = @builder.keyword_cmd(:zsuper, val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index(val[0], val[1], val[2], val[3])
+ }
+
+ brace_block: tLCURLY
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt tRCURLY
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+ | kDO
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt kEND
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ case_body: kWHEN args then compstmt cases
+ {
+ result = [ @builder.when(val[0], val[1], val[2], val[3]),
+ *val[4] ]
+ }
+
+ cases: opt_else
+ {
+ result = [ val[0] ]
+ }
+ | case_body
+
+ opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue
+ {
+ assoc_t, exc_var = val[2]
+
+ if val[1]
+ exc_list = @builder.array(nil, val[1], nil)
+ end
+
+ result = [ @builder.rescue_body(val[0],
+ exc_list, assoc_t, exc_var,
+ val[3], val[4]),
+ *val[5] ]
+ }
+ |
+ {
+ result = []
+ }
+
+ exc_list: arg_value
+ {
+ result = [ val[0] ]
+ }
+ | mrhs
+ | none
+
+ exc_var: tASSOC lhs
+ {
+ result = [ val[0], val[1] ]
+ }
+ | none
+
+ opt_ensure: kENSURE compstmt
+ {
+ result = [ val[0], val[1] ]
+ }
+ | none
+
+ literal: numeric
+ | symbol
+ | dsym
+
+ strings: string
+ {
+ result = @builder.string_compose(nil, val[0], nil)
+ }
+
+ string: string1
+ {
+ result = [ val[0] ]
+ }
+ | string string1
+ {
+ result = val[0] << val[1]
+ }
+
+ string1: tSTRING_BEG string_contents tSTRING_END
+ {
+ result = @builder.string_compose(val[0], val[1], val[2])
+ }
+ | tSTRING
+ {
+ result = @builder.string(val[0])
+ }
+ | tCHARACTER
+ {
+ result = @builder.character(val[0])
+ }
+
+ xstring: tXSTRING_BEG xstring_contents tSTRING_END
+ {
+ result = @builder.xstring_compose(val[0], val[1], val[2])
+ }
+
+ regexp: tREGEXP_BEG regexp_contents tSTRING_END tREGEXP_OPT
+ {
+ opts = @builder.regexp_options(val[3])
+ result = @builder.regexp_compose(val[0], val[1], val[2], opts)
+ }
+
+ words: tWORDS_BEG word_list tSTRING_END
+ {
+ result = @builder.words_compose(val[0], val[1], val[2])
+ }
+
+ word_list: # nothing
+ {
+ result = []
+ }
+ | word_list word tSPACE
+ {
+ result = val[0] << @builder.word(val[1])
+ }
+
+ word: string_content
+ {
+ result = [ val[0] ]
+ }
+ | word string_content
+ {
+ result = val[0] << val[1]
+ }
+
+ symbols: tSYMBOLS_BEG symbol_list tSTRING_END
+ {
+ result = @builder.symbols_compose(val[0], val[1], val[2])
+ }
+
+ symbol_list: # nothing
+ {
+ result = []
+ }
+ | symbol_list word tSPACE
+ {
+ result = val[0] << @builder.word(val[1])
+ }
+
+ qwords: tQWORDS_BEG qword_list tSTRING_END
+ {
+ result = @builder.words_compose(val[0], val[1], val[2])
+ }
+
+ qsymbols: tQSYMBOLS_BEG qsym_list tSTRING_END
+ {
+ result = @builder.symbols_compose(val[0], val[1], val[2])
+ }
+
+ qword_list: # nothing
+ {
+ result = []
+ }
+ | qword_list tSTRING_CONTENT tSPACE
+ {
+ result = val[0] << @builder.string_internal(val[1])
+ }
+
+ qsym_list: # nothing
+ {
+ result = []
+ }
+ | qsym_list tSTRING_CONTENT tSPACE
+ {
+ result = val[0] << @builder.symbol_internal(val[1])
+ }
+
+ string_contents: # nothing
+ {
+ result = []
+ }
+ | string_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+xstring_contents: # nothing
+ {
+ result = []
+ }
+ | xstring_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+regexp_contents: # nothing
+ {
+ result = []
+ }
+ | regexp_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+ string_content: tSTRING_CONTENT
+ {
+ result = @builder.string_internal(val[0])
+ }
+ | tSTRING_DVAR string_dvar
+ {
+ result = val[1]
+ }
+ | tSTRING_DBEG
+ {
+ @lexer.cond.push(false)
+ @lexer.cmdarg.push(false)
+ }
+ compstmt tSTRING_DEND
+ {
+ @lexer.cond.lexpop
+ @lexer.cmdarg.lexpop
+
+ result = @builder.begin(val[0], val[2], val[3])
+ }
+
+ string_dvar: tGVAR
+ {
+ result = @builder.gvar(val[0])
+ }
+ | tIVAR
+ {
+ result = @builder.ivar(val[0])
+ }
+ | tCVAR
+ {
+ result = @builder.cvar(val[0])
+ }
+ | backref
+
+
+ symbol: tSYMBOL
+ {
+ result = @builder.symbol(val[0])
+ }
+
+ dsym: tSYMBEG xstring_contents tSTRING_END
+ {
+ result = @builder.symbol_compose(val[0], val[1], val[2])
+ }
+
+ numeric: tINTEGER
+ {
+ result = @builder.integer(val[0])
+ }
+ | tFLOAT
+ {
+ result = @builder.float(val[0])
+ }
+ | tUMINUS_NUM tINTEGER =tLOWEST
+ {
+ result = @builder.negate(val[0],
+ @builder.integer(val[1]))
+ }
+ | tUMINUS_NUM tFLOAT =tLOWEST
+ {
+ result = @builder.negate(val[0],
+ @builder.float(val[1]))
+ }
+
+ user_variable: tIDENTIFIER
+ {
+ result = @builder.ident(val[0])
+ }
+ | tIVAR
+ {
+ result = @builder.ivar(val[0])
+ }
+ | tGVAR
+ {
+ result = @builder.gvar(val[0])
+ }
+ | tCONSTANT
+ {
+ result = @builder.const(val[0])
+ }
+ | tCVAR
+ {
+ result = @builder.cvar(val[0])
+ }
+
+keyword_variable: kNIL
+ {
+ result = @builder.nil(val[0])
+ }
+ | kSELF
+ {
+ result = @builder.self(val[0])
+ }
+ | kTRUE
+ {
+ result = @builder.true(val[0])
+ }
+ | kFALSE
+ {
+ result = @builder.false(val[0])
+ }
+ | k__FILE__
+ {
+ result = @builder.__FILE__(val[0])
+ }
+ | k__LINE__
+ {
+ result = @builder.__LINE__(val[0])
+ }
+ | k__ENCODING__
+ {
+ result = @builder.__ENCODING__(val[0])
+ }
+
+ var_ref: user_variable
+ {
+ result = @builder.accessible(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.accessible(val[0])
+ }
+
+ var_lhs: user_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ backref: tNTH_REF
+ {
+ result = @builder.nth_ref(val[0])
+ }
+ | tBACK_REF
+ {
+ result = @builder.back_ref(val[0])
+ }
+
+ superclass: term
+ {
+ result = nil
+ }
+ | tLT
+ {
+ @lexer.state = :expr_value
+ }
+ expr_value term
+ {
+ result = [ val[0], val[2] ]
+ }
+ | error term
+ {
+ yyerrok
+ result = nil
+ }
+
+ f_arglist: tLPAREN2 f_args rparen
+ {
+ result = @builder.args(val[0], val[1], val[2])
+
+ @lexer.state = :expr_value
+ }
+ | f_args term
+ {
+ result = @builder.args(nil, val[0], nil)
+ }
+
+ args_tail: f_kwarg tCOMMA f_kwrest opt_f_block_arg
+ {
+ result = val[0].concat(val[2]).concat(val[3])
+ }
+ | f_kwarg opt_f_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_kwrest opt_f_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_block_arg
+ {
+ result = [ val[0] ]
+ }
+
+ opt_args_tail: tCOMMA args_tail
+ {
+ result = val[1]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[6]).
+ concat(val[7])
+ }
+ | f_arg tCOMMA f_optarg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_optarg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_rest_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_optarg tCOMMA f_rest_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_optarg opt_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_optarg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_rest_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_rest_arg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | args_tail
+ {
+ result = val[0]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ f_bad_arg: tCONSTANT
+ {
+ diagnostic :error, :argument_const, nil, val[0]
+ }
+ | tIVAR
+ {
+ diagnostic :error, :argument_ivar, nil, val[0]
+ }
+ | tGVAR
+ {
+ diagnostic :error, :argument_gvar, nil, val[0]
+ }
+ | tCVAR
+ {
+ diagnostic :error, :argument_cvar, nil, val[0]
+ }
+
+ f_norm_arg: f_bad_arg
+ | tIDENTIFIER
+
+ f_arg_item: f_norm_arg
+ {
+ @static_env.declare val[0][0]
+
+ result = @builder.arg(val[0])
+ }
+ | tLPAREN f_margs rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ f_arg: f_arg_item
+ {
+ result = [ val[0] ]
+ }
+ | f_arg tCOMMA f_arg_item
+ {
+ result = val[0] << val[2]
+ }
+
+ f_kw: tLABEL arg_value
+ {
+ check_kwarg_name(val[0])
+
+ @static_env.declare val[0][0]
+
+ result = @builder.kwoptarg(val[0], val[1])
+ }
+
+ f_block_kw: tLABEL primary_value
+ {
+ check_kwarg_name(val[0])
+
+ @static_env.declare val[0][0]
+
+ result = @builder.kwoptarg(val[0], val[1])
+ }
+
+ f_block_kwarg: f_block_kw
+ {
+ result = [ val[0] ]
+ }
+ | f_block_kwarg tCOMMA f_block_kw
+ {
+ result = val[0] << val[2]
+ }
+
+ f_kwarg: f_kw
+ {
+ result = [ val[0] ]
+ }
+ | f_kwarg tCOMMA f_kw
+ {
+ result = val[0] << val[2]
+ }
+
+ kwrest_mark: tPOW | tDSTAR
+
+ f_kwrest: kwrest_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = [ @builder.kwrestarg(val[0], val[1]) ]
+ }
+ | kwrest_mark
+ {
+ result = [ @builder.kwrestarg(val[0]) ]
+ }
+
+ f_opt: tIDENTIFIER tEQL arg_value
+ {
+ @static_env.declare val[0][0]
+
+ result = @builder.optarg(val[0], val[1], val[2])
+ }
+
+ f_block_opt: tIDENTIFIER tEQL primary_value
+ {
+ @static_env.declare val[0][0]
+
+ result = @builder.optarg(val[0], val[1], val[2])
+ }
+
+ f_block_optarg: f_block_opt
+ {
+ result = [ val[0] ]
+ }
+ | f_block_optarg tCOMMA f_block_opt
+ {
+ result = val[0] << val[2]
+ }
+
+ f_optarg: f_opt
+ {
+ result = [ val[0] ]
+ }
+ | f_optarg tCOMMA f_opt
+ {
+ result = val[0] << val[2]
+ }
+
+ restarg_mark: tSTAR2 | tSTAR
+
+ f_rest_arg: restarg_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = [ @builder.restarg(val[0], val[1]) ]
+ }
+ | restarg_mark
+ {
+ result = [ @builder.restarg(val[0]) ]
+ }
+
+ blkarg_mark: tAMPER2 | tAMPER
+
+ f_block_arg: blkarg_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = @builder.blockarg(val[0], val[1])
+ }
+
+ opt_f_block_arg: tCOMMA f_block_arg
+ {
+ result = [ val[1] ]
+ }
+ |
+ {
+ result = []
+ }
+
+ singleton: var_ref
+ | tLPAREN2 expr rparen
+ {
+ result = val[1]
+ }
+
+ assoc_list: # nothing
+ {
+ result = []
+ }
+ | assocs trailer
+
+ assocs: assoc
+ {
+ result = [ val[0] ]
+ }
+ | assocs tCOMMA assoc
+ {
+ result = val[0] << val[2]
+ }
+
+ assoc: arg_value tASSOC arg_value
+ {
+ result = @builder.pair(val[0], val[1], val[2])
+ }
+ | tLABEL arg_value
+ {
+ result = @builder.pair_keyword(val[0], val[1])
+ }
+ | tDSTAR arg_value
+ {
+ result = @builder.kwsplat(val[0], val[1])
+ }
+
+ operation: tIDENTIFIER | tCONSTANT | tFID
+ operation2: tIDENTIFIER | tCONSTANT | tFID | op
+ operation3: tIDENTIFIER | tFID | op
+ dot_or_colon: tDOT | tCOLON2
+ opt_terms: | terms
+ opt_nl: | tNL
+ rparen: opt_nl tRPAREN
+ {
+ result = val[1]
+ }
+ rbracket: opt_nl tRBRACK
+ {
+ result = val[1]
+ }
+ trailer: | tNL | tCOMMA
+
+ term: tSEMI
+ {
+ yyerrok
+ }
+ | tNL
+
+ terms: term
+ | terms tSEMI
+
+ none: # nothing
+ {
+ result = nil
+ }
+end
+
+---- header
+
+require 'parser'
+
+Parser.check_for_encoding_support
+
+---- inner
+
+ def version
+ 20
+ end
+
+ def default_encoding
+ Encoding::UTF_8
+ end
diff --git a/test/racc/assets/ruby21.y b/test/racc/assets/ruby21.y
new file mode 100644
index 0000000000..2ac94afb0c
--- /dev/null
+++ b/test/racc/assets/ruby21.y
@@ -0,0 +1,2359 @@
+# Copyright (c) 2013 Peter Zotov <whitequark@whitequark.org>
+#
+# Parts of the source are derived from ruby_parser:
+# Copyright (c) Ryan Davis, seattle.rb
+#
+# MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+class Parser::Ruby21
+
+token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
+ kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT
+ kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kDO_LAMBDA kRETURN kYIELD kSUPER
+ kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD
+ kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__
+ k__FILE__ k__ENCODING__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT
+ tLABEL tCVAR tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT
+ tREGEXP_END tUPLUS tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ
+ tGEQ tLEQ tANDOP tOROP tMATCH tNMATCH tDOT tDOT2 tDOT3 tAREF
+ tASET tLSHFT tRSHFT tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN
+ tLPAREN2 tRPAREN tLPAREN_ARG tLBRACK tLBRACK2 tRBRACK tLBRACE
+ tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2 tTILDE tPERCENT tDIVIDE
+ tDSTAR tPLUS tMINUS tLT tGT tPIPE tBANG tCARET tLCURLY tRCURLY
+ tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tREGEXP_OPT
+ tWORDS_BEG tQWORDS_BEG tSYMBOLS_BEG tQSYMBOLS_BEG tSTRING_DBEG
+ tSTRING_DVAR tSTRING_END tSTRING_DEND tSTRING tSYMBOL
+ tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG tCHARACTER
+ tRATIONAL tIMAGINARY
+
+prechigh
+ right tBANG tTILDE tUPLUS
+ right tPOW
+ right tUMINUS_NUM tUMINUS
+ left tSTAR2 tDIVIDE tPERCENT
+ left tPLUS tMINUS
+ left tLSHFT tRSHFT
+ left tAMPER2
+ left tPIPE tCARET
+ left tGT tGEQ tLT tLEQ
+ nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
+ left tANDOP
+ left tOROP
+ nonassoc tDOT2 tDOT3
+ right tEH tCOLON
+ left kRESCUE_MOD
+ right tEQL tOP_ASGN
+ nonassoc kDEFINED
+ right kNOT
+ left kOR kAND
+ nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
+ nonassoc tLBRACE_ARG
+ nonassoc tLOWEST
+preclow
+
+rule
+
+ program: top_compstmt
+
+ top_compstmt: top_stmts opt_terms
+ {
+ result = @builder.compstmt(val[0])
+ }
+
+ top_stmts: # nothing
+ {
+ result = []
+ }
+ | top_stmt
+ {
+ result = [ val[0] ]
+ }
+ | top_stmts terms top_stmt
+ {
+ result = val[0] << val[2]
+ }
+ | error top_stmt
+ {
+ result = [ val[1] ]
+ }
+
+ top_stmt: stmt
+ | klBEGIN tLCURLY top_compstmt tRCURLY
+ {
+ result = @builder.preexe(val[0], val[1], val[2], val[3])
+ }
+
+ bodystmt: compstmt opt_rescue opt_else opt_ensure
+ {
+ rescue_bodies = val[1]
+ else_t, else_ = val[2]
+ ensure_t, ensure_ = val[3]
+
+ if rescue_bodies.empty? && !else_.nil?
+ diagnostic :warning, :useless_else, nil, else_t
+ end
+
+ result = @builder.begin_body(val[0],
+ rescue_bodies,
+ else_t, else_,
+ ensure_t, ensure_)
+ }
+
+ compstmt: stmts opt_terms
+ {
+ result = @builder.compstmt(val[0])
+ }
+
+ stmts: # nothing
+ {
+ result = []
+ }
+ | stmt_or_begin
+ {
+ result = [ val[0] ]
+ }
+ | stmts terms stmt_or_begin
+ {
+ result = val[0] << val[2]
+ }
+ | error stmt
+ {
+ result = [ val[1] ]
+ }
+
+ stmt_or_begin: stmt
+ | klBEGIN tLCURLY top_compstmt tRCURLY
+ {
+ diagnostic :error, :begin_in_method, nil, val[0]
+ }
+
+ stmt: kALIAS fitem
+ {
+ @lexer.state = :expr_fname
+ }
+ fitem
+ {
+ result = @builder.alias(val[0], val[1], val[3])
+ }
+ | kALIAS tGVAR tGVAR
+ {
+ result = @builder.alias(val[0],
+ @builder.gvar(val[1]),
+ @builder.gvar(val[2]))
+ }
+ | kALIAS tGVAR tBACK_REF
+ {
+ result = @builder.alias(val[0],
+ @builder.gvar(val[1]),
+ @builder.back_ref(val[2]))
+ }
+ | kALIAS tGVAR tNTH_REF
+ {
+ diagnostic :error, :nth_ref_alias, nil, val[2]
+ }
+ | kUNDEF undef_list
+ {
+ result = @builder.undef_method(val[0], val[1])
+ }
+ | stmt kIF_MOD expr_value
+ {
+ result = @builder.condition_mod(val[0], nil,
+ val[1], val[2])
+ }
+ | stmt kUNLESS_MOD expr_value
+ {
+ result = @builder.condition_mod(nil, val[0],
+ val[1], val[2])
+ }
+ | stmt kWHILE_MOD expr_value
+ {
+ result = @builder.loop_mod(:while, val[0], val[1], val[2])
+ }
+ | stmt kUNTIL_MOD expr_value
+ {
+ result = @builder.loop_mod(:until, val[0], val[1], val[2])
+ }
+ | stmt kRESCUE_MOD stmt
+ {
+ rescue_body = @builder.rescue_body(val[1],
+ nil, nil, nil,
+ nil, val[2])
+
+ result = @builder.begin_body(val[0], [ rescue_body ])
+ }
+ | klEND tLCURLY compstmt tRCURLY
+ {
+ result = @builder.postexe(val[0], val[1], val[2], val[3])
+ }
+ | command_asgn
+ | mlhs tEQL command_call
+ {
+ result = @builder.multi_assign(val[0], val[1], val[2])
+ }
+ | var_lhs tOP_ASGN command_call
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.index(
+ val[0], val[1], val[2], val[3]),
+ val[4], val[5])
+ }
+ | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tDOT tCONSTANT tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | backref tOP_ASGN command_call
+ {
+ @builder.op_assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL mrhs
+ {
+ result = @builder.assign(val[0], val[1],
+ @builder.array(nil, val[2], nil))
+ }
+ | mlhs tEQL mrhs_arg
+ {
+ result = @builder.multi_assign(val[0], val[1], val[2])
+ }
+ | expr
+
+ command_asgn: lhs tEQL command_call
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL command_asgn
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+
+ expr: command_call
+ | expr kAND expr
+ {
+ result = @builder.logical_op(:and, val[0], val[1], val[2])
+ }
+ | expr kOR expr
+ {
+ result = @builder.logical_op(:or, val[0], val[1], val[2])
+ }
+ | kNOT opt_nl expr
+ {
+ result = @builder.not_op(val[0], nil, val[2], nil)
+ }
+ | tBANG command_call
+ {
+ result = @builder.not_op(val[0], nil, val[1], nil)
+ }
+ | arg
+
+ expr_value: expr
+
+ command_call: command
+ | block_command
+
+ block_command: block_call
+ | block_call dot_or_colon operation2 command_args
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+ }
+
+ cmd_brace_block: tLBRACE_ARG
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt tRCURLY
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ fcall: operation
+
+ command: fcall command_args =tLOWEST
+ {
+ result = @builder.call_method(nil, nil, val[0],
+ nil, val[1], nil)
+ }
+ | fcall command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(nil, nil, val[0],
+ nil, val[1], nil)
+
+ begin_t, args, body, end_t = val[2]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | primary_value tDOT operation2 command_args =tLOWEST
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+ }
+ | primary_value tDOT operation2 command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | primary_value tCOLON2 operation2 command_args =tLOWEST
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+ }
+ | primary_value tCOLON2 operation2 command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | kSUPER command_args
+ {
+ result = @builder.keyword_cmd(:super, val[0],
+ nil, val[1], nil)
+ }
+ | kYIELD command_args
+ {
+ result = @builder.keyword_cmd(:yield, val[0],
+ nil, val[1], nil)
+ }
+ | kRETURN call_args
+ {
+ result = @builder.keyword_cmd(:return, val[0],
+ nil, val[1], nil)
+ }
+ | kBREAK call_args
+ {
+ result = @builder.keyword_cmd(:break, val[0],
+ nil, val[1], nil)
+ }
+ | kNEXT call_args
+ {
+ result = @builder.keyword_cmd(:next, val[0],
+ nil, val[1], nil)
+ }
+
+ mlhs: mlhs_basic
+ {
+ result = @builder.multi_lhs(nil, val[0], nil)
+ }
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+
+ mlhs_inner: mlhs_basic
+ {
+ result = @builder.multi_lhs(nil, val[0], nil)
+ }
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ mlhs_basic: mlhs_head
+ | mlhs_head mlhs_item
+ {
+ result = val[0].
+ push(val[1])
+ }
+ | mlhs_head tSTAR mlhs_node
+ {
+ result = val[0].
+ push(@builder.splat(val[1], val[2]))
+ }
+ | mlhs_head tSTAR mlhs_node tCOMMA mlhs_post
+ {
+ result = val[0].
+ push(@builder.splat(val[1], val[2])).
+ concat(val[4])
+ }
+ | mlhs_head tSTAR
+ {
+ result = val[0].
+ push(@builder.splat(val[1]))
+ }
+ | mlhs_head tSTAR tCOMMA mlhs_post
+ {
+ result = val[0].
+ push(@builder.splat(val[1])).
+ concat(val[3])
+ }
+ | tSTAR mlhs_node
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+ | tSTAR mlhs_node tCOMMA mlhs_post
+ {
+ result = [ @builder.splat(val[0], val[1]),
+ *val[3] ]
+ }
+ | tSTAR
+ {
+ result = [ @builder.splat(val[0]) ]
+ }
+ | tSTAR tCOMMA mlhs_post
+ {
+ result = [ @builder.splat(val[0]),
+ *val[2] ]
+ }
+
+ mlhs_item: mlhs_node
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+
+ mlhs_head: mlhs_item tCOMMA
+ {
+ result = [ val[0] ]
+ }
+ | mlhs_head mlhs_item tCOMMA
+ {
+ result = val[0] << val[1]
+ }
+
+ mlhs_post: mlhs_item
+ {
+ result = [ val[0] ]
+ }
+ | mlhs_post tCOMMA mlhs_item
+ {
+ result = val[0] << val[2]
+ }
+
+ mlhs_node: user_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index_asgn(val[0], val[1], val[2], val[3])
+ }
+ | primary_value tDOT tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tDOT tCONSTANT
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_global(val[0], val[1]))
+ }
+ | backref
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ lhs: user_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index_asgn(val[0], val[1], val[2], val[3])
+ }
+ | primary_value tDOT tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tDOT tCONSTANT
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_global(val[0], val[1]))
+ }
+ | backref
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ cname: tIDENTIFIER
+ {
+ diagnostic :error, :module_name_const, nil, val[0]
+ }
+ | tCONSTANT
+
+ cpath: tCOLON3 cname
+ {
+ result = @builder.const_global(val[0], val[1])
+ }
+ | cname
+ {
+ result = @builder.const(val[0])
+ }
+ | primary_value tCOLON2 cname
+ {
+ result = @builder.const_fetch(val[0], val[1], val[2])
+ }
+
+ fname: tIDENTIFIER | tCONSTANT | tFID
+ | op
+ | reswords
+
+ fsym: fname
+ {
+ result = @builder.symbol(val[0])
+ }
+ | symbol
+
+ fitem: fsym
+ | dsym
+
+ undef_list: fitem
+ {
+ result = [ val[0] ]
+ }
+ | undef_list tCOMMA
+ {
+ @lexer.state = :expr_fname
+ }
+ fitem
+ {
+ result = val[0] << val[3]
+ }
+
+ op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ
+ | tMATCH | tNMATCH | tGT | tGEQ | tLT | tLEQ
+ | tNEQ | tLSHFT | tRSHFT | tPLUS | tMINUS | tSTAR2
+ | tSTAR | tDIVIDE | tPERCENT | tPOW | tBANG | tTILDE
+ | tUPLUS | tUMINUS | tAREF | tASET | tDSTAR | tBACK_REF2
+
+ reswords: k__LINE__ | k__FILE__ | k__ENCODING__ | klBEGIN | klEND
+ | kALIAS | kAND | kBEGIN | kBREAK | kCASE
+ | kCLASS | kDEF | kDEFINED | kDO | kELSE
+ | kELSIF | kEND | kENSURE | kFALSE | kFOR
+ | kIN | kMODULE | kNEXT | kNIL | kNOT
+ | kOR | kREDO | kRESCUE | kRETRY | kRETURN
+ | kSELF | kSUPER | kTHEN | kTRUE | kUNDEF
+ | kWHEN | kYIELD | kIF | kUNLESS | kWHILE
+ | kUNTIL
+
+ arg: lhs tEQL arg
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL arg kRESCUE_MOD arg
+ {
+ rescue_body = @builder.rescue_body(val[3],
+ nil, nil, nil,
+ nil, val[4])
+
+ rescue_ = @builder.begin_body(val[2], [ rescue_body ])
+
+ result = @builder.assign(val[0], val[1], rescue_)
+ }
+ | var_lhs tOP_ASGN arg
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | var_lhs tOP_ASGN arg kRESCUE_MOD arg
+ {
+ rescue_body = @builder.rescue_body(val[3],
+ nil, nil, nil,
+ nil, val[4])
+
+ rescue_ = @builder.begin_body(val[2], [ rescue_body ])
+
+ result = @builder.op_assign(val[0], val[1], rescue_)
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.index(
+ val[0], val[1], val[2], val[3]),
+ val[4], val[5])
+ }
+ | primary_value tDOT tIDENTIFIER tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tDOT tCONSTANT tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
+ {
+ const = @builder.const_op_assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ result = @builder.op_assign(const, val[3], val[4])
+ }
+ | tCOLON3 tCONSTANT tOP_ASGN arg
+ {
+ const = @builder.const_op_assignable(
+ @builder.const_global(val[0], val[1]))
+ result = @builder.op_assign(const, val[2], val[3])
+ }
+ | backref tOP_ASGN arg
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | arg tDOT2 arg
+ {
+ result = @builder.range_inclusive(val[0], val[1], val[2])
+ }
+ | arg tDOT3 arg
+ {
+ result = @builder.range_exclusive(val[0], val[1], val[2])
+ }
+ | arg tPLUS arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tMINUS arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tSTAR2 arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tDIVIDE arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tPERCENT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tPOW arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | tUMINUS_NUM simple_numeric tPOW arg
+ {
+ result = @builder.unary_op(val[0],
+ @builder.binary_op(
+ val[1], val[2], val[3]))
+ }
+ | tUPLUS arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | tUMINUS arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | arg tPIPE arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tCARET arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tAMPER2 arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tCMP arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tGT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tGEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tLT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tLEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tEQQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tNEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tMATCH arg
+ {
+ result = @builder.match_op(val[0], val[1], val[2])
+ }
+ | arg tNMATCH arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | tBANG arg
+ {
+ result = @builder.not_op(val[0], nil, val[1], nil)
+ }
+ | tTILDE arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | arg tLSHFT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tRSHFT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tANDOP arg
+ {
+ result = @builder.logical_op(:and, val[0], val[1], val[2])
+ }
+ | arg tOROP arg
+ {
+ result = @builder.logical_op(:or, val[0], val[1], val[2])
+ }
+ | kDEFINED opt_nl arg
+ {
+ result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil)
+ }
+
+ | arg tEH arg opt_nl tCOLON arg
+ {
+ result = @builder.ternary(val[0], val[1],
+ val[2], val[4], val[5])
+ }
+ | primary
+
+ arg_value: arg
+
+ aref_args: none
+ | args trailer
+ | args tCOMMA assocs trailer
+ {
+ result = val[0] << @builder.associate(nil, val[2], nil)
+ }
+ | assocs trailer
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ }
+
+ paren_args: tLPAREN2 opt_call_args rparen
+ {
+ result = val
+ }
+
+ opt_paren_args: # nothing
+ {
+ result = [ nil, [], nil ]
+ }
+ | paren_args
+
+ opt_call_args: # nothing
+ {
+ result = []
+ }
+ | call_args
+ | args tCOMMA
+ | args tCOMMA assocs tCOMMA
+ {
+ result = val[0] << @builder.associate(nil, val[2], nil)
+ }
+ | assocs tCOMMA
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ }
+
+ call_args: command
+ {
+ result = [ val[0] ]
+ }
+ | args opt_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | assocs opt_block_arg
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ result.concat(val[1])
+ }
+ | args tCOMMA assocs opt_block_arg
+ {
+ assocs = @builder.associate(nil, val[2], nil)
+ result = val[0] << assocs
+ result.concat(val[3])
+ }
+ | block_arg
+ {
+ result = [ val[0] ]
+ }
+
+ command_args: {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.push(true)
+ }
+ call_args
+ {
+ @lexer.cmdarg = val[0]
+
+ result = val[1]
+ }
+
+ block_arg: tAMPER arg_value
+ {
+ result = @builder.block_pass(val[0], val[1])
+ }
+
+ opt_block_arg: tCOMMA block_arg
+ {
+ result = [ val[1] ]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ args: arg_value
+ {
+ result = [ val[0] ]
+ }
+ | tSTAR arg_value
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+ | args tCOMMA arg_value
+ {
+ result = val[0] << val[2]
+ }
+ | args tCOMMA tSTAR arg_value
+ {
+ result = val[0] << @builder.splat(val[2], val[3])
+ }
+
+ mrhs_arg: mrhs
+ {
+ result = @builder.array(nil, val[0], nil)
+ }
+ | arg_value
+
+ mrhs: args tCOMMA arg_value
+ {
+ result = val[0] << val[2]
+ }
+ | args tCOMMA tSTAR arg_value
+ {
+ result = val[0] << @builder.splat(val[2], val[3])
+ }
+ | tSTAR arg_value
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+
+ primary: literal
+ | strings
+ | xstring
+ | regexp
+ | words
+ | qwords
+ | symbols
+ | qsymbols
+ | var_ref
+ | backref
+ | tFID
+ {
+ result = @builder.call_method(nil, nil, val[0])
+ }
+ | kBEGIN
+ {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.clear
+ }
+ bodystmt kEND
+ {
+ @lexer.cmdarg = val[1]
+
+ result = @builder.begin_keyword(val[0], val[2], val[3])
+ }
+ | tLPAREN_ARG
+ {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.clear
+ }
+ expr
+ {
+ @lexer.state = :expr_endarg
+ }
+ opt_nl tRPAREN
+ {
+ @lexer.cmdarg = val[1]
+
+ result = @builder.begin(val[0], val[2], val[5])
+ }
+ | tLPAREN_ARG
+ {
+ @lexer.state = :expr_endarg
+ }
+ opt_nl tRPAREN
+ {
+ result = @builder.begin(val[0], nil, val[3])
+ }
+ | tLPAREN compstmt tRPAREN
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.const_fetch(val[0], val[1], val[2])
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.const_global(val[0], val[1])
+ }
+ | tLBRACK aref_args tRBRACK
+ {
+ result = @builder.array(val[0], val[1], val[2])
+ }
+ | tLBRACE assoc_list tRCURLY
+ {
+ result = @builder.associate(val[0], val[1], val[2])
+ }
+ | kRETURN
+ {
+ result = @builder.keyword_cmd(:return, val[0])
+ }
+ | kYIELD tLPAREN2 call_args rparen
+ {
+ result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3])
+ }
+ | kYIELD tLPAREN2 rparen
+ {
+ result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2])
+ }
+ | kYIELD
+ {
+ result = @builder.keyword_cmd(:yield, val[0])
+ }
+ | kDEFINED opt_nl tLPAREN2 expr rparen
+ {
+ result = @builder.keyword_cmd(:defined?, val[0],
+ val[2], [ val[3] ], val[4])
+ }
+ | kNOT tLPAREN2 expr rparen
+ {
+ result = @builder.not_op(val[0], val[1], val[2], val[3])
+ }
+ | kNOT tLPAREN2 rparen
+ {
+ result = @builder.not_op(val[0], val[1], nil, val[2])
+ }
+ | fcall brace_block
+ {
+ method_call = @builder.call_method(nil, nil, val[0])
+
+ begin_t, args, body, end_t = val[1]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | method_call
+ | method_call brace_block
+ {
+ begin_t, args, body, end_t = val[1]
+ result = @builder.block(val[0],
+ begin_t, args, body, end_t)
+ }
+ | tLAMBDA lambda
+ {
+ lambda_call = @builder.call_lambda(val[0])
+
+ args, (begin_t, body, end_t) = val[1]
+ result = @builder.block(lambda_call,
+ begin_t, args, body, end_t)
+ }
+ | kIF expr_value then compstmt if_tail kEND
+ {
+ else_t, else_ = val[4]
+ result = @builder.condition(val[0], val[1], val[2],
+ val[3], else_t,
+ else_, val[5])
+ }
+ | kUNLESS expr_value then compstmt opt_else kEND
+ {
+ else_t, else_ = val[4]
+ result = @builder.condition(val[0], val[1], val[2],
+ else_, else_t,
+ val[3], val[5])
+ }
+ | kWHILE
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.loop(:while, val[0], val[2], val[3],
+ val[5], val[6])
+ }
+ | kUNTIL
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.loop(:until, val[0], val[2], val[3],
+ val[5], val[6])
+ }
+ | kCASE expr_value opt_terms case_body kEND
+ {
+ *when_bodies, (else_t, else_body) = *val[3]
+
+ result = @builder.case(val[0], val[1],
+ when_bodies, else_t, else_body,
+ val[4])
+ }
+ | kCASE opt_terms case_body kEND
+ {
+ *when_bodies, (else_t, else_body) = *val[2]
+
+ result = @builder.case(val[0], nil,
+ when_bodies, else_t, else_body,
+ val[3])
+ }
+ | kFOR for_var kIN
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.for(val[0], val[1],
+ val[2], val[4],
+ val[5], val[7], val[8])
+ }
+ | kCLASS cpath superclass
+ {
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ if in_def?
+ diagnostic :error, :class_in_def, nil, val[0]
+ end
+
+ lt_t, superclass = val[2]
+ result = @builder.def_class(val[0], val[1],
+ lt_t, superclass,
+ val[4], val[5])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ }
+ | kCLASS tLSHFT expr term
+ {
+ result = @def_level
+ @def_level = 0
+
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ result = @builder.def_sclass(val[0], val[1], val[2],
+ val[5], val[6])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+
+ @def_level = val[4]
+ }
+ | kMODULE cpath
+ {
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ if in_def?
+ diagnostic :error, :module_in_def, nil, val[0]
+ end
+
+ result = @builder.def_module(val[0], val[1],
+ val[3], val[4])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ }
+ | kDEF fname
+ {
+ @def_level += 1
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ f_arglist bodystmt kEND
+ {
+ result = @builder.def_method(val[0], val[1],
+ val[3], val[4], val[5])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ @def_level -= 1
+ }
+ | kDEF singleton dot_or_colon
+ {
+ @lexer.state = :expr_fname
+ }
+ fname
+ {
+ @def_level += 1
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ f_arglist bodystmt kEND
+ {
+ result = @builder.def_singleton(val[0], val[1], val[2],
+ val[4], val[6], val[7], val[8])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ @def_level -= 1
+ }
+ | kBREAK
+ {
+ result = @builder.keyword_cmd(:break, val[0])
+ }
+ | kNEXT
+ {
+ result = @builder.keyword_cmd(:next, val[0])
+ }
+ | kREDO
+ {
+ result = @builder.keyword_cmd(:redo, val[0])
+ }
+ | kRETRY
+ {
+ result = @builder.keyword_cmd(:retry, val[0])
+ }
+
+ primary_value: primary
+
+ then: term
+ | kTHEN
+ | term kTHEN
+ {
+ result = val[1]
+ }
+
+ do: term
+ | kDO_COND
+
+ if_tail: opt_else
+ | kELSIF expr_value then compstmt if_tail
+ {
+ else_t, else_ = val[4]
+ result = [ val[0],
+ @builder.condition(val[0], val[1], val[2],
+ val[3], else_t,
+ else_, nil),
+ ]
+ }
+
+ opt_else: none
+ | kELSE compstmt
+ {
+ result = val
+ }
+
+ for_var: lhs
+ | mlhs
+
+ f_marg: f_norm_arg
+ {
+ result = @builder.arg(val[0])
+ }
+ | tLPAREN f_margs rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ f_marg_list: f_marg
+ {
+ result = [ val[0] ]
+ }
+ | f_marg_list tCOMMA f_marg
+ {
+ result = val[0] << val[2]
+ }
+
+ f_margs: f_marg_list
+ | f_marg_list tCOMMA tSTAR f_norm_arg
+ {
+ result = val[0].
+ push(@builder.restarg(val[2], val[3]))
+ }
+ | f_marg_list tCOMMA tSTAR f_norm_arg tCOMMA f_marg_list
+ {
+ result = val[0].
+ push(@builder.restarg(val[2], val[3])).
+ concat(val[5])
+ }
+ | f_marg_list tCOMMA tSTAR
+ {
+ result = val[0].
+ push(@builder.restarg(val[2]))
+ }
+ | f_marg_list tCOMMA tSTAR tCOMMA f_marg_list
+ {
+ result = val[0].
+ push(@builder.restarg(val[2])).
+ concat(val[4])
+ }
+ | tSTAR f_norm_arg
+ {
+ result = [ @builder.restarg(val[0], val[1]) ]
+ }
+ | tSTAR f_norm_arg tCOMMA f_marg_list
+ {
+ result = [ @builder.restarg(val[0], val[1]),
+ *val[3] ]
+ }
+ | tSTAR
+ {
+ result = [ @builder.restarg(val[0]) ]
+ }
+ | tSTAR tCOMMA f_marg_list
+ {
+ result = [ @builder.restarg(val[0]),
+ *val[2] ]
+ }
+
+ block_args_tail: f_block_kwarg tCOMMA f_kwrest opt_f_block_arg
+ {
+ result = val[0].concat(val[2]).concat(val[3])
+ }
+ | f_block_kwarg opt_f_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_kwrest opt_f_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_block_arg
+ {
+ result = [ val[0] ]
+ }
+
+opt_block_args_tail:
+ tCOMMA block_args_tail
+ {
+ result = val[1]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ block_param: f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[6]).
+ concat(val[7])
+ }
+ | f_arg tCOMMA f_block_optarg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_block_optarg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_rest_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA
+ | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg opt_block_args_tail
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_block_optarg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_block_optarg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_rest_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_rest_arg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | block_args_tail
+
+ opt_block_param: # nothing
+ {
+ result = @builder.args(nil, [], nil)
+ }
+ | block_param_def
+ {
+ @lexer.state = :expr_value
+ }
+
+ block_param_def: tPIPE opt_bv_decl tPIPE
+ {
+ result = @builder.args(val[0], val[1], val[2])
+ }
+ | tOROP
+ {
+ result = @builder.args(val[0], [], val[0])
+ }
+ | tPIPE block_param opt_bv_decl tPIPE
+ {
+ result = @builder.args(val[0], val[1].concat(val[2]), val[3])
+ }
+
+ opt_bv_decl: opt_nl
+ {
+ result = []
+ }
+ | opt_nl tSEMI bv_decls opt_nl
+ {
+ result = val[2]
+ }
+
+ bv_decls: bvar
+ {
+ result = [ val[0] ]
+ }
+ | bv_decls tCOMMA bvar
+ {
+ result = val[0] << val[2]
+ }
+
+ bvar: tIDENTIFIER
+ {
+ result = @builder.shadowarg(val[0])
+ }
+ | f_bad_arg
+
+ lambda: {
+ @static_env.extend_dynamic
+ }
+ f_larglist
+ {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.clear
+ }
+ lambda_body
+ {
+ @lexer.cmdarg = val[2]
+ @lexer.cmdarg.lexpop
+
+ result = [ val[1], val[3] ]
+
+ @static_env.unextend
+ }
+
+ f_larglist: tLPAREN2 f_args opt_bv_decl tRPAREN
+ {
+ result = @builder.args(val[0], val[1].concat(val[2]), val[3])
+ }
+ | f_args
+ {
+ result = @builder.args(nil, val[0], nil)
+ }
+
+ lambda_body: tLAMBEG compstmt tRCURLY
+ {
+ result = [ val[0], val[1], val[2] ]
+ }
+ | kDO_LAMBDA compstmt kEND
+ {
+ result = [ val[0], val[1], val[2] ]
+ }
+
+ do_block: kDO_BLOCK
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt kEND
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ block_call: command do_block
+ {
+ begin_t, block_args, body, end_t = val[1]
+ result = @builder.block(val[0],
+ begin_t, block_args, body, end_t)
+ }
+ | block_call dot_or_colon operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | block_call dot_or_colon operation2 opt_paren_args brace_block
+ {
+ lparen_t, args, rparen_t = val[3]
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | block_call dot_or_colon operation2 command_args do_block
+ {
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+
+ method_call: fcall paren_args
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.call_method(nil, nil, val[0],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tDOT operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 operation2 paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 operation3
+ {
+ result = @builder.call_method(val[0], val[1], val[2])
+ }
+ | primary_value tDOT paren_args
+ {
+ lparen_t, args, rparen_t = val[2]
+ result = @builder.call_method(val[0], val[1], nil,
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 paren_args
+ {
+ lparen_t, args, rparen_t = val[2]
+ result = @builder.call_method(val[0], val[1], nil,
+ lparen_t, args, rparen_t)
+ }
+ | kSUPER paren_args
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.keyword_cmd(:super, val[0],
+ lparen_t, args, rparen_t)
+ }
+ | kSUPER
+ {
+ result = @builder.keyword_cmd(:zsuper, val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index(val[0], val[1], val[2], val[3])
+ }
+
+ brace_block: tLCURLY
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt tRCURLY
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+ | kDO
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt kEND
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ case_body: kWHEN args then compstmt cases
+ {
+ result = [ @builder.when(val[0], val[1], val[2], val[3]),
+ *val[4] ]
+ }
+
+ cases: opt_else
+ {
+ result = [ val[0] ]
+ }
+ | case_body
+
+ opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue
+ {
+ assoc_t, exc_var = val[2]
+
+ if val[1]
+ exc_list = @builder.array(nil, val[1], nil)
+ end
+
+ result = [ @builder.rescue_body(val[0],
+ exc_list, assoc_t, exc_var,
+ val[3], val[4]),
+ *val[5] ]
+ }
+ |
+ {
+ result = []
+ }
+
+ exc_list: arg_value
+ {
+ result = [ val[0] ]
+ }
+ | mrhs
+ | none
+
+ exc_var: tASSOC lhs
+ {
+ result = [ val[0], val[1] ]
+ }
+ | none
+
+ opt_ensure: kENSURE compstmt
+ {
+ result = [ val[0], val[1] ]
+ }
+ | none
+
+ literal: numeric
+ | symbol
+ | dsym
+
+ strings: string
+ {
+ result = @builder.string_compose(nil, val[0], nil)
+ }
+
+ string: string1
+ {
+ result = [ val[0] ]
+ }
+ | string string1
+ {
+ result = val[0] << val[1]
+ }
+
+ string1: tSTRING_BEG string_contents tSTRING_END
+ {
+ result = @builder.string_compose(val[0], val[1], val[2])
+ }
+ | tSTRING
+ {
+ result = @builder.string(val[0])
+ }
+ | tCHARACTER
+ {
+ result = @builder.character(val[0])
+ }
+
+ xstring: tXSTRING_BEG xstring_contents tSTRING_END
+ {
+ result = @builder.xstring_compose(val[0], val[1], val[2])
+ }
+
+ regexp: tREGEXP_BEG regexp_contents tSTRING_END tREGEXP_OPT
+ {
+ opts = @builder.regexp_options(val[3])
+ result = @builder.regexp_compose(val[0], val[1], val[2], opts)
+ }
+
+ words: tWORDS_BEG word_list tSTRING_END
+ {
+ result = @builder.words_compose(val[0], val[1], val[2])
+ }
+
+ word_list: # nothing
+ {
+ result = []
+ }
+ | word_list word tSPACE
+ {
+ result = val[0] << @builder.word(val[1])
+ }
+
+ word: string_content
+ {
+ result = [ val[0] ]
+ }
+ | word string_content
+ {
+ result = val[0] << val[1]
+ }
+
+ symbols: tSYMBOLS_BEG symbol_list tSTRING_END
+ {
+ result = @builder.symbols_compose(val[0], val[1], val[2])
+ }
+
+ symbol_list: # nothing
+ {
+ result = []
+ }
+ | symbol_list word tSPACE
+ {
+ result = val[0] << @builder.word(val[1])
+ }
+
+ qwords: tQWORDS_BEG qword_list tSTRING_END
+ {
+ result = @builder.words_compose(val[0], val[1], val[2])
+ }
+
+ qsymbols: tQSYMBOLS_BEG qsym_list tSTRING_END
+ {
+ result = @builder.symbols_compose(val[0], val[1], val[2])
+ }
+
+ qword_list: # nothing
+ {
+ result = []
+ }
+ | qword_list tSTRING_CONTENT tSPACE
+ {
+ result = val[0] << @builder.string_internal(val[1])
+ }
+
+ qsym_list: # nothing
+ {
+ result = []
+ }
+ | qsym_list tSTRING_CONTENT tSPACE
+ {
+ result = val[0] << @builder.symbol_internal(val[1])
+ }
+
+ string_contents: # nothing
+ {
+ result = []
+ }
+ | string_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+xstring_contents: # nothing
+ {
+ result = []
+ }
+ | xstring_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+regexp_contents: # nothing
+ {
+ result = []
+ }
+ | regexp_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+ string_content: tSTRING_CONTENT
+ {
+ result = @builder.string_internal(val[0])
+ }
+ | tSTRING_DVAR string_dvar
+ {
+ result = val[1]
+ }
+ | tSTRING_DBEG
+ {
+ @lexer.cond.push(false)
+ @lexer.cmdarg.push(false)
+ }
+ compstmt tSTRING_DEND
+ {
+ @lexer.cond.lexpop
+ @lexer.cmdarg.lexpop
+
+ result = @builder.begin(val[0], val[2], val[3])
+ }
+
+ string_dvar: tGVAR
+ {
+ result = @builder.gvar(val[0])
+ }
+ | tIVAR
+ {
+ result = @builder.ivar(val[0])
+ }
+ | tCVAR
+ {
+ result = @builder.cvar(val[0])
+ }
+ | backref
+
+
+ symbol: tSYMBOL
+ {
+ result = @builder.symbol(val[0])
+ }
+
+ dsym: tSYMBEG xstring_contents tSTRING_END
+ {
+ result = @builder.symbol_compose(val[0], val[1], val[2])
+ }
+
+ numeric: simple_numeric
+ {
+ result = val[0]
+ }
+ | tUMINUS_NUM simple_numeric =tLOWEST
+ {
+ result = @builder.negate(val[0], val[1])
+ }
+
+ simple_numeric: tINTEGER
+ {
+ result = @builder.integer(val[0])
+ }
+ | tFLOAT
+ {
+ result = @builder.float(val[0])
+ }
+ | tRATIONAL
+ {
+ result = @builder.rational(val[0])
+ }
+ | tIMAGINARY
+ {
+ result = @builder.complex(val[0])
+ }
+
+ user_variable: tIDENTIFIER
+ {
+ result = @builder.ident(val[0])
+ }
+ | tIVAR
+ {
+ result = @builder.ivar(val[0])
+ }
+ | tGVAR
+ {
+ result = @builder.gvar(val[0])
+ }
+ | tCONSTANT
+ {
+ result = @builder.const(val[0])
+ }
+ | tCVAR
+ {
+ result = @builder.cvar(val[0])
+ }
+
+keyword_variable: kNIL
+ {
+ result = @builder.nil(val[0])
+ }
+ | kSELF
+ {
+ result = @builder.self(val[0])
+ }
+ | kTRUE
+ {
+ result = @builder.true(val[0])
+ }
+ | kFALSE
+ {
+ result = @builder.false(val[0])
+ }
+ | k__FILE__
+ {
+ result = @builder.__FILE__(val[0])
+ }
+ | k__LINE__
+ {
+ result = @builder.__LINE__(val[0])
+ }
+ | k__ENCODING__
+ {
+ result = @builder.__ENCODING__(val[0])
+ }
+
+ var_ref: user_variable
+ {
+ result = @builder.accessible(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.accessible(val[0])
+ }
+
+ var_lhs: user_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ backref: tNTH_REF
+ {
+ result = @builder.nth_ref(val[0])
+ }
+ | tBACK_REF
+ {
+ result = @builder.back_ref(val[0])
+ }
+
+ superclass: term
+ {
+ result = nil
+ }
+ | tLT
+ {
+ @lexer.state = :expr_value
+ }
+ expr_value term
+ {
+ result = [ val[0], val[2] ]
+ }
+ | error term
+ {
+ yyerrok
+ result = nil
+ }
+
+ f_arglist: tLPAREN2 f_args rparen
+ {
+ result = @builder.args(val[0], val[1], val[2])
+
+ @lexer.state = :expr_value
+ }
+ | {
+ result = @lexer.in_kwarg
+ @lexer.in_kwarg = true
+ }
+ f_args term
+ {
+ @lexer.in_kwarg = val[0]
+ result = @builder.args(nil, val[1], nil)
+ }
+
+
+ args_tail: f_kwarg tCOMMA f_kwrest opt_f_block_arg
+ {
+ result = val[0].concat(val[2]).concat(val[3])
+ }
+ | f_kwarg opt_f_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_kwrest opt_f_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_block_arg
+ {
+ result = [ val[0] ]
+ }
+
+ opt_args_tail: tCOMMA args_tail
+ {
+ result = val[1]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[6]).
+ concat(val[7])
+ }
+ | f_arg tCOMMA f_optarg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_optarg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_rest_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_optarg tCOMMA f_rest_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_optarg opt_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_optarg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_rest_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_rest_arg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | args_tail
+ {
+ result = val[0]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ f_bad_arg: tCONSTANT
+ {
+ diagnostic :error, :argument_const, nil, val[0]
+ }
+ | tIVAR
+ {
+ diagnostic :error, :argument_ivar, nil, val[0]
+ }
+ | tGVAR
+ {
+ diagnostic :error, :argument_gvar, nil, val[0]
+ }
+ | tCVAR
+ {
+ diagnostic :error, :argument_cvar, nil, val[0]
+ }
+
+ f_norm_arg: f_bad_arg
+ | tIDENTIFIER
+ {
+ @static_env.declare val[0][0]
+
+ result = val[0]
+ }
+
+ f_arg_item: f_norm_arg
+ {
+ result = @builder.arg(val[0])
+ }
+ | tLPAREN f_margs rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ f_arg: f_arg_item
+ {
+ result = [ val[0] ]
+ }
+ | f_arg tCOMMA f_arg_item
+ {
+ result = val[0] << val[2]
+ }
+
+ f_label: tLABEL
+ {
+ check_kwarg_name(val[0])
+
+ @static_env.declare val[0][0]
+
+ result = val[0]
+ }
+
+ f_kw: f_label arg_value
+ {
+ result = @builder.kwoptarg(val[0], val[1])
+ }
+ | f_label
+ {
+ result = @builder.kwarg(val[0])
+ }
+
+ f_block_kw: f_label primary_value
+ {
+ result = @builder.kwoptarg(val[0], val[1])
+ }
+ | f_label
+ {
+ result = @builder.kwarg(val[0])
+ }
+
+ f_block_kwarg: f_block_kw
+ {
+ result = [ val[0] ]
+ }
+ | f_block_kwarg tCOMMA f_block_kw
+ {
+ result = val[0] << val[2]
+ }
+
+ f_kwarg: f_kw
+ {
+ result = [ val[0] ]
+ }
+ | f_kwarg tCOMMA f_kw
+ {
+ result = val[0] << val[2]
+ }
+
+ kwrest_mark: tPOW | tDSTAR
+
+ f_kwrest: kwrest_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = [ @builder.kwrestarg(val[0], val[1]) ]
+ }
+ | kwrest_mark
+ {
+ result = [ @builder.kwrestarg(val[0]) ]
+ }
+
+ f_opt: f_norm_arg tEQL arg_value
+ {
+ result = @builder.optarg(val[0], val[1], val[2])
+ }
+
+ f_block_opt: f_norm_arg tEQL primary_value
+ {
+ result = @builder.optarg(val[0], val[1], val[2])
+ }
+
+ f_block_optarg: f_block_opt
+ {
+ result = [ val[0] ]
+ }
+ | f_block_optarg tCOMMA f_block_opt
+ {
+ result = val[0] << val[2]
+ }
+
+ f_optarg: f_opt
+ {
+ result = [ val[0] ]
+ }
+ | f_optarg tCOMMA f_opt
+ {
+ result = val[0] << val[2]
+ }
+
+ restarg_mark: tSTAR2 | tSTAR
+
+ f_rest_arg: restarg_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = [ @builder.restarg(val[0], val[1]) ]
+ }
+ | restarg_mark
+ {
+ result = [ @builder.restarg(val[0]) ]
+ }
+
+ blkarg_mark: tAMPER2 | tAMPER
+
+ f_block_arg: blkarg_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = @builder.blockarg(val[0], val[1])
+ }
+
+ opt_f_block_arg: tCOMMA f_block_arg
+ {
+ result = [ val[1] ]
+ }
+ |
+ {
+ result = []
+ }
+
+ singleton: var_ref
+ | tLPAREN2 expr rparen
+ {
+ result = val[1]
+ }
+
+ assoc_list: # nothing
+ {
+ result = []
+ }
+ | assocs trailer
+
+ assocs: assoc
+ {
+ result = [ val[0] ]
+ }
+ | assocs tCOMMA assoc
+ {
+ result = val[0] << val[2]
+ }
+
+ assoc: arg_value tASSOC arg_value
+ {
+ result = @builder.pair(val[0], val[1], val[2])
+ }
+ | tLABEL arg_value
+ {
+ result = @builder.pair_keyword(val[0], val[1])
+ }
+ | tDSTAR arg_value
+ {
+ result = @builder.kwsplat(val[0], val[1])
+ }
+
+ operation: tIDENTIFIER | tCONSTANT | tFID
+ operation2: tIDENTIFIER | tCONSTANT | tFID | op
+ operation3: tIDENTIFIER | tFID | op
+ dot_or_colon: tDOT | tCOLON2
+ opt_terms: | terms
+ opt_nl: | tNL
+ rparen: opt_nl tRPAREN
+ {
+ result = val[1]
+ }
+ rbracket: opt_nl tRBRACK
+ {
+ result = val[1]
+ }
+ trailer: | tNL | tCOMMA
+
+ term: tSEMI
+ {
+ yyerrok
+ }
+ | tNL
+
+ terms: term
+ | terms tSEMI
+
+ none: # nothing
+ {
+ result = nil
+ }
+end
+
+---- header
+
+require 'parser'
+
+Parser.check_for_encoding_support
+
+---- inner
+
+ def version
+ 21
+ end
+
+ def default_encoding
+ Encoding::UTF_8
+ end
diff --git a/test/racc/assets/ruby22.y b/test/racc/assets/ruby22.y
new file mode 100644
index 0000000000..751c0e866b
--- /dev/null
+++ b/test/racc/assets/ruby22.y
@@ -0,0 +1,2381 @@
+# Copyright (c) 2013 Peter Zotov <whitequark@whitequark.org>
+#
+# Parts of the source are derived from ruby_parser:
+# Copyright (c) Ryan Davis, seattle.rb
+#
+# MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+class Parser::Ruby22
+
+token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
+ kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT
+ kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kDO_LAMBDA kRETURN kYIELD kSUPER
+ kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD
+ kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__
+ k__FILE__ k__ENCODING__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT
+ tLABEL tCVAR tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT
+ tREGEXP_END tUPLUS tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ
+ tGEQ tLEQ tANDOP tOROP tMATCH tNMATCH tDOT tDOT2 tDOT3 tAREF
+ tASET tLSHFT tRSHFT tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN
+ tLPAREN2 tRPAREN tLPAREN_ARG tLBRACK tLBRACK2 tRBRACK tLBRACE
+ tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2 tTILDE tPERCENT tDIVIDE
+ tDSTAR tPLUS tMINUS tLT tGT tPIPE tBANG tCARET tLCURLY tRCURLY
+ tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tREGEXP_OPT
+ tWORDS_BEG tQWORDS_BEG tSYMBOLS_BEG tQSYMBOLS_BEG tSTRING_DBEG
+ tSTRING_DVAR tSTRING_END tSTRING_DEND tSTRING tSYMBOL
+ tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG tCHARACTER
+ tRATIONAL tIMAGINARY tLABEL_END
+
+prechigh
+ right tBANG tTILDE tUPLUS
+ right tPOW
+ right tUMINUS_NUM tUMINUS
+ left tSTAR2 tDIVIDE tPERCENT
+ left tPLUS tMINUS
+ left tLSHFT tRSHFT
+ left tAMPER2
+ left tPIPE tCARET
+ left tGT tGEQ tLT tLEQ
+ nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
+ left tANDOP
+ left tOROP
+ nonassoc tDOT2 tDOT3
+ right tEH tCOLON
+ left kRESCUE_MOD
+ right tEQL tOP_ASGN
+ nonassoc kDEFINED
+ right kNOT
+ left kOR kAND
+ nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
+ nonassoc tLBRACE_ARG
+ nonassoc tLOWEST
+preclow
+
+rule
+
+ program: top_compstmt
+
+ top_compstmt: top_stmts opt_terms
+ {
+ result = @builder.compstmt(val[0])
+ }
+
+ top_stmts: # nothing
+ {
+ result = []
+ }
+ | top_stmt
+ {
+ result = [ val[0] ]
+ }
+ | top_stmts terms top_stmt
+ {
+ result = val[0] << val[2]
+ }
+ | error top_stmt
+ {
+ result = [ val[1] ]
+ }
+
+ top_stmt: stmt
+ | klBEGIN tLCURLY top_compstmt tRCURLY
+ {
+ result = @builder.preexe(val[0], val[1], val[2], val[3])
+ }
+
+ bodystmt: compstmt opt_rescue opt_else opt_ensure
+ {
+ rescue_bodies = val[1]
+ else_t, else_ = val[2]
+ ensure_t, ensure_ = val[3]
+
+ if rescue_bodies.empty? && !else_.nil?
+ diagnostic :warning, :useless_else, nil, else_t
+ end
+
+ result = @builder.begin_body(val[0],
+ rescue_bodies,
+ else_t, else_,
+ ensure_t, ensure_)
+ }
+
+ compstmt: stmts opt_terms
+ {
+ result = @builder.compstmt(val[0])
+ }
+
+ stmts: # nothing
+ {
+ result = []
+ }
+ | stmt_or_begin
+ {
+ result = [ val[0] ]
+ }
+ | stmts terms stmt_or_begin
+ {
+ result = val[0] << val[2]
+ }
+ | error stmt
+ {
+ result = [ val[1] ]
+ }
+
+ stmt_or_begin: stmt
+ | klBEGIN tLCURLY top_compstmt tRCURLY
+ {
+ diagnostic :error, :begin_in_method, nil, val[0]
+ }
+
+ stmt: kALIAS fitem
+ {
+ @lexer.state = :expr_fname
+ }
+ fitem
+ {
+ result = @builder.alias(val[0], val[1], val[3])
+ }
+ | kALIAS tGVAR tGVAR
+ {
+ result = @builder.alias(val[0],
+ @builder.gvar(val[1]),
+ @builder.gvar(val[2]))
+ }
+ | kALIAS tGVAR tBACK_REF
+ {
+ result = @builder.alias(val[0],
+ @builder.gvar(val[1]),
+ @builder.back_ref(val[2]))
+ }
+ | kALIAS tGVAR tNTH_REF
+ {
+ diagnostic :error, :nth_ref_alias, nil, val[2]
+ }
+ | kUNDEF undef_list
+ {
+ result = @builder.undef_method(val[0], val[1])
+ }
+ | stmt kIF_MOD expr_value
+ {
+ result = @builder.condition_mod(val[0], nil,
+ val[1], val[2])
+ }
+ | stmt kUNLESS_MOD expr_value
+ {
+ result = @builder.condition_mod(nil, val[0],
+ val[1], val[2])
+ }
+ | stmt kWHILE_MOD expr_value
+ {
+ result = @builder.loop_mod(:while, val[0], val[1], val[2])
+ }
+ | stmt kUNTIL_MOD expr_value
+ {
+ result = @builder.loop_mod(:until, val[0], val[1], val[2])
+ }
+ | stmt kRESCUE_MOD stmt
+ {
+ rescue_body = @builder.rescue_body(val[1],
+ nil, nil, nil,
+ nil, val[2])
+
+ result = @builder.begin_body(val[0], [ rescue_body ])
+ }
+ | klEND tLCURLY compstmt tRCURLY
+ {
+ result = @builder.postexe(val[0], val[1], val[2], val[3])
+ }
+ | command_asgn
+ | mlhs tEQL command_call
+ {
+ result = @builder.multi_assign(val[0], val[1], val[2])
+ }
+ | var_lhs tOP_ASGN command_call
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.index(
+ val[0], val[1], val[2], val[3]),
+ val[4], val[5])
+ }
+ | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tDOT tCONSTANT tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | backref tOP_ASGN command_call
+ {
+ @builder.op_assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL mrhs
+ {
+ result = @builder.assign(val[0], val[1],
+ @builder.array(nil, val[2], nil))
+ }
+ | mlhs tEQL mrhs_arg
+ {
+ result = @builder.multi_assign(val[0], val[1], val[2])
+ }
+ | expr
+
+ command_asgn: lhs tEQL command_call
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL command_asgn
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+
+ expr: command_call
+ | expr kAND expr
+ {
+ result = @builder.logical_op(:and, val[0], val[1], val[2])
+ }
+ | expr kOR expr
+ {
+ result = @builder.logical_op(:or, val[0], val[1], val[2])
+ }
+ | kNOT opt_nl expr
+ {
+ result = @builder.not_op(val[0], nil, val[2], nil)
+ }
+ | tBANG command_call
+ {
+ result = @builder.not_op(val[0], nil, val[1], nil)
+ }
+ | arg
+
+ expr_value: expr
+
+ command_call: command
+ | block_command
+
+ block_command: block_call
+ | block_call dot_or_colon operation2 command_args
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+ }
+
+ cmd_brace_block: tLBRACE_ARG
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt tRCURLY
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ fcall: operation
+
+ command: fcall command_args =tLOWEST
+ {
+ result = @builder.call_method(nil, nil, val[0],
+ nil, val[1], nil)
+ }
+ | fcall command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(nil, nil, val[0],
+ nil, val[1], nil)
+
+ begin_t, args, body, end_t = val[2]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | primary_value tDOT operation2 command_args =tLOWEST
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+ }
+ | primary_value tDOT operation2 command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | primary_value tCOLON2 operation2 command_args =tLOWEST
+ {
+ result = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+ }
+ | primary_value tCOLON2 operation2 command_args cmd_brace_block
+ {
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | kSUPER command_args
+ {
+ result = @builder.keyword_cmd(:super, val[0],
+ nil, val[1], nil)
+ }
+ | kYIELD command_args
+ {
+ result = @builder.keyword_cmd(:yield, val[0],
+ nil, val[1], nil)
+ }
+ | kRETURN call_args
+ {
+ result = @builder.keyword_cmd(:return, val[0],
+ nil, val[1], nil)
+ }
+ | kBREAK call_args
+ {
+ result = @builder.keyword_cmd(:break, val[0],
+ nil, val[1], nil)
+ }
+ | kNEXT call_args
+ {
+ result = @builder.keyword_cmd(:next, val[0],
+ nil, val[1], nil)
+ }
+
+ mlhs: mlhs_basic
+ {
+ result = @builder.multi_lhs(nil, val[0], nil)
+ }
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+
+ mlhs_inner: mlhs_basic
+ {
+ result = @builder.multi_lhs(nil, val[0], nil)
+ }
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ mlhs_basic: mlhs_head
+ | mlhs_head mlhs_item
+ {
+ result = val[0].
+ push(val[1])
+ }
+ | mlhs_head tSTAR mlhs_node
+ {
+ result = val[0].
+ push(@builder.splat(val[1], val[2]))
+ }
+ | mlhs_head tSTAR mlhs_node tCOMMA mlhs_post
+ {
+ result = val[0].
+ push(@builder.splat(val[1], val[2])).
+ concat(val[4])
+ }
+ | mlhs_head tSTAR
+ {
+ result = val[0].
+ push(@builder.splat(val[1]))
+ }
+ | mlhs_head tSTAR tCOMMA mlhs_post
+ {
+ result = val[0].
+ push(@builder.splat(val[1])).
+ concat(val[3])
+ }
+ | tSTAR mlhs_node
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+ | tSTAR mlhs_node tCOMMA mlhs_post
+ {
+ result = [ @builder.splat(val[0], val[1]),
+ *val[3] ]
+ }
+ | tSTAR
+ {
+ result = [ @builder.splat(val[0]) ]
+ }
+ | tSTAR tCOMMA mlhs_post
+ {
+ result = [ @builder.splat(val[0]),
+ *val[2] ]
+ }
+
+ mlhs_item: mlhs_node
+ | tLPAREN mlhs_inner rparen
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+
+ mlhs_head: mlhs_item tCOMMA
+ {
+ result = [ val[0] ]
+ }
+ | mlhs_head mlhs_item tCOMMA
+ {
+ result = val[0] << val[1]
+ }
+
+ mlhs_post: mlhs_item
+ {
+ result = [ val[0] ]
+ }
+ | mlhs_post tCOMMA mlhs_item
+ {
+ result = val[0] << val[2]
+ }
+
+ mlhs_node: user_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index_asgn(val[0], val[1], val[2], val[3])
+ }
+ | primary_value tDOT tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tDOT tCONSTANT
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_global(val[0], val[1]))
+ }
+ | backref
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ lhs: user_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index_asgn(val[0], val[1], val[2], val[3])
+ }
+ | primary_value tDOT tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tDOT tCONSTANT
+ {
+ result = @builder.attr_asgn(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.assignable(
+ @builder.const_global(val[0], val[1]))
+ }
+ | backref
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ cname: tIDENTIFIER
+ {
+ diagnostic :error, :module_name_const, nil, val[0]
+ }
+ | tCONSTANT
+
+ cpath: tCOLON3 cname
+ {
+ result = @builder.const_global(val[0], val[1])
+ }
+ | cname
+ {
+ result = @builder.const(val[0])
+ }
+ | primary_value tCOLON2 cname
+ {
+ result = @builder.const_fetch(val[0], val[1], val[2])
+ }
+
+ fname: tIDENTIFIER | tCONSTANT | tFID
+ | op
+ | reswords
+
+ fsym: fname
+ {
+ result = @builder.symbol(val[0])
+ }
+ | symbol
+
+ fitem: fsym
+ | dsym
+
+ undef_list: fitem
+ {
+ result = [ val[0] ]
+ }
+ | undef_list tCOMMA
+ {
+ @lexer.state = :expr_fname
+ }
+ fitem
+ {
+ result = val[0] << val[3]
+ }
+
+ op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ
+ | tMATCH | tNMATCH | tGT | tGEQ | tLT | tLEQ
+ | tNEQ | tLSHFT | tRSHFT | tPLUS | tMINUS | tSTAR2
+ | tSTAR | tDIVIDE | tPERCENT | tPOW | tBANG | tTILDE
+ | tUPLUS | tUMINUS | tAREF | tASET | tDSTAR | tBACK_REF2
+
+ reswords: k__LINE__ | k__FILE__ | k__ENCODING__ | klBEGIN | klEND
+ | kALIAS | kAND | kBEGIN | kBREAK | kCASE
+ | kCLASS | kDEF | kDEFINED | kDO | kELSE
+ | kELSIF | kEND | kENSURE | kFALSE | kFOR
+ | kIN | kMODULE | kNEXT | kNIL | kNOT
+ | kOR | kREDO | kRESCUE | kRETRY | kRETURN
+ | kSELF | kSUPER | kTHEN | kTRUE | kUNDEF
+ | kWHEN | kYIELD | kIF | kUNLESS | kWHILE
+ | kUNTIL
+
+ arg: lhs tEQL arg
+ {
+ result = @builder.assign(val[0], val[1], val[2])
+ }
+ | lhs tEQL arg kRESCUE_MOD arg
+ {
+ rescue_body = @builder.rescue_body(val[3],
+ nil, nil, nil,
+ nil, val[4])
+
+ rescue_ = @builder.begin_body(val[2], [ rescue_body ])
+
+ result = @builder.assign(val[0], val[1], rescue_)
+ }
+ | var_lhs tOP_ASGN arg
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | var_lhs tOP_ASGN arg kRESCUE_MOD arg
+ {
+ rescue_body = @builder.rescue_body(val[3],
+ nil, nil, nil,
+ nil, val[4])
+
+ rescue_ = @builder.begin_body(val[2], [ rescue_body ])
+
+ result = @builder.op_assign(val[0], val[1], rescue_)
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.index(
+ val[0], val[1], val[2], val[3]),
+ val[4], val[5])
+ }
+ | primary_value tDOT tIDENTIFIER tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tDOT tCONSTANT tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
+ {
+ result = @builder.op_assign(
+ @builder.call_method(
+ val[0], val[1], val[2]),
+ val[3], val[4])
+ }
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
+ {
+ const = @builder.const_op_assignable(
+ @builder.const_fetch(val[0], val[1], val[2]))
+ result = @builder.op_assign(const, val[3], val[4])
+ }
+ | tCOLON3 tCONSTANT tOP_ASGN arg
+ {
+ const = @builder.const_op_assignable(
+ @builder.const_global(val[0], val[1]))
+ result = @builder.op_assign(const, val[2], val[3])
+ }
+ | backref tOP_ASGN arg
+ {
+ result = @builder.op_assign(val[0], val[1], val[2])
+ }
+ | arg tDOT2 arg
+ {
+ result = @builder.range_inclusive(val[0], val[1], val[2])
+ }
+ | arg tDOT3 arg
+ {
+ result = @builder.range_exclusive(val[0], val[1], val[2])
+ }
+ | arg tPLUS arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tMINUS arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tSTAR2 arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tDIVIDE arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tPERCENT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tPOW arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | tUMINUS_NUM simple_numeric tPOW arg
+ {
+ result = @builder.unary_op(val[0],
+ @builder.binary_op(
+ val[1], val[2], val[3]))
+ }
+ | tUPLUS arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | tUMINUS arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | arg tPIPE arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tCARET arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tAMPER2 arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tCMP arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tGT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tGEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tLT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tLEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tEQQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tNEQ arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tMATCH arg
+ {
+ result = @builder.match_op(val[0], val[1], val[2])
+ }
+ | arg tNMATCH arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | tBANG arg
+ {
+ result = @builder.not_op(val[0], nil, val[1], nil)
+ }
+ | tTILDE arg
+ {
+ result = @builder.unary_op(val[0], val[1])
+ }
+ | arg tLSHFT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tRSHFT arg
+ {
+ result = @builder.binary_op(val[0], val[1], val[2])
+ }
+ | arg tANDOP arg
+ {
+ result = @builder.logical_op(:and, val[0], val[1], val[2])
+ }
+ | arg tOROP arg
+ {
+ result = @builder.logical_op(:or, val[0], val[1], val[2])
+ }
+ | kDEFINED opt_nl arg
+ {
+ result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil)
+ }
+
+ # Note: MRI eventually came to rely on disambiguation based on
+ # the lexer state, but it is too contrived with the Ragel lexer,
+ # so we kept this approach. See ruby/ruby@b0c03f63e5 for
+ # the initial commit, and ruby/ruby@23352f62a for MRI revert,
+ # which we decided not to track.
+ | arg tEH
+ {
+ @lexer.push_cond
+ @lexer.cond.push(true)
+ }
+ arg opt_nl tCOLON
+ {
+ @lexer.pop_cond
+ }
+ arg
+ {
+ result = @builder.ternary(val[0], val[1],
+ val[3], val[5], val[7])
+ }
+ | primary
+
+ arg_value: arg
+
+ aref_args: none
+ | args trailer
+ | args tCOMMA assocs trailer
+ {
+ result = val[0] << @builder.associate(nil, val[2], nil)
+ }
+ | assocs trailer
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ }
+
+ paren_args: tLPAREN2 opt_call_args rparen
+ {
+ result = val
+ }
+
+ opt_paren_args: # nothing
+ {
+ result = [ nil, [], nil ]
+ }
+ | paren_args
+
+ opt_call_args: # nothing
+ {
+ result = []
+ }
+ | call_args
+ | args tCOMMA
+ | args tCOMMA assocs tCOMMA
+ {
+ result = val[0] << @builder.associate(nil, val[2], nil)
+ }
+ | assocs tCOMMA
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ }
+
+ call_args: command
+ {
+ result = [ val[0] ]
+ }
+ | args opt_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | assocs opt_block_arg
+ {
+ result = [ @builder.associate(nil, val[0], nil) ]
+ result.concat(val[1])
+ }
+ | args tCOMMA assocs opt_block_arg
+ {
+ assocs = @builder.associate(nil, val[2], nil)
+ result = val[0] << assocs
+ result.concat(val[3])
+ }
+ | block_arg
+ {
+ result = [ val[0] ]
+ }
+
+ command_args: {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.push(true)
+ }
+ call_args
+ {
+ @lexer.cmdarg = val[0]
+
+ result = val[1]
+ }
+
+ block_arg: tAMPER arg_value
+ {
+ result = @builder.block_pass(val[0], val[1])
+ }
+
+ opt_block_arg: tCOMMA block_arg
+ {
+ result = [ val[1] ]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ args: arg_value
+ {
+ result = [ val[0] ]
+ }
+ | tSTAR arg_value
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+ | args tCOMMA arg_value
+ {
+ result = val[0] << val[2]
+ }
+ | args tCOMMA tSTAR arg_value
+ {
+ result = val[0] << @builder.splat(val[2], val[3])
+ }
+
+ mrhs_arg: mrhs
+ {
+ result = @builder.array(nil, val[0], nil)
+ }
+ | arg_value
+
+ mrhs: args tCOMMA arg_value
+ {
+ result = val[0] << val[2]
+ }
+ | args tCOMMA tSTAR arg_value
+ {
+ result = val[0] << @builder.splat(val[2], val[3])
+ }
+ | tSTAR arg_value
+ {
+ result = [ @builder.splat(val[0], val[1]) ]
+ }
+
+ primary: literal
+ | strings
+ | xstring
+ | regexp
+ | words
+ | qwords
+ | symbols
+ | qsymbols
+ | var_ref
+ | backref
+ | tFID
+ {
+ result = @builder.call_method(nil, nil, val[0])
+ }
+ | kBEGIN
+ {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.clear
+ }
+ bodystmt kEND
+ {
+ @lexer.cmdarg = val[1]
+
+ result = @builder.begin_keyword(val[0], val[2], val[3])
+ }
+ | tLPAREN_ARG
+ {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.clear
+ }
+ expr
+ {
+ @lexer.state = :expr_endarg
+ }
+ opt_nl tRPAREN
+ {
+ @lexer.cmdarg = val[1]
+
+ result = @builder.begin(val[0], val[2], val[5])
+ }
+ | tLPAREN_ARG
+ {
+ @lexer.state = :expr_endarg
+ }
+ opt_nl tRPAREN
+ {
+ result = @builder.begin(val[0], nil, val[3])
+ }
+ | tLPAREN compstmt tRPAREN
+ {
+ result = @builder.begin(val[0], val[1], val[2])
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ result = @builder.const_fetch(val[0], val[1], val[2])
+ }
+ | tCOLON3 tCONSTANT
+ {
+ result = @builder.const_global(val[0], val[1])
+ }
+ | tLBRACK aref_args tRBRACK
+ {
+ result = @builder.array(val[0], val[1], val[2])
+ }
+ | tLBRACE assoc_list tRCURLY
+ {
+ result = @builder.associate(val[0], val[1], val[2])
+ }
+ | kRETURN
+ {
+ result = @builder.keyword_cmd(:return, val[0])
+ }
+ | kYIELD tLPAREN2 call_args rparen
+ {
+ result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3])
+ }
+ | kYIELD tLPAREN2 rparen
+ {
+ result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2])
+ }
+ | kYIELD
+ {
+ result = @builder.keyword_cmd(:yield, val[0])
+ }
+ | kDEFINED opt_nl tLPAREN2 expr rparen
+ {
+ result = @builder.keyword_cmd(:defined?, val[0],
+ val[2], [ val[3] ], val[4])
+ }
+ | kNOT tLPAREN2 expr rparen
+ {
+ result = @builder.not_op(val[0], val[1], val[2], val[3])
+ }
+ | kNOT tLPAREN2 rparen
+ {
+ result = @builder.not_op(val[0], val[1], nil, val[2])
+ }
+ | fcall brace_block
+ {
+ method_call = @builder.call_method(nil, nil, val[0])
+
+ begin_t, args, body, end_t = val[1]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | method_call
+ | method_call brace_block
+ {
+ begin_t, args, body, end_t = val[1]
+ result = @builder.block(val[0],
+ begin_t, args, body, end_t)
+ }
+ | tLAMBDA lambda
+ {
+ lambda_call = @builder.call_lambda(val[0])
+
+ args, (begin_t, body, end_t) = val[1]
+ result = @builder.block(lambda_call,
+ begin_t, args, body, end_t)
+ }
+ | kIF expr_value then compstmt if_tail kEND
+ {
+ else_t, else_ = val[4]
+ result = @builder.condition(val[0], val[1], val[2],
+ val[3], else_t,
+ else_, val[5])
+ }
+ | kUNLESS expr_value then compstmt opt_else kEND
+ {
+ else_t, else_ = val[4]
+ result = @builder.condition(val[0], val[1], val[2],
+ else_, else_t,
+ val[3], val[5])
+ }
+ | kWHILE
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.loop(:while, val[0], val[2], val[3],
+ val[5], val[6])
+ }
+ | kUNTIL
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.loop(:until, val[0], val[2], val[3],
+ val[5], val[6])
+ }
+ | kCASE expr_value opt_terms case_body kEND
+ {
+ *when_bodies, (else_t, else_body) = *val[3]
+
+ result = @builder.case(val[0], val[1],
+ when_bodies, else_t, else_body,
+ val[4])
+ }
+ | kCASE opt_terms case_body kEND
+ {
+ *when_bodies, (else_t, else_body) = *val[2]
+
+ result = @builder.case(val[0], nil,
+ when_bodies, else_t, else_body,
+ val[3])
+ }
+ | kFOR for_var kIN
+ {
+ @lexer.cond.push(true)
+ }
+ expr_value do
+ {
+ @lexer.cond.pop
+ }
+ compstmt kEND
+ {
+ result = @builder.for(val[0], val[1],
+ val[2], val[4],
+ val[5], val[7], val[8])
+ }
+ | kCLASS cpath superclass
+ {
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ if in_def?
+ diagnostic :error, :class_in_def, nil, val[0]
+ end
+
+ lt_t, superclass = val[2]
+ result = @builder.def_class(val[0], val[1],
+ lt_t, superclass,
+ val[4], val[5])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ }
+ | kCLASS tLSHFT expr term
+ {
+ result = @def_level
+ @def_level = 0
+
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ result = @builder.def_sclass(val[0], val[1], val[2],
+ val[5], val[6])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+
+ @def_level = val[4]
+ }
+ | kMODULE cpath
+ {
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ bodystmt kEND
+ {
+ if in_def?
+ diagnostic :error, :module_in_def, nil, val[0]
+ end
+
+ result = @builder.def_module(val[0], val[1],
+ val[3], val[4])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ }
+ | kDEF fname
+ {
+ @def_level += 1
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ f_arglist bodystmt kEND
+ {
+ result = @builder.def_method(val[0], val[1],
+ val[3], val[4], val[5])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ @def_level -= 1
+ }
+ | kDEF singleton dot_or_colon
+ {
+ @lexer.state = :expr_fname
+ }
+ fname
+ {
+ @def_level += 1
+ @static_env.extend_static
+ @lexer.push_cmdarg
+ }
+ f_arglist bodystmt kEND
+ {
+ result = @builder.def_singleton(val[0], val[1], val[2],
+ val[4], val[6], val[7], val[8])
+
+ @lexer.pop_cmdarg
+ @static_env.unextend
+ @def_level -= 1
+ }
+ | kBREAK
+ {
+ result = @builder.keyword_cmd(:break, val[0])
+ }
+ | kNEXT
+ {
+ result = @builder.keyword_cmd(:next, val[0])
+ }
+ | kREDO
+ {
+ result = @builder.keyword_cmd(:redo, val[0])
+ }
+ | kRETRY
+ {
+ result = @builder.keyword_cmd(:retry, val[0])
+ }
+
+ primary_value: primary
+
+ then: term
+ | kTHEN
+ | term kTHEN
+ {
+ result = val[1]
+ }
+
+ do: term
+ | kDO_COND
+
+ if_tail: opt_else
+ | kELSIF expr_value then compstmt if_tail
+ {
+ else_t, else_ = val[4]
+ result = [ val[0],
+ @builder.condition(val[0], val[1], val[2],
+ val[3], else_t,
+ else_, nil),
+ ]
+ }
+
+ opt_else: none
+ | kELSE compstmt
+ {
+ result = val
+ }
+
+ for_var: lhs
+ | mlhs
+
+ f_marg: f_norm_arg
+ {
+ result = @builder.arg(val[0])
+ }
+ | tLPAREN f_margs rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ f_marg_list: f_marg
+ {
+ result = [ val[0] ]
+ }
+ | f_marg_list tCOMMA f_marg
+ {
+ result = val[0] << val[2]
+ }
+
+ f_margs: f_marg_list
+ | f_marg_list tCOMMA tSTAR f_norm_arg
+ {
+ result = val[0].
+ push(@builder.restarg(val[2], val[3]))
+ }
+ | f_marg_list tCOMMA tSTAR f_norm_arg tCOMMA f_marg_list
+ {
+ result = val[0].
+ push(@builder.restarg(val[2], val[3])).
+ concat(val[5])
+ }
+ | f_marg_list tCOMMA tSTAR
+ {
+ result = val[0].
+ push(@builder.restarg(val[2]))
+ }
+ | f_marg_list tCOMMA tSTAR tCOMMA f_marg_list
+ {
+ result = val[0].
+ push(@builder.restarg(val[2])).
+ concat(val[4])
+ }
+ | tSTAR f_norm_arg
+ {
+ result = [ @builder.restarg(val[0], val[1]) ]
+ }
+ | tSTAR f_norm_arg tCOMMA f_marg_list
+ {
+ result = [ @builder.restarg(val[0], val[1]),
+ *val[3] ]
+ }
+ | tSTAR
+ {
+ result = [ @builder.restarg(val[0]) ]
+ }
+ | tSTAR tCOMMA f_marg_list
+ {
+ result = [ @builder.restarg(val[0]),
+ *val[2] ]
+ }
+
+ block_args_tail: f_block_kwarg tCOMMA f_kwrest opt_f_block_arg
+ {
+ result = val[0].concat(val[2]).concat(val[3])
+ }
+ | f_block_kwarg opt_f_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_kwrest opt_f_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_block_arg
+ {
+ result = [ val[0] ]
+ }
+
+opt_block_args_tail:
+ tCOMMA block_args_tail
+ {
+ result = val[1]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ block_param: f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[6]).
+ concat(val[7])
+ }
+ | f_arg tCOMMA f_block_optarg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_block_optarg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_rest_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA
+ | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg opt_block_args_tail
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_block_optarg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_block_optarg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_rest_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_rest_arg tCOMMA f_arg opt_block_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | block_args_tail
+
+ opt_block_param: # nothing
+ {
+ result = @builder.args(nil, [], nil)
+ }
+ | block_param_def
+ {
+ @lexer.state = :expr_value
+ }
+
+ block_param_def: tPIPE opt_bv_decl tPIPE
+ {
+ result = @builder.args(val[0], val[1], val[2])
+ }
+ | tOROP
+ {
+ result = @builder.args(val[0], [], val[0])
+ }
+ | tPIPE block_param opt_bv_decl tPIPE
+ {
+ result = @builder.args(val[0], val[1].concat(val[2]), val[3])
+ }
+
+ opt_bv_decl: opt_nl
+ {
+ result = []
+ }
+ | opt_nl tSEMI bv_decls opt_nl
+ {
+ result = val[2]
+ }
+
+ bv_decls: bvar
+ {
+ result = [ val[0] ]
+ }
+ | bv_decls tCOMMA bvar
+ {
+ result = val[0] << val[2]
+ }
+
+ bvar: tIDENTIFIER
+ {
+ result = @builder.shadowarg(val[0])
+ }
+ | f_bad_arg
+
+ lambda: {
+ @static_env.extend_dynamic
+ }
+ f_larglist
+ {
+ result = @lexer.cmdarg.dup
+ @lexer.cmdarg.clear
+ }
+ lambda_body
+ {
+ @lexer.cmdarg = val[2]
+ @lexer.cmdarg.lexpop
+
+ result = [ val[1], val[3] ]
+
+ @static_env.unextend
+ }
+
+ f_larglist: tLPAREN2 f_args opt_bv_decl tRPAREN
+ {
+ result = @builder.args(val[0], val[1].concat(val[2]), val[3])
+ }
+ | f_args
+ {
+ result = @builder.args(nil, val[0], nil)
+ }
+
+ lambda_body: tLAMBEG compstmt tRCURLY
+ {
+ result = [ val[0], val[1], val[2] ]
+ }
+ | kDO_LAMBDA compstmt kEND
+ {
+ result = [ val[0], val[1], val[2] ]
+ }
+
+ do_block: kDO_BLOCK
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt kEND
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ block_call: command do_block
+ {
+ begin_t, block_args, body, end_t = val[1]
+ result = @builder.block(val[0],
+ begin_t, block_args, body, end_t)
+ }
+ | block_call dot_or_colon operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | block_call dot_or_colon operation2 opt_paren_args brace_block
+ {
+ lparen_t, args, rparen_t = val[3]
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+ | block_call dot_or_colon operation2 command_args do_block
+ {
+ method_call = @builder.call_method(val[0], val[1], val[2],
+ nil, val[3], nil)
+
+ begin_t, args, body, end_t = val[4]
+ result = @builder.block(method_call,
+ begin_t, args, body, end_t)
+ }
+
+ method_call: fcall paren_args
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.call_method(nil, nil, val[0],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tDOT operation2 opt_paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 operation2 paren_args
+ {
+ lparen_t, args, rparen_t = val[3]
+ result = @builder.call_method(val[0], val[1], val[2],
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 operation3
+ {
+ result = @builder.call_method(val[0], val[1], val[2])
+ }
+ | primary_value tDOT paren_args
+ {
+ lparen_t, args, rparen_t = val[2]
+ result = @builder.call_method(val[0], val[1], nil,
+ lparen_t, args, rparen_t)
+ }
+ | primary_value tCOLON2 paren_args
+ {
+ lparen_t, args, rparen_t = val[2]
+ result = @builder.call_method(val[0], val[1], nil,
+ lparen_t, args, rparen_t)
+ }
+ | kSUPER paren_args
+ {
+ lparen_t, args, rparen_t = val[1]
+ result = @builder.keyword_cmd(:super, val[0],
+ lparen_t, args, rparen_t)
+ }
+ | kSUPER
+ {
+ result = @builder.keyword_cmd(:zsuper, val[0])
+ }
+ | primary_value tLBRACK2 opt_call_args rbracket
+ {
+ result = @builder.index(val[0], val[1], val[2], val[3])
+ }
+
+ brace_block: tLCURLY
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt tRCURLY
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+ | kDO
+ {
+ @static_env.extend_dynamic
+ }
+ opt_block_param compstmt kEND
+ {
+ result = [ val[0], val[2], val[3], val[4] ]
+
+ @static_env.unextend
+ }
+
+ case_body: kWHEN args then compstmt cases
+ {
+ result = [ @builder.when(val[0], val[1], val[2], val[3]),
+ *val[4] ]
+ }
+
+ cases: opt_else
+ {
+ result = [ val[0] ]
+ }
+ | case_body
+
+ opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue
+ {
+ assoc_t, exc_var = val[2]
+
+ if val[1]
+ exc_list = @builder.array(nil, val[1], nil)
+ end
+
+ result = [ @builder.rescue_body(val[0],
+ exc_list, assoc_t, exc_var,
+ val[3], val[4]),
+ *val[5] ]
+ }
+ |
+ {
+ result = []
+ }
+
+ exc_list: arg_value
+ {
+ result = [ val[0] ]
+ }
+ | mrhs
+ | none
+
+ exc_var: tASSOC lhs
+ {
+ result = [ val[0], val[1] ]
+ }
+ | none
+
+ opt_ensure: kENSURE compstmt
+ {
+ result = [ val[0], val[1] ]
+ }
+ | none
+
+ literal: numeric
+ | symbol
+ | dsym
+
+ strings: string
+ {
+ result = @builder.string_compose(nil, val[0], nil)
+ }
+
+ string: string1
+ {
+ result = [ val[0] ]
+ }
+ | string string1
+ {
+ result = val[0] << val[1]
+ }
+
+ string1: tSTRING_BEG string_contents tSTRING_END
+ {
+ result = @builder.string_compose(val[0], val[1], val[2])
+ }
+ | tSTRING
+ {
+ result = @builder.string(val[0])
+ }
+ | tCHARACTER
+ {
+ result = @builder.character(val[0])
+ }
+
+ xstring: tXSTRING_BEG xstring_contents tSTRING_END
+ {
+ result = @builder.xstring_compose(val[0], val[1], val[2])
+ }
+
+ regexp: tREGEXP_BEG regexp_contents tSTRING_END tREGEXP_OPT
+ {
+ opts = @builder.regexp_options(val[3])
+ result = @builder.regexp_compose(val[0], val[1], val[2], opts)
+ }
+
+ words: tWORDS_BEG word_list tSTRING_END
+ {
+ result = @builder.words_compose(val[0], val[1], val[2])
+ }
+
+ word_list: # nothing
+ {
+ result = []
+ }
+ | word_list word tSPACE
+ {
+ result = val[0] << @builder.word(val[1])
+ }
+
+ word: string_content
+ {
+ result = [ val[0] ]
+ }
+ | word string_content
+ {
+ result = val[0] << val[1]
+ }
+
+ symbols: tSYMBOLS_BEG symbol_list tSTRING_END
+ {
+ result = @builder.symbols_compose(val[0], val[1], val[2])
+ }
+
+ symbol_list: # nothing
+ {
+ result = []
+ }
+ | symbol_list word tSPACE
+ {
+ result = val[0] << @builder.word(val[1])
+ }
+
+ qwords: tQWORDS_BEG qword_list tSTRING_END
+ {
+ result = @builder.words_compose(val[0], val[1], val[2])
+ }
+
+ qsymbols: tQSYMBOLS_BEG qsym_list tSTRING_END
+ {
+ result = @builder.symbols_compose(val[0], val[1], val[2])
+ }
+
+ qword_list: # nothing
+ {
+ result = []
+ }
+ | qword_list tSTRING_CONTENT tSPACE
+ {
+ result = val[0] << @builder.string_internal(val[1])
+ }
+
+ qsym_list: # nothing
+ {
+ result = []
+ }
+ | qsym_list tSTRING_CONTENT tSPACE
+ {
+ result = val[0] << @builder.symbol_internal(val[1])
+ }
+
+ string_contents: # nothing
+ {
+ result = []
+ }
+ | string_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+xstring_contents: # nothing
+ {
+ result = []
+ }
+ | xstring_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+regexp_contents: # nothing
+ {
+ result = []
+ }
+ | regexp_contents string_content
+ {
+ result = val[0] << val[1]
+ }
+
+ string_content: tSTRING_CONTENT
+ {
+ result = @builder.string_internal(val[0])
+ }
+ | tSTRING_DVAR string_dvar
+ {
+ result = val[1]
+ }
+ | tSTRING_DBEG
+ {
+ @lexer.cond.push(false)
+ @lexer.cmdarg.push(false)
+ }
+ compstmt tSTRING_DEND
+ {
+ @lexer.cond.lexpop
+ @lexer.cmdarg.lexpop
+
+ result = @builder.begin(val[0], val[2], val[3])
+ }
+
+ string_dvar: tGVAR
+ {
+ result = @builder.gvar(val[0])
+ }
+ | tIVAR
+ {
+ result = @builder.ivar(val[0])
+ }
+ | tCVAR
+ {
+ result = @builder.cvar(val[0])
+ }
+ | backref
+
+
+ symbol: tSYMBOL
+ {
+ result = @builder.symbol(val[0])
+ }
+
+ dsym: tSYMBEG xstring_contents tSTRING_END
+ {
+ result = @builder.symbol_compose(val[0], val[1], val[2])
+ }
+
+ numeric: simple_numeric
+ {
+ result = val[0]
+ }
+ | tUMINUS_NUM simple_numeric =tLOWEST
+ {
+ result = @builder.negate(val[0], val[1])
+ }
+
+ simple_numeric: tINTEGER
+ {
+ result = @builder.integer(val[0])
+ }
+ | tFLOAT
+ {
+ result = @builder.float(val[0])
+ }
+ | tRATIONAL
+ {
+ result = @builder.rational(val[0])
+ }
+ | tIMAGINARY
+ {
+ result = @builder.complex(val[0])
+ }
+
+ user_variable: tIDENTIFIER
+ {
+ result = @builder.ident(val[0])
+ }
+ | tIVAR
+ {
+ result = @builder.ivar(val[0])
+ }
+ | tGVAR
+ {
+ result = @builder.gvar(val[0])
+ }
+ | tCONSTANT
+ {
+ result = @builder.const(val[0])
+ }
+ | tCVAR
+ {
+ result = @builder.cvar(val[0])
+ }
+
+keyword_variable: kNIL
+ {
+ result = @builder.nil(val[0])
+ }
+ | kSELF
+ {
+ result = @builder.self(val[0])
+ }
+ | kTRUE
+ {
+ result = @builder.true(val[0])
+ }
+ | kFALSE
+ {
+ result = @builder.false(val[0])
+ }
+ | k__FILE__
+ {
+ result = @builder.__FILE__(val[0])
+ }
+ | k__LINE__
+ {
+ result = @builder.__LINE__(val[0])
+ }
+ | k__ENCODING__
+ {
+ result = @builder.__ENCODING__(val[0])
+ }
+
+ var_ref: user_variable
+ {
+ result = @builder.accessible(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.accessible(val[0])
+ }
+
+ var_lhs: user_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+ | keyword_variable
+ {
+ result = @builder.assignable(val[0])
+ }
+
+ backref: tNTH_REF
+ {
+ result = @builder.nth_ref(val[0])
+ }
+ | tBACK_REF
+ {
+ result = @builder.back_ref(val[0])
+ }
+
+ superclass: term
+ {
+ result = nil
+ }
+ | tLT
+ {
+ @lexer.state = :expr_value
+ }
+ expr_value term
+ {
+ result = [ val[0], val[2] ]
+ }
+ | error term
+ {
+ yyerrok
+ result = nil
+ }
+
+ f_arglist: tLPAREN2 f_args rparen
+ {
+ result = @builder.args(val[0], val[1], val[2])
+
+ @lexer.state = :expr_value
+ }
+ | {
+ result = @lexer.in_kwarg
+ @lexer.in_kwarg = true
+ }
+ f_args term
+ {
+ @lexer.in_kwarg = val[0]
+ result = @builder.args(nil, val[1], nil)
+ }
+
+ args_tail: f_kwarg tCOMMA f_kwrest opt_f_block_arg
+ {
+ result = val[0].concat(val[2]).concat(val[3])
+ }
+ | f_kwarg opt_f_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_kwrest opt_f_block_arg
+ {
+ result = val[0].concat(val[1])
+ }
+ | f_block_arg
+ {
+ result = [ val[0] ]
+ }
+
+ opt_args_tail: tCOMMA args_tail
+ {
+ result = val[1]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[6]).
+ concat(val[7])
+ }
+ | f_arg tCOMMA f_optarg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_optarg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg tCOMMA f_rest_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_optarg tCOMMA f_rest_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[4]).
+ concat(val[5])
+ }
+ | f_optarg opt_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_optarg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | f_rest_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[1])
+ }
+ | f_rest_arg tCOMMA f_arg opt_args_tail
+ {
+ result = val[0].
+ concat(val[2]).
+ concat(val[3])
+ }
+ | args_tail
+ {
+ result = val[0]
+ }
+ | # nothing
+ {
+ result = []
+ }
+
+ f_bad_arg: tCONSTANT
+ {
+ diagnostic :error, :argument_const, nil, val[0]
+ }
+ | tIVAR
+ {
+ diagnostic :error, :argument_ivar, nil, val[0]
+ }
+ | tGVAR
+ {
+ diagnostic :error, :argument_gvar, nil, val[0]
+ }
+ | tCVAR
+ {
+ diagnostic :error, :argument_cvar, nil, val[0]
+ }
+
+ f_norm_arg: f_bad_arg
+ | tIDENTIFIER
+ {
+ @static_env.declare val[0][0]
+
+ result = val[0]
+ }
+
+ f_arg_asgn: f_norm_arg
+ {
+ result = val[0]
+ }
+
+ f_arg_item: f_arg_asgn
+ {
+ result = @builder.arg(val[0])
+ }
+ | tLPAREN f_margs rparen
+ {
+ result = @builder.multi_lhs(val[0], val[1], val[2])
+ }
+
+ f_arg: f_arg_item
+ {
+ result = [ val[0] ]
+ }
+ | f_arg tCOMMA f_arg_item
+ {
+ result = val[0] << val[2]
+ }
+
+ f_label: tLABEL
+ {
+ check_kwarg_name(val[0])
+
+ @static_env.declare val[0][0]
+
+ result = val[0]
+ }
+
+ f_kw: f_label arg_value
+ {
+ result = @builder.kwoptarg(val[0], val[1])
+ }
+ | f_label
+ {
+ result = @builder.kwarg(val[0])
+ }
+
+ f_block_kw: f_label primary_value
+ {
+ result = @builder.kwoptarg(val[0], val[1])
+ }
+ | f_label
+ {
+ result = @builder.kwarg(val[0])
+ }
+
+ f_block_kwarg: f_block_kw
+ {
+ result = [ val[0] ]
+ }
+ | f_block_kwarg tCOMMA f_block_kw
+ {
+ result = val[0] << val[2]
+ }
+
+ f_kwarg: f_kw
+ {
+ result = [ val[0] ]
+ }
+ | f_kwarg tCOMMA f_kw
+ {
+ result = val[0] << val[2]
+ }
+
+ kwrest_mark: tPOW | tDSTAR
+
+ f_kwrest: kwrest_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = [ @builder.kwrestarg(val[0], val[1]) ]
+ }
+ | kwrest_mark
+ {
+ result = [ @builder.kwrestarg(val[0]) ]
+ }
+
+ f_opt: f_arg_asgn tEQL arg_value
+ {
+ result = @builder.optarg(val[0], val[1], val[2])
+ }
+
+ f_block_opt: f_arg_asgn tEQL primary_value
+ {
+ result = @builder.optarg(val[0], val[1], val[2])
+ }
+
+ f_block_optarg: f_block_opt
+ {
+ result = [ val[0] ]
+ }
+ | f_block_optarg tCOMMA f_block_opt
+ {
+ result = val[0] << val[2]
+ }
+
+ f_optarg: f_opt
+ {
+ result = [ val[0] ]
+ }
+ | f_optarg tCOMMA f_opt
+ {
+ result = val[0] << val[2]
+ }
+
+ restarg_mark: tSTAR2 | tSTAR
+
+ f_rest_arg: restarg_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = [ @builder.restarg(val[0], val[1]) ]
+ }
+ | restarg_mark
+ {
+ result = [ @builder.restarg(val[0]) ]
+ }
+
+ blkarg_mark: tAMPER2 | tAMPER
+
+ f_block_arg: blkarg_mark tIDENTIFIER
+ {
+ @static_env.declare val[1][0]
+
+ result = @builder.blockarg(val[0], val[1])
+ }
+
+ opt_f_block_arg: tCOMMA f_block_arg
+ {
+ result = [ val[1] ]
+ }
+ |
+ {
+ result = []
+ }
+
+ singleton: var_ref
+ | tLPAREN2 expr rparen
+ {
+ result = val[1]
+ }
+
+ assoc_list: # nothing
+ {
+ result = []
+ }
+ | assocs trailer
+
+ assocs: assoc
+ {
+ result = [ val[0] ]
+ }
+ | assocs tCOMMA assoc
+ {
+ result = val[0] << val[2]
+ }
+
+ assoc: arg_value tASSOC arg_value
+ {
+ result = @builder.pair(val[0], val[1], val[2])
+ }
+ | tLABEL arg_value
+ {
+ result = @builder.pair_keyword(val[0], val[1])
+ }
+ | tSTRING_BEG string_contents tLABEL_END arg_value
+ {
+ result = @builder.pair_quoted(val[0], val[1], val[2], val[3])
+ }
+ | tDSTAR arg_value
+ {
+ result = @builder.kwsplat(val[0], val[1])
+ }
+
+ operation: tIDENTIFIER | tCONSTANT | tFID
+ operation2: tIDENTIFIER | tCONSTANT | tFID | op
+ operation3: tIDENTIFIER | tFID | op
+ dot_or_colon: tDOT | tCOLON2
+ opt_terms: | terms
+ opt_nl: | tNL
+ rparen: opt_nl tRPAREN
+ {
+ result = val[1]
+ }
+ rbracket: opt_nl tRBRACK
+ {
+ result = val[1]
+ }
+ trailer: | tNL | tCOMMA
+
+ term: tSEMI
+ {
+ yyerrok
+ }
+ | tNL
+
+ terms: term
+ | terms tSEMI
+
+ none: # nothing
+ {
+ result = nil
+ }
+end
+
+---- header
+
+require 'parser'
+
+Parser.check_for_encoding_support
+
+---- inner
+
+ def version
+ 22
+ end
+
+ def default_encoding
+ Encoding::UTF_8
+ end
diff --git a/test/racc/assets/scan.y b/test/racc/assets/scan.y
new file mode 100644
index 0000000000..709254ed66
--- /dev/null
+++ b/test/racc/assets/scan.y
@@ -0,0 +1,72 @@
+class P
+
+rule
+
+ a: A
+ {
+ # comment test
+
+ # comment test
+
+ # string
+ @sstring = 'squote string'
+ @dstring = 'dquote string'
+
+ # regexp
+ @regexp = /some regexp with spaces/
+
+ # gvar
+ /regexp/ === 'some regexp matches to this string'
+ @pre_match = $`
+ @matched = $&
+ @post_match = $'
+ @m = $~
+
+ # braces
+ @array = []
+ [1,2,3].each {|i|
+ @array.push i
+ }
+ 3.times { @array.push 10 }
+ }
+
+end
+
+---- inner
+
+ def parse
+ @sstring = @dstring = nil
+ @regexp = nil
+ @pre_match = @matched = @post_match = @m = nil
+
+ @src = [[:A, 'A'], [false, '$']]
+ do_parse
+
+ assert_equal 'squote string', @sstring
+ assert_equal 'dquote string', @dstring
+ assert_equal(/some regexp with spaces/, @regexp)
+ assert_equal 'some ', @pre_match
+ assert_equal 'regexp', @matched
+ assert_equal ' matches to this string', @post_match
+ assert_instance_of MatchData, @m
+ end
+
+ def assert_equal(ok, data)
+ unless ok == data
+ raise "expected <#{ok.inspect}> but is <#{data.inspect}>"
+ end
+ end
+
+ def assert_instance_of(klass, obj)
+ unless obj.instance_of?(klass)
+ raise "expected #{klass} but is #{obj.class}"
+ end
+ end
+
+ def next_token
+ @src.shift
+ end
+
+---- footer
+
+P.new.parse
diff --git a/test/racc/assets/syntax.y b/test/racc/assets/syntax.y
new file mode 100644
index 0000000000..e8bb1fb4d8
--- /dev/null
+++ b/test/racc/assets/syntax.y
@@ -0,0 +1,50 @@
+#
+# racc syntax checker
+#
+
+class M1::M2::ParserClass < S1::S2::SuperClass
+
+ token A
+ | B C
+
+ convert
+ A '5'
+ end
+
+ prechigh
+ left B
+ preclow
+
+ start target
+
+ expect 0
+
+rule
+
+ target: A B C
+ {
+ print 'abc'
+ }
+ | B C A
+ | C B A
+ {
+ print 'cba'
+ }
+ | cont
+
+ cont : A c2 B c2 C
+
+ c2 : C C C C C
+
+end
+
+---- inner
+
+ junk code !!!!
+
+kjaljlajrlaolanbla /// %%% (*((( token rule
+akiurtlajluealjflaj @@@@ end end end end __END__
+ laieu2o879urkq96ga(Q#*&%Q#
+ #&lkji END
+
+ q395q?/// liutjqlkr7
diff --git a/test/racc/assets/tp_plus.y b/test/racc/assets/tp_plus.y
new file mode 100644
index 0000000000..388ed1302d
--- /dev/null
+++ b/test/racc/assets/tp_plus.y
@@ -0,0 +1,622 @@
+# Released under an MIT License (http://www.opensource.org/licenses/MIT)
+# By Jay Strybis (https://github.com/unreal)
+
+class TPPlus::Parser
+token ASSIGN AT_SYM COMMENT JUMP IO_METHOD INPUT OUTPUT
+token NUMREG POSREG VREG SREG TIME_SEGMENT ARG UALM
+token MOVE DOT TO AT TERM OFFSET SKIP GROUP
+token SEMICOLON NEWLINE STRING
+token REAL DIGIT WORD EQUAL
+token EEQUAL NOTEQUAL GTE LTE LT GT BANG
+token PLUS MINUS STAR SLASH DIV AND OR MOD
+token IF ELSE END UNLESS FOR IN WHILE
+token WAIT_FOR WAIT_UNTIL TIMEOUT AFTER
+token FANUC_USE SET_SKIP_CONDITION NAMESPACE
+token CASE WHEN INDIRECT POSITION
+token EVAL TIMER TIMER_METHOD RAISE ABORT
+token POSITION_DATA TRUE_FALSE RUN TP_HEADER PAUSE
+token LPAREN RPAREN COLON COMMA LBRACK RBRACK LBRACE RBRACE
+token LABEL ADDRESS
+token false
+
+prechigh
+ right BANG
+ left STAR SLASH DIV MOD
+ left PLUS MINUS
+ left GT GTE LT LTE
+ left EEQUAL NOTEQUAL
+ left AND
+ left OR
+ right EQUAL
+preclow
+
+rule
+ program
+ #: statements { @interpreter.nodes = val[0].flatten }
+ : statements { @interpreter.nodes = val[0] }
+ |
+ ;
+
+
+ statements
+ : statement terminator {
+ result = [val[0]]
+ result << val[1] unless val[1].nil?
+ }
+ | statements statement terminator {
+ result = val[0] << val[1]
+ result << val[2] unless val[2].nil?
+ }
+ ;
+
+ block
+ : NEWLINE statements { result = val[1] }
+ ;
+
+ optional_newline
+ : NEWLINE
+ |
+ ;
+
+ statement
+ : comment
+ | definition
+ | namespace
+ #| assignment
+ | motion_statement
+ #| jump
+ #| io_method
+ | label_definition
+ | address
+ | conditional
+ | inline_conditional
+ | forloop
+ | while_loop
+ #| program_call
+ | use_statement
+ | set_skip_statement
+ | wait_statement
+ | case_statement
+ | fanuc_eval
+ | timer_method
+ | position_data
+ | raise
+ | tp_header_definition
+ | empty_stmt
+ | PAUSE { result = PauseNode.new }
+ | ABORT { result = AbortNode.new }
+ ;
+
+ empty_stmt
+ : NEWLINE { result = EmptyStmtNode.new() }
+ ;
+
+ tp_header_definition
+ : TP_HEADER EQUAL tp_header_value { result = HeaderNode.new(val[0],val[2]) }
+ ;
+
+ tp_header_value
+ : STRING
+ | TRUE_FALSE
+ ;
+
+ raise
+ : RAISE var_or_indirect { result = RaiseNode.new(val[1]) }
+ ;
+
+ timer_method
+ : TIMER_METHOD var_or_indirect { result = TimerMethodNode.new(val[0],val[1]) }
+ ;
+
+ fanuc_eval
+ : EVAL STRING { result = EvalNode.new(val[1]) }
+ ;
+
+ wait_statement
+ : WAIT_FOR LPAREN indirectable COMMA STRING RPAREN
+ { result = WaitForNode.new(val[2], val[4]) }
+ | WAIT_UNTIL LPAREN expression RPAREN
+ { result = WaitUntilNode.new(val[2], nil) }
+ | WAIT_UNTIL LPAREN expression RPAREN DOT wait_modifier
+ { result = WaitUntilNode.new(val[2],val[5]) }
+ | WAIT_UNTIL LPAREN expression RPAREN DOT wait_modifier DOT wait_modifier
+ { result = WaitUntilNode.new(val[2],val[5].merge(val[7])) }
+ ;
+
+ wait_modifier
+ : timeout_modifier
+ | after_modifier
+ ;
+
+ timeout_modifier
+ : swallow_newlines TIMEOUT LPAREN label RPAREN
+ { result = { label: val[3] } }
+ ;
+
+ after_modifier
+ : swallow_newlines AFTER LPAREN indirectable COMMA STRING RPAREN
+ { result = { timeout: [val[3],val[5]] } }
+ ;
+
+ label
+ : LABEL { result = val[0] }
+ ;
+
+ use_statement
+ : FANUC_USE indirectable { result = UseNode.new(val[0],val[1]) }
+ ;
+
+ # set_skip_condition x
+ set_skip_statement
+ : SET_SKIP_CONDITION expression { result = SetSkipNode.new(val[1]) }
+ ;
+
+ program_call
+ : WORD LPAREN args RPAREN { result = CallNode.new(val[0],val[2]) }
+ | RUN WORD LPAREN args RPAREN { result = CallNode.new(val[1],val[3],async: true) }
+ ;
+
+ args
+ : arg { result = [val[0]] }
+ | args COMMA arg { result = val[0] << val[2] }
+ | { result = [] }
+ ;
+
+ arg
+ : number
+ | var
+ | string
+ | address
+ ;
+
+ string
+ : STRING { result = StringNode.new(val[0]) }
+ ;
+
+ io_method
+ : IO_METHOD var_or_indirect { result = IOMethodNode.new(val[0],val[1]) }
+ | IO_METHOD LPAREN var_or_indirect RPAREN
+ { result = IOMethodNode.new(val[0],val[2]) }
+ | IO_METHOD LPAREN var_or_indirect COMMA number COMMA STRING RPAREN
+ { result = IOMethodNode.new(val[0],val[2],{ pulse_time: val[4], pulse_units: val[6] }) }
+ ;
+
+ var_or_indirect
+ : var
+ | indirect_thing
+ ;
+
+
+ jump
+ : JUMP label { result = JumpNode.new(val[1]) }
+ ;
+
+ conditional
+ : IF expression block else_block END
+ { result = ConditionalNode.new("if",val[1],val[2],val[3]) }
+ | UNLESS expression block else_block END
+ { result = ConditionalNode.new("unless",val[1],val[2],val[3]) }
+ ;
+
+ forloop
+ : FOR var IN LPAREN minmax_val TO minmax_val RPAREN block END
+ { result = ForNode.new(val[1],val[4],val[6],val[8]) }
+ ;
+
+ while_loop
+ : WHILE expression block END { result = WhileNode.new(val[1],val[2]) }
+ ;
+
+ minmax_val
+ : integer
+ | var
+ ;
+
+ namespace
+ : NAMESPACE WORD block END { result = NamespaceNode.new(val[1],val[2]) }
+ ;
+
+ case_statement
+ : CASE var swallow_newlines
+ case_conditions
+ case_else
+ END { result = CaseNode.new(val[1],val[3],val[4]) }
+ ;
+
+ case_conditions
+ : case_condition { result = val }
+ | case_conditions case_condition
+ { result = val[0] << val[1] << val[2] }
+ ;
+
+ case_condition
+ : WHEN case_allowed_condition swallow_newlines case_allowed_statement
+ terminator { result = CaseConditionNode.new(val[1],val[3]) }
+ ;
+
+ case_allowed_condition
+ : number
+ | var
+ ;
+
+ case_else
+ : ELSE swallow_newlines case_allowed_statement terminator
+ { result = CaseConditionNode.new(nil,val[2]) }
+ |
+ ;
+
+ case_allowed_statement
+ : program_call
+ | jump
+ ;
+
+ inline_conditional
+ : inlineable
+ | inlineable IF expression { result = InlineConditionalNode.new(val[1], val[2], val[0]) }
+ | inlineable UNLESS expression { result = InlineConditionalNode.new(val[1], val[2], val[0]) }
+ ;
+
+ inlineable
+ : jump
+ | assignment
+ | io_method
+ | program_call
+ ;
+
+ else_block
+ : ELSE block { result = val[1] }
+ | { result = [] }
+ ;
+
+ motion_statement
+ : MOVE DOT swallow_newlines TO LPAREN var RPAREN motion_modifiers
+ { result = MotionNode.new(val[0],val[5],val[7]) }
+ ;
+
+ motion_modifiers
+ : motion_modifier { result = val }
+ | motion_modifiers motion_modifier
+ { result = val[0] << val[1] }
+ ;
+
+ motion_modifier
+ : DOT swallow_newlines AT LPAREN speed RPAREN
+ { result = SpeedNode.new(val[4]) }
+ | DOT swallow_newlines TERM LPAREN valid_terminations RPAREN
+ { result = TerminationNode.new(val[4]) }
+ | DOT swallow_newlines OFFSET LPAREN var RPAREN
+ { result = OffsetNode.new(val[2],val[4]) }
+ | DOT swallow_newlines TIME_SEGMENT LPAREN time COMMA time_seg_actions RPAREN
+ { result = TimeNode.new(val[2],val[4],val[6]) }
+ | DOT swallow_newlines SKIP LPAREN label optional_lpos_arg RPAREN
+ { result = SkipNode.new(val[4],val[5]) }
+ ;
+
+ valid_terminations
+ : integer
+ | var
+ | MINUS DIGIT {
+ raise Racc::ParseError, sprintf("\ninvalid termination type: (%s)", val[1]) if val[1] != 1
+
+ result = DigitNode.new(val[1].to_i * -1)
+ }
+ ;
+
+ optional_lpos_arg
+ : COMMA var { result = val[1] }
+ |
+ ;
+
+ indirectable
+ : number
+ | var
+ ;
+
+ time_seg_actions
+ : program_call
+ | io_method
+ ;
+
+ time
+ : var
+ | number
+ ;
+
+ speed
+ : indirectable COMMA STRING { result = { speed: val[0], units: val[2] } }
+ | STRING { result = { speed: val[0], units: nil } }
+ ;
+
+ label_definition
+ : label { result = LabelDefinitionNode.new(val[0]) }#@interpreter.add_label(val[1]) }
+ ;
+
+ definition
+ : WORD ASSIGN definable { result = DefinitionNode.new(val[0],val[2]) }
+ ;
+
+ assignment
+ : var_or_indirect EQUAL expression { result = AssignmentNode.new(val[0],val[2]) }
+ | var_or_indirect PLUS EQUAL expression { result = AssignmentNode.new(
+ val[0],
+ ExpressionNode.new(val[0],"+",val[3])
+ )
+ }
+ | var_or_indirect MINUS EQUAL expression { result = AssignmentNode.new(
+ val[0],
+ ExpressionNode.new(val[0],"-",val[3])
+ )
+ }
+ ;
+
+ var
+ : var_without_namespaces
+ | var_with_namespaces
+ ;
+
+ var_without_namespaces
+ : WORD { result = VarNode.new(val[0]) }
+ | WORD var_method_modifiers { result = VarMethodNode.new(val[0],val[1]) }
+ ;
+
+ var_with_namespaces
+ : namespaces var_without_namespaces
+ { result = NamespacedVarNode.new(val[0],val[1]) }
+ ;
+
+ var_method_modifiers
+ : var_method_modifier { result = val[0] }
+ | var_method_modifiers var_method_modifier
+ { result = val[0].merge(val[1]) }
+ ;
+
+ var_method_modifier
+ : DOT swallow_newlines WORD { result = { method: val[2] } }
+ | DOT swallow_newlines GROUP LPAREN integer RPAREN
+ { result = { group: val[4] } }
+ ;
+
+ namespaces
+ : ns { result = [val[0]] }
+ | namespaces ns { result = val[0] << val[1] }
+ ;
+
+ ns
+ : WORD COLON COLON { result = val[0] }
+ ;
+
+
+ expression
+ : unary_expression
+ | binary_expression
+ ;
+
+ unary_expression
+ : factor { result = val[0] }
+ | address
+ | BANG factor { result = ExpressionNode.new(val[1], "!", nil) }
+ ;
+
+ binary_expression
+ : expression operator expression
+ { result = ExpressionNode.new(val[0], val[1], val[2]) }
+ ;
+
+ operator
+ : EEQUAL { result = "==" }
+ | NOTEQUAL { result = "<>" }
+ | LT { result = "<" }
+ | GT { result = ">" }
+ | GTE { result = ">=" }
+ | LTE { result = "<=" }
+ | PLUS { result = "+" }
+ | MINUS { result = "-" }
+ | OR { result = "||" }
+ | STAR { result = "*" }
+ | SLASH { result = "/" }
+ | DIV { result = "DIV" }
+ | MOD { result = "%" }
+ | AND { result = "&&" }
+ ;
+
+ factor
+ : number
+ | signed_number
+ | var
+ | indirect_thing
+ | paren_expr
+ ;
+
+ paren_expr
+ : LPAREN expression RPAREN { result = ParenExpressionNode.new(val[1]) }
+ ;
+
+ indirect_thing
+ : INDIRECT LPAREN STRING COMMA indirectable RPAREN
+ { result = IndirectNode.new(val[2].to_sym, val[4]) }
+ ;
+
+ signed_number
+ : sign DIGIT {
+ val[1] = val[1].to_i * -1 if val[0] == "-"
+ result = DigitNode.new(val[1])
+ }
+ | sign REAL { val[1] = val[1].to_f * -1 if val[0] == "-"; result = RealNode.new(val[1]) }
+ ;
+
+ sign
+ : MINUS { result = "-" }
+ ;
+
+ number
+ : integer
+ | REAL { result = RealNode.new(val[0]) }
+ ;
+
+ integer
+ : DIGIT { result = DigitNode.new(val[0]) }
+ ;
+
+ definable
+ : numreg
+ | output
+ | input
+ | posreg
+ | position
+ | vreg
+ | number
+ | signed_number
+ | argument
+ | timer
+ | ualm
+ | sreg
+ ;
+
+
+ sreg
+ : SREG LBRACK DIGIT RBRACK { result = StringRegisterNode.new(val[2].to_i) }
+ ;
+
+ ualm
+ : UALM LBRACK DIGIT RBRACK { result = UserAlarmNode.new(val[2].to_i) }
+ ;
+
+ timer
+ : TIMER LBRACK DIGIT RBRACK { result = TimerNode.new(val[2].to_i) }
+ ;
+
+ argument
+ : ARG LBRACK DIGIT RBRACK { result = ArgumentNode.new(val[2].to_i) }
+ ;
+
+ vreg
+ : VREG LBRACK DIGIT RBRACK { result = VisionRegisterNode.new(val[2].to_i) }
+ ;
+
+ position
+ : POSITION LBRACK DIGIT RBRACK { result = PositionNode.new(val[2].to_i) }
+ ;
+
+ numreg
+ : NUMREG LBRACK DIGIT RBRACK { result = NumregNode.new(val[2].to_i) }
+ ;
+
+ posreg
+ : POSREG LBRACK DIGIT RBRACK { result = PosregNode.new(val[2].to_i) }
+ ;
+
+ output
+ : OUTPUT LBRACK DIGIT RBRACK { result = IONode.new(val[0], val[2].to_i) }
+ ;
+
+ input
+ : INPUT LBRACK DIGIT RBRACK { result = IONode.new(val[0], val[2].to_i) }
+ ;
+
+ address
+ : ADDRESS { result = AddressNode.new(val[0]) }
+ ;
+
+ comment
+ : COMMENT { result = CommentNode.new(val[0]) }
+ ;
+
+ terminator
+ : NEWLINE { result = TerminatorNode.new }
+ | comment optional_newline { result = val[0] }
+ # ^-- consume newlines or else we will get an extra space from EmptyStmt in the output
+ | false
+ |
+ ;
+
+ swallow_newlines
+ : NEWLINE { result = TerminatorNode.new }
+ |
+ ;
+
+ position_data
+ : POSITION_DATA sn hash sn END
+ { result = PositionDataNode.new(val[2]) }
+ ;
+
+ sn
+ : swallow_newlines
+ ;
+
+ hash
+ : LBRACE sn hash_attributes sn RBRACE { result = val[2] }
+ | LBRACE sn RBRACE { result = {} }
+ ;
+
+ hash_attributes
+ : hash_attribute { result = val[0] }
+ | hash_attributes COMMA sn hash_attribute
+ { result = val[0].merge(val[3]) }
+ ;
+
+ hash_attribute
+ : STRING COLON hash_value { result = { val[0].to_sym => val[2] } }
+ ;
+
+ hash_value
+ : STRING
+ | hash
+ | array
+ | optional_sign DIGIT { val[1] = val[1].to_i * -1 if val[0] == "-"; result = val[1] }
+ | optional_sign REAL { val[1] = val[1].to_f * -1 if val[0] == "-"; result = val[1] }
+ | TRUE_FALSE { result = val[0] == "true" }
+ ;
+
+ optional_sign
+ : sign
+ |
+ ;
+
+ array
+ : LBRACK sn array_values sn RBRACK { result = val[2] }
+ ;
+
+ array_values
+ : array_value { result = val }
+ | array_values COMMA sn array_value { result = val[0] << val[3] }
+ ;
+
+ array_value
+ : hash_value
+ ;
+
+
+end
+
+---- inner
+
+ include TPPlus::Nodes
+
+ attr_reader :interpreter
+ def initialize(scanner, interpreter = TPPlus::Interpreter.new)
+ @scanner = scanner
+ @interpreter = interpreter
+ super()
+ end
+
+ def next_token
+ t = @scanner.next_token
+ @interpreter.line_count += 1 if t && t[0] == :NEWLINE
+
+ #puts t.inspect
+ t
+ end
+
+ def parse
+ #@yydebug =true
+
+ do_parse
+ @interpreter
+ end
+
+ def on_error(t, val, vstack)
+ raise ParseError, sprintf("Parse error on line #{@scanner.tok_line} column #{@scanner.tok_col}: %s (%s)",
+ val.inspect, token_to_str(t) || '?')
+ end
+
+ class ParseError < StandardError ; end
diff --git a/test/racc/assets/twowaysql.y b/test/racc/assets/twowaysql.y
new file mode 100644
index 0000000000..c729b08f7d
--- /dev/null
+++ b/test/racc/assets/twowaysql.y
@@ -0,0 +1,278 @@
+# Copyright 2008-2015 Takuto Wada
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+class TwoWaySQL::Parser
+
+rule
+
+sql : stmt_list
+ {
+ result = RootNode.new( val[0] )
+ }
+
+stmt_list :
+ {
+ result = []
+ }
+ | stmt_list stmt
+ {
+ result.push val[1]
+ }
+
+stmt : primary
+ | if_stmt
+ | begin_stmt
+
+begin_stmt : BEGIN stmt_list END
+ {
+ result = BeginNode.new( val[1] )
+ }
+
+if_stmt : IF sub_stmt else_stmt END
+ {
+ result = IfNode.new( val[0][1], val[1], val[2] )
+ }
+
+else_stmt : ELSE sub_stmt
+ {
+ result = val[1]
+ }
+ |
+ {
+ result = nil
+ }
+
+sub_stmt : and_stmt
+ | or_stmt
+ | stmt_list
+
+and_stmt : AND stmt_list
+ {
+ result = SubStatementNode.new( val[0][1], val[1] )
+ }
+
+or_stmt : OR stmt_list
+ {
+ result = SubStatementNode.new( val[0][1], val[1] )
+ }
+
+primary : IDENT
+ {
+ result = LiteralNode.new( val[0][1] )
+ }
+ | STRING_LITERAL
+ {
+ result = LiteralNode.new( val[0][1] )
+ }
+ | AND
+ {
+ result = LiteralNode.new( val[0][1] )
+ }
+ | OR
+ {
+ result = LiteralNode.new( val[0][1] )
+ }
+ | SPACES
+ {
+ result = WhiteSpaceNode.new( val[0][1], @preserve_space )
+ }
+ | COMMA
+ {
+ result = LiteralNode.new( val[0][1] )
+ }
+ | LPAREN
+ {
+ result = LiteralNode.new( val[0][1] )
+ }
+ | RPAREN
+ {
+ result = LiteralNode.new( val[0][1] )
+ }
+ | QUESTION
+ {
+ @num_questions += 1
+ result = QuestionNode.new( @num_questions )
+ }
+ | ACTUAL_COMMENT
+ {
+ result = ActualCommentNode.new( val[0][1] , val[0][2] )
+ }
+ | bind_var
+ | embed_var
+
+bind_var : BIND_VARIABLE STRING_LITERAL
+ {
+ result = BindVariableNode.new( val[0][1] )
+ }
+ | BIND_VARIABLE SPACES STRING_LITERAL
+ {
+ result = BindVariableNode.new( val[0][1] )
+ }
+ | BIND_VARIABLE IDENT
+ {
+ result = BindVariableNode.new( val[0][1] )
+ }
+ | BIND_VARIABLE SPACES IDENT
+ {
+ result = BindVariableNode.new( val[0][1] )
+ }
+ | PAREN_BIND_VARIABLE
+ {
+ result = ParenBindVariableNode.new( val[0][1] )
+ }
+
+embed_var : EMBED_VARIABLE IDENT
+ {
+ result = EmbedVariableNode.new( val[0][1] )
+ }
+ | EMBED_VARIABLE SPACES IDENT
+ {
+ result = EmbedVariableNode.new( val[0][1] )
+ }
+
+end
+
+
+---- inner
+
+require 'strscan'
+
+def initialize(opts={})
+ opts = {
+ :debug => false,
+ :preserve_space => true,
+ :preserve_comment => false
+ }.merge(opts)
+ @yydebug = opts[:debug]
+ @preserve_space = opts[:preserve_space]
+ @preserve_comment = opts[:preserve_comment]
+ @num_questions = 0
+end
+
+
+PAREN_EXAMPLE = '\([^\)]+\)'
+BEGIN_BIND_VARIABLE = '(\/|\#)\*([^\*]+)\*\1'
+BIND_VARIABLE_PATTERN = /\A#{BEGIN_BIND_VARIABLE}\s*/
+PAREN_BIND_VARIABLE_PATTERN = /\A#{BEGIN_BIND_VARIABLE}\s*#{PAREN_EXAMPLE}/
+EMBED_VARIABLE_PATTERN = /\A(\/|\#)\*\$([^\*]+)\*\1\s*/
+
+CONDITIONAL_PATTERN = /\A(\/|\#)\*(IF)\s+([^\*]+)\s*\*\1/
+BEGIN_END_PATTERN = /\A(\/|\#)\*(BEGIN|END)\s*\*\1/
+STRING_LITERAL_PATTERN = /\A(\'(?:[^\']+|\'\')*\')/ ## quoted string
+SPLIT_TOKEN_PATTERN = /\A(\S+?)(?=\s*(?:(?:\/|\#)\*|-{2,}|\(|\)|\,))/ ## stop on delimiters --,/*,#*,',',(,)
+LITERAL_PATTERN = /\A([^;\s]+)/
+SPACES_PATTERN = /\A(\s+)/
+QUESTION_PATTERN = /\A\?/
+COMMA_PATTERN = /\A\,/
+LPAREN_PATTERN = /\A\(/
+RPAREN_PATTERN = /\A\)/
+ACTUAL_COMMENT_PATTERN = /\A(\/|\#)\*(\s{1,}(?:.*?))\*\1/m ## start with spaces
+SEMICOLON_AT_INPUT_END_PATTERN = /\A\;\s*\Z/
+UNMATCHED_COMMENT_START_PATTERN = /\A(?:(?:\/|\#)\*)/
+
+#TODO: remove trailing spaces for S2Dao compatibility, but this spec sometimes causes SQL bugs...
+ELSE_PATTERN = /\A\-{2,}\s*ELSE\s*/
+AND_PATTERN = /\A(\ *AND)\b/i
+OR_PATTERN = /\A(\ *OR)\b/i
+
+
+def parse( io )
+ @q = []
+ io.each_line(nil) do |whole|
+ @s = StringScanner.new(whole)
+ end
+ scan_str
+
+ # @q.push [ false, nil ]
+ @q.push [ false, [@s.pos, nil] ]
+
+ ## call racc's private parse method
+ do_parse
+end
+
+
+## called by racc
+def next_token
+ @q.shift
+end
+
+
+def scan_str
+ until @s.eos? do
+ case
+ when @s.scan(AND_PATTERN)
+ @q.push [ :AND, [@s.pos, @s[1]] ]
+ when @s.scan(OR_PATTERN)
+ @q.push [ :OR, [@s.pos, @s[1]] ]
+ when @s.scan(SPACES_PATTERN)
+ @q.push [ :SPACES, [@s.pos, @s[1]] ]
+ when @s.scan(QUESTION_PATTERN)
+ @q.push [ :QUESTION, [@s.pos, nil] ]
+ when @s.scan(COMMA_PATTERN)
+ @q.push [ :COMMA, [@s.pos, ','] ]
+ when @s.scan(LPAREN_PATTERN)
+ @q.push [ :LPAREN, [@s.pos, '('] ]
+ when @s.scan(RPAREN_PATTERN)
+ @q.push [ :RPAREN, [@s.pos, ')'] ]
+ when @s.scan(ELSE_PATTERN)
+ @q.push [ :ELSE, [@s.pos, nil] ]
+ when @s.scan(ACTUAL_COMMENT_PATTERN)
+ @q.push [ :ACTUAL_COMMENT, [@s.pos, @s[1], @s[2]] ] if @preserve_comment
+ when @s.scan(BEGIN_END_PATTERN)
+ @q.push [ @s[2].intern, [@s.pos, nil] ]
+ when @s.scan(CONDITIONAL_PATTERN)
+ @q.push [ @s[2].intern, [@s.pos, @s[3]] ]
+ when @s.scan(EMBED_VARIABLE_PATTERN)
+ @q.push [ :EMBED_VARIABLE, [@s.pos, @s[2]] ]
+ when @s.scan(PAREN_BIND_VARIABLE_PATTERN)
+ @q.push [ :PAREN_BIND_VARIABLE, [@s.pos, @s[2]] ]
+ when @s.scan(BIND_VARIABLE_PATTERN)
+ @q.push [ :BIND_VARIABLE, [@s.pos, @s[2]] ]
+ when @s.scan(STRING_LITERAL_PATTERN)
+ @q.push [ :STRING_LITERAL, [@s.pos, @s[1]] ]
+ when @s.scan(SPLIT_TOKEN_PATTERN)
+ @q.push [ :IDENT, [@s.pos, @s[1]] ]
+ when @s.scan(UNMATCHED_COMMENT_START_PATTERN) ## unmatched comment start, '/*','#*'
+ raise Racc::ParseError, "unmatched comment. line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
+ when @s.scan(LITERAL_PATTERN) ## other string token
+ @q.push [ :IDENT, [@s.pos, @s[1]] ]
+ when @s.scan(SEMICOLON_AT_INPUT_END_PATTERN)
+ #drop semicolon at input end
+ else
+ raise Racc::ParseError, "syntax error at or near line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
+ end
+ end
+end
+
+
+## override racc's default on_error method
+def on_error(t, v, vstack)
+ ## cursor in value-stack is an array of two items,
+ ## that have position value as 0th item. like [731, "ctx[:limit] "]
+ cursor = vstack.find do |tokens|
+ tokens.size == 2 and tokens[0].kind_of?(Fixnum)
+ end
+ pos = cursor[0]
+ line = line_no(pos)
+ rest = @s.string[pos .. -1]
+ raise Racc::ParseError, "syntax error at or near line:[#{line}], str:[#{rest}]"
+end
+
+
+def line_no(pos)
+ lines = 0
+ scanned = @s.string[0..(pos)]
+ scanned.each_line { lines += 1 }
+ lines
+end
diff --git a/test/racc/assets/unterm.y b/test/racc/assets/unterm.y
new file mode 100644
index 0000000000..518acc7f31
--- /dev/null
+++ b/test/racc/assets/unterm.y
@@ -0,0 +1,5 @@
+# unterminated action
+
+class A
+rule
+ a: A {
diff --git a/test/racc/assets/useless.y b/test/racc/assets/useless.y
new file mode 100644
index 0000000000..3f172e341c
--- /dev/null
+++ b/test/racc/assets/useless.y
@@ -0,0 +1,12 @@
+
+
+class A
+token A B C X
+rule
+
+targ : A list B
+ | A B C
+
+list: list X
+
+end
diff --git a/test/racc/assets/yyerr.y b/test/racc/assets/yyerr.y
new file mode 100644
index 0000000000..9faae89a79
--- /dev/null
+++ b/test/racc/assets/yyerr.y
@@ -0,0 +1,46 @@
+#
+# yyerror/yyerrok/yyaccept test
+#
+
+class A
+rule
+
+target: a b c
+
+a:
+ {
+ yyerror
+ raise ArgumentError, "yyerror failed"
+ }
+ | error
+
+b:
+ {
+ yyerrok
+ }
+
+c:
+ {
+ yyaccept
+ raise ArgumentError, "yyaccept failed"
+ }
+
+end
+
+---- inner
+
+ def parse
+ do_parse
+ end
+
+ def next_token
+ [false, '$end']
+ end
+
+ def on_error( *args )
+ $stderr.puts "on_error called: args=#{args.inspect}"
+ end
+
+---- footer
+
+A.new.parse