private static AsyncParserAction HandleSingleValue(TokenPair token, ParserOptions options, ScopeData scopeData, InferredTemplateModel scope) { scope = scope?.GetInferredModelForPath(token.Value, InferredTemplateModel.UsedAs.Scalar); return(async(builder, context) => { //try to locate the value in the context, if it exists, append it. var c = context != null ? (await context.GetContextForPath(token.Value, scopeData)) : null; if (c?.Value != null) { await c.EnsureValue(); if (token.Type == TokenType.EscapedSingleValue && !options.DisableContentEscaping) { HandleContent(HtmlEncodeString(await c.RenderToString()))(builder, c); } else { HandleContent(await c.RenderToString())(builder, c); } } }); }
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); }); }
private static AsyncParserAction HandleCollectionOpen(TokenPair token, Queue <TokenPair> remainder, ParserOptions options, ScopeData scopeData, InferredTemplateModel scope) { scope = scope?.GetInferredModelForPath(token.Value, InferredTemplateModel.UsedAs.Collection); var innerTemplate = Parse(remainder, options, scopeData, scope); return(async(builder, context) => { //if we're in the same scope, just negating, then we want to use the same object var c = await context.GetContextForPath(token.Value, scopeData); //"falsey" values by Javascript standards... if (!await c.Exists()) { return; } var value = c.Value as IEnumerable; if (value != null && !(value is string) && !(value is IDictionary <string, object>)) { //Use this "lookahead" enumeration to allow the $last keyword var index = 0; var enumumerator = value.GetEnumerator(); if (!enumumerator.MoveNext()) { return; } var current = enumumerator.Current; do { var next = enumumerator.MoveNext() ? enumumerator.Current : null; var innerContext = new ContextCollection(index, next == null, options, $"[{index}]") { Value = current, Parent = c }; await innerTemplate(builder, innerContext); index++; current = next; } while (current != null && StopOrAbortBuilding(builder, context)); } else { throw new IndexedParseException( "'{0}' is used like an array by the template, but is a scalar value or object in your model.", token.Value); } }); }
private static AsyncParserAction HandlePartialDeclaration(TokenPair currentToken, Queue <TokenPair> tokens, ParserOptions options, ScopeData scopeData, InferredTemplateModel currentScope) { var partialTokens = new Queue <TokenPair>(); var token = currentToken; while (tokens.Any() && (token.Type != TokenType.PartialDeclarationClose || token.Value != currentToken.Value)) //just look for the closing tag and buffer it seperate { token = tokens.Dequeue(); partialTokens.Enqueue(token); } return(Parse(partialTokens, options, scopeData, currentScope)); //we have taken everything from the partial and created a executable function for it }
private static AsyncParserAction HandleFormattingValue(TokenPair currentToken, InferredTemplateModel scope, ScopeData scopeData) { return(async(builder, context) => { scope = scope?.GetInferredModelForPath(currentToken.Value, InferredTemplateModel.UsedAs.Scalar); if (context == null) { return; } var c = await context.GetContextForPath(currentToken.Value, scopeData); if (currentToken.FormatString != null && currentToken.FormatString.Any()) { var argList = new List <KeyValuePair <string, object> >(); foreach (var formatterArgument in currentToken.FormatString) { //if pre and suffixed by a $ its a reference to another field. //walk the path in the $ and use the value in the formatter var trimmedArg = formatterArgument.Argument.Trim(); if (trimmedArg.StartsWith("$") && trimmedArg.EndsWith("$")) { var formatContext = await context.GetContextForPath(trimmedArg.Trim('$'), scopeData); await formatContext.EnsureValue(); argList.Add(new KeyValuePair <string, object>(formatterArgument.Name, formatContext.Value)); } else { argList.Add(new KeyValuePair <string, object>(formatterArgument.Name, formatterArgument.Argument)); } } //we do NOT await the task here. We await the task only if we need the value context.Value = c.Format(argList.ToArray()); } else { context.Value = c.Format(new KeyValuePair <string, object> [0]); } }); }
private static AsyncParserAction HandleElementOpen(TokenPair token, Queue <TokenPair> remainder, ParserOptions options, ScopeData scopeData, InferredTemplateModel scope) { scope = scope?.GetInferredModelForPath(token.Value, InferredTemplateModel.UsedAs.ConditionalValue); var innerTemplate = Parse(remainder, options, scopeData, scope); return(async(builder, context) => { var c = await context.GetContextForPath(token.Value, scopeData); //"falsey" values by Javascript standards... if (await c.Exists()) { await innerTemplate(builder, c); } }); }
private static Action <ByteCounterStreamWriter, ContextObject> HandleElementOpen(TokenPair token, Queue <TokenPair> remainder, ParserOptions options, InferredTemplateModel scope) { scope = scope?.GetInferredModelForPath(token.Value, InferredTemplateModel.UsedAs.ConditionalValue); var innerTemplate = Parse(remainder, options, scope); return((builder, context) => { var c = context.GetContextForPath(token.Value); //"falsey" values by Javascript standards... if (c.Exists()) { innerTemplate(builder, c); } }); }
private static Action <ByteCounterStreamWriter, ContextObject> HandleSingleValue(TokenPair token, ParserOptions options, InferredTemplateModel scope) { scope = scope?.GetInferredModelForPath(token.Value, InferredTemplateModel.UsedAs.Scalar); return((builder, context) => { //try to locate the value in the context, if it exists, append it. var c = context?.GetContextForPath(token.Value); if (c?.Value != null) { if (token.Type == TokenType.EscapedSingleValue && !options.DisableContentEscaping) { HandleContent(HtmlEncodeString(c.ToString()))(builder, c); } else { HandleContent(c.ToString())(builder, c); } } }); }
private static Action <ByteCounterStreamWriter, ContextObject> HandleFormattingValue(TokenPair currentToken, ParserOptions options, InferredTemplateModel scope) { return((builder, context) => { scope = scope?.GetInferredModelForPath(currentToken.Value, InferredTemplateModel.UsedAs.Scalar); if (context == null) { return; } var c = context.GetContextForPath(currentToken.Value); if (currentToken.FormatString != null && currentToken.FormatString.Any()) { var argList = new List <KeyValuePair <string, object> >(); foreach (var formatterArgument in currentToken.FormatString) { object value = null; //if pre and suffixed by a $ its a reference to another field. //walk the path in the $ and use the value in the formatter var trimmedArg = formatterArgument.Argument.Trim(); if (trimmedArg.StartsWith("$") && trimmedArg.EndsWith("$")) { var formatContext = context.GetContextForPath(trimmedArg.Trim('$')); argList.Add(new KeyValuePair <string, object>(formatterArgument.Name, formatContext.Value)); } else { argList.Add(new KeyValuePair <string, object>(formatterArgument.Name, formatterArgument.Argument)); } } context.Value = c.Format(argList.ToArray()); } else { context.Value = c.Format(new KeyValuePair <string, object> [0]); } }); }
private static Action <ByteCounterStreamWriter, ContextObject> PrintFormattedValues(TokenPair currentToken, ParserOptions options, InferredTemplateModel currentScope) { return((builder, context) => { if (context == null) { return; } string value = null; if (context.Value != null) { value = context.ToString(); } HandleContent(value)(builder, context); }); }
private static Action <ByteCounterStreamWriter, ContextObject> ParseFormatting(TokenPair token, Queue <TokenPair> tokens, ParserOptions options, InferredTemplateModel currentScope = null) { var buildArray = new List <Action <ByteCounterStreamWriter, ContextObject> >(); buildArray.Add(HandleFormattingValue(token, options, currentScope)); var nonPrintToken = false; while (tokens.Any() && !nonPrintToken) { var currentToken = tokens.Peek(); switch (currentToken.Type) { case TokenType.Format: buildArray.Add(HandleFormattingValue(tokens.Dequeue(), options, currentScope)); break; case TokenType.PrintFormatted: buildArray.Add(PrintFormattedValues(tokens.Dequeue(), options, currentScope)); break; case TokenType.CollectionOpen: buildArray.Add(HandleCollectionOpen(tokens.Dequeue(), tokens, options, currentScope)); break; default: //The folloring cannot be formatted and the result of the formatting operation has used. //continue with the original Context nonPrintToken = true; break; } } return((builder, context) => { //the formatting will may change the object. Clone the current Context to leave the root one untouched var contextClone = context.Clone(); foreach (var a in buildArray.TakeWhile(e => StopOrAbortBuilding(builder, context))) { a(builder, contextClone); } }); }
private static Tuple <Tokenizer.HeaderTokenMatch, IValueDocumentItem>[] ParseArgumentHeader(TokenPair currentToken) { var argumentMap = new List <Tuple <Tokenizer.HeaderTokenMatch, IValueDocumentItem> >(); foreach (var formatterPart in currentToken.Format?.FormatString ?? new Tokenizer.HeaderTokenMatch[0]) { argumentMap.Add(new Tuple <Tokenizer.HeaderTokenMatch, IValueDocumentItem>(formatterPart, ParseAsPath(formatterPart))); } return(argumentMap.ToArray()); }