Skip to content

Commit 2d1abe7

Browse files
committed
chore(labrat): Convert to TS
1 parent f020553 commit 2d1abe7

File tree

5 files changed

+127
-54
lines changed

5 files changed

+127
-54
lines changed

packages/labrat/index.test.js renamed to packages/labrat/index.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import assert from 'node:assert/strict';
22
import test from 'node:test';
33

4-
import {Matcher, RuleApplication, Sequence, Choice, Terminal, Repetition} from './index.js';
4+
import {Matcher, RuleApplication, Sequence, Choice, Terminal, Repetition} from './index.ts';
55

66
test('basic matcher', () => {
77
const m = new Matcher({
88
start: new RuleApplication('exp'),
99
exp: new Sequence([
1010
new RuleApplication('var'),
11-
new Repetition(new Sequence([new RuleApplication('op'), new RuleApplication('var')])),
11+
new Repetition(new Sequence([new RuleApplication('op'), new RuleApplication('var')]))
1212
]),
1313
op: new Choice([new Terminal('+'), new Terminal('-')]),
14-
var: new Choice([new Terminal('x'), new Terminal('y'), new Terminal('z')]),
14+
var: new Choice([new Terminal('x'), new Terminal('y'), new Terminal('z')])
1515
});
1616

1717
assert.ok(m.match('x'));
@@ -27,11 +27,11 @@ test('left recursion', () => {
2727
new Sequence([
2828
new RuleApplication('mulExp'),
2929
new Terminal('+'),
30-
new RuleApplication('priExp'),
30+
new RuleApplication('priExp')
3131
]),
32-
new RuleApplication('priExp'),
32+
new RuleApplication('priExp')
3333
]),
34-
priExp: new Choice([new Terminal('pi'), new Terminal('x')]),
34+
priExp: new Choice([new Terminal('pi'), new Terminal('x')])
3535
});
3636
assert.ok(g.match('pi+pi+x'));
3737
});

packages/labrat/index.js renamed to packages/labrat/index.ts

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,32 @@
1+
interface PExpr {
2+
eval(matcher: Matcher): CstNode;
3+
}
4+
5+
type RuleDict = {
6+
[name: string]: PExpr;
7+
};
8+
9+
type CstNode = string | true | null | CstNode[];
10+
11+
interface MemoRec {
12+
cst: CstNode;
13+
nextPos: number;
14+
used?: boolean;
15+
}
16+
17+
type MemoCol = {[key: string]: MemoRec};
18+
119
class Matcher {
2-
constructor(rules) {
20+
rules: RuleDict;
21+
input: string = '';
22+
pos: number = 0;
23+
memoTable: MemoCol[] = [];
24+
25+
constructor(rules: RuleDict) {
326
this.rules = rules;
427
}
528

6-
match(input) {
29+
match(input: string): CstNode {
730
this.input = input;
831
this.pos = 0;
932
this.memoTable = [];
@@ -14,7 +37,7 @@ class Matcher {
1437
return null;
1538
}
1639

17-
memoTableAt(pos) {
40+
memoTableAt(pos: number): MemoCol {
1841
let memo = this.memoTable[pos];
1942
if (!memo) {
2043
// Lazily initialize the memo column.
@@ -23,17 +46,17 @@ class Matcher {
2346
return memo;
2447
}
2548

26-
hasMemoizedResult(ruleName) {
49+
hasMemoizedResult(ruleName: string) {
2750
return !!this.memoTableAt(this.pos)[ruleName];
2851
}
2952

30-
memoizeResult(ruleName, pos, cst) {
53+
memoizeResult(ruleName: string, pos: number, cst: CstNode): MemoRec {
3154
const result = {cst, nextPos: this.pos};
3255
this.memoTableAt(pos)[ruleName] = result;
3356
return result;
3457
}
3558

36-
useMemoizedResult(ruleName) {
59+
useMemoizedResult(ruleName: string) {
3760
const result = this.memoTableAt(this.pos)[ruleName];
3861
// Unconditionally set the position. If it was a failure, `result.cst`
3962
// is `null` and the assignment to `this.pos` is a noop.
@@ -44,13 +67,13 @@ class Matcher {
4467
return result.cst;
4568
}
4669

47-
memoizeLRFailureAtCurrPos(ruleName) {
70+
memoizeLRFailureAtCurrPos(ruleName: string) {
4871
const lrFailure = {cst: null, nextPos: -1, used: false};
4972
this.memoTableAt(this.pos)[ruleName] = lrFailure;
5073
return lrFailure;
5174
}
5275

53-
consume(c) {
76+
consume(c: string) {
5477
if (this.input[this.pos] === c) {
5578
this.pos++;
5679
return true;
@@ -59,12 +82,14 @@ class Matcher {
5982
}
6083
}
6184

62-
class RuleApplication {
63-
constructor(ruleName) {
85+
class RuleApplication implements PExpr {
86+
ruleName: string;
87+
88+
constructor(ruleName: string) {
6489
this.ruleName = ruleName;
6590
}
6691

67-
eval(matcher) {
92+
eval(matcher: Matcher) {
6893
if (matcher.hasMemoizedResult(this.ruleName)) {
6994
return matcher.useMemoizedResult(this.ruleName);
7095
}
@@ -85,12 +110,14 @@ class RuleApplication {
85110
}
86111
}
87112

88-
class Terminal {
89-
constructor(str) {
113+
class Terminal implements PExpr {
114+
str: string;
115+
116+
constructor(str: string) {
90117
this.str = str;
91118
}
92119

93-
eval(matcher) {
120+
eval(matcher: Matcher) {
94121
for (let i = 0; i < this.str.length; i++) {
95122
if (!matcher.consume(this.str[i])) {
96123
return null;
@@ -100,12 +127,14 @@ class Terminal {
100127
}
101128
}
102129

103-
class Choice {
104-
constructor(exps) {
130+
class Choice implements PExpr {
131+
exps: PExpr[];
132+
133+
constructor(exps: PExpr[]) {
105134
this.exps = exps;
106135
}
107136

108-
eval(matcher) {
137+
eval(matcher: Matcher) {
109138
const origPos = matcher.pos;
110139
for (let i = 0; i < this.exps.length; i++) {
111140
matcher.pos = origPos;
@@ -118,13 +147,15 @@ class Choice {
118147
}
119148
}
120149

121-
class Sequence {
122-
constructor(exps) {
150+
class Sequence implements PExpr {
151+
exps: PExpr[];
152+
153+
constructor(exps: PExpr[]) {
123154
this.exps = exps;
124155
}
125156

126-
eval(matcher) {
127-
const ans = [];
157+
eval(matcher: Matcher) {
158+
const ans: CstNode = [];
128159
for (let i = 0; i < this.exps.length; i++) {
129160
const exp = this.exps[i];
130161
const cst = exp.eval(matcher);
@@ -139,12 +170,14 @@ class Sequence {
139170
}
140171
}
141172

142-
class Not {
143-
constructor(exp) {
173+
class Not implements PExpr {
174+
exp: PExpr;
175+
176+
constructor(exp: PExpr) {
144177
this.exp = exp;
145178
}
146179

147-
eval(matcher) {
180+
eval(matcher: Matcher) {
148181
const origPos = matcher.pos;
149182
if (this.exp.eval(matcher) === null) {
150183
matcher.pos = origPos;
@@ -154,13 +187,15 @@ class Not {
154187
}
155188
}
156189

157-
class Repetition {
158-
constructor(exp) {
190+
class Repetition implements PExpr {
191+
exp: PExpr;
192+
193+
constructor(exp: PExpr) {
159194
this.exp = exp;
160195
}
161196

162-
eval(matcher) {
163-
const ans = [];
197+
eval(matcher: Matcher) {
198+
const ans: CstNode = [];
164199
while (true) {
165200
const origPos = matcher.pos;
166201
const cst = this.exp.eval(matcher);

packages/labrat/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
"private": true,
33
"type": "module",
44
"scripts": {
5-
"test": "node --test"
5+
"test": "node --test",
6+
"build": "tsc --noEmit"
7+
},
8+
"devDependencies": {
9+
"@types/node": "^16.18.126",
10+
"typescript": "^5.8.3"
611
}
712
}

packages/labrat/tsconfig.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"files": ["index.ts", "index.test.ts"],
3+
4+
// Based on the following:
5+
// https://www.npmjs.com/package/@tsconfig/recommended
6+
// https://www.npmjs.com/package/@tsconfig/node-ts
7+
// https://www.npmjs.com/package/@tsconfig/node24
8+
"compilerOptions": {
9+
"erasableSyntaxOnly": true,
10+
"esModuleInterop": true,
11+
"forceConsistentCasingInFileNames": true,
12+
"module": "nodenext",
13+
"moduleResolution": "nodenext",
14+
"rewriteRelativeImportExtensions": true,
15+
"skipLibCheck": true,
16+
"strict": true,
17+
"verbatimModuleSyntax": true
18+
},
19+
"$schema": "https://json.schemastore.org/tsconfig"
20+
}

0 commit comments

Comments
 (0)