@chr33s/pdf-dfa 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.
- package/README.md +91 -0
- package/dfa.d.ts +44 -0
- package/dist/compile.d.ts +6 -0
- package/dist/compile.js +22 -0
- package/dist/compile.js.map +1 -0
- package/dist/dfa.d.ts +16 -0
- package/dist/dfa.js +81 -0
- package/dist/dfa.js.map +1 -0
- package/dist/grammar.d.ts +11 -0
- package/dist/grammar.js +1266 -0
- package/dist/grammar.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes.d.ts +113 -0
- package/dist/nodes.js +241 -0
- package/dist/nodes.js.map +1 -0
- package/dist/state-machine.d.ts +29 -0
- package/dist/state-machine.js +71 -0
- package/dist/state-machine.js.map +1 -0
- package/dist/symbol-table.d.ts +17 -0
- package/dist/symbol-table.js +64 -0
- package/dist/symbol-table.js.map +1 -0
- package/dist/utils.d.ts +12 -0
- package/dist/utils.js +34 -0
- package/dist/utils.js.map +1 -0
- package/package.json +41 -0
- package/scripts/build-grammar.ts +33 -0
- package/src/compile.ts +31 -0
- package/src/dfa.ts +104 -0
- package/src/grammar.js +1312 -0
- package/src/grammar.peg +72 -0
- package/src/index.ts +9 -0
- package/src/nodes.ts +308 -0
- package/src/state-machine.ts +94 -0
- package/src/symbol-table.ts +78 -0
- package/src/utils.ts +38 -0
- package/test/compile.test.ts +131 -0
- package/test/dfa.test.ts +87 -0
- package/test/nodes.test.ts +324 -0
- package/test/parse-build.test.ts +50 -0
- package/test/state-machine.test.ts +132 -0
- package/test/symbol-table.test.ts +69 -0
- package/test/utils.test.ts +108 -0
- package/tsconfig.json +16 -0
- package/tsconfig.test.json +8 -0
- package/tsconfig.typecheck.json +16 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,132 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
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
ADDED