# 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 ' 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