0

Good day I have simple grammar which describe columns for select. User can specify simple column name, column name and subcolumns for them (and so on), or use '*' to select all fields.

Some example:

    exp = "(Id)" - select only Id column
    exp = "(Id, Name)" - select Id and Name columns
    exp = "(Id, Type(Id, Name)" - select Id, Type.Id, Type.Name columns
    exp = "(Id, Type(*))" - select Id and all columns in Type

so, i define token enum:

    enum FieldExpressionToken
    {
        Undefined,
        
        [Token(Example = "(")]
        LBracket,
        
        [Token(Example = ")")]
        RBracket,
        
        [Token(Example = ",")]
        Comma,
        
        [Token(Example = ":")]
        Colon,
        
        [Token(Example = "*")]
        Star,
        
        Identifier
    }

and tokenizer for them:

    static class FieldExpressionTokenizer
    {
        private static TextParser<TextSpan> FieldNameToken { get; } =
            Span.Regex(@"@?[a-zA-Z_]\w*(\.@?[a-zA-Z_]\w*)*");
    
        public static Tokenizer<FieldExpressionToken> Instance { get; } = 
            new TokenizerBuilder<FieldExpressionToken>()
                .Match(Character.EqualTo('*'), FieldExpressionToken.Star)
                .Match(Character.EqualTo('('), FieldExpressionToken.LBracket)
                .Match(Character.EqualTo(')'), FieldExpressionToken.RBracket)
                .Match(Character.EqualTo(','), FieldExpressionToken.Comma)
                .Match(Character.EqualTo(':'), FieldExpressionToken.Colon)
                .Match(FieldNameToken, FieldExpressionToken.Identifier)
                .Build();
    }

now i try to create parser. Parser for single field:

    private static readonly TokenListParser<FieldExpressionToken, Field> Field = 
         from identifier in Token
            .EqualTo(FieldExpressionToken.Identifier)
            .Or(Token.EqualTo(FieldExpressionToken.Star))
            .Named("column name")
        select new Field(identifier.ToStringValue());

Parser for composite field:

    private static readonly TokenListParser<FieldExpressionToken, Field> ComplexField =
        from identifier in Token
            .EqualTo(FieldExpressionToken.Identifier)
            .Or(Token.EqualTo(FieldExpressionToken.Star))
            .Named("column name")
            .Try()
            .Then(x => Token
                 .EqualTo(FieldExpressionToken.Colon)
                 .IgnoreThen(Parse.Ref(() => FieldList))
                 .Named("child fields")
                 .Select(value => new Field(x.ToStringValue(), new[] {value})))
        select identifier;

Parser for list:

    private static readonly TokenListParser<FieldExpressionToken, Field> FieldList =
        from begin in Token.EqualTo(FieldExpressionToken.LBracket)
        from fields in Parse
            .Ref(() => FieldExpression)
            .Named("fields")
            .ManyDelimitedBy(
                Token.EqualTo(FieldExpressionToken.Comma), 
                end: Token.EqualTo(FieldExpressionToken.RBracket))
        select new Field("", fields.Select(x => new Field(x.ToString())));

and total expression parser:

    private static TokenListParser<FieldExpressionToken, Field> FieldExpression { get; } =
        Field
            .Or(ComplexField)
            .Or(FieldList);

i can parse simple expression like (Name) or (Id,Name), but on expression (Name:(Id)) i get error Syntax error (line 1, column 6): unexpected :, expected ).

can anybody help me to correct my parser?

1 Answers1

0

ok.. probably i found solution, but i'm not sure fo 100%

i change order in overal expression parser to:


    private static TokenListParser<FieldExpressionToken, Field> FieldExpression { get; } =
                ComplexField
                    .Or(Field)
                    .Or(FieldList);

and update ComplexFiled parser:


    private static readonly TokenListParser<FieldExpressionToken, Field> ComplexField =
                 from identifier in Token
                     .EqualTo(FieldExpressionToken.Identifier)
                     .Or(Token.EqualTo(FieldExpressionToken.Star))
                     .Named("column name")
                     .Then(x => Token
                         .EqualTo(FieldExpressionToken.Colon)
                         .IgnoreThen(Parse.Ref(() => FieldList))
                         .Named("child fields")
                         .Select(value => new Field(x.ToStringValue(), new[] {value})))
                     .Try()
                 select identifier;

and not it's work!