Пример #1
0
            /// <summary>Parse a template description into tokens</summary>
            private static IEnumerable <Token> Parse(Src src, bool root = false)
            {
                // Recursively build the token tree
                for (; Extract.AdvanceToNonDelim(src, " \t\r\n");)
                {
                    switch (src)
                    {
                    case TemplateMark:
                    {
                        src.Next();                                 // Skip the '*'
                        var flags = ETemplateFlags.Recursive;
                        if (!root)
                        {
                            flags |= ETemplateFlags.Hidden;
                            flags &= ~ETemplateFlags.Recursive;
                        }
                        if (src == HiddenMark)
                        {
                            src.Next();
                            flags |= ETemplateFlags.Hidden;
                            flags &= ~ETemplateFlags.Recursive;
                        }
                        if (src == RootLevelOnlyMark)
                        {
                            src.Next();
                            flags |= ETemplateFlags.RootLevelOnly;
                            flags &= ~ETemplateFlags.Recursive;
                        }

                        // Read the template keyword
                        var keyword = Extract.Identifier(out var kw, src) ? kw : throw new ScriptException(Script.EResult.InvalidValue, src.Location, $"Invalid template keyword");

                        // Buffer the remaining template description
                        var len = BufferTemplateDeclaration(src);

                        // Create the template instance
                        var remaining = src.Limit - len;
                        using (var limit = Scope.Create(() => src.Limit = len, () => src.Limit = remaining))
                            yield return(new Template(keyword, flags, Parse(src)));

                        break;
                    }

                    case ReferenceMark:
                    case ExpandMark:
                    {
                        // Skip the '@' or '$'
                        var expand = src == ExpandMark;
                        src.Next();

                        // Read the keyword of the referenced template
                        var keyword = Extract.Identifier(out var kw, src) ? kw : throw new ScriptException(Script.EResult.InvalidValue, src.Location, $"Invalid template keyword");
                        yield return(new TemplateRef(keyword, expand));

                        break;
                    }

                    case '{':
                    case '}':
                    {
                        if (src == '{')
                        {
                            src.Next();
                        }
                        else
                        {
                            throw new ScriptException(Script.EResult.TokenNotFound, src.Location, "Unmatched '}' token");
                        }

                        // Buffer the content within the section
                        int len = 0;
                        for (var nest = 1; src[len] != 0 && nest != 0; ++len)
                        {
                            nest += src[len] == '{' ? 1 : 0;
                            nest -= src[len] == '}' ? 1 : 0;
                        }
                        if (src[len - 1] != '}')
                        {
                            throw new ScriptException(Script.EResult.TokenNotFound, src.Location, "Unmatched '{' token");
                        }

                        var remaining = src.Limit - (len - 1);
                        using (var limit = Scope.Create(() => src.Limit = len - 1, () => src.Limit = remaining))
                            yield return(new Section(Parse(src)));

                        src.Next();
                        break;
                    }

                    case '[':
                    case ']':
                    {
                        if (src == '[')
                        {
                            src.Next();
                        }
                        else
                        {
                            throw new ScriptException(Script.EResult.TokenNotFound, src.Location, "Unmatched ']' token");
                        }

                        // Buffer the content within the optional
                        int len = 0;
                        for (var nest = 1; src[len] != 0 && nest != 0; ++len)
                        {
                            nest += src[len] == '[' ? 1 : 0;
                            nest -= src[len] == ']' ? 1 : 0;
                        }
                        if (src[len - 1] != ']')
                        {
                            throw new ScriptException(Script.EResult.TokenNotFound, src.Location, "Unmatched '[' token");
                        }

                        var remaining = src.Limit - (len - 1);
                        using (var limit = Scope.Create(() => src.Limit = len - 1, () => src.Limit = remaining))
                            yield return(new Optional(Parse(src)));

                        src.Next();
                        break;
                    }

                    case '(':
                    case ')':
                    {
                        if (src == '(')
                        {
                            src.Next();
                        }
                        else
                        {
                            throw new ScriptException(Script.EResult.TokenNotFound, src.Location, "Unmatched ')' token");
                        }

                        // Buffer the content within the repeat
                        int len = 0;
                        for (var nest = 1; src[len] != 0 && nest != 0; ++len)
                        {
                            nest += src[len] == '(' ? 1 : 0;
                            nest -= src[len] == ')' ? 1 : 0;
                        }
                        if (src[len - 1] != ')')
                        {
                            throw new ScriptException(Script.EResult.TokenNotFound, src.Location, $"Unmatched '(' token");
                        }

                        var remaining = src.Limit - (len - 1);
                        using (var limit = Scope.Create(() => src.Limit = len - 1, () => src.Limit = remaining))
                            yield return(new Repeat(Parse(src)));

                        src.Next();
                        break;
                    }

                    case '<':
                    case '>':
                    {
                        if (src == '<')
                        {
                            src.Next();
                        }
                        else
                        {
                            throw new ScriptException(Script.EResult.TokenNotFound, src.Location, $"Unmatched '>' token");
                        }
                        var name = Extract.Identifier(out var kw, src) ? kw : throw new ScriptException(Script.EResult.InvalidValue, src.Location, $"Invalid field identifier");
                        if (src != '>')
                        {
                            throw new ScriptException(Script.EResult.TokenNotFound, src.Location, $"Unmatched '<' token");
                        }
                        yield return(new Field(name));

                        src.Next();
                        break;
                    }

                    case '|':
                    {
                        yield return(new Select());

                        src.Next();
                        break;
                    }

                    case ';':
                    {
                        yield return(new LineBreak());

                        src.Next();
                        break;
                    }

                    default:
                    {
                        // Literal text
                        Extract.BufferWhile(src, (s, i) => !" \n*@{}[]()<>|".Contains(s[i]) ? 1 : 0, 0, out var len);
                        yield return(new Literal(src.Buffer.ToString(0, len)));

                        src.Next(len);
                        break;
                    }
                    }
                }
            }