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