/// <summary> /// Check if text has symbol for current action. /// </summary> /// <param name="text">Text to check.</param> /// <param name="index">Index in text.</param> /// <param name="action">Action to check.</param> /// <param name="length">Length of symbol is set here.</param> /// <returns></returns> internal static bool IsSymbol(string text, int index, ParserActions action, ref int length) { // Get symbol list for action. SymbolList list = null; switch (action) { case ParserActions.Escape: list = EscapeSymbol; break; case ParserActions.VariableStart: list = VariableStartSymbol; break; case ParserActions.VariableEnd: list = VariableEndSymbol; break; case ParserActions.VariableDelimiter: list = VariableDelimiterSymbol; break; case ParserActions.ArgumentStart: list = ArgumentStartSymbol; break; case ParserActions.ArgumentEnd: list = ArgumentEndSymbol; break; case ParserActions.ArgumentDelimiter: list = ArgumentDelimiterSymbol; break; case ParserActions.LiteralStart: list = LiteralStartSymbol; break; case ParserActions.LiteralEnd: list = LiteralEndSymbol; break; case ParserActions.BlockIgnore: list = IgnoreBlockSymbol; break; case ParserActions.AlwaysIgnore: list = IgnoreAlwaysSymbol; break; default: throw new InvalidOperationException(); } return(list.Test(text, index, ref length)); }
private static AsyncParserAction ParseFormatting(TokenPair token, Queue <TokenPair> tokens, ParserOptions options, ScopeData scopeData, InferredTemplateModel currentScope = null) { var buildArray = new ParserActions(); buildArray.MakeAction(HandleFormattingValue(token, currentScope, scopeData)); var nonPrintToken = false; while (tokens.Any() && !nonPrintToken) //only take as few tokens we need for formatting. { var currentToken = tokens.Peek(); switch (currentToken.Type) { case TokenType.Format: //this will invoke the formatter and copy the scope. //we must copy the scope as the formatting action might break our chain and we are no longer able to //construct a valid path up //after that there is always a PrintFormatted type that will print the "current" scope and //reset it to the origial scope before we have entered the scope buildArray.MakeAction(HandleFormattingValue(tokens.Dequeue(), currentScope, scopeData)); break; case TokenType.PrintFormatted: tokens.Dequeue(); //this must be the flow token type that has no real value execpt for a dot buildArray.MakeAction(PrintFormattedValues()); break; case TokenType.CollectionOpen: //in this case we are in a formatting expression followed by a #each. //after this we need to reset the context so handle the open here buildArray.MakeAction(HandleCollectionOpen(tokens.Dequeue(), tokens, options, scopeData, currentScope)); break; case TokenType.ElementOpen: //in this case we are in a formatting expression followed by a #. //after this we need to reset the context so handle the open here buildArray.MakeAction(HandleElementOpen(tokens.Dequeue(), tokens, options, scopeData, currentScope)); break; case TokenType.InvertedElementOpen: //in this case we are in a formatting expression followed by a ^. //after this we need to reset the context so handle the open here buildArray.MakeAction(HandleElementOpen(tokens.Dequeue(), tokens, options, scopeData, currentScope)); break; default: //The following cannot be formatted and the result of the formatting operation has used. //continue with the original Context nonPrintToken = true; break; } } return(async(builder, context) => { //the formatting will may change the object. Clone the current Context to leave the root one untouched var contextClone = context.Clone(); await buildArray.ExecuteWith(builder, contextClone); }); }
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); }); }