ucl/cmdlang/ast.go

92 lines
2.1 KiB
Go

package cmdlang
import (
"io"
"github.com/alecthomas/participle/v2"
"github.com/alecthomas/participle/v2/lexer"
)
type astLiteral struct {
Str *string `parser:"@String"`
}
type astHashKey struct {
Literal *astLiteral `parser:"@@"`
Ident *string `parser:"| @Ident"`
Var *string `parser:"| DOLLAR @Ident"`
Sub *astPipeline `parser:"| LP @@ RP"`
}
type astElementPair struct {
Left astCmdArg `parser:"@@"`
Right *astCmdArg `parser:"( COLON @@ )? NL?"`
}
type astListOrHash struct {
EmptyList bool `parser:"@(LS RS)"`
EmptyHash bool `parser:"| @(LS COLON RS)"`
Elements []*astElementPair `parser:"| LS NL? @@+ @@* RS"`
}
type astBlock struct {
Names []string `parser:"LC NL? (PIPE @Ident+ PIPE NL?)?"`
Statements []*astStatements `parser:"@@ NL? RC"`
}
type astMaybeSub struct {
Sub *astPipeline `parser:"@@?"`
}
type astCmdArg struct {
Literal *astLiteral `parser:"@@"`
Ident *string `parser:"| @Ident"`
Var *string `parser:"| DOLLAR @Ident"`
MaybeSub *astMaybeSub `parser:"| LP @@ RP"`
ListOrHash *astListOrHash `parser:"| @@"`
Block *astBlock `parser:"| @@"`
}
type astCmd struct {
Name astCmdArg `parser:"@@"`
Args []astCmdArg `parser:"@@*"`
}
type astPipeline struct {
First *astCmd `parser:"@@"`
Rest []*astCmd `parser:"( PIPE @@ )*"`
}
type astStatements struct {
First *astPipeline `parser:"@@"`
Rest []*astPipeline `parser:"( NL+ @@ )*"` // TODO: also add support for newlines
}
type astScript struct {
Statements *astStatements `parser:"NL* @@ NL*"`
}
var scanner = lexer.MustStateful(lexer.Rules{
"Root": {
{"Whitespace", `[ \t]+`, nil},
{"String", `"(\\"|[^"])*"`, nil},
{"DOLLAR", `\$`, nil},
{"COLON", `\:`, nil},
{"LP", `\(`, nil},
{"RP", `\)`, nil},
{"LS", `\[`, nil},
{"RS", `\]`, nil},
{"LC", `\{`, nil},
{"RC", `\}`, nil},
{"NL", `[;\n][; \n\t]*`, nil},
{"PIPE", `\|`, nil},
{"Ident", `[\w-]+`, nil},
},
})
var parser = participle.MustBuild[astScript](participle.Lexer(scanner),
participle.Elide("Whitespace"))
func parse(r io.Reader) (*astScript, error) {
return parser.Parse("test", r)
}