aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--driver/parser_test.go4
-rw-r--r--grammar/grammar.go24
-rw-r--r--spec/parser.go30
-rw-r--r--spec/parser_test.go32
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])
}
}