public static void Init(GrammarObject grammar)
        {
            var window = CreateInstance <ReconnectGrammarPopup>();

            window.position = new Rect(0, 0, 300, 450);
            window.CenterOnMainWin();
            window.grammar = grammar;
            window.ShowPopup();
        }
        /// <summary>
        ///   Instantiates new <see cref="GrammarObject" /> linked to grammar with id.
        /// </summary>
        /// <param name="grammarId">Id of grammar that should be linked to newly instantiated <see cref="GrammarObject" />.</param>
        /// <returns>
        ///   When resolved, a reference to newly created <see cref="GameObject" />. When rejected, an
        ///   <see cref="Exception" /> with info about error that occured.
        /// </returns>
        public static IPromise <GameObject> InstantiateGrammar(string grammarId)
        {
            if (!GrammarList.ContainsKey(grammarId))
            {
                return(Promise <GameObject> .Rejected(new ApplicationException("Instantiate grammar request error:\nRequested grammar not found.")));
            }
            var grammar = GrammarList[grammarId];

            var newObject = GrammarObject.Construct(grammar);

            Selection.objects = new Object[] { newObject };
            return(Promise <GameObject> .Resolved(newObject));
        }
Example #3
0
        public override List <Message> CheckValidity()
        {
            var errors = new List <Message>();

            foreach (var nt in NonterminalSymbols.Keys)
            {
                foreach (var tok in Lookaheads.Keys)
                {
                    if (this[nt, tok].Count > 1)
                    {
                        errors.Add(Message.Error(
                                       $"Грамматика не является LL(1): для нетерминала {GrammarObject.Userify(nt)} и токена {GrammarObject.Userify(tok)} допустимо несколько альтернатив: {String.Join(", ", this[nt, tok].Select(e=>GrammarObject.Userify(e)))}",
                                       GrammarObject.GetLocation(nt),
                                       "LanD"
                                       ));
                    }
                }
            }

            return(errors);
        }
Example #4
0
        public string ToString(int state, string lookahead = null, string padding = "")
        {
            var altPosGroups = Items[state]
                               .Where(i => String.IsNullOrEmpty(lookahead) || i.Lookahead == lookahead)
                               .GroupBy(i => new { i.Alternative, i.Position });
            var strings = new List <string>();

            foreach (var group in altPosGroups)
            {
                var userified = GrammarObject.UserifyElementwise(group.Key.Alternative);
                userified.Insert(group.Key.Position, "\u2022");

                var groupString = $"{String.Join("   ", userified)}";
                if (String.IsNullOrEmpty(lookahead))
                {
                    groupString += $"    |    {String.Join(", ", group.Select(l => GrammarObject.Userify(l.Lookahead)))}";
                }

                strings.Add(groupString);
            }

            return(String.Join($"{Environment.NewLine}{padding}", strings));
        }
Example #5
0
        private HashSet <string> GetStopTokens(LocalOptions options, IEnumerable <string> followSequence)
        {
            /// Если с Any не связана последовательность стоп-символов
            if (!options.AnyOptions.ContainsKey(AnyOption.Except))
            {
                /// Определяем множество токенов, которые могут идти после Any
                var tokensAfterText = GrammarObject.First(followSequence.ToList());
                /// Само Any во входном потоке нам и так не встретится, а вывод сообщения об ошибке будет красивее
                tokensAfterText.Remove(Grammar.ANY_TOKEN_NAME);

                /// Если указаны токены, которые нужно однозначно включать в Any
                if (options.AnyOptions.ContainsKey(AnyOption.Include))
                {
                    tokensAfterText.ExceptWith(options.AnyOptions[AnyOption.Include]);
                }

                return(tokensAfterText);
            }
            else
            {
                return(options.AnyOptions[AnyOption.Except]);
            }
        }
Example #6
0
        public override List <Message> CheckValidity()
        {
            var errors = new List <Message>();

            for (var itemIdx = 0; itemIdx < Actions.GetLength(0); ++itemIdx)
            {
                for (var lookaheadIdx = 0; lookaheadIdx < Actions.GetLength(1); ++lookaheadIdx)
                {
                    if (Actions[itemIdx, lookaheadIdx].Count > 1)
                    {
                        /// Сразу вытаскиваем токен, по которому возникает неоднозначность
                        var lookahead = Lookaheads.FirstOrDefault(l => l.Value == lookaheadIdx).Key;
                        /// Формируем строковые представления действий, которые по этому токену можно сделать
                        var actions = Actions[itemIdx, lookaheadIdx].Select(a => a is ReduceAction
                                                                ? $"{a.ActionName} по ветке {GrammarObject.Userify(((ReduceAction)a).ReductionAlternative)} нетерминала {GrammarObject.Userify(((ReduceAction)a).ReductionAlternative.NonterminalSymbolName)}"
                                                                : $"{a.ActionName}").ToList();

                        var messageText = $"Грамматика не является LR(1): для токена {GrammarObject.Userify(lookahead)} и состояния{Environment.NewLine}"
                                          + $"\t\t{ToString(itemIdx, lookahead, "\t\t")}{Environment.NewLine}"
                                          + $"\tвозможны действия:{Environment.NewLine}" + "\t\t" + String.Join(Environment.NewLine + "\t\t", actions);

                        /// Для Shift/Reduce конфликта кидаем предупреждение, а не соо об ошибке
                        if (Actions[itemIdx, lookaheadIdx].Count == 2 &&
                            Actions[itemIdx, lookaheadIdx].Any(a => a is ReduceAction) &&
                            Actions[itemIdx, lookaheadIdx].Any(a => a is ShiftAction))
                        {
                            errors.Add(Message.Warning(
                                           messageText,
                                           null,
                                           "LanD"
                                           ));
                        }
                        else
                        {
                            errors.Add(Message.Error(
                                           messageText,
                                           null,
                                           "LanD"
                                           ));
                        }
                    }
                }
            }

            /// Проверяем состояния на наличие нескольких пунктов перед Any
            for (var i = 0; i < Items.Count; ++i)
            {
                if (Items[i].GroupBy(m => new { m.Alternative, m.Position })
                    .Where(g => g.First().Next == Grammar.ANY_TOKEN_NAME).Count() > 1)
                {
                    errors.Add(Message.Error(
                                   $"Any-конфликт: согласно состоянию{Environment.NewLine}" + $"\t\t{ToString(i, null, "\t\t")}{Environment.NewLine}"
                                   + $"\tпрефикс, оканчивающийся на Any может быть разобран несколькими способами",
                                   null,
                                   "LanD"
                                   ));
                }
            }

            return(errors);
        }
Example #7
0
        protected override Node ParsingAlgorithm(string text)
        {
            Node root = null;

            /// Множество индексов токенов, на которых запускалось восстановление
            PositionsWhereRecoveryStarted = new HashSet <int>();
            /// Создаём стек для уровней вложенности пар
            NestingStack = new Stack <int>();
            /// Готовим лексер
            LexingStream = new ComplexTokenStream(GrammarObject, Lexer, text, Log);
            /// Читаем первую лексему из входного потока
            var token = LexingStream.GetNextToken();

            /// Создаём стек
            Stack = new ParsingStack();
            Stack.Push(0);
            NestingStack.Push(0);

            while (true)
            {
                if (token.Name == Grammar.ERROR_TOKEN_NAME)
                {
                    break;
                }

                var currentState = Stack.PeekState();

                if (EnableTracing && token.Name != Grammar.ERROR_TOKEN_NAME && token.Name != Grammar.ANY_TOKEN_NAME)
                {
                    Log.Add(Message.Trace(
                                $"Текущий токен: {this.GetTokenInfoForMessage(token)} | Стек: {Stack.ToString(GrammarObject)}",
                                token.Location.Start
                                ));
                }

                if (Table[currentState, token.Name].Count > 0)
                {
                    if (token.Name == Grammar.ANY_TOKEN_NAME)
                    {
                        token = SkipAny(NodeGenerator.Generate(Grammar.ANY_TOKEN_NAME), true);

                        /// Если при пропуске текста произошла ошибка, прерываем разбор
                        if (token.Name == Grammar.ERROR_TOKEN_NAME)
                        {
                            break;
                        }
                        else
                        {
                            continue;
                        }
                    }

                    var action = GetAction(currentState, token.Name);

                    /// Если нужно произвести перенос
                    if (action is ShiftAction)
                    {
                        var tokenNode = NodeGenerator.Generate(token.Name);
                        tokenNode.SetValue(token.Text);
                        tokenNode.SetLocation(token.Location.Start, token.Location.End);

                        var shift = (ShiftAction)action;
                        /// Вносим в стек новое состояние
                        Stack.Push(tokenNode, shift.TargetItemIndex);
                        NestingStack.Push(LexingStream.GetPairsCount());

                        if (EnableTracing)
                        {
                            Log.Add(Message.Trace(
                                        $"Перенос",
                                        token.Location.Start
                                        ));
                        }

                        token = LexingStream.GetNextToken();
                    }
                    /// Если нужно произвести свёртку
                    else if (action is ReduceAction reduce)
                    {
                        var parentNode = NodeGenerator.Generate(reduce.ReductionAlternative.NonterminalSymbolName);

                        /// Снимаем со стека символы ветки, по которой нужно произвести свёртку
                        for (var i = 0; i < reduce.ReductionAlternative.Count; ++i)
                        {
                            parentNode.AddFirstChild(Stack.PeekSymbol());
                            Stack.Pop();
                            NestingStack.Pop();
                        }
                        currentState = Stack.PeekState();

                        /// Кладём на стек состояние, в которое нужно произвести переход
                        Stack.Push(
                            parentNode,
                            Table.Transitions[currentState][reduce.ReductionAlternative.NonterminalSymbolName]
                            );
                        NestingStack.Push(LexingStream.GetPairsCount());

                        if (EnableTracing)
                        {
                            Log.Add(Message.Trace(
                                        $"Свёртка по правилу {GrammarObject.Userify(reduce.ReductionAlternative)} -> {GrammarObject.Userify(reduce.ReductionAlternative.NonterminalSymbolName)}",
                                        token.Location.Start
                                        ));
                        }

                        continue;
                    }
                    else if (action is AcceptAction)
                    {
                        root = Stack.PeekSymbol();
                        break;
                    }
                }
                else if (token.Name == Grammar.ANY_TOKEN_NAME)
                {
                    Log.Add(Message.Warning(
                                $"Неожиданный символ {this.GetTokenInfoForMessage(LexingStream.CurrentToken)} для состояния{Environment.NewLine}\t\t" + Table.ToString(Stack.PeekState(), null, "\t\t"),
                                LexingStream.CurrentToken.Location.Start
                                ));

                    token = ErrorRecovery();
                }
                else
                {
                    /// Если встретился неожиданный токен, но он в списке пропускаемых
                    if (GrammarObject.Options.IsSet(ParsingOption.SKIP, token.Name))
                    {
                        token = LexingStream.GetNextToken();
                    }
                    else
                    {
                        if (EnableTracing)
                        {
                            Log.Add(Message.Trace(
                                        $"Попытка трактовать текущий токен как начало участка, соответствующего Any",
                                        token.Location.Start
                                        ));
                        }

                        token = Lexer.CreateToken(Grammar.ANY_TOKEN_NAME);
                    }
                }
            }

            if (root != null)
            {
                TreePostProcessing(root);

                if (LexingStream.CustomBlocks?.Count > 0)
                {
                    var visitor = new InsertCustomBlocksVisitor(GrammarObject, LexingStream.CustomBlocks);
                    root.Accept(visitor);
                    root = visitor.Root;

                    foreach (var block in visitor.CustomBlocks)
                    {
                        Log.Add(Message.Error(
                                    $"Блок \"{block.Start.Value[0]}\" прорезает несколько сущностей программы или находится в области, " +
                                    $"не учитываемой при синтаксическом анализе",
                                    block.Start.Location.Start
                                    ));
                    }
                }
            }

            return(root);
        }
Example #8
0
        private IToken SkipAny(Node anyNode, bool enableRecovery)
        {
            var nestingCopy = LexingStream.GetPairsState();
            var token       = LexingStream.CurrentToken;
            var tokenIndex  = LexingStream.CurrentIndex;
            var rawActions  = Table[Stack.PeekState(), Grammar.ANY_TOKEN_NAME];

            if (EnableTracing)
            {
                Log.Add(Message.Trace(
                            $"Инициирован пропуск Any | Стек: {Stack.ToString(GrammarObject)} | Состояние: {Environment.NewLine}\t\t"
                            + Table.ToString(Stack.PeekState(), null, "\t\t"),
                            token.Location.Start
                            ));
            }

            /// Пока по Any нужно производить свёртки (ячейка таблицы непуста и нет конфликтов)
            while (rawActions.Count == 1 && rawActions.First() is ReduceAction)
            {
                var reduce     = (ReduceAction)rawActions.First();
                var parentNode = NodeGenerator.Generate(reduce.ReductionAlternative.NonterminalSymbolName);

                /// Снимаем со стека символы ветки, по которой нужно произвести свёртку
                for (var i = 0; i < reduce.ReductionAlternative.Count; ++i)
                {
                    parentNode.AddFirstChild(Stack.PeekSymbol());
                    Stack.Pop();
                    NestingStack.Pop();
                }

                /// Кладём на стек состояние, в которое нужно произвести переход
                Stack.Push(
                    parentNode,
                    Table.Transitions[Stack.PeekState()][reduce.ReductionAlternative.NonterminalSymbolName]
                    );
                NestingStack.Push(LexingStream.GetPairsCount());

                rawActions = Table[Stack.PeekState(), Grammar.ANY_TOKEN_NAME];
            }

            /// Берём опции из нужного вхождения Any
            var marker = Table.Items[Stack.PeekState()].First(i => i.Next == Grammar.ANY_TOKEN_NAME);

            anyNode.Options = marker.Alternative[marker.Position].Options;

            /// Производим перенос
            var shift = (ShiftAction)rawActions.Where(a => a is ShiftAction).Single();

            /// Вносим в стек новое состояние
            Stack.Push(anyNode, shift.TargetItemIndex);
            NestingStack.Push(LexingStream.GetPairsCount());

            if (EnableTracing)
            {
                Log.Add(Message.Trace(
                            $"Поиск окончания последовательности, соответствующей Any | Стек: {Stack.ToString(GrammarObject)} | Состояние: {Environment.NewLine}\t\t"
                            + Table.ToString(Stack.PeekState(), null, "\t\t"),
                            token.Location.Start
                            ));
            }

            var stopTokens  = GetStopTokens(anyNode.Options, Stack.PeekState());
            var ignorePairs = anyNode.Options.AnyOptions.ContainsKey(AnyOption.IgnorePairs);

            var startLocation = anyNode.Location?.Start
                                ?? token.Location.Start;
            var endLocation = anyNode.Location?.End;
            var anyLevel    = LexingStream.GetPairsCount();

            /// Пропускаем токены, пока не найдём тот, для которого
            /// в текущем состоянии нужно выполнить перенос или свёртку
            while (!stopTokens.Contains(token.Name) &&
                   (ignorePairs || LexingStream.CurrentTokenDirection != Direction.Up) &&
                   !anyNode.Options.Contains(AnyOption.Avoid, token.Name) &&
                   token.Name != Grammar.EOF_TOKEN_NAME &&
                   token.Name != Grammar.ERROR_TOKEN_NAME)
            {
                anyNode.Value.Add(token.Text);
                endLocation = token.Location.End;

                if (ignorePairs)
                {
                    token = LexingStream.GetNextToken();
                }
                else
                {
                    token = LexingStream.GetNextToken(anyLevel, out List <IToken> skippedBuffer);

                    if (skippedBuffer.Count > 0)
                    {
                        anyNode.Value.AddRange(skippedBuffer.Select(t => t.Text));
                        endLocation = skippedBuffer.Last().Location.End;
                    }
                }
            }

            if (endLocation != null)
            {
                anyNode.SetLocation(startLocation, endLocation);
            }

            if (token.Name == Grammar.ERROR_TOKEN_NAME)
            {
                return(token);
            }

            /// Если дошли до конца входной строки, и это было не по плану
            if (!stopTokens.Contains(token.Name))
            {
                if (enableRecovery)
                {
                    var message = Message.Trace(
                        $"Ошибка при пропуске {Grammar.ANY_TOKEN_NAME}: неожиданный токен {GrammarObject.Userify(token.Name)}, ожидался один из токенов {String.Join(", ", stopTokens.Select(t => GrammarObject.Userify(t)))}",
                        token.Location.Start
                        );

                    if (GrammarObject.Options.IsSet(ParsingOption.RECOVERY))
                    {
                        ++Statistics.RecoveryTimesAny;
                        Statistics.LongestRollback =
                            Math.Max(Statistics.LongestRollback, LexingStream.CurrentIndex - tokenIndex);

                        message.Type = MessageType.Warning;
                        Log.Add(message);

                        LexingStream.MoveTo(tokenIndex, nestingCopy);

                        return(ErrorRecovery(stopTokens,
                                             anyNode.Options.Contains(AnyOption.Avoid, token.Name) ? token.Name : null));
                    }
                    else
                    {
                        message.Type = MessageType.Error;
                        Log.Add(message);
                        return(Lexer.CreateToken(Grammar.ERROR_TOKEN_NAME));
                    }
                }
                else
                {
                    Log.Add(Message.Error(
                                $"Ошибка при пропуске {Grammar.ANY_TOKEN_NAME} в процессе восстановления: неожиданный токен {GrammarObject.Userify(token.Name)}, ожидался один из токенов {String.Join(", ", stopTokens.Select(t => GrammarObject.Userify(t)))}",
                                token.Location.Start
                                ));

                    return(Lexer.CreateToken(Grammar.ERROR_TOKEN_NAME));
                }
            }

            return(token);
        }
Example #9
0
        public override IToken GetNextToken()
        {
            switch (CurrentTokenDirection)
            {
            case Direction.Down:
                PairStack.Push(OpenedPair);
                OpenedPair = null;
                break;

            case Direction.Up:
                PairStack.Pop();
                break;
            }

            CurrentTokenDirection = Direction.Forward;

            var token = base.GetNextToken();

            if (CustomBlockDefinition != null &&
                CustomBlockDefinition.BaseToken == token.Name)
            {
                if (CustomBlockDefinition.IsStartLexem(token.Text))
                {
                    var newBlock = new CustomBlockNode
                    {
                        Location = new SegmentLocation {
                            Start = token.Location.Start
                        },
                        Start = new Node(token.Name, new LocalOptions {
                            ExactMatch = true, Priority = LocalOptions.BASE_PRIORITY
                        })
                    };

                    newBlock.Start.SetLocation(token.Location.Start, token.Location.End);
                    newBlock.Start.SetValue(CustomBlockDefinition.GetName(token.Text));

                    CustomBlockStack.Push(newBlock);
                }
                else if (CustomBlockDefinition.IsEndLexem(token.Text))
                {
                    /// Отлавливаем ситуацию, когда количество закрытий блока не совпадает с количеством открытий
                    if (CustomBlockStack.Count == 0)
                    {
                        Log.Add(Message.Error(
                                    $"Неожиданная закрывающая конструкция {this.GetTokenInfoForMessage(token)} для пользовательского блока",
                                    token.Location.Start
                                    ));
                    }
                    else
                    {
                        var currentBlock = CustomBlockStack.Pop();

                        currentBlock.End = new Node(token.Name);
                        currentBlock.End.SetLocation(token.Location.Start, token.Location.End);
                        currentBlock.Location.End = token.Location.End;

                        CustomBlocks.Add(currentBlock);
                    }
                }
            }

            /// Предполагается, что токен может быть началом ровно одной пары, или концом ровно одной пары,
            /// или одновременно началом и концом ровно одной пары
            var closed = GrammarObject.Pairs.FirstOrDefault(p => p.Value.Right.Contains(token.Name));

            /// Если текущий токен закрывает некоторую конструкцию
            if (closed.Value != null)
            {
                /// и при этом не открывает её же
                if (!closed.Value.Left.Contains(token.Name))
                {
                    /// проверяем, есть ли на стеке то, что можно этой конструкцией закрыть
                    if (PairStack.Count == 0)
                    {
                        Log.Add(Message.Error(
                                    $"Отсутствует открывающая конструкция для парной закрывающей {this.GetTokenInfoForMessage(token)}",
                                    token.Location.Start
                                    ));

                        return(Lexer.CreateToken(Grammar.ERROR_TOKEN_NAME));
                    }
                    else if (PairStack.Peek() != closed.Value)
                    {
                        Log.Add(Message.Error(
                                    $"Неожиданная закрывающая конструкция {this.GetTokenInfoForMessage(token)}, ожидается {String.Join("или ", PairStack.Peek().Right.Select(e => GrammarObject.Userify(e)))} для открывающей конструкции {String.Join("или ", PairStack.Peek().Left.Select(e => GrammarObject.Userify(e)))}",
                                    token.Location.Start
                                    ));

                        return(Lexer.CreateToken(Grammar.ERROR_TOKEN_NAME));
                    }
                    else
                    {
                        CurrentTokenDirection = Direction.Up;
                    }
                }
                /// иначе, если текущий токен одновременно открывающий и закрывающий
                else
                {
                    /// и есть что закрыть, закрываем
                    if (PairStack.Count > 0 && PairStack.Peek() == closed.Value)
                    {
                        CurrentTokenDirection = Direction.Up;
                    }
                    else
                    {
                        CurrentTokenDirection = Direction.Down;
                        OpenedPair            = closed.Value;
                    }
                }
            }
            else
            {
                var opened = GrammarObject.Pairs.FirstOrDefault(p => p.Value.Left.Contains(token.Name));

                if (opened.Value != null)
                {
                    CurrentTokenDirection = Direction.Down;
                    OpenedPair            = opened.Value;
                }
            }

            return(token);
        }
Example #10
0
        /// <summary>
        /// LL(1) разбор
        /// </summary>
        /// <returns>
        /// Корень дерева разбора
        /// </returns>
        protected override Node ParsingAlgorithm(string text)
        {
            /// Контроль вложенностей пар
            NestingLevel = new Dictionary <Node, int>();
            PositionsWhereRecoveryStarted = new HashSet <int>();

            /// Готовим лексер и стеки
            LexingStream = new ComplexTokenStream(GrammarObject, Lexer, text, Log);
            Stack        = new Stack <Node>();
            /// Кладём на стек стартовый символ
            var root = NodeGenerator.Generate(GrammarObject.StartSymbol);

            Stack.Push(NodeGenerator.Generate(Grammar.EOF_TOKEN_NAME));
            Stack.Push(root);

            /// Читаем первую лексему из входного потока
            var token = LexingStream.GetNextToken();

            /// Пока не прошли полностью правило для стартового символа
            while (Stack.Count > 0)
            {
                if (token.Name == Grammar.ERROR_TOKEN_NAME)
                {
                    break;
                }

                var stackTop = Stack.Peek();

                if (EnableTracing)
                {
                    Log.Add(Message.Trace(
                                $"Текущий токен: {this.GetTokenInfoForMessage(token)}\t |\t Стек: {StackString}",
                                LexingStream.CurrentToken.Location.Start
                                ));
                }

                /// Если символ на вершине стека совпадает с текущим токеном
                if (stackTop.Symbol == token.Name)
                {
                    if (token.Name == Grammar.ANY_TOKEN_NAME)
                    {
                        token = SkipAny(NodeGenerator.Generate(Grammar.ANY_TOKEN_NAME), true);
                    }
                    else
                    {
                        var node = Stack.Pop();
                        node.SetLocation(token.Location.Start, token.Location.End);
                        node.SetValue(token.Text);

                        token = LexingStream.GetNextToken();
                    }

                    continue;
                }

                /// Если на вершине стека нетерминал, выбираем альтернативу по таблице
                if (GrammarObject[stackTop.Symbol] is NonterminalSymbol)
                {
                    var alternatives = Table[stackTop.Symbol, token.Name];

                    if (alternatives.Count > 0)
                    {
                        if (token.Name == Grammar.ANY_TOKEN_NAME)
                        {
                            /// Поддерживаем свойство immediate error detection для Any
                            var runtimeFirst = Stack.Select(e => e.Symbol).ToList();

                            if (GrammarObject.First(runtimeFirst).Contains(Grammar.ANY_TOKEN_NAME))
                            {
                                token = SkipAny(NodeGenerator.Generate(Grammar.ANY_TOKEN_NAME), true);
                            }
                            else
                            {
                                Log.Add(Message.Warning(
                                            $"Неожиданный символ {this.GetTokenInfoForMessage(LexingStream.CurrentToken)}, ожидался один из следующих символов: {String.Join(", ", runtimeFirst.Select(t => GrammarObject.Userify(t)))}",
                                            token.Location.Start
                                            ));

                                token = ErrorRecovery();
                            }
                        }
                        else
                        {
                            ApplyAlternative(alternatives[0]);
                        }

                        continue;
                    }
                }

                /// Если не смогли ни сопоставить текущий токен с терминалом на вершине стека,
                /// ни найти ветку правила для нетерминала на вершине стека
                if (token.Name == Grammar.ANY_TOKEN_NAME)
                {
                    Log.Add(Message.Warning(
                                GrammarObject.Tokens.ContainsKey(stackTop.Symbol) ?
                                $"Неожиданный символ {this.GetTokenInfoForMessage(LexingStream.CurrentToken)}, ожидался символ {GrammarObject.Userify(stackTop.Symbol)}" :
                                $"Неожиданный символ {this.GetTokenInfoForMessage(LexingStream.CurrentToken)}, ожидался один из следующих символов: {String.Join(", ", Table[stackTop.Symbol].Where(t => t.Value.Count > 0).Select(t => GrammarObject.Userify(t.Key)))}",
                                LexingStream.CurrentToken.Location.Start
                                ));

                    token = ErrorRecovery();
                }
                /// Если непонятно, что делать с текущим токеном, и он конкретный
                /// (не Any), заменяем его на Any
                else
                {
                    /// Если встретился неожиданный токен, но он в списке пропускаемых
                    if (GrammarObject.Options.IsSet(ParsingOption.SKIP, token.Name))
                    {
                        token = LexingStream.GetNextToken();
                    }
                    else
                    {
                        Log.Add(Message.Trace(
                                    $"Попытка трактовать текущий токен как начало участка, соответствующего Any",
                                    token.Location.Start
                                    ));

                        token = Lexer.CreateToken(Grammar.ANY_TOKEN_NAME);
                    }
                }
            }

            TreePostProcessing(root);

            if (LexingStream.CustomBlocks?.Count > 0)
            {
                var visitor = new InsertCustomBlocksVisitor(GrammarObject, LexingStream.CustomBlocks);
                root.Accept(visitor);
                root = visitor.Root;

                foreach (var block in visitor.CustomBlocks)
                {
                    Log.Add(Message.Error(
                                $"Блок \"{block.Start.Value[0]}\" прорезает несколько сущностей программы или находится в области, " +
                                $"не учитываемой при синтаксическом анализе",
                                block.Start.Location.Start
                                ));
                }
            }

            return(root);
        }
Example #11
0
        private IToken ErrorRecovery(HashSet <string> stopTokens = null, string avoidedToken = null)
        {
            if (!GrammarObject.Options.IsSet(ParsingOption.RECOVERY))
            {
                Log.Add(Message.Error(
                            $"Возобновление разбора в случае ошибки отключено",
                            LexingStream.CurrentToken.Location.Start
                            ));

                return(Lexer.CreateToken(Grammar.ERROR_TOKEN_NAME));
            }

            if (!PositionsWhereRecoveryStarted.Add(LexingStream.CurrentIndex))
            {
                Log.Add(Message.Error(
                            $"Возобновление разбора невозможно: восстановление в позиции токена {this.GetTokenInfoForMessage(LexingStream.CurrentToken)} уже проводилось",
                            LexingStream.CurrentToken.Location.Start
                            ));

                return(Lexer.CreateToken(Grammar.ERROR_TOKEN_NAME));
            }

            Log.Add(Message.Warning(
                        $"Процесс восстановления запущен в позиции токена {this.GetTokenInfoForMessage(LexingStream.CurrentToken)}",
                        LexingStream.CurrentToken.Location.Start
                        ));

            var recoveryStartTime = DateTime.Now;

            /// То, что мы хотели разобрать, и не смогли
            var currentNode = Stack.Pop();

            /// Поднимаемся по уже построенной части дерева, пока не встретим
            /// пригодный для восстановления нетерминал.
            do
            {
                if (currentNode.Parent != null)
                {
                    var childIndex = currentNode.Parent.Children.IndexOf(currentNode);

                    for (var i = 0; i < currentNode.Parent.Children.Count - childIndex - 1; ++i)
                    {
                        Stack.Pop();
                    }
                }

                /// Переходим к родителю
                currentNode = currentNode.Parent;
            }
            /// Ищем дальше, если
            while (currentNode != null && (
                       /// текущий символ не входит в список тех, на которых можно восстановиться, или
                       !GrammarObject.Options.IsSet(ParsingOption.RECOVERY, currentNode.Symbol) ||
                       /// при разборе соответствующей сущности уже пошли по Any-ветке
                       ParsedStartsWithAny(currentNode) ||
                       /// ошибка произошла на таком же Any
                       IsUnsafeAny(stopTokens, avoidedToken, currentNode)
                       ));

            if (currentNode != null)
            {
                List <IToken> skippedBuffer;

                if (LexingStream.GetPairsCount() != NestingLevel[currentNode])
                {
                    var currentToken = LexingStream.CurrentToken;
                    /// Пропускаем токены, пока не поднимемся на тот же уровень вложенности,
                    /// на котором раскрывали нетерминал
                    LexingStream.GetNextToken(NestingLevel[currentNode], out skippedBuffer);
                    skippedBuffer.Insert(0, currentToken);
                }
                else
                {
                    skippedBuffer = new List <IToken>();
                }

                var anyNode = NodeGenerator.Generate(Grammar.ANY_TOKEN_NAME);

                anyNode.Value = currentNode.GetValue();
                anyNode.Value.AddRange(skippedBuffer.Select(t => t.Text));

                if (currentNode.Location != null)
                {
                    anyNode.SetLocation(currentNode.Location.Start, currentNode.Location.End);
                }

                currentNode.ResetChildren();
                Stack.Push(currentNode);

                if (skippedBuffer.Count > 0)
                {
                    anyNode.SetLocation(
                        anyNode.Location?.Start ?? skippedBuffer[0].Location.Start,
                        skippedBuffer.Last().Location.End
                        );
                }

                Log.Add(Message.Warning(
                            $"Найдено предполагаемое начало {Grammar.ANY_TOKEN_NAME}",
                            anyNode.Location?.Start ?? LexingStream.CurrentToken.Location.Start
                            ));

                Log.Add(Message.Warning(
                            $"Попытка продолжить разбор на нетерминале {GrammarObject.Userify(currentNode.Symbol)} в позиции токена {this.GetTokenInfoForMessage(LexingStream.CurrentToken)}",
                            LexingStream.CurrentToken.Location.Start
                            ));

                /// Пытаемся пропустить Any в этом месте
                var token = SkipAny(anyNode, false);

                /// Если Any успешно пропустили и возобновили разбор,
                /// возвращаем токен, с которого разбор продолжается
                if (token.Name != Grammar.ERROR_TOKEN_NAME)
                {
                    Log.Add(Message.Warning(
                                $"Произведено восстановление на уровне {GrammarObject.Userify(currentNode.Symbol)}, разбор продолжен с токена {this.GetTokenInfoForMessage(token)}",
                                token.Location.Start
                                ));

                    Statistics.RecoveryTimes     += 1;
                    Statistics.RecoveryTimeSpent += DateTime.Now - recoveryStartTime;

                    return(token);
                }
            }

            Log.Add(Message.Error(
                        $"Не удалось продолжить разбор",
                        null
                        ));

            return(Lexer.CreateToken(Grammar.ERROR_TOKEN_NAME));
        }
Example #12
0
        /// <summary>
        /// Пропуск токенов в позиции, задаваемой символом Any
        /// </summary>
        /// <returns>
        /// Токен, найденный сразу после символа Any
        /// </returns>
        private IToken SkipAny(Node anyNode, bool enableRecovery)
        {
            var nestingCopy = LexingStream.GetPairsState();
            var tokenIndex  = LexingStream.CurrentIndex;
            var token       = LexingStream.CurrentToken;

            var stackTop = Stack.Peek();

            if (EnableTracing)
            {
                Log.Add(Message.Trace(
                            $"Инициирован пропуск Any\t |\t Стек: {StackString}",
                            token.Location.Start
                            ));
            }

            /// Пока по Any нужно раскрывать очередной нетерминал
            while (GrammarObject[stackTop.Symbol] is NonterminalSymbol)
            {
                ApplyAlternative(Table[stackTop.Symbol, Grammar.ANY_TOKEN_NAME][0]);
                stackTop = Stack.Peek();
            }

            /// В итоге первым терминалом, который окажется на стеке, должен быть Any
            /// Подменяем свежесгенерированный узел для Any на переданный извне
            anyNode.Options = stackTop.Options.Clone();
            var anyIndex = stackTop.Parent.Children.IndexOf(stackTop);

            stackTop.Parent.Children.RemoveAt(anyIndex);
            stackTop.Parent.InsertChild(anyNode, anyIndex);
            Stack.Pop();

            if (EnableTracing)
            {
                Log.Add(Message.Trace(
                            $"Поиск окончания последовательности, соответствующей Any\t |\t Стек: {StackString}",
                            token.Location.Start
                            ));
            }

            var stopTokens  = GetStopTokens(anyNode.Options, Stack.Select(n => n.Symbol));
            var ignorePairs = anyNode.Options.AnyOptions.ContainsKey(AnyOption.IgnorePairs);

            /// Если Any непустой (текущий токен - это не токен,
            /// который может идти после Any)
            if (!stopTokens.Contains(token.Name))
            {
                /// Проверка на случай, если допропускаем текст в процессе восстановления
                if (anyNode.Location == null)
                {
                    anyNode.SetLocation(token.Location.Start, token.Location.End);
                }

                /// Смещение для участка, подобранного как текст
                var endLocation = token.Location.End;
                var anyLevel    = LexingStream.GetPairsCount();

                while (!stopTokens.Contains(token.Name) &&
                       (ignorePairs || LexingStream.CurrentTokenDirection != Direction.Up) &&
                       !anyNode.Options.Contains(AnyOption.Avoid, token.Name) &&
                       token.Name != Grammar.EOF_TOKEN_NAME &&
                       token.Name != Grammar.ERROR_TOKEN_NAME)
                {
                    anyNode.Value.Add(token.Text);
                    endLocation = token.Location.End;

                    if (ignorePairs)
                    {
                        token = LexingStream.GetNextToken();
                    }
                    else
                    {
                        token = LexingStream.GetNextToken(anyLevel, out List <IToken> skippedBuffer);

                        /// Если при пропуске до токена на том же уровне
                        /// пропустили токены с более глубокой вложенностью
                        if (skippedBuffer.Count > 0)
                        {
                            anyNode.Value.AddRange(skippedBuffer.Select(t => t.Text));
                            endLocation = skippedBuffer.Last().Location.End;
                        }
                    }
                }

                anyNode.SetLocation(anyNode.Location.Start, endLocation);

                if (token.Name == Grammar.ERROR_TOKEN_NAME)
                {
                    return(token);
                }

                if (!stopTokens.Contains(token.Name))
                {
                    var message = Message.Trace(
                        $"Ошибка при пропуске {Grammar.ANY_TOKEN_NAME}: неожиданный токен {GrammarObject.Userify(token.Name)}, ожидался один из следующих символов: { String.Join(", ", stopTokens.Select(t => GrammarObject.Userify(t))) }",
                        token.Location.Start
                        );

                    message.Type = enableRecovery ? MessageType.Warning : MessageType.Error;
                    Log.Add(message);

                    if (enableRecovery)
                    {
                        ++Statistics.RecoveryTimesAny;
                        Statistics.LongestRollback =
                            Math.Max(Statistics.LongestRollback, LexingStream.CurrentIndex - tokenIndex);

                        LexingStream.MoveTo(tokenIndex, nestingCopy);
                        anyNode.Reset();
                        ///	Возвращаем узел обратно на стек
                        Stack.Push(anyNode);

                        return(ErrorRecovery(stopTokens,
                                             anyNode.Options.Contains(AnyOption.Avoid, token.Name) ? token.Name : null));
                    }
                    else
                    {
                        return(Lexer.CreateToken(Grammar.ERROR_TOKEN_NAME));
                    }
                }
            }

            return(token);
        }