Exemple #1
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);
        }
Exemple #2
0
        public async Task <SharpPage> Load()
        {
            if (IsImmutable)
            {
                return(this);
            }

            string contents;

            using (var stream = File.OpenRead())
            {
                contents = await stream.ReadToEndAsync();
            }

            foreach (var preprocessor in Context.Preprocessors)
            {
                contents = preprocessor(contents);
            }

            var lastModified = File.LastModified;
            var fileContents = contents.AsMemory();
            var pageVars     = new Dictionary <string, object>();

            var pos          = 0;
            var bodyContents = fileContents;

            fileContents.AdvancePastWhitespace().TryReadLine(out ReadOnlyMemory <char> line, ref pos);
            var lineComment = ScriptLanguage.LineComment;

            if (line.StartsWith(Format.ArgsPrefix) || (lineComment != null && line.StartsWith(lineComment + Format.ArgsPrefix)))
            {
                while (fileContents.TryReadLine(out line, ref pos))
                {
                    if (line.Trim().Length == 0)
                    {
                        continue;
                    }


                    if (line.StartsWith(Format.ArgsSuffix) || (lineComment != null && line.StartsWith(lineComment + Format.ArgsSuffix)))
                    {
                        break;
                    }

                    if (lineComment != null && line.StartsWith(lineComment))
                    {
                        line = line.Slice(lineComment.Length).TrimStart();
                    }

                    var colonPos = line.IndexOf(':');
                    var spacePos = line.IndexOf(' ');
                    var bracePos = line.IndexOf('{');
                    var sep      = colonPos >= 0 ? ':' : ' ';

                    if (bracePos > 0 && spacePos > 0 && colonPos > spacePos)
                    {
                        sep = ' ';
                    }

                    line.SplitOnFirst(sep, out var first, out var last);

                    var key = first.Trim().ToString();
                    pageVars[key] = !last.IsEmpty ? last.Trim().ToString() : "";
                }

                //When page has variables body starts from first non whitespace after variables end
                var argsSuffixPos = line.LastIndexOf(Format.ArgsSuffix);
                if (argsSuffixPos >= 0)
                {
                    //Start back from the end of the ArgsSuffix
                    pos -= line.Length - argsSuffixPos - Format.ArgsSuffix.Length;
                }
                bodyContents = fileContents.SafeSlice(pos).AdvancePastWhitespace();
            }

            var pageFragments = pageVars.TryGetValue("ignore", out object ignore) &&
                                ("page".Equals(ignore.ToString()) || "template".Equals(ignore.ToString()))
                ? new List <PageFragment> {
                new PageStringFragment(bodyContents)
            }
                : ScriptLanguage.Parse(Context, bodyContents);

            foreach (var fragment in pageFragments)
            {
                if (fragment is PageVariableFragment var && var.Binding == ScriptConstants.Page)
                {
                    IsLayout = true;
                    break;
                }
            }

            lock (semaphore)
            {
                LastModified      = lastModified;
                LastModifiedCheck = DateTime.UtcNow;
                FileContents      = fileContents;
                Args          = pageVars;
                BodyContents  = bodyContents;
                PageFragments = pageFragments.ToArray();

                HasInit    = true;
                LayoutPage = Format.ResolveLayout(this);
            }

            if (LayoutPage != null)
            {
                if (!LayoutPage.HasInit)
                {
                    await LayoutPage.Load();
                }
                else
                {
                    if (Context.DebugMode || Context.CheckForModifiedPagesAfter != null &&
                        DateTime.UtcNow - LayoutPage.LastModifiedCheck >= Context.CheckForModifiedPagesAfter.Value)
                    {
                        LayoutPage.File.Refresh();
                        LayoutPage.LastModifiedCheck = DateTime.UtcNow;
                        if (LayoutPage.File.LastModified != LayoutPage.LastModified)
                        {
                            await LayoutPage.Load();
                        }
                    }
                }
            }

            return(this);
        }
Exemple #3
0
        public static List <PageFragment> ParseScript(this ScriptContext context, ReadOnlyMemory <char> text)
        {
            var                   to             = new List <PageFragment>();
            ScriptLanguage        scriptLanguage = null;
            ReadOnlyMemory <char> modifiers      = default;
            ReadOnlyMemory <char> prevBlock      = default;
            int                   startBlockPos  = -1;
            var                   cursorPos      = 0;
            var                   lastBlockPos   = 0;

            const int delim = 3; // '```'.length

            while (text.TryReadLine(out var line, ref cursorPos))
            {
                var lineLength = line.Length;
                line = line.AdvancePastWhitespace();

                if (line.StartsWith("```"))
                {
                    if (scriptLanguage != null && startBlockPos >= 0 && line.Slice(delim).AdvancePastWhitespace().IsEmpty) //is end block
                    {
                        var templateFragments = ScriptTemplate.Language.Parse(context, prevBlock);
                        to.AddRange(templateFragments);

                        var blockBody      = text.ToLineStart(cursorPos, lineLength, startBlockPos);
                        var blockFragments = scriptLanguage.Parse(context, blockBody, modifiers);
                        to.AddRange(blockFragments);

                        prevBlock      = default;
                        startBlockPos  = -1;
                        scriptLanguage = null;
                        modifiers      = null;
                        lastBlockPos   = cursorPos;
                        continue;
                    }

                    if (line.SafeGetChar(delim).IsValidVarNameChar())
                    {
                        line = line.Slice(delim).ParseVarName(out var blockNameSpan);

                        var blockName = blockNameSpan.ToString();
                        scriptLanguage = context.GetScriptLanguage(blockName);
                        if (scriptLanguage == null)
                        {
                            continue;
                        }

                        modifiers = line.AdvancePastChar('|');
                        var delimLen = text.Span.SafeCharEquals(cursorPos - 2, '\r') ? 2 : 1;
                        prevBlock     = text.Slice(lastBlockPos, cursorPos - lastBlockPos - lineLength - delimLen);
                        startBlockPos = cursorPos;
                    }
                }
            }

            var remainingBlock = text.Slice(lastBlockPos);

            if (!remainingBlock.IsEmpty)
            {
                var templateFragments = ScriptTemplate.Language.Parse(context, remainingBlock);
                to.AddRange(templateFragments);
            }

            return(to);
        }