diff options
Diffstat (limited to 'test/racc/assets')
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 |