} // CaptureTokens // Read JavaScript source text and output a C# file. progClassName // is the name of the C# class we generate. inputFileLabel denotes // the source of the input text; it is inserted in a comment in the // C# source. public static void CompileToCSharp( TextReader input, TextWriter output, string progClassName, string inputFileLabel, bool forEvalCode ) { Tokenizer tokenizer = new Tokenizer(input); TokenListNode tokenList = CaptureTokens(tokenizer); Phase1Parser parser = new Phase1Parser(new Retokenizer(tokenList)); ProgramInfo programInfo = new ProgramInfo(); FunctionInfo rootFunc = new FunctionInfo(programInfo, null, null); parser.ParseProgram(rootFunc); PrettyPrinter pp = new PrettyPrinter(output); pp.Line("// JANET compiler output for source file " + inputFileLabel); pp.Line("// "); CSharpGenerator gen = new CSharpGenerator( rootFunc, pp, progClassName, forEvalCode ); Phase2Parser parser2 = new Phase2Parser( new Retokenizer(tokenList), gen ); parser2.ParseProgram(rootFunc); } // CompileToCSharp
} // Phase2Parser constructor // Parse a program from the input stream, given the FunctionInfo // object for the program's root scope. public void ParseProgram(FunctionInfo info) { gen.EmitProgramPrefix(); CGFuncInfo cgFuncInfo = gen.NewFuncInfo(info); curCGFuncInfo = cgFuncInfo; gen.EmitMainFunction(ParseSourceElements(info, cgFuncInfo)); Trace.Assert(curCGFuncInfo == cgFuncInfo); Trace.Assert(loops.Count == 0); Trace.Assert(withs.Count == 0); curCGFuncInfo = null; gen.EmitProgramSuffix(); } // ParseProgram
} // ParseProgram // Parse a SourceElements (i.e. the main program, or a function body) // and return the generated code. private StmtFrag ParseSourceElements( FunctionInfo info, CGFuncInfo cgFuncInfo ) { SrcLoc loc; loc.lineNum = 1; loc.colNum = 1; loc.absPosition = 0; loc.len = 1; StmtFrag frag = gen.NewStmtFrag(loc); while (!tok.atEnd && !tok.PeekOp("}")) if (tok.TryMatchKeyword("function")) ParseFunction(info.GetNextChild(), false); else frag.Append(ParseStatement(info)); return frag; } // ParseSourceElements
} // ParseSourceElements // Parse a FunctionDeclaration or FunctionExpression. The "function" // keyword should already have been matched. private void ParseFunction(FunctionInfo info, bool isExpression) { // Save the current loop and with stacks. OPTIMIZATION: don't need to // do this if the stack was empty. Stack savedLoops = loops; loops = new Stack(); Stack savedWiths = withs; withs = new Stack(); CGFuncInfo savedCGFuncInfo = curCGFuncInfo; CGFuncInfo cgFuncInfo = gen.NewFuncInfo(info); curCGFuncInfo = cgFuncInfo; string functionName = null; if (isExpression) tok.TryMatchID(out functionName); else functionName = tok.MatchID(); // Scan to the end of the parameter list. We don't bother parsing // it in detail, as this was done during the first phase. while (!tok.TryMatchOp(")")) tok.Match(); tok.MatchOp("{"); StmtFrag body = ParseSourceElements(info, cgFuncInfo); tok.MatchOp("}"); gen.EmitFunction(info, body); Trace.Assert(loops.Count == 0); Trace.Assert(withs.Count == 0); Trace.Assert(curCGFuncInfo == cgFuncInfo); loops = savedLoops; withs = savedWiths; curCGFuncInfo = savedCGFuncInfo; } // ParseFunction
// Construct a FunctionInfo object. parent should be the info object // for the enclosing function, or null if we are the root program. // nameInParent should be the function name, or null for functions that // are defined in an expression (even if the function is named in the // expression, since such names don't create an entry in the parent's // scope). nameInParent should also be null for the root function. // // If parent is not null, we add ourselves to its children list. public FunctionInfo(ProgramInfo program, FunctionInfo parent, string nameInParent) { program_ = program; parent_ = parent; firstChild_ = null; lastChild_ = null; nextSib_ = null; childScan_ = null; nameInParent_ = nameInParent; callsEval_ = false; usesArgs_ = false; params_ = new StringCollection(); locals_ = new StringCollection(); // Register ourselves in our parent scope. if (parent != null) { if (parent.firstChild_ == null) { Trace.Assert(parent.lastChild_ == null); parent.firstChild_ = this; parent.childScan_ = this; parent.lastChild_ = this; } else { Trace.Assert(parent.lastChild_ != null); parent.lastChild_.nextSib_ = this; parent.lastChild_ = this; } } } // FunctionInfo constructor
} // 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, filling in info with any declarations. // See ParseExpression for an explanation of allowIN. private void ParseVariableDeclaration(FunctionInfo info, bool allowIN) { info.AddLocalName(tok.MatchID()); if (tok.TryMatchOp("=")) ParseExpression(info, allowIN, assignmentPrec); } // ParseVariableDeclaration
} // CSharpGenerator constructor // Create a CGFuncInfo object for the given function. public CGFuncInfo NewFuncInfo(FunctionInfo func) { return new CSharpGenFuncInfo(func); } // NewFuncInfo
} // ParseExpression // Simplified version of ParseExpression (no isLHS parameter). private void ParseExpression( FunctionInfo info, bool allowIN, int maxPrecedence ) { bool isLHS; // Unused result ParseExpression(info, allowIN, maxPrecedence, out isLHS); } // ParseExpression
} // StringLiteralExpr // Return an expression to reference the given identifier. // enclosingWiths should be a stack of WithInfo objects, giving // all with scopes in the current function that enclose the identifier // reference. // // HACK snewman 8/13/01: this method, and ArgumentsExpr below, need a // way to search "with" statements in enclosing scopes (for function // expressions and expr code). public ExprFrag IdentifierExpr( SrcLoc loc, string id, FunctionInfo function, Stack enclosingWiths ) { // HACK snewman 8/20/01: extend this to support function scopes // where not all identifiers are known at compile time (because // they invoke "eval"). // Determine which enclosing scope, if any, defines the identifier. FunctionInfo bindingScope = function; while (bindingScope != null && !bindingScope.IDBoundInThisScope(id)) bindingScope = bindingScope.parent; // HACK snewman 8/20/01: add support for locals that are implemented // directly as C# locals. I think this needs to be added in some // other places as well, such as EmitFunction. if (bindingScope == null || bindingScope.isRoot) return new CSharpVariableRef(loc, id, null, enclosingWiths); else if (bindingScope == function) return new CSharpVariableRef(loc, id, function, enclosingWiths); else { // HACK snewman 8/20/01: add support for referencing a local // variable of an enclosing scope. throw new ParseError( "Access to parent function variables not yet implemented", loc ); } } // IdentifierExpr
} // 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
} // ParseFunction // Parse a Block. The stream should be positioned at the "{" token. private StmtFrag ParseBlock(FunctionInfo info) { StmtFrag frag = gen.NewStmtFrag(tok.Prev().loc); tok.MatchOp("{"); while (!tok.TryMatchOp("}")) frag.Append(ParseStatement(info)); return frag; } // ParseBlock
} // ParsePrimaryExpression // Match an ArgumentList (if any) and the trailing ')'. Fill in info // with any declarations. void MatchOptionalArgumentList(FunctionInfo info) { if (!tok.PeekOp(")")) { do ParseExpression(info, true, assignmentPrec); while (tok.TryMatchOp(",")); } tok.MatchOp(")"); } // MatchOptionalArgumentList
} // ParseLeftHandSideExpression // Parse a PrimaryExpression, filling in info with any declarations. void ParsePrimaryExpression(FunctionInfo info) { string id; double d; string s; if (tok.TryMatchKeyword("this")) {} else if (tok.TryMatchKeyword("null")) {} else if (tok.TryMatchKeyword("true")) {} else if (tok.TryMatchKeyword("false")) {} else if (tok.TryMatchOp("(")) { ParseExpression(info, true); tok.MatchOp(")"); } else if (tok.TryMatchOp("{")) { if (!tok.PeekOp("}")) { do { if (tok.TryMatchNumLit(out d)) {} else if (tok.TryMatchStringLit(out s)) {} else tok.MatchID(); tok.MatchOp(":"); ParseExpression(info, true, assignmentPrec); } while (tok.TryMatchOp(",")); } tok.MatchOp("}"); } else if (tok.TryMatchOp("[")) { bool requireComma = false; while (!tok.PeekOp("]")) if (tok.TryMatchOp(",")) requireComma = false; else if (!requireComma) { ParseExpression(info, true, assignmentPrec); requireComma = true; } else break; tok.MatchOp("]"); } else if (tok.TryMatchID(out id)) { if (id == "eval") info.CallsEval(); else if (id == "arguments") info.UsesArgs(); // If the id is not bound in any enclosing scope, then list it // as a potential global. Note that this is conservative -- it // may actually be bound by a declaration we haven't parsed yet. // // HACK snewman 8/9/01: as currently implemented, this will not // filter out identifiers that are bound by an enclosing "catch" // clause. if (!info.IDHasNonglobalBinding(id)) info.program.PotentialGlobal(id); } else if (tok.TryMatchNumLit(out d)) {} else if (tok.TryMatchStringLit(out s)) {} else { tok.Match(); ReportError("bad token when an expression was expected"); } } // ParsePrimaryExpression
} // ParseExpression // Parse a LeftHandSideExpression, filling in info with any declarations. // // 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. void ParseLeftHandSideExpression(FunctionInfo info, bool checkForCallExpr) { if (tok.TryMatchKeyword("new")) { ParseLeftHandSideExpression(info, false); if (tok.TryMatchOp("(")) MatchOptionalArgumentList(info); } else if (tok.TryMatchKeyword("function")) ParseFunction(info, true); else ParsePrimaryExpression(info); while (true) if (checkForCallExpr && tok.TryMatchOp("(")) MatchOptionalArgumentList(info); else if (tok.TryMatchOp("[")) { this.ParseExpression(info, true); tok.MatchOp("]"); } else if (tok.TryMatchOp(".")) tok.MatchID(); else break; } // ParseLeftHandSideExpression
} // ParseExpression // Parse an Expression, filling in info with any declarations. // // 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. // // We set isLHS to true if this was a simple LeftHandSideExpression, // false if it included any operators. void ParseExpression( FunctionInfo info, bool allowIN, int maxPrecedence, out bool isLHS ) { // Note that we don't strictly follow the ECMAScript grammar here. // The grammar is designed to prevent assignment to a non-lvalue. // This is a hassle to implement in the parser, and leads to confusing // error messages. Instead, we allow any expression (except an // assignment expression) to parse as the left-hand side of an // assignment, but then we report an error if the parsed LHS is not // a valid LeftHandSideExpression. isLHS = true; // Parse any prefix operators. OperatorInfo matchedOp; while (TryMatchOperator(true, -1, out matchedOp, true)) isLHS = false; ParseLeftHandSideExpression(info, true); while (TryMatchOperator(allowIN, maxPrecedence, out matchedOp, true)) { if (matchedOp.text == "?") { ParseExpression(info, allowIN, assignmentPrec); tok.MatchOp(":"); ParseExpression(info, allowIN, assignmentPrec); } else if (matchedOp.opType == OperatorInfo.Types.assignment) { if (!isLHS) ReportError("illegal expression in left side of assignment"); ParseExpression( info, allowIN, matchedOp.precedence, out isLHS ); } else if (matchedOp.opType == OperatorInfo.Types.binary) ParseExpression( info, allowIN, matchedOp.precedence-1, out isLHS ); isLHS = false; } } // ParseExpression
} // 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
} // 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
} // ParsePrimaryExpression // Match an ArgumentList (if any) and the trailing ')'. Return // an array of ExprFrag objects. private ArrayList MatchOptionalArgumentList(FunctionInfo info) { ArrayList argList = new ArrayList(); if (!tok.PeekOp(")")) { do argList.Add(ParseExpression(info, true, assignmentPrec)); while (tok.TryMatchOp(",")); } tok.MatchOp(")"); return argList; } // MatchOptionalArgumentList
} // ArgumentsExpr // Return a FunctionExpr. public ExprFrag FunctionExpr(SrcLoc loc, FunctionInfo childInfo) { // HACK snewman 8/15/01: implement function expressions. throw new ParseError( "Function expressions not yet implemented", loc ); } // FunctionExpr
} // Construct // Return an expression for the keyword "this". public ExprFrag ThisExpr(SrcLoc loc, FunctionInfo info) { if (info.isRoot) return new CSharpRHSExpr(loc, "globals"); else return new CSharpRHSExpr(loc, "this_"); } // ThisExpr
// Construct a CSharpVariableRef for a reference at the given location, // to the given variable, in the given function, and in the given stack // of WithInfo objects. function should be null for global variable // references. internal CSharpVariableRef( SrcLoc loc, string id, FunctionInfo function, Stack enclosingWiths ) : base(loc) { this.id = id; this.function = function; this.enclosingWiths = enclosingWiths; } // CSharpVariableRef constructor
} // IdentifierExpr // Return an expression to reference the identifier "arguments". // enclosingWiths should be a stack of WithInfo objects, giving // all with scopes in the current function that enclose the identifier // reference. public ExprFrag ArgumentsExpr( SrcLoc loc, FunctionInfo function, Stack enclosingWiths ) { // HACK snewman 8/20/01: implement this. Support "with" // scopes... we may be able to just fall through into // IdentifierExpr. throw new ParseError( "\"arguments\" not yet implemented", loc ); } // ArgumentsExpr
} // ParseVariableDeclaration // Parse an Expression, filling in info with any declarations. // // 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.) private void ParseExpression(FunctionInfo info, bool allowIN) { ParseExpression(info, allowIN, 99); } // ParseExpression
internal CSharpGenFuncInfo(FunctionInfo func) { this.func = func; }
} // 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
// Construct a CSharpGenerator. Parameters: // // rootInfo Info object for the program's root function. // pp Object where we write the generated program. // progClassName C# class name to use for the generated program. // forEvalCode True if the code being compiled came from a // call to eval(). public CSharpGenerator( FunctionInfo rootInfo, PrettyPrinter pp, String progClassName, bool forEvalCode ) { this.rootInfo = rootInfo; this.pp = pp; this.progClassName = progClassName; this.forEvalCode = forEvalCode; } // CSharpGenerator constructor
} // ParseVariableDeclaration // 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.) private ExprFrag ParseExpression(FunctionInfo info, bool allowIN) { return ParseExpression(info, allowIN, 99); } // ParseExpression
} // EmitMainFunction // Generate code for the given function (other than the main function), // given the function's FunctionInfo object and its body. public void EmitFunction(FunctionInfo info, StmtFrag body) { // HACK snewman 8/15/01: review the way locals are handled here, to // make sure we're declaring them in precise accordance with the spec. // HACK snewman 8/15/01: add support for nested functions if (info.firstChild != null) throw new ParseError( "EmitFunction: nested functions not yet implemented", body.loc ); string functionLabel, functionName; if (info.nameInParent == null) { functionLabel = "Anonymous function"; functionName = "anon_"; } else { functionLabel = "Function \"" + info.nameInParent + "\""; functionName = info.nameInParent + "_"; } functionName = MakeUniqueMethodName(functionName); pp.Line(); pp.Line("// {0}", functionLabel); pp.Line("public static object {0}(object this_, params object[] args)", functionName); pp.Indent(); pp.Line("{"); pp.Text("JActivationObject activation = new JActivationObject(args"); foreach (string paramName in info.paramNames) pp.Text(", \"{0}\"", paramName); pp.EndLine(");"); foreach (string varName in info.locals) pp.Line("activation.Put(\"{0}\", JUndefinedObject.instance);", varName); EmitStatements(body); pp.Line("return JUndefinedObject.instance;"); pp.Line("{0} // {1}", "}", functionName); pp.Outdent(); } // EmitFunction
} // 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