예제 #1
0
 private bool IsUnsafeAny(HashSet <string> oldStopTokens, string avoidedToken, Node currentNode)
 {
     return(oldStopTokens != null && LexingStream.GetPairsCount() == NestingLevel[currentNode] && (
                RecoveryCache[currentNode.Symbol].Item1.Contains(AnyOption.Avoid, LexingStream.CurrentToken.Name) ||
                GetStopTokens(RecoveryCache[currentNode.Symbol].Item1,
                              RecoveryCache[currentNode.Symbol].Item2.Concat(Stack.Select(n => n.Symbol))).Except(oldStopTokens).Count() == 0 &&
                (avoidedToken == null || RecoveryCache[currentNode.Symbol].Item1.Contains(AnyOption.Avoid, avoidedToken))
                ));
 }
예제 #2
0
        private void ApplyAlternative(Alternative alternativeToApply)
        {
            var stackTop = Stack.Pop();

            if (!String.IsNullOrEmpty(alternativeToApply.Alias))
            {
                stackTop.Alias = alternativeToApply.Alias;
            }

            NestingLevel[stackTop] = LexingStream.GetPairsCount();

            for (var i = alternativeToApply.Count - 1; i >= 0; --i)
            {
                var newNode = NodeGenerator.Generate(alternativeToApply[i].Symbol, alternativeToApply[i].Options.Clone());

                stackTop.AddFirstChild(newNode);
                Stack.Push(newNode);
            }
        }
예제 #3
0
        private bool IsUnsafeAny(HashSet <string> oldStopTokens, string avoidedToken)
        {
            if (oldStopTokens != null && LexingStream.GetPairsCount() == NestingStack.Peek())
            {
                var anyOptions = Table.Items[Stack.PeekState()]
                                 .Where(i => i.Position == 0 && i.Next == Grammar.ANY_TOKEN_NAME)
                                 .Select(i => i.Alternative[0].Options)
                                 .FirstOrDefault();

                var nextState = Table[Stack.PeekState(), Grammar.ANY_TOKEN_NAME]
                                .OfType <ShiftAction>().FirstOrDefault()
                                .TargetItemIndex;

                return(anyOptions.Contains(AnyOption.Avoid, LexingStream.CurrentToken.Name) ||
                       GetStopTokens(anyOptions, nextState).Except(oldStopTokens).Count() == 0 &&
                       (avoidedToken == null || anyOptions.Contains(AnyOption.Avoid, avoidedToken)));
            }

            return(false);
        }
예제 #4
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;

            PointLocation startLocation = null;
            PointLocation endLocation   = null;
            var           value         = new List <string>();

            var previouslyMatched      = (Node)null;
            var derivationProds        = new HashSet <PathFragment>();
            var initialDerivationProds = new HashSet <PathFragment>();

            /// Снимаем со стека состояния до тех пор, пока не находим состояние,
            /// в котором есть пункт A -> * Any ...
            do
            {
                if (Stack.CountSymbols > 0)
                {
                    if (Stack.PeekSymbol().Location != null)
                    {
                        startLocation = Stack.PeekSymbol().Location.Start;
                        if (endLocation == null)
                        {
                            endLocation = Stack.PeekSymbol().Location.End;
                        }
                    }

                    value = Stack.PeekSymbol().GetValue()
                            .Concat(value).ToList();

                    /// Запоминаем снятый со стека символ - это то, что было успешно распознано
                    previouslyMatched = Stack.PeekSymbol();
                }

                Stack.Pop();
                NestingStack.Pop();

                if (Stack.CountStates > 0)
                {
                    /// Выбираем пункты, продукции которых потенциально могут участвовать
                    /// в выводе текущего префикса из стартового символа
                    initialDerivationProds = new HashSet <PathFragment>(
                        Table.Items[Stack.PeekState()]
                        .Where
                            (i =>
                            /// Точка должна стоять перед символом, только что снятым со стека
                            i.Next == previouslyMatched.Symbol &&
                            /// Если это не первая выборка, на предыдущем шаге в выборке должен был быть пункт
                            /// с той же альтернативой, но точкой на один символ дальше
                            (derivationProds.Count == 0 || derivationProds.Any(p => p.Alt.Equals(i.Alternative) && p.Pos == i.Position + 1))
                            )
                        .Select(i => new PathFragment {
                        Alt = i.Alternative, Pos = i.Position
                    })
                        );

                    derivationProds = new HashSet <PathFragment>(initialDerivationProds);

                    var oldCount = 0;

                    while (oldCount != derivationProds.Count)
                    {
                        oldCount = derivationProds.Count;

                        /// Добавляем к списку пункты, порождающие уже добавленные пункты
                        derivationProds.UnionWith(Table.Items[Stack.PeekState()]
                                                  .Where(i => derivationProds.Any(p => p.Pos == 0 && p.Alt.NonterminalSymbolName == i.Next))
                                                  .Select(i => new PathFragment {
                            Alt = i.Alternative, Pos = i.Position
                        })
                                                  );
                    }
                }
            }while (Stack.CountStates > 0 && (derivationProds.Count == initialDerivationProds.Count ||
                                              derivationProds.Except(initialDerivationProds).All(p => !GrammarObject.Options.IsSet(ParsingOption.RECOVERY, p.Alt[p.Pos])) ||
                                              StartsWithAny(previouslyMatched) ||
                                              IsUnsafeAny(stopTokens, avoidedToken))
                    );

            if (Stack.CountStates > 0)
            {
                if (LexingStream.GetPairsCount() != NestingStack.Peek())
                {
                    var skippedBuffer = new List <IToken>();

                    /// Запоминаем токен, на котором произошла ошибка
                    var currentToken = LexingStream.CurrentToken;
                    /// Пропускаем токены, пока не поднимемся на тот же уровень вложенности,
                    /// на котором раскрывали нетерминал
                    LexingStream.GetNextToken(NestingStack.Peek(), out skippedBuffer);
                    skippedBuffer.Insert(0, currentToken);

                    value.AddRange(skippedBuffer.Select(t => t.Text));
                    endLocation = skippedBuffer.Last().Location.End;
                }

                /// Пытаемся пропустить Any в этом месте,
                /// Any захватывает участок с начала последнего
                /// снятого со стека символа до места восстановления
                var anyNode = NodeGenerator.Generate(Grammar.ANY_TOKEN_NAME);
                if (startLocation != null)
                {
                    anyNode.SetLocation(startLocation, endLocation);
                }
                anyNode.Value = value.ToList();

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

                Log.Add(Message.Warning(
                            $"Попытка продолжить разбор в состоянии {Environment.NewLine}\t\t{Table.ToString(Stack.PeekState(), null, "\t\t")}\tв позиции токена {this.GetTokenInfoForMessage(LexingStream.CurrentToken)}",
                            LexingStream.CurrentToken.Location.Start
                            ));

                var token = SkipAny(anyNode, false);

                /// Если Any успешно пропустили и возобновили разбор,
                /// возвращаем токен, с которого разбор продолжается

                if (token.Name != Grammar.ERROR_TOKEN_NAME)
                {
                    Statistics.RecoveryTimes     += 1;
                    Statistics.RecoveryTimeSpent += DateTime.Now - recoveryStartTime;

                    return(token);
                }
            }

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

            return(Lexer.CreateToken(Grammar.ERROR_TOKEN_NAME));
        }
예제 #5
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);
        }
예제 #6
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));
        }
예제 #7
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);
        }