diff options
Diffstat (limited to 'grammar')
-rw-r--r-- | grammar/first_test.go | 10 | ||||
-rw-r--r-- | grammar/follow_test.go | 10 | ||||
-rw-r--r-- | grammar/grammar.go | 71 | ||||
-rw-r--r-- | grammar/grammar_test.go | 683 | ||||
-rw-r--r-- | grammar/lalr1_test.go | 2 | ||||
-rw-r--r-- | grammar/lr0_test.go | 4 | ||||
-rw-r--r-- | grammar/parsing_table_test.go | 4 | ||||
-rw-r--r-- | grammar/semantic_error.go | 4 | ||||
-rw-r--r-- | grammar/slr1_test.go | 2 |
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 |