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