clarity-pattern-parser 4.0.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/README.md +176 -1
  2. package/TODO.md +22 -2
  3. package/dist/ast/Node.d.ts +43 -11
  4. package/dist/ast/Visitor.d.ts +31 -31
  5. package/dist/index.browser.js +1248 -1495
  6. package/dist/index.browser.js.map +1 -1
  7. package/dist/index.d.ts +12 -16
  8. package/dist/index.esm.js +1218 -1460
  9. package/dist/index.esm.js.map +1 -1
  10. package/dist/index.js +1217 -1464
  11. package/dist/index.js.map +1 -1
  12. package/dist/patterns/And.d.ts +37 -24
  13. package/dist/patterns/Cursor.d.ts +35 -0
  14. package/dist/patterns/CursorHistory.d.ts +30 -0
  15. package/dist/patterns/Literal.d.ts +36 -19
  16. package/dist/patterns/Not.d.ts +26 -11
  17. package/dist/patterns/Or.d.ts +31 -22
  18. package/dist/patterns/ParseError.d.ts +6 -8
  19. package/dist/patterns/ParseResult.d.ts +6 -0
  20. package/dist/patterns/Pattern.d.ts +17 -26
  21. package/dist/patterns/Reference.d.ts +31 -12
  22. package/dist/patterns/Regex.d.ts +42 -21
  23. package/dist/patterns/Repeat.d.ts +38 -20
  24. package/dist/patterns/clonePatterns.d.ts +2 -0
  25. package/dist/patterns/filterOutNull.d.ts +2 -0
  26. package/dist/patterns/findPattern.d.ts +2 -0
  27. package/dist/patterns/getNextPattern.d.ts +2 -0
  28. package/jest.config.js +2 -1
  29. package/package.json +4 -5
  30. package/rollup.config.js +1 -1
  31. package/src/ast/Node.test.ts +254 -0
  32. package/src/ast/Node.ts +171 -23
  33. package/src/index.ts +11 -19
  34. package/src/intellisense/AutoComplete.test.ts +72 -0
  35. package/src/intellisense/AutoComplete.ts +146 -0
  36. package/src/intellisense/Suggestion.ts +13 -0
  37. package/src/intellisense/SuggestionOption.ts +4 -0
  38. package/src/{tests/cssPatterns → intellisense/css}/cssValue.ts +1 -1
  39. package/src/{tests/cssPatterns → intellisense/css}/divider.ts +2 -1
  40. package/src/intellisense/css/hex.ts +6 -0
  41. package/src/{tests/cssPatterns → intellisense/css}/method.ts +8 -9
  42. package/src/intellisense/css/name.ts +5 -0
  43. package/src/{tests/javascriptPatterns → intellisense/css}/number.ts +3 -3
  44. package/src/intellisense/css/spaces.ts +6 -0
  45. package/src/intellisense/css/unit.ts +10 -0
  46. package/src/{tests/cssPatterns → intellisense/css}/value.ts +1 -1
  47. package/src/{tests/cssPatterns → intellisense/css}/values.ts +1 -1
  48. package/src/intellisense/javascript/Javascript.test.ts +203 -0
  49. package/src/intellisense/javascript/arrayLiteral.ts +25 -0
  50. package/src/intellisense/javascript/deleteStatement.ts +14 -0
  51. package/src/intellisense/javascript/escapedCharacter.ts +50 -0
  52. package/src/intellisense/javascript/exponent.ts +26 -0
  53. package/src/intellisense/javascript/expression.ts +87 -0
  54. package/src/intellisense/javascript/expressionStatement.ts +29 -0
  55. package/src/intellisense/javascript/fraction.ts +13 -0
  56. package/src/intellisense/javascript/infixOperator.ts +36 -0
  57. package/src/intellisense/javascript/integer.ts +7 -0
  58. package/src/intellisense/javascript/invocation.ts +28 -0
  59. package/src/intellisense/javascript/literal.ts +14 -0
  60. package/src/intellisense/javascript/name.ts +3 -0
  61. package/src/intellisense/javascript/numberLiteral.ts +10 -0
  62. package/src/intellisense/javascript/objectLiteral.ts +30 -0
  63. package/src/intellisense/javascript/optionalSpaces.ts +3 -0
  64. package/src/intellisense/javascript/parameters.ts +20 -0
  65. package/src/intellisense/javascript/prefixOperator.ts +13 -0
  66. package/src/intellisense/javascript/propertyAccess.ts +23 -0
  67. package/src/intellisense/javascript/stringLiteral.ts +28 -0
  68. package/src/patterns/And.test.ts +299 -0
  69. package/src/patterns/And.ts +222 -119
  70. package/src/patterns/Cursor.test.ts +93 -0
  71. package/src/patterns/Cursor.ts +130 -0
  72. package/src/patterns/CursorHistory.test.ts +54 -0
  73. package/src/patterns/CursorHistory.ts +95 -0
  74. package/src/patterns/Literal.test.ts +134 -0
  75. package/src/patterns/Literal.ts +151 -61
  76. package/src/patterns/Not.test.ts +88 -0
  77. package/src/patterns/Not.ts +74 -33
  78. package/src/patterns/Or.test.ts +105 -0
  79. package/src/patterns/Or.ts +106 -98
  80. package/src/patterns/ParseError.ts +3 -7
  81. package/src/patterns/ParseResult.ts +7 -0
  82. package/src/patterns/Pattern.ts +18 -150
  83. package/src/patterns/Reference.test.ts +104 -0
  84. package/src/patterns/Reference.ts +94 -94
  85. package/src/patterns/Regex.test.ts +101 -0
  86. package/src/patterns/Regex.ts +129 -60
  87. package/src/patterns/Repeat.test.ts +196 -0
  88. package/src/patterns/Repeat.ts +208 -104
  89. package/src/patterns/clonePatterns.ts +5 -0
  90. package/src/patterns/filterOutNull.ts +13 -0
  91. package/src/patterns/findPattern.ts +25 -0
  92. package/src/patterns/getNextPattern.test.ts +39 -0
  93. package/src/patterns/getNextPattern.ts +18 -0
  94. package/src/Cursor.ts +0 -141
  95. package/src/CursorHistory.ts +0 -146
  96. package/src/TextSuggester.ts +0 -317
  97. package/src/ast/Visitor.ts +0 -271
  98. package/src/patterns/LookAhead.ts +0 -32
  99. package/src/patterns/Recursive.ts +0 -92
  100. package/src/tests/And.test.ts +0 -180
  101. package/src/tests/ComplexExamples.test.ts +0 -86
  102. package/src/tests/CssPatterns.test.ts +0 -90
  103. package/src/tests/CursorHistory.test.ts +0 -107
  104. package/src/tests/Cusor.test.ts +0 -174
  105. package/src/tests/HtmlPatterns.test.ts +0 -34
  106. package/src/tests/Literal.test.ts +0 -79
  107. package/src/tests/LookAhead.test.ts +0 -44
  108. package/src/tests/Not.test.ts +0 -51
  109. package/src/tests/Or.test.ts +0 -113
  110. package/src/tests/Pattern.test.ts +0 -290
  111. package/src/tests/Recursive.test.ts +0 -64
  112. package/src/tests/Reference.test.ts +0 -16
  113. package/src/tests/Repeat.test.ts +0 -75
  114. package/src/tests/SpeedTest.test.ts +0 -31
  115. package/src/tests/TextSuggester.test.ts +0 -297
  116. package/src/tests/Visitor.test.ts +0 -331
  117. package/src/tests/cssPatterns/hex.ts +0 -5
  118. package/src/tests/cssPatterns/name.ts +0 -5
  119. package/src/tests/cssPatterns/number.ts +0 -8
  120. package/src/tests/cssPatterns/spaces.ts +0 -5
  121. package/src/tests/cssPatterns/unit.ts +0 -8
  122. package/src/tests/htmlPatterns/element.ts +0 -49
  123. package/src/tests/javascriptPatterns/boolean.ts +0 -10
  124. package/src/tests/javascriptPatterns/json.ts +0 -67
  125. package/src/tests/javascriptPatterns/name.ts +0 -5
  126. package/src/tests/javascriptPatterns/objectLiteral.ts +0 -40
  127. package/src/tests/javascriptPatterns/string.ts +0 -84
  128. package/src/tests/javascriptPatterns/unit.ts +0 -8
  129. package/src/tests/javascriptPatterns/whitespace.ts +0 -44
  130. package/src/tests/naturalLanguage/filter.ts +0 -37
  131. package/src/tests/patterns/sentence.ts +0 -37
  132. /package/src/{tests/cssPatterns → intellisense/css}/optionalSpaces.ts +0 -0
@@ -0,0 +1,105 @@
1
+ import { Cursor } from "./Cursor";
2
+ import { Node } from "../ast/Node";
3
+ import { Literal } from "./Literal";
4
+ import { Or } from "./Or";
5
+ import { And } from "./And";
6
+
7
+ describe("Or", () => {
8
+ test("Empty Options", () => {
9
+ expect(() => {
10
+ new Or("bad", []);
11
+ }).toThrowError();
12
+ });
13
+
14
+ test("One Option Successful", () => {
15
+ const a = new Or("a", [new Literal("a", "A")]);
16
+ const cursor = new Cursor("A");
17
+ const result = a.parse(cursor);
18
+ const expected = new Node("literal", "a", 0, 0, [], "A")
19
+
20
+ expect(result).toEqual(expected);
21
+ });
22
+
23
+ test("One Option Failed", () => {
24
+ const a = new Or("a", [new Literal("a", "A")]);
25
+ const cursor = new Cursor("B");
26
+ const result = a.parse(cursor);
27
+
28
+ expect(result).toEqual(null);
29
+ expect(cursor.index).toBe(0);
30
+ expect(cursor.hasError).toBeTruthy();
31
+ });
32
+
33
+ test("Two Option", () => {
34
+ const a = new Or("a-b", [new Literal("a", "A"), new Literal("b", "B")]);
35
+ const cursor = new Cursor("AB");
36
+ let result = a.parse(cursor);
37
+ let expected = new Node("literal", "a", 0, 0, [], "A")
38
+
39
+ expect(result).toEqual(expected);
40
+
41
+ cursor.next();
42
+
43
+ result = a.parse(cursor);
44
+ expected = new Node("or", "a-b", 0, 0, [
45
+ new Node("literal", "b", 0, 0, [], "B")
46
+ ], "B");
47
+ });
48
+
49
+ test("Failed (Optional)", () => {
50
+ const a = new Or("a", [new Literal("a", "A")], true);
51
+ const cursor = new Cursor("B");
52
+ const result = a.parse(cursor);
53
+
54
+ expect(result).toBeNull();
55
+ expect(cursor.hasError).toBeFalsy();
56
+ });
57
+
58
+ test("Get Tokens", () => {
59
+ const aOrB = new Or("a-b", [new Literal("a", "A"), new Literal("b", "B")]);
60
+ const tokens = aOrB.getTokens();
61
+ const expected = ["A", "B"];
62
+
63
+ expect(tokens).toEqual(expected);
64
+ });
65
+
66
+ test("Get Next Tokens", () => {
67
+ const a = new Or("a", [new Literal("a", "A")]);
68
+ const parent = new And("parent", [a, new Literal("b", "B")]);
69
+ const tokens = parent.children[0].getNextTokens(parent.children[0].children[0]);
70
+ const expected = ["B"];
71
+
72
+ expect(tokens).toEqual(expected);
73
+ });
74
+
75
+ test("Get Next Tokens Without A Parent", () => {
76
+ const a = new Or("a", [new Literal("a", "A")]);
77
+ const tokens = a.getNextTokens(a.children[0]);
78
+ const expected: string[] = [];
79
+
80
+ expect(tokens).toEqual(expected);
81
+ });
82
+
83
+ test("Get Next Pattern", () => {
84
+ const a = new Or("a", [new Literal("a", "A")]);
85
+ const nextToken = a.getNextPattern();
86
+
87
+ expect(nextToken).toBeNull();
88
+ });
89
+
90
+ test("Properties", () => {
91
+ const a = new Or("a", [new Literal("a", "A")]);
92
+
93
+ expect(a.type).toBe("or");
94
+ expect(a.name).toBe("a");
95
+ expect(a.isOptional).toBeFalsy();
96
+ expect(a.parent).toBeNull();
97
+ expect(a.children[0].name).toBe("a");
98
+ });
99
+
100
+ test("Parse Text", () => {
101
+ const a = new Or("a", [new Literal("a", "A")]);
102
+ const { ast: result } = a.parseText("B");
103
+ expect(result).toBeNull();
104
+ });
105
+ });
@@ -1,132 +1,140 @@
1
- import Pattern from "./Pattern";
2
- import ParseError from "./ParseError";
3
- import Cursor from "../Cursor";
4
- import Node from "../ast/Node";
5
-
6
- export default class Or extends Pattern {
7
- public patternIndex: number = 0;
8
- public errors: ParseError[] = [];
9
- public node: Node | null = null;
10
- public cursor: Cursor | null = null;
11
- public mark: number = 0;
12
- public parseError: ParseError | null = null;
13
-
14
- constructor(name: string, patterns: Pattern[], isOptional = false) {
15
- super("or", name, patterns, isOptional);
16
- this.assertArguments();
17
- }
18
-
19
- private assertArguments() {
20
- if (this._children.length < 2) {
21
- throw new Error(
22
- "Invalid Argument: OrValue needs to have more than one value pattern."
23
- );
24
- }
1
+ import { Node } from "../ast/Node";
2
+ import { Cursor } from "./Cursor";
3
+ import { Pattern } from "./Pattern";
4
+ import { clonePatterns } from "./clonePatterns";
5
+ import { getNextPattern } from "./getNextPattern";
6
+ import { findPattern } from "./findPattern";
7
+
8
+ export class Or implements Pattern {
9
+ private _type: string;
10
+ private _name: string;
11
+ private _parent: Pattern | null;
12
+ private _children: Pattern[];
13
+ private _isOptional: boolean;
14
+ private _node: Node | null;
15
+ private _firstIndex: number;
16
+
17
+ get type(): string {
18
+ return this._type;
19
+ }
25
20
 
26
- const hasOptionalChildren = this._children.some(
27
- (pattern) => pattern.isOptional
28
- );
21
+ get name(): string {
22
+ return this._name;
23
+ }
29
24
 
30
- if (hasOptionalChildren) {
31
- throw new Error("OrValues cannot have optional patterns.");
32
- }
25
+ get parent(): Pattern | null {
26
+ return this._parent;
33
27
  }
34
28
 
35
- private resetState(cursor: Cursor) {
36
- this.patternIndex = 0;
37
- this.errors = [];
38
- this.node = null;
39
- this.cursor = cursor;
40
- this.mark = cursor.mark();
29
+ set parent(pattern: Pattern | null) {
30
+ this._parent = pattern;
41
31
  }
42
32
 
43
- private safelyGetCursor() {
44
- const cursor = this.cursor;
33
+ get children(): Pattern[] {
34
+ return this._children;
35
+ }
36
+
37
+ get isOptional(): boolean {
38
+ return this._isOptional;
39
+ }
45
40
 
46
- if (cursor == null) {
47
- throw new Error("Couldn't find cursor.");
41
+ constructor(name: string, options: Pattern[], isOptional: boolean = false) {
42
+ if (options.length === 0) {
43
+ throw new Error("Need at least one pattern with an 'or' pattern.");
44
+ }
45
+
46
+ const children = clonePatterns(options, false);
47
+ this._assignChildrenToParent(children);
48
+
49
+ this._type = "or";
50
+ this._name = name;
51
+ this._parent = null;
52
+ this._children = children;
53
+ this._isOptional = isOptional;
54
+ this._node = null;
55
+ this._firstIndex = 0;
56
+ }
57
+
58
+ private _assignChildrenToParent(children: Pattern[]): void {
59
+ for (const child of children) {
60
+ child.parent = this;
48
61
  }
49
- return cursor;
50
62
  }
51
63
 
52
- parse(cursor: Cursor) {
53
- this.resetState(cursor);
54
- this.tryToParse();
64
+ parseText(text: string) {
65
+ const cursor = new Cursor(text);
66
+ const ast = this.parse(cursor)
55
67
 
56
- return this.node;
68
+ return {
69
+ ast,
70
+ cursor
71
+ };
57
72
  }
58
73
 
59
- private tryToParse() {
60
- const cursor = this.safelyGetCursor();
74
+ parse(cursor: Cursor): Node | null {
75
+ this._firstIndex = cursor.index;
76
+ this._node = null;
61
77
 
62
- while (true) {
63
- const pattern = this._children[this.patternIndex];
64
- const node = pattern.parse(cursor);
65
- const hasError = cursor.hasUnresolvedError();
78
+ const node = this._tryToParse(cursor);
66
79
 
67
- if (hasError) {
68
- const shouldBreak = this.processError();
69
- if (shouldBreak) {
70
- break;
71
- }
72
- } else if (node != null) {
73
- this.processResult(node);
74
- break;
75
- }
80
+ if (node != null) {
81
+ cursor.resolveError();
82
+ return node
83
+ }
84
+
85
+ if (!this._isOptional) {
86
+ cursor.recordErrorAt(this._firstIndex, this)
87
+ return null;
76
88
  }
89
+
90
+ cursor.resolveError();
91
+ cursor.moveTo(this._firstIndex);
92
+ return null;
77
93
  }
78
94
 
79
- private processError() {
80
- const cursor = this.safelyGetCursor();
81
- const isLastPattern = this.patternIndex + 1 === this._children.length;
95
+ private _tryToParse(cursor: Cursor): Node | null {
96
+ for (const pattern of this._children) {
97
+ cursor.moveTo(this._firstIndex);
98
+ const result = pattern.parse(cursor);
82
99
 
83
- if (!isLastPattern) {
84
- this.patternIndex++;
85
- cursor.resolveError();
86
- cursor.moveToMark(this.mark);
87
- return false;
88
- } else {
89
- if (this._isOptional) {
90
- cursor.resolveError();
91
- cursor.moveToMark(this.mark);
100
+ if (!cursor.hasError) {
101
+ return result;
92
102
  }
93
- this.node = null;
94
- return true;
103
+
104
+ cursor.resolveError();
95
105
  }
106
+
107
+ return null
96
108
  }
97
109
 
98
- private processResult(node: Node) {
99
- const cursor = this.safelyGetCursor();
110
+ getTokens(): string[] {
111
+ const tokens: string[] = [];
100
112
 
101
- this.node = new Node(
102
- "or",
103
- this.name,
104
- node.startIndex,
105
- node.endIndex,
106
- [node],
107
- node.value
108
- );
113
+ for (const child of this._children) {
114
+ tokens.push(...child.getTokens());
115
+ }
109
116
 
110
- cursor.index = this.node.endIndex;
111
- cursor.addMatch(this, this.node);
117
+ return tokens;
112
118
  }
113
119
 
114
- clone(name?: string, isOptional?: boolean) {
115
- if (name == null) {
116
- name = this.name;
120
+ getNextTokens(_lastMatched: Pattern): string[] {
121
+ if (this._parent === null) {
122
+ return [];
117
123
  }
118
124
 
119
- if (isOptional == null) {
120
- isOptional = this._isOptional;
121
- }
125
+ return this._parent.getNextTokens(this);
126
+ }
127
+
128
+ getNextPattern(): Pattern | null {
129
+ return getNextPattern(this)
130
+ }
122
131
 
123
- return new Or(name, this._children, isOptional);
132
+ findPattern(isMatch: (p: Pattern)=>boolean): Pattern | null{
133
+ return findPattern(this, isMatch);
124
134
  }
125
135
 
126
- getTokens() {
127
- return this._children.reduce<string[]>(
128
- (acc, c) => acc.concat(c.getTokens()),
129
- []
130
- );
136
+ clone(name = this._name, isOptional = this._isOptional): Pattern {
137
+ const or = new Or(name, this._children, isOptional);
138
+ return or;
131
139
  }
132
140
  }
@@ -1,14 +1,10 @@
1
- import Pattern from "./Pattern";
1
+ import { Pattern } from "./Pattern";
2
2
 
3
- export default class ParseError {
4
- public message: string;
5
- public name: string;
3
+ export class ParseError {
6
4
  public index: number;
7
5
  public pattern: Pattern;
8
6
 
9
- constructor(message: string, index: number, pattern: Pattern) {
10
- this.name = "ParseError";
11
- this.message = message;
7
+ constructor(index: number, pattern: Pattern) {
12
8
  this.index = index;
13
9
  this.pattern = pattern;
14
10
  }
@@ -0,0 +1,7 @@
1
+ import { Node } from "../ast/Node";
2
+ import { Cursor } from "./Cursor";
3
+
4
+ export interface ParseResult {
5
+ ast: Node | null;
6
+ cursor: Cursor;
7
+ }
@@ -1,151 +1,19 @@
1
- import Cursor from "../Cursor";
2
- import Node from "../ast/Node";
3
-
4
- export default abstract class Pattern {
5
- protected _type: string;
6
- protected _name: string;
7
- protected _children: Pattern[];
8
- protected _parent: Pattern | null;
9
- protected _isOptional = false;
10
-
11
- get isOptional() {
12
- return this._isOptional;
13
- }
14
-
15
- constructor(
16
- type: string,
17
- name: string,
18
- children: Pattern[] = [],
19
- isOptional = false
20
- ) {
21
- this._type = type;
22
- this._name = name;
23
- this._children = [];
24
- this._parent = null;
25
- this._isOptional = isOptional;
26
- this.children = children;
27
- }
28
-
29
- abstract parse(cursor: Cursor): Node | null;
30
-
31
- exec(text: string) {
32
- const cursor = new Cursor(text);
33
- const node = this.parse(cursor);
34
-
35
- if (cursor.didSuccessfullyParse()) {
36
- return node;
37
- } else {
38
- return null;
39
- }
40
- }
41
-
42
- test(text: string) {
43
- return this.exec(text) != null;
44
- }
45
-
46
- get name() {
47
- return this._name;
48
- }
49
-
50
- get type() {
51
- return this._type;
52
- }
53
-
54
- get parent() {
55
- return this._parent;
56
- }
57
-
58
- set parent(value: Pattern | null) {
59
- this._parent = value;
60
- }
61
-
62
- get children() {
63
- return this._children;
64
- }
65
-
66
- set children(value) {
67
- this._children = value;
68
- this.cloneChildren();
69
- this.assignAsParent();
70
- }
71
-
72
- abstract clone(name?: string): Pattern;
73
- abstract getTokens(): string[];
74
-
75
- getTokenValue(): string | null {
76
- return null;
77
- }
78
-
79
- getNextTokens(): string[] {
80
- const parent = this._parent;
81
-
82
- if (parent != null) {
83
- const siblings = parent.children;
84
- const index = siblings.findIndex((c) => c === this);
85
- const nextSibling = siblings[index + 1];
86
-
87
- // I don't like this, so I think we need to rethink this.
88
- if (parent.type.indexOf("repeat") === 0) {
89
- const tokens = parent.getNextTokens();
90
- if (index === 0 && siblings.length > 1) {
91
- return nextSibling.getTokens().concat(tokens);
92
- } else if (index === 1) {
93
- return siblings[0].getTokens();
94
- } else {
95
- return this.getTokens().concat(tokens);
96
- }
97
- }
98
-
99
- // Another thing I don't like.
100
- if (
101
- this._parent?.type?.indexOf("and") === 0 &&
102
- nextSibling != null &&
103
- nextSibling.isOptional
104
- ) {
105
- let tokens: string[] = [];
106
-
107
- for (let x = index + 1; x < siblings.length; x++) {
108
- const child = siblings[x];
109
-
110
- if (child.isOptional) {
111
- tokens = tokens.concat(child.getTokens());
112
- } else {
113
- tokens = tokens.concat(child.getTokens());
114
- break;
115
- }
116
-
117
- if (x === siblings.length - 1) {
118
- tokens = tokens.concat(this._parent.getNextTokens());
119
- }
120
- }
121
-
122
- return tokens;
123
- }
124
-
125
- // If you are an or you have already qualified.
126
- if (parent.type.indexOf("or") === 0) {
127
- return parent.getNextTokens();
128
- }
129
-
130
- if (nextSibling != null) {
131
- return nextSibling.getTokens();
132
- } else {
133
- return parent.getNextTokens();
134
- }
135
- }
136
-
137
- return [];
138
- }
139
-
140
- private cloneChildren() {
141
- this._children = this._children.map((pattern) => {
142
- return pattern.clone();
143
- });
144
-
145
- Object.freeze(this._children);
146
- }
147
-
148
- private assignAsParent() {
149
- this._children.forEach((child) => (child.parent = this));
150
- }
1
+ import { Cursor } from "./Cursor";
2
+ import { Node } from "../ast/Node";
3
+ import { ParseResult } from "./ParseResult"
4
+
5
+ export interface Pattern {
6
+ type: string;
7
+ name: string;
8
+ parent: Pattern | null;
9
+ children: Pattern[];
10
+ isOptional: boolean;
11
+
12
+ parse(cursor: Cursor): Node | null;
13
+ parseText(text: string): ParseResult;
14
+ clone(name?: string, isOptional?: boolean): Pattern;
15
+ getTokens(): string[];
16
+ getNextTokens(lastMatched: Pattern): string[];
17
+ getNextPattern(): Pattern | null;
18
+ findPattern(isMatch:(p: Pattern)=>boolean): Pattern | null;
151
19
  }
@@ -0,0 +1,104 @@
1
+ import { Cursor, Repeat } from "..";
2
+ import { Node } from "../ast/Node";
3
+ import { And } from "./And";
4
+ import { findPattern } from "./findPattern";
5
+ import { Literal } from "./Literal";
6
+ import { Or } from "./Or";
7
+ import { Reference } from "./Reference";
8
+ import { Regex } from "./Regex";
9
+
10
+ function createValuePattern() {
11
+ const number = new Regex("number", "\\d+");
12
+ const openBracket = new Literal("open-bracket", "[");
13
+ const closeBracket = new Literal("close-bracket", "]");
14
+ const divider = new Regex("divider", "\\s*,\\s+");
15
+ const valueRef = new Reference("value");
16
+ const values = new Repeat("values", valueRef, divider);
17
+ const array = new And("array", [openBracket, values, closeBracket]);
18
+ const value = new Or("value", [number, array]);
19
+
20
+ number.setTokens(["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]);
21
+ divider.setTokens([", "])
22
+
23
+ return value;
24
+ }
25
+
26
+ describe("Reference", () => {
27
+ test("One Deep Successful Parse", () => {
28
+ const value = createValuePattern();
29
+ const cursor = new Cursor("[1, 2]");
30
+ const result = value.parse(cursor);
31
+
32
+ const expected = new Node("and", "array", 0, 5, [
33
+ new Node("literal", "open-bracket", 0, 0, [], "["),
34
+ new Node("repeat", "values", 1, 4, [
35
+ new Node("regex", "number", 1, 1, [], "1"),
36
+ new Node("regex", "divider", 2, 3, [], ", "),
37
+ new Node("regex", "number", 4, 4, [], "2")
38
+ ]),
39
+ new Node("literal", "close-bracket", 5, 5, [], "]"),
40
+ ])
41
+
42
+ expect(result).toEqual(expected);
43
+ });
44
+
45
+ test("No Reference Pattern", () => {
46
+ const ref = new Reference("bad-reference");
47
+
48
+ expect(() => {
49
+ ref.parse(new Cursor("text"))
50
+ }).toThrowError()
51
+ });
52
+
53
+ test("Get Tokens", () => {
54
+ const value = createValuePattern();
55
+ const ref = findPattern(value, (p) => p.type === "reference");
56
+ const tokens = ref?.getTokens();
57
+ const expected = ["["];
58
+
59
+ expect(tokens).toEqual(expected);
60
+ });
61
+
62
+ test("Get Next Tokens", () => {
63
+ const value = createValuePattern();
64
+ const ref = findPattern(value, (p) => p.type === "reference");
65
+
66
+ // The value passed to getNextTokens doesn't matter.
67
+ // I just needed it to be a pattern.
68
+ const tokens = ref?.getNextTokens(value);
69
+ const expected = ["]"];
70
+
71
+ expect(tokens).toEqual(expected);
72
+ });
73
+
74
+ test("Get Next Tokens With No Parent", () => {
75
+ const ref = new Reference("bad-reference");
76
+ const tokens = ref.getNextTokens(new Literal("bogus", "bogus"))
77
+
78
+ expect(tokens).toEqual([]);
79
+ });
80
+
81
+ test("Get Next Pattern", () => {
82
+ const ref = new Reference("ref");
83
+ const nextPattern = ref.getNextPattern();
84
+
85
+ expect(nextPattern).toBeNull()
86
+ });
87
+
88
+ test("Properties", () => {
89
+ const ref = new Reference("ref");
90
+
91
+ expect(ref.type).toBe("reference");
92
+ expect(ref.name).toBe("ref");
93
+ expect(ref.isOptional).toBeFalsy();
94
+ expect(ref.parent).toBe(null)
95
+ expect(ref.children).toEqual([])
96
+ });
97
+
98
+ test("Parse Text", () => {
99
+ const value = createValuePattern();
100
+ const reference = findPattern(value, p => p.type === "reference") as Reference
101
+ const { ast: result } = reference.parseText("B");
102
+ expect(result).toBeNull()
103
+ });
104
+ });