Exemplo n.º 1
0
        private ScriptExpression TransformKeyword(ScriptExpression leftOperand)
        {
            // In case we are in liquid and we are assigning to a textscript keyword, we escape the variable with a nested expression
            if (_isLiquid && leftOperand is IScriptVariablePath &&
                IsTextScriptKeyword(((IScriptVariablePath)leftOperand).GetFirstPath()) &&
                !(leftOperand is ScriptNestedExpression))
            {
                ScriptNestedExpression nestedExpression = new ScriptNestedExpression
                {
                    Expression = leftOperand,
                    Span       = leftOperand.Span
                };

                // If the variable has any trivia, we copy them to the NestedExpression instead
                if (_isKeepTrivia && leftOperand.Trivias != null)
                {
                    nestedExpression.Trivias = leftOperand.Trivias;
                    leftOperand.Trivias      = null;
                }

                return(nestedExpression);
            }

            return(leftOperand);
        }
Exemplo n.º 2
0
        private ScriptExpression ParseParenthesis(ref bool hasAnonymousFunction)
        {
            // unit test: 106-parenthesis.txt
            ScriptNestedExpression expression = Open <ScriptNestedExpression>();

            NextToken(); // Skip (
            expression.Expression = ExpectAndParseExpression(expression, ref hasAnonymousFunction);

            if (Current.Type == TokenType.CloseParent)
            {
                NextToken();
            }
            else
            {
                // unit test: 106-parenthesis-error1.txt
                LogError(Current, string.Format(RS.SpanCloseBracketMismatch, expression.Span.Start, Current.Type));
            }
            return(Close(expression));
        }
Exemplo n.º 3
0
        private ScriptExpression ParseVariable()
        {
            var currentToken = Current;
            var currentSpan  = CurrentSpan;
            var endSpan      = currentSpan;
            var text         = GetAsText(currentToken);

            // Return ScriptLiteral for null, true, false
            // Return ScriptAnonymousFunction
            switch (text)
            {
            case "null":
                var nullValue = Open <ScriptLiteral>();
                NextToken();
                return(Close(nullValue));

            case "true":
                var trueValue = Open <ScriptLiteral>();
                trueValue.Value = true;
                NextToken();
                return(Close(trueValue));

            case "false":
                var falseValue = Open <ScriptLiteral>();
                falseValue.Value = false;
                NextToken();
                return(Close(falseValue));

            case "do":
                var functionExp = Open <ScriptAnonymousFunction>();
                functionExp.Function = ParseFunctionStatement(true);
                var func = Close(functionExp);
                return(func);

            case "this":
                if (!_isLiquid)
                {
                    var thisExp = Open <ScriptThisExpression>();
                    NextToken();
                    return(Close(thisExp));
                }
                break;
            }

            // Keeps trivia before this token
            List <ScriptTrivia> triviasBefore = null;

            if (_isKeepTrivia && _trivias.Count > 0)
            {
                triviasBefore = new List <ScriptTrivia>();
                triviasBefore.AddRange(_trivias);
                _trivias.Clear();
            }

            NextToken();
            var scope = ScriptVariableScope.Global;

            if (text.StartsWith("$"))
            {
                scope = ScriptVariableScope.Local;
                text  = text.Substring(1);

                // Convert $0, $1... $n variable into $[0] $[1]...$[n] variables
                int index;
                if (int.TryParse(text, out index))
                {
                    var indexerExpression = new ScriptIndexerExpression {
                        Span = currentSpan,

                        Target = new ScriptVariableLocal(ScriptVariable.Arguments.Name)
                        {
                            Span = currentSpan
                        },

                        Index = new ScriptLiteral()
                        {
                            Span = currentSpan, Value = index
                        }
                    };

                    if (_isKeepTrivia)
                    {
                        if (triviasBefore != null)
                        {
                            indexerExpression.Target.AddTrivias(triviasBefore, true);
                        }
                        FlushTrivias(indexerExpression.Index, false);
                    }

                    return(indexerExpression);
                }
            }
            else if (text == "for" || text == "while" || text == "tablerow" || (_isLiquid && (text == "forloop" || text == "tablerowloop")))
            {
                if (Current.Type == TokenType.Dot)
                {
                    NextToken();
                    if (Current.Type == TokenType.Identifier)
                    {
                        endSpan = CurrentSpan;
                        var loopVariableText = GetAsText(Current);
                        NextToken();

                        scope = ScriptVariableScope.Loop;
                        if (_isLiquid)
                        {
                            switch (loopVariableText)
                            {
                            case "first":
                                text = ScriptVariable.LoopFirst.Name;
                                break;

                            case "last":
                                text = ScriptVariable.LoopLast.Name;
                                break;

                            case "index0":
                                text = ScriptVariable.LoopIndex.Name;
                                break;

                            case "rindex0":
                                text = ScriptVariable.LoopRIndex.Name;
                                break;

                            case "rindex":
                            case "index":
                                // Because forloop.index is 1 based index, we need to create a binary expression
                                // to support it here
                                bool isrindex = loopVariableText == "rindex";

                                var nested = new ScriptNestedExpression()
                                {
                                    Expression = new ScriptBinaryExpression()
                                    {
                                        Operator = ScriptBinaryOperator.Add,
                                        Left     = new ScriptVariableLoop(isrindex ? ScriptVariable.LoopRIndex.Name : ScriptVariable.LoopIndex.Name)
                                        {
                                            Span = currentSpan
                                        },
                                        Right = new ScriptLiteral(1)
                                        {
                                            Span = currentSpan
                                        },
                                        Span = currentSpan
                                    },
                                    Span = currentSpan
                                };

                                if (_isKeepTrivia)
                                {
                                    if (triviasBefore != null)
                                    {
                                        nested.AddTrivias(triviasBefore, true);
                                    }
                                    FlushTrivias(nested, false);
                                }
                                return(nested);

                            case "length":
                                text = ScriptVariable.LoopLength.Name;
                                break;

                            case "col":
                                if (text != "tablerowloop")
                                {
                                    // unit test: 108-variable-loop-error2.txt
                                    LogError(currentToken, $"The loop variable <{text}.col> is invalid");
                                }
                                text = ScriptVariable.TableRowCol.Name;
                                break;

                            default:
                                text = text + "." + loopVariableText;
                                LogError(currentToken, $"The liquid loop variable <{text}> is not supported");
                                break;
                            }
                        }
                        else
                        {
                            switch (loopVariableText)
                            {
                            case "first":
                                text = ScriptVariable.LoopFirst.Name;
                                break;

                            case "last":
                                if (text == "while")
                                {
                                    // unit test: 108-variable-loop-error2.txt
                                    LogError(currentToken, "The loop variable <while.last> is invalid");
                                }
                                text = ScriptVariable.LoopLast.Name;
                                break;

                            case "changed":
                                if (text == "while")
                                {
                                    // unit test: 108-variable-loop-error2.txt
                                    LogError(currentToken, "The loop variable <while.changed> is invalid");
                                }
                                text = ScriptVariable.LoopChanged.Name;
                                break;

                            case "length":
                                if (text == "while")
                                {
                                    // unit test: 108-variable-loop-error2.txt
                                    LogError(currentToken, "The loop variable <while.length> is invalid");
                                }
                                text = ScriptVariable.LoopLength.Name;
                                break;

                            case "even":
                                text = ScriptVariable.LoopEven.Name;
                                break;

                            case "odd":
                                text = ScriptVariable.LoopOdd.Name;
                                break;

                            case "index":
                                text = ScriptVariable.LoopIndex.Name;
                                break;

                            case "rindex":
                                if (text == "while")
                                {
                                    // unit test: 108-variable-loop-error2.txt
                                    LogError(currentToken, "The loop variable <while.rindex> is invalid");
                                }
                                text = ScriptVariable.LoopRIndex.Name;
                                break;

                            case "col":
                                if (text != "tablerow")
                                {
                                    // unit test: 108-variable-loop-error2.txt
                                    LogError(currentToken, $"The loop variable <{text}.col> is invalid");
                                }
                                text = ScriptVariable.TableRowCol.Name;
                                break;

                            default:
                                text = text + "." + loopVariableText;
                                // unit test: 108-variable-loop-error1.txt
                                LogError(currentToken, $"The loop variable <{text}> is not supported");
                                break;
                            }
                        }

                        // We no longer checks at parse time usage of loop variables, as they can be used in a wrap context
                        //if (!IsInLoop())
                        //{
                        //    LogError(currentToken, $"Unexpected variable <{text}> outside of a loop");
                        //}
                    }
                    else
                    {
                        LogError(currentToken, $"Invalid token `{Current.Type}`. The loop variable <{text}> dot must be followed by an identifier");
                    }
                }
            }
            else if (_isLiquid && text == "continue")
            {
                scope = ScriptVariableScope.Local;
            }

            var result = ScriptVariable.Create(text, scope);

            result.Span = new SourceSpan {
                FileName = currentSpan.FileName,
                Start    = currentSpan.Start,
                End      = endSpan.End
            };

            // A liquid variable can have `-` in its identifier
            // If this is the case, we need to translate it to `this["this"]` instead
            if (_isLiquid && text.IndexOf('-') >= 0)
            {
                var newExp = new ScriptIndexerExpression {
                    Target = new ScriptThisExpression()
                    {
                        Span = result.Span
                    },
                    Index = new ScriptLiteral(text)
                    {
                        Span = result.Span
                    },
                    Span = result.Span
                };

                // Flush any trivias after
                if (_isKeepTrivia)
                {
                    if (triviasBefore != null)
                    {
                        newExp.Target.AddTrivias(triviasBefore, true);
                    }
                    FlushTrivias(newExp, false);
                }
                // Return the expression
                return(newExp);
            }

            if (_isKeepTrivia)
            {
                // Flush any trivias after
                if (triviasBefore != null)
                {
                    result.AddTrivias(triviasBefore, true);
                }
                FlushTrivias(result, false);
            }
            return(result);
        }
Exemplo n.º 4
0
        private ScriptIfStatement ParseIfStatement(bool invert, ScriptKeyword elseKeyword = null)
        {
            // unit test: 200-if-else-statement.txt
            var ifStatement = Open <ScriptIfStatement>();

            ifStatement.ElseKeyword = elseKeyword;

            if (_isLiquid && elseKeyword != null)
            {
                // Parse elseif
                Open(ifStatement.IfKeyword);
                NextToken();
                Close(ifStatement.IfKeyword);
            }
            else
            {
                if (_isLiquid && invert)         // we have an unless
                {
                    Open(ifStatement.IfKeyword); // still transfer trivias to IfKeyword
                    NextToken();
                    Close(ifStatement.IfKeyword);
                }
                else
                {
                    ExpectAndParseKeywordTo(ifStatement.IfKeyword); // Parse if keyword
                }
            }

            var condition = ExpectAndParseExpression(ifStatement, allowAssignment: false);

            // Transform a `if condition` to `if !(condition)`
            if (invert)
            {
                var invertCondition = ScriptUnaryExpression.Wrap(ScriptUnaryOperator.Not, ScriptToken.Exclamation(), ScriptNestedExpression.Wrap(condition, _isKeepTrivia), _isKeepTrivia);
                condition = invertCondition;
            }

            ifStatement.Condition = condition;

            if (ExpectEndOfStatement())
            {
                ifStatement.Then = ParseBlockStatement(ifStatement);
            }

            return(Close(ifStatement));
        }