public void Visit(UnaryOperator node) { if (node != null) { if (node.Operand != null) { node.Operand.Accept(this); } node.Index = NextOrderIndex; } }
public override void Visit(UnaryOperator node) { if (node != null && !node.OperatorInConditionalCompilationComment) { // if this is a unary logical-not operator, then we will just remove the // logical-not operation if (node.OperatorToken == JSToken.LogicalNot) { if (m_measure) { // measure // removes the not operator character, but also might remove parens that we would // no longer need. --m_delta; if (node.Operand is BinaryOperator || node.Operand is Conditional || node.Operand is GroupingOperator) { // those operators are lesser-precedence than the logical-not coperator and would've // added parens that we now don't need m_delta -= 2; } } else { // convert // just replace the not with its own operand, unless the child // itself is a grouping operator, in which case we will replace it // with the grouping operand to get rid of the parens var grouping = node.Operand as GroupingOperator; if (grouping != null) { node.Parent.ReplaceChild(node, grouping.Operand); } else { node.Parent.ReplaceChild(node, node.Operand); } } } else { // same logic as most nodes for the other operators TypicalHandler(node); } } }
public void Visit(UnaryOperator node) { if (node != null) { // only a negation is allowed -- and even then, I'm not sure // if it has already been integrated into the numeric value yet. if (node.OperatorToken == JSToken.Minus) { m_writer.Write('-'); if (node.Operand != null) { node.Operand.Accept(this); } } else { // invalid! ignore IsValid = false; } } }
public void Visit(UnaryOperator node) { // lesser precedence than the new operator; use parens m_needsParens = true; }
public void Visit(UnaryOperator node) { // not applicable; terminate }
public override void Visit(BinaryOperator node) { if (node != null) { base.Visit(node); // see if this operation is subtracting zero from a lookup -- that is typically done to // coerce a value to numeric. There's a simpler way: unary plus operator. if (node.OperatorToken == JSToken.Minus && m_parser.Settings.IsModificationAllowed(TreeModifications.SimplifyStringToNumericConversion)) { Lookup lookup = node.Operand1 as Lookup; if (lookup != null) { ConstantWrapper right = node.Operand2 as ConstantWrapper; if (right != null && right.IsIntegerLiteral && right.ToNumber() == 0) { // okay, so we have "lookup - 0" // this is done frequently to force a value to be numeric. // There is an easier way: apply the unary + operator to it. // transform: lookup - 0 => +lookup var unary = new UnaryOperator(node.Context, m_parser) { Operand = lookup, OperatorToken = JSToken.Plus }; node.Parent.ReplaceChild(node, unary); // because we recursed at the top of this function, we don't need to Analyze // the new Unary node. This visitor's method for UnaryOperator only does something // if the operand is a constant -- and this one is a Lookup. And we already analyzed // the lookup. } } } else if ((node.OperatorToken == JSToken.StrictEqual || node.OperatorToken == JSToken.StrictNotEqual) && m_parser.Settings.IsModificationAllowed(TreeModifications.ReduceStrictOperatorIfTypesAreSame)) { PrimitiveType leftType = node.Operand1.FindPrimitiveType(); if (leftType != PrimitiveType.Other) { PrimitiveType rightType = node.Operand2.FindPrimitiveType(); if (leftType == rightType) { // the are the same known types. We can reduce the operators node.OperatorToken = node.OperatorToken == JSToken.StrictEqual ? JSToken.Equal : JSToken.NotEqual; } else if (rightType != PrimitiveType.Other) { // they are not the same, but they are both known. We can completely remove the operator // and replace it with true (!==) or false (===). // transform: x !== y => true // transform: x === y => false node.Context.HandleError(JSError.StrictComparisonIsAlwaysTrueOrFalse, false); node.Parent.ReplaceChild( node, new ConstantWrapper(node.OperatorToken == JSToken.StrictNotEqual, PrimitiveType.Boolean, node.Context, m_parser)); // because we are essentially removing the node from the AST, be sure to detach any references DetachReferences.Apply(node); } } } else if (node.IsAssign) { var lookup = node.Operand1 as Lookup; if (lookup != null) { if (lookup.VariableField != null && lookup.VariableField.InitializationOnly) { // the field is an initialization-only field -- we should NOT be assigning to it lookup.Context.HandleError(JSError.AssignmentToConstant, true); } else if (m_scopeStack.Peek().UseStrict) { if (lookup.VariableField == null || lookup.VariableField.FieldType == FieldType.UndefinedGlobal) { // strict mode cannot assign to undefined fields node.Operand1.Context.HandleError(JSError.StrictModeUndefinedVariable, true); } else if(lookup.VariableField.FieldType == FieldType.Arguments || (lookup.VariableField.FieldType == FieldType.Predefined && string.CompareOrdinal(lookup.Name, "eval") == 0)) { // strict mode cannot assign to lookup "eval" or "arguments" node.Operand1.Context.HandleError(JSError.StrictModeInvalidAssign, true); } } } } else if ((node.Parent is Block || (node.Parent is CommaOperator && node.Parent.Parent is Block)) && (node.OperatorToken == JSToken.LogicalOr || node.OperatorToken == JSToken.LogicalAnd)) { // this is an expression statement where the operator is || or && -- basically // it's a shortcut for an if-statement: // expr1&&expr2; ==> if(expr1)expr2; // expr1||expr2; ==> if(!expr1)expr2; // let's check to see if the not of expr1 is smaller. If so, we can not the expression // and change the operator var logicalNot = new LogicalNot(node.Operand1, node.Parser); if (logicalNot.Measure() < 0) { // it would be smaller! Change it. // transform: expr1&&expr2 => !expr1||expr2 // transform: expr1||expr2 => !expr1&&expr2 logicalNot.Apply(); node.OperatorToken = node.OperatorToken == JSToken.LogicalAnd ? JSToken.LogicalOr : JSToken.LogicalAnd; } } } }
public override void Visit(UnaryOperator node) { if (node != null) { // depth-first base.Visit(node); DoUnaryNode(node); } }
private void DoUnaryNode(UnaryOperator node) { if (!node.OperatorInConditionalCompilationComment && m_parser.Settings.IsModificationAllowed(TreeModifications.EvaluateNumericExpressions)) { var literalOperand = node.Operand as ConstantWrapper; switch(node.OperatorToken) { case JSToken.Void: // see if our operand is a ConstantWrapper if (literalOperand != null) { // either number, string, boolean, or null. // the void operator evaluates its operand and returns undefined. Since evaluating a literal // does nothing, then it doesn't matter what the heck it is. Replace it with a zero -- a one- // character literal. node.Operand = new ConstantWrapper(0, PrimitiveType.Number, node.Context, m_parser); } break; case JSToken.TypeOf: if (literalOperand != null) { // either number, string, boolean, or null. // the operand is a literal. Therefore we already know what the typeof // operator will return. Just short-circuit that behavior now and replace the operator // with a string literal of the proper value string typeName = null; if (literalOperand.IsStringLiteral) { // "string" typeName = "string"; } else if (literalOperand.IsNumericLiteral) { // "number" typeName = "number"; } else if (literalOperand.IsBooleanLiteral) { // "boolean" typeName = "boolean"; } else if (literalOperand.Value == null) { // "object" typeName = "object"; } if (!string.IsNullOrEmpty(typeName)) { ReplaceNodeWithLiteral(node, new ConstantWrapper(typeName, PrimitiveType.String, node.Context, m_parser)); } } else if (node.Operand is ObjectLiteral) { ReplaceNodeWithLiteral(node, new ConstantWrapper("object", PrimitiveType.String, node.Context, m_parser)); } break; case JSToken.Plus: if (literalOperand != null) { try { // replace with a constant representing operand.ToNumber, ReplaceNodeWithLiteral(node, new ConstantWrapper(literalOperand.ToNumber(), PrimitiveType.Number, node.Context, m_parser)); } catch (InvalidCastException) { // some kind of casting in ToNumber caused a situation where we don't want // to perform the combination on these operands } } break; case JSToken.Minus: if (literalOperand != null) { try { // replace with a constant representing the negative of operand.ToNumber ReplaceNodeWithLiteral(node, new ConstantWrapper(-literalOperand.ToNumber(), PrimitiveType.Number, node.Context, m_parser)); } catch (InvalidCastException) { // some kind of casting in ToNumber caused a situation where we don't want // to perform the combination on these operands } } break; case JSToken.BitwiseNot: if (literalOperand != null) { try { // replace with a constant representing the bitwise-not of operant.ToInt32 ReplaceNodeWithLiteral(node, new ConstantWrapper(Convert.ToDouble(~literalOperand.ToInt32()), PrimitiveType.Number, node.Context, m_parser)); } catch (InvalidCastException) { // some kind of casting in ToNumber caused a situation where we don't want // to perform the combination on these operands } } break; case JSToken.LogicalNot: if (literalOperand != null) { // replace with a constant representing the opposite of operand.ToBoolean try { ReplaceNodeWithLiteral(node, new ConstantWrapper(!literalOperand.ToBoolean(), PrimitiveType.Boolean, node.Context, m_parser)); } catch (InvalidCastException) { // ignore any invalid cast exceptions } } break; } } }
public override void Visit(UnaryOperator node) { if (node != null) { base.Visit(node); // strict mode has some restrictions if (node.OperatorToken == JSToken.Delete) { if (m_scopeStack.Peek().UseStrict) { // operand of a delete operator cannot be a variable name, argument name, or function name // which means it can't be a lookup if (node.Operand is Lookup) { node.Context.HandleError(JSError.StrictModeInvalidDelete, true); } } } else if (node.OperatorToken == JSToken.Increment || node.OperatorToken == JSToken.Decrement) { var lookup = node.Operand as Lookup; if (lookup != null) { if (lookup.VariableField != null && lookup.VariableField.InitializationOnly) { // can't increment or decrement a constant! lookup.Context.HandleError(JSError.AssignmentToConstant, true); } // and strict mode has some restrictions we want to check now if (m_scopeStack.Peek().UseStrict) { // the operator cannot be the eval function or arguments object. // that means the operator is a lookup, and the field for that lookup // is the arguments object or the predefined "eval" object. if (lookup.VariableField == null || lookup.VariableField.FieldType == FieldType.UndefinedGlobal || lookup.VariableField.FieldType == FieldType.Arguments || (lookup.VariableField.FieldType == FieldType.Predefined && string.CompareOrdinal(lookup.Name, "eval") == 0)) { node.Operand.Context.HandleError(JSError.StrictModeInvalidPreOrPost, true); } } } } else { // if the operand is a numeric literal ConstantWrapper constantWrapper = node.Operand as ConstantWrapper; if (constantWrapper != null && constantWrapper.IsNumericLiteral) { // get the value of the constant. We've already screened it for numeric, so // we don't have to worry about catching any errors double doubleValue = constantWrapper.ToNumber(); // if this is a unary minus... if (node.OperatorToken == JSToken.Minus && m_parser.Settings.IsModificationAllowed(TreeModifications.ApplyUnaryMinusToNumericLiteral)) { // negate the value constantWrapper.Value = -doubleValue; // replace us with the negated constant if (node.Parent.ReplaceChild(node, constantWrapper)) { // the context for the minus will include the number (its operand), // but the constant will just be the number. Update the context on // the constant to be a copy of the context on the operator constantWrapper.Context = node.Context.Clone(); } } else if (node.OperatorToken == JSToken.Plus && m_parser.Settings.IsModificationAllowed(TreeModifications.RemoveUnaryPlusOnNumericLiteral)) { // +NEG is still negative, +POS is still positive, and +0 is still 0. // so just get rid of the unary operator altogether if (node.Parent.ReplaceChild(node, constantWrapper)) { // the context for the unary will include the number (its operand), // but the constant will just be the number. Update the context on // the constant to be a copy of the context on the operator constantWrapper.Context = node.Context.Clone(); } } } } } }
private void DoBinaryOperator(BinaryOperator node) { if (m_parser.Settings.EvalLiteralExpressions) { // if this is an assign operator, an in, or an instanceof, then we won't // try to evaluate it if (!node.IsAssign && node.OperatorToken != JSToken.In && node.OperatorToken != JSToken.InstanceOf) { if (node.OperatorToken == JSToken.StrictEqual || node.OperatorToken == JSToken.StrictNotEqual) { // the operator is a strict equality (or not-equal). // check the primitive types of the two operands -- if they are known but not the same, we can // shortcut the whole process by just replacing this node with a boolean literal. var leftType = node.Operand1.FindPrimitiveType(); if (leftType != PrimitiveType.Other) { var rightType = node.Operand2.FindPrimitiveType(); if (rightType != PrimitiveType.Other) { // both sides are known if (leftType != rightType) { // they are not the same type -- replace with a boolean and bail ReplaceNodeWithLiteral( node, new ConstantWrapper(node.OperatorToken == JSToken.StrictEqual ? false : true, PrimitiveType.Boolean, node.Context, m_parser)); return; } // they are the same type -- we can change the operator to simple equality/not equality node.OperatorToken = node.OperatorToken == JSToken.StrictEqual ? JSToken.Equal : JSToken.NotEqual; } } } // see if the left operand is a literal number, boolean, string, or null ConstantWrapper left = node.Operand1 as ConstantWrapper; if (left != null) { if (node.OperatorToken == JSToken.Comma) { // the comma operator evaluates the left, then evaluates the right and returns it. // but if the left is a literal, evaluating it doesn't DO anything, so we can replace the // entire operation with the right-hand operand ConstantWrapper rightConstant = node.Operand2 as ConstantWrapper; if (rightConstant != null) { // we'll replace the operator with the right-hand operand, BUT it's a constant, too. // first check to see if replacing this node with a constant will result in creating // a member-bracket operator that can be turned into a member-dot. If it is, then that // method will handle the replacement. But if it doesn't, then we should just replace // the comma with the right-hand operand. if (!ReplaceMemberBracketWithDot(node, rightConstant)) { ReplaceNodeWithLiteral(node, rightConstant); } } else if (node is CommaOperator) { // this is a collection of expression statements that we've joined together as // an extended comma operator. var list = node.Operand2 as AstNodeList; if (list == null) { // not a list, just a single item, so we can just // replace this entire node with the one element ReplaceNodeCheckParens(node, node.Operand2); } else if (list.Count == 1) { // If the list has a single element, then we can just // replace this entire node with the one element ReplaceNodeCheckParens(node, list[0]); } else if (list.Count == 0) { // the recursion ended up emptying the list, so we can just delete // this node altogether ReplaceNodeCheckParens(node, null); } else { // more than one item in the list // move the first item from the list to the left-hand side var firstItem = list[0]; list.RemoveAt(0); node.Operand1 = firstItem; // if there's only one item left in the list, we can get rid of the // extra list node and make it just the remaining node if (list.Count == 1) { firstItem = list[0]; list.RemoveAt(0); node.Operand2 = firstItem; } } } else { // replace the comma operator with the right-hand operand ReplaceNodeCheckParens(node, node.Operand2); } } else { // see if the right operand is a literal number, boolean, string, or null ConstantWrapper right = node.Operand2 as ConstantWrapper; if (right != null) { // then they are both constants and we can evaluate the operation EvalThisOperator(node, left, right); } else { // see if the right is a binary operator that can be combined with ours BinaryOperator rightBinary = node.Operand2 as BinaryOperator; if (rightBinary != null) { ConstantWrapper rightLeft = rightBinary.Operand1 as ConstantWrapper; if (rightLeft != null) { // eval our left and the right-hand binary's left and put the combined operation as // the child of the right-hand binary EvalToTheRight(node, left, rightLeft, rightBinary); } else { ConstantWrapper rightRight = rightBinary.Operand2 as ConstantWrapper; if (rightRight != null) { EvalFarToTheRight(node, left, rightRight, rightBinary); } } } } } } else { // left is not a constantwrapper. See if the right is ConstantWrapper right = node.Operand2 as ConstantWrapper; if (right != null) { // the right is a constant. See if the the left is a binary operator... BinaryOperator leftBinary = node.Operand1 as BinaryOperator; if (leftBinary != null) { // ...with a constant on the right, and the operators can be combined ConstantWrapper leftRight = leftBinary.Operand2 as ConstantWrapper; if (leftRight != null) { EvalToTheLeft(node, right, leftRight, leftBinary); } else { ConstantWrapper leftLeft = leftBinary.Operand1 as ConstantWrapper; if (leftLeft != null) { EvalFarToTheLeft(node, right, leftLeft, leftBinary); } } } else if (m_parser.Settings.IsModificationAllowed(TreeModifications.SimplifyStringToNumericConversion)) { // see if it's a lookup and this is a minus operation and the constant is a zero Lookup lookup = node.Operand1 as Lookup; if (lookup != null && node.OperatorToken == JSToken.Minus && right.IsIntegerLiteral && right.ToNumber() == 0) { // okay, so we have "lookup - 0" // this is done frequently to force a value to be numeric. // There is an easier way: apply the unary + operator to it. var unary = new UnaryOperator(node.Context, m_parser) { Operand = lookup, OperatorToken = JSToken.Plus }; ReplaceNodeCheckParens(node, unary); } } } // TODO: shouldn't we check if they BOTH are binary operators? (a*6)*(5*b) ==> a*30*b (for instance) } } } }
//--------------------------------------------------------------------------------------- // ParsePostfixExpression // // PostfixExpression: // LeftHandSideExpression | // LeftHandSideExpression '++' | // LeftHandSideExpression '--' // //--------------------------------------------------------------------------------------- private AstNode ParsePostfixExpression(AstNode ast, out bool isLeftHandSideExpr) { isLeftHandSideExpr = true; Context exprCtx = null; if (null != ast) { if (!m_foundEndOfLine) { if (JSToken.Increment == m_currentToken.Token) { isLeftHandSideExpr = false; exprCtx = ast.Context.Clone(); exprCtx.UpdateWith(m_currentToken); ast = new UnaryOperator(exprCtx, this) { Operand = ast, OperatorToken = m_currentToken.Token, OperatorContext = m_currentToken.Clone(), IsPostfix = true }; GetNextToken(); } else if (JSToken.Decrement == m_currentToken.Token) { isLeftHandSideExpr = false; exprCtx = ast.Context.Clone(); exprCtx.UpdateWith(m_currentToken); ast = new UnaryOperator(exprCtx, this) { Operand = ast, OperatorToken = m_currentToken.Token, OperatorContext = m_currentToken.Clone(), IsPostfix = true }; GetNextToken(); } } } return ast; }
//--------------------------------------------------------------------------------------- // ParseUnaryExpression // // UnaryExpression : // PostfixExpression | // 'delete' UnaryExpression | // 'void' UnaryExpression | // 'typeof' UnaryExpression | // '++' UnaryExpression | // '--' UnaryExpression | // '+' UnaryExpression | // '-' UnaryExpression | // '~' UnaryExpression | // '!' UnaryExpression // //--------------------------------------------------------------------------------------- private AstNode ParseUnaryExpression(out bool isLeftHandSideExpr, bool isMinus) { isLeftHandSideExpr = false; bool dummy = false; Context exprCtx = null; AstNode expr = null; TryItAgain: AstNode ast = null; switch (m_currentToken.Token) { case JSToken.Void: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new UnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JSToken.Void }; break; case JSToken.TypeOf: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new UnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JSToken.TypeOf }; break; case JSToken.Plus: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new UnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JSToken.Plus }; break; case JSToken.Minus: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, true); ast = new UnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JSToken.Minus }; break; case JSToken.BitwiseNot: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new UnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JSToken.BitwiseNot }; break; case JSToken.LogicalNot: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new UnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JSToken.LogicalNot }; break; case JSToken.Delete: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new UnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JSToken.Delete }; break; case JSToken.Increment: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new UnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JSToken.Increment }; break; case JSToken.Decrement: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new UnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JSToken.Decrement }; break; case JSToken.ConditionalCommentStart: // skip past the start to the next token exprCtx = m_currentToken.Clone(); GetNextToken(); if (m_currentToken.Token == JSToken.ConditionalCommentEnd) { // empty conditional-compilation comment -- ignore GetNextToken(); goto TryItAgain; } else if (m_currentToken.Token == JSToken.ConditionalCompilationOn) { // /*@cc_on -- check for @IDENT@*/ or !@*/ GetNextToken(); if (m_currentToken.Token == JSToken.ConditionalCompilationVariable) { // /*@cc_on@IDENT -- check for @*/ ast = new ConstantWrapperPP(m_currentToken.Clone(), this) { VarName = m_currentToken.Code, ForceComments = true }; GetNextToken(); if (m_currentToken.Token == JSToken.ConditionalCommentEnd) { // skip the close and keep going GetNextToken(); } else { // too complicated CCTooComplicated(null); goto TryItAgain; } } else if (m_currentToken.Token == JSToken.LogicalNot) { // /*@cc_on! -- check for @*/ var operatorContext = m_currentToken.Clone(); GetNextToken(); if (m_currentToken.Token == JSToken.ConditionalCommentEnd) { // we have /*@cc_on!@*/ GetNextToken(); expr = ParseUnaryExpression(out dummy, false); exprCtx.UpdateWith(expr.Context); var unary = new UnaryOperator(exprCtx, this) { Operand = expr, OperatorContext = operatorContext, OperatorToken = JSToken.LogicalNot }; unary.OperatorInConditionalCompilationComment = true; unary.ConditionalCommentContainsOn = true; ast = unary; } else { // too complicated CCTooComplicated(null); goto TryItAgain; } } else { // too complicated CCTooComplicated(null); goto TryItAgain; } } else if (m_currentToken.Token == JSToken.LogicalNot) { // /*@! -- check for @*/ var operatorContext = m_currentToken.Clone(); GetNextToken(); if (m_currentToken.Token == JSToken.ConditionalCommentEnd) { // we have /*@!@*/ GetNextToken(); expr = ParseUnaryExpression(out dummy, false); exprCtx.UpdateWith(expr.Context); var unary = new UnaryOperator(exprCtx, this) { Operand = expr, OperatorContext = operatorContext, OperatorToken = JSToken.LogicalNot }; unary.OperatorInConditionalCompilationComment = true; ast = unary; } else { // too complicated CCTooComplicated(null); goto TryItAgain; } } else if (m_currentToken.Token == JSToken.ConditionalCompilationVariable) { // @IDENT -- check for @*/ ast = new ConstantWrapperPP(m_currentToken.Clone(), this) { VarName = m_currentToken.Code, ForceComments = true }; GetNextToken(); if (m_currentToken.Token == JSToken.ConditionalCommentEnd) { // skip the close and keep going GetNextToken(); } else { // too complicated CCTooComplicated(null); goto TryItAgain; } } else { // we ONLY support /*@id@*/ or /*@cc_on@id@*/ or /*@!@*/ or /*@cc_on!@*/ in expressions right now. // throw an error, skip to the end of the comment, then ignore it and start // looking for the next token. CCTooComplicated(null); goto TryItAgain; } break; default: m_noSkipTokenSet.Add(NoSkipTokenSet.s_PostfixExpressionNoSkipTokenSet); try { ast = ParseLeftHandSideExpression(isMinus); } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_PostfixExpressionNoSkipTokenSet, exc) == -1) { throw; } else { if (exc._partiallyComputedNode == null) SkipTokensAndThrow(); else ast = exc._partiallyComputedNode; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_PostfixExpressionNoSkipTokenSet); } ast = ParsePostfixExpression(ast, out isLeftHandSideExpr); break; } return ast; }