} // ParseExpression // Parse a LeftHandSideExpression. // // If checkForCallExpr is false, then we don't check for CallExpressions. // This is used when parsing the function in a "new" expression, to // avoid parsing arguments to "new" as part of the expression that // specifies the function. private ExprFrag ParseLeftHandSideExpression( FunctionInfo info, bool checkForCallExpr ) { ExprFrag expr; if (tok.TryMatchKeyword("new")) { SrcLoc newLoc = tok.Prev().loc; ExprFrag constructor = ParseLeftHandSideExpression(info, false); ArrayList args = null; if (tok.TryMatchOp("(")) args = MatchOptionalArgumentList(info); expr = gen.Construct(newLoc, constructor, args); } else if (tok.TryMatchKeyword("function")) { SrcLoc functionLoc = tok.Prev().loc; FunctionInfo childInfo = info.GetNextChild(); ParseFunction(childInfo, true); expr = gen.FunctionExpr(functionLoc, childInfo); } else expr = ParsePrimaryExpression(info); while (true) if (checkForCallExpr && tok.TryMatchOp("(")) { SrcLoc opLoc = tok.Prev().loc; ArrayList args = MatchOptionalArgumentList(info); expr = gen.Call(opLoc, expr, args); } else if (tok.TryMatchOp("[")) { SrcLoc opLoc = tok.Prev().loc; expr = gen.ArrayReference(opLoc, expr, this.ParseExpression(info, true)); tok.MatchOp("]"); } else if (tok.TryMatchOp(".")) { SrcLoc opLoc = tok.Prev().loc; expr = gen.FieldReference(opLoc, expr, tok.MatchID()); } else return expr; } // ParseLeftHandSideExpression
} // ParseStatement // Parse a VariableDeclaration. See ParseExpression for an explanation // of allowIN. We set varName to the name of the declared variable. private StmtFrag ParseVariableDeclaration( FunctionInfo info, bool allowIN, out string varName, out SrcLoc varLoc ) { varName = tok.MatchID(); varLoc = tok.Prev().loc; if (tok.TryMatchOp("=")) { SrcLoc opLoc = tok.Prev().loc; ExprFrag lhs = gen.IdentifierExpr( varLoc, varName, info, CloneStack(withs) ); ExprFrag rhs = ParseExpression(info, allowIN, assignmentPrec); ExprFrag assignment = gen.BinaryExpr(opLoc, "=", lhs, rhs); return gen.ExpressionStmt(assignment); } else return gen.NewStmtFrag(tok.Prev().loc); } // ParseVariableDeclaration
} // BinaryExpr // Return an expression built from an unary prefix or suffix operator. public ExprFrag UnaryExpr(SrcLoc loc, string op, bool isSuffix, ExprFrag x) { string finalCode; if (op == "delete") finalCode = ((CSharpExprFrag)x).GenerateDelete(); else if (op == "++" || op == "--") finalCode = ((CSharpExprFrag)x).GenerateIncDec(op == "++", isSuffix); else if (op == "typeof") finalCode = ((CSharpExprFrag)x).GenerateTypeof(); else { string xCode = ((CSharpExprFrag)x).GenerateRHS(); string opName = (isSuffix) ? (string)(suffixOpTable[op]) : (string)(prefixOpTable[op]); finalCode = String.Format("Op.{0}({1})", opName, xCode); } return new CSharpRHSExpr(loc, finalCode); } // UnaryExpr
// Return an expression built from a binary operator. public ExprFrag BinaryExpr(SrcLoc loc, string op, ExprFrag x, ExprFrag y) { // HACK snewman 8/15/01: && and || shouldn't return hard-coded // true or false, they should always return one of the two // operands. (This is difficult to fix in C#, but will be easy // in the direct-to-CIL implementation.) string finalCode; CSharpExprFrag xExpr = (CSharpExprFrag)x; CSharpExprFrag yExpr = (CSharpExprFrag)y; if (op == "=") finalCode = xExpr.GenerateAssignment(yExpr.GenerateRHS()); else { string xCode = xExpr.GenerateRHS(); string yCode = yExpr.GenerateRHS(); if (op == "&&") finalCode = String.Format( "Support.BoolTest({0}) ? ({1}) : false", xCode, yCode ); else if (op == "||") finalCode = String.Format( "Support.BoolTest({0}) ? true : ({1})", xCode, yCode ); else { string opName = (string)(binaryOpTable[op]); if (opName == "$$compound$$") { // HACK snewman 8/16/01: compound assignment operators are // a bit of a hassle to map down to C# code, so for now we're // just splitting them into a standard operator followed by // a standard assignment. This is not really kosher, because // the side effects of the LHS are performed twice, and the // order of evaluatation is twisted. ExprFrag rhs = BinaryExpr(loc, op.Substring(0, op.Length-1), x, y); return BinaryExpr(loc, "=", x, rhs); } finalCode = String.Format("Op.{0}({1}, {2})", opName, xCode, yCode); } } return new CSharpRHSExpr(loc, finalCode); } // BinaryExpr
} // TryCatchFinally // Return a ?: expression. public ExprFrag ConditionalExpr( SrcLoc loc, ExprFrag condition, ExprFrag p1, ExprFrag p2 ) { string condCode = ((CSharpExprFrag)condition).GenerateRHS(); string p1Code = ((CSharpExprFrag)p1 ).GenerateRHS(); string p2Code = ((CSharpExprFrag)p2 ).GenerateRHS(); string finalCode = String.Format( "Support.BoolTest({0}) ? ({1}) : ({2})", condCode, p1Code, p2Code ); return new CSharpRHSExpr(loc, finalCode); } // ConditionalExpr
} // ParseForStatement // Parse a Statement. private StmtFrag ParseStatement(FunctionInfo info) { StringCollection labels = MatchLabelSet(); if (tok.TryMatchKeyword("do")) { LoopInfo loopInfo = gen.NewLoopInfo( curCGFuncInfo, tok.Prev().loc, labels ); loops.Push(loopInfo); StmtFrag body = ParseStatement(info); tok.MatchKeyword("while"); tok.MatchOp("("); ExprFrag condition = ParseExpression(info, true); tok.MatchOp(")"); tok.MatchOp(";"); LoopInfo temp = (LoopInfo) loops.Pop(); Trace.Assert(temp == loopInfo); return gen.DoWhile(loopInfo, body, condition); } else if (tok.TryMatchKeyword("while")) { LoopInfo loopInfo = gen.NewLoopInfo( curCGFuncInfo, tok.Prev().loc, labels ); loops.Push(loopInfo); tok.MatchOp("("); ExprFrag condition = ParseExpression(info, true); tok.MatchOp(")"); StmtFrag body = ParseStatement(info); LoopInfo temp = (LoopInfo) loops.Pop(); Trace.Assert(temp == loopInfo); return gen.WhileDo(loopInfo, condition, body); } else if (tok.TryMatchKeyword("for")) return ParseForStatement(info, labels); else { bool isSwitch = tok.PeekKeyword("switch"); LoopInfo labelInfo = null; if (labels != null || isSwitch) { labelInfo = gen.NewLabeledStmtInfo( curCGFuncInfo, tok.Prev().loc, labels, isSwitch ); loops.Push(labelInfo); } StmtFrag stmt; if (tok.PeekOp("{")) stmt = ParseBlock(info); else if (tok.TryMatchKeyword("var")) { stmt = gen.NewStmtFrag(tok.Prev().loc); do { string varName; SrcLoc varLoc; stmt.Append(ParseVariableDeclaration( info, true, out varName, out varLoc )); } while (tok.TryMatchOp(",")); tok.MatchOp(";"); } else if (tok.TryMatchOp(";")) { // EmptyStatement stmt = gen.NewStmtFrag(tok.Prev().loc); } else if (tok.TryMatchKeyword("if")) { SrcLoc ifLoc = tok.Prev().loc; tok.MatchOp("("); ExprFrag condition = ParseExpression(info, true); tok.MatchOp(")"); StmtFrag ifClause = ParseStatement(info); StmtFrag elseClause = null; if (tok.TryMatchKeyword("else")) elseClause = ParseStatement(info); stmt = gen.IfThenElse(ifLoc, condition, ifClause, elseClause); } else if (tok.TryMatchKeyword("continue")) { SrcLoc continueLoc = tok.Prev().loc; // HACK snewman 8/7/01: the grammar (ContinueStatement) specifies // "no LineTerminator here". string id; tok.TryMatchID(out id); tok.MatchOp(";"); stmt = gen.Continue(continueLoc, id, CloneStack(loops)); } else if (tok.TryMatchKeyword("break")) { SrcLoc breakLoc = tok.Prev().loc; // HACK snewman 8/7/01: the grammar (BreakStatement) specifies // "no LineTerminator here". string id; tok.TryMatchID(out id); tok.MatchOp(";"); stmt = gen.Break(breakLoc, id, CloneStack(loops)); } else if (tok.TryMatchKeyword("return")) { SrcLoc returnLoc = tok.Prev().loc; // HACK snewman 8/7/01: the grammar (ReturnStatement) specifies // "no LineTerminator here". if (tok.TryMatchOp(";")) stmt = gen.Return(returnLoc, null); else { ExprFrag value = ParseExpression(info, true); tok.MatchOp(";"); stmt = gen.Return(returnLoc, value); } } else if (tok.TryMatchKeyword("with")) { SrcLoc withLoc = tok.Prev().loc; tok.MatchOp("("); ExprFrag value = ParseExpression(info, true); tok.MatchOp(")"); WithInfo withInfo = gen.NewWithInfo(curCGFuncInfo, withLoc); withs.Push(withInfo); StmtFrag body = ParseStatement(info); WithInfo temp = (WithInfo) withs.Pop(); Trace.Assert(temp == withInfo); stmt = gen.With(withInfo, value, body); } else if (tok.TryMatchKeyword("switch")) { SrcLoc switchLoc = tok.Prev().loc; SwitchInfo switchInfo = gen.NewSwitchInfo(); tok.MatchOp("("); ExprFrag switchValue = ParseExpression(info, true); tok.MatchOp(")"); tok.MatchOp("{"); while (!tok.TryMatchOp("}")) { ExprFrag caseValue; if (tok.TryMatchKeyword("default")) caseValue = null; else { tok.MatchKeyword("case"); caseValue = ParseExpression(info, true); } StmtFrag clauseStmt = null; tok.MatchOp(":"); while ( !tok.PeekOp("}") && !tok.PeekKeyword("case") && !tok.PeekKeyword("default") ) { StmtFrag tempStmt = ParseStatement(info); if (clauseStmt == null) clauseStmt = tempStmt; else clauseStmt.Append(tempStmt); } switchInfo.AddCase(caseValue, clauseStmt); } stmt = gen.Switch(switchLoc, switchValue, switchInfo); } else if (tok.TryMatchKeyword("throw")) { SrcLoc throwLoc = tok.Prev().loc; // HACK snewman 8/7/01: the grammar (ThrowStatement) specifies // "no LineTerminator here". ExprFrag value = ParseExpression(info, true); tok.MatchOp(";"); stmt = gen.Throw(throwLoc, value); } else if (tok.TryMatchKeyword("try")) { SrcLoc tryLoc = tok.Prev().loc; StmtFrag tryBody = ParseBlock(info); String catchVar = null; WithInfo catchWithInfo = null; StmtFrag catchBody = null; StmtFrag finallyBody = null; if (tok.TryMatchKeyword("catch")) { SrcLoc catchLoc = tok.Prev().loc; tok.MatchOp("("); catchVar = tok.MatchID(); tok.MatchOp(")"); catchWithInfo = gen.NewWithInfo(curCGFuncInfo, catchLoc); withs.Push(catchWithInfo); catchBody = ParseBlock(info); WithInfo temp = (WithInfo) withs.Pop(); Trace.Assert(temp == catchWithInfo); } if (tok.TryMatchKeyword("finally")) finallyBody = ParseBlock(info); stmt = gen.TryCatchFinally( tryLoc, tryBody, catchVar, catchWithInfo, catchBody, finallyBody ); } else { ExprFrag expr = ParseExpression(info, true); tok.MatchOp(";"); stmt = gen.ExpressionStmt(expr); } if (labelInfo != null) { LoopInfo temp2 = (LoopInfo) loops.Pop(); Trace.Assert(temp2 == labelInfo); stmt = gen.LabeledStmt(labelInfo, stmt); } return stmt; } } // ParseStatement
} // NewSwitchInfo // Return a statement which evaluates the given expression. public StmtFrag ExpressionStmt(ExprFrag expr) { CSharpStmtFrag frag = new CSharpStmtFrag(expr.loc); frag.Append(((CSharpExprFrag)expr).GenerateRHS() + ";"); return frag; } // ExpressionStmt
public void AddEntry(ExprFrag value) { entries.Add(value); }
} // Call // Return an expression for a constructor call (a "new" expression). // args is an array of ExprFrags, or null if no parameter list was // provided. public ExprFrag Construct(SrcLoc loc, ExprFrag functionExpr, ArrayList args) { String code = ((CSharpExprFrag)functionExpr).GenerateCall(args, true); return new CSharpRHSExpr(loc, code); } // Construct
} // For // Return a for/in statement. public StmtFrag ForIn( LoopInfo loopInfo, StmtFrag init, ExprFrag lhs, ExprFrag rhs, StmtFrag body ) { CSharpLoopInfo csLoopInfo = (CSharpLoopInfo)loopInfo; // HACK snewman 8/15/01: implement for/in statements. throw new ParseError( "For/In statements not yet implemented", csLoopInfo.loc ); } // ForIn
} // WhileDo // Return a for statement. init is the loop initializer, cond is the // loop control expression, and step is the loop increment expression. // Any or all of init, cond, and step can be null. public StmtFrag For( LoopInfo loopInfo, StmtFrag init, ExprFrag cond, ExprFrag step, StmtFrag body ) { CSharpLoopInfo csLoopInfo = (CSharpLoopInfo)loopInfo; CSharpStmtFrag frag = new CSharpStmtFrag(csLoopInfo.loc); frag.Append(init); CSharpExprFrag csCondition = (CSharpExprFrag)cond; frag.Append("while (Support.BoolTest(" + csCondition.GenerateRHS() + "))"); ((CSharpStmtFrag)body).Append("continueTarget_{0}:", csLoopInfo.index + 1); ((CSharpStmtFrag)body).Append(ExpressionStmt(step)); frag.AppendIndentedBody(body); frag.Append("breakTarget_{0}: {1}", csLoopInfo.index + 1, "{}"); return frag; } // For
} // IfThenElse // Return a do...while statement. public StmtFrag DoWhile( LoopInfo loopInfo, StmtFrag body, ExprFrag condition ) { CSharpLoopInfo csLoopInfo = (CSharpLoopInfo)loopInfo; CSharpStmtFrag frag = new CSharpStmtFrag(csLoopInfo.loc); CSharpExprFrag csCondition = (CSharpExprFrag)condition; frag.Append("do"); ((CSharpStmtFrag)body).Append("continueTarget_{0}:", csLoopInfo.index + 1); frag.AppendIndentedBody(body); frag.Append("while (Support.BoolTest(" + csCondition.GenerateRHS() + "))"); frag.Append("breakTarget_{0}: {1}", csLoopInfo.index + 1, "{}"); return frag; } // DoWhile
} // ParseLeftHandSideExpression // Parse a PrimaryExpression. private ExprFrag ParsePrimaryExpression(FunctionInfo info) { string id; double d; string s; if (tok.TryMatchKeyword("this")) return gen.ThisExpr(tok.Prev().loc, info); else if (tok.TryMatchKeyword("null")) return gen.NullExpr(tok.Prev().loc); else if (tok.TryMatchKeyword("true")) return gen.BoolLiteralExpr(tok.Prev().loc, true); else if (tok.TryMatchKeyword("false")) return gen.BoolLiteralExpr(tok.Prev().loc, false); else if (tok.TryMatchOp("(")) { ExprFrag expr = ParseExpression(info, true); tok.MatchOp(")"); return expr; } else if (tok.TryMatchOp("{")) { SrcLoc startLoc = tok.Prev().loc; ObjectLiteralInfo litInfo = gen.NewObjectLiteralInfo(); if (!tok.PeekOp("}")) { do { double tempD; if (tok.TryMatchNumLit(out tempD)) { // HACK snewman 7/26/01: format according to the ECMA spec. id = System.Convert.ToString(tempD); } else if (tok.TryMatchStringLit(out id)) {} else id = tok.MatchID(); SrcLoc idLoc = tok.Prev().loc; tok.MatchOp(":"); ExprFrag value = ParseExpression(info, true, assignmentPrec); litInfo.AddProp(idLoc, id, value); } while (tok.TryMatchOp(",")); } tok.MatchOp("}"); return gen.ObjectLiteralExpr(startLoc, litInfo); } else if (tok.TryMatchOp("[")) { SrcLoc startLoc = tok.Prev().loc; ArrayLiteralInfo arrayInfo = gen.NewArrayLiteralInfo(); bool anyCommas = false; bool requireComma = false; while (!tok.PeekOp("]")) if (tok.TryMatchOp(",")) { anyCommas = true; if (!requireComma) arrayInfo.AddEntry(null); else requireComma = false; } else if (!requireComma) { ExprFrag value = ParseExpression(info, true, assignmentPrec); arrayInfo.AddEntry(value); requireComma = true; } else break; // If the list ends with a comma, then add one more null entry // to the end. if (anyCommas && !requireComma) arrayInfo.AddEntry(null); tok.MatchOp("]"); return gen.ArrayLiteralExpr(startLoc, arrayInfo); } else if (tok.TryMatchID(out id)) { SrcLoc idLoc = tok.Prev().loc; if (id == "eval") return gen.IdentifierExpr(idLoc, id, info, CloneStack(withs)); else if (id == "arguments") return gen.ArgumentsExpr(idLoc, info, CloneStack(withs)); else return gen.IdentifierExpr(idLoc, id, info, CloneStack(withs)); } else if (tok.TryMatchNumLit(out d)) return gen.NumLiteralExpr(tok.Prev().loc, d); else if (tok.TryMatchStringLit(out s)) return gen.StringLiteralExpr(tok.Prev().loc, s); else { // One of the if clauses should fire; if not, the phase 1 // parser would have reported an error. Trace.Assert(false); return null; } } // ParsePrimaryExpression
} // ParseExpression // Parse an Expression. // // If allowIN is false, then we don't look for "in" operators. (This // implements the "NoIn" variant of the grammar, e.g. ExpressionNoIn // as opposed to Expression.) // // This method can also be used to parse "smaller" nonterminals such // as AssignmentExpression, ConditionalExpression, and so on down to // MultiplicativeExpression. This is controlled by the maxPrecedence // parameter: we ignore any operators with a looser (numerically // greater) precedence than maxPrecedence. private ExprFrag ParseExpression( FunctionInfo info, bool allowIN, int maxPrecedence ) { // Note that we use a simpler grammar than ECMAScript, because // we don't need to catch assignment to non-lvalue in the parser // (it will have been caught in phase 1). // Parse any prefix operators. Stack stackedOps = new Stack(); Stack stackedOpsLocs = new Stack(); OperatorInfo matchedOp; while (TryMatchOperator(true, -1, out matchedOp, true)) { stackedOps.Push(matchedOp); stackedOpsLocs.Push(tok.Prev().loc); } ExprFrag expr = ParseLeftHandSideExpression(info, true); if (stackedOps.Count > 0) { while (TryMatchOperator(allowIN, unarySuffixPrec, out matchedOp, true)) { Trace.Assert(matchedOp.opType == OperatorInfo.Types.unarySuffix); expr = gen.UnaryExpr( tok.Prev().loc, matchedOp.text, matchedOp.opType == OperatorInfo.Types.unarySuffix, expr ); } while (stackedOps.Count > 0) { OperatorInfo curOp = (OperatorInfo)stackedOps.Pop(); expr = gen.UnaryExpr( (SrcLoc)stackedOpsLocs.Pop(), curOp.text, curOp.opType == OperatorInfo.Types.unarySuffix, expr ); } } // if (stackedOps.Count > 0) while (TryMatchOperator(allowIN, maxPrecedence, out matchedOp, true)) { SrcLoc opLoc = tok.Prev().loc; if (matchedOp.text == "?") { ExprFrag p1 = ParseExpression(info, allowIN, assignmentPrec); tok.MatchOp(":"); ExprFrag p2 = ParseExpression(info, allowIN, assignmentPrec); expr = gen.ConditionalExpr(opLoc, expr, p1, p2); } else if (matchedOp.opType == OperatorInfo.Types.assignment) expr = gen.BinaryExpr( opLoc, matchedOp.text, expr, ParseExpression(info, allowIN, matchedOp.precedence) ); else if (matchedOp.opType == OperatorInfo.Types.binary) expr = gen.BinaryExpr( opLoc, matchedOp.text, expr, ParseExpression(info, allowIN, matchedOp.precedence-1) ); else { Trace.Assert(matchedOp.opType == OperatorInfo.Types.unarySuffix); expr = gen.UnaryExpr( opLoc, matchedOp.text, matchedOp.opType == OperatorInfo.Types.unarySuffix, expr ); } } return expr; } // ParseExpression
} // UnaryExpr // Return an expression for an array dereference. public ExprFrag ArrayReference(SrcLoc loc, ExprFrag arrayExpr, ExprFrag index) { string arrayCode = ((CSharpExprFrag)arrayExpr).GenerateRHS(); string indexCode = ((CSharpExprFrag)index).GenerateRHS(); return new CSharpArrayRef(loc, arrayCode, indexCode); } // ArrayReference
} // LabeledStmt // Return a return statement. value will be null if no value was // specified in the statement. public StmtFrag Return(SrcLoc loc, ExprFrag value) { CSharpStmtFrag frag = new CSharpStmtFrag(loc); if (value != null) frag.Append("return " + ((CSharpExprFrag)value).GenerateRHS() + ";"); else frag.Append("return;"); return frag; } // Return
} // ArrayReference // Return an expression for a field dereference. public ExprFrag FieldReference(SrcLoc loc, ExprFrag objectExpr, string id) { string objectCode = ((CSharpExprFrag)objectExpr).GenerateRHS(); return new CSharpFieldRef(loc, objectCode, id); } // FieldReference
} // Return // Return a throw statement. public StmtFrag Throw(SrcLoc loc, ExprFrag value) { CSharpStmtFrag frag = new CSharpStmtFrag(loc); frag.Append("throw Support.WrapException(" + ((CSharpExprFrag)value).GenerateRHS() + ");"); return frag; } // Throw
public void AddProp(SrcLoc loc, string id, ExprFrag value) { Entry newEntry = new Entry(); newEntry.loc = loc; newEntry.id = id; newEntry.value = value; entries.Add(newEntry); }
} // Throw // Return a with statement. public StmtFrag With(WithInfo withInfo, ExprFrag value, StmtFrag body) { CSharpWithInfo csWithInfo = (CSharpWithInfo)withInfo; CSharpStmtFrag frag = new CSharpStmtFrag(csWithInfo.loc); frag.Append( "object withTemp_{0} = ({1});", csWithInfo.index+1, ((CSharpExprFrag)value).GenerateRHS() ); frag.Append(body); return frag; } // With
public void AddCase(ExprFrag caseValue, StmtFrag caseStmt) { Entry newEntry = new Entry(); newEntry.caseValue = caseValue; newEntry.caseStmt = caseStmt; entries.Add(newEntry); }
} // With // Return a switch statement. public StmtFrag Switch( SrcLoc loc, ExprFrag switchValue, SwitchInfo switchInfo ) { // HACK snewman 8/15/01: implement switch statements. throw new ParseError( "Switch statements not yet implemented", loc ); } // Switch
} // ExpressionStmt // Return an if/then or if/then/else statement. If there was no else // clause, elseClause will be null. public StmtFrag IfThenElse( SrcLoc loc, ExprFrag condition, StmtFrag ifClause, StmtFrag elseClause ) { CSharpStmtFrag frag = new CSharpStmtFrag(loc); CSharpExprFrag csCondition = (CSharpExprFrag)condition; frag.Append("if (Support.BoolTest(" + csCondition.GenerateRHS() + "))"); frag.AppendIndentedBody(ifClause); if (elseClause != null) { frag.Append("else"); frag.AppendIndentedBody(elseClause); } return frag; } // IfThenElse
} // MatchLabelSet // Parse a for statement. The caller should already have matched // the "for" keyword. private StmtFrag ParseForStatement(FunctionInfo info, StringCollection labels) { LoopInfo loopInfo = gen.NewLoopInfo(curCGFuncInfo, tok.Prev().loc, labels); loops.Push(loopInfo); // HACK snewman 8/13/01: need to review this section against the // specific semantics for "for" statements, and especially for/in // statements, in section 12.6 of the ECMA spec. tok.MatchOp("("); bool isIn; // True for for/in, false otherwise StmtFrag init=null; // Initializer; used for both types of loop ExprFrag cond=null; // Condition; only used for non-in loops ExprFrag update=null; // Update step; only used for non-in loops ExprFrag inLHS=null; // LHS expression where in loops put prop names ExprFrag inRHS=null; // RHS object that in loops iterate over if (tok.TryMatchKeyword("var")) { string varName; SrcLoc varLoc; init = ParseVariableDeclaration(info, false, out varName, out varLoc); isIn = tok.TryMatchKeyword("in"); if (isIn) inLHS = gen.IdentifierExpr( varLoc, varName, info, CloneStack(withs) ); else { while (tok.TryMatchOp(",")) init.Append(ParseVariableDeclaration( info, false, out varName, out varLoc )); } } else { if (!tok.PeekOp(";")) { ExprFrag initExpr = ParseExpression(info, false, 99); isIn = tok.TryMatchKeyword("in"); if (isIn) inLHS = initExpr; else init = gen.ExpressionStmt(initExpr); } else isIn = false; } if (isIn) inRHS = ParseExpression(info, true); else { tok.MatchOp(";"); if (!tok.PeekOp(";")) cond = ParseExpression(info, true); tok.MatchOp(";"); if (!tok.PeekOp(")")) update = ParseExpression(info, true); } tok.MatchOp(")"); StmtFrag body = ParseStatement(info); LoopInfo temp = (LoopInfo) loops.Pop(); Trace.Assert(temp == loopInfo); if (isIn) return gen.ForIn(loopInfo, init, inLHS, inRHS, body); else return gen.For(loopInfo, init, cond, update, body); } // ParseForStatement