Beispiel #1
0
        /// <summary>
        ///     Parses the Tokens into a Document.
        /// </summary>
        /// <param name="tokenizerResult">The result of an Tokenizer.Tokenize call.</param>
        /// <param name="options">The ParserOptions</param>
        /// <returns></returns>
        public static IDocumentItem Parse(TokenizerResult tokenizerResult, ParserOptions options)
        {
            var buildStack = new Stack <DocumentScope>();
            //this is the scope id that determines a scope that is using let or alias variables
            int variableScope = 1;
            var getScope      = new Func <int>(() => variableScope++);

            //instead of recursive calling the parse function we stack the current document
            buildStack.Push(new DocumentScope(new MorestachioDocument(), () => 0));

            DocumentScope GetVariabeScope()
            {
                return(buildStack.FirstOrDefault(e => e.VariableScopeNumber != -1));
            }

            foreach (var currentToken in tokenizerResult.Tokens)
            {
                var currentDocumentItem = buildStack.Peek();                 //get the latest document

                if (currentToken.Type.Equals(TokenType.Content))
                {
                    currentDocumentItem.Document.Add(new ContentDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                }
                else if (currentToken.Type.Equals(TokenType.If))
                {
                    var nestedDocument = new IfExpressionScopeDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.IfNot))
                {
                    var nestedDocument = new IfNotExpressionScopeDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.Else))
                {
                    var nestedDocument = new ElseExpressionScopeDocumentItem()
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.CollectionOpen))
                {
                    var nestedDocument = new EachDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.WhileLoopOpen))
                {
                    var nestedDocument = new WhileLoopDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.DoLoopOpen))
                {
                    var nestedDocument = new DoLoopDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.ElementOpen))
                {
                    var nestedDocument = new ExpressionScopeDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.RepeatLoopOpen))
                {
                    var nestedDocument = new RepeatDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.InvertedElementOpen))
                {
                    var invertedScope = new InvertedExpressionScopeDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(invertedScope, getScope));
                    currentDocumentItem.Document.Add(invertedScope);
                }
                else if (currentToken.Type.Equals(TokenType.CollectionClose) ||
                         currentToken.Type.Equals(TokenType.ElementClose) ||
                         currentToken.Type.Equals(TokenType.IfClose) ||
                         currentToken.Type.Equals(TokenType.ElseClose) ||
                         currentToken.Type.Equals(TokenType.WhileLoopClose) ||
                         currentToken.Type.Equals(TokenType.DoLoopClose) ||
                         currentToken.Type.Equals(TokenType.RepeatLoopClose))
                {
                    DocumentScope scope = buildStack.Peek();
                    if (scope.HasAlias)                     //are we in a alias then remove it
                    {
                        foreach (var scopeLocalVariable in scope.LocalVariables)
                        {
                            currentDocumentItem.Document.Add(new RemoveAliasDocumentItem(scopeLocalVariable, scope.VariableScopeNumber));
                        }
                    }
                    // remove the last document from the stack and go back to the parents
                    buildStack.Pop();
                }
                else if (currentToken.Type.Equals(TokenType.EscapedSingleValue) ||
                         currentToken.Type.Equals(TokenType.UnescapedSingleValue))
                {
                    currentDocumentItem.Document.Add(new PathDocumentItem(currentToken.MorestachioExpression,
                                                                          currentToken.Type.Equals(TokenType.EscapedSingleValue))
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                }
                else if (currentToken.Type.Equals(TokenType.PartialDeclarationOpen))
                {
                    // currently same named partials will override each other
                    // to allow recursive calls of partials we first have to declare the partial and then load it as we would parse
                    // -the partial as a whole and then add it to the list would lead to unknown calls of partials inside the partial
                    var nestedDocument = new MorestachioDocument();
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    currentDocumentItem.Document.Add(new PartialDocumentItem(currentToken.Value, nestedDocument)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                }
                else if (currentToken.Type.Equals(TokenType.PartialDeclarationClose))
                {
                    buildStack.Pop();
                }
                else if (currentToken.Type.Equals(TokenType.RenderPartial))
                {
                    currentDocumentItem.Document.Add(new RenderPartialDocumentItem(currentToken.Value, currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation,
                    });
                }
                else if (currentToken.Type.Equals(TokenType.Alias))
                {
                    var scope             = GetVariabeScope();
                    var aliasDocumentItem = new AliasDocumentItem(currentToken.Value, scope.VariableScopeNumber)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    currentDocumentItem.Document.Add(aliasDocumentItem);
                    currentDocumentItem.LocalVariables.Add(currentToken.Value);
                }
                else if (currentToken.Type.Equals(TokenType.VariableVar))
                {
                    var evaluateVariableDocumentItem = new EvaluateVariableDocumentItem(currentToken.Value,
                                                                                        currentToken.MorestachioExpression);
                    currentDocumentItem.Document.Add(evaluateVariableDocumentItem);
                }
                else if (currentToken.Type.Equals(TokenType.WriteLineBreak))
                {
                    currentDocumentItem.Document.Add(new TextEditDocumentItem(new AppendLineBreakTextOperation()));
                }
                else if (currentToken.Type.Equals(TokenType.TrimLineBreak))
                {
                    currentDocumentItem.Document.Add(new TextEditDocumentItem(new TrimLineBreakTextOperation()
                    {
                        LineBreaks = 1
                    }));
                }
                else if (currentToken.Type.Equals(TokenType.TrimLineBreaks))
                {
                    currentDocumentItem.Document.Add(new TextEditDocumentItem(new TrimLineBreakTextOperation()
                    {
                        LineBreaks = -1
                    }));
                }
                else if (currentToken.Type.Equals(TokenType.TrimEverything))
                {
                    currentDocumentItem.Document.Add(new TextEditDocumentItem(new TrimAllWhitespacesTextOperation()));
                }
                else if (currentToken.Type.Equals(TokenType.VariableLet))
                {
                    var scope = 0;
                    if (buildStack.Count > 1)
                    {
                        scope = GetVariabeScope()
                                .VariableScopeNumber;
                    }
                    var evaluateVariableDocumentItem = new EvaluateVariableDocumentItem(currentToken.Value,
                                                                                        currentToken.MorestachioExpression, scope);
                    currentDocumentItem.Document.Add(evaluateVariableDocumentItem);
                    if (buildStack.Count > 1)
                    {
                        currentDocumentItem.LocalVariables.Add(currentToken.Value);
                    }
                }
                else if (currentToken.Type.Equals(TokenType.Comment) || currentToken.Type.Equals(TokenType.BlockComment))
                {
                    //just ignore this part and print nothing
                }
                else
                {
                    var customDocumentItemProvider =
                        options.CustomDocumentItemProviders.FirstOrDefault(e => e.ShouldParse(currentToken, options));
                    var documentItem = customDocumentItemProvider?.Parse(currentToken, options, buildStack, getScope);
                    if (documentItem != null)
                    {
                        currentDocumentItem.Document.Add(documentItem);
                    }
                }
            }

            if (buildStack.Count != 1)
            {
                //var invalidScopedElements = buildStack
                //throw new MorestachioSyntaxError(new Tokenizer.CharacterLocation(){Character = }, );
                throw new InvalidOperationException(
                          "There is an Error with the Parser. The Parser still contains unscoped builds: " +
                          buildStack.Select(e => e.Document.GetType().Name).Aggregate((e, f) => e + ", " + f));
            }

            return(buildStack.Pop().Document);
        }
Beispiel #2
0
        /// <summary>
        ///     Parses the Tokens into a Document.
        /// </summary>
        /// <param name="tokens">The tokens.</param>
        /// <returns></returns>
        internal static IDocumentItem Parse(Queue <TokenPair> tokens, ParserOptions options)
        {
            var buildStack = new Stack <DocumentScope>();

            //instead of recursive calling the parse function we stack the current document
            buildStack.Push(new DocumentScope(new MorestachioDocument()));

            while (tokens.Any())
            {
                var currentToken        = tokens.Dequeue();
                var currentDocumentItem = buildStack.Peek();                 //get the latest document

                if (currentToken.Type.Equals(TokenType.Comment))
                {
                    //just ignore this part and print nothing
                }
                else if (currentToken.Type.Equals(TokenType.Content))
                {
                    currentDocumentItem.Document.Add(new ContentDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                }
                else if (currentToken.Type.Equals(TokenType.If))
                {
                    var nestedDocument = new IfExpressionScopeDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.IfNot))
                {
                    var nestedDocument = new IfNotExpressionScopeDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.Else))
                {
                    var nestedDocument = new ElseExpressionScopeDocumentItem()
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.CollectionOpen))
                {
                    var nestedDocument = new EachDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.WhileLoopOpen))
                {
                    var nestedDocument = new WhileLoopDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.DoLoopOpen))
                {
                    var nestedDocument = new DoLoopDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.ElementOpen))
                {
                    var nestedDocument = new ExpressionScopeDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.InvertedElementOpen))
                {
                    var invertedScope = new InvertedExpressionScopeDocumentItem(currentToken.MorestachioExpression)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(invertedScope));
                    currentDocumentItem.Document.Add(invertedScope);
                }
                else if (currentToken.Type.Equals(TokenType.CollectionClose) ||
                         currentToken.Type.Equals(TokenType.ElementClose) ||
                         currentToken.Type.Equals(TokenType.IfClose) ||
                         currentToken.Type.Equals(TokenType.ElseClose) ||
                         currentToken.Type.Equals(TokenType.WhileLoopClose) ||
                         currentToken.Type.Equals(TokenType.DoLoopClose))
                {
                    if (buildStack.Peek().HasAlias)                     //are we in a alias then remove it
                    {
                        currentDocumentItem.Document.Add(new RemoveAliasDocumentItem(buildStack.Peek().AliasName));
                        buildStack.Pop();
                    }
                    // remove the last document from the stack and go back to the parents
                    buildStack.Pop();
                }
                else if (currentToken.Type.Equals(TokenType.EscapedSingleValue) ||
                         currentToken.Type.Equals(TokenType.UnescapedSingleValue))
                {
                    currentDocumentItem.Document.Add(new PathDocumentItem(currentToken.MorestachioExpression,
                                                                          currentToken.Type.Equals(TokenType.EscapedSingleValue))
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                }
                else if (currentToken.Type.Equals(TokenType.PartialDeclarationOpen))
                {
                    // currently same named partials will override each other
                    // to allow recursive calls of partials we first have to declare the partial and then load it as we would parse
                    // -the partial as a whole and then add it to the list would lead to unknown calls of partials inside the partial
                    var nestedDocument = new MorestachioDocument();
                    buildStack.Push(new DocumentScope(nestedDocument));
                    currentDocumentItem.Document.Add(new PartialDocumentItem(currentToken.Value, nestedDocument)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                }
                else if (currentToken.Type.Equals(TokenType.PartialDeclarationClose))
                {
                    currentDocumentItem.Document.Add(new RenderPartialDoneDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                    buildStack.Pop();
                }
                else if (currentToken.Type.Equals(TokenType.RenderPartial))
                {
                    currentDocumentItem.Document.Add(new RenderPartialDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                }
                else if (currentToken.Type.Equals(TokenType.Alias))
                {
                    var aliasDocumentItem = new AliasDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    currentDocumentItem.Document.Add(aliasDocumentItem);
                    buildStack.Push(new DocumentScope(aliasDocumentItem, currentToken.Value));
                }
                else if (currentToken.Type.Equals(TokenType.VariableDeclaration))
                {
                    var evaluateVariableDocumentItem = new EvaluateVariableDocumentItem(currentToken.Value, currentToken.MorestachioExpression);
                    currentDocumentItem.Document.Add(evaluateVariableDocumentItem);
                }
                else
                {
                    var customDocumentItemProvider =
                        options.CustomDocumentItemProviders.FirstOrDefault(e => e.ShouldParse(currentToken, options));
                    if (customDocumentItemProvider != null)
                    {
                        var documentItem = customDocumentItemProvider.Parse(currentToken, options, buildStack);
                        currentDocumentItem.Document.Add(documentItem);
                    }
                }
            }

            if (buildStack.Count != 1)
            {
                //var invalidScopedElements = buildStack
                //throw new MorestachioSyntaxError(new Tokenizer.CharacterLocation(){Character = }, );
                throw new InvalidOperationException(
                          "There is an Error with the Parser. The Parser still contains unscoped builds: " +
                          buildStack.Select(e => e.Document.Kind).Aggregate((e, f) => e + ", " + f));
            }

            return(buildStack.Pop().Document);
        }
Beispiel #3
0
        /// <summary>
        ///     Parses the Tokens into a Document.
        /// </summary>
        /// <param name="tokens">The tokens.</param>
        /// <returns></returns>
        internal static IDocumentItem Parse(Queue <TokenPair> tokens)
        {
            var buildStack = new Stack <DocumentScope>();

            //instead of recursive calling the parse function we stack the current document
            buildStack.Push(new DocumentScope(new MorestachioDocument()));

            while (tokens.Any())
            {
                var currentToken        = tokens.Dequeue();
                var currentDocumentItem = buildStack.Peek();                 //get the latest document

                if (currentToken.Type == TokenType.Comment)
                {
                    //just ignore this part and print nothing
                }
                else if (currentToken.Type == TokenType.Content)
                {
                    currentDocumentItem.Document.Add(new ContentDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                }
                else if (currentToken.Type == TokenType.If)
                {
                    var nestedDocument = new IfExpressionScopeDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type == TokenType.IfNot)
                {
                    var nestedDocument = new IfNotExpressionScopeDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type == TokenType.Else)
                {
                    var nestedDocument = new ElseExpressionScopeDocumentItem()
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type == TokenType.CollectionOpen)
                {
                    var nestedDocument = new CollectionDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type == TokenType.ElementOpen)
                {
                    var nestedDocument = new ExpressionScopeDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(nestedDocument));
                    currentDocumentItem.Document.Add(nestedDocument);
                }
                else if (currentToken.Type == TokenType.InvertedElementOpen)
                {
                    var invertedScope = new InvertedExpressionScopeDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(invertedScope));
                    currentDocumentItem.Document.Add(invertedScope);
                }
                else if (currentToken.Type == TokenType.CollectionClose ||
                         currentToken.Type == TokenType.ElementClose ||
                         currentToken.Type == TokenType.IfClose ||
                         currentToken.Type == TokenType.ElseClose)
                {
                    if (buildStack.Peek().HasAlias)                     //are we in a alias then remove it
                    {
                        currentDocumentItem.Document.Add(new RemoveAliasDocumentItem(buildStack.Peek().AliasName));
                        buildStack.Pop();
                    }
                    // remove the last document from the stack and go back to the parents
                    buildStack.Pop();

                    if (buildStack.Peek().IsFormattingScope
                        )                 //is the remaining scope a formatting one. If it is pop it and return to its parent
                    {
                        buildStack.Pop();
                    }
                }
                else if (currentToken.Type == TokenType.Print)
                {
                    currentDocumentItem.Document.Add(new PrintContextValue());
                    buildStack.Pop();                     //Print formatted can only be followed by a Format and if not the parser should have not emited it
                }
                else if (currentToken.Type == TokenType.Format)
                {
                    if (buildStack.Peek().IsFormattingScope)
                    {
                        buildStack.Pop();
                    }

                    var formatterItem = new IsolatedContextDocumentItem
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new DocumentScope(formatterItem, true));
                    formatterItem.Add(new CallFormatterDocumentItem(ParseArgumentHeader(currentToken),
                                                                    currentToken.Value,
                                                                    string.IsNullOrWhiteSpace(currentToken.Format?.FormatterName) ? null : currentToken.Format.FormatterName));

                    currentDocumentItem.Document.Add(formatterItem);
                }
                else if (currentToken.Type == TokenType.EscapedSingleValue ||
                         currentToken.Type == TokenType.UnescapedSingleValue)
                {
                    currentDocumentItem.Document.Add(new PathDocumentItem(currentToken.Value,
                                                                          currentToken.Type == TokenType.EscapedSingleValue)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                }
                else if (currentToken.Type == TokenType.PartialDeclarationOpen)
                {
                    // currently same named partials will override each other
                    // to allow recursive calls of partials we first have to declare the partial and then load it as we would parse
                    // -the partial as a whole and then add it to the list would lead to unknown calls of partials inside the partial
                    var nestedDocument = new MorestachioDocument();
                    buildStack.Push(new DocumentScope(nestedDocument));
                    currentDocumentItem.Document.Add(new PartialDocumentItem(currentToken.Value, nestedDocument)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                }
                else if (currentToken.Type == TokenType.PartialDeclarationClose)
                {
                    currentDocumentItem.Document.Add(new RenderPartialDoneDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                    buildStack.Pop();
                }
                else if (currentToken.Type == TokenType.RenderPartial)
                {
                    currentDocumentItem.Document.Add(new RenderPartialDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                }
                else if (currentToken.Type == TokenType.Alias)
                {
                    var aliasDocumentItem = new AliasDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    currentDocumentItem.Document.Add(aliasDocumentItem);
                    buildStack.Push(new DocumentScope(aliasDocumentItem, currentToken.Value));
                }
            }

            if (buildStack.Count != 1)
            {
                //var invalidScopedElements = buildStack
                //throw new MorestachioSyntaxError(new Tokenizer.CharacterLocation(){Character = }, );
                throw new InvalidOperationException(
                          "There is an Error with the Parser. The Parser still contains unscoped builds: " +
                          buildStack.Select(e => e.Document.Kind).Aggregate((e, f) => e + ", " + f));
            }

            return(buildStack.Pop().Document);
        }
Beispiel #4
0
        /// <summary>
        ///     Parses the Tokens into a Document.
        /// </summary>
        /// <param name="tokenizerResult">The result of an Tokenizer.Tokenize call.</param>
        /// <param name="options">The ParserOptions</param>
        /// <returns></returns>
        public static IDocumentItem Parse(TokenizerResult tokenizerResult, ParserOptions options)
        {
            var buildStack = new Stack <DocumentScope>();
            //this is the scope id that determines a scope that is using let or alias variables
            int variableScope = 1;
            var getScope      = new Func <int>(() => variableScope++);

            //instead of recursive calling the parse function we stack the current document
            buildStack.Push(new DocumentScope(new MorestachioDocument(), () => 0));
            var textEdits = new List <TextEditDocumentItem>();

            DocumentScope GetVariableScope()
            {
                return(buildStack.FirstOrDefault(e => e.VariableScopeNumber != -1));
            }

            IEnumerable <ITokenOption> GetPublicOptions(TokenPair token)
            {
                var publicOptions = token.TokenOptions?.Where(e => e.Persistent).ToArray();

                return(publicOptions?.Length > 0 ? publicOptions : null);
            }

            bool TryAdd(IDocumentItem document, IDocumentItem child)
            {
                if (document is IBlockDocumentItem block)
                {
                    block.Add(child);
                    return(true);
                }

                return(false);
            }

            void CloseScope(Stack <DocumentScope> documentScopes, TokenPair currentToken, DocumentScope currentDocumentItem)
            {
                DocumentScope scope = documentScopes.Peek();

                if (!(scope.Document is IBlockDocumentItem blockDocument))
                {
                    throw new InvalidOperationException(
                              $"Closing an token '{currentToken.Type}' at '{currentToken.TokenLocation}'" +
                              $" that is not of type '{typeof(IBlockDocumentItem)}' is not possible.");
                }

                blockDocument.BlockClosingOptions = GetPublicOptions(currentToken);

                if (scope.HasAlias)                 //are we in a alias then remove it
                {
                    foreach (var scopeLocalVariable in scope.LocalVariables)
                    {
                        TryAdd(currentDocumentItem.Document, new RemoveAliasDocumentItem(currentToken.TokenLocation,
                                                                                         scopeLocalVariable,
                                                                                         scope.VariableScopeNumber, null));
                    }
                }

                // remove the last document from the stack and go back to the parents
                documentScopes.Pop();
            }

            foreach (var currentToken in tokenizerResult)
            {
                var currentDocumentItem = buildStack.Peek();                 //get the latest document
                if (currentToken.Type.Equals(TokenType.Content))
                {
                    var contentDocumentItem = new ContentDocumentItem(currentToken.TokenLocation, currentToken.Value, GetPublicOptions(currentToken));
                    TryAdd(currentDocumentItem.Document, contentDocumentItem);

                    if (tokenizerResult.Previous.HasValue)
                    {
                        if (tokenizerResult.Previous.Value.FindOption <bool>("Embedded.TrimTailing"))
                        {
                            TryAdd(contentDocumentItem, new TextEditDocumentItem(tokenizerResult.Previous.Value.TokenLocation, new TrimLineBreakTextOperation()
                            {
                                LineBreaks             = 0,
                                LineBreakTrimDirection = LineBreakTrimDirection.Begin
                            }, EmbeddedInstructionOrigin.Previous, GetPublicOptions(currentToken)));
                        }
                        if (tokenizerResult.Previous.Value.FindOption <bool>("Embedded.TrimAllTailing"))
                        {
                            TryAdd(contentDocumentItem, new TextEditDocumentItem(tokenizerResult.Previous.Value.TokenLocation, new TrimLineBreakTextOperation()
                            {
                                LineBreaks             = -1,
                                LineBreakTrimDirection = LineBreakTrimDirection.Begin
                            }, EmbeddedInstructionOrigin.Previous, GetPublicOptions(currentToken)));
                        }
                    }

                    if (tokenizerResult.Next.HasValue)
                    {
                        if (tokenizerResult.Next.Value.FindOption <bool>("Embedded.TrimLeading"))
                        {
                            TryAdd(contentDocumentItem, new TextEditDocumentItem(tokenizerResult.Next.Value.TokenLocation, new TrimLineBreakTextOperation()
                            {
                                LineBreaks             = 0,
                                LineBreakTrimDirection = LineBreakTrimDirection.End
                            }, EmbeddedInstructionOrigin.Next, GetPublicOptions(currentToken)));
                        }
                        if (tokenizerResult.Next.Value.FindOption <bool>("Embedded.TrimAllLeading"))
                        {
                            TryAdd(contentDocumentItem, new TextEditDocumentItem(tokenizerResult.Next.Value.TokenLocation, new TrimLineBreakTextOperation()
                            {
                                LineBreaks             = -1,
                                LineBreakTrimDirection = LineBreakTrimDirection.End
                            }, EmbeddedInstructionOrigin.Next, GetPublicOptions(currentToken)));
                        }
                    }

                    foreach (var textEditDocumentItem in textEdits)
                    {
                        TryAdd(contentDocumentItem, textEditDocumentItem);
                    }
                    textEdits.Clear();
                }
                else if (currentToken.Type.Equals(TokenType.If))
                {
                    var nestedDocument = new IfExpressionScopeDocumentItem(currentToken.TokenLocation,
                                                                           currentToken.MorestachioExpression, GetPublicOptions(currentToken), false);
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    TryAdd(currentDocumentItem.Document, nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.IfNot))
                {
                    var nestedDocument = new IfExpressionScopeDocumentItem(currentToken.TokenLocation,
                                                                           currentToken.MorestachioExpression, GetPublicOptions(currentToken), true);
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    TryAdd(currentDocumentItem.Document, nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.Else))
                {
                    var nestedDocument = new ElseExpressionScopeDocumentItem(currentToken.TokenLocation, GetPublicOptions(currentToken));
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    if (currentDocumentItem.Document is IfExpressionScopeDocumentItem ifDocument)
                    {
                        ifDocument.Add(nestedDocument);
                    }
                }
                else if (currentToken.Type.Equals(TokenType.ElseIf))
                {
                    var nestedDocument = new ElseIfExpressionScopeDocumentItem(currentToken.TokenLocation,
                                                                               currentToken.MorestachioExpression,
                                                                               GetPublicOptions(currentToken));

                    var documentScope = new DocumentScope(nestedDocument, getScope);
                    buildStack.Push(documentScope);
                    if (currentDocumentItem.Document is IfExpressionScopeDocumentItem ifDocument)
                    {
                        ifDocument.Add(nestedDocument);
                    }
                    //AddIfDocument(currentToken, documentScope);
                }
                else if (currentToken.Type.Equals(TokenType.CollectionOpen))
                {
                    var nestedDocument = new EachDocumentItem(currentToken.TokenLocation, currentToken.MorestachioExpression, GetPublicOptions(currentToken));
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    TryAdd(currentDocumentItem.Document, nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.SwitchOpen))
                {
                    var nestedDocument = new SwitchDocumentItem(currentToken.TokenLocation,
                                                                currentToken.MorestachioExpression,
                                                                currentToken.FindOption <bool>("ScopeTo"), GetPublicOptions(currentToken));
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    TryAdd(currentDocumentItem.Document, nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.SwitchCaseOpen))
                {
                    var nestedDocument = new SwitchCaseDocumentItem(currentToken.TokenLocation, currentToken.MorestachioExpression, GetPublicOptions(currentToken));
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    TryAdd(currentDocumentItem.Document, nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.SwitchDefaultOpen))
                {
                    var nestedDocument = new SwitchDefaultDocumentItem(currentToken.TokenLocation, GetPublicOptions(currentToken));
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    TryAdd(currentDocumentItem.Document, nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.WhileLoopOpen))
                {
                    var nestedDocument = new WhileLoopDocumentItem(currentToken.TokenLocation, currentToken.MorestachioExpression, GetPublicOptions(currentToken));
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    TryAdd(currentDocumentItem.Document, nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.DoLoopOpen))
                {
                    var nestedDocument = new DoLoopDocumentItem(currentToken.TokenLocation, currentToken.MorestachioExpression, GetPublicOptions(currentToken));
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    TryAdd(currentDocumentItem.Document, nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.ElementOpen))
                {
                    var nestedDocument = new ExpressionScopeDocumentItem(currentToken.TokenLocation, currentToken.MorestachioExpression, GetPublicOptions(currentToken));
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    TryAdd(currentDocumentItem.Document, nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.RepeatLoopOpen))
                {
                    var nestedDocument = new RepeatDocumentItem(currentToken.TokenLocation, currentToken.MorestachioExpression, GetPublicOptions(currentToken));
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    TryAdd(currentDocumentItem.Document, nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.InvertedElementOpen))
                {
                    var nestedDocument = new InvertedExpressionScopeDocumentItem(currentToken.TokenLocation,
                                                                                 currentToken.MorestachioExpression, GetPublicOptions(currentToken));
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                    TryAdd(currentDocumentItem.Document, nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.CollectionClose) ||
                         currentToken.Type.Equals(TokenType.ElementClose) ||
                         currentToken.Type.Equals(TokenType.IfClose) ||
                         currentToken.Type.Equals(TokenType.ElseClose) ||
                         currentToken.Type.Equals(TokenType.ElseIfClose) ||
                         currentToken.Type.Equals(TokenType.WhileLoopClose) ||
                         currentToken.Type.Equals(TokenType.DoLoopClose) ||
                         currentToken.Type.Equals(TokenType.RepeatLoopClose) ||
                         currentToken.Type.Equals(TokenType.SwitchCaseClose) ||
                         currentToken.Type.Equals(TokenType.SwitchDefaultClose) ||
                         currentToken.Type.Equals(TokenType.SwitchClose))
                {
                    CloseScope(buildStack, currentToken, currentDocumentItem);
                }
                else if (currentToken.Type.Equals(TokenType.EscapedSingleValue) ||
                         currentToken.Type.Equals(TokenType.UnescapedSingleValue))
                {
                    var nestedDocument = new PathDocumentItem(currentToken.TokenLocation,
                                                              currentToken.MorestachioExpression,
                                                              currentToken.Type.Equals(TokenType.EscapedSingleValue), GetPublicOptions(currentToken));
                    TryAdd(currentDocumentItem.Document, nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.PartialDeclarationOpen))
                {
                    // currently same named partials will override each other
                    // to allow recursive calls of partials we first have to declare the partial and then load it as we would parse
                    // -the partial as a whole and then add it to the list would lead to unknown calls of partials inside the partial
                    var partialDocumentItem = new PartialDocumentItem(currentToken.TokenLocation,
                                                                      currentToken.Value,
                                                                      GetPublicOptions(currentToken));

                    buildStack.Push(new DocumentScope(partialDocumentItem, getScope));

                    TryAdd(currentDocumentItem.Document, partialDocumentItem);
                }
                else if (currentToken.Type.Equals(TokenType.PartialDeclarationClose))
                {
                    CloseScope(buildStack, currentToken, currentDocumentItem);
                    //buildStack.Pop();
                }
                else if (currentToken.Type.Equals(TokenType.RenderPartial))
                {
                    TryAdd(currentDocumentItem.Document, new RenderPartialDocumentItem(currentToken.TokenLocation,
                                                                                       currentToken.Value,
                                                                                       currentToken.MorestachioExpression, GetPublicOptions(currentToken)));
                }
                else if (currentToken.Type.Equals(TokenType.ImportPartial))
                {
                    TryAdd(currentDocumentItem.Document, new ImportPartialDocumentItem(currentToken.TokenLocation,
                                                                                       currentToken.MorestachioExpression,
                                                                                       currentToken.FindOption <IMorestachioExpression>("Context"),
                                                                                       GetPublicOptions(currentToken)));
                }
                else if (currentToken.Type.Equals(TokenType.IsolationScopeOpen))
                {
                    var nestedDocument =
                        new IsolationScopeDocumentItem(currentToken.TokenLocation,
                                                       currentToken.FindOption <IsolationOptions>("IsolationType"),
                                                       currentToken.FindOption <IMorestachioExpression>("IsolationScopeArg"),
                                                       GetPublicOptions(currentToken));
                    TryAdd(currentDocumentItem.Document, nestedDocument);
                    buildStack.Push(new DocumentScope(nestedDocument, getScope));
                }
                else if (currentToken.Type.Equals(TokenType.IsolationScopeClose))
                {
                    CloseScope(buildStack, currentToken, currentDocumentItem);
                }
                else if (currentToken.Type.Equals(TokenType.Alias))
                {
                    var scope          = GetVariableScope();
                    var nestedDocument = new AliasDocumentItem(currentToken.TokenLocation,
                                                               currentToken.Value,
                                                               scope.VariableScopeNumber,
                                                               GetPublicOptions(currentToken));
                    TryAdd(currentDocumentItem.Document, nestedDocument);
                    currentDocumentItem.LocalVariables.Add(currentToken.Value);
                }
                else if (currentToken.Type.Equals(TokenType.VariableVar))
                {
                    EvaluateVariableDocumentItem nestedDocument;

                    var isolationParent = buildStack.FirstOrDefault(e => e.Document is IsolationScopeDocumentItem doc &&
                                                                    doc.Isolation.HasFlag(IsolationOptions.VariableIsolation));
                    if (isolationParent != null)
                    {
                        nestedDocument = new EvaluateVariableDocumentItem(currentToken.TokenLocation,
                                                                          currentToken.Value,
                                                                          currentToken.MorestachioExpression,
                                                                          isolationParent.VariableScopeNumber,
                                                                          GetPublicOptions(currentToken));
                        isolationParent.LocalVariables.Add(currentToken.Value);
                    }
                    else
                    {
                        nestedDocument = new EvaluateVariableDocumentItem(currentToken.TokenLocation,
                                                                          currentToken.Value,
                                                                          currentToken.MorestachioExpression, GetPublicOptions(currentToken));
                    }

                    TryAdd(currentDocumentItem.Document, nestedDocument);
                }
                else if (currentToken.Type.Equals(TokenType.VariableLet))
                {
                    var scope = 0;
                    if (buildStack.Count > 1)
                    {
                        scope = GetVariableScope()
                                .VariableScopeNumber;
                    }
                    var nestedDocument = new EvaluateLetVariableDocumentItem(currentToken.TokenLocation,
                                                                             currentToken.Value,
                                                                             currentToken.MorestachioExpression, scope, GetPublicOptions(currentToken));

                    TryAdd(currentDocumentItem.Document, nestedDocument);
                    if (buildStack.Count > 1)
                    {
                        currentDocumentItem.LocalVariables.Add(currentToken.Value);
                    }
                }
                else if (currentToken.Type.Equals(TokenType.WriteLineBreak))
                {
                    TryAdd(currentDocumentItem.Document, new TextEditDocumentItem(currentToken.TokenLocation,
                                                                                  new AppendLineBreakTextOperation(),
                                                                                  currentToken.IsEmbeddedToken, GetPublicOptions(currentToken)));
                }
                else if (currentToken.Type.Equals(TokenType.TrimLineBreak))
                {
                    textEdits.Add(new TextEditDocumentItem(currentToken.TokenLocation,
                                                           new TrimLineBreakTextOperation()
                    {
                        LineBreaks             = 1,
                        LineBreakTrimDirection = LineBreakTrimDirection.Begin
                    }, currentToken.IsEmbeddedToken, GetPublicOptions(currentToken)));
                }
                else if (currentToken.Type.Equals(TokenType.TrimLineBreaks))
                {
                    textEdits.Add(new TextEditDocumentItem(currentToken.TokenLocation,
                                                           new TrimLineBreakTextOperation()
                    {
                        LineBreaks             = currentToken.FindOption <bool>("All") ? -1 : 0,
                        LineBreakTrimDirection = LineBreakTrimDirection.Begin
                    }, currentToken.IsEmbeddedToken, GetPublicOptions(currentToken)));
                }
                else if (currentToken.Type.Equals(TokenType.TrimPrependedLineBreaks))
                {
                    textEdits.Add(new TextEditDocumentItem(currentToken.TokenLocation,
                                                           new TrimLineBreakTextOperation()
                    {
                        LineBreaks             = currentToken.FindOption <bool>("All") ? -1 : 0,
                        LineBreakTrimDirection = LineBreakTrimDirection.End
                    }, currentToken.IsEmbeddedToken, GetPublicOptions(currentToken)));
                }
                else if (currentToken.Type.Equals(TokenType.TrimEverything))
                {
                    textEdits.Add(new TextEditDocumentItem(currentToken.TokenLocation,
                                                           new TrimAllWhitespacesTextOperation(),
                                                           currentToken.IsEmbeddedToken, GetPublicOptions(currentToken)));
                }
                else if (currentToken.Type.Equals(TokenType.Comment) || currentToken.Type.Equals(TokenType.BlockComment))
                {
                    //just ignore this part and print nothing
                    if (options.TokenizeComments)
                    {
                        TryAdd(currentDocumentItem.Document,
                               new CommentDocumentItem(currentToken.TokenLocation, currentToken.Value,
                                                       GetPublicOptions(currentToken), currentToken.Type.Equals(TokenType.BlockComment)));
                    }
                }
                else
                {
                    var tokenOptions = GetPublicOptions(currentToken);
                    var customDocumentItemProvider =
                        options.CustomDocumentItemProviders.FindTokenProvider(currentToken, options, tokenOptions);
                    var nestedDocument = customDocumentItemProvider?.Parse(currentToken, options, buildStack, getScope, tokenOptions);
                    if (nestedDocument != null)
                    {
                        TryAdd(currentDocumentItem.Document, nestedDocument);
                    }
                }
            }

            if (buildStack.Count != 1)
            {
                //var invalidScopedElements = buildStack
                //throw new MorestachioSyntaxError(new Tokenizer.CharacterLocation(){Character = }, );
                throw new InvalidOperationException(
                          "There is an Error with the Parser. The Parser still contains unscoped builds: " +
                          buildStack.Select(e => e.Document.GetType().Name).Aggregate((e, f) => e + ", " + f));
            }

            return(buildStack.Pop().Document);
        }