@chr33s/pdf-dfa 5.0.0 → 5.0.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chr33s/pdf-dfa",
3
- "version": "5.0.0",
3
+ "version": "5.0.1",
4
4
  "description": "Deterministic finite automata compiler",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -1,33 +0,0 @@
1
- import { readFile, writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import peggy from "peggy";
5
-
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = path.dirname(__filename);
8
- const projectRoot = path.resolve(__dirname, "..");
9
- const srcDir = path.join(projectRoot, "src");
10
- const grammarPath = path.join(srcDir, "grammar.peg");
11
- const outputPath = path.join(srcDir, "grammar.js");
12
-
13
- async function buildGrammar(): Promise<void> {
14
- const grammar = await readFile(grammarPath, "utf8");
15
-
16
- const parserSource = peggy.generate(grammar, {
17
- cache: true,
18
- output: "source",
19
- format: "es",
20
- });
21
-
22
- const banner = [
23
- "// @ts-nocheck",
24
- "// This file is generated by scripts/build-grammar.ts. Do not edit by hand.\n",
25
- ].join("\n");
26
- await writeFile(outputPath, `${banner}${parserSource}`);
27
- console.log(`Generated ${path.relative(projectRoot, outputPath)}`);
28
- }
29
-
30
- buildGrammar().catch((error) => {
31
- console.error("Failed to build grammar:", error);
32
- process.exitCode = 1;
33
- });
@@ -1,131 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import compile from "../src/compile.js";
3
-
4
- describe("state machine compiler", () => {
5
- test("should compile a state machine with a single literal", () => {
6
- const stateMachine = compile("a = 0; b = 1; main = a;");
7
- const matches = Array.from(stateMachine.match([0, 0, 1, 0]));
8
- expect(matches).toEqual([
9
- [0, 0, []],
10
- [1, 1, []],
11
- [3, 3, []],
12
- ]);
13
- });
14
-
15
- test("should compile a state machine with a concatenation", () => {
16
- const stateMachine = compile("a = 0; b = 1; main = a b;");
17
- const matches = Array.from(stateMachine.match([0, 0, 1, 1, 0, 1, 0]));
18
- expect(matches).toEqual([
19
- [1, 2, []],
20
- [4, 5, []],
21
- ]);
22
- });
23
-
24
- test("should compile a state machine with an alternation", () => {
25
- const stateMachine = compile("a = 0; b = 1; main = (a b) | (b a);");
26
- const matches = Array.from(stateMachine.match([0, 0, 1, 1, 0, 1, 0]));
27
- expect(matches).toEqual([
28
- [1, 2, []],
29
- [3, 4, []],
30
- [5, 6, []],
31
- ]);
32
- });
33
-
34
- test("should compile a state machine with a repeat", () => {
35
- const stateMachine = compile("a = 0; b = 1; main = (a b)+;");
36
- const matches = Array.from(stateMachine.match([0, 0, 1, 0, 1, 1, 0, 1]));
37
- expect(matches).toEqual([
38
- [1, 4, []],
39
- [6, 7, []],
40
- ]);
41
- });
42
-
43
- test("should compile a state machine with an optional repeat", () => {
44
- const stateMachine = compile("a = 0; b = 1; main = b a (a b)*;");
45
- const matches = Array.from(stateMachine.match([0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0]));
46
- expect(matches).toEqual([
47
- [2, 7, []],
48
- [9, 10, []],
49
- ]);
50
- });
51
-
52
- test("should compile a state machine with an optional group", () => {
53
- const stateMachine = compile("a = 0; b = 1; main = b a (a b)?;");
54
- const matches = Array.from(stateMachine.match([0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0]));
55
- expect(matches).toEqual([
56
- [2, 5, []],
57
- [9, 10, []],
58
- ]);
59
- });
60
-
61
- test("should compile a state machine with an exact repetition", () => {
62
- const stateMachine = compile("a = 0; b = 1; main = a{3} b;");
63
- const matches = Array.from(stateMachine.match([0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1]));
64
- expect(matches).toEqual([[3, 6, []]]);
65
- });
66
-
67
- test("should compile a state machine with a minimum repetition", () => {
68
- const stateMachine = compile("a = 0; b = 1; main = a{3,} b;");
69
- const matches = Array.from(stateMachine.match([0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1]));
70
- expect(matches).toEqual([
71
- [3, 6, []],
72
- [7, 11, []],
73
- ]);
74
- });
75
-
76
- test("should compile a state machine with a maximum repetition", () => {
77
- const stateMachine = compile("a = 0; b = 1; main = a{,3} b;");
78
- const matches = Array.from(stateMachine.match([0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1]));
79
- expect(matches).toEqual([
80
- [0, 2, []],
81
- [3, 6, []],
82
- [10, 11, []],
83
- [12, 12, []],
84
- ]);
85
- });
86
-
87
- test("should compile a state machine with a minimum and maximum repetition", () => {
88
- const stateMachine = compile("a = 0; b = 1; main = a{3,5} b;");
89
- const matches = Array.from(
90
- stateMachine.match([0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1]),
91
- );
92
- expect(matches).toEqual([
93
- [3, 6, []],
94
- [7, 11, []],
95
- ]);
96
- });
97
-
98
- test("should compile a state machine with tags", () => {
99
- const stateMachine = compile("a = 0; b = 1; main = x:(b a) | y:(a b);");
100
- const input = [1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0];
101
- const matches = Array.from(stateMachine.match(input));
102
- expect(matches).toEqual([
103
- [2, 3, ["x"]],
104
- [4, 5, ["y"]],
105
- [6, 7, ["y"]],
106
- [9, 10, ["x"]],
107
- ]);
108
-
109
- const applied: Array<[string, number, number, number[]]> = [];
110
- stateMachine.apply(input, {
111
- x: (start: number, end: number, slice: number[]) => applied.push(["x", start, end, slice]),
112
- y: (start: number, end: number, slice: number[]) => applied.push(["y", start, end, slice]),
113
- });
114
-
115
- expect(applied).toEqual([
116
- ["x", 2, 3, [1, 0]],
117
- ["y", 4, 5, [0, 1]],
118
- ["y", 6, 7, [0, 1]],
119
- ["x", 9, 10, [1, 0]],
120
- ]);
121
- });
122
-
123
- test("should compile a state machine with external symbols", () => {
124
- const stateMachine = compile("main = a b;", { a: 0, b: 1 });
125
- const matches = Array.from(stateMachine.match([0, 0, 1, 1, 0, 1, 0]));
126
- expect(matches).toEqual([
127
- [1, 2, []],
128
- [4, 5, []],
129
- ]);
130
- });
131
- });
package/test/dfa.test.ts DELETED
@@ -1,87 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import buildDFA from "../src/dfa.js";
3
- import { Alternation, Concatenation, Literal, Repeat, Tag } from "../src/nodes.js";
4
-
5
- describe("buildDFA", () => {
6
- test("should build DFA for single literal", () => {
7
- const root = new Literal(0);
8
- const states = buildDFA(root, 1);
9
-
10
- // Should have fail state (0), initial state (1), and at least one accepting state
11
- expect(states.length).toBeGreaterThanOrEqual(2);
12
- expect(states[0].accepting).toBe(false); // Fail state
13
- });
14
-
15
- test("should build DFA for concatenation", () => {
16
- const root = new Concatenation(new Literal(0), new Literal(1));
17
- const states = buildDFA(root, 2);
18
-
19
- expect(states.length).toBeGreaterThanOrEqual(2);
20
- // Verify there's at least one accepting state
21
- const hasAccepting = states.some((s) => s.accepting);
22
- expect(hasAccepting).toBe(true);
23
- });
24
-
25
- test("should build DFA for alternation", () => {
26
- const root = new Alternation(new Literal(0), new Literal(1));
27
- const states = buildDFA(root, 2);
28
-
29
- expect(states.length).toBeGreaterThanOrEqual(2);
30
- const hasAccepting = states.some((s) => s.accepting);
31
- expect(hasAccepting).toBe(true);
32
- });
33
-
34
- test("should build DFA for repeat *", () => {
35
- const root = new Repeat(new Literal(0), "*");
36
- const states = buildDFA(root, 1);
37
-
38
- expect(states.length).toBeGreaterThanOrEqual(2);
39
- });
40
-
41
- test("should build DFA for repeat +", () => {
42
- const root = new Repeat(new Literal(0), "+");
43
- const states = buildDFA(root, 1);
44
-
45
- expect(states.length).toBeGreaterThanOrEqual(2);
46
- });
47
-
48
- test("should build DFA for repeat ?", () => {
49
- const root = new Repeat(new Literal(0), "?");
50
- const states = buildDFA(root, 1);
51
-
52
- expect(states.length).toBeGreaterThanOrEqual(2);
53
- });
54
-
55
- test("should build DFA with tags", () => {
56
- const lit = new Literal(0);
57
- const tag = new Tag("myTag");
58
- const root = new Concatenation(tag, lit);
59
- const states = buildDFA(root, 1);
60
-
61
- // Find state with the tag
62
- const statesWithTags = states.filter((s) => s.tags.size > 0);
63
- expect(statesWithTags.length).toBeGreaterThan(0);
64
- expect(statesWithTags.some((s) => s.tags.has("myTag"))).toBe(true);
65
- });
66
-
67
- test("should have fail state with no transitions", () => {
68
- const root = new Literal(0);
69
- const states = buildDFA(root, 1);
70
-
71
- const failState = states[0];
72
- expect(failState.accepting).toBe(false);
73
- expect(failState.transitions.every((t) => t === 0)).toBe(true);
74
- });
75
-
76
- test("should handle complex nested expression", () => {
77
- // ((a | b) c)+
78
- const inner = new Alternation(new Literal(0), new Literal(1));
79
- const concat = new Concatenation(inner, new Literal(2));
80
- const root = new Repeat(concat, "+");
81
- const states = buildDFA(root, 3);
82
-
83
- expect(states.length).toBeGreaterThanOrEqual(2);
84
- const hasAccepting = states.some((s) => s.accepting);
85
- expect(hasAccepting).toBe(true);
86
- });
87
- });
@@ -1,324 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import {
3
- Alternation,
4
- Assignment,
5
- buildRepetition,
6
- Comment,
7
- Concatenation,
8
- EndMarker,
9
- Literal,
10
- Repeat,
11
- Tag,
12
- Variable,
13
- } from "../src/nodes.js";
14
-
15
- describe("nodes", () => {
16
- describe("Variable", () => {
17
- test("should store name", () => {
18
- const v = new Variable("test");
19
- expect(v.name).toBe("test");
20
- });
21
-
22
- test("should throw on nullable access", () => {
23
- const v = new Variable("test");
24
- expect(() => v.nullable).toThrow("Variable nodes must be resolved before evaluation");
25
- });
26
-
27
- test("should throw on firstpos access", () => {
28
- const v = new Variable("test");
29
- expect(() => v.firstpos).toThrow("Variable nodes must be resolved before evaluation");
30
- });
31
-
32
- test("should throw on lastpos access", () => {
33
- const v = new Variable("test");
34
- expect(() => v.lastpos).toThrow("Variable nodes must be resolved before evaluation");
35
- });
36
-
37
- test("should copy correctly", () => {
38
- const v = new Variable("test");
39
- const copy = v.copy();
40
- expect(copy.name).toBe("test");
41
- expect(copy).not.toBe(v);
42
- });
43
- });
44
-
45
- describe("Comment", () => {
46
- test("should store value", () => {
47
- const c = new Comment("this is a comment");
48
- expect(c.value).toBe("this is a comment");
49
- });
50
- });
51
-
52
- describe("Assignment", () => {
53
- test("should store variable and expression", () => {
54
- const variable = new Variable("x");
55
- const expr = new Literal(42);
56
- const assign = new Assignment(variable, expr);
57
- expect(assign.variable).toBe(variable);
58
- expect(assign.expression).toBe(expr);
59
- });
60
- });
61
-
62
- describe("Alternation", () => {
63
- test("should be nullable if either child is nullable", () => {
64
- const a = new Literal(1);
65
- const b = new Repeat(new Literal(2), "?");
66
- const alt = new Alternation(a, b);
67
- expect(alt.nullable).toBe(true);
68
- });
69
-
70
- test("should not be nullable if both children are not nullable", () => {
71
- const a = new Literal(1);
72
- const b = new Literal(2);
73
- const alt = new Alternation(a, b);
74
- expect(alt.nullable).toBe(false);
75
- });
76
-
77
- test("should compute firstpos as union of children", () => {
78
- const a = new Literal(1);
79
- const b = new Literal(2);
80
- const alt = new Alternation(a, b);
81
- expect(alt.firstpos.size).toBe(2);
82
- expect(alt.firstpos.has(a)).toBe(true);
83
- expect(alt.firstpos.has(b)).toBe(true);
84
- });
85
-
86
- test("should compute lastpos as union of children", () => {
87
- const a = new Literal(1);
88
- const b = new Literal(2);
89
- const alt = new Alternation(a, b);
90
- expect(alt.lastpos.size).toBe(2);
91
- expect(alt.lastpos.has(a)).toBe(true);
92
- expect(alt.lastpos.has(b)).toBe(true);
93
- });
94
-
95
- test("should copy correctly", () => {
96
- const a = new Literal(1);
97
- const b = new Literal(2);
98
- const alt = new Alternation(a, b);
99
- const copy = alt.copy();
100
- expect(copy).not.toBe(alt);
101
- expect((copy.a as Literal).value).toBe(1);
102
- expect((copy.b as Literal).value).toBe(2);
103
- });
104
- });
105
-
106
- describe("Concatenation", () => {
107
- test("should be nullable only if both children are nullable", () => {
108
- const a = new Repeat(new Literal(1), "?");
109
- const b = new Repeat(new Literal(2), "?");
110
- const concat = new Concatenation(a, b);
111
- expect(concat.nullable).toBe(true);
112
- });
113
-
114
- test("should not be nullable if one child is not nullable", () => {
115
- const a = new Literal(1);
116
- const b = new Repeat(new Literal(2), "?");
117
- const concat = new Concatenation(a, b);
118
- expect(concat.nullable).toBe(false);
119
- });
120
-
121
- test("should compute firstpos including second child if first is nullable", () => {
122
- const a = new Repeat(new Literal(1), "?");
123
- const b = new Literal(2);
124
- const concat = new Concatenation(a, b);
125
- expect(concat.firstpos.size).toBe(2);
126
- });
127
-
128
- test("should compute firstpos only from first child if not nullable", () => {
129
- const a = new Literal(1);
130
- const b = new Literal(2);
131
- const concat = new Concatenation(a, b);
132
- expect(concat.firstpos.size).toBe(1);
133
- expect(concat.firstpos.has(a)).toBe(true);
134
- });
135
-
136
- test("should compute lastpos including first child if second is nullable", () => {
137
- const a = new Literal(1);
138
- const b = new Repeat(new Literal(2), "?");
139
- const concat = new Concatenation(a, b);
140
- expect(concat.lastpos.size).toBe(2);
141
- });
142
-
143
- test("should copy correctly", () => {
144
- const a = new Literal(1);
145
- const b = new Literal(2);
146
- const concat = new Concatenation(a, b);
147
- const copy = concat.copy();
148
- expect(copy).not.toBe(concat);
149
- });
150
-
151
- test("should calculate followpos correctly", () => {
152
- const a = new Literal(1);
153
- const b = new Literal(2);
154
- const concat = new Concatenation(a, b);
155
- concat.calcFollowpos();
156
- expect(a.followpos.has(b)).toBe(true);
157
- });
158
- });
159
-
160
- describe("Repeat", () => {
161
- test("should be nullable for * operator", () => {
162
- const r = new Repeat(new Literal(1), "*");
163
- expect(r.nullable).toBe(true);
164
- });
165
-
166
- test("should be nullable for ? operator", () => {
167
- const r = new Repeat(new Literal(1), "?");
168
- expect(r.nullable).toBe(true);
169
- });
170
-
171
- test("should not be nullable for + operator", () => {
172
- const r = new Repeat(new Literal(1), "+");
173
- expect(r.nullable).toBe(false);
174
- });
175
-
176
- test("should compute firstpos from expression", () => {
177
- const lit = new Literal(1);
178
- const r = new Repeat(lit, "*");
179
- expect(r.firstpos.has(lit)).toBe(true);
180
- });
181
-
182
- test("should compute lastpos from expression", () => {
183
- const lit = new Literal(1);
184
- const r = new Repeat(lit, "*");
185
- expect(r.lastpos.has(lit)).toBe(true);
186
- });
187
-
188
- test("should calculate followpos for * operator", () => {
189
- const lit = new Literal(1);
190
- const r = new Repeat(lit, "*");
191
- r.calcFollowpos();
192
- expect(lit.followpos.has(lit)).toBe(true);
193
- });
194
-
195
- test("should calculate followpos for + operator", () => {
196
- const lit = new Literal(1);
197
- const r = new Repeat(lit, "+");
198
- r.calcFollowpos();
199
- expect(lit.followpos.has(lit)).toBe(true);
200
- });
201
-
202
- test("should not calculate followpos for ? operator", () => {
203
- const lit = new Literal(1);
204
- const r = new Repeat(lit, "?");
205
- r.calcFollowpos();
206
- expect(lit.followpos.size).toBe(0);
207
- });
208
-
209
- test("should copy correctly", () => {
210
- const r = new Repeat(new Literal(1), "*");
211
- const copy = r.copy();
212
- expect(copy).not.toBe(r);
213
- expect(copy.op).toBe("*");
214
- });
215
- });
216
-
217
- describe("Literal", () => {
218
- test("should store value", () => {
219
- const lit = new Literal(42);
220
- expect(lit.value).toBe(42);
221
- });
222
-
223
- test("should not be nullable", () => {
224
- const lit = new Literal(42);
225
- expect(lit.nullable).toBe(false);
226
- });
227
-
228
- test("should have itself in firstpos", () => {
229
- const lit = new Literal(42);
230
- expect(lit.firstpos.has(lit)).toBe(true);
231
- });
232
-
233
- test("should have itself in lastpos", () => {
234
- const lit = new Literal(42);
235
- expect(lit.lastpos.has(lit)).toBe(true);
236
- });
237
-
238
- test("should copy to new instance with same value", () => {
239
- const lit = new Literal(42);
240
- const copy = lit.copy();
241
- expect(copy).not.toBe(lit);
242
- expect(copy.value).toBe(42);
243
- });
244
- });
245
-
246
- describe("EndMarker", () => {
247
- test("should not be nullable", () => {
248
- const em = new EndMarker();
249
- expect(em.nullable).toBe(false);
250
- });
251
-
252
- test("should have itself in firstpos", () => {
253
- const em = new EndMarker();
254
- expect(em.firstpos.has(em)).toBe(true);
255
- });
256
-
257
- test("should have itself in lastpos", () => {
258
- const em = new EndMarker();
259
- expect(em.lastpos.has(em)).toBe(true);
260
- });
261
- });
262
-
263
- describe("Tag", () => {
264
- test("should store name", () => {
265
- const t = new Tag("myTag");
266
- expect(t.name).toBe("myTag");
267
- });
268
-
269
- test("should be nullable", () => {
270
- const t = new Tag("myTag");
271
- expect(t.nullable).toBe(true);
272
- });
273
-
274
- test("should have itself in firstpos", () => {
275
- const t = new Tag("myTag");
276
- expect(t.firstpos.has(t)).toBe(true);
277
- });
278
-
279
- test("should have itself in lastpos", () => {
280
- const t = new Tag("myTag");
281
- expect(t.lastpos.has(t)).toBe(true);
282
- });
283
-
284
- test("should copy to new instance with same name", () => {
285
- const t = new Tag("myTag");
286
- const copy = t.copy();
287
- expect(copy).not.toBe(t);
288
- expect(copy.name).toBe("myTag");
289
- });
290
- });
291
-
292
- describe("buildRepetition", () => {
293
- test("should throw for negative min", () => {
294
- expect(() => buildRepetition(new Literal(1), -1, 5)).toThrow("Invalid repetition range");
295
- });
296
-
297
- test("should throw when min > max", () => {
298
- expect(() => buildRepetition(new Literal(1), 5, 3)).toThrow("Invalid repetition range");
299
- });
300
-
301
- test("should build exact repetition (min = max)", () => {
302
- const result = buildRepetition(new Literal(1), 3, 3);
303
- // Should be Concatenation(Concatenation(Literal, Literal), Literal)
304
- expect(result).toBeInstanceOf(Concatenation);
305
- });
306
-
307
- test("should build unbounded repetition (max = Infinity)", () => {
308
- const result = buildRepetition(new Literal(1), 2, Infinity);
309
- // Should include a Repeat with *
310
- expect(result).toBeInstanceOf(Concatenation);
311
- });
312
-
313
- test("should build range repetition", () => {
314
- const result = buildRepetition(new Literal(1), 1, 3);
315
- expect(result).toBeInstanceOf(Concatenation);
316
- });
317
-
318
- test("should build optional repetition (min = 0, max = 1)", () => {
319
- const result = buildRepetition(new Literal(1), 0, 1);
320
- expect(result).toBeInstanceOf(Repeat);
321
- expect((result as Repeat).op).toBe("?");
322
- });
323
- });
324
- });
@@ -1,50 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import { build, parse } from "../src/compile.js";
3
- import StateMachine from "../src/state-machine.js";
4
-
5
- describe("parse", () => {
6
- test("should parse simple grammar", () => {
7
- const table = parse("a = 0; main = a;");
8
- expect(table.symbols.a).toBe(0);
9
- expect(table.main).toBeDefined();
10
- });
11
-
12
- test("should parse grammar with external symbols", () => {
13
- const table = parse("main = a;", { a: 42 });
14
- expect(table.symbols.a).toBe(42);
15
- });
16
-
17
- test("should parse complex grammar", () => {
18
- const table = parse("a = 0; b = 1; main = (a b)+ | a*;");
19
- expect(table.symbols.a).toBe(0);
20
- expect(table.symbols.b).toBe(1);
21
- expect(table.main).toBeDefined();
22
- });
23
-
24
- test("should parse grammar with tags", () => {
25
- const table = parse("a = 0; main = x:(a);");
26
- expect(table.main).toBeDefined();
27
- });
28
- });
29
-
30
- describe("build", () => {
31
- test("should build state machine from symbol table", () => {
32
- const table = parse("a = 0; main = a;");
33
- const sm = build(table);
34
- expect(sm).toBeInstanceOf(StateMachine);
35
- });
36
-
37
- test("should build working state machine", () => {
38
- const table = parse("a = 0; b = 1; main = a b;");
39
- const sm = build(table);
40
- const matches = Array.from(sm.match([0, 1]));
41
- expect(matches).toEqual([[0, 1, []]]);
42
- });
43
-
44
- test("should build state machine with tags", () => {
45
- const table = parse("a = 0; main = x:(a);");
46
- const sm = build(table);
47
- const matches = Array.from(sm.match([0]));
48
- expect(matches).toEqual([[0, 0, ["x"]]]);
49
- });
50
- });
@@ -1,132 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import StateMachine, { FAIL_STATE, INITIAL_STATE } from "../src/state-machine.js";
3
-
4
- describe("StateMachine", () => {
5
- describe("constants", () => {
6
- test("INITIAL_STATE is 1", () => {
7
- expect(INITIAL_STATE).toBe(1);
8
- });
9
-
10
- test("FAIL_STATE is 0", () => {
11
- expect(FAIL_STATE).toBe(0);
12
- });
13
- });
14
-
15
- describe("match", () => {
16
- test("should return empty matches for empty input", () => {
17
- const sm = new StateMachine({
18
- stateTable: [[], [0]],
19
- accepting: [false, true],
20
- tags: [[], []],
21
- });
22
- const matches = Array.from(sm.match([]));
23
- expect(matches).toEqual([]);
24
- });
25
-
26
- test("should handle no accepting states", () => {
27
- const sm = new StateMachine({
28
- stateTable: [[1], [1]],
29
- accepting: [false, false],
30
- tags: [[], []],
31
- });
32
- const matches = Array.from(sm.match([0, 0, 0]));
33
- expect(matches).toEqual([]);
34
- });
35
-
36
- test("should handle single accepting state at start", () => {
37
- const sm = new StateMachine({
38
- stateTable: [[], [2], [0]],
39
- accepting: [false, false, true],
40
- tags: [[], [], []],
41
- });
42
- const matches = Array.from(sm.match([0]));
43
- expect(matches).toEqual([[0, 0, []]]);
44
- });
45
-
46
- test("should handle multiple consecutive matches", () => {
47
- const sm = new StateMachine({
48
- stateTable: [[], [2], [0]],
49
- accepting: [false, false, true],
50
- tags: [[], [], []],
51
- });
52
- const matches = Array.from(sm.match([0, 0, 0]));
53
- expect(matches).toEqual([
54
- [0, 0, []],
55
- [1, 1, []],
56
- [2, 2, []],
57
- ]);
58
- });
59
-
60
- test("should return tags for accepting states", () => {
61
- const sm = new StateMachine({
62
- stateTable: [[], [2], [0]],
63
- accepting: [false, false, true],
64
- tags: [[], [], ["myTag"]],
65
- });
66
- const matches = Array.from(sm.match([0]));
67
- expect(matches).toEqual([[0, 0, ["myTag"]]]);
68
- });
69
-
70
- test("should handle undefined state transitions gracefully", () => {
71
- const sm = new StateMachine({
72
- stateTable: [[], [2], []],
73
- accepting: [false, false, true],
74
- tags: [[], [], []],
75
- });
76
- // Symbol 1 has no transition from state 1
77
- const matches = Array.from(sm.match([0, 1]));
78
- expect(matches).toEqual([[0, 0, []]]);
79
- });
80
- });
81
-
82
- describe("apply", () => {
83
- test("should call action handlers for each tag", () => {
84
- const sm = new StateMachine({
85
- stateTable: [[], [2], [0]],
86
- accepting: [false, false, true],
87
- tags: [[], [], ["action1"]],
88
- });
89
-
90
- const calls: Array<[string, number, number, number[]]> = [];
91
- sm.apply([0, 0], {
92
- action1: (start, end, slice) => calls.push(["action1", start, end, slice]),
93
- });
94
-
95
- expect(calls).toEqual([
96
- ["action1", 0, 0, [0]],
97
- ["action1", 1, 1, [0]],
98
- ]);
99
- });
100
-
101
- test("should not call missing action handlers", () => {
102
- const sm = new StateMachine({
103
- stateTable: [[], [2], [0]],
104
- accepting: [false, false, true],
105
- tags: [[], [], ["missingAction"]],
106
- });
107
-
108
- const calls: string[] = [];
109
- sm.apply([0], {
110
- otherAction: () => calls.push("called"),
111
- });
112
-
113
- expect(calls).toEqual([]);
114
- });
115
-
116
- test("should handle multiple tags on same match", () => {
117
- const sm = new StateMachine({
118
- stateTable: [[], [2], [0]],
119
- accepting: [false, false, true],
120
- tags: [[], [], ["tag1", "tag2"]],
121
- });
122
-
123
- const calls: string[] = [];
124
- sm.apply([0], {
125
- tag1: () => calls.push("tag1"),
126
- tag2: () => calls.push("tag2"),
127
- });
128
-
129
- expect(calls).toEqual(["tag1", "tag2"]);
130
- });
131
- });
132
- });
@@ -1,69 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import { Assignment, Literal, Variable } from "../src/nodes.js";
3
- import SymbolTable from "../src/symbol-table.js";
4
-
5
- describe("SymbolTable", () => {
6
- test("should process assignments and store variables", () => {
7
- const statements = [
8
- new Assignment(new Variable("a"), new Literal(0)),
9
- new Assignment(new Variable("main"), new Variable("a")),
10
- ];
11
-
12
- const table = new SymbolTable(statements);
13
- expect(table.variables.a).toBeInstanceOf(Literal);
14
- expect((table.variables.a as Literal).value).toBe(0);
15
- });
16
-
17
- test("should throw when main is not declared", () => {
18
- const statements = [new Assignment(new Variable("a"), new Literal(0))];
19
-
20
- expect(() => new SymbolTable(statements)).toThrow("No main variable declaration found");
21
- });
22
-
23
- test("should throw for undeclared identifiers", () => {
24
- const statements = [new Assignment(new Variable("main"), new Variable("undeclared"))];
25
-
26
- expect(() => new SymbolTable(statements)).toThrow("Undeclared identifier undeclared");
27
- });
28
-
29
- test("should resolve variable references", () => {
30
- const statements = [
31
- new Assignment(new Variable("a"), new Literal(0)),
32
- new Assignment(new Variable("b"), new Variable("a")),
33
- new Assignment(new Variable("main"), new Variable("b")),
34
- ];
35
-
36
- const table = new SymbolTable(statements);
37
- expect(table.main).toBeInstanceOf(Literal);
38
- expect((table.main as Literal).value).toBe(0);
39
- });
40
-
41
- test("should add external symbols", () => {
42
- const statements = [new Assignment(new Variable("main"), new Variable("external"))];
43
-
44
- const table = new SymbolTable(statements, { external: 42 });
45
- expect(table.symbols.external).toBe(42);
46
- expect(table.main).toBeInstanceOf(Literal);
47
- expect((table.main as Literal).value).toBe(42);
48
- });
49
-
50
- test("should track symbol count", () => {
51
- const statements = [
52
- new Assignment(new Variable("a"), new Literal(0)),
53
- new Assignment(new Variable("b"), new Literal(1)),
54
- new Assignment(new Variable("main"), new Variable("a")),
55
- ];
56
-
57
- const table = new SymbolTable(statements);
58
- // Size counts all literal assignments: a=0, b=1, and the resolved main=a (which is 0)
59
- expect(table.size).toBe(3);
60
- });
61
-
62
- test("should include external symbols in size count", () => {
63
- const statements = [new Assignment(new Variable("main"), new Variable("ext"))];
64
-
65
- const table = new SymbolTable(statements, { ext: 0 });
66
- // Size counts: external symbol ext=0, and the resolved main (which is also a literal)
67
- expect(table.size).toBe(2);
68
- });
69
- });
@@ -1,108 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import { addAll, equal, union } from "../src/utils.js";
3
-
4
- describe("utils", () => {
5
- describe("union", () => {
6
- test("should return union of two sets", () => {
7
- const a = new Set([1, 2, 3]);
8
- const b = new Set([3, 4, 5]);
9
- const result = union(a, b);
10
- expect(result).toEqual(new Set([1, 2, 3, 4, 5]));
11
- });
12
-
13
- test("should not modify original sets", () => {
14
- const a = new Set([1, 2]);
15
- const b = new Set([3, 4]);
16
- union(a, b);
17
- expect(a).toEqual(new Set([1, 2]));
18
- expect(b).toEqual(new Set([3, 4]));
19
- });
20
-
21
- test("should handle empty sets", () => {
22
- const a = new Set<number>();
23
- const b = new Set([1, 2]);
24
- expect(union(a, b)).toEqual(new Set([1, 2]));
25
- expect(union(b, a)).toEqual(new Set([1, 2]));
26
- });
27
-
28
- test("should handle both empty sets", () => {
29
- const a = new Set<number>();
30
- const b = new Set<number>();
31
- expect(union(a, b)).toEqual(new Set());
32
- });
33
-
34
- test("should work with iterable as second argument", () => {
35
- const a = new Set([1, 2]);
36
- const b = [3, 4];
37
- const result = union(a, b);
38
- expect(result).toEqual(new Set([1, 2, 3, 4]));
39
- });
40
- });
41
-
42
- describe("addAll", () => {
43
- test("should add all items from source to target", () => {
44
- const target = new Set([1, 2]);
45
- const source = new Set([3, 4]);
46
- addAll(target, source);
47
- expect(target).toEqual(new Set([1, 2, 3, 4]));
48
- });
49
-
50
- test("should handle duplicates", () => {
51
- const target = new Set([1, 2, 3]);
52
- const source = new Set([2, 3, 4]);
53
- addAll(target, source);
54
- expect(target).toEqual(new Set([1, 2, 3, 4]));
55
- });
56
-
57
- test("should handle empty source", () => {
58
- const target = new Set([1, 2]);
59
- const source = new Set<number>();
60
- addAll(target, source);
61
- expect(target).toEqual(new Set([1, 2]));
62
- });
63
-
64
- test("should work with array as source", () => {
65
- const target = new Set([1, 2]);
66
- const source = [3, 4, 5];
67
- addAll(target, source);
68
- expect(target).toEqual(new Set([1, 2, 3, 4, 5]));
69
- });
70
- });
71
-
72
- describe("equal", () => {
73
- test("should return true for identical sets", () => {
74
- const a = new Set([1, 2, 3]);
75
- expect(equal(a, a)).toBe(true);
76
- });
77
-
78
- test("should return true for equal sets with same elements", () => {
79
- const a = new Set([1, 2, 3]);
80
- const b = new Set([1, 2, 3]);
81
- expect(equal(a, b)).toBe(true);
82
- });
83
-
84
- test("should return true for equal sets with different insertion order", () => {
85
- const a = new Set([1, 2, 3]);
86
- const b = new Set([3, 2, 1]);
87
- expect(equal(a, b)).toBe(true);
88
- });
89
-
90
- test("should return false for sets with different sizes", () => {
91
- const a = new Set([1, 2, 3]);
92
- const b = new Set([1, 2]);
93
- expect(equal(a, b)).toBe(false);
94
- });
95
-
96
- test("should return false for sets with different elements", () => {
97
- const a = new Set([1, 2, 3]);
98
- const b = new Set([1, 2, 4]);
99
- expect(equal(a, b)).toBe(false);
100
- });
101
-
102
- test("should return true for empty sets", () => {
103
- const a = new Set<number>();
104
- const b = new Set<number>();
105
- expect(equal(a, b)).toBe(true);
106
- });
107
- });
108
- });
package/tsconfig.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "allowJs": true,
5
- "rootDir": "src",
6
- "outDir": "dist",
7
- "types": ["node"]
8
- },
9
- "include": [
10
- "dfa.d.ts",
11
- "src/**/*.ts"
12
- ],
13
- "exclude": [
14
- "dist"
15
- ]
16
- }
@@ -1,8 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "types": ["node"],
5
- "noEmit": true
6
- },
7
- "include": ["**/*.ts"]
8
- }
@@ -1,16 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "noEmit": true,
5
- "rootDir": "."
6
- },
7
- "include": [
8
- "**/*.ts",
9
- ],
10
- "exclude": [
11
- "dist",
12
- "node_modules",
13
- "src/grammar.js",
14
- "vendor"
15
- ]
16
- }
package/vitest.config.ts DELETED
@@ -1,8 +0,0 @@
1
- import { defineConfig } from "vitest/config";
2
-
3
- export default defineConfig({
4
- test: {
5
- environment: "node",
6
- include: ["test/**/*.test.ts"],
7
- },
8
- });