public void Visit(ConstantWrapperPP node) { // we're good }
public void Visit(ConstantWrapperPP node) { // it's a constant, so we don't care }
public void Visit(ConstantWrapperPP node) { ReportError(node); }
//--------------------------------------------------------------------------------------- // ParseLeftHandSideExpression // // LeftHandSideExpression : // PrimaryExpression Accessor | // 'new' LeftHandSideExpression | // FunctionExpression // // PrimaryExpression : // 'this' | // Identifier | // Literal | // '(' Expression ')' // // FunctionExpression : // 'function' OptionalFuncName '(' FormalParameterList ')' { FunctionBody } // // OptionalFuncName : // <empty> | // Identifier //--------------------------------------------------------------------------------------- private AstNode ParseLeftHandSideExpression(bool isMinus) { AstNode ast = null; bool isFunction = false; 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_scanner.GetIdentifier(), m_currentToken.Clone(), this); break; case JSToken.ConditionalCommentStart: // skip past the start to the next token GetNextToken(); if (m_currentToken.Token == JSToken.PreprocessorConstant) { // we have /*@id ast = new ConstantWrapperPP(m_currentToken.Code, true, m_currentToken.Clone(), this); 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. 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; } } 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.StringLiteral, PrimitiveType.String, m_currentToken.Clone(), this); 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 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); } 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); } 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.PreprocessorConstant: ast = new ConstantWrapperPP(m_currentToken.Code, false, m_currentToken.Clone(), this); 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(source, flags, m_currentToken.Clone(), this); break; } goto default; // expression case JSToken.LeftParenthesis: { // save the current context reference Context openParenContext = m_currentToken.Clone(); GetNextToken(); m_noSkipTokenSet.Add(NoSkipTokenSet.s_ParenExpressionNoSkipToken); try { // parse an expression ast = ParseExpression(); // update the expression's context with the context of the open paren ast.Context.UpdateWith(openParenContext); 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 ast = exc._partiallyComputedNode; } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ParenExpressionNoSkipToken); } if (ast == null) //this can only happen when catching the exception and nothing was sent up by the caller SkipTokensAndThrow(); } break; // array initializer case JSToken.LeftBracket: Context listCtx = m_currentToken.Clone(); AstNodeList list = new AstNodeList(m_currentToken.Clone(), this); GetNextToken(); while (JSToken.RightBracket != m_currentToken.Token) { if (JSToken.Comma != m_currentToken.Token) { m_noSkipTokenSet.Add(NoSkipTokenSet.s_ArrayInitNoSkipTokenSet); try { list.Append(ParseExpression(true)); if (JSToken.Comma != m_currentToken.Token) { if (JSToken.RightBracket != m_currentToken.Token) ReportError(JSError.NoRightBracket); break; } else { // we have a comma -- skip it 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, 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)); // 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, list); break; // object initializer case JSToken.LeftCurly: Context objCtx = m_currentToken.Clone(); GetNextToken(); // we're going to keep the keys and values in separate lists, but make sure // that the indexes correlate (keyList[n] is associated with valueList[n]) List<ObjectLiteralField> keyList = new List<ObjectLiteralField>(); List<AstNode> valueList = new List<AstNode>(); 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.GetIdentifier(), PrimitiveType.String, m_currentToken.Clone(), this); break; case JSToken.StringLiteral: field = new ObjectLiteralField(m_scanner.StringLiteral, PrimitiveType.String, m_currentToken.Clone(), this); 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 (m_scanner.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, string, number, or getter/setter. // see if it's a token that COULD be an identifier. ident = JSKeyword.CanBeIdentifier(m_currentToken.Token); if (ident != null) { // don't throw a warning -- it's okay to have a keyword that // can be an identifier here. field = new ObjectLiteralField(ident, PrimitiveType.String, m_currentToken.Clone(), this); } else { ReportError(JSError.NoMemberIdentifier); field = new ObjectLiteralField("_#Missing_Field#_" + s_cDummyName++, PrimitiveType.String, CurrentPositionContext(), 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 { GetNextToken(); value = ParseExpression(true); } } // put the pair into the list of fields keyList.Add(field); valueList.Add(value); if (JSToken.RightCurly == m_currentToken.Token) break; else { if (JSToken.Comma == m_currentToken.Token) { // skip the comma 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_scanner.GotEndOfLine) { 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; keyList.Add(field); valueList.Add(value); } if (IndexOfToken(NoSkipTokenSet.s_ObjectInitNoSkipTokenSet, exc) == -1) { exc._partiallyComputedNode = new ObjectLiteral(objCtx, this, keyList.ToArray(), valueList.ToArray()); 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, keyList.ToArray(), valueList.ToArray()); break; // function expression case JSToken.Function: ast = ParseFunction(FunctionType.Expression, m_currentToken.Clone()); isFunction = true; break; case JSToken.AspNetBlock: ast = ParseAspNetBlock(consumeSemicolonIfPossible: false); break; default: string identifier = JSKeyword.CanBeIdentifier(m_currentToken.Token); if (null != identifier) { ast = new Lookup(identifier, m_currentToken.Clone(), this); } else { ReportError(JSError.ExpressionExpected); SkipTokensAndThrow(); } break; } // can be a CallExpression, that is, followed by '.' or '(' or '[' if (!isFunction) GetNextToken(); return MemberExpression(ast, newContexts); }
public void Visit(ConstantWrapperPP node) { // invalid! ignore IsValid = false; }