blob: 24972af0e7e1930e78bf343707acdfbebd2c50d1 [file] [log] [blame]
%{
/***** BEGIN LICENSE BLOCK *****
* Version: CPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Common Public
* License Version 1.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.eclipse.org/legal/cpl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
* Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
* Copyright (C) 2001-2004 Stefan Matthias Aust <sma@3plus4.de>
* Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
* Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
* Copyright (C) 2004-2006 Thomas E Enebo <enebo@acm.org>
* Copyright (C) 2004 Charles O Nutter <headius@headius.com>
* Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the CPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the CPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.parser;
import java.io.IOException;
import org.jruby.ast.AliasNode;
import org.jruby.ast.ArgsNode;
import org.jruby.ast.ArgumentNode;
import org.jruby.ast.ArrayNode;
import org.jruby.ast.AssignableNode;
import org.jruby.ast.BackRefNode;
import org.jruby.ast.BeginNode;
import org.jruby.ast.BlockAcceptingNode;
import org.jruby.ast.BlockArgNode;
import org.jruby.ast.BlockNode;
import org.jruby.ast.BlockPassNode;
import org.jruby.ast.BreakNode;
import org.jruby.ast.CallNode;
import org.jruby.ast.CaseNode;
import org.jruby.ast.ClassNode;
import org.jruby.ast.ClassVarNode;
import org.jruby.ast.Colon2Node;
import org.jruby.ast.Colon3Node;
import org.jruby.ast.ConstDeclNode;
import org.jruby.ast.DRegexpNode;
import org.jruby.ast.DStrNode;
import org.jruby.ast.DSymbolNode;
import org.jruby.ast.DXStrNode;
import org.jruby.ast.DefinedNode;
import org.jruby.ast.DefnNode;
import org.jruby.ast.DefsNode;
import org.jruby.ast.DotNode;
import org.jruby.ast.EnsureNode;
import org.jruby.ast.EvStrNode;
import org.jruby.ast.FCallNode;
import org.jruby.ast.FloatNode;
import org.jruby.ast.ForNode;
import org.jruby.ast.GlobalVarNode;
import org.jruby.ast.HashNode;
import org.jruby.ast.IfNode;
import org.jruby.ast.InstVarNode;
import org.jruby.ast.IterNode;
import org.jruby.ast.ListNode;
import org.jruby.ast.ModuleNode;
import org.jruby.ast.MultipleAsgnNode;
import org.jruby.ast.NewlineNode;
import org.jruby.ast.NextNode;
import org.jruby.ast.NilNode;
import org.jruby.ast.Node;
import org.jruby.ast.NotNode;
import org.jruby.ast.OpAsgnAndNode;
import org.jruby.ast.OpAsgnNode;
import org.jruby.ast.OpAsgnOrNode;
import org.jruby.ast.OpElementAsgnNode;
import org.jruby.ast.PostExeNode;
import org.jruby.ast.RedoNode;
import org.jruby.ast.RegexpNode;
import org.jruby.ast.RescueBodyNode;
import org.jruby.ast.RescueNode;
import org.jruby.ast.RetryNode;
import org.jruby.ast.ReturnNode;
import org.jruby.ast.SClassNode;
import org.jruby.ast.SValueNode;
import org.jruby.ast.SelfNode;
import org.jruby.ast.SplatNode;
import org.jruby.ast.StarNode;
import org.jruby.ast.StrNode;
import org.jruby.ast.SymbolNode;
import org.jruby.ast.ToAryNode;
import org.jruby.ast.UndefNode;
import org.jruby.ast.UntilNode;
import org.jruby.ast.VAliasNode;
import org.jruby.ast.WhenNode;
import org.jruby.ast.WhileNode;
import org.jruby.ast.XStrNode;
import org.jruby.ast.YieldNode;
import org.jruby.ast.ZArrayNode;
import org.jruby.ast.ZSuperNode;
import org.jruby.ast.ZeroArgNode;
import org.jruby.ast.types.ILiteralNode;
import org.jruby.ast.types.INameNode;
import org.jruby.common.IRubyWarnings;
import org.jruby.lexer.yacc.ISourcePosition;
import org.jruby.lexer.yacc.ISourcePositionHolder;
import org.jruby.lexer.yacc.LexState;
import org.jruby.lexer.yacc.LexerSource;
import org.jruby.lexer.yacc.RubyYaccLexer;
import org.jruby.lexer.yacc.StrTerm;
import org.jruby.lexer.yacc.SyntaxException;
import org.jruby.lexer.yacc.Token;
import org.jruby.runtime.Visibility;
import org.jruby.util.IdUtil;
import org.jruby.util.ByteList;
public class DefaultRubyParser {
private ParserSupport support;
private RubyYaccLexer lexer;
private IRubyWarnings warnings;
public DefaultRubyParser() {
support = new ParserSupport();
lexer = new RubyYaccLexer();
lexer.setParserSupport(support);
}
public void setWarnings(IRubyWarnings warnings) {
this.warnings = warnings;
support.setWarnings(warnings);
lexer.setWarnings(warnings);
}
%}
%token <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__
%token <Token> tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR
%token <Node> tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER
%token <FloatNode> tFLOAT
%token <RegexpNode> tREGEXP_END
%type <Node> singleton strings string string1 xstring regexp
%type <Node> string_contents xstring_contents string_content method_call
%type <Node> words qwords word literal numeric dsym cpath command_call
%type <Node> compstmt bodystmt stmts stmt expr arg primary command
%type <Node> expr_value primary_value opt_else cases if_tail exc_var
%type <Node> call_args call_args2 open_args opt_ensure paren_args superclass
%type <Node> command_args var_ref opt_paren_args block_call block_command
%type <Node> f_arglist f_args f_opt undef_list string_dvar backref
%type <Node> mrhs mlhs_item mlhs_node arg_value case_body exc_list aref_args
%type <Node> block_var opt_block_var lhs none
%type <ListNode> qword_list word_list f_arg f_optarg
%type <ListNode> args when_args mlhs_head assocs assoc assoc_list
%type <BlockPassNode> opt_block_arg block_arg none_block_pass
%type <BlockArgNode> opt_f_block_arg f_block_arg
%type <IterNode> brace_block do_block cmd_brace_block
%type <MultipleAsgnNode> mlhs mlhs_basic mlhs_entry
%type <RescueBodyNode> opt_rescue
%type <AssignableNode> var_lhs
%type <Token> variable
%type <Token> fitem sym symbol operation operation2 operation3 cname fname op
%type <Token> f_norm_arg f_rest_arg dot_or_colon restarg_mark blkarg_mark
%token <Token> tUPLUS /* unary+ */
%token <Token> tUMINUS /* unary- */
%token <Token> tUMINUS_NUM /* unary- */
%token <Token> tPOW /* ** */
%token <Token> tCMP /* <=> */
%token <Token> tEQ /* == */
%token <Token> tEQQ /* === */
%token <Token> tNEQ /* != */
%token <Token> tGEQ /* >= */
%token <Token> tLEQ /* <= */
%token <Token> tANDOP tOROP /* && and || */
%token <Token> tMATCH tNMATCH /* =~ and !~ */
%token <Token> tDOT /* Is just '.' in ruby and not a token */
%token <Token> tDOT2 tDOT3 /* .. and ... */
%token <Token> tAREF tASET /* [] and []= */
%token <Token> tLSHFT tRSHFT /* << and >> */
%token <Token> tCOLON2 /* :: */
%token <Token> tCOLON3 /* :: at EXPR_BEG */
%token <Token> tOP_ASGN /* +=, -= etc. */
%token <Token> tASSOC /* => */
%token <Token> tLPAREN /* ( */
%token <Token> tLPAREN2 /* ( Is just '(' in ruby and not a token */
%token <Token> tRPAREN /* ) */
%token <Token> tLPAREN_ARG /* ( */
%token <Token> tLBRACK /* [ */
%token <Token> tRBRACK /* ] */
%token <Token> tLBRACE /* { */
%token <Token> tLBRACE_ARG /* { */
%token <Token> tSTAR /* * */
%token <Token> tSTAR2 /* * Is just '*' in ruby and not a token */
%token <Token> tAMPER /* & */
%token <Token> tAMPER2 /* & Is just '&' in ruby and not a token */
%token <Token> tTILDE /* ` is just '`' in ruby and not a token */
%token <Token> tPERCENT /* % is just '%' in ruby and not a token */
%token <Token> tDIVIDE /* / is just '/' in ruby and not a token */
%token <Token> tPLUS /* + is just '+' in ruby and not a token */
%token <Token> tMINUS /* - is just '-' in ruby and not a token */
%token <Token> tLT /* < is just '<' in ruby and not a token */
%token <Token> tGT /* > is just '>' in ruby and not a token */
%token <Token> tPIPE /* | is just '|' in ruby and not a token */
%token <Token> tBANG /* ! is just '!' in ruby and not a token */
%token <Token> tCARET /* ^ is just '^' in ruby and not a token */
%token <Token> tLCURLY /* { is just '{' in ruby and not a token */
%token <Token> tRCURLY /* } is just '}' in ruby and not a token */
%token <Token> tBACK_REF2 /* { is just '`' in ruby and not a token */
%token <Token> tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tWORDS_BEG tQWORDS_BEG
%token <Token> tSTRING_DBEG tSTRING_DVAR tSTRING_END
/*
* precedence table
*/
%nonassoc tLOWEST
%nonassoc tLBRACE_ARG
%nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
%left kOR kAND
%right kNOT
%nonassoc kDEFINED
%right '=' tOP_ASGN
%left kRESCUE_MOD
%right '?' ':'
%nonassoc tDOT2 tDOT3
%left tOROP
%left tANDOP
%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
%left tGT tGEQ tLT tLEQ
%left tPIPE tCARET
%left tAMPER2
%left tLSHFT tRSHFT
%left tPLUS tMINUS
%left tSTAR2 tDIVIDE tPERCENT
%right tUMINUS_NUM tUMINUS
%right tPOW
%right tBANG tTILDE tUPLUS
%token <Integer> tLAST_TOKEN
%%
program : {
lexer.setState(LexState.EXPR_BEG);
support.initTopLocalVariables();
} compstmt {
if ($2 != null) {
/* last expression should not be void */
if ($2 instanceof BlockNode) {
support.checkUselessStatement($<BlockNode>2.getLast());
} else {
support.checkUselessStatement($2);
}
}
support.getResult().setAST(support.addRootNode($2, getPosition($2)));
}
bodystmt : compstmt opt_rescue opt_else opt_ensure {
Node node = $1;
if ($2 != null) {
node = new RescueNode(getPosition($1, true), $1, $2, $3);
} else if ($3 != null) {
warnings.warn(getPosition($1), "else without rescue is useless");
node = support.appendToBlock($1, $3);
}
if ($4 != null) {
node = new EnsureNode(getPosition($1), node, $4);
}
$$ = node;
}
compstmt : stmts opt_terms {
if ($1 instanceof BlockNode) {
support.checkUselessStatements($<BlockNode>1);
}
$$ = $1;
}
stmts : none
| stmt {
$$ = support.newline_node($1, getPosition($1, true));
}
| stmts terms stmt {
$$ = support.appendToBlock($1, support.newline_node($3, getPosition($3, true)));
}
| error stmt {
$$ = $2;
}
stmt : kALIAS fitem {
lexer.setState(LexState.EXPR_FNAME);
} fitem {
$$ = new AliasNode(support.union($1, $4), (String) $2.getValue(), (String) $4.getValue());
}
| kALIAS tGVAR tGVAR {
$$ = new VAliasNode(getPosition($1), (String) $2.getValue(), (String) $3.getValue());
}
| kALIAS tGVAR tBACK_REF {
$$ = new VAliasNode(getPosition($1), (String) $2.getValue(), "$" + $<BackRefNode>3.getType()); // XXX
}
| kALIAS tGVAR tNTH_REF {
yyerror("can't make alias for the number variables");
}
| kUNDEF undef_list {
$$ = $2;
}
| stmt kIF_MOD expr_value {
$$ = new IfNode(support.union($1, $3), support.getConditionNode($3), $1, null);
}
| stmt kUNLESS_MOD expr_value {
$$ = new IfNode(support.union($1, $3), support.getConditionNode($3), null, $1);
}
| stmt kWHILE_MOD expr_value {
if ($1 != null && $1 instanceof BeginNode) {
$$ = new WhileNode(getPosition($1), support.getConditionNode($3), $<BeginNode>1.getBodyNode(), false);
} else {
$$ = new WhileNode(getPosition($1), support.getConditionNode($3), $1, true);
}
}
| stmt kUNTIL_MOD expr_value {
if ($1 != null && $1 instanceof BeginNode) {
$$ = new UntilNode(getPosition($1), support.getConditionNode($3), $<BeginNode>1.getBodyNode());
} else {
$$ = new UntilNode(getPosition($1), support.getConditionNode($3), $1);
}
}
| stmt kRESCUE_MOD stmt {
$$ = new RescueNode(getPosition($1), $1, new RescueBodyNode(getPosition($1), null,$3, null), null);
}
| klBEGIN {
if (support.isInDef() || support.isInSingle()) {
yyerror("BEGIN in method");
}
support.pushLocalScope();
} tLCURLY compstmt tRCURLY {
support.getResult().addBeginNode(support.getCurrentScope(), $4);
support.popCurrentScope();
$$ = null; //XXX 0;
}
| klEND tLCURLY compstmt tRCURLY {
if (support.isInDef() || support.isInSingle()) {
yyerror("END in method; use at_exit");
}
$$ = new PostExeNode(getPosition($1), $3);
}
| lhs '=' command_call {
support.checkExpression($3);
$$ = support.node_assign($1, $3);
}
| mlhs '=' command_call {
support.checkExpression($3);
if ($1.getHeadNode() != null) {
$1.setValueNode(new ToAryNode(getPosition($1), $3));
} else {
$1.setValueNode(new ArrayNode(getPosition($1), $3));
}
$$ = $1;
}
| var_lhs tOP_ASGN command_call {
support.checkExpression($3);
String name = $<INameNode>1.getName();
String asgnOp = (String) $2.getValue();
if (asgnOp.equals("||")) {
$1.setValueNode($3);
$$ = new OpAsgnOrNode(support.union($1, $3), support.gettable2(name, $1.getPosition()), $1);
} else if (asgnOp.equals("&&")) {
$1.setValueNode($3);
$$ = new OpAsgnAndNode(support.union($1, $3), support.gettable2(name, $1.getPosition()), $1);
} else {
$1.setValueNode(support.getOperatorCallNode(support.gettable2(name, $1.getPosition()), asgnOp, $3));
$1.setPosition(support.union($1, $3));
$$ = $1;
}
}
| primary_value '[' aref_args tRBRACK tOP_ASGN command_call {
$$ = new OpElementAsgnNode(getPosition($1), $1, (String) $5.getValue(), $3, $6);
}
| primary_value tDOT tIDENTIFIER tOP_ASGN command_call {
$$ = new OpAsgnNode(getPosition($1), $1, $5, (String) $3.getValue(), (String) $4.getValue());
}
| primary_value tDOT tCONSTANT tOP_ASGN command_call {
$$ = new OpAsgnNode(getPosition($1), $1, $5, (String) $3.getValue(), (String) $4.getValue());
}
| primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call {
$$ = new OpAsgnNode(getPosition($1), $1, $5, (String) $3.getValue(), (String) $4.getValue());
}
| backref tOP_ASGN command_call {
support.backrefAssignError($1);
}
| lhs '=' mrhs {
$$ = support.node_assign($1, new SValueNode(getPosition($1), $3));
}
| mlhs '=' arg_value {
if ($1.getHeadNode() != null) {
$1.setValueNode(new ToAryNode(getPosition($1), $3));
} else {
$1.setValueNode(new ArrayNode(getPosition($1), $3));
}
$$ = $1;
}
| mlhs '=' mrhs {
$<AssignableNode>1.setValueNode($3);
$$ = $1;
$1.setPosition(support.union($1, $3));
}
| expr
expr : command_call
| expr kAND expr {
$$ = support.newAndNode($1, $3);
}
| expr kOR expr {
$$ = support.newOrNode($1, $3);
}
| kNOT expr {
$$ = new NotNode(support.union($1, $2), support.getConditionNode($2));
}
| tBANG command_call {
$$ = new NotNode(support.union($1, $2), support.getConditionNode($2));
}
| arg
expr_value : expr {
support.checkExpression($1);
}
command_call : command
| block_command
| kRETURN call_args {
$$ = new ReturnNode(support.union($1, $2), support.ret_args($2, getPosition($1)));
}
| kBREAK call_args {
$$ = new BreakNode(support.union($1, $2), support.ret_args($2, getPosition($1)));
}
| kNEXT call_args {
$$ = new NextNode(support.union($1, $2), support.ret_args($2, getPosition($1)));
}
block_command : block_call
| block_call tDOT operation2 command_args {
$$ = support.new_call($1, $3, $4, null);
}
| block_call tCOLON2 operation2 command_args {
$$ = support.new_call($1, $3, $4, null);
}
cmd_brace_block : tLBRACE_ARG {
support.pushBlockScope();
} opt_block_var compstmt tRCURLY {
$$ = new IterNode(getPosition($1), $3, support.getCurrentScope(), $4);
support.popCurrentScope();
}
command : operation command_args %prec tLOWEST {
$$ = support.new_fcall($1, $2, null);
}
| operation command_args cmd_brace_block {
$$ = support.new_fcall($1, $2, $3);
}
| primary_value tDOT operation2 command_args %prec tLOWEST {
$$ = support.new_call($1, $3, $4, null);
}
| primary_value tDOT operation2 command_args cmd_brace_block {
$$ = support.new_call($1, $3, $4, $5);
}
| primary_value tCOLON2 operation2 command_args %prec tLOWEST {
$$ = support.new_call($1, $3, $4, null);
}
| primary_value tCOLON2 operation2 command_args cmd_brace_block {
$$ = support.new_call($1, $3, $4, $5);
}
| kSUPER command_args {
$$ = support.new_super($2, $1); // .setPosFrom($2);
}
| kYIELD command_args {
$$ = support.new_yield(getPosition($1), $2);
}
mlhs : mlhs_basic
| tLPAREN mlhs_entry tRPAREN {
$$ = $2;
}
mlhs_entry : mlhs_basic
| tLPAREN mlhs_entry tRPAREN {
$$ = new MultipleAsgnNode(getPosition($1), new ArrayNode(getPosition($1), $2), null);
}
mlhs_basic : mlhs_head {
$$ = new MultipleAsgnNode(getPosition($1), $1, null);
}
| mlhs_head mlhs_item {
//mirko: check
$$ = new MultipleAsgnNode(support.union($<Node>1, $<Node>2), $1.add($2), null);
$<Node>1.setPosition(support.union($<Node>1, $<Node>2));
}
| mlhs_head tSTAR mlhs_node {
$$ = new MultipleAsgnNode(getPosition($1), $1, $3);
}
| mlhs_head tSTAR {
$$ = new MultipleAsgnNode(getPosition($1), $1, new StarNode(getPosition(null)));
}
| tSTAR mlhs_node {
$$ = new MultipleAsgnNode(getPosition($1), null, $2);
}
| tSTAR {
$$ = new MultipleAsgnNode(getPosition($1), null, new StarNode(getPosition(null)));
}
mlhs_item : mlhs_node
| tLPAREN mlhs_entry tRPAREN {
$$ = $2;
}
mlhs_head : mlhs_item ',' {
$$ = new ArrayNode($1.getPosition(), $1);
}
| mlhs_head mlhs_item ',' {
$$ = $1.add($2);
}
mlhs_node : variable {
$$ = support.assignable($1, null);
}
| primary_value '[' aref_args tRBRACK {
$$ = support.aryset($1, $3);
}
| primary_value tDOT tIDENTIFIER {
$$ = support.attrset($1, (String) $3.getValue());
}
| primary_value tCOLON2 tIDENTIFIER {
$$ = support.attrset($1, (String) $3.getValue());
}
| primary_value tDOT tCONSTANT {
$$ = support.attrset($1, (String) $3.getValue());
}
| primary_value tCOLON2 tCONSTANT {
if (support.isInDef() || support.isInSingle()) {
yyerror("dynamic constant assignment");
}
ISourcePosition position = support.union($1, $3);
$$ = new ConstDeclNode(position, null, new Colon2Node(position, $1, (String) $3.getValue()), null);
}
| tCOLON3 tCONSTANT {
if (support.isInDef() || support.isInSingle()) {
yyerror("dynamic constant assignment");
}
ISourcePosition position = support.union($1, $2);
$$ = new ConstDeclNode(position, null, new Colon3Node(position, (String) $2.getValue()), null);
}
| backref {
support.backrefAssignError($1);
}
lhs : variable {
$$ = support.assignable($1, null);
}
| primary_value '[' aref_args tRBRACK {
$$ = support.aryset($1, $3);
}
| primary_value tDOT tIDENTIFIER {
$$ = support.attrset($1, (String) $3.getValue());
}
| primary_value tCOLON2 tIDENTIFIER {
$$ = support.attrset($1, (String) $3.getValue());
}
| primary_value tDOT tCONSTANT {
$$ = support.attrset($1, (String) $3.getValue());
}
| primary_value tCOLON2 tCONSTANT {
if (support.isInDef() || support.isInSingle()) {
yyerror("dynamic constant assignment");
}
ISourcePosition position = support.union($1, $3);
$$ = new ConstDeclNode(position, null, new Colon2Node(position, $1, (String) $3.getValue()), null);
}
| tCOLON3 tCONSTANT {
if (support.isInDef() || support.isInSingle()) {
yyerror("dynamic constant assignment");
}
ISourcePosition position = support.union($1, $2);
$$ = new ConstDeclNode(position, null, new Colon3Node(position, (String) $2.getValue()), null);
}
| backref {
support.backrefAssignError($1);
}
cname : tIDENTIFIER {
yyerror("class/module name must be CONSTANT");
}
| tCONSTANT
cpath : tCOLON3 cname {
$$ = new Colon3Node(support.union($1, $2), (String) $2.getValue());
}
| cname {
$$ = new Colon2Node($1.getPosition(), null, (String) $1.getValue());
}
| primary_value tCOLON2 cname {
$$ = new Colon2Node(support.union($1, $3), $1, (String) $3.getValue());
}
// Token:fname - A function name [!null]
fname : tIDENTIFIER | tCONSTANT | tFID
| op {
lexer.setState(LexState.EXPR_END);
$$ = $1;
}
// FIXME: reswords is really Keyword which is not a Token...This should bomb
| reswords {
lexer.setState(LexState.EXPR_END);
$$ = $<>1;
}
fitem : fname | symbol
undef_list : fitem {
$$ = new UndefNode(getPosition($1), (String) $1.getValue());
}
| undef_list ',' {
lexer.setState(LexState.EXPR_FNAME);
} fitem {
$$ = support.appendToBlock($1, new UndefNode(getPosition($1), (String) $4.getValue()));
}
// Token:op - inline operations [!null]
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
// Keyword:reswords - reserved words [!null]
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
arg : lhs '=' arg {
$$ = support.node_assign($1, $3);
// FIXME: Consider fixing node_assign itself rather than single case
$<Node>$.setPosition(support.union($1, $3));
}
| lhs '=' arg kRESCUE_MOD arg {
ISourcePosition position = support.union($4, $5);
$$ = support.node_assign($1, new RescueNode(position, $3, new RescueBodyNode(position, null, $5, null), null));
}
| var_lhs tOP_ASGN arg {
support.checkExpression($3);
String name = $<INameNode>1.getName();
String asgnOp = (String) $2.getValue();
if (asgnOp.equals("||")) {
$1.setValueNode($3);
$$ = new OpAsgnOrNode(support.union($1, $3), support.gettable2(name, $1.getPosition()), $1);
} else if (asgnOp.equals("&&")) {
$1.setValueNode($3);
$$ = new OpAsgnAndNode(support.union($1, $3), support.gettable2(name, $1.getPosition()), $1);
} else {
$1.setValueNode(support.getOperatorCallNode(support.gettable2(name, $1.getPosition()), asgnOp, $3));
$1.setPosition(support.union($1, $3));
$$ = $1;
}
}
| primary_value '[' aref_args tRBRACK tOP_ASGN arg {
$$ = new OpElementAsgnNode(getPosition($1), $1, (String) $5.getValue(), $3, $6);
}
| primary_value tDOT tIDENTIFIER tOP_ASGN arg {
$$ = new OpAsgnNode(getPosition($1), $1, $5, (String) $3.getValue(), (String) $4.getValue());
}
| primary_value tDOT tCONSTANT tOP_ASGN arg {
$$ = new OpAsgnNode(getPosition($1), $1, $5, (String) $3.getValue(), (String) $4.getValue());
}
| primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg {
$$ = new OpAsgnNode(getPosition($1), $1, $5, (String) $3.getValue(), (String) $4.getValue());
}
| primary_value tCOLON2 tCONSTANT tOP_ASGN arg {
yyerror("constant re-assignment");
}
| tCOLON3 tCONSTANT tOP_ASGN arg {
yyerror("constant re-assignment");
}
| backref tOP_ASGN arg {
support.backrefAssignError($1);
}
| arg tDOT2 arg {
support.checkExpression($1);
support.checkExpression($3);
$$ = new DotNode(support.union($1, $3), $1, $3, false);
}
| arg tDOT3 arg {
support.checkExpression($1);
support.checkExpression($3);
$$ = new DotNode(support.union($1, $3), $1, $3, true);
}
| arg tPLUS arg {
$$ = support.getOperatorCallNode($1, "+", $3, getPosition(null));
}
| arg tMINUS arg {
$$ = support.getOperatorCallNode($1, "-", $3, getPosition(null));
}
| arg tSTAR2 arg {
$$ = support.getOperatorCallNode($1, "*", $3, getPosition(null));
}
| arg tDIVIDE arg {
$$ = support.getOperatorCallNode($1, "/", $3, getPosition(null));
}
| arg tPERCENT arg {
$$ = support.getOperatorCallNode($1, "%", $3, getPosition(null));
}
| arg tPOW arg {
$$ = support.getOperatorCallNode($1, "**", $3, getPosition(null));
}
| tUMINUS_NUM tINTEGER tPOW arg {
$$ = support.getOperatorCallNode(support.getOperatorCallNode($2, "**", $4, getPosition(null)), "-@");
}
| tUMINUS_NUM tFLOAT tPOW arg {
$$ = support.getOperatorCallNode(support.getOperatorCallNode($2, "**", $4, getPosition(null)), "-@");
}
| tUPLUS arg {
if ($2 != null && $2 instanceof ILiteralNode) {
$$ = $2;
} else {
$$ = support.getOperatorCallNode($2, "+@");
}
}
| tUMINUS arg {
$$ = support.getOperatorCallNode($2, "-@");
}
| arg tPIPE arg {
$$ = support.getOperatorCallNode($1, "|", $3, getPosition(null));
}
| arg tCARET arg {
$$ = support.getOperatorCallNode($1, "^", $3, getPosition(null));
}
| arg tAMPER2 arg {
$$ = support.getOperatorCallNode($1, "&", $3, getPosition(null));
}
| arg tCMP arg {
$$ = support.getOperatorCallNode($1, "<=>", $3, getPosition(null));
}
| arg tGT arg {
$$ = support.getOperatorCallNode($1, ">", $3, getPosition(null));
}
| arg tGEQ arg {
$$ = support.getOperatorCallNode($1, ">=", $3, getPosition(null));
}
| arg tLT arg {
$$ = support.getOperatorCallNode($1, "<", $3, getPosition(null));
}
| arg tLEQ arg {
$$ = support.getOperatorCallNode($1, "<=", $3, getPosition(null));
}
| arg tEQ arg {
$$ = support.getOperatorCallNode($1, "==", $3, getPosition(null));
}
| arg tEQQ arg {
$$ = support.getOperatorCallNode($1, "===", $3, getPosition(null));
}
| arg tNEQ arg {
$$ = new NotNode(support.union($1, $3), support.getOperatorCallNode($1, "==", $3, getPosition(null)));
}
| arg tMATCH arg {
$$ = support.getMatchNode($1, $3);
}
| arg tNMATCH arg {
$$ = new NotNode(support.union($1, $3), support.getMatchNode($1, $3));
}
| tBANG arg {
$$ = new NotNode(support.union($1, $2), support.getConditionNode($2));
}
| tTILDE arg {
$$ = support.getOperatorCallNode($2, "~");
}
| arg tLSHFT arg {
$$ = support.getOperatorCallNode($1, "<<", $3, getPosition(null));
}
| arg tRSHFT arg {
$$ = support.getOperatorCallNode($1, ">>", $3, getPosition(null));
}
| arg tANDOP arg {
$$ = support.newAndNode($1, $3);
}
| arg tOROP arg {
$$ = support.newOrNode($1, $3);
}
| kDEFINED opt_nl arg {
$$ = new DefinedNode(getPosition($1), $3);
}
| arg '?' arg ':' arg {
$$ = new IfNode(getPosition($1), support.getConditionNode($1), $3, $5);
}
| primary {
$$ = $1;
}
arg_value : arg {
support.checkExpression($1);
$$ = $1;
}
aref_args : none
| command opt_nl {
warnings.warn(getPosition($1), "parenthesize argument(s) for future version");
$$ = new ArrayNode(getPosition($1), $1);
}
| args trailer {
$$ = $1;
}
| args ',' tSTAR arg opt_nl {
support.checkExpression($4);
$$ = support.arg_concat(getPosition($1), $1, $4);
}
| assocs trailer {
$$ = new ArrayNode(getPosition($1), new HashNode(getPosition(null), $1));
}
| tSTAR arg opt_nl {
support.checkExpression($2);
$$ = new NewlineNode(getPosition($1), new SplatNode(getPosition($1), $2));
}
paren_args : tLPAREN2 none tRPAREN {
$$ = new ArrayNode(support.union($1, $3));
}
| tLPAREN2 call_args opt_nl tRPAREN {
$$ = $2;
$<Node>$.setPosition(support.union($1, $4));
}
| tLPAREN2 block_call opt_nl tRPAREN {
warnings.warn(getPosition($1), "parenthesize argument(s) for future version");
$$ = new ArrayNode(getPosition($1), $2);
}
| tLPAREN2 args ',' block_call opt_nl tRPAREN {
warnings.warn(getPosition($1), "parenthesize argument(s) for future version");
$$ = $2.add($4);
}
opt_paren_args: none | paren_args
// Node:call_args - Arguments for a function call
call_args : command {
warnings.warn($1.getPosition(), "parenthesize argument(s) for future version");
$$ = new ArrayNode(getPosition($1), $1);
}
| args opt_block_arg {
$$ = support.arg_blk_pass($1, $2);
}
| args ',' tSTAR arg_value opt_block_arg {
$$ = support.arg_concat(getPosition($1), $1, $4);
$$ = support.arg_blk_pass($<Node>$, $5);
}
| assocs opt_block_arg {
$$ = new ArrayNode(getPosition($1), new HashNode(getPosition(null), $1));
$$ = support.arg_blk_pass((Node)$$, $2);
}
| assocs ',' tSTAR arg_value opt_block_arg {
$$ = support.arg_concat(getPosition($1), new ArrayNode(getPosition($1), new HashNode(getPosition(null), $1)), $4);
$$ = support.arg_blk_pass((Node)$$, $5);
}
| args ',' assocs opt_block_arg {
$$ = $1.add(new HashNode(getPosition(null), $3));
$$ = support.arg_blk_pass((Node)$$, $4);
}
| args ',' assocs ',' tSTAR arg opt_block_arg {
support.checkExpression($6);
$$ = support.arg_concat(getPosition($1), $1.add(new HashNode(getPosition(null), $3)), $6);
$$ = support.arg_blk_pass((Node)$$, $7);
}
| tSTAR arg_value opt_block_arg {
$$ = support.arg_blk_pass(new SplatNode(getPosition($1), $2), $3);
}
| block_arg {}
call_args2 : arg_value ',' args opt_block_arg {
$$ = support.arg_blk_pass(new ArrayNode(getPosition($1), $1).addAll($3), $4);
}
| arg_value ',' block_arg {
$$ = support.arg_blk_pass(new ArrayNode(getPosition($1), $1), $3);
}
| arg_value ',' tSTAR arg_value opt_block_arg {
$$ = support.arg_concat(getPosition($1), new ArrayNode(getPosition($1), $1), $4);
$$ = support.arg_blk_pass((Node)$$, $5);
}
| arg_value ',' args ',' tSTAR arg_value opt_block_arg {
$$ = support.arg_concat(getPosition($1), new ArrayNode(getPosition($1), $1).addAll(new HashNode(getPosition(null), $3)), $6);
$$ = support.arg_blk_pass((Node)$$, $7);
}
| assocs opt_block_arg {
$$ = new ArrayNode(getPosition($1), new HashNode(getPosition(null), $1));
$$ = support.arg_blk_pass((Node)$$, $2);
}
| assocs ',' tSTAR arg_value opt_block_arg {
$$ = support.arg_concat(getPosition($1), new ArrayNode(getPosition($1), new HashNode(getPosition(null), $1)), $4);
$$ = support.arg_blk_pass((Node)$$, $5);
}
| arg_value ',' assocs opt_block_arg {
$$ = new ArrayNode(getPosition($1), $1).add(new HashNode(getPosition(null), $3));
$$ = support.arg_blk_pass((Node)$$, $4);
}
| arg_value ',' args ',' assocs opt_block_arg {
$$ = new ArrayNode(getPosition($1), $1).addAll($3).add(new HashNode(getPosition(null), $5));
$$ = support.arg_blk_pass((Node)$$, $6);
}
| arg_value ',' assocs ',' tSTAR arg_value opt_block_arg {
$$ = support.arg_concat(getPosition($1), new ArrayNode(getPosition($1), $1).add(new HashNode(getPosition(null), $3)), $6);
$$ = support.arg_blk_pass((Node)$$, $7);
}
| arg_value ',' args ',' assocs ',' tSTAR arg_value opt_block_arg {
$$ = support.arg_concat(getPosition($1), new ArrayNode(getPosition($1), $1).addAll($3).add(new HashNode(getPosition(null), $5)), $8);
$$ = support.arg_blk_pass((Node)$$, $9);
}
| tSTAR arg_value opt_block_arg {
$$ = support.arg_blk_pass(new SplatNode(getPosition($1), $2), $3);
}
| block_arg {}
command_args : /* none */ {
$$ = new Long(lexer.getCmdArgumentState().begin());
} open_args {
lexer.getCmdArgumentState().reset($<Long>1.longValue());
$$ = $2;
}
open_args : call_args
| tLPAREN_ARG {
lexer.setState(LexState.EXPR_ENDARG);
} tRPAREN {
warnings.warn(getPosition($1), "don't put space before argument parentheses");
$$ = null;
}
| tLPAREN_ARG call_args2 {
lexer.setState(LexState.EXPR_ENDARG);
} tRPAREN {
warnings.warn(getPosition($1), "don't put space before argument parentheses");
$$ = $2;
}
block_arg : tAMPER arg_value {
support.checkExpression($2);
$$ = new BlockPassNode(support.union($1, $2), $2);
}
opt_block_arg : ',' block_arg {
$$ = $2;
}
| none_block_pass
args : arg_value {
$$ = new ArrayNode(getPosition2($1), $1);
}
| args ',' arg_value {
$$ = $1.add($3);
}
mrhs : args ',' arg_value {
$$ = $1.add($3);
}
| args ',' tSTAR arg_value {
$$ = support.arg_concat(getPosition($1), $1, $4);
}
| tSTAR arg_value {
$$ = new SplatNode(getPosition($1), $2);
}
primary : literal
| strings
| xstring
| regexp
| words
| qwords
| var_ref
| backref
| tFID {
$$ = new FCallNode($1.getPosition(), (String) $1.getValue(), null);
}
| kBEGIN bodystmt kEND {
$$ = new BeginNode(support.union($1, $3), $2);
}
| tLPAREN_ARG expr {
lexer.setState(LexState.EXPR_ENDARG);
} opt_nl tRPAREN {
warnings.warning(getPosition($1), "(...) interpreted as grouped expression");
$$ = $2;
}
| tLPAREN compstmt tRPAREN {
$$ = $2;
}
| primary_value tCOLON2 tCONSTANT {
$$ = new Colon2Node(support.union($1, $3), $1, (String) $3.getValue());
}
| tCOLON3 tCONSTANT {
$$ = new Colon3Node(support.union($1, $2), (String) $2.getValue());
}
| primary_value '[' aref_args tRBRACK {
if ($1 instanceof SelfNode) {
$$ = new FCallNode(getPosition($1), "[]", $3);
} else {
$$ = new CallNode(getPosition($1), $1, "[]", $3);
}
}
| tLBRACK aref_args tRBRACK {
ISourcePosition position = support.union($1, $3);
if ($2 == null) {
$$ = new ZArrayNode(position); /* zero length array */
} else {
$$ = $2;
$<ISourcePositionHolder>$.setPosition(position);
}
}
| tLBRACE assoc_list tRCURLY {
$$ = new HashNode(support.union($1, $3), $2);
}
| kRETURN {
$$ = new ReturnNode($1.getPosition(), null);
}
| kYIELD tLPAREN2 call_args tRPAREN {
$$ = support.new_yield(support.union($1, $4), $3);
}
| kYIELD tLPAREN2 tRPAREN {
$$ = new YieldNode(support.union($1, $3), null, false);
}
| kYIELD {
$$ = new YieldNode($1.getPosition(), null, false);
}
| kDEFINED opt_nl tLPAREN2 expr tRPAREN {
$$ = new DefinedNode(getPosition($1), $4);
}
| operation brace_block {
$$ = new FCallNode(support.union($1, $2), (String) $1.getValue(), null, $2);
}
| method_call
| method_call brace_block {
if ($1 != null &&
$<BlockAcceptingNode>1.getIterNode() instanceof BlockPassNode) {
throw new SyntaxException(getPosition($1), "Both block arg and actual block given.");
}
$<BlockAcceptingNode>1.setIterNode($2);
$<Node>1.setPosition(support.union($1, $2));
}
| kIF expr_value then compstmt if_tail kEND {
$$ = new IfNode(support.union($1, $6), support.getConditionNode($2), $4, $5);
}
| kUNLESS expr_value then compstmt opt_else kEND {
$$ = new IfNode(support.union($1, $6), support.getConditionNode($2), $5, $4);
}
| kWHILE {
lexer.getConditionState().begin();
} expr_value do {
lexer.getConditionState().end();
} compstmt kEND {
$$ = new WhileNode(support.union($1, $7), support.getConditionNode($3), $6);
}
| kUNTIL {
lexer.getConditionState().begin();
} expr_value do {
lexer.getConditionState().end();
} compstmt kEND {
$$ = new UntilNode(getPosition($1), support.getConditionNode($3), $6);
}
| kCASE expr_value opt_terms case_body kEND {
$$ = new CaseNode(support.union($1, $5), $2, $4);
}
| kCASE opt_terms case_body kEND {
$$ = new CaseNode(support.union($1, $4), null, $3);
}
| kCASE opt_terms kELSE compstmt kEND {
$$ = $4;
}
| kFOR block_var kIN {
lexer.getConditionState().begin();
} expr_value do {
lexer.getConditionState().end();
} compstmt kEND {
$$ = new ForNode(support.union($1, $9), $2, $8, $5);
}
| kCLASS cpath superclass {
if (support.isInDef() || support.isInSingle()) {
yyerror("class definition in method body");
}
support.pushLocalScope();
} bodystmt kEND {
$$ = new ClassNode(support.union($1, $6), $<Colon3Node>2, support.getCurrentScope(), $5, $3);
support.popCurrentScope();
}
| kCLASS tLSHFT expr {
$$ = new Boolean(support.isInDef());
support.setInDef(false);
} term {
$$ = new Integer(support.getInSingle());
support.setInSingle(0);
support.pushLocalScope();
} bodystmt kEND {
$$ = new SClassNode(support.union($1, $8), $3, support.getCurrentScope(), $7);
support.popCurrentScope();
support.setInDef($<Boolean>4.booleanValue());
support.setInSingle($<Integer>6.intValue());
}
| kMODULE cpath {
if (support.isInDef() || support.isInSingle()) {
yyerror("module definition in method body");
}
support.pushLocalScope();
} bodystmt kEND {
$$ = new ModuleNode(support.union($1, $5), $<Colon3Node>2, support.getCurrentScope(), $4);
support.popCurrentScope();
}
| kDEF fname {
support.setInDef(true);
support.pushLocalScope();
} f_arglist bodystmt kEND {
/* NOEX_PRIVATE for toplevel */
$$ = new DefnNode(support.union($1, $6), new ArgumentNode($2.getPosition(), (String) $2.getValue()), $<ArgsNode>4, support.getCurrentScope(), $5, Visibility.PRIVATE);
support.popCurrentScope();
support.setInDef(false);
}
| kDEF singleton dot_or_colon {
lexer.setState(LexState.EXPR_FNAME);
} fname {
support.setInSingle(support.getInSingle() + 1);
support.pushLocalScope();
lexer.setState(LexState.EXPR_END); /* force for args */
} f_arglist bodystmt kEND {
$$ = new DefsNode(support.union($1, $9), $2, new ArgumentNode($5.getPosition(), (String) $5.getValue()), $<ArgsNode>7, support.getCurrentScope(), $8);
support.popCurrentScope();
support.setInSingle(support.getInSingle() - 1);
}
| kBREAK {
$$ = new BreakNode($1.getPosition());
}
| kNEXT {
$$ = new NextNode($1.getPosition());
}
| kREDO {
$$ = new RedoNode($1.getPosition());
}
| kRETRY {
$$ = new RetryNode($1.getPosition());
}
primary_value : primary {
support.checkExpression($1);
$$ = $1;
}
then : term
| ":"
| kTHEN
| term kTHEN
do : term
| ":"
| kDO_COND
if_tail : opt_else
| kELSIF expr_value then compstmt if_tail {
//mirko: support.union($<ISourcePositionHolder>1.getPosition(), getPosition($<ISourcePositionHolder>1)) ?
$$ = new IfNode(getPosition($1), support.getConditionNode($2), $4, $5);
}
opt_else : none
| kELSE compstmt {
$$ = $2;
}
block_var : lhs
| mlhs {}
opt_block_var : none
| tPIPE /* none */ tPIPE {
$$ = new ZeroArgNode(support.union($1, $2));
}
| tOROP {
$$ = new ZeroArgNode($1.getPosition());
}
| tPIPE block_var tPIPE {
$$ = $2;
// Include pipes on multiple arg type
if ($2 instanceof MultipleAsgnNode) {
$2.setPosition(support.union($1, $3));
}
}
do_block : kDO_BLOCK {
support.pushBlockScope();
} opt_block_var compstmt kEND {
$$ = new IterNode(support.union($1, $5), $3, support.getCurrentScope(), $4);
support.popCurrentScope();
}
block_call : command do_block {
if ($1 != null &&
$<BlockAcceptingNode>1.getIterNode() instanceof BlockPassNode) {
throw new SyntaxException(getPosition($1), "Both block arg and actual block given.");
}
$<BlockAcceptingNode>1.setIterNode($2);
$<Node>1.setPosition(support.union($1, $2));
}
| block_call tDOT operation2 opt_paren_args {
$$ = support.new_call($1, $3, $4, null);
}
| block_call tCOLON2 operation2 opt_paren_args {
$$ = support.new_call($1, $3, $4, null);
}
method_call : operation paren_args {
$$ = support.new_fcall($1, $2, null);
}
| primary_value tDOT operation2 opt_paren_args {
$$ = support.new_call($1, $3, $4, null);
}
| primary_value tCOLON2 operation2 paren_args {
$$ = support.new_call($1, $3, $4, null);
}
| primary_value tCOLON2 operation3 {
$$ = support.new_call($1, $3, null, null);
}
| kSUPER paren_args {
$$ = support.new_super($2, $1);
}
| kSUPER {
$$ = new ZSuperNode($1.getPosition());
}
// IterNode:brace_block - block invocation argument (foo >{...}< | foo >do end<) [!null]
brace_block : tLCURLY {
support.pushBlockScope();
} opt_block_var compstmt tRCURLY {
$$ = new IterNode(support.union($1, $5), $3, support.getCurrentScope(), $4);
support.popCurrentScope();
}
| kDO {
support.pushBlockScope();
} opt_block_var compstmt kEND {
$$ = new IterNode(support.union($1, $5), $3, support.getCurrentScope(), $4);
$<ISourcePositionHolder>0.setPosition(support.union($<ISourcePositionHolder>0, $<ISourcePositionHolder>$));
support.popCurrentScope();
}
case_body : kWHEN when_args then compstmt cases {
$$ = new WhenNode(support.union($1, support.unwrapNewlineNode($4)), $2, $4, $5);
}
when_args : args
| args ',' tSTAR arg_value {
$$ = $1.add(new WhenNode(getPosition($1), $4, null, null));
}
| tSTAR arg_value {
$$ = new ArrayNode(getPosition($1), new WhenNode(getPosition($1), $2, null, null));
}
cases : opt_else | case_body
opt_rescue : kRESCUE exc_list exc_var then compstmt opt_rescue {
Node node;
if ($3 != null) {
node = support.appendToBlock(support.node_assign($3, new GlobalVarNode(getPosition($1), "$!")), $5);
if($5 != null) {
node.setPosition(support.unwrapNewlineNode($5).getPosition());
}
} else {
node = $5;
}
$$ = new RescueBodyNode(getPosition($1, true), $2, node, $6);
}
| {$$ = null;}
exc_list : arg_value {
$$ = new ArrayNode($1.getPosition(), $1);
}
| mrhs
| none
exc_var : tASSOC lhs {
$$ = $2;
}
| none
opt_ensure : kENSURE compstmt {
if ($2 != null) {
$$ = $2;
} else {
$$ = new NilNode(getPosition(null));
}
}
| none
literal : numeric
| symbol {
$$ = new SymbolNode($1.getPosition(), (String) $1.getValue());
}
| dsym
strings : string {
if ($1 instanceof EvStrNode) {
$$ = new DStrNode(getPosition($1)).add($1);
} else {
$$ = $1;
}
}
string : string1
| string string1 {
$$ = support.literal_concat(getPosition($1), $1, $2);
}
string1 : tSTRING_BEG string_contents tSTRING_END {
$$ = $2;
$<ISourcePositionHolder>$.setPosition(support.union($1, $3));
int extraLength = ((String) $1.getValue()).length() - 1;
// We may need to subtract addition offset off of first
// string fragment (we optimistically take one off in
// ParserSupport.literal_concat). Check token length
// and subtract as neeeded.
if (($2 instanceof DStrNode) && extraLength > 0) {
Node strNode = ((DStrNode)$2).get(0);
assert strNode != null;
strNode.getPosition().adjustStartOffset(-extraLength);
}
}
xstring : tXSTRING_BEG xstring_contents tSTRING_END {
ISourcePosition position = support.union($1, $3);
if ($2 == null) {
$$ = new XStrNode(position, null);
} else if ($2 instanceof StrNode) {
$$ = new XStrNode(position, (ByteList) $<StrNode>2.getValue().clone());
} else if ($2 instanceof DStrNode) {
$$ = new DXStrNode(position, $<DStrNode>2);
$<Node>$.setPosition(position);
} else {
$$ = new DXStrNode(position).add($2);
}
}
regexp : tREGEXP_BEG xstring_contents tREGEXP_END {
int options = $3.getOptions();
Node node = $2;
if (node == null) {
$$ = new RegexpNode(getPosition($1), ByteList.create(""), options & ~ReOptions.RE_OPTION_ONCE);
} else if (node instanceof StrNode) {
$$ = new RegexpNode($2.getPosition(), (ByteList) ((StrNode) node).getValue().clone(), options & ~ReOptions.RE_OPTION_ONCE);
} else if (node instanceof DStrNode) {
$$ = new DRegexpNode(getPosition($1), (DStrNode) node, options, (options & ReOptions.RE_OPTION_ONCE) != 0);
} else {
$$ = new DRegexpNode(getPosition($1), options, (options & ReOptions.RE_OPTION_ONCE) != 0).add(node);
}
}
words : tWORDS_BEG ' ' tSTRING_END {
$$ = new ZArrayNode(support.union($1, $3));
}
| tWORDS_BEG word_list tSTRING_END {
$$ = $2;
$<ISourcePositionHolder>$.setPosition(support.union($1, $3));
}
word_list : /* none */ {
$$ = new ArrayNode(getPosition(null));
}
| word_list word ' ' {
$$ = $1.add($2 instanceof EvStrNode ? new DStrNode(getPosition($1)).add($2) : $2);
}
word : string_content
| word string_content {
$$ = support.literal_concat(getPosition($1), $1, $2);
}
qwords : tQWORDS_BEG ' ' tSTRING_END {
$$ = new ZArrayNode(support.union($1, $3));
}
| tQWORDS_BEG qword_list tSTRING_END {
$$ = $2;
$<ISourcePositionHolder>$.setPosition(support.union($1, $3));
}
qword_list : /* none */ {
$$ = new ArrayNode(getPosition(null));
}
| qword_list tSTRING_CONTENT ' ' {
$$ = $1.add($2);
}
string_contents: /* none */ {
$$ = new StrNode($<Token>0.getPosition(), ByteList.create(""));
}
| string_contents string_content {
$$ = support.literal_concat(getPosition($1), $1, $2);
}
xstring_contents: /* none */ {
$$ = null;
}
| xstring_contents string_content {
$$ = support.literal_concat(getPosition($1), $1, $2);
}
string_content : tSTRING_CONTENT {
$$ = $1;
}
| tSTRING_DVAR {
$$ = lexer.getStrTerm();
lexer.setStrTerm(null);
lexer.setState(LexState.EXPR_BEG);
} string_dvar {
lexer.setStrTerm($<StrTerm>2);
$$ = new EvStrNode(support.union($1, $3), $3);
}
| tSTRING_DBEG {
$$ = lexer.getStrTerm();
lexer.setStrTerm(null);
lexer.setState(LexState.EXPR_BEG);
} compstmt tRCURLY {
lexer.setStrTerm($<StrTerm>2);
$$ = support.newEvStrNode(support.union($1, $4), $3);
}
string_dvar : tGVAR {
$$ = new GlobalVarNode($1.getPosition(), (String) $1.getValue());
}
| tIVAR {
$$ = new InstVarNode($1.getPosition(), (String) $1.getValue());
}
| tCVAR {
$$ = new ClassVarNode($1.getPosition(), (String) $1.getValue());
}
| backref
symbol : tSYMBEG sym {
lexer.setState(LexState.EXPR_END);
$$ = $2;
$<ISourcePositionHolder>$.setPosition(support.union($1, $2));
}
sym : fname | tIVAR | tGVAR | tCVAR
dsym : tSYMBEG xstring_contents tSTRING_END {
lexer.setState(LexState.EXPR_END);
// DStrNode: :"some text #{some expression}"
// StrNode: :"some text"
// EvStrNode :"#{some expression}"
if ($2 == null) {
yyerror("empty symbol literal");
}
if ($2 instanceof DStrNode) {
$$ = new DSymbolNode(support.union($1, $3), $<DStrNode>2);
} else {
ISourcePosition position = support.union($2, $3);
// We substract one since tsymbeg is longer than one
// and we cannot union it directly so we assume quote
// is one character long and subtract for it.
position.adjustStartOffset(-1);
$2.setPosition(position);
$$ = new DSymbolNode(support.union($1, $3));
$<DSymbolNode>$.add($2);
}
}
// Node:numeric - numeric value [!null]
numeric : tINTEGER | tFLOAT {
$$ = $1;
}
| tUMINUS_NUM tINTEGER %prec tLOWEST {
$$ = support.negateInteger($2);
}
| tUMINUS_NUM tFLOAT %prec tLOWEST {
$$ = support.negateFloat($2);
}
// Token:variable - name (special and normal onces)
variable : tIDENTIFIER | tIVAR | tGVAR | tCONSTANT | tCVAR
| kNIL {
$$ = new Token("nil", $1.getPosition());
}
| kSELF {
$$ = new Token("self", $1.getPosition());
}
| kTRUE {
$$ = new Token("true", $1.getPosition());
}
| kFALSE {
$$ = new Token("false", $1.getPosition());
}
| k__FILE__ {
$$ = new Token("__FILE__", $1.getPosition());
}
| k__LINE__ {
$$ = new Token("__LINE__", $1.getPosition());
}
var_ref : variable {
$$ = support.gettable((String) $1.getValue(), $1.getPosition());
}
var_lhs : variable {
$$ = support.assignable($1, null);
}
backref : tNTH_REF | tBACK_REF
superclass : term {
$$ = null;
}
| tLT {
lexer.setState(LexState.EXPR_BEG);
} expr_value term {
$$ = $3;
}
| error term {
yyerrok();
$$ = null;
}
// f_arglist: Function Argument list for definitions
f_arglist : tLPAREN2 f_args opt_nl tRPAREN {
$$ = $2;
$<ISourcePositionHolder>$.setPosition(support.union($1, $4));
lexer.setState(LexState.EXPR_BEG);
}
| f_args term {
$$ = $1;
}
f_args : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg {
$$ = new ArgsNode(support.union($1, $6), $1, $3, ((Integer) $5.getValue()).intValue(), $6);
}
| f_arg ',' f_optarg opt_f_block_arg {
$$ = new ArgsNode(getPosition($1), $1, $3, -1, $4);
}
| f_arg ',' f_rest_arg opt_f_block_arg {
$$ = new ArgsNode(support.union($1, $4), $1, null, ((Integer) $3.getValue()).intValue(), $4);
}
| f_arg opt_f_block_arg {
$$ = new ArgsNode($<ISourcePositionHolder>1.getPosition(), $1, null, -1, $2);
}
| f_optarg ',' f_rest_arg opt_f_block_arg {
$$ = new ArgsNode(getPosition($1), null, $1, ((Integer) $3.getValue()).intValue(), $4);
}
| f_optarg opt_f_block_arg {
$$ = new ArgsNode(getPosition($1), null, $1, -1, $2);
}
| f_rest_arg opt_f_block_arg {
$$ = new ArgsNode(getPosition($1), null, null, ((Integer) $1.getValue()).intValue(), $2);
}
| f_block_arg {
$$ = new ArgsNode(getPosition($1), null, null, -1, $1);
}
| /* none */ {
$$ = new ArgsNode(support.createEmptyArgsNodePosition(getPosition(null)), null, null, -1, null);
}
f_norm_arg : tCONSTANT {
yyerror("formal argument cannot be a constant");
}
| tIVAR {
yyerror("formal argument cannot be an instance variable");
}
| tCVAR {
yyerror("formal argument cannot be a class variable");
}
| tIDENTIFIER {
String identifier = (String) $1.getValue();
if (IdUtil.getVarType(identifier) != IdUtil.LOCAL_VAR) {
yyerror("formal argument must be local variable");
} else if (support.getCurrentScope().getLocalScope().isDefined(identifier) >= 0) {
yyerror("duplicate argument name");
}
support.getCurrentScope().getLocalScope().addVariable(identifier);
$$ = $1;
}
f_arg : f_norm_arg {
$$ = new ListNode($<ISourcePositionHolder>1.getPosition());
((ListNode) $$).add(new ArgumentNode($<ISourcePositionHolder>1.getPosition(), (String) $1.getValue()));
}
| f_arg ',' f_norm_arg {
$1.add(new ArgumentNode($<ISourcePositionHolder>3.getPosition(), (String) $3.getValue()));
$1.setPosition(support.union($1, $3));
$$ = $1;
}
f_opt : tIDENTIFIER '=' arg_value {
String identifier = (String) $1.getValue();
if (IdUtil.getVarType(identifier) != IdUtil.LOCAL_VAR) {
yyerror("formal argument must be local variable");
} else if (support.getCurrentScope().getLocalScope().isDefined(identifier) >= 0) {
yyerror("duplicate optional argument name");
}
support.getCurrentScope().getLocalScope().addVariable(identifier);
$$ = support.assignable($1, $3);
}
f_optarg : f_opt {
$$ = new BlockNode(getPosition($1)).add($1);
}
| f_optarg ',' f_opt {
$$ = support.appendToBlock($1, $3);
}
restarg_mark : tSTAR2 | tSTAR
f_rest_arg : restarg_mark tIDENTIFIER {
String identifier = (String) $2.getValue();
if (IdUtil.getVarType(identifier) != IdUtil.LOCAL_VAR) {
yyerror("rest argument must be local variable");
} else if (support.getCurrentScope().getLocalScope().isDefined(identifier) >= 0) {
yyerror("duplicate rest argument name");
}
$1.setValue(new Integer(support.getCurrentScope().getLocalScope().addVariable(identifier)));
$$ = $1;
}
| restarg_mark {
$1.setValue(new Integer(-2));
$$ = $1;
}
blkarg_mark : tAMPER2 | tAMPER
f_block_arg : blkarg_mark tIDENTIFIER {
String identifier = (String) $2.getValue();
if (IdUtil.getVarType(identifier) != IdUtil.LOCAL_VAR) {
yyerror("block argument must be local variable");
} else if (support.getCurrentScope().getLocalScope().isDefined(identifier) >= 0) {
yyerror("duplicate block argument name");
}
$$ = new BlockArgNode(support.union($1, $2), support.getCurrentScope().getLocalScope().addVariable(identifier), identifier);
}
opt_f_block_arg: ',' f_block_arg {
$$ = $2;
}
| /* none */ {
$$ = null;
}
singleton : var_ref {
if (!($1 instanceof SelfNode)) {
support.checkExpression($1);
}
$$ = $1;
}
| tLPAREN2 {
lexer.setState(LexState.EXPR_BEG);
} expr opt_nl tRPAREN {
if ($3 instanceof ILiteralNode) {
yyerror("Can't define single method for literals.");
}
support.checkExpression($3);
$$ = $3;
}
// ListNode:assoc_list - list of hash values pairs, like assocs but also
// will accept ordinary list-style (e.g. a,b,c,d or a=>b,c=>d) [?null]
assoc_list : none { // [!null]
$$ = new ArrayNode(getPosition(null));
}
| assocs trailer { // [!null]
$$ = $1;
}
| args trailer {
if ($1.size() % 2 != 0) {
yyerror("Odd number list for Hash.");
}
$$ = $1;
}
// ListNode:assocs - list of hash value pairs (e.g. a => b, c => d) [!null]
assocs : assoc // [!null]
| assocs ',' assoc { // [!null]
$$ = $1.addAll($3);
}
// ListNode:assoc - A single hash value pair (e.g. a => b) [!null]
assoc : arg_value tASSOC arg_value { // [!null]
$$ = new ArrayNode(support.union($1, $3), $1).add($3);
}
// or a Ruby 1.9 style hash value pair (e.g. a: b) [!null]
| variable ':' arg_value {
$$ = new ArrayNode(support.union($1, $3), new SymbolNode($1.getPosition(), (String) $1.getValue())).add($3);
}
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 */ | '\n'
trailer : /* none */ | '\n' | ','
term : ';' {
yyerrok();
}
| '\n'
terms : term
| terms ';' {
yyerrok();
}
none : /* none */ {
$$ = null;
}
none_block_pass: /* none */ {
$$ = null;
}
%%
/** The parse method use an lexer stream and parse it to an AST node
* structure
*/
public RubyParserResult parse(RubyParserConfiguration configuration, LexerSource source) {
support.reset();
support.setConfiguration(configuration);
support.setResult(new RubyParserResult());
lexer.reset();
lexer.setSource(source);
try {
//yyparse(lexer, new jay.yydebug.yyAnim("JRuby", 9));
//yyparse(lexer, new jay.yydebug.yyDebugAdapter());
yyparse(lexer, null);
} catch (IOException e) {
e.printStackTrace();
} catch (yyException e) {
e.printStackTrace();
}
return support.getResult();
}
// +++
// Helper Methods
void yyerrok() {}
/**
* Since we can recieve positions at times we know can be null we
* need an extra safety net here.
*/
private ISourcePosition getPosition2(ISourcePositionHolder pos) {
return pos == null ? lexer.getPosition(null, false) : pos.getPosition();
}
private ISourcePosition getPosition(ISourcePositionHolder start) {
return getPosition(start, false);
}
private ISourcePosition getPosition(ISourcePositionHolder start, boolean inclusive) {
if (start != null) {
return lexer.getPosition(start.getPosition(), inclusive);
}
return lexer.getPosition(null, inclusive);
}
}