public static Func <ScriptScopeContext, object, object> GetMemberExpression(Type targetType, ReadOnlyMemory <char> expression)
        {
            if (targetType == null)
            {
                throw new ArgumentNullException(nameof(targetType));
            }
            if (expression.IsNullOrWhiteSpace())
            {
                throw new ArgumentNullException(nameof(expression));
            }

            var key = targetType.FullName + ':' + expression;

            if (BinderCache.TryGetValue(key, out var fn))
            {
                return(fn);
            }

            BinderCache[key] = fn = Compile(targetType, expression);

            return(fn);
        }
Example #2
0
        public Action <ScriptScopeContext, object, object> GetAssignExpression(Type targetType, ReadOnlyMemory <char> expression)
        {
            if (targetType == null)
            {
                throw new ArgumentNullException(nameof(targetType));
            }
            if (expression.IsNullOrWhiteSpace())
            {
                throw new ArgumentNullException(nameof(expression));
            }

            var key = targetType.FullName + ':' + expression;

            if (AssignExpressionCache.TryGetValue(key, out var fn))
            {
                return(fn);
            }

            AssignExpressionCache[key] = fn = SharpPageUtils.CompileAssign(targetType, expression);

            return(fn);
        }
        public static List <PageFragment> ParseTemplatePage(ReadOnlyMemory <char> text)
        {
            var to = new List <PageFragment>();

            if (text.IsNullOrWhiteSpace())
            {
                return(to);
            }

            int pos;
            var lastPos = 0;

            while ((pos = text.IndexOf("{{", lastPos)) != -1)
            {
                var block = text.Slice(lastPos, pos - lastPos);
                if (!block.IsNullOrEmpty())
                {
                    to.Add(new PageStringFragment(block));
                }

                var varStartPos = pos + 2;

                var firstChar = text.Span[varStartPos];
                if (firstChar == '*') //comment
                {
                    lastPos = text.IndexOf("*}}", varStartPos) + 3;
                }
                else if (firstChar == '#') //block statement
                {
                    var literal = text.Slice(varStartPos + 1);
                    literal = literal.ParseVarName(out var blockNameSpan);

                    var blockName  = blockNameSpan.ToString();
                    var endExprPos = literal.IndexOf("}}");
                    if (endExprPos == -1)
                    {
                        throw new SyntaxErrorException($"Unterminated '{blockName}' block expression, near '{literal.DebugLiteral()}'");
                    }

                    var blockExpr = literal.Slice(0, endExprPos).Trim();
                    literal = literal.Advance(endExprPos + 2);

                    if (!ScriptConfig.DontEvaluateBlocksNamed.Contains(blockName))
                    {
                        literal = literal.ParseStatementBody(blockNameSpan, out var body);
                        var elseStatements = new List <PageElseBlock>();

                        while (literal.StartsWith("{{else"))
                        {
                            literal = literal.ParseElseStatement(blockName, out var elseStatement);
                            elseStatements.Add(elseStatement);
                        }

                        literal = literal.Advance(2 + 1 + blockName.Length + 2);

                        //remove new line after partial block end tag
                        literal = literal.TrimFirstNewLine();

                        var length       = text.Length - pos - literal.Length;
                        var originalText = text.Slice(pos, length);
                        lastPos = pos + length;

                        var statement = new PageBlockFragment(originalText, blockName, blockExpr, body, elseStatements);
                        to.Add(statement);
                    }
                    else
                    {
                        var endBlock    = "{{/" + blockName + "}}";
                        var endBlockPos = literal.IndexOf(endBlock);
                        if (endBlockPos == -1)
                        {
                            throw new SyntaxErrorException($"Unterminated end block '{endBlock}'");
                        }

                        var endBlockBody = literal.Slice(0, endBlockPos);
                        literal = literal.Advance(endBlockPos + endBlock.Length).TrimFirstNewLine();
                        var body = new List <PageFragment> {
                            new PageStringFragment(endBlockBody)
                        };

                        var length       = text.Length - pos - literal.Length;
                        var originalText = text.Slice(pos, length);
                        lastPos = pos + length;

                        var statement = new PageBlockFragment(originalText, blockName, blockExpr, body);
                        to.Add(statement);
                    }
                }
                else
                {
                    var literal = text.Slice(varStartPos).Span;
                    literal = literal.ParseJsExpression(out var expr, filterExpression: true);

                    var filters = new List <JsCallExpression>();

                    if (!literal.StartsWith("}}"))
                    {
                        literal = literal.AdvancePastWhitespace();
                        if (literal.FirstCharEquals(FilterSep))
                        {
                            literal = literal.Advance(1);

                            while (true)
                            {
                                literal = literal.ParseJsCallExpression(out var filter, filterExpression: true);

                                filters.Add(filter);

                                literal = literal.AdvancePastWhitespace();

                                if (literal.IsNullOrEmpty())
                                {
                                    throw new SyntaxErrorException("Unterminated filter expression");
                                }

                                if (literal.StartsWith("}}"))
                                {
                                    literal = literal.Advance(2);
                                    break;
                                }

                                if (!literal.FirstCharEquals(FilterSep))
                                {
                                    throw new SyntaxErrorException(
                                              $"Expected filter separator '|' but was {literal.DebugFirstChar()}");
                                }

                                literal = literal.Advance(1);
                            }
                        }
                        else
                        {
                            if (!literal.IsNullOrEmpty())
                            {
                                literal = literal.Advance(1);
                            }
                        }
                    }
                    else
                    {
                        literal = literal.Advance(2);
                    }

                    var length       = text.Length - pos - literal.Length;
                    var originalText = text.Slice(pos, length);
                    lastPos = pos + length;

                    var varFragment = new PageVariableFragment(originalText, expr, filters);
                    to.Add(varFragment);

                    var newLineLen = literal.StartsWith("\n")
                        ? 1
                        : literal.StartsWith("\r\n")
                            ? 2
                            : 0;

                    if (newLineLen > 0)
                    {
                        var lastExpr   = varFragment.FilterExpressions?.LastOrDefault();
                        var filterName = lastExpr?.Name ??
                                         varFragment?.InitialExpression?.Name ?? varFragment.Binding;
                        if (filterName != null && ScriptConfig.RemoveNewLineAfterFiltersNamed.Contains(filterName))
                        {
                            lastPos += newLineLen;
                        }
                    }
                }
            }

            if (lastPos != text.Length)
            {
                var lastBlock = lastPos == 0 ? text : text.Slice(lastPos);
                to.Add(new PageStringFragment(lastBlock));
            }

            return(to);
        }
Example #4
0
        public static List <PageFragment> ParseTemplate(this ScriptContext context, ReadOnlyMemory <char> text)
        {
            var to = new List <PageFragment>();

            if (text.IsNullOrWhiteSpace())
            {
                return(to);
            }

            int pos;
            var lastPos = 0;

            int nextPos()
            {
                var c1 = text.IndexOf("{{", lastPos);
                var c2 = text.IndexOf("{|", lastPos);

                if (c2 == -1)
                {
                    return(c1);
                }

                return(c1 == -1 ? c2 : c1 < c2 ? c1 : c2);
            }

            while ((pos = nextPos()) != -1)
            {
                var block = text.Slice(lastPos, pos - lastPos);
                if (!block.IsNullOrEmpty())
                {
                    to.Add(new PageStringFragment(block));
                }

                var varStartPos = pos + 2;

                if (varStartPos >= text.Span.Length)
                {
                    throw new SyntaxErrorException($"Unterminated '{{{{' expression, near '{text.Slice(lastPos).DebugLiteral()}'");
                }

                if (text.Span.SafeCharEquals(varStartPos - 1, '|')) // lang expression syntax {|lang ... |} https://flow.org/en/docs/types/objects/#toc-exact-object-types
                {
                    var literal = text.Slice(varStartPos);

                    ScriptLanguage lang = null;
                    if (literal.SafeGetChar(0).IsValidVarNameChar())
                    {
                        literal = literal.ParseVarName(out var langSpan);

                        lang = context.GetScriptLanguage(langSpan.ToString());
                        if (lang != null)
                        {
                            var endPos = literal.IndexOf("|}");
                            if (endPos == -1)
                            {
                                throw new SyntaxErrorException($"Unterminated '|}}' expression, near '{text.Slice(varStartPos).DebugLiteral()}'");
                            }

                            var exprStr          = literal.Slice(0, endPos);
                            var langExprFragment = lang.Parse(context, exprStr);
                            to.AddRange(langExprFragment);
                        }
                    }
                    if (lang == null)
                    {
                        var nextLastPos = text.IndexOf("|}", varStartPos) + 2;
                        block = text.Slice(pos, nextLastPos - pos);
                        if (!block.IsNullOrEmpty())
                        {
                            to.Add(new PageStringFragment(block));
                        }
                    }

                    lastPos = text.IndexOf("|}", varStartPos) + 2;
                    continue;
                }

                var firstChar = text.Span[varStartPos];
                if (firstChar == '*') //comment
                {
                    lastPos = text.IndexOf("*}}", varStartPos) + 3;
                    if (text.Span.SafeCharEquals(lastPos, '\r'))
                    {
                        lastPos++;
                    }
                    if (text.Span.SafeCharEquals(lastPos, '\n'))
                    {
                        lastPos++;
                    }
                }
                else if (firstChar == '#') //block statement
                {
                    var literal = text.Slice(varStartPos + 1);

                    literal = literal.ParseTemplateScriptBlock(context, out var blockFragment);

                    var length = text.Length - pos - literal.Length;
                    blockFragment.OriginalText = text.Slice(pos, length);
                    lastPos = pos + length;

                    to.Add(blockFragment);
                }
                else
                {
                    var literal = text.Slice(varStartPos).Span;
                    literal = literal.ParseJsExpression(out var expr, filterExpression: true);

                    var filters = new List <JsCallExpression>();

                    if (!literal.StartsWith("}}"))
                    {
                        literal = literal.AdvancePastWhitespace();
                        if (literal.FirstCharEquals(FilterSep))
                        {
                            literal = literal.AdvancePastPipeOperator();

                            while (true)
                            {
                                literal = literal.ParseJsCallExpression(out var filter, filterExpression: true);

                                filters.Add(filter);

                                literal = literal.AdvancePastWhitespace();

                                if (literal.IsNullOrEmpty())
                                {
                                    throw new SyntaxErrorException("Unterminated filter expression");
                                }

                                if (literal.StartsWith("}}"))
                                {
                                    literal = literal.Advance(2);
                                    break;
                                }

                                if (!literal.FirstCharEquals(FilterSep))
                                {
                                    throw new SyntaxErrorException(
                                              $"Expected pipeline operator '|>' but was {literal.DebugFirstChar()}");
                                }

                                literal = literal.AdvancePastPipeOperator();
                            }
                        }
                        else if (!literal.AdvancePastWhitespace().IsNullOrEmpty())
                        {
                            throw new SyntaxErrorException($"Unexpected syntax '{literal.ToString()}', Expected pipeline operator '|>'");
                        }
                    }
                    else
                    {
                        literal = literal.Advance(2);
                    }

                    var length       = text.Length - pos - literal.Length;
                    var originalText = text.Slice(pos, length);
                    lastPos = pos + length;

                    var varFragment = new PageVariableFragment(originalText, expr, filters);
                    to.Add(varFragment);

                    var newLineLen = literal.StartsWith("\n")
                        ? 1
                        : literal.StartsWith("\r\n")
                            ? 2
                            : 0;

                    if (newLineLen > 0)
                    {
                        var lastExpr   = varFragment.FilterExpressions?.LastOrDefault();
                        var filterName = lastExpr?.Name ??
                                         varFragment?.InitialExpression?.Name ?? varFragment.Binding;
                        if ((filterName != null && context.RemoveNewLineAfterFiltersNamed.Contains(filterName)) ||
                            expr is JsVariableDeclaration)
                        {
                            lastPos += newLineLen;
                        }
                    }
                }
            }

            if (lastPos != text.Length)
            {
                var lastBlock = lastPos == 0 ? text : text.Slice(lastPos);
                to.Add(new PageStringFragment(lastBlock));
            }

            return(to);
        }
        public static List <PageFragment> ParseTemplate(this ScriptContext context, ReadOnlyMemory <char> text)
        {
            var to = new List <PageFragment>();

            if (text.IsNullOrWhiteSpace())
            {
                return(to);
            }

            int pos;
            var lastPos = 0;

            while ((pos = text.IndexOf("{{", lastPos)) != -1)
            {
                var block = text.Slice(lastPos, pos - lastPos);
                if (!block.IsNullOrEmpty())
                {
                    to.Add(new PageStringFragment(block));
                }

                var varStartPos = pos + 2;

                if (varStartPos >= text.Span.Length)
                {
                    throw new SyntaxErrorException($"Unterminated '{{{{' expression, near '{text.Slice(lastPos).DebugLiteral()}'");
                }

                var firstChar = text.Span[varStartPos];
                if (firstChar == '*') //comment
                {
                    lastPos = text.IndexOf("*}}", varStartPos) + 3;
                    if (text.Span.SafeCharEquals(lastPos, '\r'))
                    {
                        lastPos++;
                    }
                    if (text.Span.SafeCharEquals(lastPos, '\n'))
                    {
                        lastPos++;
                    }
                }
                else if (firstChar == '#') //block statement
                {
                    var literal = text.Slice(varStartPos + 1);

                    literal = literal.ParseTemplateScriptBlock(context, out var blockFragment);

                    var length = text.Length - pos - literal.Length;
                    blockFragment.OriginalText = text.Slice(pos, length);
                    lastPos = pos + length;

                    to.Add(blockFragment);
                }
                else
                {
                    var literal = text.Slice(varStartPos).Span;
                    literal = literal.ParseJsExpression(out var expr, filterExpression: true);

                    var filters = new List <JsCallExpression>();

                    if (!literal.StartsWith("}}"))
                    {
                        literal = literal.AdvancePastWhitespace();
                        if (literal.FirstCharEquals(FilterSep))
                        {
                            literal = literal.Advance(1);

                            while (true)
                            {
                                literal = literal.ParseJsCallExpression(out var filter, filterExpression: true);

                                filters.Add(filter);

                                literal = literal.AdvancePastWhitespace();

                                if (literal.IsNullOrEmpty())
                                {
                                    throw new SyntaxErrorException("Unterminated filter expression");
                                }

                                if (literal.StartsWith("}}"))
                                {
                                    literal = literal.Advance(2);
                                    break;
                                }

                                if (!literal.FirstCharEquals(FilterSep))
                                {
                                    throw new SyntaxErrorException(
                                              $"Expected filter separator '|' but was {literal.DebugFirstChar()}");
                                }

                                literal = literal.Advance(1);
                            }
                        }
                        else
                        {
                            if (!literal.IsNullOrEmpty())
                            {
                                literal = literal.Advance(1);
                            }
                        }
                    }
                    else
                    {
                        literal = literal.Advance(2);
                    }

                    var length       = text.Length - pos - literal.Length;
                    var originalText = text.Slice(pos, length);
                    lastPos = pos + length;

                    var varFragment = new PageVariableFragment(originalText, expr, filters);
                    to.Add(varFragment);

                    var newLineLen = literal.StartsWith("\n")
                        ? 1
                        : literal.StartsWith("\r\n")
                            ? 2
                            : 0;

                    if (newLineLen > 0)
                    {
                        var lastExpr   = varFragment.FilterExpressions?.LastOrDefault();
                        var filterName = lastExpr?.Name ??
                                         varFragment?.InitialExpression?.Name ?? varFragment.Binding;
                        if (filterName != null && context.RemoveNewLineAfterFiltersNamed.Contains(filterName))
                        {
                            lastPos += newLineLen;
                        }
                    }
                }
            }

            if (lastPos != text.Length)
            {
                var lastBlock = lastPos == 0 ? text : text.Slice(lastPos);
                to.Add(new PageStringFragment(lastBlock));
            }

            return(to);
        }