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