public static void GenerateNodes(JSContext context, TextWriter textWriter, AstNodeList nodes) { foreach (AstNode node in nodes) { GenerateNode(context, textWriter, node); } }
public StatementListNode(NodeArgs args, AstNodeList statements) : base(args) { ChildNodes.Clear(); foreach (AstNode stmt in statements) AddChild(null, stmt); }
//Node traversal public IEnumerable <AstNode> GetAll() { AstNodeList result = new AstNodeList(); AddAll(result); return(result); }
static void RotateOpeator(BinaryExpression node, AstNodeList rightSide) { if (rightSide.Count == 0) { // the list is empty -- remove the node altogether node.Parent.ReplaceChild(node, null); } else if (rightSide.Count == 1) { // the list has only one item -- replace the node with the one item node.Parent.ReplaceChild(node, rightSide[0]); } else if (rightSide.Count == 2) { // there are only two items -- rotate the first to the left-hand side // and replace the right-hand side with the second item node.Operand1 = rightSide[0]; node.Operand2 = rightSide[1]; } else { // there will still be more than one left in the list after we peel off the // first one. rotate the first item to the left-hand side var temp = rightSide[0]; rightSide.RemoveAt(0); node.Operand1 = temp; } }
protected Procedure(AstNode definition) { if (definition.GetNodeType() == NodeType.Method)//Chapus medio alto. Mucho codigo copiado y pegado, deberia de existir alguno nodo que sea padre de MethodNode y FuncionNode para arreglar esto { ParamCount = ((MethodNode)definition).ParamList.Count; ParamNames = new string[ParamCount]; var i = 0; foreach (var param in ((MethodNode)definition).ParamList) { ParamNames[i] = ((IdentifierNode)param).Value; i++; } Stmts = new AstNodeList(((MethodNode)definition).Stmts); } else if (definition.GetNodeType() == NodeType.Function) { ParamCount = ((FunctionNode)definition).ParamList.Count; ParamNames = new string[ParamCount]; var i = 0; foreach (var param in ((FunctionNode)definition).ParamList) { ParamNames[i] = ((IdentifierNode)param).Value; i++; } Stmts = new AstNodeList(((FunctionNode)definition).Stmts); } else { throw new Exception("No se puede instanciar Function con un nodo tipo: " + (definition == null ? "null" : definition.GetNodeType().ToString())); } var name = ((IdentifierNode)definition.ChildNodes[0]).Value.ToLowerInvariant(); Name = name; }
private static void RecurseParameters(AstNodeList parameterList, AstNode node) { if (node != null) { // if this is a comma operator, then we need to var binOp = node as BinaryOperator; if (binOp != null && binOp.OperatorToken == JSToken.Comma) { // there are two or more parameters - recurse the list so we get them added left to right, // converting each one to a binding object RecurseParameters(parameterList, binOp.Operand1); // comma operators can flatten lots of commas to an element on the left, and subsequent // elements in a list on the right. var rightList = binOp.Operand2 as AstNodeList; if (rightList != null) { foreach (var listItem in rightList.Children) { parameterList.Append(ConvertToParameter(listItem, parameterList.Count)); } } else { // nope, just a single item parameterList.Append(ConvertToParameter(binOp.Operand2, parameterList.Count)); } } else { // nope; single operand to convert to a parameter parameterList.Append(ConvertToParameter(node, 0)); } } }
private Expression GetExpressionFrom(AstNodeList astNodeList) { if (astNodeList.Count == 1) { return GetExpression((Token) astNodeList[0]); } Expression firstExpression, secondExpression; var operationToken = (Token) astNodeList[1].ChildNodes[0]; if (astNodeList[0].ChildNodes != null && astNodeList[0].ChildNodes.Count != 1) { firstExpression = GetExpressionFrom(astNodeList[0].ChildNodes); } else { firstExpression = GetExpression(((Token) astNodeList[0].ChildNodes[0])); //firstExpression = Expression.Constant(decimal.Parse(((Token) astNodeList[0].ChildNodes[0]).Text), // typeof (decimal?)); } if (astNodeList[2].ChildNodes != null && astNodeList[2].ChildNodes.Count != 1) { secondExpression = GetExpressionFrom(astNodeList[2].ChildNodes); } else { secondExpression = GetExpression(((Token)astNodeList[2].ChildNodes[0])); //secondExpression = Expression.Constant(decimal.Parse(((Token) astNodeList[2].ChildNodes[0]).Text), // typeof (decimal?)); } return DoOperation(operationToken, firstExpression, secondExpression); }
public AstNodeArgs(BnfTerm term, CompilerContext context, SourceSpan span, AstNodeList childNodes) { Context = context; Term = term; Span = span; ChildNodes = childNodes; }
public virtual void Visit(AstNodeList node) { if (node != null) { AcceptChildren(node); } }
private Expression GetExpressionFrom(AstNodeList astNodeList) { if (astNodeList.Count == 1) { return(GetExpression((Token)astNodeList[0])); } Expression firstExpression, secondExpression; var operationToken = (Token)astNodeList[1].ChildNodes[0]; if (astNodeList[0].ChildNodes != null && astNodeList[0].ChildNodes.Count != 1) { firstExpression = GetExpressionFrom(astNodeList[0].ChildNodes); } else { firstExpression = GetExpression(((Token)astNodeList[0].ChildNodes[0])); //firstExpression = Expression.Constant(decimal.Parse(((Token) astNodeList[0].ChildNodes[0]).Text), // typeof (decimal?)); } if (astNodeList[2].ChildNodes != null && astNodeList[2].ChildNodes.Count != 1) { secondExpression = GetExpressionFrom(astNodeList[2].ChildNodes); } else { secondExpression = GetExpression(((Token)astNodeList[2].ChildNodes[0])); //secondExpression = Expression.Constant(decimal.Parse(((Token) astNodeList[2].ChildNodes[0]).Text), // typeof (decimal?)); } return(DoOperation(operationToken, firstExpression, secondExpression)); }
public void ResetForPrecompilation() { GraphNodeUID = 0; CodeBlockIndex = 0; RuntimeTableIndex = 0; //Initialize the dynamic string table and dynamic function table DynamicVariableTable = new DynamicVariableTable(); DynamicFunctionTable = new DynamicFunctionTable(); if (Options.SuppressBuildOutput) { // don't log any of the build related messages // just accumulate them in relevant containers with // BuildStatus object // BuildStatus = new BuildStatus(this, false, false, false); } else { BuildStatus = new BuildStatus(this, Options.BuildOptWarningAsError); } if (AstNodeList != null) { AstNodeList.Clear(); } ExpressionUID = 0; ForLoopBlockIndex = Constants.kInvalidIndex; }
public void Visit(AstNodeList node) { if (node != null) { DoesRequire = true; } }
public AstNode() { ChildNodes = new AstNodeList(); Evaluate = DoEvaluate; SetValue = DoSetValue; NodeType = AstNodeType.Unknown; }
private void AddAll(AstNodeList list) { list.Add(this); foreach (AstNode child in this.ChildNodes) { child.AddAll(list); } }
public StatementListNode(NodeArgs args, AstNodeList statements) : base(args) { ChildNodes.Clear(); foreach (AstNode stmt in statements) { AddChild(null, stmt); } }
public void ReplaceChildNodes(AstNodeList nodeList) { ChildNodes.Clear(); foreach (AstNode node in nodeList) { AddChild(node); } }
public static Expression CompileStatementList(AstNodeList childNode) { Expression lastExpressionNode = null; var block = new List<Expression>(); //they all statement but, inside the statement held the tpe foreach (var statement in childNode) { if (statement.ChildNodes[0] is VariableDeclarationNode) { //var xx; // var is ChildNodes[0] , xx is ChildNodes[1] var declaration = (VariableDeclarationNode)statement.ChildNodes[0]; VariableList.Add((ParameterExpression)declaration.GenerateExpression(statement.ChildNodes[0].ChildNodes[1])); } else if (statement.ChildNodes[0] is VariableAssignmentNode) { //xx=2+3; // xx os a variable token , = is a symbol token , expression var assignment = (VariableAssignmentNode)statement.ChildNodes[0]; var expressionNode = (ExpressionNode) statement.ChildNodes[0].ChildNodes[2]; var variableToken = (Token)statement.ChildNodes[0].ChildNodes[0]; var expression1 = expressionNode.GenerateExpression(null); var expression2 = VariableList.Get(variableToken.Text); block.Add(assignment.GenerateExpression(new TwoExpressionsDto { Expression1 = expression1, Expression2 = expression2 })); } else if (statement.ChildNodes[0] is ExpressionNode) { var expressionNode = (ExpressionNode) statement.ChildNodes[0]; lastExpressionNode = expressionNode.GenerateExpression(null); } else if (statement.ChildNodes[0] is IfStatementNode) { var expressionNode = (IfStatementNode)statement.ChildNodes[0]; block.Add(expressionNode.GenerateExpression(null)); } else { Debug.WriteLine("Whaaaaaaaat how? it seems like you changed the grammer without changing the code"); } } if (lastExpressionNode != null) { block.Add(lastExpressionNode); return Expression.Block(block); } if (block.Any()) { return Expression.Block(block); } return null; }
public FunctionCallNode(NodeArgs args, VarRefNode name, AstNodeList arguments) : base(args) { ChildNodes.Clear(); NameRef = name; NameRef.Flags |= AstNodeFlags.SuppressNotDefined; AddChild("Name", NameRef); Arguments = arguments; foreach (AstNode arg in Arguments) AddChild("Arg", arg); }//constructor
public static Expression CompileStatementList(AstNodeList childNode) { Expression lastExpressionNode = null; var block = new List <Expression>(); //they all statement but, inside the statement held the tpe foreach (var statement in childNode) { if (statement.ChildNodes[0] is VariableDeclarationNode) { //var xx; // var is ChildNodes[0] , xx is ChildNodes[1] var declaration = (VariableDeclarationNode)statement.ChildNodes[0]; VariableList.Add((ParameterExpression)declaration.GenerateExpression(statement.ChildNodes[0].ChildNodes[1])); } else if (statement.ChildNodes[0] is VariableAssignmentNode) { //xx=2+3; // xx os a variable token , = is a symbol token , expression var assignment = (VariableAssignmentNode)statement.ChildNodes[0]; var expressionNode = (ExpressionNode)statement.ChildNodes[0].ChildNodes[2]; var variableToken = (Token)statement.ChildNodes[0].ChildNodes[0]; var expression1 = expressionNode.GenerateExpression(null); var expression2 = VariableList.Get(variableToken.Text); block.Add(assignment.GenerateExpression(new TwoExpressionsDto { Expression1 = expression1, Expression2 = expression2 })); } else if (statement.ChildNodes[0] is ExpressionNode) { var expressionNode = (ExpressionNode)statement.ChildNodes[0]; lastExpressionNode = expressionNode.GenerateExpression(null); } else if (statement.ChildNodes[0] is IfStatementNode) { var expressionNode = (IfStatementNode)statement.ChildNodes[0]; block.Add(expressionNode.GenerateExpression(null)); } else { Debug.WriteLine("Whaaaaaaaat how? it seems like you changed the grammer without changing the code"); } } if (lastExpressionNode != null) { block.Add(lastExpressionNode); return(Expression.Block(block)); } if (block.Any()) { return(Expression.Block(block)); } return(null); }
public virtual void Visit(AstNodeList node) { if (node != null) { foreach (var childNode in node.Children) { childNode.Accept(this); } } }
public override void Visit(AstNodeList node) { if (node != null && node.Count > 0) { // this is really only ever not-ed when it's the right-hand operand // of a comma operator, which we flattened to decrease stack recursion. // so to logical-not this element, we only need to not the last item // in the list (because all the others are comma-separated) node[node.Count - 1].Accept(this); } }
public FunctionCallNode(NodeArgs args, VarRefNode name, AstNodeList arguments) : base(args) { ChildNodes.Clear(); NameRef = name; NameRef.Flags |= AstNodeFlags.SuppressNotDefined; AddChild("Name", NameRef); Arguments = arguments; foreach (AstNode arg in Arguments) { AddChild("Arg", arg); } }//constructor
public virtual AstNode FoldConstants() { var childrenCopy = new AstNodeList(); foreach (var child in ChildNodes) { var nChild = (child as CompilableNode).FoldConstants(); if (nChild != null) childrenCopy.Add(nChild); } ChildNodes.Clear(); ChildNodes.AddRange(childrenCopy); return this; }
private AstNode CreateNode(ActionRecord reduceAction, SourceSpan sourceSpan, AstNodeList childNodes) { IGrammarTerm nonTeminal = reduceAction.NonTerminal; AstNode result; AstNodeArgs args = new AstNodeArgs(nonTeminal, sourceSpan, childNodes); Type ntNodeType = nonTeminal.NodeType ?? typeof(AstNode); bool isList = nonTeminal.IsSet(TermOptions.IsList); if (isList && childNodes.Count > 1 && childNodes[0].Term == nonTeminal) { result = childNodes[0]; AstNode newChild = childNodes[childNodes.Count - 1]; newChild.Parent = result; result.ChildNodes.Add(newChild); return(result); } if (nonTeminal.IsSet(TermOptions.IsStarList) && childNodes.Count == 1) { childNodes = childNodes[0].ChildNodes; } if (!isList && !nonTeminal.IsSet(TermOptions.IsPunctuation) && childNodes.Count == 1) { Type childNodeType = childNodes[0].Term.NodeType ?? typeof(AstNode); if (childNodeType == ntNodeType || childNodeType.IsSubclassOf(ntNodeType)) { return(childNodes[0]); } } result = null; if (ntNodeType == typeof(AstNode)) { result = new AstNode(args); } else { ConstructorInfo ctor = ntNodeType.GetConstructor(new Type[] { typeof(AstNodeArgs) }); if (ctor == null) { throw new Exception("Failed to located constructor: " + ntNodeType.ToString() + "(AstNodeArgs args)"); } result = (AstNode)ctor.Invoke(new object[] { args }); } return(result); }
public virtual void Visit(AstNodeList node) { if (node != null) { foreach (var element in node.Children) { if (element != null) { element.Accept(this); } } } }
public CondFormNode(NodeArgs args, AstNodeList clauses, AstNode elseClause) : base(args) { ChildNodes.Clear(); Clauses = clauses; foreach (AstNode clause in clauses) { clause.Role = "Arg"; ChildNodes.Add(clause); } ElseClause = elseClause; if (ElseClause != null) { ElseClause.Role = "else"; ChildNodes.Add(ElseClause); } }
private void ExecuteReduceAction(ActionRecord action) { ParserState oldState = _currentState; int popCnt = action.PopCount; //Get new node's child nodes - these are nodes being popped from the stack AstNodeList childNodes = new AstNodeList(); for (int i = 0; i < action.PopCount; i++) { AstNode child = Stack[Stack.Count - popCnt + i].Node; if (!child.Term.IsSet(TermOptions.IsPunctuation)) { childNodes.Add(child); } } //recover state, location and pop the stack SourceSpan newNodeSpan; if (popCnt == 0) { newNodeSpan = new SourceSpan(_currentToken.Location, 0); } else { SourceLocation firstPopLoc = Stack[Stack.Count - popCnt].Node.Location; int lastPopEndPos = Stack[Stack.Count - 1].Node.Span.EndPos; newNodeSpan = new SourceSpan(firstPopLoc, lastPopEndPos - firstPopLoc.Position); _currentState = Stack[Stack.Count - popCnt].State; Stack.Pop(popCnt); } //Create new node AstNode node = CreateNode(action, newNodeSpan, childNodes); // Push node/current state into the stack Stack.Push(node, _currentState); //switch to new state ActionRecord gotoAction; if (_currentState.Actions.TryGetValue(action.NonTerminal.Key, out gotoAction)) { _currentState = gotoAction.NewState; } else { //should never happen throw new CompilerException(string.Format("Cannot find transition for input {0}; state: {1}, popped state: {2}", action.NonTerminal, oldState, _currentState)); } }//method
private static bool NotJustPrimitives(AstNodeList nodeList) { // if any node in the list isn't a constant wrapper (boolean, number, string) // or a unary (presumably a negative number), then we've got something other than // a primitive in the list (array or object) foreach (var child in nodeList) { if (!(child is ConstantWrapper) && !(child is UnaryExpression)) { return(true); } } // if we get here, then everything is a primitive return(false); }
public GlobalFunctionExpr(AstNodeArgs args) : base(args) { this.FunctionName = (Token)args.ChildNodes[0].DepthFirstTraversal().OfType <Token>().Single(); AstNodeList funcArgs = args.ChildNodes[2].ChildNodes; this.InputParameter1 = (ExpressionNode)funcArgs[0]; if (funcArgs.Count > 1) { this.InputParameter2 = (ExpressionNode)funcArgs[1]; } if (funcArgs.Count > 2) { this.InputParameter3 = (ExpressionNode)funcArgs[2]; } }
private void ExecuteReduceAction(ActionRecord action) { ParserState oldState = _currentState; int popCnt = action.PopCount; AstNodeList childNodes = new AstNodeList(); for (int i = 0; i < action.PopCount; i++) { AstNode child = _stack[_stack.Count - popCnt + i].Node; if (!child.Term.IsSet(TermOptions.IsPunctuation)) { childNodes.Add(child); } } SourceSpan newNodeSpan; if (popCnt == 0) { newNodeSpan = new SourceSpan(_currentToken.Location, 0); } else { SourceLocation firstPopLoc = _stack[_stack.Count - popCnt].Node.Location; int lastPopEndPos = _stack[_stack.Count - 1].Node.Span.EndPos; newNodeSpan = new SourceSpan(firstPopLoc, lastPopEndPos - firstPopLoc.Position); _currentState = _stack[_stack.Count - popCnt].State; _stack.Pop(popCnt); } AstNode node = CreateNode(action, newNodeSpan, childNodes); _stack.Push(node, _currentState); ActionRecord gotoAction; if (_currentState.Actions.TryGetValue(action.NonTerminal.Key, out gotoAction)) { _currentState = gotoAction.NewState; } else { throw new CompilerException(string.Format("Cannot find transition for input {0}; state: {1}, popped state: {2}", action.NonTerminal, oldState, _currentState)); } }
public static AstNodeList ToParameters(AstNode node) { AstNodeList parameterList = null; if (node != null) { parameterList = new AstNodeList(node.Context); // ignore any parentheses around the parameter(s) var groupingOperator = node as GroupingOperator; RecurseParameters( parameterList, groupingOperator != null ? groupingOperator.Operand : node); } return(parameterList); }
public virtual void Visit(AstNodeList node) { if (node != null) { var count = node.Count; for (var i = 0; i < count; i++) { var element = node[i]; if (element != null) { element.Accept(this); } } } }
public void Visit(AstNodeList node) { if (node != null) { for (var ndx = 0; ndx < node.Count; ++ndx) { if (ndx > 0) { m_writer.Write(','); } if (node[ndx] != null) { node[ndx].Accept(this); } } } }
public void Visit(AstNodeList node) { // add the names for each item in the list // this assumes that the items are name declarations! // (like parameter lists) if (node != null) { var count = node.Count; for (var i = 0; i < count; i++) { var itemNode = node[i]; if (itemNode != null) { itemNode.Accept(this); } } } }
public void Visit(AstNodeList node) { if (node != null) { for (var ndx = 0; ndx < node.Count; ++ndx) { if (ndx > 0) { m_writer.Write(','); if (m_settings.OutputMode == OutputMode.MultipleLines) { m_writer.Write(' '); } } if (node[ndx] != null) { node[ndx].Accept(this); } } } }
public ScriptExprList(AstNodeArgs args) : base(args) { exprList = (ChildNodes[0] is ScriptExpr) ? ChildNodes : ChildNodes[0].ChildNodes; }
private void ExecuteReduceAction(ActionRecord action) { ParserState oldState = _currentState; int popCnt = action.PopCount; //Get new node's child nodes - these are nodes being popped from the stack AstNodeList childNodes = new AstNodeList(); for (int i = 0; i < action.PopCount; i++) { AstNode child = Stack[Stack.Count - popCnt + i].Node; if (!child.Term.IsSet(TermOptions.IsPunctuation)) childNodes.Add(child); } //recover state, location and pop the stack SourceSpan newNodeSpan; if (popCnt == 0) { newNodeSpan = new SourceSpan(_currentToken.Location, 0); } else { SourceLocation firstPopLoc = Stack[Stack.Count - popCnt].Node.Location; int lastPopEndPos = Stack[Stack.Count - 1].Node.Span.EndPos; newNodeSpan = new SourceSpan(firstPopLoc, lastPopEndPos - firstPopLoc.Position); _currentState = Stack[Stack.Count - popCnt].State; Stack.Pop(popCnt); } //Create new node AstNode node = CreateNode(action, newNodeSpan, childNodes); // Push node/current state into the stack Stack.Push(node, _currentState); //switch to new state ActionRecord gotoAction; if (_currentState.Actions.TryGetValue(action.NonTerminal.Key, out gotoAction)) { _currentState = gotoAction.NewState; } else //should never happen throw new CompilerException( string.Format("Cannot find transition for input {0}; state: {1}, popped state: {2}", action.NonTerminal, oldState, _currentState)); }
//--------------------------------------------------------------------------------------- // MemberExpression // // Accessor : // <empty> | // Arguments Accessor // '[' Expression ']' Accessor | // '.' Identifier Accessor | // // Don't have this function throwing an exception without checking all the calling sites. // There is state in instance variable that is saved on the calling stack in some function // (i.e ParseFunction and ParseClass) and you don't want to blow up the stack //--------------------------------------------------------------------------------------- private AstNode MemberExpression(AstNode expression, List<Context> newContexts) { for (; ; ) { m_noSkipTokenSet.Add(NoSkipTokenSet.s_MemberExprNoSkipTokenSet); try { switch (m_currentToken.Token) { case JSToken.LeftParenthesis: AstNodeList args = null; RecoveryTokenException callError = null; m_noSkipTokenSet.Add(NoSkipTokenSet.s_ParenToken); try { args = ParseExpressionList(JSToken.RightParenthesis); } catch (RecoveryTokenException exc) { args = (AstNodeList)exc._partiallyComputedNode; if (IndexOfToken(NoSkipTokenSet.s_ParenToken, exc) == -1) callError = exc; // thrown later on } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ParenToken); } expression = new CallNode(expression.Context.CombineWith(args.Context), this) { Function = expression, Arguments = args, InBrackets = false }; if (null != newContexts && newContexts.Count > 0) { (newContexts[newContexts.Count - 1]).UpdateWith(expression.Context); if (!(expression is CallNode)) { expression = new CallNode(newContexts[newContexts.Count - 1], this) { Function = expression, Arguments = new AstNodeList(CurrentPositionContext(), this) }; } else { expression.Context = newContexts[newContexts.Count - 1]; } ((CallNode)expression).IsConstructor = true; newContexts.RemoveAt(newContexts.Count - 1); } if (callError != null) { callError._partiallyComputedNode = expression; throw callError; } GetNextToken(); break; case JSToken.LeftBracket: m_noSkipTokenSet.Add(NoSkipTokenSet.s_BracketToken); try { // // ROTOR parses a[b,c] as a call to a, passing in the arguments b and c. // the correct parse is a member lookup on a of c -- the "b,c" should be // a single expression with a comma operator that evaluates b but only // returns c. // So we'll change the default behavior from parsing an expression list to // parsing a single expression, but returning a single-item list (or an empty // list if there is no expression) so the rest of the code will work. // //args = ParseExpressionList(JSToken.RightBracket); GetNextToken(); args = new AstNodeList(CurrentPositionContext(), this); AstNode accessor = ParseExpression(); if (accessor != null) { args.Append(accessor); } } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_BracketToken, exc) == -1) { if (exc._partiallyComputedNode != null) { exc._partiallyComputedNode = new CallNode(expression.Context.CombineWith(m_currentToken.Clone()), this) { Function = expression, Arguments = (AstNodeList)exc._partiallyComputedNode, InBrackets = true }; } else { exc._partiallyComputedNode = expression; } throw; } else args = (AstNodeList)exc._partiallyComputedNode; } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BracketToken); } expression = new CallNode(expression.Context.CombineWith(m_currentToken.Clone()), this) { Function = expression, Arguments = args, InBrackets = true }; // there originally was code here in the ROTOR sources that checked the new context list and // changed this member call to a constructor call, effectively combining the two. I believe they // need to remain separate. // remove the close bracket token GetNextToken(); break; case JSToken.AccessField: ConstantWrapper id = null; Context nameContext = m_currentToken.Clone(); GetNextToken(); if (JSToken.Identifier != m_currentToken.Token) { string identifier = JSKeyword.CanBeIdentifier(m_currentToken.Token); if (null != identifier) { // don't report an error here -- it's actually okay to have a property name // that is a keyword which is okay to be an identifier. For instance, // jQuery has a commonly-used method named "get" to make an ajax request //ForceReportInfo(JSError.KeywordUsedAsIdentifier); id = new ConstantWrapper(identifier, PrimitiveType.String, m_currentToken.Clone(), this); } else if (JSScanner.IsValidIdentifier(m_currentToken.Code)) { // it must be a keyword, because it can't technically be an identifier, // but it IS a valid identifier format. Throw a warning but still // create the constant wrapper so we can output it as-is ReportError(JSError.KeywordUsedAsIdentifier, m_currentToken.Clone(), true); id = new ConstantWrapper(m_currentToken.Code, PrimitiveType.String, m_currentToken.Clone(), this); } else { ReportError(JSError.NoIdentifier); SkipTokensAndThrow(expression); } } else { id = new ConstantWrapper(m_scanner.Identifier, PrimitiveType.String, m_currentToken.Clone(), this); } GetNextToken(); expression = new Member(expression.Context.CombineWith(id.Context), this) { Root = expression, Name = id.Context.Code, NameContext = nameContext.CombineWith(id.Context) }; break; default: if (null != newContexts) { while (newContexts.Count > 0) { (newContexts[newContexts.Count - 1]).UpdateWith(expression.Context); expression = new CallNode(newContexts[newContexts.Count - 1], this) { Function = expression, Arguments = new AstNodeList(CurrentPositionContext(), this) }; ((CallNode)expression).IsConstructor = true; newContexts.RemoveAt(newContexts.Count - 1); } } return expression; } } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_MemberExprNoSkipTokenSet, exc) != -1) expression = exc._partiallyComputedNode; else { Debug.Assert(exc._partiallyComputedNode == expression); throw; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_MemberExprNoSkipTokenSet); } } }
private FunctionObject ParseFunction(FunctionType functionType, Context fncCtx) { Lookup name = null; AstNodeList formalParameters = null; Block body = null; bool inExpression = (functionType == FunctionType.Expression); Context paramsContext = null; GetNextToken(); // get the function name or make an anonymous function if in expression "position" if (JSToken.Identifier == m_currentToken.Token) { name = new Lookup(m_currentToken.Clone(), this) { Name = m_scanner.Identifier }; GetNextToken(); } else { string identifier = JSKeyword.CanBeIdentifier(m_currentToken.Token); if (null != identifier) { name = new Lookup(m_currentToken.Clone(), this) { Name = identifier }; GetNextToken(); } else { if (!inExpression) { // if this isn't a function expression, then we need to throw an error because // function DECLARATIONS always need a valid identifier name ReportError(JSError.NoIdentifier, m_currentToken.Clone(), true); // BUT if the current token is a left paren, we don't want to use it as the name. // (fix for issue #14152) if (m_currentToken.Token != JSToken.LeftParenthesis && m_currentToken.Token != JSToken.LeftCurly) { identifier = m_currentToken.Code; name = new Lookup(CurrentPositionContext(), this) { Name = identifier }; GetNextToken(); } } } } // make a new state and save the old one List<BlockType> blockType = m_blockType; m_blockType = new List<BlockType>(16); Dictionary<string, LabelInfo> labelTable = m_labelTable; m_labelTable = new Dictionary<string, LabelInfo>(); try { // get the formal parameters if (JSToken.LeftParenthesis != m_currentToken.Token) { // we expect a left paren at this point for standard cross-browser support. // BUT -- some versions of IE allow an object property expression to be a function name, like window.onclick. // we still want to throw the error, because it syntax errors on most browsers, but we still want to // be able to parse it and return the intended results. // Skip to the open paren and use whatever is in-between as the function name. Doesn't matter that it's // an invalid identifier; it won't be accessible as a valid field anyway. bool expandedIndentifier = false; while (m_currentToken.Token != JSToken.LeftParenthesis && m_currentToken.Token != JSToken.LeftCurly && m_currentToken.Token != JSToken.Semicolon && m_currentToken.Token != JSToken.EndOfFile) { name.Context.UpdateWith(m_currentToken); GetNextToken(); expandedIndentifier = true; } // if we actually expanded the identifier context, then we want to report that // the function name needs to be an identifier. Otherwise we didn't expand the // name, so just report that we expected an open paren at this point. if (expandedIndentifier) { name.Name = name.Context.Code; name.Context.HandleError(JSError.FunctionNameMustBeIdentifier, false); } else { ReportError(JSError.NoLeftParenthesis, true); } } if (m_currentToken.Token == JSToken.LeftParenthesis) { // create the parameter list formalParameters = new AstNodeList(m_currentToken.Clone(), this); paramsContext = m_currentToken.Clone(); // skip the open paren GetNextToken(); // create the list of arguments and update the context while (JSToken.RightParenthesis != m_currentToken.Token) { String id = null; m_noSkipTokenSet.Add(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet); try { ParameterDeclaration paramDecl = null; if (JSToken.Identifier != m_currentToken.Token && (id = JSKeyword.CanBeIdentifier(m_currentToken.Token)) == null) { if (JSToken.LeftCurly == m_currentToken.Token) { ReportError(JSError.NoRightParenthesis); break; } else if (JSToken.Comma == m_currentToken.Token) { // We're missing an argument (or previous argument was malformed and // we skipped to the comma.) Keep trying to parse the argument list -- // we will skip the comma below. ReportError(JSError.SyntaxError, true); } else { ReportError(JSError.SyntaxError, true); SkipTokensAndThrow(); } } else { if (null == id) { id = m_scanner.Identifier; } paramDecl = new ParameterDeclaration(m_currentToken.Clone(), this) { Name = id, Position = formalParameters.Count }; formalParameters.Append(paramDecl); GetNextToken(); } // got an arg, it should be either a ',' or ')' if (JSToken.RightParenthesis == m_currentToken.Token) { break; } else if (JSToken.Comma == m_currentToken.Token) { // append the comma context as the terminator for the parameter paramDecl.IfNotNull(p => p.TerminatingContext = m_currentToken.Clone()); } else { // deal with error in some "intelligent" way if (JSToken.LeftCurly == m_currentToken.Token) { ReportError(JSError.NoRightParenthesis); break; } else { if (JSToken.Identifier == m_currentToken.Token) { // it's possible that the guy was writing the type in C/C++ style (i.e. int x) ReportError(JSError.NoCommaOrTypeDefinitionError); } else ReportError(JSError.NoComma); } } GetNextToken(); } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet, exc) == -1) throw; } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet); } } fncCtx.UpdateWith(m_currentToken); GetNextToken(); } // read the function body of non-abstract functions. if (JSToken.LeftCurly != m_currentToken.Token) ReportError(JSError.NoLeftCurly, true); m_blockType.Add(BlockType.Block); m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet); m_noSkipTokenSet.Add(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); try { // parse the block locally to get the exact end of function body = new Block(m_currentToken.Clone(), this); body.BraceOnNewLine = m_foundEndOfLine; GetNextToken(); var possibleDirectivePrologue = true; while (JSToken.RightCurly != m_currentToken.Token) { try { // function body's are SourceElements (Statements + FunctionDeclarations) var statement = ParseStatement(true); if (possibleDirectivePrologue) { var constantWrapper = statement as ConstantWrapper; if (constantWrapper != null && constantWrapper.PrimitiveType == PrimitiveType.String) { // if it's already a directive prologues, we're good to go if (!(constantWrapper is DirectivePrologue)) { // make the statement a directive prologue instead of a constant wrapper statement = new DirectivePrologue(constantWrapper.Value.ToString(), constantWrapper.Context, constantWrapper.Parser) { MayHaveIssues = constantWrapper.MayHaveIssues }; } } else if (!m_newModule) { // no longer considering constant wrappers possibleDirectivePrologue = false; } } else if (m_newModule) { // we scanned into a new module -- we might find directive prologues again possibleDirectivePrologue = true; } // add it to the body body.Append(statement); } catch (RecoveryTokenException exc) { if (exc._partiallyComputedNode != null) { body.Append(exc._partiallyComputedNode); } if (IndexOfToken(NoSkipTokenSet.s_StartStatementNoSkipTokenSet, exc) == -1) throw; } } // make sure any important comments before the closing brace are kept if (m_importantComments.Count > 0 && m_settings.PreserveImportantComments && m_settings.IsModificationAllowed(TreeModifications.PreserveImportantComments)) { // we have important comments before the EOF. Add the comment(s) to the program. foreach (var importantComment in m_importantComments) { body.Append(new ImportantComment(importantComment, this)); } m_importantComments.Clear(); } body.Context.UpdateWith(m_currentToken); fncCtx.UpdateWith(m_currentToken); } catch (EndOfFileException) { // if we get an EOF here, we never had a chance to find the closing curly-brace fncCtx.HandleError(JSError.UnclosedFunction, true); } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exc) == -1) { exc._partiallyComputedNode = new FunctionObject(fncCtx, this) { FunctionType = (inExpression ? FunctionType.Expression : FunctionType.Declaration), IdContext = name.IfNotNull(n => n.Context), Name = name.IfNotNull(n => n.Name), ParameterDeclarations = formalParameters, ParametersContext = paramsContext, Body = body }; throw; } } finally { m_blockType.RemoveAt(m_blockType.Count - 1); m_noSkipTokenSet.Remove(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet); } GetNextToken(); } finally { // restore state m_blockType = blockType; m_labelTable = labelTable; } return new FunctionObject(fncCtx, this) { FunctionType = functionType, IdContext = name.IfNotNull(n => n.Context), Name = name.IfNotNull(n => n.Name), ParameterDeclarations = formalParameters, ParametersContext = paramsContext, Body = body }; }
private AstNode ParseSwitchStatement() { Context switchCtx = m_currentToken.Clone(); AstNode expr = null; AstNodeList cases = null; var braceOnNewLine = false; Context braceContext = null; m_blockType.Add(BlockType.Switch); try { // read switch(expr) GetNextToken(); if (JSToken.LeftParenthesis != m_currentToken.Token) ReportError(JSError.NoLeftParenthesis); GetNextToken(); m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); m_noSkipTokenSet.Add(NoSkipTokenSet.s_SwitchNoSkipTokenSet); try { expr = ParseExpression(); if (JSToken.RightParenthesis != m_currentToken.Token) { ReportError(JSError.NoRightParenthesis); } GetNextToken(); if (JSToken.LeftCurly != m_currentToken.Token) { ReportError(JSError.NoLeftCurly); } braceOnNewLine = m_foundEndOfLine; braceContext = m_currentToken.Clone(); GetNextToken(); } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1 && IndexOfToken(NoSkipTokenSet.s_SwitchNoSkipTokenSet, exc) == -1) { // give up exc._partiallyComputedNode = null; throw; } else { if (exc._partiallyComputedNode == null) expr = new ConstantWrapper(true, PrimitiveType.Boolean, CurrentPositionContext(), this); else expr = exc._partiallyComputedNode; if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) != -1) { if (exc._token == JSToken.RightParenthesis) GetNextToken(); if (JSToken.LeftCurly != m_currentToken.Token) { ReportError(JSError.NoLeftCurly); } braceOnNewLine = m_foundEndOfLine; braceContext = m_currentToken.Clone(); GetNextToken(); } } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_SwitchNoSkipTokenSet); m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); } // parse the switch body cases = new AstNodeList(CurrentPositionContext(), this); bool defaultStatement = false; m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet); try { while (JSToken.RightCurly != m_currentToken.Token) { SwitchCase caseClause = null; AstNode caseValue = null; var caseCtx = m_currentToken.Clone(); Context colonContext = null; m_noSkipTokenSet.Add(NoSkipTokenSet.s_CaseNoSkipTokenSet); try { if (JSToken.Case == m_currentToken.Token) { // get the case GetNextToken(); caseValue = ParseExpression(); } else if (JSToken.Default == m_currentToken.Token) { // get the default if (defaultStatement) { // we report an error but we still accept the default ReportError(JSError.DupDefault, true); } else { defaultStatement = true; } GetNextToken(); } else { // This is an error, there is no case or default. Assume a default was missing and keep going defaultStatement = true; ReportError(JSError.BadSwitch); } if (JSToken.Colon != m_currentToken.Token) { ReportError(JSError.NoColon); } else { colonContext = m_currentToken.Clone(); } // read the statements inside the case or default GetNextToken(); } catch (RecoveryTokenException exc) { // right now we can only get here for the 'case' statement if (IndexOfToken(NoSkipTokenSet.s_CaseNoSkipTokenSet, exc) == -1) { // ignore the current case or default exc._partiallyComputedNode = null; throw; } else { caseValue = exc._partiallyComputedNode; if (exc._token == JSToken.Colon) { GetNextToken(); } } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_CaseNoSkipTokenSet); } m_blockType.Add(BlockType.Block); try { var statements = new Block(m_currentToken.Clone(), this); m_noSkipTokenSet.Add(NoSkipTokenSet.s_SwitchNoSkipTokenSet); m_noSkipTokenSet.Add(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); try { while (JSToken.RightCurly != m_currentToken.Token && JSToken.Case != m_currentToken.Token && JSToken.Default != m_currentToken.Token) { try { // parse a Statement, not a SourceElement statements.Append(ParseStatement(false)); } catch (RecoveryTokenException exc) { if (exc._partiallyComputedNode != null) { statements.Append(exc._partiallyComputedNode); exc._partiallyComputedNode = null; } if (IndexOfToken(NoSkipTokenSet.s_StartStatementNoSkipTokenSet, exc) == -1) { throw; } } } } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_SwitchNoSkipTokenSet, exc) == -1) { caseClause = new SwitchCase(caseCtx, this) { CaseValue = caseValue, ColonContext = colonContext, Statements = statements }; cases.Append(caseClause); throw; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); m_noSkipTokenSet.Remove(NoSkipTokenSet.s_SwitchNoSkipTokenSet); } caseCtx.UpdateWith(statements.Context); caseClause = new SwitchCase(caseCtx, this) { CaseValue = caseValue, ColonContext = colonContext, Statements = statements }; cases.Append(caseClause); } finally { m_blockType.RemoveAt(m_blockType.Count - 1); } } } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exc) == -1) { //save what you can a rethrow switchCtx.UpdateWith(CurrentPositionContext()); exc._partiallyComputedNode = new Switch(switchCtx, this) { Expression = expr, BraceContext = braceContext, Cases = cases, BraceOnNewLine = braceOnNewLine }; throw; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet); } switchCtx.UpdateWith(m_currentToken); GetNextToken(); } finally { m_blockType.RemoveAt(m_blockType.Count - 1); } return new Switch(switchCtx, this) { Expression = expr, BraceContext = braceContext, Cases = cases, BraceOnNewLine = braceOnNewLine }; }
private AstNode ParseLeftHandSideExpression(bool isMinus) { AstNode ast = null; bool skipToken = true; List<Context> newContexts = null; TryItAgain: // new expression while (JSToken.New == m_currentToken.Token) { if (null == newContexts) newContexts = new List<Context>(4); newContexts.Add(m_currentToken.Clone()); GetNextToken(); } JSToken token = m_currentToken.Token; switch (token) { // primary expression case JSToken.Identifier: ast = new Lookup(m_currentToken.Clone(), this) { Name = m_scanner.Identifier }; break; case JSToken.ConditionalCommentStart: // skip past the start to the next token GetNextToken(); if (m_currentToken.Token == JSToken.ConditionalCompilationVariable) { // we have /*@id ast = new ConstantWrapperPP(m_currentToken.Clone(), this) { VarName = m_currentToken.Code, ForceComments = true }; GetNextToken(); if (m_currentToken.Token == JSToken.ConditionalCommentEnd) { // skip past the closing comment GetNextToken(); } else { // we ONLY support /*@id@*/ in expressions right now. If there's not // a closing comment after the ID, then we don't support it. // throw an error, skip to the end of the comment, then ignore it and start // looking for the next token. CCTooComplicated(null); goto TryItAgain; } } else if (m_currentToken.Token == JSToken.ConditionalCommentEnd) { // empty conditional comment! Ignore. GetNextToken(); goto TryItAgain; } else { // we DON'T have "/*@IDENT". We only support "/*@IDENT @*/", so since this isn't // and id, throw the error, skip to the end of the comment, and ignore it // by looping back and looking for the NEXT token. m_currentToken.HandleError(JSError.ConditionalCompilationTooComplex); // skip to end of conditional comment while (m_currentToken.Token != JSToken.EndOfFile && m_currentToken.Token != JSToken.ConditionalCommentEnd) { GetNextToken(); } GetNextToken(); goto TryItAgain; } break; case JSToken.This: ast = new ThisLiteral(m_currentToken.Clone(), this); break; case JSToken.StringLiteral: ast = new ConstantWrapper(m_scanner.StringLiteralValue, PrimitiveType.String, m_currentToken.Clone(), this) { MayHaveIssues = m_scanner.LiteralHasIssues }; break; case JSToken.IntegerLiteral: case JSToken.NumericLiteral: { Context numericContext = m_currentToken.Clone(); double doubleValue; if (ConvertNumericLiteralToDouble(m_currentToken.Code, (token == JSToken.IntegerLiteral), out doubleValue)) { // conversion worked fine // check for some boundary conditions var mayHaveIssues = m_scanner.LiteralHasIssues; if (doubleValue == double.MaxValue) { ReportError(JSError.NumericMaximum, numericContext, true); } else if (isMinus && -doubleValue == double.MinValue) { ReportError(JSError.NumericMinimum, numericContext, true); } // create the constant wrapper from the value ast = new ConstantWrapper(doubleValue, PrimitiveType.Number, numericContext, this) { MayHaveIssues = mayHaveIssues }; } else { // check to see if we went overflow if (double.IsInfinity(doubleValue)) { ReportError(JSError.NumericOverflow, numericContext, true); } // regardless, we're going to create a special constant wrapper // that simply echos the input as-is ast = new ConstantWrapper(m_currentToken.Code, PrimitiveType.Other, numericContext, this) { MayHaveIssues = true }; } break; } case JSToken.True: ast = new ConstantWrapper(true, PrimitiveType.Boolean, m_currentToken.Clone(), this); break; case JSToken.False: ast = new ConstantWrapper(false, PrimitiveType.Boolean, m_currentToken.Clone(), this); break; case JSToken.Null: ast = new ConstantWrapper(null, PrimitiveType.Null, m_currentToken.Clone(), this); break; case JSToken.ConditionalCompilationVariable: ast = new ConstantWrapperPP(m_currentToken.Clone(), this) { VarName = m_currentToken.Code, ForceComments = false }; break; case JSToken.DivideAssign: // normally this token is not allowed on the left-hand side of an expression. // BUT, this might be the start of a regular expression that begins with an equals sign! // we need to test to see if we can parse a regular expression, and if not, THEN // we can fail the parse. case JSToken.Divide: // could it be a regexp? String source = m_scanner.ScanRegExp(); if (source != null) { // parse the flags (if any) String flags = m_scanner.ScanRegExpFlags(); // create the literal ast = new RegExpLiteral(m_currentToken.Clone(), this) { Pattern = source, PatternSwitches = flags }; break; } goto default; // expression case JSToken.LeftParenthesis: { var groupingOp = new GroupingOperator(m_currentToken.Clone(), this); ast = groupingOp; GetNextToken(); m_noSkipTokenSet.Add(NoSkipTokenSet.s_ParenExpressionNoSkipToken); try { // parse an expression groupingOp.Operand = ParseExpression(); if (JSToken.RightParenthesis != m_currentToken.Token) { ReportError(JSError.NoRightParenthesis); } else { // add the closing paren to the expression context ast.Context.UpdateWith(m_currentToken); } } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_ParenExpressionNoSkipToken, exc) == -1) throw; else groupingOp.Operand = exc._partiallyComputedNode; } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ParenExpressionNoSkipToken); } } break; // array initializer case JSToken.LeftBracket: Context listCtx = m_currentToken.Clone(); GetNextToken(); AstNodeList list = new AstNodeList(CurrentPositionContext(), this); while (JSToken.RightBracket != m_currentToken.Token) { if (JSToken.Comma != m_currentToken.Token) { m_noSkipTokenSet.Add(NoSkipTokenSet.s_ArrayInitNoSkipTokenSet); try { var expression = ParseExpression(true); list.Append(expression); if (JSToken.Comma != m_currentToken.Token) { if (JSToken.RightBracket != m_currentToken.Token) { ReportError(JSError.NoRightBracket); } break; } else { // we have a comma -- skip it after adding it as a terminator // on the previous expression expression.IfNotNull(e => e.TerminatingContext = m_currentToken.Clone()); GetNextToken(); // if the next token is the closing brackets, then we need to // add a missing value to the array because we end in a comma and // we need to keep it for cross-platform compat. // TECHNICALLY, that puts an extra item into the array for most modern browsers, but not ALL. if (m_currentToken.Token == JSToken.RightBracket) { list.Append(new ConstantWrapper(Missing.Value, PrimitiveType.Other, m_currentToken.Clone(), this)); } } } catch (RecoveryTokenException exc) { if (exc._partiallyComputedNode != null) list.Append(exc._partiallyComputedNode); if (IndexOfToken(NoSkipTokenSet.s_ArrayInitNoSkipTokenSet, exc) == -1) { listCtx.UpdateWith(CurrentPositionContext()); exc._partiallyComputedNode = new ArrayLiteral(listCtx, this) { Elements = list }; throw; } else { if (JSToken.RightBracket == m_currentToken.Token) break; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ArrayInitNoSkipTokenSet); } } else { // comma -- missing array item in the list list.Append(new ConstantWrapper(Missing.Value, PrimitiveType.Other, m_currentToken.Clone(), this) { TerminatingContext = m_currentToken.Clone() }); // skip over the comma GetNextToken(); // if the next token is the closing brace, then we end with a comma -- and we need to // add ANOTHER missing value to make sure this last comma doesn't get left off. // TECHNICALLY, that puts an extra item into the array for most modern browsers, but not ALL. if (m_currentToken.Token == JSToken.RightBracket) { list.Append(new ConstantWrapper(Missing.Value, PrimitiveType.Other, m_currentToken.Clone(), this)); } } } listCtx.UpdateWith(m_currentToken); ast = new ArrayLiteral(listCtx, this) { Elements = list }; break; // object initializer case JSToken.LeftCurly: Context objCtx = m_currentToken.Clone(); GetNextToken(); var propertyList = new AstNodeList(CurrentPositionContext(), this); if (JSToken.RightCurly != m_currentToken.Token) { for (; ; ) { ObjectLiteralField field = null; AstNode value = null; bool getterSetter = false; string ident; switch (m_currentToken.Token) { case JSToken.Identifier: field = new ObjectLiteralField(m_scanner.Identifier, PrimitiveType.String, m_currentToken.Clone(), this); break; case JSToken.StringLiteral: field = new ObjectLiteralField(m_scanner.StringLiteralValue, PrimitiveType.String, m_currentToken.Clone(), this) { MayHaveIssues = m_scanner.LiteralHasIssues }; break; case JSToken.IntegerLiteral: case JSToken.NumericLiteral: { double doubleValue; if (ConvertNumericLiteralToDouble(m_currentToken.Code, (m_currentToken.Token == JSToken.IntegerLiteral), out doubleValue)) { // conversion worked fine field = new ObjectLiteralField( doubleValue, PrimitiveType.Number, m_currentToken.Clone(), this ); } else { // something went wrong and we're not sure the string representation in the source is // going to convert to a numeric value well if (double.IsInfinity(doubleValue)) { ReportError(JSError.NumericOverflow, m_currentToken.Clone(), true); } // use the source as the field name, not the numeric value field = new ObjectLiteralField( m_currentToken.Code, PrimitiveType.Other, m_currentToken.Clone(), this); } break; } case JSToken.Get: case JSToken.Set: if (PeekToken() == JSToken.Colon) { // the field is either "get" or "set" and isn't the special Mozilla getter/setter field = new ObjectLiteralField(m_currentToken.Code, PrimitiveType.String, m_currentToken.Clone(), this); } else { // ecma-script get/set property construct getterSetter = true; bool isGet = (m_currentToken.Token == JSToken.Get); value = ParseFunction( (JSToken.Get == m_currentToken.Token ? FunctionType.Getter : FunctionType.Setter), m_currentToken.Clone() ); FunctionObject funcExpr = value as FunctionObject; if (funcExpr != null) { // getter/setter is just the literal name with a get/set flag field = new GetterSetter( funcExpr.Name, isGet, funcExpr.IdContext.Clone(), this ); } else { ReportError(JSError.FunctionExpressionExpected); } } break; default: // NOT: identifier token, string, number, or getter/setter. // see if it's a token that COULD be an identifierName. ident = m_scanner.Identifier; if (JSScanner.IsValidIdentifier(ident)) { // BY THE SPEC, if it's a valid identifierName -- which includes reserved words -- then it's // okay for object literal syntax. However, reserved words here won't work in all browsers, // so if it is a reserved word, let's throw a low-sev cross-browser warning on the code. if (JSKeyword.CanBeIdentifier(m_currentToken.Token) == null) { ReportError(JSError.ObjectLiteralKeyword, m_currentToken.Clone(), true); } field = new ObjectLiteralField(ident, PrimitiveType.String, m_currentToken.Clone(), this); } else { // throw an error but use it anyway, since that's what the developer has going on ReportError(JSError.NoMemberIdentifier, m_currentToken.Clone(), true); field = new ObjectLiteralField(m_currentToken.Code, PrimitiveType.String, m_currentToken.Clone(), this); } break; } if (field != null) { if (!getterSetter) { GetNextToken(); } m_noSkipTokenSet.Add(NoSkipTokenSet.s_ObjectInitNoSkipTokenSet); try { if (!getterSetter) { // get the value if (JSToken.Colon != m_currentToken.Token) { ReportError(JSError.NoColon, true); value = ParseExpression(true); } else { field.ColonContext = m_currentToken.Clone(); GetNextToken(); value = ParseExpression(true); } } // put the pair into the list of fields var propCtx = field.Context.Clone().CombineWith(value.IfNotNull(v => v.Context)); var property = new ObjectLiteralProperty(propCtx, this) { Name = field, Value = value }; propertyList.Append(property); if (JSToken.RightCurly == m_currentToken.Token) { break; } else { if (JSToken.Comma == m_currentToken.Token) { // skip the comma after adding it to the property as a terminating context property.IfNotNull(p => p.TerminatingContext = m_currentToken.Clone()); GetNextToken(); // if the next token is the right-curly brace, then we ended // the list with a comma, which is perfectly fine if (m_currentToken.Token == JSToken.RightCurly) { break; } } else { if (m_foundEndOfLine) { ReportError(JSError.NoRightCurly); } else ReportError(JSError.NoComma, true); SkipTokensAndThrow(); } } } catch (RecoveryTokenException exc) { if (exc._partiallyComputedNode != null) { // the problem was in ParseExpression trying to determine value value = exc._partiallyComputedNode; var propCtx = field.Context.Clone().CombineWith(value.IfNotNull(v => v.Context)); var property = new ObjectLiteralProperty(propCtx, this) { Name = field, Value = value }; propertyList.Append(property); } if (IndexOfToken(NoSkipTokenSet.s_ObjectInitNoSkipTokenSet, exc) == -1) { exc._partiallyComputedNode = new ObjectLiteral(objCtx, this) { Properties = propertyList }; throw; } else { if (JSToken.Comma == m_currentToken.Token) GetNextToken(); if (JSToken.RightCurly == m_currentToken.Token) break; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ObjectInitNoSkipTokenSet); } } } } objCtx.UpdateWith(m_currentToken); ast = new ObjectLiteral(objCtx, this) { Properties = propertyList }; break; // function expression case JSToken.Function: ast = ParseFunction(FunctionType.Expression, m_currentToken.Clone()); skipToken = false; break; case JSToken.AspNetBlock: ast = new AspNetBlockNode(m_currentToken.Clone(), this) { AspNetBlockText = m_currentToken.Code }; break; default: string identifier = JSKeyword.CanBeIdentifier(m_currentToken.Token); if (null != identifier) { ast = new Lookup(m_currentToken.Clone(), this) { Name = identifier }; } else { ReportError(JSError.ExpressionExpected); SkipTokensAndThrow(); } break; } // can be a CallExpression, that is, followed by '.' or '(' or '[' if (skipToken) GetNextToken(); return MemberExpression(ast, newContexts); }
// // expression elements we shouldn't get to // public void Visit(AstNodeList node) { Debug.Fail("shouldn't get here"); }
public void Visit(AstNodeList node) { if (node != null) { // don't bother setting the order of the list itself, just the items for (var ndx = 0; ndx < node.Count; ++ndx) { var item = node[ndx]; if (item != null) { item.Accept(this); } } } }
public void Visit(AstNodeList node) { // not applicable; terminate }
private AstNode CreateNode(ActionRecord reduceAction, SourceSpan sourceSpan, AstNodeList childNodes) { IGrammarTerm nonTeminal = reduceAction.NonTerminal; AstNode result; AstNodeArgs args = new AstNodeArgs(nonTeminal, sourceSpan, childNodes); Type ntNodeType = nonTeminal.NodeType ?? typeof(AstNode); bool isList = nonTeminal.IsSet(TermOptions.IsList); if (isList && childNodes.Count > 1 && childNodes[0].Term == nonTeminal) { result = childNodes[0]; AstNode newChild = childNodes[childNodes.Count - 1]; newChild.Parent = result; result.ChildNodes.Add(newChild); return result; } if (nonTeminal.IsSet(TermOptions.IsStarList) && childNodes.Count == 1) { childNodes = childNodes[0].ChildNodes; } if (!isList && !nonTeminal.IsSet(TermOptions.IsPunctuation) && childNodes.Count == 1) { Type childNodeType = childNodes[0].Term.NodeType ?? typeof(AstNode); if (childNodeType == ntNodeType || childNodeType.IsSubclassOf(ntNodeType)) return childNodes[0]; } result = null; if (ntNodeType == typeof(AstNode)) { result = new AstNode(args); } else { ConstructorInfo ctor = ntNodeType.GetConstructor(new Type[] { typeof(AstNodeArgs) }); if (ctor == null) throw new Exception("Failed to located constructor: " + ntNodeType.ToString() + "(AstNodeArgs args)"); result = (AstNode)ctor.Invoke(new object[] { args }); } return result; }
/// <summary> /// Parse the source code using the given settings, getting back an abstract syntax tree Block node as the root /// representing the list of statements in the source code. /// </summary> /// <param name="settings">code settings to use to process the source code</param> /// <returns>root Block node representing the top-level statements</returns> public Block Parse(CodeSettings settings) { // initialize the scanner with our settings // make sure the RawTokens setting is OFF or we won't be able to create our AST InitializeScanner(settings); // make sure we initialize the global scope's strict mode to our flag, whether or not it // is true. This means if the setting is false, we will RESET the flag to false if we are // reusing the scope and a previous Parse call had code that set it to strict with a // program directive. GlobalScope.UseStrict = m_settings.StrictMode; // make sure the global scope knows about our known global names GlobalScope.SetAssumedGlobals(m_settings); // start of a new module m_newModule = true; Block scriptBlock; Block returnBlock; switch (m_settings.SourceMode) { case JavaScriptSourceMode.Program: // simply parse a block of statements returnBlock = scriptBlock = ParseStatements(); break; case JavaScriptSourceMode.Expression: // create a block, get the first token, add in the parse of a single expression, // and we'll go fron there. returnBlock = scriptBlock = new Block(CurrentPositionContext(), this); GetNextToken(); try { var expr = ParseExpression(); if (expr != null) { scriptBlock.Append(expr); scriptBlock.UpdateWith(expr.Context); } } catch (EndOfFileException) { Debug.WriteLine("EOF"); } break; case JavaScriptSourceMode.EventHandler: // we're going to create the global block, add in a function expression with a single // parameter named "event", and then we're going to parse the input as the body of that // function expression. We're going to resolve the global block, but only return the body // of the function. scriptBlock = new Block(null, this); var parameters = new AstNodeList(null, this); parameters.Append(new ParameterDeclaration(null, this) { Name = "event", RenameNotAllowed = true }); var funcExpression = new FunctionObject(null, this) { FunctionType = FunctionType.Expression, ParameterDeclarations = parameters }; scriptBlock.Append(funcExpression); returnBlock = ParseStatements(); funcExpression.Body = returnBlock; break; default: Debug.Fail("Unexpected source mode enumeration"); return null; } // resolve everything ResolutionVisitor.Apply(scriptBlock, GlobalScope, m_settings); if (scriptBlock != null && Settings.MinifyCode) { // this visitor doesn't just reorder scopes. It also combines the adjacent var variables, // unnests blocks, identifies prologue directives, and sets the strict mode on scopes. ReorderScopeVisitor.Apply(scriptBlock, this); // analyze the entire node tree (needed for hypercrunch) // root to leaf (top down) var analyzeVisitor = new AnalyzeNodeVisitor(this); scriptBlock.Accept(analyzeVisitor); // analyze the scope chain (also needed for hypercrunch) // root to leaf (top down) m_globalScope.AnalyzeScope(); // if we want to crunch any names.... if (m_settings.LocalRenaming != LocalRenaming.KeepAll && m_settings.IsModificationAllowed(TreeModifications.LocalRenaming)) { // then do a top-down traversal of the scope tree. For each field that had not // already been crunched (globals and outers will already be crunched), crunch // the name with a crunch iterator that does not use any names in the verboten set. m_globalScope.AutoRenameFields(); } // if we want to evaluate literal expressions, do so now if (m_settings.EvalLiteralExpressions) { var visitor = new EvaluateLiteralVisitor(this); scriptBlock.Accept(visitor); } // if any of the conditions we check for in the final pass are available, then // make the final pass if (m_settings.IsModificationAllowed(TreeModifications.BooleanLiteralsToNotOperators)) { var visitor = new FinalPassVisitor(this); scriptBlock.Accept(visitor); } // we want to walk all the scopes to make sure that any generated // variables that haven't been crunched have been assigned valid // variable names that don't collide with any existing variables. m_globalScope.ValidateGeneratedNames(); } if (returnBlock.Parent != null) { returnBlock.Parent = null; } return returnBlock; }
internal AstNodeArgs(IGrammarTerm term, SourceSpan span, AstNodeList childNodes) { Term = term; Span = span; ChildNodes = childNodes; }
// Override this method in language grammar if you want a custom node creation mechanism. public virtual AstNode CreateNode(CompilerContext context, ActionRecord reduceAction, SourceSpan sourceSpan, AstNodeList childNodes) { return null; }
//--------------------------------------------------------------------------------------- // ParseExpressionList // // Given a starting this.currentToken '(' or '[', parse a list of expression separated by // ',' until matching ')' or ']' //--------------------------------------------------------------------------------------- private AstNodeList ParseExpressionList(JSToken terminator) { Context listCtx = m_currentToken.Clone(); GetNextToken(); AstNodeList list = new AstNodeList(listCtx, this); if (terminator != m_currentToken.Token) { for (; ; ) { m_noSkipTokenSet.Add(NoSkipTokenSet.s_ExpressionListNoSkipTokenSet); try { AstNode item; if (JSToken.Comma == m_currentToken.Token) { item = new ConstantWrapper(Missing.Value, PrimitiveType.Other, m_currentToken.Clone(), this); list.Append(item); } else if (terminator == m_currentToken.Token) { break; } else { item = ParseExpression(true); list.Append(item); } if (terminator == m_currentToken.Token) { break; } else { if (JSToken.Comma == m_currentToken.Token) { item.IfNotNull(n => n.TerminatingContext = m_currentToken.Clone()); } else { if (terminator == JSToken.RightParenthesis) { // in ASP+ it's easy to write a semicolon at the end of an expression // not realizing it is going to go inside a function call // (ie. Response.Write()), so make a special check here if (JSToken.Semicolon == m_currentToken.Token) { if (JSToken.RightParenthesis == PeekToken()) { ReportError(JSError.UnexpectedSemicolon, true); GetNextToken(); break; } } ReportError(JSError.NoRightParenthesisOrComma); } else { ReportError(JSError.NoRightBracketOrComma); } SkipTokensAndThrow(); } } } catch (RecoveryTokenException exc) { if (exc._partiallyComputedNode != null) list.Append(exc._partiallyComputedNode); if (IndexOfToken(NoSkipTokenSet.s_ExpressionListNoSkipTokenSet, exc) == -1) { exc._partiallyComputedNode = list; throw; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ExpressionListNoSkipTokenSet); } GetNextToken(); } } listCtx.UpdateWith(m_currentToken); return list; }
}//method private AstNode CreateNode(ActionRecord reduceAction, SourceSpan sourceSpan, AstNodeList childNodes) { NonTerminal nt = reduceAction.NonTerminal; AstNode result; NodeArgs nodeArgs = new NodeArgs(_context, nt, sourceSpan, childNodes); if (nt.NodeCreator != null) { result = nt.NodeCreator(nodeArgs); if (result != null) return result; } Type defaultNodeType = _context.Compiler.Grammar.DefaultNodeType; Type ntNodeType = nt.NodeType ?? defaultNodeType ?? typeof(AstNode); // Check if NonTerminal is a list // List nodes are produced by .Plus() or .Star() methods of BnfElement // In this case, we have a left-recursive list formation production: // ntList -> ntList + delim? + ntElem // We check if we have already created the list node for ntList (in the first child); // if yes, we use this child as a result directly, without creating new list node. // The other incoming child - the last one - is a new list member; // we simply add it to child list of the result ntList node. Optional "delim" node is simply thrown away. bool isList = nt.IsSet(TermOptions.IsList); if (isList && childNodes.Count > 1 && childNodes[0].Term == nt) { result = childNodes[0]; AstNode newChild = childNodes[childNodes.Count - 1]; newChild.Parent = result; result.ChildNodes.Add(newChild); return result; } //Check for StarList produced by MakeStarRule; in this case the production is: ntList -> Empty | Elem+ // where Elem+ is non-empty list of elements. The child list we are actually interested in is one-level lower if (nt.IsSet(TermOptions.IsStarList) && childNodes.Count == 1) { childNodes = childNodes[0].ChildNodes; } // Check for "node-bubbling" case. For identity productions like // A -> B // the child node B is usually a subclass of node A, // so child node B can be used directly in place of the A. So we simply return child node as a result. // TODO: probably need a grammar option to enable/disable this behavior explicitly bool canBubble = (Data.Grammar.FlagIsSet(LanguageFlags.BubbleNodes)) && !isList && !nt.IsSet(TermOptions.IsPunctuation) && childNodes.Count == 1 && (childNodes[0].Term is NonTerminal); if (canBubble) { NonTerminal childNT = childNodes[0].Term as NonTerminal; Type childNodeType = childNT.NodeType ?? defaultNodeType ?? typeof(AstNode); if (childNodeType == ntNodeType || childNodeType.IsSubclassOf(ntNodeType)) return childNodes[0]; } // Try using Grammar's CreateNode method result = Data.Grammar.CreateNode(_context, reduceAction, sourceSpan, childNodes); if (result != null) return result; //Finally create node directly. For perf reasons we try using "new" for AstNode type (faster), and // activator for all custom types (slower) if (ntNodeType == typeof(AstNode)) return new AstNode(nodeArgs); // if (ntNodeType.GetConstructor(new Type[] {typeof(AstNodeList)}) != null) // return (AstNode)Activator.CreateInstance(ntNodeType, childNodes); if (ntNodeType.GetConstructor(new Type[] {typeof(NodeArgs)}) != null) return (AstNode) Activator.CreateInstance(ntNodeType, nodeArgs); //The following should never happen - we check that constructor exists when we validate grammar. string msg = string.Format( @"AST Node class {0} does not have a constructor for automatic node creation. Provide a constructor with a single NodeArgs parameter, or use NodeCreator delegate property in NonTerminal.", ntNodeType); throw new GrammarErrorException(msg); }
private AstNode CreateNode(ActionRecord reduceAction, SourceSpan sourceSpan, AstNodeList childNodes) { NonTerminal nt = reduceAction.NonTerminal; AstNode result; AstNodeArgs args = new AstNodeArgs(nt, _context, sourceSpan, childNodes); result = nt.InvokeNodeCreator(args); if (result != null) return result; Type defaultNodeType = _context.Compiler.Grammar.DefaultNodeType; Type ntNodeType = nt.NodeType ?? defaultNodeType ?? typeof(AstNode); // Check if NonTerminal is a list // List nodes are produced by .Plus() or .Star() methods of BnfElement // In this case, we have a left-recursive list formation production: // ntList -> ntList + delim? + ntElem // We check if we have already created the list node for ntList (in the first child); // if yes, we use this child as a result directly, without creating new list node. // The other incoming child - the last one - is a new list member; // we simply add it to child list of the result ntList node. Optional "delim" node is simply thrown away. bool isList = nt.IsSet(TermOptions.IsList); if (isList && childNodes.Count > 1 && childNodes[0].Term == nt) { result = childNodes[0]; AstNode newChild = childNodes[childNodes.Count - 1]; newChild.Parent = result; result.ChildNodes.Add(newChild); return result; } //Check for StarList produced by MakeStarList; in this case the production is: ntList -> Empty | Elem+ // where Elem+ is non-empty list of elements. The child list we are actually interested in is one-level lower if (nt.IsSet(TermOptions.IsStarList) && childNodes.Count == 1) { childNodes = childNodes[0].ChildNodes; } // Check for "node-bubbling" case. For identity productions like // A -> B // the child node B is usually a subclass of node A, // so child node B can be used directly in place of the A. So we simply return child node as a result. // TODO: probably need a grammar option to enable/disable this behavior explicitly if (!isList && !nt.IsSet(TermOptions.IsPunctuation) && childNodes.Count == 1) { Type childNodeType = childNodes[0].Term.NodeType ?? defaultNodeType ?? typeof(AstNode); if (childNodeType == ntNodeType || childNodeType.IsSubclassOf(ntNodeType)) return childNodes[0]; } // Try using Grammar's CreateNode method result = Data.Grammar.CreateNode(_context, reduceAction, sourceSpan, childNodes); if (result == null) { //Finally create node directly. For perf reasons we try using "new" for AstNode type (faster), and // activator for all custom types (slower) if (ntNodeType == typeof(AstNode)) result = new AstNode(args); else #if PocketPC || SILVERLIGHT { ConstructorInfo ctor = ntNodeType.GetConstructor(new Type[] { typeof(AstNodeArgs) }); if (ctor == null) throw new Exception("Failed to located constructor: " + ntNodeType.ToString() + "(AstNodeArgs args)"); result = (AstNode)ctor.Invoke(new object[] { args }); } #else { result = (AstNode)Activator.CreateInstance(ntNodeType, args); } #endif } if (result != null) nt.OnNodeCreated(result); return result; }
// // IVisitor implementations // public override void Visit(AstNodeList node) { if (node != null) { var commaOperator = node.Parent as CommaOperator; AstNodeList list; if (commaOperator != null && (list = commaOperator.Operand2 as AstNodeList) != null) { // this list is part of a comma-operator, which is a collection of contiguous // expression statements that we combined together. What we want to do is // delete all constant elements from the list. // if the parent is a block, then this was just a collection of statements and // we can delete ALL constant expressions. But if the parent is not a block, then // we will want to keep the last one as-is because it is the return value of the // overall expression. for (var ndx = list.Count - (node.Parent is Block ? 1 : 2); ndx >= 0; --ndx) { if (list[ndx] is ConstantWrapper) { list.RemoveAt(ndx); } } } // then normally recurse whatever is left over base.Visit(node); } }
private void ExecuteReduceAction(ActionRecord action) { ParserState oldState = _currentState; int popCnt = action.PopCount; AstNodeList childNodes = new AstNodeList(); for (int i = 0; i < action.PopCount; i++) { AstNode child = _stack[_stack.Count - popCnt + i].Node; if (!child.Term.IsSet(TermOptions.IsPunctuation)) childNodes.Add(child); } SourceSpan newNodeSpan; if (popCnt == 0) { newNodeSpan = new SourceSpan(_currentToken.Location, 0); } else { SourceLocation firstPopLoc = _stack[_stack.Count - popCnt].Node.Location; int lastPopEndPos = _stack[_stack.Count - 1].Node.Span.EndPos; newNodeSpan = new SourceSpan(firstPopLoc, lastPopEndPos - firstPopLoc.Position); _currentState = _stack[_stack.Count - popCnt].State; _stack.Pop(popCnt); } AstNode node = CreateNode(action, newNodeSpan, childNodes); _stack.Push(node, _currentState); ActionRecord gotoAction; if (_currentState.Actions.TryGetValue(action.NonTerminal.Key, out gotoAction)) { _currentState = gotoAction.NewState; } else throw new CompilerException(string.Format("Cannot find transition for input {0}; state: {1}, popped state: {2}", action.NonTerminal, oldState, _currentState)); }