diff options
author | Ryo Nihei <nihei.dev@gmail.com> | 2022-04-16 11:37:08 +0900 |
---|---|---|
committer | Ryo Nihei <nihei.dev@gmail.com> | 2022-04-16 15:24:09 +0900 |
commit | 8340b9f1dc1339d88762f361e284ea4ad6c079d7 (patch) | |
tree | 6eadd8a15e01283b6c89900f5a7f9ede6c9238c4 | |
parent | Prohibit using the same element multiple times in the #ast directive (diff) | |
download | urubu-8340b9f1dc1339d88762f361e284ea4ad6c079d7.tar.gz urubu-8340b9f1dc1339d88762f361e284ea4ad6c079d7.tar.xz |
Add tests for compiler
-rw-r--r-- | grammar/grammar_test.go | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/grammar/grammar_test.go b/grammar/grammar_test.go index 2bb2358..1448d3c 100644 --- a/grammar/grammar_test.go +++ b/grammar/grammar_test.go @@ -8,6 +8,216 @@ import ( "github.com/nihei9/vartan/spec" ) +func TestGrammarBuilderOK(t *testing.T) { + type okTest struct { + caption string + specSrc string + validate func(t *testing.T, g *Grammar) + } + + nameTests := []*okTest{ + { + caption: "the `%name` can be the same identifier as a non-terminal symbol", + specSrc: ` +%name s +s + : foo + ; + +foo + : 'foo'; +`, + validate: func(t *testing.T, g *Grammar) { + expected := "s" + if g.name != expected { + t.Fatalf("unexpected name: want: %v, got: %v", expected, g.name) + } + }, + }, + { + caption: "the `%name` can be the same identifier as a terminal symbol", + specSrc: ` +%name foo +s + : foo + ; + +foo + : 'foo'; +`, + validate: func(t *testing.T, g *Grammar) { + expected := "foo" + if g.name != expected { + t.Fatalf("unexpected name: want: %v, got: %v", expected, g.name) + } + }, + }, + { + caption: "the `%name` can be the same identifier as the error symbol", + specSrc: ` +%name error +s + : foo + | error + ; + +foo + : 'foo'; +`, + validate: func(t *testing.T, g *Grammar) { + expected := "error" + if g.name != expected { + t.Fatalf("unexpected name: want: %v, got: %v", expected, g.name) + } + }, + }, + { + caption: "the `%name` can be the same identifier as a fragment", + specSrc: ` +%name f +s + : foo + ; + +foo + : "\f{f}"; +fragment f + : 'foo'; +`, + validate: func(t *testing.T, g *Grammar) { + expected := "f" + if g.name != expected { + t.Fatalf("unexpected name: want: %v, got: %v", expected, g.name) + } + }, + }, + } + + modeTests := []*okTest{ + { + caption: "a `#mode` can be the same identifier as a non-terminal symbol", + specSrc: ` +%name test +s + : foo bar + ; + +foo #push s + : 'foo'; +bar #mode s + : 'bar'; +`, + validate: func(t *testing.T, g *Grammar) { + kind := "bar" + expectedMode := "s" + for _, e := range g.lexSpec.Entries { + if e.Kind.String() == kind && e.Modes[0].String() == expectedMode { + return + } + } + t.Fatalf("symbol having expected mode was not found: want: %v #mode %v", kind, expectedMode) + }, + }, + { + caption: "a `#mode` can be the same identifier as a terminal symbol", + specSrc: ` +%name test +s + : foo bar + ; + +foo #push bar + : 'foo'; +bar #mode bar + : 'bar'; +`, + validate: func(t *testing.T, g *Grammar) { + kind := "bar" + expectedMode := "bar" + for _, e := range g.lexSpec.Entries { + if e.Kind.String() == kind && e.Modes[0].String() == expectedMode { + return + } + } + t.Fatalf("symbol having expected mode was not found: want: %v #mode %v", kind, expectedMode) + }, + }, + { + caption: "a `#mode` can be the same identifier as the error symbol", + specSrc: ` +%name test +s + : foo bar + | error + ; + +foo #push error + : 'foo'; +bar #mode error + : 'bar'; +`, + validate: func(t *testing.T, g *Grammar) { + kind := "bar" + expectedMode := "error" + for _, e := range g.lexSpec.Entries { + if e.Kind.String() == kind && e.Modes[0].String() == expectedMode { + return + } + } + t.Fatalf("symbol having expected mode was not found: want: %v #mode %v", kind, expectedMode) + }, + }, + { + caption: "a `#mode` can be the same identifier as a fragment", + specSrc: ` +%name test +s + : foo bar + ; + +foo #push f + : "\f{f}"; +bar #mode f + : 'bar'; +fragment f + : 'foo'; +`, + validate: func(t *testing.T, g *Grammar) { + kind := "bar" + expectedMode := "f" + for _, e := range g.lexSpec.Entries { + if e.Kind.String() == kind && e.Modes[0].String() == expectedMode { + return + } + } + t.Fatalf("symbol having expected mode was not found: want: %v #mode %v", kind, expectedMode) + }, + }, + } + + var tests []*okTest + tests = append(tests, nameTests...) + tests = append(tests, modeTests...) + + for _, test := range tests { + t.Run(test.caption, func(t *testing.T) { + ast, err := spec.Parse(strings.NewReader(test.specSrc)) + if err != nil { + t.Fatal(err) + } + + b := GrammarBuilder{ + AST: ast, + } + g, err := b.Build() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + test.validate(t, g) + }) + } +} + func TestGrammarBuilderSpecError(t *testing.T) { type specErrTest struct { caption string @@ -786,6 +996,20 @@ foo errs: []*SemanticError{semErrDuplicateElem}, }, { + caption: "a label can appear in the `#ast` directive only once", + specSrc: ` +%name test + +s + : foo@x #ast x x + ; + +foo + : 'foo'; +`, + errs: []*SemanticError{semErrDuplicateElem}, + }, + { caption: "a symbol can appear in the `#ast` directive only once, even if the symbol has a label", specSrc: ` %name test |