Esempio n. 1
0
        /// <summary>
        /// Constructs a SyntaxList&lt;SeparatedElement&lt;TElement&gt;&gt; from a list of items and separators.
        /// </summary>
        public static SyntaxList <SeparatedElement <TElement> > MakeSeparatedList <TElement>(IReadOnlyList <object> elements)
            where TElement : SyntaxElement
        {
            if (elements == null)
            {
                return(SyntaxList <SeparatedElement <TElement> > .Empty());
            }

            var separatedElements = new List <SeparatedElement <TElement> >();

            for (int i = 0; i < elements.Count; i += 2)
            {
                var element   = (TElement)elements[i];
                var separator = (i < elements.Count - 1) ? (SyntaxToken)elements[i + 1] : null;
                separatedElements.Add(new SeparatedElement <TElement>(element, separator));
            }

            return(new SyntaxList <SeparatedElement <TElement> >(separatedElements));
        }
        /// <summary>
        /// Creates a parser that parsers command grammars to produce command parsers.. obviously.
        /// </summary>
        private static Parser <char, Parser <LexicalToken, SyntaxElement> > CreateCommandGrammarParser(
            GlobalState globals,
            Parser <LexicalToken, Command> command)
        {
            var q = Q.From(globals);

            var StringName =
                Rule(Token(SyntaxKind.StringLiteralToken), token => (Name) new TokenName(token));

            var GuidLiteral =
                Convert(
                    And(
                        Match(t => IsHex(t.Text)),
                        ZeroOrMore(
                            And(
                                Match(t => t.Kind == SyntaxKind.MinusToken && t.Trivia.Length == 0),
                                Match(t => IsHex(t.Text) && t.Trivia.Length == 0)))),
                    (IReadOnlyList <LexicalToken> list) =>
            {
                var text = string.Concat(list.Select(e => e.Text));
                return((Expression) new LiteralExpression(SyntaxKind.GuidLiteralExpression,
                                                          SyntaxToken.Literal(list[0].Trivia, text, SyntaxKind.GuidLiteralToken)));
            }).WithTag("<guid>");

            var Name =
                First(
                    q.IdentifierName,
                    q.BrackettedName,
                    q.BracedName,
                    StringName)
                .WithTag("<name>");

            var ColumnNameReference =
                Rule(Name, name => (Expression) new NameReference(name, SymbolMatch.Column))
                .WithCompletionHint(Editor.CompletionHint.Column)
                .WithTag("<column>");

            var TableNameReference =
                Rule(Name, name => (Expression) new NameReference(name, SymbolMatch.Table))
                .WithCompletionHint(Editor.CompletionHint.Table)
                .WithTag("<table>");

            var DatabaseNameReference =
                Rule(Name, name => (Expression) new NameReference(name, SymbolMatch.Database))
                .WithCompletionHint(Editor.CompletionHint.Database)
                .WithTag("<database>");

            var ClusterNameReference =
                Rule(Name, name => (Expression) new NameReference(name, SymbolMatch.Cluster))
                .WithCompletionHint(Editor.CompletionHint.Cluster)
                .WithTag("<cluster>");

            var DatabaseFunctionNameReference =
                Rule(Name, name => (Expression) new NameReference(name, SymbolMatch.Function))
                .WithCompletionHint(Editor.CompletionHint.DatabaseFunction)
                .WithTag("<function>");

            var DatabaseOrTableReference =
                Rule(Name, name => (Expression) new NameReference(name, SymbolMatch.Database | SymbolMatch.Table))
                .WithCompletionHint(Editor.CompletionHint.Database | Editor.CompletionHint.Table)
                .WithTag("<database_or_table>");

            var DatabaseTableNameReference =
                ApplyZeroOrMore(
                    DatabaseOrTableReference,
                    _left =>
                    Rule(_left, Token(".").Hide(), Required(TableNameReference, Q.MissingNameReference),
                         (expr, dot, selector) => (Expression) new PathExpression(expr, dot, selector)))
                .WithTag("<database_table>");

            var DatabaseOrTableOrColumnReference =
                Rule(Name, name => (Expression) new NameReference(name, SymbolMatch.Database | SymbolMatch.Table | SymbolMatch.Column))
                .WithCompletionHint(Editor.CompletionHint.Database | Editor.CompletionHint.Table | Editor.CompletionHint.Column)
                .WithTag("<database_or_table_or_column>");

            var DatabaseTableColumnNameReference =
                ApplyZeroOrMore(
                    DatabaseOrTableOrColumnReference,
                    _left =>
                    Rule(_left, Token(".").Hide(), Required(DatabaseOrTableOrColumnReference, Q.MissingNameReference),
                         (expr, dot, selector) => (Expression) new PathExpression(expr, dot, selector)))
                .WithTag("<database_table_column>");

            var TableOrColumnReference =
                Rule(Name, name => (Expression) new NameReference(name, SymbolMatch.Table | SymbolMatch.Column))
                .WithCompletionHint(Editor.CompletionHint.Table | Editor.CompletionHint.Column)
                .WithTag("<table_or_column>");

            var TableColumnNameReference =
                ApplyZeroOrMore(
                    TableOrColumnReference,
                    _left =>
                    Rule(_left, Token(".").Hide(), Required(TableOrColumnReference, Q.MissingNameReference),
                         (expr, dot, selector) => (Expression) new PathExpression(expr, dot, selector)))
                .WithTag("<table_column>");

            var KustoStringLiteralInfo =
                new ParserInfo(
                    q.StringLiteral.Cast <SyntaxElement>(),
                    new CustomElementDescriptor(hint: Editor.CompletionHint.Literal),
                    () => (SyntaxElement)Q.MissingStringLiteral());

            var KustoGuidLiteralInfo =
                new ParserInfo(
                    GuidLiteral.Cast <SyntaxElement>(),
                    new CustomElementDescriptor(hint: Editor.CompletionHint.Literal),
                    () => (SyntaxElement)Q.MissingValue());

            var KustoValueInfo =
                new ParserInfo(
                    First(GuidLiteral.Cast <SyntaxElement>(), q.Literal.Cast <SyntaxElement>()),
                    new CustomElementDescriptor(hint: Editor.CompletionHint.Literal),
                    () => (SyntaxElement)Q.MissingValue());

            var KustoTypeInfo =
                new ParserInfo(
                    q.ParamTypeExtended.Cast <SyntaxElement>(),
                    new CustomElementDescriptor(hint: Editor.CompletionHint.Syntax),
                    () => (SyntaxElement)Q.MissingType());

            var NameDeclarationOrStringLiteral =
                First(
                    q.SimpleNameDeclarationExpression,
                    q.StringLiteral);

            var KustoNameDeclarationInfo =
                new ParserInfo(
                    NameDeclarationOrStringLiteral.Cast <SyntaxElement>(),
                    new CustomElementDescriptor(hint: Editor.CompletionHint.None),
                    () => (SyntaxElement)Q.MissingNameDeclaration());

            var KustoColumnNameInfo =
                new ParserInfo(
                    ColumnNameReference.Cast <SyntaxElement>(),
                    new CustomElementDescriptor(hint: Editor.CompletionHint.Column),
                    () => (SyntaxElement)Q.MissingNameReference());

            var KustoTableNameInfo =
                new ParserInfo(
                    TableNameReference.Cast <SyntaxElement>(),
                    new CustomElementDescriptor(hint: Editor.CompletionHint.Table),
                    () => (SyntaxElement)Q.MissingNameReference());

            var KustoDatabaseNameInfo =
                new ParserInfo(
                    DatabaseNameReference.Cast <SyntaxElement>(),
                    new CustomElementDescriptor(hint: Editor.CompletionHint.Database),
                    () => (SyntaxElement)Q.MissingNameReference());

            var KustoDatabaseTableNameInfo =
                new ParserInfo(
                    DatabaseTableNameReference.Cast <SyntaxElement>(),
                    new CustomElementDescriptor(hint: Editor.CompletionHint.Table),
                    () => (SyntaxElement)Q.MissingNameReference());

            var KustoClusterNameInfo =
                new ParserInfo(
                    ClusterNameReference.Cast <SyntaxElement>(),
                    new CustomElementDescriptor(hint: Editor.CompletionHint.Cluster),
                    () => (SyntaxElement)Q.MissingNameReference());

            var KustoFunctionNameInfo =
                new ParserInfo(
                    DatabaseFunctionNameReference.Cast <SyntaxElement>(),
                    new CustomElementDescriptor(Editor.CompletionHint.Function),
                    () => (SyntaxElement)Q.MissingNameReference());

            var KustoDatabaseTableColumnNameInfo =
                new ParserInfo(
                    DatabaseTableColumnNameReference.Cast <SyntaxElement>(),
                    new CustomElementDescriptor(hint: Editor.CompletionHint.Column),
                    () => (SyntaxElement)Q.MissingNameReference());

            var KustoTableColumnNameInfo =
                new ParserInfo(
                    TableColumnNameReference.Cast <SyntaxElement>(),
                    new CustomElementDescriptor(hint: Editor.CompletionHint.Column),
                    () => (SyntaxElement)Q.MissingNameReference());

            var KustoFunctionDeclaration =
                new ParserInfo(
                    Rule(
                        q.FunctionParameters,
                        q.FunctionBody,
                        (p, b) => (SyntaxElement) new FunctionDeclaration(null, p, b)),
                    new CustomElementDescriptor(hint: Editor.CompletionHint.Syntax),
                    () => new FunctionDeclaration(
                        null,
                        new FunctionParameters(
                            CreateMissingToken(SyntaxKind.OpenParenToken),
                            SyntaxList <SeparatedElement <FunctionParameter> > .Empty(),
                            CreateMissingToken(SyntaxKind.CloseParenToken)),
                        new FunctionBody(
                            CreateMissingToken(SyntaxKind.OpenBraceToken),
                            SyntaxList <SeparatedElement <Statement> > .Empty(),
                            null,
                            null,
                            CreateMissingToken(SyntaxKind.CloseBraceToken))));

            var CommandInput =
                First(
                    If(Token(SyntaxKind.DotToken),
                       command.Cast <SyntaxElement>()),
                    q.StatementList.Cast <SyntaxElement>());

            var KustoCommandInputInfo =
                new ParserInfo(
                    CommandInput,
                    new CustomElementDescriptor(CompletionHint.Tabular),
                    () => (SyntaxElement)Q.MissingExpression());

            var InputTextTokens = ZeroOrMore(AnyTokenButEnd);

            SourceProducer <LexicalToken, SyntaxToken> InputTextBuilder = (source, start, length) =>
            {
                if (length > 0)
                {
                    var builder = new StringBuilder();
                    var token   = source.Peek(start);
                    var trivia  = token.Trivia;
                    builder.Append(source.Peek(start).Text);

                    for (int i = 1; i < length; i++)
                    {
                        token = source.Peek(start + i);
                        builder.Append(token.Trivia);
                        builder.Append(token.Text);
                    }

                    return(SyntaxToken.Other(trivia, builder.ToString(), SyntaxKind.InputTextToken));
                }
                else
                {
                    return(SyntaxToken.Other("", "", SyntaxKind.InputTextToken));
                }
            };

            var InputText =
                Convert(InputTextTokens, InputTextBuilder);

            var KustoInputText =
                new ParserInfo(
                    InputText.Cast <SyntaxElement>(),
                    new CustomElementDescriptor(CompletionHint.None),
                    () => (SyntaxElement)SyntaxToken.Other("", "", SyntaxKind.InputTextToken));

            var BrackettedInputTextTokens =
                ZeroOrMore(Not(Token("]")));

            var BrackettedInputText =
                Convert(BrackettedInputTextTokens, InputTextBuilder);

            var KustoBrackettedInputText =
                new ParserInfo(
                    BrackettedInputText.Cast <SyntaxElement>(),
                    new CustomElementDescriptor(CompletionHint.None),
                    () => (SyntaxElement)SyntaxToken.Other("", "", SyntaxKind.InputTextToken));

            var grammar = GrammarGrammar.CreateParser(
                getRule: name => {
                switch (name)
                {
                case "value": return(KustoValueInfo);

                case "timespan": return(KustoValueInfo);

                case "datetime": return(KustoValueInfo);

                case "string": return(KustoStringLiteralInfo);

                case "bool": return(KustoValueInfo);

                case "long": return(KustoValueInfo);

                case "int": return(KustoValueInfo);

                case "decimal": return(KustoValueInfo);

                case "real": return(KustoValueInfo);

                case "type": return(KustoTypeInfo);

                case "guid": return(KustoGuidLiteralInfo);

                case "name": return(KustoNameDeclarationInfo);

                case "column": return(KustoColumnNameInfo);

                case "table_column": return(KustoTableColumnNameInfo);

                case "database_table_column": return(KustoDatabaseTableColumnNameInfo);

                case "table": return(KustoTableNameInfo);

                case "database_table": return(KustoDatabaseTableNameInfo);

                case "database": return(KustoDatabaseNameInfo);

                case "cluster": return(KustoClusterNameInfo);

                case "function": return(KustoFunctionNameInfo);

                case "function_declaration": return(KustoFunctionDeclaration);

                case "input_query": return(KustoCommandInputInfo);

                case "input_data": return(KustoInputText);

                case "bracketted_input_data": return(KustoBrackettedInputText);

                default: return(null);
                }
            },

                createTerm: textAndOffset =>
                new ParserInfo(
                    Token(textAndOffset.Value, GetCompletionKind(textAndOffset)).Cast <SyntaxElement>(),
                    new CustomElementDescriptor(hint: Editor.CompletionHint.Syntax),
                    () => CreateMissingToken(textAndOffset.Value),
                    isTerm: true),

                createOptional: elem =>
                new ParserInfo(
                    Optional(elem.Parser),
                    new CustomElementDescriptor(elem.Element.CompletionHint, isOptional: true),
                    elem.Missing),

                createRequired: elem =>
                new ParserInfo(
                    Required(elem.Parser, elem.Missing),
                    elem.Element,
                    elem.Missing),

                createTagged: (elem, tag) =>
                elem.WithElement(elem.Element.WithName(tag)),

                createSequence: list =>
            {
                if (list.Count == 1)
                {
                    return(list[0]);
                }
                else
                {
                    var shape = list.Select(t => t.Element).ToArray();

                    var parsers = new List <Parser <LexicalToken> >();

                    bool required = false;
                    for (int i = 0; i < list.Count; i++)
                    {
                        var t = list[i];

                        // everything after first element or first sequence of terms will be required
                        // this enables building appropriate custom nodes with missing elements that cue correct completion.
                        var notRequired = i == 0 || (i == 1 && t.IsTerm);
                        required       |= !notRequired;

                        var p = required && !t.Parser.IsOptional
                                ? Required(t.Parser, t.Missing)
                                : t.Parser;

                        parsers.Add(p);
                    }

                    var parser = Produce <SyntaxElement>(
                        Sequence(parsers.ToArray()),
                        (IReadOnlyList <object> elemList) =>
                        new CustomNode(shape, elemList.Cast <SyntaxElement>().ToArray()));

                    return(new ParserInfo(
                               parser,
                               list[0].Element,
                               () => new CustomNode(shape, list.Select(t => t.Missing()).ToArray())));
                }
            },

                createAlternation: list =>
                new ParserInfo(
                    Best(list.Select(t => t.Parser).ToArray()),
                    list[0].Element,
                    list[0].Missing,
                    list[0].IsTerm),

                createZeroOrMore: elem =>
                new ParserInfo(
                    List(
                        elem.Parser,
                        elem.Missing,
                        oneOrMore: false,
                        producer: (elements) => (SyntaxElement) new SyntaxList <SyntaxElement>(elements.ToArray())),
                    new CustomElementDescriptor(elem.Element.CompletionHint, isOptional: false),
                    () => new SyntaxList <SyntaxElement>(new SyntaxElement[] { })),

                createOneOrMore: elem =>
                new ParserInfo(
                    List(
                        elem.Parser,
                        elem.Missing,
                        oneOrMore: true,
                        producer: (elements) => (SyntaxElement) new SyntaxList <SyntaxElement>(elements.ToArray())),
                    new CustomElementDescriptor(elem.Element.CompletionHint, isOptional: false),
                    () => new SyntaxList <SyntaxElement>(new SyntaxElement[] { elem.Missing() })),

                createZeroOrMoreSeparated: (elem, sep) =>
                new ParserInfo(
                    SeparatedList <SyntaxElement, SyntaxElement>(
                        elem.Parser,
                        sep.Parser,
                        elem.Missing,
                        missingSeparator: null, //sep.Missing,
                        endOfList: null,        //notEndOfList
                        oneOrMore: false,
                        allowTrailingSeparator: false,
                        producer: elements => MakeSeparatedList <SyntaxElement>(elements.ToArray())),
                    new CustomElementDescriptor(elem.Element.CompletionHint, isOptional: false),
                    () => new SyntaxList <SeparatedElement <SyntaxElement> >(new SeparatedElement <SyntaxElement>[] { })),

                createOneOrMoreSeparated: (elem, sep) =>
                new ParserInfo(
                    SeparatedList <SyntaxElement, SyntaxElement>(
                        elem.Parser,
                        sep.Parser,
                        elem.Missing,
                        missingSeparator: null,
                        endOfList: null,
                        oneOrMore: true,
                        allowTrailingSeparator: false,
                        producer: elements => MakeSeparatedList <SyntaxElement>(elements.ToArray())),
                    new CustomElementDescriptor(elem.Element.CompletionHint, isOptional: false),
                    () => new SyntaxList <SeparatedElement <SyntaxElement> >(new[] { new SeparatedElement <SyntaxElement>(elem.Missing()) }))
                );

            return(Parsers <char> .Rule(grammar, g => g.Parser));
        }