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