/// <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); }
/// <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 == 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 EachDocumentItem(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)); } else if (currentToken.Type == TokenType.VariableDeclaration) { var evaluateVariableDocumentItem = new EvaluateVariableDocumentItem(currentToken.Value); currentDocumentItem.Document.Add(evaluateVariableDocumentItem); buildStack.Push(new DocumentScope(evaluateVariableDocumentItem)); } else if (currentToken.Type == TokenType.VariableSet) { if (buildStack.Peek().IsFormattingScope) //is the remaining scope a formatting one. If it is pop it and return to its parent { buildStack.Pop(); } // remove the last document from the stack and go back to the parents buildStack.Pop(); } 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); }