aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md16
-rw-r--r--compiler/compiler_test.go103
-rw-r--r--spec/spec.go15
3 files changed, 123 insertions, 11 deletions
diff --git a/README.md b/README.md
index eef1233..bb0f558 100644
--- a/README.md
+++ b/README.md
@@ -113,14 +113,14 @@ top level object:
entry object:
-| Field | Type | Nullable | Description |
-|----------|------------------|----------|-----------------------------------------------------------------------------------------------|
-| kind | string | false | A name of a token kind |
-| pattern | string | false | A pattern in a regular expression |
-| modes | array of strings | true | Mode names that an entry is enabled in (default: "default") |
-| push | string | true | A mode name that the lexer pushes to own mode stack when a token matching the pattern appears |
-| pop | bool | true | When `pop` is `true`, the lexer pops a mode from own mode stack. |
-| fragment | bool | true | When `fragment` is `true`, its entry is a fragment. |
+| Field | Type | Nullable | Description |
+|----------|------------------|----------|-----------------------------------------------------------------------------------------------------------------------|
+| kind | string | false | A name of a token kind. The name must be unique, but duplicate names between fragments and non-fragments are allowed. |
+| pattern | string | false | A pattern in a regular expression |
+| modes | array of strings | true | Mode names that an entry is enabled in (default: "default") |
+| push | string | true | A mode name that the lexer pushes to own mode stack when a token matching the pattern appears |
+| pop | bool | true | When `pop` is `true`, the lexer pops a mode from own mode stack. |
+| fragment | bool | true | When `fragment` is `true`, its entry is a fragment. |
See [Regular Expression Syntax](#regular-expression-syntax) for more details on the regular expression syntax.
diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go
new file mode 100644
index 0000000..c76bb24
--- /dev/null
+++ b/compiler/compiler_test.go
@@ -0,0 +1,103 @@
+package compiler
+
+import (
+ "encoding/json"
+ "fmt"
+ "testing"
+
+ "github.com/nihei9/maleeni/spec"
+)
+
+func TestCompile(t *testing.T) {
+ tests := []struct {
+ Caption string
+ Spec string
+ Err bool
+ }{
+ {
+ Caption: "allow duplicates names between fragments and non-fragments",
+ Spec: `
+{
+ "entries": [
+ {
+ "kind": "a2z",
+ "pattern": "\\f{a2z}"
+ },
+ {
+ "fragment": true,
+ "kind": "a2z",
+ "pattern": "[a-z]"
+ }
+ ]
+}
+`,
+ },
+ {
+ Caption: "don't allow duplicates names in non-fragments",
+ Spec: `
+{
+ "entries": [
+ {
+ "kind": "a2z",
+ "pattern": "a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z"
+ },
+ {
+ "kind": "a2z",
+ "pattern": "[a-z]"
+ }
+ ]
+}
+`,
+ Err: true,
+ },
+ {
+ Caption: "don't allow duplicates names in fragments",
+ Spec: `
+{
+ "entries": [
+ {
+ "kind": "a2z",
+ "pattern": "\\f{a2z}"
+ },
+ {
+ "fragments": true,
+ "kind": "a2z",
+ "pattern": "a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z"
+ },
+ {
+ "fragments": true,
+ "kind": "a2z",
+ "pattern": "[a-z]"
+ }
+ ]
+}
+`,
+ Err: true,
+ },
+ }
+ for i, tt := range tests {
+ t.Run(fmt.Sprintf("#%v %s", i, tt.Caption), func(t *testing.T) {
+ lspec := &spec.LexSpec{}
+ err := json.Unmarshal([]byte(tt.Spec), lspec)
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ clspec, err := Compile(lspec)
+ if tt.Err {
+ if err == nil {
+ t.Fatalf("expected an error")
+ }
+ if clspec != nil {
+ t.Fatalf("Compile function mustn't return a compiled specification")
+ }
+ } else {
+ if err != nil {
+ t.Fatalf("unexpected error")
+ }
+ if clspec == nil {
+ t.Fatalf("Compile function must return a compiled specification")
+ }
+ }
+ })
+ }
+}
diff --git a/spec/spec.go b/spec/spec.go
index 61955ac..829008a 100644
--- a/spec/spec.go
+++ b/spec/spec.go
@@ -143,11 +143,20 @@ func (s *LexSpec) Validate() error {
}
{
ks := map[string]struct{}{}
+ fks := map[string]struct{}{}
for _, e := range s.Entries {
- if _, exist := ks[e.Kind.String()]; exist {
- return fmt.Errorf("kinds `%v` are duplicates", e.Kind)
+ // Allow duplicate names between fragments and non-fragments.
+ if e.Fragment {
+ if _, exist := fks[e.Kind.String()]; exist {
+ return fmt.Errorf("kinds `%v` are duplicates", e.Kind)
+ }
+ fks[e.Kind.String()] = struct{}{}
+ } else {
+ if _, exist := ks[e.Kind.String()]; exist {
+ return fmt.Errorf("kinds `%v` are duplicates", e.Kind)
+ }
+ ks[e.Kind.String()] = struct{}{}
}
- ks[e.Kind.String()] = struct{}{}
}
}
return nil