diff options
-rw-r--r-- | driver/parser_test.go | 4 | ||||
-rw-r--r-- | grammar/grammar.go | 24 | ||||
-rw-r--r-- | spec/parser.go | 30 | ||||
-rw-r--r-- | spec/parser_test.go | 32 |
4 files changed, 56 insertions, 34 deletions
diff --git a/driver/parser_test.go b/driver/parser_test.go index 2954ca2..8713893 100644 --- a/driver/parser_test.go +++ b/driver/parser_test.go @@ -123,8 +123,10 @@ push_m2: "-->" #push m2; pop_m1 : "<-" #pop; #mode m2 pop_m2: "<--" #pop; +#mode default m1 m2 +whitespace: "\u{0020}+" #skip; `, - src: `->--><--<-`, + src: ` -> --> <-- <- `, }, { specSrc: ` diff --git a/grammar/grammar.go b/grammar/grammar.go index 3344b64..b9f65dc 100644 --- a/grammar/grammar.go +++ b/grammar/grammar.go @@ -42,11 +42,14 @@ func NewGrammar(root *spec.RootNode) (*Grammar, error) { dir := prod.Directive switch dir.Name { case "mode": - if dir.Parameter == nil || dir.Parameter.ID == "" { + if len(dir.Parameters) == 0 { return nil, fmt.Errorf("'mode' directive needs an ID parameter") } - modes = []mlspec.LexModeName{ - mlspec.LexModeName(dir.Parameter.ID), + for _, param := range dir.Parameters { + if param.ID == "" { + return nil, fmt.Errorf("'mode' directive needs an ID parameter") + } + modes = append(modes, mlspec.LexModeName(param.ID)) } default: return nil, fmt.Errorf("invalid directive name '%v'", dir.Name) @@ -60,20 +63,17 @@ func NewGrammar(root *spec.RootNode) (*Grammar, error) { dir := alt.Directive switch dir.Name { case "skip": - param := dir.Parameter - if param != nil { + if len(dir.Parameters) > 0 { return nil, fmt.Errorf("'skip' directive needs no parameter") } skip = append(skip, mlspec.LexKind(prod.LHS)) case "push": - param := dir.Parameter - if param == nil || param.ID == "" { + if len(dir.Parameters) != 1 || dir.Parameters[0].ID == "" { return nil, fmt.Errorf("'push' directive needs an ID parameter") } - push = mlspec.LexModeName(param.ID) + push = mlspec.LexModeName(dir.Parameters[0].ID) case "pop": - param := dir.Parameter - if param != nil { + if len(dir.Parameters) > 0 { return nil, fmt.Errorf("'pop' directive needs no parameter") } pop = true @@ -214,10 +214,10 @@ func NewGrammar(root *spec.RootNode) (*Grammar, error) { dir := alt.Directive switch dir.Name { case "ast": - param := dir.Parameter - if param == nil || param.Tree == nil { + if len(dir.Parameters) != 1 || dir.Parameters[0].Tree == nil { return nil, fmt.Errorf("'ast' directive needs a tree parameter") } + param := dir.Parameters[0] lhsText, ok := symTab.toText(p.lhs) if !ok || param.Tree.Name != lhsText { return nil, fmt.Errorf("a name of a tree structure must be the same ID as an LHS of a production; LHS: %v", lhsText) diff --git a/spec/parser.go b/spec/parser.go index 8322a3f..da17498 100644 --- a/spec/parser.go +++ b/spec/parser.go @@ -26,8 +26,8 @@ type ElementNode struct { } type DirectiveNode struct { - Name string - Parameter *ParameterNode + Name string + Parameters []*ParameterNode } type ParameterNode struct { @@ -260,10 +260,25 @@ func (p *parser) parseDirective() *DirectiveNode { } name := p.lastTok.text - var param *ParameterNode + var params []*ParameterNode + for { + param := p.parseParameter() + if param == nil { + break + } + params = append(params, param) + } + + return &DirectiveNode{ + Name: name, + Parameters: params, + } +} + +func (p *parser) parseParameter() *ParameterNode { switch { case p.consume(tokenKindID): - param = &ParameterNode{ + return &ParameterNode{ ID: p.lastTok.text, } case p.consume(tokenKindTreeNodeOpen): @@ -292,7 +307,7 @@ func (p *parser) parseDirective() *DirectiveNode { raiseSyntaxError(synErrTreeUnclosed) } - param = &ParameterNode{ + return &ParameterNode{ Tree: &TreeStructNode{ Name: name, Children: children, @@ -300,10 +315,7 @@ func (p *parser) parseDirective() *DirectiveNode { } } - return &DirectiveNode{ - Name: name, - Parameter: param, - } + return nil } func (p *parser) consume(expected tokenKind) bool { diff --git a/spec/parser_test.go b/spec/parser_test.go index 788c4b3..628dd6f 100644 --- a/spec/parser_test.go +++ b/spec/parser_test.go @@ -25,10 +25,10 @@ func TestParse(t *testing.T) { alt.Directive = dir return alt } - dir := func(name string, param *ParameterNode) *DirectiveNode { + dir := func(name string, params ...*ParameterNode) *DirectiveNode { return &DirectiveNode{ - Name: name, - Parameter: param, + Name: name, + Parameters: params, } } idParam := func(id string) *ParameterNode { @@ -209,6 +209,8 @@ push_m2: "-->" #push m2; pop_m1 : "<-" #pop; #mode m2 pop_m2: "<--" #pop; +#mode default m1 m2 +whitespace: "\u{0020}+" #skip; `, ast: &RootNode{ Productions: []*ProductionNode{ @@ -241,7 +243,7 @@ pop_m2: "<--" #pop; prod("pop_m1", withAltDir( alt(pat(`<-`)), - dir("pop", nil), + dir("pop"), ), ), dir("mode", idParam("m1")), @@ -250,11 +252,20 @@ pop_m2: "<--" #pop; prod("pop_m2", withAltDir( alt(pat(`<--`)), - dir("pop", nil), + dir("pop"), ), ), dir("mode", idParam("m2")), ), + withProdDir( + prod("whitespace", + withAltDir( + alt(pat(`\u{0020}+`)), + dir("skip"), + ), + ), + dir("mode", idParam("default"), idParam("m1"), idParam("m2")), + ), }, }, }, @@ -419,14 +430,11 @@ func testDirective(t *testing.T, dir, expected *DirectiveNode) { if expected.Name != dir.Name { t.Fatalf("unexpected directive name; want: %+v, got: %+v", expected.Name, dir.Name) } - if expected.Parameter == nil && dir.Parameter != nil { - t.Fatalf("unexpected directive parameter; want: nil, got: %+v", dir.Parameter) + if len(expected.Parameters) != len(dir.Parameters) { + t.Fatalf("unexpected directive parameter; want: %+v, got: %+v", expected.Parameters, dir.Parameters) } - if expected.Parameter != nil { - if dir.Parameter == nil { - t.Fatalf("unexpected directive parameter; want: %+v, got: nil", expected.Parameter) - } - testParameter(t, dir.Parameter, expected.Parameter) + for i, param := range dir.Parameters { + testParameter(t, param, expected.Parameters[i]) } } |