private void ParseLiquidStatement(string identifier, ScriptStatement parent, ref ScriptStatement statement, ref bool hasEnd, ref bool nextStatement)
        {
            Token startToken = Current;

            if (!_isLiquidTagSection)
            {
                statement = ParseLiquidExpressionStatement(parent);
                return;
            }

            if (identifier != "when" && identifier != "case" && !identifier.StartsWith("end") && parent is ScriptCaseStatement)
            {
                // 205-case-when-statement-error1.txt
                LogError(startToken, string.Format(RS.UnexpectedTokenInCase, GetAsText(startToken)));
            }

            ScriptStatement startStatement = null;
            string          pendingStart   = null;

            switch (identifier)
            {
            case "endif":
                startStatement = FindFirstStatementExpectingEnd() as ScriptIfStatement;
                pendingStart   = "`if`/`else`";
                // Handle after the switch
                break;

            case "endifchanged":
                startStatement = FindFirstStatementExpectingEnd() as ScriptIfStatement;
                pendingStart   = "`ifchanged`";
                // Handle after the switch
                break;

            case "endunless":
                startStatement = FindFirstStatementExpectingEnd() as ScriptIfStatement;
                pendingStart   = "`unless`";
                break;

            case "endfor":
                startStatement = FindFirstStatementExpectingEnd() as ScriptForStatement;
                pendingStart   = "`for`";
                break;

            case "endcase":
                startStatement = FindFirstStatementExpectingEnd() as ScriptCaseStatement;
                pendingStart   = "`case`";
                break;

            case "endcapture":
                startStatement = FindFirstStatementExpectingEnd() as ScriptCaptureStatement;
                pendingStart   = "`capture`";
                break;

            case "endtablerow":
                startStatement = FindFirstStatementExpectingEnd() as ScriptTableRowStatement;
                pendingStart   = "`tablerow`";
                break;

            case "case":
                statement = ParseCaseStatement();
                break;

            case "when":
                ScriptWhenStatement whenStatement = ParseWhenStatement();
                var whenParent = parent as ScriptConditionStatement;
                if (parent is ScriptWhenStatement)
                {
                    ((ScriptWhenStatement)whenParent).Next = whenStatement;
                }
                else if (parent is ScriptCaseStatement)
                {
                    statement = whenStatement;
                }
                else
                {
                    nextStatement = false;

                    // unit test: TODO
                    LogError(startToken, RS.WhenConditionSyntaxError);
                }
                hasEnd = true;
                break;

            case "if":
                statement = ParseIfStatement(false, false);
                break;

            case "ifchanged":
                statement = ParseLiquidIfChanged();
                break;

            case "unless":
                CheckNotInCase(parent, startToken);
                statement = ParseIfStatement(true, false);
                break;

            case "else":
            case "elsif":
                ScriptConditionStatement nextCondition = ParseElseStatement(identifier == "elsif");
                var parentCondition = parent as ScriptConditionStatement;
                if (parent is ScriptIfStatement || parent is ScriptWhenStatement)
                {
                    if (parent is ScriptIfStatement)
                    {
                        ((ScriptIfStatement)parentCondition).Else = nextCondition;
                    }
                    else
                    {
                        if (identifier == "elsif")
                        {
                            LogError(startToken, RS.ElseIfConditionSyntaxError);
                        }

                        ((ScriptWhenStatement)parentCondition).Next = nextCondition;
                    }
                }
                else
                {
                    nextStatement = false;

                    // unit test: 201-if-else-error3.txt
                    LogError(startToken, RS.ElseConditionSyntaxError);
                }
                hasEnd = true;
                break;

            case "for":
                statement = ParseForStatement <ScriptForStatement>();
                break;

            case "tablerow":
                statement = ParseForStatement <ScriptTableRowStatement>();
                break;

            case "cycle":
                statement = ParseLiquidCycleStatement();
                break;

            case "break":
                statement = Open <ScriptBreakStatement>();
                NextToken();
                ExpectEndOfStatement(statement);
                Close(statement);
                break;

            case "continue":
                statement = Open <ScriptContinueStatement>();
                NextToken();
                ExpectEndOfStatement(statement);
                Close(statement);
                break;

            case "assign":
                if (_isKeepTrivia)
                {
                    _trivias.Clear();
                }

                NextToken();     // skip assign

                Token token = _token;
                // Try to parse an expression
                ScriptExpressionStatement expressionStatement = ParseExpressionStatement();
                // If we don't have an assign expression, this is not a valid assign
                if (!(expressionStatement.Expression is ScriptAssignExpression))
                {
                    LogError(token, RS.ExpectAssignmentExpression);
                }

                statement = expressionStatement;
                break;

            case "capture":
                statement = ParseCaptureStatement();
                break;

            case "increment":
                statement = ParseLiquidIncDecStatement(false);
                break;

            case "decrement":
                statement = ParseLiquidIncDecStatement(true);
                break;

            case "include":
                statement = ParseLiquidIncludeStatement();
                break;

            default:
                statement = ParseLiquidExpressionStatement(parent);
                break;
            }

            if (pendingStart != null)
            {
                if (_isKeepTrivia)
                {
                    _trivias.Add(new ScriptTrivia(CurrentSpan, ScriptTriviaType.End));
                }

                NextToken();

                hasEnd        = true;
                nextStatement = false;

                if (startStatement == null)
                {
                    LogError(startToken, string.Format(RS.IdentifierPendingStartMissing, pendingStart, identifier));
                }

                ExpectEndOfStatement(startStatement);
                if (_isKeepTrivia)
                {
                    FlushTrivias(startStatement, false);
                }
            }
        }
        private void ParseTextScriptStatement(string identifier, ScriptStatement parent, ref ScriptStatement statement, ref bool hasEnd, ref bool nextStatement)
        {
            Token startToken = Current;

            switch (identifier)
            {
            case "end":
                hasEnd        = true;
                nextStatement = false;

                if (_isKeepTrivia)
                {
                    _trivias.Add(new ScriptTrivia(CurrentSpan, ScriptTriviaType.End, _lexer.Text));
                }

                NextToken();

                ScriptStatement matchingStatement = FindFirstStatementExpectingEnd();
                ExpectEndOfStatement(matchingStatement);
                if (_isKeepTrivia)
                {
                    FlushTrivias(matchingStatement, false);
                }

                break;

            case "wrap":
                CheckNotInCase(parent, startToken);
                statement = ParseWrapStatement();
                break;

            case "if":
                CheckNotInCase(parent, startToken);
                statement = ParseIfStatement(false, false);
                break;

            case "case":
                CheckNotInCase(parent, startToken);
                statement = ParseCaseStatement();
                break;

            case "when":
                ScriptWhenStatement whenStatement = ParseWhenStatement();
                var whenParent = parent as ScriptConditionStatement;
                if (parent is ScriptWhenStatement)
                {
                    ((ScriptWhenStatement)whenParent).Next = whenStatement;
                }
                else if (parent is ScriptCaseStatement)
                {
                    statement = whenStatement;
                }
                else
                {
                    nextStatement = false;

                    // unit test: TODO
                    LogError(startToken, RS.WhenConditionSyntaxError);
                }
                hasEnd = true;
                break;

            case "else":
            case "elseif":
                ScriptConditionStatement nextCondition = ParseElseStatement(identifier == "elseif");
                var parentCondition = parent as ScriptConditionStatement;
                if (parent is ScriptIfStatement || parent is ScriptWhenStatement)
                {
                    if (parent is ScriptIfStatement)
                    {
                        ((ScriptIfStatement)parentCondition).Else = nextCondition;
                    }
                    else
                    {
                        if (identifier == "elseif")
                        {
                            LogError(startToken, RS.ElseIfConditionSyntaxError);
                        }

                        ((ScriptWhenStatement)parentCondition).Next = nextCondition;
                    }
                }
                else
                {
                    nextStatement = false;

                    // unit test: 201-if-else-error3.txt
                    LogError(startToken, RS.ElseConditionSyntaxError);
                }
                hasEnd = true;
                break;

            case "for":
                CheckNotInCase(parent, startToken);
                if (PeekToken().Type == TokenType.Dot)
                {
                    statement = ParseExpressionStatement();
                }
                else
                {
                    statement = ParseForStatement <ScriptForStatement>();
                }
                break;

            case "tablerow":
                CheckNotInCase(parent, startToken);
                if (PeekToken().Type == TokenType.Dot)
                {
                    statement = ParseExpressionStatement();
                }
                else
                {
                    statement = ParseForStatement <ScriptTableRowStatement>();
                }
                break;

            case "with":
                CheckNotInCase(parent, startToken);
                statement = ParseWithStatement();
                break;

            case "import":
                CheckNotInCase(parent, startToken);
                statement = ParseImportStatement();
                break;

            case "readonly":
                CheckNotInCase(parent, startToken);
                statement = ParseReadOnlyStatement();
                break;

            case "while":
                CheckNotInCase(parent, startToken);
                if (PeekToken().Type == TokenType.Dot)
                {
                    statement = ParseExpressionStatement();
                }
                else
                {
                    statement = ParseWhileStatement();
                }
                break;

            case "break":
                CheckNotInCase(parent, startToken);
                statement = Open <ScriptBreakStatement>();
                NextToken();
                ExpectEndOfStatement(statement);
                Close(statement);

                // This has to be done at execution time, because of the wrap statement
                //if (!IsInLoop())
                //{
                //    LogError(statement, RS.StatementOutsideLoopError);
                //}
                break;

            case "continue":
                CheckNotInCase(parent, startToken);
                statement = Open <ScriptContinueStatement>();
                NextToken();
                ExpectEndOfStatement(statement);
                Close(statement);

                // This has to be done at execution time, because of the wrap statement
                //if (!IsInLoop())
                //{
                //    LogError(statement, RS.StatementOutsideLoopError);
                //}
                break;

            case "func":
                CheckNotInCase(parent, startToken);
                statement = ParseFunctionStatement(false);
                break;

            case "ret":
                CheckNotInCase(parent, startToken);
                statement = ParseReturnStatement();
                break;

            case "capture":
                CheckNotInCase(parent, startToken);
                statement = ParseCaptureStatement();
                break;

            default:
                CheckNotInCase(parent, startToken);
                // Otherwise it is an expression statement
                statement = ParseExpressionStatement();
                break;
            }
        }