Я определил грамматику с 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
}
Я что-то упустил? Любая помощь будет действительно оценена.