Пример #1
0
        private bool ParseMedium(PsiBuilder builder)
        {
            var start = builder.Mark();

            if (!ParseHigh(builder))
            {
                builder.Drop(start);
                return(false);
            }

            while (builder.GetTokenType() == SpringTokenType.MEDIUM_BINOP)
            {
                AdvanceWithSpaces(builder);

                if (!ParseHigh(builder))
                {
                    builder.Drop(start);
                    return(false);
                }

                builder.DoneBeforeWhitespaces(start, SpringCompositeNodeType.MEDIUM_BINOP, null);
                builder.Precede(start);
            }

            builder.Drop(start);
            return(true);
        }
Пример #2
0
        private bool ParseExpression(PsiBuilder builder)
        {
            SkipWhitespace(builder);
            int  startExpr = builder.Mark();
            bool res       = ParseSimpleExpression(builder);

            if (!res)
            {
                // builder.Error("Expected Expression");
                builder.RollbackTo(startExpr);
                return(false);
            }
            else
            {
                while (builder.GetTokenType() == SpringTokenType.REL_OPS)
                {
                    builder.AdvanceLexer();
                    if (!ParseSimpleExpression(builder))
                    {
                        builder.Error("Expected SIMPLE EXPRESSION");
                        break;
                    }
                }
            }
            builder.Done(startExpr, SpringCompositeNodeType.EXPR, null);
            SkipWhitespace(builder);
            return(res);
        }
Пример #3
0
 private static void SkipWhitespace(PsiBuilder builder)
 {
     while (builder.GetTokenType() == SpringTokenType.WS || builder.GetTokenType() == SpringTokenType.COMMENT)
     {
         builder.AdvanceLexer();
     }
 }
Пример #4
0
        public IFile ParseFile()
        {
            using (var def = Lifetime.Define())
            {
                var builder  = new PsiBuilder(myLexer, SpringFileNodeType.Instance, new TokenFactory(), def.Lifetime);
                var fileMark = builder.Mark();

                StringBuilder b = new StringBuilder();
                foreach (var tok in myLexer.Tokens())
                {
                    b.Append(tok + " ");
                }

                b.ToString();

                ParseDefines(builder);

                builder.Done(fileMark, SpringFileNodeType.Instance, null);
                var file = (IFile)builder.BuildTree();

                SprintTreeScopeUtl.InitScopes(file);

                var sb = new StringBuilder();
                DebugUtil.DumpPsi(new StringWriter(sb), file);
                sb.ToString();

                return(file);
            }
        }
Пример #5
0
        private bool ParseLetBinding(PsiBuilder builder)
        {
            bool lexerAdvanced = false;
            var  mark          = builder.Mark();

            if (builder.GetTokenType() == SpringTokenType.LPAREN)
            {
                AdvanceSkippingWhitespace(builder);
                var identParsed = ParseIdentDecl(builder);
                SkipWhitespace(builder);

                var markExpr   = builder.Mark();
                var exprParsed = ParseExpr(builder);
                SkipWhitespace(builder);
                builder.Done(markExpr, SpringCompositeNodeType.BIND, null);

                lexerAdvanced = identParsed || exprParsed;

                if (builder.GetTokenType() == SpringTokenType.RPAREN)
                {
                    AdvanceSkippingWhitespace(builder);
                    builder.Drop(mark);
                }
                else
                {
                    builder.Error(mark, "Expect ')' to close binding");
                }
            }
            else
            {
                builder.Error(mark, "Expected binding!");
            }

            return(lexerAdvanced);
        }
Пример #6
0
        private bool ParseHigh(PsiBuilder builder)
        {
            var start = builder.Mark();

            if (!ParseIdent(builder))
            {
                builder.Drop(start);
                return(false);
            }

            if (builder.GetTokenType() == SpringTokenType.HIGH_BINOP)
            {
                AdvanceWithSpaces(builder);
                if (!ParseHigh(builder))
                {
                    builder.Drop(start);
                    return(false);
                }
                builder.DoneBeforeWhitespaces(start, SpringCompositeNodeType.HIGH_BINOP, null);
                return(true);
            }

            builder.Drop(start);
            return(true);
        }
Пример #7
0
        private bool ParseSeq(PsiBuilder builder)
        {
            var start = builder.Mark();

            if (!ParseStmt(builder))
            {
                builder.Drop(start);
                return(false);
            }

            if (builder.Eof() || builder.GetTokenType() == SpringTokenType.RFBRACKET)
            {
                builder.Drop(start);
                return(true);
            }

            if (!ParseSeq(builder))
            {
                builder.Drop(start);
                return(false);
            }

            builder.DoneBeforeWhitespaces(start, SpringCompositeNodeType.SEQ, null);
            return(true);
        }
Пример #8
0
        private static int?ParseRule(PsiBuilder builder, int?ruleMarker)
        {
            if (builder.GetTokenType() == GherkinTokenTypes.RULE_KEYWORD)
            {
                if (ruleMarker != null)
                {
                    builder.DoneBeforeWhitespaces(ruleMarker.Value, GherkinNodeTypes.RULE, null);
                }

                ruleMarker = builder.Mark();
                builder.AdvanceLexer();
                if (builder.GetTokenType() == GherkinTokenTypes.COLON)
                {
                    builder.AdvanceLexer();
                }
                else
                {
                    return(ruleMarker);
                }

                while (builder.GetTokenType() == GherkinTokenTypes.TEXT ||
                       builder.GetTokenType() == GherkinTokenTypes.WHITE_SPACE ||
                       builder.GetTokenType() == GherkinTokenTypes.NEW_LINE)
                {
                    builder.AdvanceLexer();
                }
            }

            return(ruleMarker);
        }
Пример #9
0
        private bool ParseAssign(PsiBuilder builder)
        {
            var start = builder.Mark();

            if (builder.GetTokenType() != SpringTokenType.IDENT)
            {
                builder.Drop(start);
                builder.Error("Missing variable");
                return(false);
            }

            AdvanceWithSpaces(builder);

            if (builder.GetTokenType() != SpringTokenType.ASSIGN)
            {
                builder.Drop(start);
                builder.Error("Missing ':='");
                return(false);
            }

            AdvanceWithSpaces(builder);

            if (!ParseLogic(builder))
            {
                builder.Drop(start);
                return(false);
            }

            builder.DoneBeforeWhitespaces(start, SpringCompositeNodeType.ASSIGN, null);
            return(true);
        }
Пример #10
0
        public IFile ParseFile()
        {
            using (var lifetimeDefinition = Lifetime.Define())
            {
                var builder    = new PsiBuilder(_lexer, GherkinNodeTypes.FILE, null, lifetimeDefinition.Lifetime);
                var fileMarker = builder.Mark();

                while (!builder.Eof())
                {
                    var tokenType = builder.GetTokenType();

                    if (tokenType == GherkinTokenTypes.FEATURE_KEYWORD)
                    {
                        ParseFeature(builder);
                    }
                    else if (tokenType == GherkinTokenTypes.TAG)
                    {
                        ParseTags(builder);
                    }
                    else if (tokenType == GherkinTokenTypes.COMMENT)
                    {
                        ParseComments(builder);
                    }
                    else
                    {
                        builder.AdvanceLexer();
                    }
                }

                builder.Done(fileMarker, GherkinNodeTypes.FILE, new GherkinFile.FileMetadata(_sourceFile?.Name, _lang));
                var resultTree = (GherkinFile)builder.BuildTree();

                return(resultTree);
            }
        }
Пример #11
0
        private void ParseComments(PsiBuilder builder)
        {
            while (builder.GetTokenType() == GherkinTokenTypes.COMMENT)
            {
                var commentMarker = builder.Mark();
                var commentText   = builder.GetTokenText();
                builder.AdvanceLexer();
                var match = LanguagePattern.Match(commentText);
                if (match.Success)
                {
                    _lang = match.Groups["lang"].Value;
                    builder.DoneBeforeWhitespaces(commentMarker, GherkinNodeTypes.LANGUAGE_COMMENT, _lang);
                }
                else
                {
                    builder.Drop(commentMarker);
                }

                if (builder.GetTokenType() == GherkinTokenTypes.WHITE_SPACE ||
                    builder.GetTokenType() == GherkinTokenTypes.NEW_LINE)
                {
                    builder.AdvanceLexer();
                }
            }
        }
Пример #12
0
        public IFile ParseFile()
        {
            using (var def = Lifetime.Define())
            {
                var builder = new PsiBuilder(_lexer, SpringFileNodeType.Instance, new TokenFactory(), def.Lifetime);
                var lexer   = new ToylangLexer(new AntlrInputStream(_lexer.Buffer.GetText()));
                var parser  = new ToylangParser(new CommonTokenStream(lexer));
                parser.AddErrorListener(new SpringErrorListener(builder));
                var visitor = new NodeVisitor(builder);

                // Begin Top level File
                var fileBeginMark = builder.Mark();

                // Inner structure
                visitor.Visit(parser.file());

                // End Top level File
                builder.ResetCurrentLexeme(visitor.MaxTokenIndexConsumed, visitor.MaxTokenIndexConsumed);
                builder.Done(fileBeginMark, SpringFileNodeType.Instance, null);

                var compositeElement = builder.BuildTree();
                var file             = (IFile)compositeElement;
                return(file);
            }
        }
Пример #13
0
        private static void ParseTable(PsiBuilder builder)
        {
            var marker         = builder.Mark();
            var rowMarker      = builder.Mark();
            var headerNodeType = GherkinNodeTypes.TABLE_HEADER_ROW;
            int?cellMarker     = null;

            var wasLineBreak      = false;
            var possibleEmptyCell = false;

            while (builder.GetTokenType() == GherkinTokenTypes.PIPE ||
                   builder.GetTokenType() == GherkinTokenTypes.TABLE_CELL ||
                   builder.GetTokenType() == GherkinTokenTypes.WHITE_SPACE)
            {
                var tokenType = builder.GetTokenType();
                if (tokenType == GherkinTokenTypes.TABLE_CELL && cellMarker == null)
                {
                    cellMarker = builder.Mark();
                }
                else if (tokenType != GherkinTokenTypes.TABLE_CELL && cellMarker != null)
                {
                    builder.Done(cellMarker.Value, GherkinNodeTypes.TABLE_CELL, null);
                    cellMarker        = null;
                    possibleEmptyCell = false;
                }

                if (tokenType == GherkinTokenTypes.PIPE)
                {
                    if (wasLineBreak)
                    {
                        possibleEmptyCell = true;
                        builder.Done(rowMarker, headerNodeType, null);
                        headerNodeType = GherkinNodeTypes.TABLE_ROW;
                        rowMarker      = builder.Mark();
                    }
                    else
                    {
                        if (possibleEmptyCell)
                        {
                            cellMarker = builder.Mark();
                            builder.Done(cellMarker.Value, GherkinNodeTypes.TABLE_CELL, null);
                            cellMarker = null;
                        }

                        possibleEmptyCell = true;
                    }
                }

                wasLineBreak = IsLineBreak(builder);
                builder.AdvanceLexer();
            }

            if (cellMarker.HasValue)
            {
                builder.Done(cellMarker.Value, GherkinNodeTypes.TABLE_CELL, null);
            }

            builder.Done(rowMarker, headerNodeType, null);
            builder.Done(marker, GherkinNodeTypes.TABLE, null);
        }
Пример #14
0
        private void ParseBlock(PsiBuilder builder)
        {
            while (!builder.Eof())
            {
                var tt = builder.GetTokenType();
                if (tt == SpringTokenType.LeftBrace)
                {
                    var start = builder.Mark();
                    builder.AdvanceLexer();
                    ParseBlock(builder);

                    if (builder.GetTokenType() != SpringTokenType.RightBrace)
                    {
                        builder.Error("Expected '}'");
                    }
                    else
                    {
                        builder.AdvanceLexer();
                    }

                    builder.Done(start, SpringCompositeNodeType.BLOCK, null);
                }
                else if (tt == SpringTokenType.RightBrace)
                {
                    return;
                }
                else
                {
                    builder.AdvanceLexer();
                }
            }
        }
Пример #15
0
        private bool ParseTerm(PsiBuilder builder)
        {
            SkipWhitespace(builder);
            int  startTerm = builder.Mark();
            bool res       = ParseFactor(builder);

            if (!res)
            {
                builder.Error("Expected FACTOR");
            }
            else
            {
                while (builder.GetTokenType() == SpringTokenType.FACTOR_OPS)
                {
                    builder.AdvanceLexer();
                    if (!ParseFactor(builder))
                    {
                        builder.Error("expected FACTOR");
                        break;
                    }
                }
            }
            builder.Done(startTerm, SpringCompositeNodeType.TERM, null);
            SkipWhitespace(builder);
            return(res);
        }
Пример #16
0
        private bool ParseSimpleExpression(PsiBuilder builder)
        {
            SkipWhitespace(builder);
            int  startSimpleExpr = builder.Mark();
            bool res             = ParseTerm(builder);

            if (!res)
            {
                builder.Error("Expected TERM");
            }
            else
            {
                // throw new Exception("Token past Term: " + builder.GetTokenType().ToString() + " \"" + builder.GetTokenText() + '"');
                while (builder.GetTokenType() == SpringTokenType.TERM_OPS)
                {
                    builder.AdvanceLexer();
                    if (!ParseTerm(builder))
                    {
                        builder.Error("Expected TERM");
                        break;
                    }
                }
            }
            builder.Done(startSimpleExpr, SpringCompositeNodeType.SIMPLE_EXPR, null);
            SkipWhitespace(builder);
            return(res);
        }
Пример #17
0
        private bool ParseCompoundStmt(PsiBuilder builder)
        {
            SkipWhitespace(builder);
            int startCompoundStmt = builder.Mark();

            if (builder.GetTokenType() != SpringTokenType.BEGIN)
            {
                builder.RollbackTo(startCompoundStmt);
                return(false);
            }

            builder.AdvanceLexer();
            if (!ParseStmtList(builder))
            {
                builder.Error("Expected STATEMENT");
            }

            if (builder.GetTokenType() != SpringTokenType.END)
            {
                builder.Error("Expected \"end\"");
            }
            else
            {
                builder.AdvanceLexer();
            }

            builder.Done(startCompoundStmt, SpringCompositeNodeType.COMPOUND_STMT, null);
            SkipWhitespace(builder);
            return(true);
        }
Пример #18
0
        private bool ParseAssignStmt(PsiBuilder builder)
        {
            SkipWhitespace(builder);
            int startAssignStmt = builder.Mark();

            if (!ParseVariableTypecast(builder))
            {
                if (builder.GetTokenType() != SpringTokenType.IDENTIFIER)
                {
                    builder.RollbackTo(startAssignStmt);
                    return(false);
                }

                builder.AdvanceLexer();
            }

            SkipWhitespace(builder);

            if (builder.GetTokenType() != SpringTokenType.ASSGN)
            {
                builder.RollbackTo(startAssignStmt);
                return(false);
            }

            builder.AdvanceLexer();
            SkipWhitespace(builder);

            if (!ParseExpression(builder))
            {
                builder.Error("Expected EXPRESSION");
            }
            builder.Done(startAssignStmt, SpringCompositeNodeType.ASSGN_STMT, null);
            SkipWhitespace(builder);
            return(true);
        }
Пример #19
0
        private bool ParseCaseHead(PsiBuilder builder)
        {
            SkipWhitespace(builder);
            var curToken = builder.GetTokenType();

            if (!curToken.IsConstantLiteral && !curToken.IsStringLiteral)
            {
                return(false);
            }

            builder.AdvanceLexer();
            SkipWhitespace(builder);
            if (builder.GetTokenType() == SpringTokenType.DOUBLEDOTS)
            {
                builder.AdvanceLexer();
                SkipWhitespace(builder);
                curToken = builder.GetTokenType();
                if (!curToken.IsConstantLiteral && !curToken.IsStringLiteral)
                {
                    builder.Error("expected constant");
                }
                else
                {
                    builder.AdvanceLexer();
                }
            }

            SkipWhitespace(builder);
            while (builder.GetTokenType() == SpringTokenType.COMMA)
            {
                builder.AdvanceLexer();
                SkipWhitespace(builder);
                curToken = builder.GetTokenType();
                if (!curToken.IsConstantLiteral && !curToken.IsStringLiteral)
                {
                    break;
                }

                builder.AdvanceLexer();
                SkipWhitespace(builder);
                if (builder.GetTokenType() == SpringTokenType.DOUBLEDOTS)
                {
                    builder.AdvanceLexer();
                    SkipWhitespace(builder);
                    curToken = builder.GetTokenType();
                    if (!curToken.IsConstantLiteral && !curToken.IsStringLiteral)
                    {
                        builder.Error("expected constant");
                    }
                    else
                    {
                        builder.AdvanceLexer();
                    }
                }
                SkipWhitespace(builder);
            }
            SkipWhitespace(builder);
            return(true);
        }
Пример #20
0
 private void AdvanceWithSpaces(PsiBuilder builder)
 {
     builder.AdvanceLexer();
     while (builder.GetTokenType() == SpringTokenType.WHITE_SPACE)
     {
         builder.AdvanceLexer();
     }
 }
Пример #21
0
 private static void SkipWhitespace(PsiBuilder builder)
 {
     while (builder.GetTokenType() == GherkinTokenTypes.WHITE_SPACE ||
            builder.GetTokenType() == GherkinTokenTypes.NEW_LINE)
     {
         builder.AdvanceLexer();
     }
 }
Пример #22
0
        private bool ParseWithStmt(PsiBuilder builder)
        {
            SkipWhitespace(builder);
            if (builder.GetTokenType() != SpringTokenType.WITH)
            {
                return(false);
            }

            int mark = builder.Mark();

            builder.AdvanceLexer();
            SkipWhitespace(builder);

            if (builder.GetTokenType() != SpringTokenType.IDENTIFIER)
            {
                builder.Error("Expected variable identifier");
            }
            else
            {
                builder.AdvanceLexer();
            }

            SkipWhitespace(builder);
            while (builder.GetTokenType() == SpringTokenType.COMMA)
            {
                builder.AdvanceLexer();
                SkipWhitespace(builder);
                if (builder.GetTokenType() == SpringTokenType.IDENTIFIER)
                {
                    builder.AdvanceLexer();
                    SkipWhitespace(builder);
                }
                else
                {
                    builder.Error("Expected variable identifier");
                    break;
                }
            }

            SkipWhitespace(builder);
            if (builder.GetTokenType() != SpringTokenType.DO)
            {
                builder.Error("expected 'do'");
            }
            else
            {
                builder.AdvanceLexer();
            }

            if (!ParseStatement(builder))
            {
                builder.Error("expected STMT");
            }

            builder.Done(mark, SpringCompositeNodeType.WITH_STMT, null);
            SkipWhitespace(builder);
            return(true);
        }
Пример #23
0
        private static bool IsLineBreak(PsiBuilder builder)
        {
            if (builder.GetTokenType() != GherkinTokenTypes.WHITE_SPACE)
            {
                return(false);
            }

            return(builder.GetTokenText()?.Contains("\n") == true);
        }
Пример #24
0
 private void SkipWhitespace(PsiBuilder builder)
 {
     while (builder.GetTokenType() == SpringTokenType.WHITESPACE ||
            builder.GetTokenType() == SpringTokenType.SINGLE_LINE_COMMENT ||
            builder.GetTokenType() == SpringTokenType.MULTILINE_COMMENT)
     {
         builder.AdvanceLexer();
     }
 }
Пример #25
0
        private bool ParseFactor(PsiBuilder builder)
        {
            SkipWhitespace(builder);
            int  startFactor = builder.Mark();
            var  tt          = builder.GetTokenType();
            bool result      = false;

            if (ParseFunctionCall(builder))
            {
                result = true;
            }
            else if (ParseValueTypecast(builder))
            {
                result = true;
            }
            else if (ParseVariableTypecast(builder))
            {
                result = true;
            }
            else if (ParseSetCtor(builder))
            {
                result = true;
            }
            else if (ParseAddrOf(builder))
            {
                result = true;
            }
            else if (tt == SpringTokenType.IDENTIFIER || tt == SpringTokenType.NUMBER || tt == SpringTokenType.STRING)
            {
                builder.AdvanceLexer();
                result = true;
            }
            else if (tt == SpringTokenType.LBRACKET)
            {
                builder.AdvanceLexer();
                result = ParseExpression(builder);
                if (builder.GetTokenType() != SpringTokenType.RBRACKET)
                {
                    builder.Error("Expected )");
                }
                else
                {
                    builder.AdvanceLexer();
                    result = true;
                }
            }
            else
            {
                builder.Error("Expected FACTOR");
            }
            builder.Done(startFactor, SpringCompositeNodeType.FACTOR, null);
            SkipWhitespace(builder);
            return(result);
        }
Пример #26
0
        private void ParseDefines(PsiBuilder builder)
        {
            SkipWhitespace(builder);
            while (!builder.Eof())
            {
                var tt = builder.GetTokenType();
                if (tt == SpringTokenType.LPAREN)
                {
                    var start = builder.Mark();
                    AdvanceSkippingWhitespace(builder);
                    if (builder.GetTokenType() == SpringTokenType.DEFINE)
                    {
                        AdvanceSkippingWhitespace(builder);
                        ParseIdentDecl(builder);
                        SkipWhitespace(builder);
                        ParseExpr(builder);
                        SkipWhitespace(builder);
                    }
                    else
                    {
                        builder.Error("Expected definition!");
                    }

                    /*
                     * if (builder.GetTokenType() == SpringTokenType.RPAREN)
                     *  builder.AdvanceLexer();
                     * else
                     *  builder.Error("Expected ')' to close definition!");
                     *
                     * builder.Done(start, SpringCompositeNodeType.DEFINE, null);
                     */

                    SkipWhitespace(builder);
                    if (builder.GetTokenType() == SpringTokenType.RPAREN)
                    {
                        builder.AdvanceLexer();
                        builder.Done(start, SpringCompositeNodeType.DEFINE, null);
                    }
                    else
                    {
                        builder.Error(start, "Expected ')' to close definition!");
                    }
                }
                else
                {
                    var tokenType = builder.GetTokenType();
                    var mark      = builder.Mark();
                    builder.AdvanceLexer();
                    builder.Error(mark, "Expected '(', but got: " + tokenType.TokenRepresentation);
                }

                SkipWhitespace(builder);
            }
        }
Пример #27
0
        private static void ParseTags(PsiBuilder builder)
        {
            while (builder.GetTokenType() == GherkinTokenTypes.TAG)
            {
                var tagMarker = builder.Mark();
                builder.AdvanceLexer();
                builder.DoneBeforeWhitespaces(tagMarker, GherkinNodeTypes.TAG, null);

                SkipWhitespace(builder);
            }
        }
Пример #28
0
        private static bool ParseStepParameter(PsiBuilder builder)
        {
            if (builder.GetTokenType() != GherkinTokenTypes.STEP_PARAMETER_TEXT)
            {
                return(false);
            }

            var stepParameterMarker = builder.Mark();

            builder.AdvanceLexer();
            builder.Done(stepParameterMarker, GherkinNodeTypes.STEP_PARAMETER, null);
            return(true);
        }
Пример #29
0
        private void ParseFeature(PsiBuilder builder)
        {
            var featureMarker = builder.Mark();

            Assertion.Assert(builder.GetTokenType() == GherkinTokenTypes.FEATURE_KEYWORD,
                             "_builder.GetTokenType() == GherkinTokenTypes.FEATURE_KEYWORD");

            int? descMarker   = null;
            bool wasLineBreak = false;

            do
            {
                builder.AdvanceLexer();

                var tokenType = builder.GetTokenType();
                if (tokenType == GherkinTokenTypes.TEXT && descMarker == null)
                {
                    if (wasLineBreak)
                    {
                        descMarker = builder.Mark();
                    }
                }

                if (GherkinTokenTypes.SCENARIOS_KEYWORDS[tokenType] ||
                    tokenType == GherkinTokenTypes.RULE_KEYWORD ||
                    tokenType == GherkinTokenTypes.BACKGROUND_KEYWORD ||
                    tokenType == GherkinTokenTypes.TAG)
                {
                    if (descMarker != null)
                    {
                        builder.DoneBeforeWhitespaces(descMarker.Value, GherkinNodeTypes.FEATURE_HEADER, null);
                        descMarker = null;
                    }

                    ParseFeatureElements(builder);
                }

                wasLineBreak = IsLineBreak(builder);
                if (wasLineBreak)
                {
                    SkipGroupedWhiteSpaces(builder);
                }
            } while (builder.GetTokenType() != GherkinTokenTypes.FEATURE_KEYWORD && !builder.Eof());

            if (descMarker != null)
            {
                builder.DoneBeforeWhitespaces(descMarker.Value, GherkinNodeTypes.FEATURE_HEADER, null);
            }

            builder.DoneBeforeWhitespaces(featureMarker, GherkinNodeTypes.FEATURE, null);
        }
Пример #30
0
        private void ParseStep(PsiBuilder builder)
        {
            var marker      = builder.Mark();
            var keywordText = builder.GetTokenText();

            builder.AdvanceLexer();
            while (builder.GetTokenType() == GherkinTokenTypes.TEXT ||
                   builder.GetTokenType() == GherkinTokenTypes.STEP_PARAMETER_BRACE ||
                   builder.GetTokenType() == GherkinTokenTypes.STEP_PARAMETER_TEXT ||
                   builder.GetTokenType() == GherkinTokenTypes.WHITE_SPACE ||
                   builder.GetTokenType() == GherkinTokenTypes.NEW_LINE)
            {
                if (IsLineBreak(builder))
                {
                    break;
                }

                if (!ParseStepParameter(builder))
                {
                    builder.AdvanceLexer();
                }
            }

            SkipGroupedWhiteSpaces(builder);

            var nextToken = builder.GetTokenType(1);

            if (nextToken == GherkinTokenTypes.PIPE)
            {
                builder.AdvanceLexer();
                ParseTable(builder);
            }
            else if (nextToken == GherkinTokenTypes.PYSTRING)
            {
                builder.AdvanceLexer();
                ParsePystring(builder);
            }

            var stepKind          = _keywordProvider.GetStepKind(_lang, keywordText);
            var effectiveStepKind = stepKind;

            if (stepKind == GherkinStepKind.And)
            {
                effectiveStepKind = _lastStepKind;
            }
            else if (stepKind == GherkinStepKind.Given || stepKind == GherkinStepKind.When || stepKind == GherkinStepKind.Then)
            {
                _lastStepKind = effectiveStepKind;
            }
            builder.DoneBeforeWhitespaces(marker, GherkinNodeTypes.STEP, (stepKind, effectiveStepKind));
        }