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)) )); }
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); } }
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); }
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)); }
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); }
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)); }
/// <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); }