aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyo Nihei <nihei.dev@gmail.com>2022-04-16 11:37:08 +0900
committerRyo Nihei <nihei.dev@gmail.com>2022-04-16 15:24:09 +0900
commit8340b9f1dc1339d88762f361e284ea4ad6c079d7 (patch)
tree6eadd8a15e01283b6c89900f5a7f9ede6c9238c4
parentProhibit using the same element multiple times in the #ast directive (diff)
downloadurubu-8340b9f1dc1339d88762f361e284ea4ad6c079d7.tar.gz
urubu-8340b9f1dc1339d88762f361e284ea4ad6c079d7.tar.xz
Add tests for compiler
-rw-r--r--grammar/grammar_test.go224
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