Parser, написанный с Participle, не может определить условия выражения - PullRequest
0 голосов
/ 08 марта 2020

Я определил грамматику с Participle для разбора выражений, подобных этим:

ident eq 1
ident gt (#now - #duration('PT2H')
ident1 lt ident2 and (ident3 plus 1 eq 5)

# идентифицирует макрос, который может иметь ноль или более параметров.

Выражение выше успешно проанализировано ... тогда как следующее выражение не выполнено:

ident lt #mymacro('param1', 'param2')

Вот моя грамматика:

package test

import (
    "github.com/alecthomas/participle"
    "github.com/alecthomas/participle/lexer"
    "io"
)

type Term struct {
    Identifier *string  `  @Ident`
    Integer    *int     `| @Int`
    Decimal    *float64 `| @Float`
    String     *string  `| @String`
    Bool       *bool    `| @("true" | "false")`
    Macro      *Macro   `| @@`
}

type Macro struct {
    Name string  `@("#") @Ident`
    Args []*Term `("(" (@@ ("," @@)*)? ")")?`
}

type Maths struct {
    Term1 *Term  `@@`
    Op    string `@("plus" | "minus" | "mul" | "div")`
    Term2 *Term  `@@`
}

type TermOrMaths struct {
    Maths      *Maths `  @@`
    ParenMaths *Maths `| "(" @@ ")"`
    Term       *Term  `| @@`
}

type Equality struct {
    TermOrMaths1 *TermOrMaths `@@`
    Op           string       `@("eq" | "neq")`
    TermOrMaths2 *TermOrMaths `@@`
}

type Comparison struct {
    TermOrMaths1 *TermOrMaths `@@`
    Op           string       `@("gt" | "gte" | "lt" | "lte")`
    TermOrMaths2 *TermOrMaths `@@`
}

type ParenExpression struct {
    Not         bool          `@("not")?`
    Expressions []*Expression `"(" @@+ ")"`
}

type Expression struct {
    Op              *string          `  @("and" | "or")`
    ParenExpression *ParenExpression `| @@`
    Equality        *Equality        `| @@`
    Comparison      *Comparison      `| @@`
}

type Grammar struct {
    Expressions []*Expression `@@+`
}

type parser struct {
    p *participle.Parser
}

func newParser() *parser {
    return &parser{
        p: participle.MustBuild(&Grammar{}, participle.UseLookahead(2)),
    }
}

func (p *parser) parse(r io.Reader) (error, *Grammar) {
    grammar := &Grammar{}
    err := p.p.Parse(r, grammar)

    return err, grammar
}

Проблема в том, что первый операнд правильно идентифицирован как Term, а вторая часть обозначена как Maths вместо Macro.

Ниже приведен код для его проверки:

package test

import (
    "fmt"
    "strconv"
)

func main() {
    r := strings.NewReader("ident lt #mymacro('param1', 'param2')")

    err, grammar := parser.parse(r)
    if err != nil {
       fmt.Println("Error parsing expression")
    } else {
       fmt.Println(emitGrammar(grammar))
    }
}

func emitGrammar(g *Grammar) string {
    var sb strings.Builder

    for _, e := range g.Expressions {
        sb.WriteString(emitExpression(e))
    }

    return sb.String()
}

func emitExpression(e *Expression) string {
    var s string

    if e.Op != nil {
        s = fmt.Sprintf(" %s ", *e.Op)
    } else if e.ParenExpression != nil {
        s = emitParenExpression(e.ParenExpression)
    } else if e.Equality != nil {
        s = emitEquality(e.Equality)
    } else if e.Comparison != nil {
        s = emitComparison(e.Comparison)
    }

    return s
}

func emitParenExpression(pe *ParenExpression) string {
    var sb strings.Builder

i    f pe.Not {
       sb.WriteString("not ")
    }

    sb.WriteString("(")

    for _, e := range pe.Expressions {
        sb.WriteString(emitExpression(e))
    }

    sb.WriteString(")")

    return sb.String()
}

func emitEquality(e *Equality) string {
    t1 := emitTermOrMaths(e.TermOrMaths1)
    t2 := emitTermOrMaths(e.TermOrMaths2)

    return fmt.Sprintf("%s %s %s", t1, e.Op, t2)
}

func emitComparison(c *Comparison) string {
    t1 := emitTermOrMaths(c.TermOrMaths1)
    t2 := emitTermOrMaths(c.TermOrMaths2)

    return fmt.Sprintf("%s %s %s", t1, c.Op, t2)
}

func emitMacro(m *Macro) string {
    var sb strings.Builder

    sb.WriteString(m.Name)

    if m.Args != nil {
        sb.WriteString("(")
        var s string
        for i, a := range m.Args {
            if a.Identifier != nil {
                s = *a.Identifier
            } else if a.Macro != nil {
                s = emitMacro(a.Macro)
            } else if a.Integer != nil {
                s = strconv.Itoa(*a.Integer)
            } else if a.Decimal != nil {
                s = strconv.FormatFloat(*a.Decimal, 'f', -1, 64)
            } else if a.String != nil {
                s = fmt.Sprintf("'%s'", *a.String)
            } else if a.Bool != nil {
                s = strconv.FormatBool(*a.Bool)
            }

            if i > 0 {
                sb.WriteString(", ")
            }
            sb.WriteString(s)
        }

         sb.WriteString(")")
    }

    return sb.String()
}

func emitMaths(m *Maths) string {
    t1 := emitTerm(m.Term1)
    t2 := emitTerm(m.Term2)

    return fmt.Sprintf("%s %s %s", t1, m.Op, t2)
}

func emitTerm(t *Term) string {
    var s string

    if t.Identifier != nil {
        s = *t.Identifier
    } else if t.Integer != nil {
        s = strconv.Itoa(*t.Integer)
    } else if t.Decimal != nil {
        s = strconv.FormatFloat(*t.Decimal, 'f', -1, 64)
    } else if t.String != nil {
        s = fmt.Sprintf("'%s'", *t.String)
    } else if t.Bool != nil {
        s = strconv.FormatBool(*t.Bool)
    } else if t.Macro != nil {
        s = emitMacro(t.Macro)
    }

    return s
}

func emitTermOrMaths(tm *TermOrMaths) string {
    var s string

    if tm.Maths != nil {
        s = emitMaths(tm.Maths)
    } else if tm.ParenMaths != nil {
        s = fmt.Sprintf("(%s)", emitMaths(tm.ParenMaths))
    } else if tm.Term != nil {
        s = emitTerm(tm.Term)
    }

    return s
}

Я что-то упустил? Любая помощь будет действительно оценена.

...