// reads message content in JSON format from the current position. public void Read(Message msg) { _dc = new char[iCharBufSz]; _cc = GetNextCharJs(); _tlast = Next(); AsMessage(msg, msg.GetDescriptor()); }
private JsKeyword(JsToken token, string name, JsKeyword next) { m_name = name; m_token = token; m_length = m_name.Length; m_next = next; }
/*internal bool Exists(string target) { JSKeyword keyword = this; while (keyword != null) { if (keyword.m_name == target) { return true; } keyword = keyword.m_next; } return false; }*/ internal static string CanBeIdentifier(JsToken keyword) { switch (keyword) { // always allowed case JsToken.Get: return "get"; case JsToken.Set: return "set"; // not in strict mode case JsToken.Implements: return "implements"; case JsToken.Interface: return "interface"; case JsToken.Let: return "let"; case JsToken.Package: return "package"; case JsToken.Private: return "private"; case JsToken.Protected: return "protected"; case JsToken.Public: return "public"; case JsToken.Static: return "static"; case JsToken.Yield: return "yield"; // apparently never allowed for Chrome, so we want to treat it // differently, too case JsToken.Native: return "native"; // no other tokens can be identifiers default: return null; } }
public JsContext(JsDocumentContext document, int startLineNumber, int startLinePosition, int startPosition, int endLineNumber, int endLinePosition, int endPosition, JsToken token) : this(document) { StartLineNumber = startLineNumber; StartLinePosition = startLinePosition; StartPosition = startPosition; EndLineNumber = endLineNumber; EndLinePosition = endLinePosition; EndPosition = endPosition; Token = token; }
//--------------------------------------------------------------------------------------- // ParseExpressionList // // Given a starting this.currentToken '(' or '[', parse a list of expression separated by // ',' until matching ')' or ']' //--------------------------------------------------------------------------------------- private JsAstNodeList ParseExpressionList(JsToken terminator) { JsContext listCtx = m_currentToken.Clone(); GetNextToken(); JsAstNodeList list = new JsAstNodeList(listCtx, this); if (terminator != m_currentToken.Token) { for (; ; ) { m_noSkipTokenSet.Add(NoSkipTokenSet.s_ExpressionListNoSkipTokenSet); try { JsAstNode item; if (JsToken.Comma == m_currentToken.Token) { item = new JsConstantWrapper(JsMissing.Value, JsPrimitiveType.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; }
/// <summary> /// Initializes a new instance of the <see cref="JsFilterExpressionStatement"/> class. /// </summary> /// <param name="originalText">The original text.</param> /// <param name="expr">The expr.</param> /// <param name="filters">The filters.</param> public JsFilterExpressionStatement(string originalText, JsToken expr, params JsCallExpression[] filters) { FilterExpression = new PageVariableFragment(originalText.AsMemory(), expr, new List <JsCallExpression>(filters)); }
private JsAstNode ParseExpression(JsAstNode leftHandSide, bool single, bool bCanAssign, JsToken inToken) { // new op stack with dummy op Stack<JsContext> opsStack = new Stack<JsContext>(); opsStack.Push(null); // term stack, push left-hand side onto it Stack<JsAstNode> termStack = new Stack<JsAstNode>(); termStack.Push(leftHandSide); JsAstNode expr = null; try { for (; ; ) { // if 'binary op' or 'conditional' // if we are looking for a single expression, then also bail when we hit a comma // inToken is a special case because of the for..in syntax. When ParseExpression is called from // for, inToken = JSToken.In which excludes JSToken.In from the list of operators, otherwise // inToken = JSToken.None which is always true if the first condition is true if (JsScanner.IsProcessableOperator(m_currentToken.Token) && inToken != m_currentToken.Token && (!single || m_currentToken.Token != JsToken.Comma)) { // for the current token, get the operator precedence and whether it's a right-association operator var prec = JsScanner.GetOperatorPrecedence(m_currentToken); bool rightAssoc = JsScanner.IsRightAssociativeOperator(m_currentToken.Token); // while the current operator has lower precedence than the operator at the top of the stack // or it has the same precedence and it is left associative (that is, no 'assign op' or 'conditional') var stackPrec = JsScanner.GetOperatorPrecedence(opsStack.Peek()); while (prec < stackPrec || prec == stackPrec && !rightAssoc) { // pop the top two elements off the stack along with the current operator, // combine them, then push the results back onto the term stack JsAstNode operand2 = termStack.Pop(); JsAstNode operand1 = termStack.Pop(); expr = CreateExpressionNode(opsStack.Pop(), operand1, operand2); termStack.Push(expr); // get the precendence of the current item on the top of the op stack stackPrec = JsScanner.GetOperatorPrecedence(opsStack.Peek()); } // now the current operator has higher precedence that every scanned operators on the stack, or // it has the same precedence as the one at the top of the stack and it is right associative // push operator and next term // but first: special case conditional '?:' if (JsToken.ConditionalIf == m_currentToken.Token) { // pop term stack JsAstNode condition = termStack.Pop(); // if this is an assignment, throw a warning in case the developer // meant to use == instead of = // but no warning if the condition is wrapped in parens. var binOp = condition as JsBinaryOperator; if (binOp != null && binOp.OperatorToken == JsToken.Assign) { condition.Context.HandleError(JsError.SuspectAssignment); } var questionCtx = m_currentToken.Clone(); GetNextToken(); // get expr1 in logOrExpr ? expr1 : expr2 JsAstNode operand1 = ParseExpression(true); JsContext colonCtx = null; if (JsToken.Colon != m_currentToken.Token) { ReportError(JsError.NoColon); } else { colonCtx = m_currentToken.Clone(); } GetNextToken(); // get expr2 in logOrExpr ? expr1 : expr2 JsAstNode operand2 = ParseExpression(true, inToken); expr = new JsConditional(condition.Context.CombineWith(operand2.Context), this) { Condition = condition, QuestionContext = questionCtx, TrueExpression = operand1, ColonContext = colonCtx, FalseExpression = operand2 }; termStack.Push(expr); } else { if (JsScanner.IsAssignmentOperator(m_currentToken.Token)) { if (!bCanAssign) { ReportError(JsError.IllegalAssignment); SkipTokensAndThrow(); } } else { // if the operator is a comma, we can get another assign; otherwise we can't bCanAssign = (m_currentToken.Token == JsToken.Comma); } // push the operator onto the operators stack opsStack.Push(m_currentToken.Clone()); // push new term GetNextToken(); if (bCanAssign) { termStack.Push(ParseUnaryExpression(out bCanAssign, false)); } else { bool dummy; termStack.Push(ParseUnaryExpression(out dummy, false)); } } } else { // done with expression; go and unwind the stack of expressions/operators break; } } // there are still operators to be processed while (opsStack.Peek() != null) { // pop the top two term and the top operator, combine them into a new term, // and push the results back onto the term stacck JsAstNode operand2 = termStack.Pop(); JsAstNode operand1 = termStack.Pop(); expr = CreateExpressionNode(opsStack.Pop(), operand1, operand2); // push node onto the stack termStack.Push(expr); } Debug.Assert(termStack.Count == 1); return termStack.Pop(); } catch (RecoveryTokenException exc) { exc._partiallyComputedNode = leftHandSide; throw; } }
internal RecoveryTokenException(JsToken token, JsAstNode partialAST) : base() { _token = token; _partiallyComputedNode = partialAST; }
private int IndexOfToken(JsToken[] tokens, JsToken token) { int i, c; for (i = 0, c = tokens.Length; i < c; i++) if (tokens[i] == token) break; if (i >= c) i = -1; else { // assume that the caller will deal with the token so move the state back to normal m_useCurrentForNext = false; } return i; }
private bool TokenInList(JsToken[] tokens, RecoveryTokenException exc) { return (-1 != IndexOfToken(tokens, exc._token)); }
internal bool HasToken(JsToken token) { foreach (JsToken[] tokenSet in m_tokenSetList) { for (int ndx = 0; ndx < tokenSet.Length; ++ndx) { if (tokenSet[ndx] == token) { return true; } } } return false; }
protected Value AsValueJs(Value data) { switch (_tlast) { case JsToken.String: data.String = AtString(); break; case JsToken.Int: data.Bigint = GetLongJs(); break; case JsToken.Number: data.Number = double.Parse(AtString(), System.Globalization.NumberStyles.Float); break; case JsToken.False: data.Bool = false; break; case JsToken.True: data.Bool = true; break; case JsToken.Null: data.ClearKind(); break; case JsToken.ObjectStart: data.Members = AsStructJs(new Struct()); break; case JsToken.ArrayStart: var list = new ListValue(); for (GetValue(); _tlast != JsToken.ArrayEnd;) { AsValueJs(list.AddValues(new Value())); if ((_tlast = Next()) == JsToken.Comma) GetValue(); else if (_tlast != JsToken.ArrayEnd && (int)_tlast < (int)JsToken.ObjectStart) Expected("value"); } data.Elements = list; break; default: Expected("json data element"); break; } return data; }
protected Struct AsStructJs(Struct data) { if (_tlast != JsToken.ObjectStart) Expected("json object"); var comma = false; while ((_tlast = Next()) != JsToken.ObjectEnd) { if (comma) if (_tlast != JsToken.Comma) Expected("comma"); else _tlast = Next(); else comma = true; if (_tlast != JsToken.String) Expected("name"); _last_name = _sdata; if (Next() != JsToken.Semicolon) Expected(":"); GetValue(); var jdata = AsValueJs(new Value()); data.AddFields(_last_name, jdata); } return data; }
private JsToken GetValue() { return (int)(_tlast = Next()) >= (int)JsToken.ObjectStart ? _tlast : Expected("value"); }
public void AsArray(Message msg, FieldDescriptor pos) { for (GetValue(); _tlast != JsToken.ArrayEnd; ) { if (_tlast != JsToken.Null) msg.Get(pos, this); if ((_tlast = Next()) == JsToken.Comma) GetValue(); else if (_tlast != JsToken.ArrayEnd && (int)_tlast < (int)JsToken.ObjectStart) Expected("value"); } }
/// <summary> /// Parses the js expression. /// </summary> /// <param name="literal">The literal.</param> /// <param name="token">The token.</param> /// <returns>ReadOnlySpan<System.Char>.</returns> public static ReadOnlySpan <char> ParseJsExpression(this ReadOnlyMemory <char> literal, out JsToken token) => literal.Span.ParseJsExpression(out token, filterExpression: false);
/// <summary> /// Parses the js expression. /// </summary> /// <param name="literal">The literal.</param> /// <param name="token">The token.</param> /// <returns>ReadOnlySpan<System.Char>.</returns> public static ReadOnlySpan <char> ParseJsExpression(this string literal, out JsToken token) => literal.AsSpan().ParseJsExpression(out token);
public static object eval(ScriptContext context, JsToken token, Dictionary <string, object> args = null) { return(ScriptLanguage.UnwrapValue(token.Evaluate(new ScriptScopeContext(new PageResult(context.EmptyPage), null, args)))); }
//--------------------------------------------------------------------------------------- // ParseIdentifierInitializer // // Does the real work of parsing a single variable declaration. // inToken is JSToken.In whenever the potential expression that initialize a variable // cannot contain an 'in', as in the for statement. inToken is JSToken.None otherwise //--------------------------------------------------------------------------------------- private JsAstNode ParseIdentifierInitializer(JsToken inToken) { string variableName = null; JsAstNode assignmentExpr = null; RecoveryTokenException except = null; GetNextToken(); if (JsToken.Identifier != m_currentToken.Token) { String identifier = JsKeyword.CanBeIdentifier(m_currentToken.Token); if (null != identifier) { variableName = identifier; } else { // make up an identifier assume we're done with the var statement if (JsScanner.IsValidIdentifier(m_currentToken.Code)) { // it's probably just a keyword ReportError(JsError.NoIdentifier, m_currentToken.Clone(), true); variableName = m_currentToken.Code; } else { ReportError(JsError.NoIdentifier); return null; } } } else { variableName = m_scanner.Identifier; } JsContext idContext = m_currentToken.Clone(); JsContext context = m_currentToken.Clone(); JsContext assignContext = null; bool ccSpecialCase = false; bool ccOn = false; GetNextToken(); m_noSkipTokenSet.Add(NoSkipTokenSet.s_VariableDeclNoSkipTokenSet); try { if (m_currentToken.Token == JsToken.ConditionalCommentStart) { ccSpecialCase = true; GetNextToken(); if (m_currentToken.Token == JsToken.ConditionalCompilationOn) { GetNextToken(); if (m_currentToken.Token == JsToken.ConditionalCommentEnd) { // forget about it; just ignore the whole thing because it's empty ccSpecialCase = false; } else { ccOn = true; } } } if (JsToken.Assign == m_currentToken.Token || JsToken.Equal == m_currentToken.Token) { assignContext = m_currentToken.Clone(); if (JsToken.Equal == m_currentToken.Token) { ReportError(JsError.NoEqual, true); } // move past the equals sign GetNextToken(); if (m_currentToken.Token == JsToken.ConditionalCommentEnd) { // so we have var id/*@ =@*/ or var id//@=<EOL> // we only support the equal sign inside conditional comments IF // the initializer value is there as well. ccSpecialCase = false; m_currentToken.HandleError(JsError.ConditionalCompilationTooComplex); GetNextToken(); } try { assignmentExpr = ParseExpression(true, inToken); } catch (RecoveryTokenException exc) { assignmentExpr = exc._partiallyComputedNode; throw; } finally { if (null != assignmentExpr) { context.UpdateWith(assignmentExpr.Context); } } } else if (ccSpecialCase) { // so we have "var id /*@" or "var id //@", but the next character is NOT an equal sign. // we don't support this structure, either. ccSpecialCase = false; m_currentToken.HandleError(JsError.ConditionalCompilationTooComplex); // skip to end of conditional comment while (m_currentToken.Token != JsToken.EndOfFile && m_currentToken.Token != JsToken.ConditionalCommentEnd) { GetNextToken(); } GetNextToken(); } // if the current token is not an end-of-conditional-comment token now, // then we're not in our special case scenario if (m_currentToken.Token == JsToken.ConditionalCommentEnd) { GetNextToken(); } else if (ccSpecialCase) { // we have "var id/*@=expr" but the next token is not the closing comment. // we don't support this structure, either. ccSpecialCase = false; m_currentToken.HandleError(JsError.ConditionalCompilationTooComplex); // the assignment expression was apparently wiothin the conditional compilation // comment, but we're going to ignore it. So clear it out. assignmentExpr = null; // skip to end of conditional comment while (m_currentToken.Token != JsToken.EndOfFile && m_currentToken.Token != JsToken.ConditionalCommentEnd) { GetNextToken(); } GetNextToken(); } } catch (RecoveryTokenException exc) { // If the exception is in the vardecl no-skip set then we successfully // recovered to the end of the declaration and can just return // normally. Otherwise we re-throw after constructing the partial result. if (IndexOfToken(NoSkipTokenSet.s_VariableDeclNoSkipTokenSet, exc) == -1) except = exc; } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_VariableDeclNoSkipTokenSet); } JsVariableDeclaration result = new JsVariableDeclaration(context, this) { Identifier = variableName, NameContext = idContext, AssignContext = assignContext, Initializer = assignmentExpr }; result.IsCCSpecialCase = ccSpecialCase; if (ccSpecialCase) { // by default, set the flag depending on whether we encountered a @cc_on statement. // might be overridden by the node in analyze phase result.UseCCOn = ccOn; } if (null != except) { except._partiallyComputedNode = result; throw except; } return result; }
protected void AsMessage(Message msg, MessageDescriptor ci) { if (ci.HasOptions) { AsMessageEx(msg, ci); return; } // should be ready to pick up value if parsing into Json data node special class. if (_tlast != JsToken.ObjectStart) Expected("object start"); // parse JSON object into message fields. var comma = false; FieldDescriptor kvfs = null; while (true) { if ((_tlast = Next()) == JsToken.ObjectEnd) { if (kvfs == null) break; kvfs = null; continue; } if (!comma) comma = true; else if (_tlast != JsToken.Comma) Expected("comma"); else _tlast = Next(); if (_tlast != JsToken.String) Expected("name"); _last_name = _sdata; if (Next() != JsToken.Semicolon) Expected(":"); if (GetValue() == JsToken.Null) continue; // when parsing JSON object into map, populate next entry. if (kvfs != null) { msg.Get(kvfs, this); continue; } // find message field descriptor by name. var fs = ci.Find(_last_name); if (fs == null) continue; if (_tlast != JsToken.ArrayStart) // save field desc for extracting kv-map values. if (fs.DataType != WireType.MapEntry) msg.Get(fs, this); else if (_tlast == JsToken.ObjectStart) { kvfs = fs; comma = false; } else Expected("object start"); else AsArray(msg, fs); } }
private bool TokenInList(JsToken[] tokens, JsToken token) { return (-1 != IndexOfToken(tokens, token)); }
/// <summary> /// Initializes a new instance of the <see cref="JsExpressionStatement"/> class. /// </summary> /// <param name="expression">The expression.</param> public JsExpressionStatement(JsToken expression) { Expression = expression; }
internal void Add(JsToken[] tokens) { m_tokenSetList.Add(tokens); }
public void Can_use_ToJsAst_to_generate_Esprima_AST() { JsToken token = JS.expression("{ key: a.prop == 1 ? b < 2 : c > 3 }"); // "{ key: a.prop == 1 ? b < 2 : c > 3 }".ParseJsExpression(out token); Dictionary <string, object> ast = token.ToJsAst(); ast.ToJson().IndentJson().Print(); var expected = new Dictionary <string, object> { ["type"] = "ObjectExpression", ["properties"] = new List <object> { new Dictionary <string, object> { ["type"] = "Property", ["key"] = new Dictionary <string, object> { ["type"] = "Identifier", ["name"] = "key", }, ["computed"] = false, ["value"] = new Dictionary <string, object> { ["type"] = "ConditionalExpression", ["test"] = new Dictionary <string, object> { ["type"] = "BinaryExpression", ["operator"] = "==", ["left"] = new Dictionary <string, object> { ["type"] = "MemberExpression", ["computed"] = false, ["object"] = new Dictionary <string, object> { ["type"] = "Identifier", ["name"] = "a", }, ["property"] = new Dictionary <string, object> { ["type"] = "Identifier", ["name"] = "prop", } }, ["right"] = new Dictionary <string, object> { ["type"] = "Literal", ["value"] = 1, ["raw"] = "1", }, }, ["consequent"] = new Dictionary <string, object> { ["type"] = "BinaryExpression", ["operator"] = "<", ["left"] = new Dictionary <string, object> { ["type"] = "Identifier", ["name"] = "b", }, ["right"] = new Dictionary <string, object> { ["type"] = "Literal", ["value"] = 2, ["raw"] = "2", }, }, ["alternate"] = new Dictionary <string, object> { ["type"] = "BinaryExpression", ["operator"] = ">", ["left"] = new Dictionary <string, object> { ["type"] = "Identifier", ["name"] = "c", }, ["right"] = new Dictionary <string, object> { ["type"] = "Literal", ["value"] = 3, ["raw"] = "3", }, }, }, ["kind"] = "init", ["method"] = false, ["shorthand"] = false, } } }; "Expected: ".Print(); expected.ToJson().IndentJson().Print(); Assert.That(ast, Is.EqualTo(expected)); }
internal void Remove(JsToken[] tokens) { bool wasRemoved = m_tokenSetList.Remove(tokens); Debug.Assert(wasRemoved, "Token set not in no-skip list"); }
public JsLogicalExpression(JsToken left, JsLogicOperator operand, JsToken right) { Left = left; Operand = operand; Right = right; }
//--------------------------------------------------------------------------------------- // IndexOfToken // // check whether the recovery token is a good one for the caller //--------------------------------------------------------------------------------------- private int IndexOfToken(JsToken[] tokens, RecoveryTokenException exc) { return IndexOfToken(tokens, exc._token); }
private JsAstNode ParseExpression(bool single, JsToken inToken) { bool bAssign; JsAstNode lhs = ParseUnaryExpression(out bAssign, false); return ParseExpression(lhs, single, bAssign, inToken); }
private JsKeyword(JsToken token, string name) : this(token, name, null) { }
/// <summary> /// Initializes a new instance of the <see cref="JsFilterExpressionStatement"/> class. /// </summary> /// <param name="originalText">The original text.</param> /// <param name="expr">The expr.</param> /// <param name="filters">The filters.</param> public JsFilterExpressionStatement(ReadOnlyMemory <char> originalText, JsToken expr, List <JsCallExpression> filters) { FilterExpression = new PageVariableFragment(originalText, expr, filters); }
public static string OperatorString(JsToken token) { switch (token) { case JsToken.Decrement: return "--"; case JsToken.Delete: return "delete"; case JsToken.Increment: return "++"; case JsToken.TypeOf: return "typeof"; case JsToken.Void: return "void"; case JsToken.LogicalNot: return "!"; case JsToken.BitwiseNot: return "~"; case JsToken.Minus: return "-"; case JsToken.Plus: return "+"; case JsToken.Multiply: return "*"; case JsToken.BitwiseAnd: return "&"; case JsToken.BitwiseOr: return "|"; case JsToken.BitwiseXor: return "^"; case JsToken.LogicalAnd: return "&&"; case JsToken.LogicalOr: return "||"; case JsToken.Assign: return "="; case JsToken.BitwiseAndAssign: return "&="; case JsToken.BitwiseOrAssign: return "|="; case JsToken.BitwiseXorAssign: return "^="; case JsToken.Comma: return ","; case JsToken.Equal: return "=="; case JsToken.GreaterThan: return ">"; case JsToken.GreaterThanEqual: return ">="; case JsToken.In: return "in"; case JsToken.InstanceOf: return "instanceof"; case JsToken.LeftShift: return "<<"; case JsToken.LeftShiftAssign: return "<<="; case JsToken.LessThan: return "<"; case JsToken.LessThanEqual: return "<="; case JsToken.MinusAssign: return "-="; case JsToken.Modulo: return "%"; case JsToken.ModuloAssign: return "%="; case JsToken.MultiplyAssign: return "*="; case JsToken.NotEqual: return "!="; case JsToken.PlusAssign: return "+="; case JsToken.RightShift: return ">>"; case JsToken.RightShiftAssign: return ">>="; case JsToken.StrictEqual: return "==="; case JsToken.StrictNotEqual: return "!=="; case JsToken.UnsignedRightShift: return ">>>"; case JsToken.UnsignedRightShiftAssign: return ">>>="; case JsToken.Divide: return "/"; case JsToken.DivideAssign: return "/="; case JsToken.Let: return "let"; case JsToken.Const: return "const"; default: return string.Empty; } }
/// <summary> /// Initializes a new instance of the <see cref="JsConditionalExpression"/> class. /// </summary> /// <param name="test">The test.</param> /// <param name="consequent">The consequent.</param> /// <param name="alternate">The alternate.</param> /// <exception cref="ServiceStack.Script.SyntaxErrorException">Test Expression missing in Conditional Expression</exception> /// <exception cref="ServiceStack.Script.SyntaxErrorException">Consequent Expression missing in Conditional Expression</exception> /// <exception cref="ServiceStack.Script.SyntaxErrorException">Alternate Expression missing in Conditional Expression</exception> public JsConditionalExpression(JsToken test, JsToken consequent, JsToken alternate) { Test = test ?? throw new SyntaxErrorException("Test Expression missing in Conditional Expression"); Consequent = consequent ?? throw new SyntaxErrorException("Consequent Expression missing in Conditional Expression"); Alternate = alternate ?? throw new SyntaxErrorException("Alternate Expression missing in Conditional Expression"); }