Пример #1
0
        /// <summary>
        ///		Parses the Tokens into a Document.
        /// </summary>
        /// <param name="tokens">The tokens.</param>
        /// <param name="options">The options.</param>
        /// <returns></returns>
        internal static IDocumentItem Parse(Queue <TokenPair> tokens, ParserOptions options)
        {
            var buildStack = new Stack <FormattingScope>();

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

            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.Item1.Add(new ContentDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                }
                else if (currentToken.Type == TokenType.CollectionOpen)
                {
                    var nestedDocument = new CollectionDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new FormattingScope(nestedDocument, false));
                    currentDocumentItem.Item1.Add(nestedDocument);
                }
                else if (currentToken.Type == TokenType.ElementOpen)
                {
                    var nestedDocument = new ExpressionScopeDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new FormattingScope(nestedDocument, false));
                    currentDocumentItem.Item1.Add(nestedDocument);
                }
                else if (currentToken.Type == TokenType.InvertedElementOpen)
                {
                    var invertedScope = new InvertedExpressionScopeDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new FormattingScope(invertedScope, false));
                    currentDocumentItem.Item1.Add(invertedScope);
                }
                else if (currentToken.Type == TokenType.CollectionClose || currentToken.Type == TokenType.ElementClose)
                {
                    // remove the last document from the stack and go back to the parents
                    buildStack.Pop();
                    if (buildStack.Peek().Item2)                     //is the remaining scope a formatting one. If it is pop it and return to its parent
                    {
                        buildStack.Pop();
                    }
                }
                else if (currentToken.Type == TokenType.PrintFormatted)
                {
                    currentDocumentItem.Item1.Add(new PrintFormattedItem());
                    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().Item2)
                    {
                        buildStack.Pop();
                    }
                    var formatterItem = new IsolatedContextDocumentItem()
                    {
                        ExpressionStart = currentToken.TokenLocation
                    };
                    buildStack.Push(new FormattingScope(formatterItem, true));
                    formatterItem.Add(new CallFormatterDocumentItem(currentToken.FormatString, currentToken.Value));
                    currentDocumentItem.Item1.Add(formatterItem);
                }
                else if (currentToken.Type == TokenType.EscapedSingleValue ||
                         currentToken.Type == TokenType.UnescapedSingleValue)
                {
                    currentDocumentItem.Item1.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 FormattingScope(nestedDocument, false));
                    currentDocumentItem.Item1.Add(new PartialDocumentItem(currentToken.Value, nestedDocument)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                }
                else if (currentToken.Type == TokenType.PartialDeclarationClose)
                {
                    currentDocumentItem.Item1.Add(new RenderPartialDoneDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                    buildStack.Pop();
                }
                else if (currentToken.Type == TokenType.RenderPartial)
                {
                    currentDocumentItem.Item1.Add(new RenderPartialDocumentItem(currentToken.Value)
                    {
                        ExpressionStart = currentToken.TokenLocation
                    });
                }
            }

            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.Item1.Kind).Aggregate((e, f) => e + ", " + f));
            }

            return(buildStack.Pop().Item1);
        }
Пример #2
0
 internal MorestachioDocumentInfo([NotNull] ParserOptions options, [CanBeNull] IDocumentItem document, [CanBeNull] IEnumerable <IMorestachioError> errors)
 {
     ParserOptions = options ?? throw new ArgumentNullException(nameof(options));
     Document      = document;
     Errors        = errors ?? Enumerable.Empty <IMorestachioError>();
 }
Пример #3
0
        public static IEnumerable <TokenPair> Tokenize(ParserOptions parserOptions)
        {
            var templateString = parserOptions.Template ?? "";
            var matches        = TokenFinder.Matches(templateString);
            var scopestack     = new Stack <Tuple <string, int> >();

            var idx = 0;

            var parseErrors = new List <IndexedParseException>();
            var lines       = new List <int>();

            foreach (Match m in matches)
            {
                //yield front content.
                if (m.Index > idx)
                {
                    yield return(new TokenPair(TokenType.Content, templateString.Substring(idx, m.Index - idx)));
                }

                if (m.Value.StartsWith("{{#each"))
                {
                    scopestack.Push(Tuple.Create(m.Value, m.Index));
                    var token = m.Value.TrimStart('{').TrimEnd('}').TrimStart('#').Trim();
                    token = token.Substring(4);

                    if (token.StartsWith(" ") && token.Trim() != "")
                    {
                        token = token.Trim();
                        if (FormatInExpressionFinder.IsMatch(token))
                        {
                            foreach (var tokenizeFormattable in TokenizeFormattables(token, templateString, m, lines,
                                                                                     parseErrors))
                            {
                                yield return(tokenizeFormattable);
                            }

                            yield return(new TokenPair(TokenType.CollectionOpen, "."));
                        }
                        else
                        {
                            yield return(new TokenPair(TokenType.CollectionOpen,
                                                       Validated(token, templateString, m.Index, lines, parseErrors).Trim()));
                        }
                    }
                    else
                    {
                        var location = HumanizeCharacterLocation(templateString, m.Index, lines);
                        parseErrors.Add(new IndexedParseException(location,
                                                                  @"The 'each' block being opened requires a model path to be specified in the form '{{{{#each <name>}}}}'."));
                    }
                }
                else if (m.Value.StartsWith("{{/each"))
                {
                    if (m.Value != "{{/each}}")
                    {
                        var location = HumanizeCharacterLocation(templateString, m.Index, lines);
                        parseErrors.Add(new IndexedParseException(location,
                                                                  @"The syntax to close the 'each' block should be: '{{{{/each}}}}'."));
                    }
                    else if (scopestack.Any() && scopestack.Peek().Item1.StartsWith("{{#each"))
                    {
                        var token = scopestack.Pop().Item1;
                        yield return(new TokenPair(TokenType.CollectionClose, token));
                    }
                    else
                    {
                        var location = HumanizeCharacterLocation(templateString, m.Index, lines);
                        parseErrors.Add(new IndexedParseException(location,
                                                                  @"An 'each' block is being closed, but no corresponding opening element ('{{{{#each <name>}}}}') was detected."));
                    }
                }
                else if (m.Value.StartsWith("{{#"))
                {
                    //open group
                    var token = m.Value.TrimStart('{').TrimEnd('}').TrimStart('#').Trim();


                    if (scopestack.Any() && scopestack.Peek().Item1 == token)
                    {
                        yield return(new TokenPair(TokenType.ElementClose,
                                                   Validated(token, templateString, m.Index, lines, parseErrors)));
                    }
                    else
                    {
                        scopestack.Push(Tuple.Create(token, m.Index));
                    }

                    yield return(new TokenPair(TokenType.ElementOpen,
                                               Validated(token, templateString, m.Index, lines, parseErrors)));
                }
                else if (m.Value.StartsWith("{{^"))
                {
                    //open inverted group
                    var token = m.Value.TrimStart('{').TrimEnd('}').TrimStart('^').Trim();

                    if (scopestack.Any() && scopestack.Peek().Item1 == token)
                    {
                        yield return(new TokenPair(TokenType.ElementClose,
                                                   Validated(token, templateString, m.Index, lines, parseErrors)));
                    }
                    else
                    {
                        scopestack.Push(Tuple.Create(token, m.Index));
                    }

                    yield return(new TokenPair(TokenType.InvertedElementOpen,
                                               Validated(token, templateString, m.Index, lines, parseErrors)));
                }
                else if (m.Value.StartsWith("{{/"))
                {
                    var token = m.Value.TrimStart('{').TrimEnd('}').TrimStart('/').Trim();
                    //close group
                    if (scopestack.Any() && scopestack.Peek().Item1 == token)
                    {
                        scopestack.Pop();
                        yield return(new TokenPair(TokenType.ElementClose,
                                                   Validated(token, templateString, m.Index, lines, parseErrors)));
                    }
                    else
                    {
                        var location = HumanizeCharacterLocation(templateString, m.Index, lines);
                        parseErrors.Add(new IndexedParseException(location,
                                                                  "It appears that open and closing elements are mismatched."));
                    }
                }
                else if (m.Value.StartsWith("{{{") | m.Value.StartsWith("{{&"))
                {
                    //escaped single element
                    var token = m.Value.TrimStart('{').TrimEnd('}').TrimStart('&').Trim();
                    yield return(new TokenPair(TokenType.UnescapedSingleValue,
                                               Validated(token, templateString, m.Index, lines, parseErrors)));
                }
                else if (m.Value.StartsWith("{{!"))
                {
                    //it's a comment drop this on the floor, no need to even yield it.
                }
                else
                {
                    //unsingle value.
                    var token = m.Value.TrimStart('{').TrimEnd('}').Trim();
                    if (FormatInExpressionFinder.IsMatch(token))
                    {
                        foreach (var tokenizeFormattable in TokenizeFormattables(token, templateString, m, lines,
                                                                                 parseErrors))
                        {
                            yield return(tokenizeFormattable);
                        }

                        yield return(new TokenPair(TokenType.PrintFormatted, "."));
                    }
                    else
                    {
                        yield return(new TokenPair(TokenType.EscapedSingleValue,
                                                   Validated(token, templateString, m.Index, lines, parseErrors)));
                    }
                }

                //move forward in the string.
                idx = m.Index + m.Length;
            }

            if (idx < templateString.Length)
            {
                yield return(new TokenPair(TokenType.Content, templateString.Substring(idx)));
            }

            #region Assert that any scopes opened must be closed.

            if (scopestack.Any())
            {
                var scopes = scopestack.Select(k =>
                {
                    var value = k.Item1.Trim('{', '#', '}');
                    if (value.StartsWith("each "))
                    {
                        value = value.Substring(5);
                    }

                    return(new
                    {
                        scope = value,
                        location = HumanizeCharacterLocation(templateString, k.Item2, lines)
                    });
                }).Reverse()
                             .ToArray();

                parseErrors.AddRange(scopes.Select(unclosedScope => new IndexedParseException(unclosedScope.location,
                                                                                              "A scope block to the following path was opened but not closed: '{0}', please close it using the appropriate syntax.",
                                                                                              unclosedScope.scope)));
            }

            #endregion

            //We want to throw an aggregate template exception, but in due time.
            if (!parseErrors.Any())
            {
                yield break;
            }

            var innerExceptions = parseErrors.OrderBy(k => k.LineNumber).ThenBy(k => k.CharacterOnLine).ToArray();
            throw new AggregateException(innerExceptions);
        }
Пример #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));

            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);
        }
Пример #5
0
 /// <summary>
 /// Initializes a new instance of the <see cref="MorestachioDocumentInfo"/> class.
 /// </summary>
 /// <param name="options">The options.</param>
 /// <param name="document">The document.</param>
 public MorestachioDocumentInfo([NotNull] ParserOptions options, [NotNull] IDocumentItem document)
     : this(options, document ?? throw new ArgumentNullException(nameof(document)), null)
 {
 }
        public async Task <MorestachioDocumentResult> CreateAsync([NotNull] object data, CancellationToken token)
        {
            if (Errors.Any())
            {
                throw new AggregateException("You cannot Create this Template as there are one or more Errors. See Inner Exception for more infos.", Errors.Select(e => e.GetException())).Flatten();
            }

            if (Document is MorestachioDocument morestachioDocument && morestachioDocument.MorestachioVersion !=
                MorestachioDocument.GetMorestachioVersion())
            {
                throw new InvalidOperationException($"The supplied version in the Morestachio document " +
                                                    $"'{morestachioDocument.MorestachioVersion}'" +
                                                    $" is not compatible with the current morestachio version of " +
                                                    $"'{MorestachioDocument.GetMorestachioVersion()}'");
            }

            var timeoutCancellation = new CancellationTokenSource();

            if (ParserOptions.Timeout != TimeSpan.Zero)
            {
                timeoutCancellation.CancelAfter(ParserOptions.Timeout);
                var anyCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutCancellation.Token);
                token = anyCancellationToken.Token;
            }
            var sourceStream = ParserOptions.SourceFactory();

            try
            {
                if (sourceStream == null)
                {
                    throw new NullReferenceException("The created stream is null.");
                }

                if (!sourceStream.CanWrite)
                {
                    throw new InvalidOperationException($"The stream '{sourceStream.GetType()}' is ReadOnly.");
                }

                using (var byteCounterStream = new ByteCounterStream(sourceStream,
                                                                     ParserOptions.Encoding, BufferSize, true, ParserOptions))
                {
                    var context = new ContextObject(ParserOptions, "", null)
                    {
                        Value             = data,
                        CancellationToken = token
                    };

                    using (var scopeData = new ScopeData())
                    {
                        await MorestachioDocument.ProcessItemsAndChildren(new[] { Document }, byteCounterStream,
                                                                          context, scopeData);
                    }
                }

                if (timeoutCancellation.IsCancellationRequested)
                {
                    sourceStream.Dispose();
                    throw new TimeoutException($"The requested timeout of '{ParserOptions.Timeout:g}' for report generation was reached.");
                }
            }
            catch
            {
                //If there is any exception while generating the template we must dispose any data written to the stream as it will never returned and might
                //create a memory leak with this. This is also true for a timeout
                sourceStream?.Dispose();
                throw;
            }
            return(new MorestachioDocumentResult()
            {
                Stream = sourceStream
            });
        }
Пример #7
0
 public static MorestachioDocumentInfo ParseWithOptions([NotNull] ParserOptions parsingOptions)
 {
     return(ParseWithOptionsAsync(parsingOptions).Result);
 }
Пример #8
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);
        }
Пример #9
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);
        }
Пример #10
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ContextObject"/> class.
 /// </summary>
 /// <param name="options">The options.</param>
 public ContextObject([NotNull] ParserOptions options)
 {
     Options = options;
 }
Пример #11
0
        internal static AsyncParserAction Parse(Queue <TokenPair> tokens, ParserOptions options, ScopeData scopeData,
                                                InferredTemplateModel currentScope = null)
        {
            var buildArray = new ParserActions();

            while (tokens.Any())
            {
                var currentToken = tokens.Dequeue();
                switch (currentToken.Type)
                {
                case TokenType.Comment:
                    break;

                case TokenType.Content:
                    buildArray.MakeAction(HandleContent(currentToken.Value));
                    break;

                case TokenType.CollectionOpen:
                    buildArray.MakeAction(HandleCollectionOpen(currentToken, tokens, options, scopeData, currentScope));
                    break;

                case TokenType.ElementOpen:
                    buildArray.MakeAction(HandleElementOpen(currentToken, tokens, options, scopeData, currentScope));
                    break;

                case TokenType.InvertedElementOpen:
                    buildArray.MakeAction(HandleInvertedElementOpen(currentToken, tokens, options, scopeData, currentScope));
                    break;

                case TokenType.CollectionClose:
                case TokenType.ElementClose:
                    // This should immediately return if we're in the element scope,
                    // -and if we're not, this should have been detected by the tokenizer!
                    return(async(builder, context) =>
                    {
                        await buildArray.ExecuteWith(builder, context);
                    });

                case TokenType.Format:
                    buildArray.MakeAction(ParseFormatting(currentToken, tokens, options, scopeData, currentScope));
                    break;

                case TokenType.EscapedSingleValue:
                case TokenType.UnescapedSingleValue:
                    buildArray.MakeAction(HandleSingleValue(currentToken, options, scopeData, currentScope));
                    break;

                case 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
                    AsyncParserAction handlePartialDeclaration = null;
                    scopeData.Partials[currentToken.Value] = async(outputStream, context) =>
                    {
                        if (handlePartialDeclaration != null)
                        {
                            await handlePartialDeclaration(outputStream, context);
                        }
                        else
                        {
                            throw new InvalidOperationException($"Partial '{currentToken.Value}' was executed before created was completed.");
                        }
                    };
                    handlePartialDeclaration = HandlePartialDeclaration(currentToken, tokens, options, scopeData, currentScope);
                    break;

                case TokenType.RenderPartial:
                    var partialName = currentToken.Value;
                    var partialCode = scopeData.Partials[partialName];
                    buildArray.MakeAction(async(a, f) =>
                    {
                        var currentPartial = partialName + "_" + scopeData.PartialDepth.Count;
                        scopeData.PartialDepth.Push(currentPartial);
                        if (scopeData.PartialDepth.Count >= options.PartialStackSize)
                        {
                            switch (options.StackOverflowBehavior)
                            {
                            case ParserOptions.PartialStackOverflowBehavior.FailWithException:
                                throw new MustachioStackOverflowException(
                                    $"You have exceeded the maximum stack Size for nested Partial calls of '{options.PartialStackSize}'. See Data for call stack")
                                {
                                    Data =
                                    {
                                        { "Callstack", scopeData.PartialDepth }
                                    }
                                };
                                break;

                            case ParserOptions.PartialStackOverflowBehavior.FailSilent:

                                break;

                            default:
                                throw new ArgumentOutOfRangeException();
                            }
                        }
                        else
                        {
                            await partialCode(a, f);
                        }
                        scopeData.PartialDepth.Pop();
                    });
                    break;
                }
            }

            return(async(builder, context) =>
            {
                await buildArray.ExecuteWith(builder, context);
            });
        }
        public async MorestachioDocumentResultPromise CreateAsync([NotNull] object data, CancellationToken token)
        {
            if (Errors.Any())
            {
                throw new AggregateException("You cannot Create this Template as there are one or more Errors. See Inner Exception for more infos.", Errors.Select(e => e.GetException())).Flatten();
            }

            if (Document is MorestachioDocument morestachioDocument && morestachioDocument.MorestachioVersion !=
                MorestachioDocument.GetMorestachioVersion())
            {
                throw new InvalidOperationException($"The supplied version in the Morestachio document " +
                                                    $"'{morestachioDocument.MorestachioVersion}'" +
                                                    $" is not compatible with the current morestachio version of " +
                                                    $"'{MorestachioDocument.GetMorestachioVersion()}'");
            }

            var timeoutCancellation = new CancellationTokenSource();

            if (ParserOptions.Timeout != TimeSpan.Zero)
            {
                timeoutCancellation.CancelAfter(ParserOptions.Timeout);
                var anyCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutCancellation.Token);
                token = anyCancellationToken.Token;
            }

            PerformanceProfiler profiler = null;

            using (var byteCounterStream = ParserOptions.StreamFactory.GetByteCounterStream(ParserOptions))
            {
                if (byteCounterStream?.Stream == null)
                {
                    throw new NullReferenceException("The created stream is null.");
                }
                var context   = ParserOptions.CreateContextObject("", token, data);
                var scopeData = new ScopeData();
                try
                {
                    if (ParserOptions.ProfileExecution)
                    {
                        scopeData.Profiler = profiler = new PerformanceProfiler(true);
                    }
                    await MorestachioDocument.ProcessItemsAndChildren(new[] { Document }, byteCounterStream,
                                                                      context, scopeData);

                    if (timeoutCancellation.IsCancellationRequested)
                    {
                        throw new TimeoutException($"The requested timeout of '{ParserOptions.Timeout:g}' for template generation was reached.");
                    }
                }
                finally
                {
                    if (!CaptureVariables)
                    {
                        scopeData.Dispose();
                        scopeData.Variables.Clear();
                    }
                }

                return(new MorestachioDocumentResult(byteCounterStream.Stream,
                                                     profiler,
                                                     scopeData.Variables.ToDictionary(e => e.Key, e => scopeData.GetFromVariable(e.Value).Value)));
            }
        }