Пример #1
0
 /// <summary>
 ///     ctor
 /// </summary>
 /// <param name="inferredModel"></param>
 /// <param name="parserOptions"></param>
 /// <param name="tokens"></param>
 internal ExtendedParseInformation(InferredTemplateModel inferredModel, ParserOptions parserOptions, Queue <TokenPair> tokens)
 {
     InferredModel    = inferredModel;
     ParserOptions    = parserOptions;
     TemplateTokens   = tokens;
     InternalTemplate = new Lazy <Action <Parser.ByteCounterStreamWriter, ContextObject> >(() => Parser.Parse(TemplateTokens, ParserOptions, ParserOptions.WithModelInference ? InferredModel : null));
 }
Пример #2
0
        internal static Action <ByteCounterStreamWriter, ContextObject> Parse(Queue <TokenPair> tokens, ParserOptions options,
                                                                              InferredTemplateModel currentScope = null)
        {
            var buildArray = new List <Action <ByteCounterStreamWriter, ContextObject> >();

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

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

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

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

                case TokenType.InvertedElementOpen:
                    buildArray.Add(HandleInvertedElementOpen(currentToken, tokens, options, 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((builder, context) =>
                    {
                        foreach (var a in buildArray.TakeWhile(e => StopOrAbortBuilding(builder, context)))
                        {
                            a(builder, context);
                        }
                    });

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

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

            return((builder, context) =>
            {
                foreach (var a in buildArray.TakeWhile(e => StopOrAbortBuilding(builder, context)))
                {
                    a(builder, context);
                }
            });
        }
Пример #3
0
        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);
                    }
                }
            });
        }
Пример #4
0
        public static ExtendedParseInformation ParseWithOptions([NotNull] ParserOptions parsingOptions)
        {
            if (parsingOptions == null)
            {
                throw new ArgumentNullException(nameof(parsingOptions));
            }

            if (parsingOptions.SourceFactory == null)
            {
                throw new ArgumentNullException(nameof(parsingOptions), "The given Stream is null");
            }

            var tokens        = new Queue <TokenPair>(Tokenizer.Tokenize(parsingOptions));
            var inferredModel = new InferredTemplateModel();

            var extendedParseInformation = new ExtendedParseInformation(inferredModel, parsingOptions, tokens);

            if (parsingOptions.WithModelInference)
            {
                //we preparse the template once to get the model
                var s = extendedParseInformation.InternalTemplate.Value;
            }

            return(extendedParseInformation);
        }
 /// <summary>
 ///     ctor
 /// </summary>
 /// <param name="inferredModel"></param>
 /// <param name="parserOptions"></param>
 /// <param name="tokens"></param>
 internal ExtendedParseInformation(InferredTemplateModel inferredModel, ParserOptions parserOptions, Queue <TokenPair> tokens)
 {
     InferredModel    = inferredModel;
     ParserOptions    = parserOptions;
     TemplateTokens   = tokens;
     InternalTemplate = new Lazy <Parser.AsyncParserAction>(
         () => Parser.Parse(TemplateTokens, ParserOptions, new Parser.ScopeData(), ParserOptions.WithModelInference ? InferredModel : null));
 }
Пример #6
0
        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);
                }
            });
        }
Пример #7
0
        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
        }
Пример #8
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);
                }
            });
        }
Пример #9
0
        private InferredTemplateModel GetContextForPath(Queue <string> elements)
        {
            var retval = this;

            if (elements.Any())
            {
                var element = elements.Dequeue();
                if (element.StartsWith(".."))
                {
                    if (Parent != null)
                    {
                        retval = Parent.GetContextForPath(elements);
                    }
                    else
                    {
                        //Calling "../" too much may be "ok" in that if we're at root,
                        //we may just stop recursion and traverse down the path.
                        retval = GetContextForPath(elements);
                    }
                }
                //TODO: handle array accessors and maybe "special" keys.
                else
                {
                    //ALWAYS return the context, even if the value is null.

                    InferredTemplateModel innerContext = null;
                    if (!Children.TryGetValue(element, out innerContext))
                    {
                        innerContext        = new InferredTemplateModel();
                        innerContext.Key    = element;
                        innerContext.Parent = this;
                        Children[element]   = innerContext;
                    }

                    retval = innerContext.GetContextForPath(elements);
                }
            }

            return(retval);
        }
Пример #10
0
        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);
                }
            });
        }
Пример #11
0
        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);
                    }
                }
            });
        }
Пример #12
0
        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]);
                }
            });
        }
Пример #13
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);
            });
        }
Пример #14
0
        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);
                }
            });
        }
Пример #15
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);
            });
        }
Пример #16
0
        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]);
                }
            });
        }
Пример #17
0
        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);
            });
        }