aboutsummaryrefslogtreecommitdiff
path: root/grammar
diff options
context:
space:
mode:
Diffstat (limited to 'grammar')
-rw-r--r--grammar/first_test.go10
-rw-r--r--grammar/follow_test.go10
-rw-r--r--grammar/grammar.go71
-rw-r--r--grammar/grammar_test.go683
-rw-r--r--grammar/lalr1_test.go2
-rw-r--r--grammar/lr0_test.go4
-rw-r--r--grammar/parsing_table_test.go4
-rw-r--r--grammar/semantic_error.go4
-rw-r--r--grammar/slr1_test.go2
9 files changed, 570 insertions, 220 deletions
diff --git a/grammar/first_test.go b/grammar/first_test.go
index 578825e..134f4bd 100644
--- a/grammar/first_test.go
+++ b/grammar/first_test.go
@@ -24,7 +24,7 @@ func TestGenFirst(t *testing.T) {
{
caption: "productions contain only non-empty productions",
src: `
-%name test
+#name test;
expr
: expr add term
@@ -63,7 +63,7 @@ id: "[A-Za-z_][0-9A-Za-z_]*";
{
caption: "productions contain the empty start production",
src: `
-%name test
+#name test;
s
:
@@ -77,7 +77,7 @@ s
{
caption: "productions contain an empty production",
src: `
-%name test
+#name test;
s
: foo bar
@@ -96,7 +96,7 @@ bar: "bar";
{
caption: "a start production contains a non-empty alternative and empty alternative",
src: `
-%name test
+#name test;
s
: foo
@@ -113,7 +113,7 @@ foo: "foo";
{
caption: "a production contains non-empty alternative and empty alternative",
src: `
-%name test
+#name test;
s
: foo
diff --git a/grammar/follow_test.go b/grammar/follow_test.go
index af3f064..719582f 100644
--- a/grammar/follow_test.go
+++ b/grammar/follow_test.go
@@ -22,7 +22,7 @@ func TestFollowSet(t *testing.T) {
{
caption: "productions contain only non-empty productions",
src: `
-%name test
+#name test;
expr
: expr add term
@@ -52,7 +52,7 @@ id: "[A-Za-z_][0-9A-Za-z_]*";
{
caption: "productions contain an empty start production",
src: `
-%name test
+#name test;
s
:
@@ -66,7 +66,7 @@ s
{
caption: "productions contain an empty production",
src: `
-%name test
+#name test;
s
: foo
@@ -84,7 +84,7 @@ foo
{
caption: "a start production contains a non-empty alternative and empty alternative",
src: `
-%name test
+#name test;
s
: foo
@@ -100,7 +100,7 @@ foo: "foo";
{
caption: "a production contains non-empty alternative and empty alternative",
src: `
-%name test
+#name test;
s
: foo
diff --git a/grammar/grammar.go b/grammar/grammar.go
index d46d70e..da0460b 100644
--- a/grammar/grammar.go
+++ b/grammar/grammar.go
@@ -109,30 +109,30 @@ func (b *GrammarBuilder) Build() (*Grammar, error) {
var specName string
{
errOccurred := false
- for _, md := range b.AST.MetaData {
- if md.Name != "name" {
+ for _, dir := range b.AST.Directives {
+ if dir.Name != "name" {
continue
}
- if len(md.Parameters) != 1 || md.Parameters[0].ID == "" {
+ if len(dir.Parameters) != 1 || dir.Parameters[0].ID == "" {
b.errs = append(b.errs, &verr.SpecError{
- Cause: semErrMDInvalidParam,
+ Cause: semErrDirInvalidParam,
Detail: "'name' takes just one ID parameter",
- Row: md.Pos.Row,
- Col: md.Pos.Col,
+ Row: dir.Pos.Row,
+ Col: dir.Pos.Col,
})
errOccurred = true
break
}
- specName = md.Parameters[0].ID
+ specName = dir.Parameters[0].ID
break
}
if specName == "" && !errOccurred {
b.errs = append(b.errs, &verr.SpecError{
- Cause: semErrMDMissingName,
+ Cause: semErrNoGrammarName,
})
}
}
@@ -986,40 +986,63 @@ func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prods *productionS
termPrec := map[symbolNum]int{}
termAssoc := map[symbolNum]assocType{}
{
+ var precGroup []*spec.DirectiveNode
+ for _, dir := range b.AST.Directives {
+ if dir.Name == "prec" {
+ if dir.Parameters == nil || len(dir.Parameters) != 1 || dir.Parameters[0].Group == nil {
+ b.errs = append(b.errs, &verr.SpecError{
+ Cause: semErrDirInvalidParam,
+ Detail: "'prec' needs just one directive group",
+ Row: dir.Pos.Row,
+ Col: dir.Pos.Col,
+ })
+ continue
+ }
+ precGroup = dir.Parameters[0].Group
+ continue
+ }
+
+ if dir.Name != "name" && dir.Name != "prec" {
+ b.errs = append(b.errs, &verr.SpecError{
+ Cause: semErrDirInvalidName,
+ Row: dir.Pos.Row,
+ Col: dir.Pos.Col,
+ })
+ continue
+ }
+ }
+
precN := precMin
- for _, md := range b.AST.MetaData {
+ for _, dir := range precGroup {
var assocTy assocType
- switch md.Name {
+ switch dir.Name {
case "left":
assocTy = assocTypeLeft
case "right":
assocTy = assocTypeRight
- case "name":
- // Since `name` is used for a purpose other than priority, we will ignore it here.
- continue
default:
b.errs = append(b.errs, &verr.SpecError{
- Cause: semErrMDInvalidName,
- Row: md.Pos.Row,
- Col: md.Pos.Col,
+ Cause: semErrDirInvalidName,
+ Row: dir.Pos.Row,
+ Col: dir.Pos.Col,
})
return nil, nil
}
- if len(md.Parameters) == 0 {
+ if len(dir.Parameters) == 0 {
b.errs = append(b.errs, &verr.SpecError{
- Cause: semErrMDInvalidParam,
+ Cause: semErrDirInvalidParam,
Detail: "associativity needs at least one symbol",
- Row: md.Pos.Row,
- Col: md.Pos.Col,
+ Row: dir.Pos.Row,
+ Col: dir.Pos.Col,
})
return nil, nil
}
ASSOC_PARAM_LOOP:
- for _, p := range md.Parameters {
+ for _, p := range dir.Parameters {
if p.ID == "" {
b.errs = append(b.errs, &verr.SpecError{
- Cause: semErrMDInvalidParam,
+ Cause: semErrDirInvalidParam,
Detail: "a parameter must be an ID",
Row: p.Pos.Row,
Col: p.Pos.Col,
@@ -1030,7 +1053,7 @@ func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prods *productionS
sym, ok := symTab.toSymbol(p.ID)
if !ok {
b.errs = append(b.errs, &verr.SpecError{
- Cause: semErrMDInvalidParam,
+ Cause: semErrDirInvalidParam,
Detail: fmt.Sprintf("'%v' is undefined", p.ID),
Row: p.Pos.Row,
Col: p.Pos.Col,
@@ -1039,7 +1062,7 @@ func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prods *productionS
}
if !sym.isTerminal() {
b.errs = append(b.errs, &verr.SpecError{
- Cause: semErrMDInvalidParam,
+ Cause: semErrDirInvalidParam,
Detail: fmt.Sprintf("associativity can take only terminal symbol ('%v' is a non-terminal)", p.ID),
Row: p.Pos.Row,
Col: p.Pos.Col,
diff --git a/grammar/grammar_test.go b/grammar/grammar_test.go
index 46458b0..07d8b58 100644
--- a/grammar/grammar_test.go
+++ b/grammar/grammar_test.go
@@ -17,9 +17,10 @@ func TestGrammarBuilderOK(t *testing.T) {
nameTests := []*okTest{
{
- caption: "the `%name` can be the same identifier as a non-terminal symbol",
+ caption: "the `#name` can be the same identifier as a non-terminal symbol",
specSrc: `
-%name s
+#name s;
+
s
: foo
;
@@ -35,9 +36,10 @@ foo
},
},
{
- caption: "the `%name` can be the same identifier as a terminal symbol",
+ caption: "the `#name` can be the same identifier as a terminal symbol",
specSrc: `
-%name foo
+#name foo;
+
s
: foo
;
@@ -53,9 +55,10 @@ foo
},
},
{
- caption: "the `%name` can be the same identifier as the error symbol",
+ caption: "the `#name` can be the same identifier as the error symbol",
specSrc: `
-%name error
+#name error;
+
s
: foo
| error
@@ -72,9 +75,10 @@ foo
},
},
{
- caption: "the `%name` can be the same identifier as a fragment",
+ caption: "the `#name` can be the same identifier as a fragment",
specSrc: `
-%name f
+#name f;
+
s
: foo
;
@@ -97,7 +101,8 @@ fragment f
{
caption: "a `#mode` can be the same identifier as a non-terminal symbol",
specSrc: `
-%name test
+#name test;
+
s
: foo bar
;
@@ -121,7 +126,8 @@ bar #mode s
{
caption: "a `#mode` can be the same identifier as a terminal symbol",
specSrc: `
-%name test
+#name test;
+
s
: foo bar
;
@@ -145,7 +151,8 @@ bar #mode bar
{
caption: "a `#mode` can be the same identifier as the error symbol",
specSrc: `
-%name test
+#name test;
+
s
: foo bar
| error
@@ -170,7 +177,8 @@ bar #mode error
{
caption: "a `#mode` can be the same identifier as a fragment",
specSrc: `
-%name test
+#name test;
+
s
: foo bar
;
@@ -194,11 +202,28 @@ fragment f
},
},
{
+ caption: "a `#prec` allows the empty directive group",
+ specSrc: `
+#name test;
+
+#prec ();
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ },
+ {
caption: "a production has the same precedence and associativity as the right-most terminal symbol",
specSrc: `
-%name test
+#name test;
-%left foo
+#prec (
+ #left foo
+);
s
: foo bar // This alternative has the same precedence and associativity as the right-most terminal symbol 'bar', not 'foo'.
@@ -236,10 +261,12 @@ bar
{
caption: "a production has the same precedence and associativity as the right-most terminal symbol",
specSrc: `
-%name test
+#name test;
-%left foo
-%right bar
+#prec (
+ #left foo
+ #right bar
+);
s
: foo bar // This alternative has the same precedence and associativity as the right-most terminal symbol 'bar'.
@@ -275,11 +302,13 @@ bar
},
},
{
- caption: "the `#prec` directive changes only precedence, not associativity",
+ caption: "the `#prec` directive applied to an alternative changes only precedence, not associativity",
specSrc: `
-%name test
+#name test;
-%left foo
+#prec (
+ #left foo
+);
s
: foo bar #prec foo
@@ -315,12 +344,14 @@ bar
},
},
{
- caption: "the `#prec` directive changes only precedence, not associativity",
+ caption: "the `#prec` directive applied to an alternative changes only precedence, not associativity",
specSrc: `
-%name test
+#name test;
-%left foo
-%right bar
+#prec (
+ #left foo
+ #right bar
+);
s
: foo bar #prec foo
@@ -385,7 +416,9 @@ bar
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
- test.validate(t, g)
+ if test.validate != nil {
+ test.validate(t, g)
+ }
})
}
}
@@ -401,7 +434,7 @@ func TestGrammarBuilderSpecError(t *testing.T) {
{
caption: "a production `b` is unused",
specSrc: `
-%name test
+#name test;
a
: foo
@@ -418,7 +451,7 @@ foo
{
caption: "a terminal symbol `bar` is unused",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -434,7 +467,7 @@ bar
{
caption: "a production `b` and terminal symbol `bar` is unused",
specSrc: `
-%name test
+#name test;
a
: foo
@@ -456,7 +489,7 @@ bar
{
caption: "a production cannot have production directives",
specSrc: `
-%name test
+#name test;
s #prec foo
: foo
@@ -470,7 +503,7 @@ foo
{
caption: "a lexical production cannot have alternative directives",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -484,7 +517,7 @@ foo
{
caption: "a production directive must not be duplicated",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -498,7 +531,7 @@ foo #skip #skip
{
caption: "an alternative directive must not be duplicated",
specSrc: `
-%name test
+#name test;
s
: foo bar #ast foo bar #ast foo bar
@@ -514,7 +547,7 @@ bar
{
caption: "a production must not have a duplicate alternative (non-empty alternatives)",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -529,7 +562,7 @@ foo
{
caption: "a production must not have a duplicate alternative (non-empty and split alternatives)",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -552,7 +585,7 @@ bar
{
caption: "a production must not have a duplicate alternative (empty alternatives)",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -571,7 +604,7 @@ foo
{
caption: "a production must not have a duplicate alternative (empty and split alternatives)",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -593,7 +626,7 @@ foo
{
caption: "a terminal symbol and a non-terminal symbol (start symbol) are duplicates",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -609,7 +642,7 @@ s
{
caption: "a terminal symbol and a non-terminal symbol (not start symbol) are duplicates",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -629,11 +662,11 @@ a
errs: []*SemanticError{semErrDuplicateName},
},
{
- caption: "an invalid associativity type",
+ caption: "an invalid top-level directive",
specSrc: `
-%name test
+#name test;
-%foo
+#foo;
s
: a
@@ -642,12 +675,12 @@ s
a
: 'a';
`,
- errs: []*SemanticError{semErrMDInvalidName},
+ errs: []*SemanticError{semErrDirInvalidName},
},
{
caption: "a label must be unique in an alternative",
specSrc: `
-%name test
+#name test;
s
: foo@x bar@x
@@ -663,7 +696,7 @@ bar
{
caption: "a label cannot be the same name as terminal symbols",
specSrc: `
-%name test
+#name test;
s
: foo bar@foo
@@ -679,7 +712,7 @@ bar
{
caption: "a label cannot be the same name as non-terminal symbols",
specSrc: `
-%name test
+#name test;
s
: foo@a
@@ -700,10 +733,85 @@ bar
},
}
- nameTests := []*specErrTest{
+ nameDirTests := []*specErrTest{
+ {
+ caption: "the `#name` directive is required",
+ specSrc: `
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrNoGrammarName},
+ },
+ {
+ caption: "the `#name` directive needs an ID parameter",
+ specSrc: `
+#name;
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ {
+ caption: "the `#name` directive cannot take a pattern parameter",
+ specSrc: `
+#name "test";
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ {
+ caption: "the `#name` directive cannot take a string parameter",
+ specSrc: `
+#name 'test';
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ {
+ caption: "the `#name` directive takes just one parameter",
+ specSrc: `
+#name test1 test2;
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ }
+
+ precDirTests := []*specErrTest{
{
- caption: "the `%name` is required",
+ caption: "the `#prec` directive needs a directive group parameter",
specSrc: `
+#name test;
+
+#prec;
+
s
: foo
;
@@ -711,12 +819,14 @@ s
foo
: 'foo';
`,
- errs: []*SemanticError{semErrMDMissingName},
+ errs: []*SemanticError{semErrDirInvalidParam},
},
{
- caption: "the `%name` needs an ID parameter",
+ caption: "the `#prec` directive cannot take an ID parameter",
specSrc: `
-%name
+#name test;
+
+#prec foo;
s
: foo
@@ -725,12 +835,14 @@ s
foo
: 'foo';
`,
- errs: []*SemanticError{semErrMDInvalidParam},
+ errs: []*SemanticError{semErrDirInvalidParam},
},
{
- caption: "the `%name` takes just one parameter",
+ caption: "the `#prec` directive cannot take a pattern parameter",
specSrc: `
-%name test1 test2
+#name test;
+
+#prec "foo";
s
: foo
@@ -739,17 +851,51 @@ s
foo
: 'foo';
`,
- errs: []*SemanticError{semErrMDInvalidParam},
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ {
+ caption: "the `#prec` directive cannot take a string parameter",
+ specSrc: `
+#name test;
+
+#prec 'foo';
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ {
+ caption: "the `#prec` directive takes just one directive group parameter",
+ specSrc: `
+#name test;
+
+#prec () ();
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
},
}
- leftTests := []*specErrTest{
+ leftDirTests := []*specErrTest{
{
- caption: "the `%left` needs ID parameters",
+ caption: "the `#left` directive needs ID parameters",
specSrc: `
-%name test
+#name test;
-%left
+#prec (
+ #left
+);
s
: foo
@@ -758,14 +904,16 @@ s
foo
: 'foo';
`,
- errs: []*SemanticError{semErrMDInvalidParam},
+ errs: []*SemanticError{semErrDirInvalidParam},
},
{
- caption: "the `%left` cannot take an undefined symbol",
+ caption: "the `#left` directive cannot take an undefined symbol",
specSrc: `
-%name test
+#name test;
-%left x
+#prec (
+ #left x
+);
s
: foo
@@ -774,14 +922,16 @@ s
foo
: 'foo';
`,
- errs: []*SemanticError{semErrMDInvalidParam},
+ errs: []*SemanticError{semErrDirInvalidParam},
},
{
- caption: "the `%left` cannot take a non-terminal symbol",
+ caption: "the `#left` directive cannot take a non-terminal symbol",
specSrc: `
-%name test
+#name test;
-%left s
+#prec (
+ #left s
+);
s
: foo
@@ -790,14 +940,16 @@ s
foo
: 'foo';
`,
- errs: []*SemanticError{semErrMDInvalidParam},
+ errs: []*SemanticError{semErrDirInvalidParam},
},
{
- caption: "the `%left` cannot take a pattern parameter",
+ caption: "the `#left` directive cannot take a pattern parameter",
specSrc: `
-%name test
+#name test;
-%left "foo"
+#prec (
+ #left "foo"
+);
s
: foo
@@ -806,14 +958,16 @@ s
foo
: 'foo';
`,
- errs: []*SemanticError{semErrMDInvalidParam},
+ errs: []*SemanticError{semErrDirInvalidParam},
},
{
- caption: "the `%left` cannot take a string parameter",
+ caption: "the `#left` directive cannot take a string parameter",
specSrc: `
-%name test
+#name test;
-%left 'foo'
+#prec (
+ #left 'foo'
+);
s
: foo
@@ -822,14 +976,34 @@ s
foo
: 'foo';
`,
- errs: []*SemanticError{semErrMDInvalidParam},
+ errs: []*SemanticError{semErrDirInvalidParam},
},
{
- caption: "the `%left` cannot be specified multiple times for a symbol",
+ caption: "the `#left` directive cannot take a directive parameter",
specSrc: `
-%name test
+#name test;
-%left foo foo
+#prec (
+ #left ()
+);
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ {
+ caption: "the `#left` dirctive cannot be specified multiple times for a symbol",
+ specSrc: `
+#name test;
+
+#prec (
+ #left foo foo
+);
s
: foo
@@ -843,10 +1017,12 @@ foo
{
caption: "a symbol cannot have different precedence",
specSrc: `
-%name test
+#name test;
-%left foo
-%left foo
+#prec (
+ #left foo
+ #left foo
+);
s
: foo
@@ -860,10 +1036,12 @@ foo
{
caption: "a symbol cannot have different associativity",
specSrc: `
-%name test
+#name test;
-%right foo
-%left foo
+#prec (
+ #right foo
+ #left foo
+);
s
: foo
@@ -876,13 +1054,15 @@ foo
},
}
- rightTests := []*specErrTest{
+ rightDirTests := []*specErrTest{
{
- caption: "the `%right` needs ID parameters",
+ caption: "the `#right` directive needs ID parameters",
specSrc: `
-%name test
+#name test;
-%right
+#prec (
+ #right
+);
s
: foo
@@ -891,14 +1071,16 @@ s
foo
: 'foo';
`,
- errs: []*SemanticError{semErrMDInvalidParam},
+ errs: []*SemanticError{semErrDirInvalidParam},
},
{
- caption: "the `%right` cannot take an undefined symbol",
+ caption: "the `#right` directive cannot take an undefined symbol",
specSrc: `
-%name test
+#name test;
-%right x
+#prec (
+ #right x
+);
s
: foo
@@ -907,14 +1089,16 @@ s
foo
: 'foo';
`,
- errs: []*SemanticError{semErrMDInvalidParam},
+ errs: []*SemanticError{semErrDirInvalidParam},
},
{
- caption: "the `%right` cannot take a non-terminal symbol",
+ caption: "the `#right` directive cannot take a non-terminal symbol",
specSrc: `
-%name test
+#name test;
-%right s
+#prec (
+ #right s
+);
s
: foo
@@ -923,14 +1107,16 @@ s
foo
: 'foo';
`,
- errs: []*SemanticError{semErrMDInvalidParam},
+ errs: []*SemanticError{semErrDirInvalidParam},
},
{
- caption: "the `%right` cannot take a pattern parameter",
+ caption: "the `#right` directive cannot take a pattern parameter",
specSrc: `
-%name test
+#name test;
-%right "foo"
+#prec (
+ #right "foo"
+);
s
: foo
@@ -939,14 +1125,16 @@ s
foo
: 'foo';
`,
- errs: []*SemanticError{semErrMDInvalidParam},
+ errs: []*SemanticError{semErrDirInvalidParam},
},
{
- caption: "the `%right` cannot take a string parameter",
+ caption: "the `#right` directive cannot take a string parameter",
specSrc: `
-%name test
+#name test;
-%right 'foo'
+#prec (
+ #right 'foo'
+);
s
: foo
@@ -955,14 +1143,34 @@ s
foo
: 'foo';
`,
- errs: []*SemanticError{semErrMDInvalidParam},
+ errs: []*SemanticError{semErrDirInvalidParam},
},
{
- caption: "the `%right` cannot be specified multiple times for a symbol",
+ caption: "the `#right` directive cannot take a directive group parameter",
specSrc: `
-%name test
+#name test;
-%right foo foo
+#prec (
+ #right ()
+);
+
+s
+ : foo
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ {
+ caption: "the `#right` directive cannot be specified multiple times for a symbol",
+ specSrc: `
+#name test;
+
+#prec (
+ #right foo foo
+);
s
: foo
@@ -976,10 +1184,12 @@ foo
{
caption: "a symbol cannot have different precedence",
specSrc: `
-%name test
+#name test;
-%right foo
-%right foo
+#prec (
+ #right foo
+ #right foo
+);
s
: foo
@@ -993,10 +1203,12 @@ foo
{
caption: "a symbol cannot have different associativity",
specSrc: `
-%name test
+#name test;
-%left foo
-%right foo
+#prec (
+ #left foo
+ #right foo
+);
s
: foo
@@ -1013,7 +1225,7 @@ foo
{
caption: "cannot use the error symbol as a non-terminal symbol",
specSrc: `
-%name test
+#name test;
s
: error
@@ -1032,7 +1244,7 @@ foo: 'foo';
{
caption: "cannot use the error symbol as a terminal symbol",
specSrc: `
-%name test
+#name test;
s
: error
@@ -1045,7 +1257,7 @@ error: 'error';
{
caption: "cannot use the error symbol as a terminal symbol, even if given the skip directive",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -1064,7 +1276,7 @@ error #skip
{
caption: "the `#ast` directive needs ID or label prameters",
specSrc: `
-%name test
+#name test;
s
: foo #ast
@@ -1078,7 +1290,7 @@ foo
{
caption: "the `#ast` directive cannot take a pattern parameter",
specSrc: `
-%name test
+#name test;
s
: foo #ast "foo"
@@ -1092,7 +1304,7 @@ foo
{
caption: "the `#ast` directive cannot take a string parameter",
specSrc: `
-%name test
+#name test;
s
: foo #ast 'foo'
@@ -1104,9 +1316,23 @@ foo
errs: []*SemanticError{semErrDirInvalidParam},
},
{
+ caption: "the `#ast` directive cannot take a directive group parameter",
+ specSrc: `
+#name test;
+
+s
+ : foo #ast ()
+ ;
+
+foo
+ : "foo";
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ {
caption: "a parameter of the `#ast` directive must be either a symbol or a label in an alternative",
specSrc: `
-%name test
+#name test;
s
: foo bar #ast foo x
@@ -1122,7 +1348,7 @@ bar
{
caption: "a symbol in a different alternative cannot be a parameter of the `#ast` directive",
specSrc: `
-%name test
+#name test;
s
: foo #ast bar
@@ -1139,7 +1365,7 @@ bar
{
caption: "a label in a different alternative cannot be a parameter of the `#ast` directive",
specSrc: `
-%name test
+#name test;
s
: foo #ast b
@@ -1156,7 +1382,7 @@ bar
{
caption: "a symbol can appear in the `#ast` directive only once",
specSrc: `
-%name test
+#name test;
s
: foo #ast foo foo
@@ -1170,7 +1396,7 @@ foo
{
caption: "a label can appear in the `#ast` directive only once",
specSrc: `
-%name test
+#name test;
s
: foo@x #ast x x
@@ -1184,7 +1410,7 @@ foo
{
caption: "a symbol can appear in the `#ast` directive only once, even if the symbol has a label",
specSrc: `
-%name test
+#name test;
s
: foo@x #ast foo x
@@ -1198,7 +1424,7 @@ foo
{
caption: "symbol `foo` is ambiguous because it appears in an alternative twice",
specSrc: `
-%name test
+#name test;
s
: foo foo #ast foo
@@ -1212,7 +1438,7 @@ foo
{
caption: "symbol `foo` is ambiguous because it appears in an alternative twice, even if one of them has a label",
specSrc: `
-%name test
+#name test;
s
: foo@x foo #ast foo
@@ -1226,7 +1452,7 @@ foo
{
caption: "the expansion operator cannot be applied to a terminal symbol",
specSrc: `
-%name test
+#name test;
s
: foo #ast foo...
@@ -1240,7 +1466,7 @@ foo
{
caption: "the expansion operator cannot be applied to a pattern",
specSrc: `
-%name test
+#name test;
s
: foo "bar"@b #ast foo b...
@@ -1254,7 +1480,7 @@ foo
{
caption: "the expansion operator cannot be applied to a string",
specSrc: `
-%name test
+#name test;
s
: foo 'bar'@b #ast foo b...
@@ -1267,11 +1493,11 @@ foo
},
}
- precDirTests := []*specErrTest{
+ altPrecDirTests := []*specErrTest{
{
caption: "the `#prec` directive needs an ID parameter",
specSrc: `
-%name test
+#name test;
s
: foo #prec
@@ -1285,7 +1511,7 @@ foo
{
caption: "the `#prec` directive cannot take an undefined symbol",
specSrc: `
-%name test
+#name test;
s
: foo #prec x
@@ -1299,7 +1525,7 @@ foo
{
caption: "the `#prec` directive cannot take a non-terminal symbol",
specSrc: `
-%name test
+#name test;
s
: a #prec b
@@ -1322,7 +1548,7 @@ bar
{
caption: "the `#prec` directive cannot take a pattern parameter",
specSrc: `
-%name test
+#name test;
s
: foo #prec "foo"
@@ -1336,7 +1562,7 @@ foo
{
caption: "the `#prec` directive cannot take a string parameter",
specSrc: `
-%name test
+#name test;
s
: foo #prec 'foo'
@@ -1348,9 +1574,23 @@ foo
errs: []*SemanticError{semErrDirInvalidParam},
},
{
+ caption: "the `#prec` directive cannot take a directive parameter",
+ specSrc: `
+#name test;
+
+s
+ : foo #prec ()
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ {
caption: "a symbol the `#prec` directive takes must be given precedence explicitly",
specSrc: `
-%name test
+#name test;
s
: foo bar #prec foo
@@ -1369,9 +1609,7 @@ bar
{
caption: "the `#recover` directive cannot take an ID parameter",
specSrc: `
-%name test
-
-%name test
+#name test;
s
: foo #recover foo
@@ -1385,9 +1623,7 @@ foo
{
caption: "the `#recover` directive cannot take a pattern parameter",
specSrc: `
-%name test
-
-%name test
+#name test;
s
: foo #recover "foo"
@@ -1401,9 +1637,7 @@ foo
{
caption: "the `#recover` directive cannot take a string parameter",
specSrc: `
-%name test
-
-%name test
+#name test;
s
: foo #recover 'foo'
@@ -1414,13 +1648,27 @@ foo
`,
errs: []*SemanticError{semErrDirInvalidParam},
},
+ {
+ caption: "the `#recover` directive cannot take a directive group parameter",
+ specSrc: `
+#name test;
+
+s
+ : foo #recover ()
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
}
fragmentTests := []*specErrTest{
{
caption: "a production cannot contain a fragment",
specSrc: `
-%name test
+#name test;
s
: f
@@ -1434,7 +1682,7 @@ fragment f
{
caption: "fragments cannot be duplicated",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -1455,7 +1703,7 @@ fragment f
{
caption: "the `#alias` directive needs a string parameter",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -1469,7 +1717,7 @@ foo #alias
{
caption: "the `#alias` directive takes just one string parameter",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -1483,7 +1731,7 @@ foo #alias 'Foo' 'FOO'
{
caption: "the `#alias` directive cannot take an ID parameter",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -1497,7 +1745,7 @@ foo #alias Foo
{
caption: "the `#alias` directive cannot take a pattern parameter",
specSrc: `
-%name test
+#name test;
s
: foo
@@ -1508,13 +1756,27 @@ foo #alias "Foo"
`,
errs: []*SemanticError{semErrDirInvalidParam},
},
+ {
+ caption: "the `#alias` directive cannot take a directive group parameter",
+ specSrc: `
+#name test;
+
+s
+ : foo
+ ;
+
+foo #alias ()
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
}
- modeTests := []*specErrTest{
+ modeDirTests := []*specErrTest{
{
caption: "the `#mode` directive needs an ID parameter",
specSrc: `
-%name test
+#name test;
s
: foo bar
@@ -1530,7 +1792,7 @@ bar #mode
{
caption: "the `#mode` directive cannot take a pattern parameter",
specSrc: `
-%name test
+#name test;
s
: foo bar
@@ -1546,7 +1808,7 @@ bar #mode "mode_1"
{
caption: "the `#mode` directive cannot take a string parameter",
specSrc: `
-%name test
+#name test;
s
: foo bar
@@ -1559,13 +1821,29 @@ bar #mode 'mode_1'
`,
errs: []*SemanticError{semErrDirInvalidParam},
},
+ {
+ caption: "the `#mode` directive cannot take a directive group parameter",
+ specSrc: `
+#name test;
+
+s
+ : foo bar
+ ;
+
+foo #push mode_1
+ : 'foo';
+bar #mode ()
+ : 'bar';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
}
- pushTests := []*specErrTest{
+ pushDirTests := []*specErrTest{
{
caption: "the `#push` directive needs an ID parameter",
specSrc: `
-%name test
+#name test;
s
: foo bar
@@ -1581,7 +1859,7 @@ bar #mode mode_1
{
caption: "the `#push` directive takes just one ID parameter",
specSrc: `
-%name test
+#name test;
s
: foo bar
@@ -1597,7 +1875,7 @@ bar #mode mode_1
{
caption: "the `#push` directive cannot take a pattern parameter",
specSrc: `
-%name test
+#name test;
s
: foo bar
@@ -1613,7 +1891,7 @@ bar #mode mode_1
{
caption: "the `#push` directive cannot take a string parameter",
specSrc: `
-%name test
+#name test;
s
: foo bar
@@ -1626,13 +1904,29 @@ bar #mode mode_1
`,
errs: []*SemanticError{semErrDirInvalidParam},
},
+ {
+ caption: "the `#push` directive cannot take a directive group parameter",
+ specSrc: `
+#name test;
+
+s
+ : foo bar
+ ;
+
+foo #push ()
+ : 'foo';
+bar #mode mode_1
+ : 'bar';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
}
- popTests := []*specErrTest{
+ popDirTests := []*specErrTest{
{
caption: "the `#pop` directive cannot take an ID parameter",
specSrc: `
-%name test
+#name test;
s
: foo bar baz
@@ -1650,7 +1944,7 @@ baz #pop mode_1
{
caption: "the `#pop` directive cannot take a pattern parameter",
specSrc: `
-%name test
+#name test;
s
: foo bar baz
@@ -1668,7 +1962,7 @@ baz #pop "mode_1"
{
caption: "the `#pop` directive cannot take a string parameter",
specSrc: `
-%name test
+#name test;
s
: foo bar baz
@@ -1683,13 +1977,31 @@ baz #pop 'mode_1'
`,
errs: []*SemanticError{semErrDirInvalidParam},
},
+ {
+ caption: "the `#pop` directive cannot take a directive parameter",
+ specSrc: `
+#name test;
+
+s
+ : foo bar baz
+ ;
+
+foo #push mode_1
+ : 'foo';
+bar #mode mode_1
+ : 'bar';
+baz #pop ()
+ : 'baz';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
}
skipDirTests := []*specErrTest{
{
caption: "the `#skip` directive cannot take an ID parameter",
specSrc: `
-%name test
+#name test;
s
: foo bar
@@ -1705,7 +2017,7 @@ bar
{
caption: "the `#skip` directive cannot take a pattern parameter",
specSrc: `
-%name test
+#name test;
s
: foo bar
@@ -1721,7 +2033,7 @@ bar
{
caption: "the `#skip` directive cannot take a string parameter",
specSrc: `
-%name test
+#name test;
s
: foo bar
@@ -1735,9 +2047,25 @@ bar
errs: []*SemanticError{semErrDirInvalidParam},
},
{
+ caption: "the `#skip` directive cannot take a directive group parameter",
+ specSrc: `
+#name test;
+
+s
+ : foo bar
+ ;
+
+foo #skip ()
+ : 'foo';
+bar
+ : 'bar';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ {
caption: "a terminal symbol used in productions cannot have the skip directive",
specSrc: `
-%name test
+#name test;
s
: foo bar
@@ -1754,18 +2082,19 @@ bar
var tests []*specErrTest
tests = append(tests, prodTests...)
- tests = append(tests, nameTests...)
- tests = append(tests, leftTests...)
- tests = append(tests, rightTests...)
+ tests = append(tests, nameDirTests...)
+ tests = append(tests, precDirTests...)
+ tests = append(tests, leftDirTests...)
+ tests = append(tests, rightDirTests...)
tests = append(tests, errorSymTests...)
tests = append(tests, astDirTests...)
- tests = append(tests, precDirTests...)
+ tests = append(tests, altPrecDirTests...)
tests = append(tests, recoverDirTests...)
tests = append(tests, fragmentTests...)
tests = append(tests, aliasDirTests...)
- tests = append(tests, modeTests...)
- tests = append(tests, pushTests...)
- tests = append(tests, popTests...)
+ tests = append(tests, modeDirTests...)
+ tests = append(tests, pushDirTests...)
+ tests = append(tests, popDirTests...)
tests = append(tests, skipDirTests...)
for _, test := range tests {
t.Run(test.caption, func(t *testing.T) {
diff --git a/grammar/lalr1_test.go b/grammar/lalr1_test.go
index beb2707..94dfd65 100644
--- a/grammar/lalr1_test.go
+++ b/grammar/lalr1_test.go
@@ -10,7 +10,7 @@ import (
func TestGenLALR1Automaton(t *testing.T) {
// This grammar belongs to LALR(1) class, not SLR(1).
src := `
-%name test
+#name test;
S: L eq R | R;
L: ref R | id;
diff --git a/grammar/lr0_test.go b/grammar/lr0_test.go
index cde3f0a..1b20d78 100644
--- a/grammar/lr0_test.go
+++ b/grammar/lr0_test.go
@@ -17,7 +17,7 @@ type expectedLRState struct {
func TestGenLR0Automaton(t *testing.T) {
src := `
-%name test
+#name test;
expr
: expr add term
@@ -227,7 +227,7 @@ id: "[A-Za-z_][0-9A-Za-z_]*";
func TestLR0AutomatonContainingEmptyProduction(t *testing.T) {
src := `
-%name test
+#name test;
s
: foo bar
diff --git a/grammar/parsing_table_test.go b/grammar/parsing_table_test.go
index 833a4d4..522ec1c 100644
--- a/grammar/parsing_table_test.go
+++ b/grammar/parsing_table_test.go
@@ -16,7 +16,7 @@ type expectedState struct {
func TestGenLALRParsingTable(t *testing.T) {
src := `
-%name test
+#name test;
S: L eq R | R;
L: ref R | id;
@@ -288,7 +288,7 @@ id: "[A-Za-z0-9_]+";
func TestGenSLRParsingTable(t *testing.T) {
src := `
-%name test
+#name test;
expr
: expr add term
diff --git a/grammar/semantic_error.go b/grammar/semantic_error.go
index c81cb5f..a843719 100644
--- a/grammar/semantic_error.go
+++ b/grammar/semantic_error.go
@@ -15,9 +15,7 @@ func (e *SemanticError) Error() string {
}
var (
- semErrMDInvalidName = newSemanticError("invalid meta data name")
- semErrMDInvalidParam = newSemanticError("invalid parameter")
- semErrMDMissingName = newSemanticError("name is missing")
+ semErrNoGrammarName = newSemanticError("name is missing")
semErrDuplicateAssoc = newSemanticError("associativity and precedence cannot be specified multiple times for a symbol")
semErrUndefinedPrec = newSemanticError("symbol must has precedence")
semErrUnusedProduction = newSemanticError("unused production")
diff --git a/grammar/slr1_test.go b/grammar/slr1_test.go
index 954446f..6748802 100644
--- a/grammar/slr1_test.go
+++ b/grammar/slr1_test.go
@@ -9,7 +9,7 @@ import (
func TestGenSLR1Automaton(t *testing.T) {
src := `
-%name test
+#name test;
expr
: expr add term