Пример #1
0
        /// <summary>
        /// Инициализирует новый экземпляр класса <see cref="CommandTree"/> с указанным начальным узлом.
        /// </summary>
        internal CommandTree(CommandTreeNode startNode)
        {
            StartNode = startNode;

            _globalCommands  = new Dictionary <CommandTreeNode, RunMetadata>();
            _contextCommands = new MultiValueDictionary <CommandTreeNode, RunMetadata>(new RunMetadataByGroupComparer());
        }
Пример #2
0
 /// <summary>
 /// Связывает указанный узел с метаданными запуска команды.
 /// </summary>
 /// <param name="endNode">Узел, на котором команда завершается.</param>
 /// <param name="metadata"> Метаданные запуска.</param>
 internal void AddMetadata(CommandTreeNode endNode, RunMetadata metadata)
 {
     if (metadata.IsGlobal)
     {
         _globalCommands.Add(endNode, metadata);
     }
     else
     {
         _contextCommands.Add(endNode, metadata);
     }
 }
Пример #3
0
        /// <summary>
        /// Создаёт последовательность узлов в дереве c помощью указанного лексера шаблонов команд.
        /// </summary>
        /// <param name="lexer">Лексер.</param>
        /// <param name="startNode">Стартовый узел дерева.</param>
        /// <returns>Последний узел созданной последовательности типа <see cref="TokenType.EndOfText"/>.</returns>
        internal static CommandTreeNode CreateTreeNodes(IPatternLexer lexer, CommandTreeNode startNode)
        {
            Debug.Assert(lexer != null);
            Debug.Assert(startNode != null);

            var curPointer           = startNode;
            var unprocessedOpenParen = 0;
            var stackAfterOpenParen  = new Stack <CommandTreeNode>();

            while (curPointer.TokenPrototype.Type != TokenType.EndOfText)
            {
                var token = lexer.NextPatternToken();
                if (token.Type == TokenType.OpenParen)
                {
                    unprocessedOpenParen++;
                }
                else if (token.Type == TokenType.CloseParen)
                {
                    if (stackAfterOpenParen.Count == 0)
                    {
                        throw new WrongPatternException(token,
                                                        "Количество закрывающих скобок превышает количество открывающих.");
                    }

                    if (unprocessedOpenParen > 0)
                    {
                        throw new WrongPatternException(token,
                                                        "Группа не может быть пустой (внутри скобок должны быть значимые токены).");
                    }

                    var startAfterNode = stackAfterOpenParen.Pop();
                    var endNode        = curPointer;
                    endNode.IsGroupEnd = true;

                    var actionPeek = lexer.NextPatternToken();

                    if (actionPeek.Type == TokenType.Plus)
                    {
                        // создаём цикличную ссылку, тем самым, группа может повторяться.

                        if (endNode.NextNodes.ContainsKey(startAfterNode.TokenPrototype))
                        {
                            throw new WrongPatternException(actionPeek, "Группа не может содержать только одну подгруппу.");
                        }

                        var backReference = new CommandTreeNode.Reference(startAfterNode, true);
                        endNode.NextNodes.Add(startAfterNode.TokenPrototype, backReference);
                    }
                    else
                    {
                        throw new WrongPatternException(actionPeek,
                                                        $"После закрывающей скобки может быть только квантификатор '+' - 1 или более раз. Но был встречен токен: {actionPeek}.");
                    }
                }
                else
                {
                    if (curPointer.NextNodes.TryGetValue(token, out CommandTreeNode.Reference reference))
                    {
                        curPointer = reference.Node;
                    }
                    else
                    {
                        var newNode = new CommandTreeNode(token)
                        {
                            InGroupStartCount = unprocessedOpenParen
                        };

                        var newReference = new CommandTreeNode.Reference(newNode, false);

                        while (unprocessedOpenParen > 0)
                        {
                            stackAfterOpenParen.Push(newNode);
                            unprocessedOpenParen--;
                        }

                        curPointer.NextNodes.Add(token, newReference);
                        curPointer = newNode;
                    }
                }
            }

            if (stackAfterOpenParen.Count != 0)
            {
                throw new WrongPatternException(stackAfterOpenParen.Pop().TokenPrototype,
                                                "Количество открывающих скобок превышает количество закрывающих.");
            }

            Debug.Assert(curPointer.NextNodes.Count == 0);
            return(curPointer);
        }
Пример #4
0
        /// <summary>
        /// Совершает обход префиксного дерева токенов с помощью указанного лексера.
        /// </summary>
        /// <param name="lexer">Лексер исходного текста.</param>
        /// <param name="start">Стартовый узел дерева.</param>
        /// <returns>Последний узел и контейнер значений. Если обход закончен неудачно, контейнер значений будет являться <see langword="null"/>.</returns>
        internal static (CommandTreeNode endNode, ValueContainer container) Traverse(ISourceLexer lexer, CommandTreeNode start)
        {
            var currentNode = start;
            var stack       = new Stack <int>();
            var values      = new List <object>();

            var isSequenceRepeat = false;

            while (true)
            {
                var token = lexer.NextSourceToken();
                if (!currentNode.NextNodes.TryGetValue(token, out var reference))
                {
                    break;
                }

                currentNode = reference.Node;

                if (token.Type == TokenType.EndOfText)
                {
                    return(currentNode, new ValueContainer(values.ToArray()));
                }

                if (reference.IsBack)
                {
                    isSequenceRepeat = true;
                }

                var startCount = currentNode.InGroupStartCount;
                if (isSequenceRepeat)
                {
                    if (startCount > 0)
                    {
                        stack.Push(values.Count);
                    }
                }
                else
                {
                    while (startCount > 0)
                    {
                        stack.Push(values.Count);
                        startCount--;
                    }
                }

                if (TokenFacts.IsPlaceholder(currentNode.TokenPrototype.Type))
                {
                    values.Add(token);
                }

                if (currentNode.IsGroupEnd)
                {
                    Debug.Assert(stack.Count > 0);

                    var index             = stack.Pop();
                    var newContainerToken = ReduceToIterationContainer(values, index);

                    if (isSequenceRepeat)
                    {
                        var groupContainerToken = values[^ 1] as TokenWithValue <GroupContainer>;
Пример #5
0
 /// <summary>
 /// Получает метаданные запуска по указанному узлу контекстного типа.
 /// </summary>
 internal RunMetadata GetContextMetadata(CommandTreeNode endNode, RunMetadata executedCommand)
 {
     return(_contextCommands.TryGetActualValue(endNode, executedCommand, out var context)
         ? context
         : null);
 }
Пример #6
0
 /// <summary>
 /// Получает метаданные запуска по указанному узлу глобального типа.
 /// </summary>
 internal RunMetadata GetGlobalMetadata(CommandTreeNode endNode)
 {
     return(_globalCommands.GetValueOrDefault(endNode, null));
 }
Пример #7
0
        /// <summary>
        /// Инициализирует новый экземпляр класса <see cref="CommandTreeBuilder"/>.
        /// </summary>
        public CommandTreeBuilder()
        {
            var startNode = new CommandTreeNode(new Token(TokenType.Unknown));

            _tree = new CommandTree(startNode);
        }
Пример #8
0
 internal Reference(CommandTreeNode node, bool isBack)
 {
     Node   = node;
     IsBack = isBack;
 }