示例#1
0
 private ScriptExpression ExpectAndParseExpression(ScriptNode parentNode, ref bool hasAnonymousExpression, ScriptExpression parentExpression = null, int newPrecedence = 0,
                                                   string message = null, ParseExpressionMode mode = ParseExpressionMode.Default)
 {
     if (StartAsExpression())
     {
         return(ParseExpression(parentNode, ref hasAnonymousExpression, parentExpression, newPrecedence, mode));
     }
     LogError(parentNode, CurrentSpan, message ?? $"Expecting <expression> instead of `{Current.Type}`");
     return(null);
 }
 private ScriptExpression ExpectAndParseExpression(ScriptNode parentNode, ScriptExpression parentExpression = null, int newPrecedence = 0, string message = null,
                                                   ParseExpressionMode mode = ParseExpressionMode.Default, bool allowAssignment       = true)
 {
     if (StartAsExpression())
     {
         return(ParseExpression(parentNode, parentExpression, newPrecedence, mode, allowAssignment));
     }
     LogError(parentNode, CurrentSpan, message ?? $"Expecting <expression> instead of `{Current.Type}`");
     return(null);
 }
示例#3
0
        private ScriptExpression ExpectAndParseExpression(ScriptNode parentNode, ref bool hasAnonymousExpression, ScriptExpression parentExpression = null, int newPrecedence = 0,
                                                          string message = null, ParseExpressionMode mode = ParseExpressionMode.Default)
        {
            if (StartAsExpression())
            {
                return(ParseExpression(parentNode, ref hasAnonymousExpression, parentExpression, newPrecedence, mode));
            }

            LogError(parentNode, CurrentSpan, message ?? string.Format(RS.ExpectToken, "expression", Current.Type));
            return(null);
        }
示例#4
0
 private ScriptExpression ExpectAndParseExpressionAndAnonymous(ScriptNode parentNode, out bool hasAnonymousFunction, ParseExpressionMode mode = ParseExpressionMode.Default)
 {
     hasAnonymousFunction = false;
     if (StartAsExpression())
     {
         return(ParseExpression(parentNode, ref hasAnonymousFunction, null, 0, mode));
     }
     LogError(parentNode, CurrentSpan, $"Expecting <expression> instead of `{Current.Type}`");
     return(null);
 }
示例#5
0
        private ScriptExpression ParseExpression(ScriptNode parentNode, ref bool hasAnonymousFunction, ScriptExpression parentExpression = null, int precedence = 0,
                                                 ParseExpressionMode mode = ParseExpressionMode.Default)
        {
            int expressionCount = 0;

            _expressionLevel++;
            var expressionDepthBeforeEntering = _expressionDepth;

            EnterExpression();
            try
            {
                ScriptFunctionCall functionCall = null;
parseExpression:
                expressionCount++;
                ScriptExpression leftOperand = null;
                switch (Current.Type)
                {
                case TokenType.Identifier:
                case TokenType.IdentifierSpecial:
                    leftOperand = ParseVariable();

                    // In case of liquid template, we accept the syntax colon after a tag
                    if (_isLiquid && parentNode is ScriptPipeCall && Current.Type == TokenType.Colon)
                    {
                        NextToken();
                    }

                    // Special handle of the $$ block delegate variable
                    if (ScriptVariable.BlockDelegate.Equals(leftOperand))
                    {
                        if (expressionCount != 1 || _expressionLevel > 1)
                        {
                            LogError("Cannot use block delegate $$ in a nested expression");
                        }

                        if (!(parentNode is ScriptExpressionStatement))
                        {
                            LogError(parentNode, "Cannot use block delegate $$ outside an expression statement");
                        }

                        return(leftOperand);
                    }
                    break;

                case TokenType.Integer:
                    leftOperand = ParseInteger();
                    break;

                case TokenType.Float:
                    leftOperand = ParseFloat();
                    break;

                case TokenType.String:
                    leftOperand = ParseString();
                    break;

                case TokenType.ImplicitString:
                    leftOperand = ParseImplicitString();
                    break;

                case TokenType.VerbatimString:
                    leftOperand = ParseVerbatimString();
                    break;

                case TokenType.OpenParent:
                    leftOperand = ParseParenthesis(ref hasAnonymousFunction);
                    break;

                case TokenType.OpenBrace:
                    leftOperand = ParseObjectInitializer();
                    break;

                case TokenType.OpenBracket:
                    leftOperand = ParseArrayInitializer();
                    break;

                case TokenType.Not:
                case TokenType.Minus:
                case TokenType.Arroba:
                case TokenType.Plus:
                case TokenType.Caret:
                    leftOperand = ParseUnaryExpression(ref hasAnonymousFunction);
                    break;
                }

                // Should not happen but in case
                if (leftOperand == null)
                {
                    if (functionCall != null)
                    {
                        LogError($"Unexpected token `{GetAsText(Current)}` while parsing function call `{functionCall}`");
                    }
                    else
                    {
                        LogError($"Unexpected token `{GetAsText(Current)}` while parsing expression");
                    }
                    return(null);
                }

                if (leftOperand is ScriptAnonymousFunction)
                {
                    hasAnonymousFunction = true;
                }

                while (!hasAnonymousFunction)
                {
                    if (_isLiquid && Current.Type == TokenType.Comma && functionCall != null)
                    {
                        NextToken(); // Skip the comma for arguments in a function call
                    }

                    // Parse Member expression are expected to be followed only by an identifier
                    if (Current.Type == TokenType.Dot)
                    {
                        var nextToken = PeekToken();
                        if (nextToken.Type == TokenType.Identifier)
                        {
                            NextToken();

                            if (GetAsText(Current) == "empty" && PeekToken().Type == TokenType.Question)
                            {
                                var memberExpression = Open <ScriptIsEmptyExpression>();
                                NextToken(); // skip empty
                                NextToken(); // skip ?
                                memberExpression.Target = leftOperand;
                                leftOperand             = Close(memberExpression);
                            }
                            else
                            {
                                var memberExpression = Open <ScriptMemberExpression>();
                                memberExpression.Target = leftOperand;
                                var member = ParseVariable();
                                if (!(member is ScriptVariable))
                                {
                                    LogError("Unexpected literal member `{member}`");
                                    return(null);
                                }
                                memberExpression.Member = (ScriptVariable)member;
                                leftOperand             = Close(memberExpression);
                            }
                        }
                        else
                        {
                            LogError(nextToken, $"Invalid token `{nextToken.Type}`. The dot operator is expected to be followed by a plain identifier");
                            return(null);
                        }
                        continue;
                    }

                    // If we have a bracket but left operand is a (variable || member || indexer), then we consider next as an indexer
                    // unit test: 130-indexer-accessor-accept1.txt
                    if (Current.Type == TokenType.OpenBracket && leftOperand is IScriptVariablePath && !IsPreviousCharWhitespace())
                    {
                        NextToken();
                        var indexerExpression = Open <ScriptIndexerExpression>();
                        indexerExpression.Target = leftOperand;
                        // unit test: 130-indexer-accessor-error5.txt
                        indexerExpression.Index = ExpectAndParseExpression(indexerExpression, ref hasAnonymousFunction, functionCall, 0, $"Expecting <index_expression> instead of `{Current.Type}`");

                        if (Current.Type != TokenType.CloseBracket)
                        {
                            LogError($"Unexpected `{Current.Type}`. Expecting ']'");
                        }
                        else
                        {
                            NextToken();
                        }

                        leftOperand = Close(indexerExpression);
                        continue;
                    }

                    if (mode == ParseExpressionMode.BasicExpression)
                    {
                        break;
                    }

                    if (Current.Type == TokenType.Equal)
                    {
                        var assignExpression = Open <ScriptAssignExpression>();

                        if (_expressionLevel > 1)
                        {
                            // unit test: 101-assign-complex-error1.txt
                            LogError(assignExpression, $"Expression is only allowed for a top level assignment");
                        }

                        NextToken();

                        assignExpression.Target = TransformKeyword(leftOperand);

                        // unit test: 105-assign-error3.txt
                        assignExpression.Value = ExpectAndParseExpression(assignExpression, ref hasAnonymousFunction, parentExpression);

                        leftOperand = Close(assignExpression);
                        continue;
                    }

                    // Handle binary operators here
                    ScriptBinaryOperator binaryOperatorType;
                    if (BinaryOperators.TryGetValue(Current.Type, out binaryOperatorType) || (_isLiquid && TryLiquidBinaryOperator(out binaryOperatorType)))
                    {
                        var newPrecedence = GetOperatorPrecedence(binaryOperatorType);

                        // Check precedence to see if we should "take" this operator here (Thanks TimJones for the tip code! ;)
                        if (newPrecedence <= precedence)
                        {
                            break;
                        }

                        // We fake entering an expression here to limit the number of expression
                        EnterExpression();
                        var binaryExpression = Open <ScriptBinaryExpression>();
                        binaryExpression.Left     = leftOperand;
                        binaryExpression.Operator = binaryOperatorType;

                        NextToken(); // skip the operator

                        // unit test: 110-binary-simple-error1.txt
                        binaryExpression.Right = ExpectAndParseExpression(binaryExpression, ref hasAnonymousFunction,
                                                                          functionCall ?? parentExpression, newPrecedence,
                                                                          $"Expecting an <expression> to the right of the operator instead of `{Current.Type}`");
                        leftOperand = Close(binaryExpression);

                        continue;
                    }

                    if (precedence > 0)
                    {
                        break;
                    }

                    if (StartAsExpression())
                    {
                        // If we can parse a statement, we have a method call
                        if (parentExpression != null)
                        {
                            break;
                        }

                        // Parse named parameters
                        var paramContainer = parentNode as IScriptNamedArgumentContainer;
                        if (Current.Type == TokenType.Identifier && (parentNode is IScriptNamedArgumentContainer || !_isLiquid && PeekToken().Type == TokenType.Colon))
                        {
                            if (paramContainer == null)
                            {
                                if (functionCall == null)
                                {
                                    functionCall            = Open <ScriptFunctionCall>();
                                    functionCall.Target     = leftOperand;
                                    functionCall.Span.Start = leftOperand.Span.Start;
                                }
                                else
                                {
                                    functionCall.Arguments.Add(leftOperand);
                                }
                                Close(leftOperand);
                            }

                            while (true)
                            {
                                if (Current.Type != TokenType.Identifier)
                                {
                                    break;
                                }

                                var parameter     = Open <ScriptNamedArgument>();
                                var parameterName = GetAsText(Current);
                                parameter.Name = parameterName;

                                // Skip argument name
                                NextToken();

                                if (paramContainer != null)
                                {
                                    paramContainer.AddParameter(Close(parameter));
                                }
                                else
                                {
                                    functionCall.Arguments.Add(parameter);
                                }

                                // If we have a colon, we have a value
                                // otherwise it is a boolean argument name
                                if (Current.Type == TokenType.Colon)
                                {
                                    NextToken();
                                    parameter.Value    = ExpectAndParseExpression(parentNode, mode: ParseExpressionMode.BasicExpression);
                                    parameter.Span.End = parameter.Value.Span.End;
                                }

                                if (functionCall != null)
                                {
                                    functionCall.Span.End = parameter.Span.End;
                                }
                            }

                            // As we have handled leftOperand here, we don't let the function out of this while to pick up the leftOperand
                            if (functionCall != null)
                            {
                                leftOperand  = functionCall;
                                functionCall = null;
                            }

                            // We don't allow anything after named parameters
                            break;
                        }

                        if (functionCall == null)
                        {
                            functionCall        = Open <ScriptFunctionCall>();
                            functionCall.Target = leftOperand;

                            // If we need to convert liquid to scriban functions:
                            if (_isLiquid && Options.LiquidFunctionsToScriban)
                            {
                                TransformLiquidFunctionCallToScriban(functionCall);
                            }

                            functionCall.Span.Start = leftOperand.Span.Start;
                        }
                        else
                        {
                            functionCall.Arguments.Add(leftOperand);
                        }
                        goto parseExpression;
                    }

                    if (Current.Type == TokenType.Pipe)
                    {
                        if (functionCall != null)
                        {
                            functionCall.Arguments.Add(leftOperand);
                            leftOperand = functionCall;
                        }

                        var pipeCall = Open <ScriptPipeCall>();
                        pipeCall.From = leftOperand;
                        NextToken(); // skip |

                        // unit test: 310-func-pipe-error1.txt
                        pipeCall.To = ExpectAndParseExpression(pipeCall, ref hasAnonymousFunction);
                        return(Close(pipeCall));
                    }

                    break;
                }

                if (functionCall != null)
                {
                    functionCall.Arguments.Add(leftOperand);
                    functionCall.Span.End = leftOperand.Span.End;
                    return(functionCall);
                }
                return(Close(leftOperand));
            }
            finally
            {
                LeaveExpression();
                // Force to restore back to a level
                _expressionDepth = expressionDepthBeforeEntering;
                _expressionLevel--;
            }
        }
示例#6
0
        private ScriptExpression ParseExpression(ScriptNode parentNode, ScriptExpression parentExpression = null, int precedence = 0, ParseExpressionMode mode = ParseExpressionMode.Default)
        {
            bool hasAnonymousFunction = false;

            return(ParseExpression(parentNode, ref hasAnonymousFunction, parentExpression, precedence, mode));
        }
示例#7
0
        private ScriptExpression ExpectAndParseExpressionAndAnonymous(ScriptNode parentNode, out bool hasAnonymousFunction, ParseExpressionMode mode = ParseExpressionMode.Default)
        {
            hasAnonymousFunction = false;
            if (StartAsExpression())
            {
                return(ParseExpression(parentNode, ref hasAnonymousFunction, null, 0, mode));
            }

            LogError(parentNode, CurrentSpan, string.Format(RS.ExpectToken, "expression", Current.Type));
            return(null);
        }