Skip to content

Commit caa52b0

Browse files
committed
feat(tests): use startAt parameter
TODO: create dependent type for output
1 parent 50863c8 commit caa52b0

7 files changed

Lines changed: 166 additions & 112 deletions

File tree

apps/parser/src/validate.ts

Lines changed: 70 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import { assert, assertEquals, assertGreater } from '@std/assert';
22
import type {
3-
BodyCstChildren,
4-
ConstantCstChildren,
5-
DeclarationCstChildren,
6-
ExpressionCstChildren,
7-
FileCstChildren,
8-
IfPredBodyCstChildren,
9-
StatementCstChildren,
3+
BodyCstNode,
4+
ConstantCstNode,
5+
DeclarationCstNode,
6+
ExpressionCstNode,
7+
FileCstNode,
8+
IfPredBodyCstNode,
109
StatementCstNode,
11-
TypeCstChildren,
12-
ValueCstChildren,
10+
TypeCstNode,
11+
ValueCstNode,
1312
} from '@/generated/cst-types.ts';
1413

1514
export const skip = undefined;
@@ -28,19 +27,21 @@ export type ValidationFunction =
2827
| typeof constant
2928
| typeof type;
3029

31-
export function file(node: FileCstChildren, args?: Statement[] | None) {
30+
export function file(node: FileCstNode, args?: Statement[] | None) {
31+
assertEquals(node.name, 'file');
32+
const file = node.children;
3233
// biome-ignore lint/complexity/useOptionalChain: args could be false without being nullish
3334
if (args && args.length) {
34-
assert(node.statement, `File: expected 1+ statements but received ${node.statement?.length}`);
35+
assert(file.statement, `File: expected 1+ statements but received ${file.statement?.length}`);
3536
}
3637
if (args === false) {
3738
assert(
38-
!node.statement?.length,
39-
`File: expected 0 statements but received ${node.statement?.length}`,
39+
!file.statement?.length,
40+
`File: expected 0 statements but received ${file.statement?.length}`,
4041
);
4142
}
42-
if (node.statement && args !== false) {
43-
statement_list(node.statement, args);
43+
if (file.statement && args !== false) {
44+
statement_list(file.statement, args);
4445
}
4546
}
4647

@@ -59,7 +60,7 @@ function statement_list(statements: StatementCstNode[], args?: Statement[]) {
5960
);
6061
}
6162
for (let i = 0; i < statements.length; i++) {
62-
statement(statements[i].children, args?.[i]);
63+
statement(statements[i], args?.[i]);
6364
}
6465
}
6566

@@ -89,7 +90,9 @@ export type Statement =
8990
| ['expression', Expression | Skip]
9091
| None
9192
| Skip;
92-
export function statement(stmt: StatementCstChildren, args?: Statement) {
93+
export function statement(node: StatementCstNode, args?: Statement) {
94+
assertEquals(node.name, 'statement');
95+
const stmt = node.children;
9396
if (args === none) {
9497
assert(
9598
Object.keys(stmt).length === 1 && stmt.SEMI,
@@ -103,7 +106,7 @@ export function statement(stmt: StatementCstChildren, args?: Statement) {
103106
`Statement: expected ${args[0]} but received declaration`,
104107
);
105108
}
106-
declaration(stmt.declaration[0].children, (args?.[1] as Declaration) || skip);
109+
declaration(stmt.declaration[0], (args?.[1] as Declaration) || skip);
107110
} else if (stmt.BREAK) {
108111
if (args) {
109112
assertEquals(args[0], 'break', `Statement: expected ${args[0]} but received break`);
@@ -125,7 +128,7 @@ export function statement(stmt: StatementCstChildren, args?: Statement) {
125128
);
126129
if (stmt.expression) {
127130
assertEquals(stmt.expression.length, 1);
128-
expression(stmt.expression[0].children, (args?.[1] as Expression) || skip);
131+
expression(stmt.expression[0], (args?.[1] as Expression) || skip);
129132
}
130133
} else if (stmt.IF && stmt.ifPredBody) {
131134
if (args) {
@@ -141,10 +144,10 @@ export function statement(stmt: StatementCstChildren, args?: Statement) {
141144
`Statement: expected ${p.length} if-preds but received ${predBody.length}`,
142145
);
143146
}
144-
ifPredBody(predBody[bodyCount++].children, (p && (p[0] as IfPredBody)) || skip);
147+
ifPredBody(predBody[bodyCount++], (p && (p[0] as IfPredBody)) || skip);
145148
if (stmt.ELIF) {
146149
stmt.ELIF.forEach(() => {
147-
ifPredBody(predBody[bodyCount].children, (p && (p[bodyCount] as IfPredBody)) || skip);
150+
ifPredBody(predBody[bodyCount], (p && (p[bodyCount] as IfPredBody)) || skip);
148151
bodyCount++;
149152
});
150153
}
@@ -153,7 +156,7 @@ export function statement(stmt: StatementCstChildren, args?: Statement) {
153156
`Statement > else: expected ${!!e} but received ${!!stmt.body}`,
154157
);
155158
if (stmt.ELSE && stmt.body) {
156-
body(stmt.body[0].children, e as Body);
159+
body(stmt.body[0], e as Body);
157160
}
158161
} else if (stmt.WHILE && stmt.expression) {
159162
if (args) {
@@ -170,9 +173,9 @@ export function statement(stmt: StatementCstChildren, args?: Statement) {
170173
stmt.body?.[bodyCount],
171174
`Statement > do: expected body but received ${stmt.body?.[bodyCount]}`,
172175
);
173-
body(stmt.body[bodyCount++].children, d as Body);
176+
body(stmt.body[bodyCount++], d as Body);
174177
}
175-
expression(stmt.expression[0].children, we as Expression);
178+
expression(stmt.expression[0], we as Expression);
176179
assert(
177180
wb === skip || (wb ? !stmt.SEMI : stmt.SEMI),
178181
`Statement > while: expected ${!!wb} but received ${!stmt.SEMI}`,
@@ -182,7 +185,7 @@ export function statement(stmt: StatementCstChildren, args?: Statement) {
182185
stmt.body?.[bodyCount],
183186
`Statement > while: expected body but received ${stmt.body?.[bodyCount]}`,
184187
);
185-
body(stmt.body[bodyCount++].children, wb as Body);
188+
body(stmt.body[bodyCount++], wb as Body);
186189
}
187190
assert(
188191
f === skip || (f ? stmt.FINALLY : !stmt.FINALLY),
@@ -193,18 +196,18 @@ export function statement(stmt: StatementCstChildren, args?: Statement) {
193196
stmt.body?.[bodyCount],
194197
`Statement > finally: expected body but received ${stmt.body?.[bodyCount]}`,
195198
);
196-
body(stmt.body[bodyCount++].children, f as Body);
199+
body(stmt.body[bodyCount++], f as Body);
197200
}
198201
} else if (stmt.body) {
199202
if (args) {
200203
assertEquals(args[0], 'body', `Statement: expected ${args[0]} but received body`);
201204
}
202-
body(stmt.body[0].children, args?.[1] as Body);
205+
body(stmt.body[0], args?.[1] as Body);
203206
} else if (stmt.expression) {
204207
if (args) {
205208
assertEquals(args[0], 'expression', `Statement: expected ${args[0]} but received expression`);
206209
}
207-
expression(stmt.expression[0].children, args?.[1] as Expression);
210+
expression(stmt.expression[0], args?.[1] as Expression);
208211
} else {
209212
throw new Error(`Validation: unhandled statement type!\n${JSON.stringify(stmt, null, 2)}`);
210213
}
@@ -214,7 +217,9 @@ export type IfPredBody =
214217
| ['declaration', Declaration | Skip, Body | Skip]
215218
| ['expression', Expression | Skip, Body | Skip]
216219
| Skip;
217-
export function ifPredBody(predBody: IfPredBodyCstChildren, args?: IfPredBody) {
220+
export function ifPredBody(node: IfPredBodyCstNode, args?: IfPredBody) {
221+
assertEquals(node.name, 'ifPredBody');
222+
const predBody = node.children;
218223
if (predBody.LET && predBody.declaration) {
219224
if (args) {
220225
assertEquals(
@@ -223,7 +228,7 @@ export function ifPredBody(predBody: IfPredBodyCstChildren, args?: IfPredBody) {
223228
`IfPredBody: expected ${args[0]} but received declaration`,
224229
);
225230
}
226-
declaration(predBody.declaration[0].children, args?.[1] as Declaration | Skip);
231+
declaration(predBody.declaration[0], args?.[1] as Declaration | Skip);
227232
} else if (predBody.expression) {
228233
if (args) {
229234
assertEquals(
@@ -232,16 +237,18 @@ export function ifPredBody(predBody: IfPredBodyCstChildren, args?: IfPredBody) {
232237
`IfPredBody: expected ${args[0]} but received expression`,
233238
);
234239
}
235-
expression(predBody.expression[0].children, args?.[1] as Expression | Skip);
240+
expression(predBody.expression[0], args?.[1] as Expression | Skip);
236241
} else {
237242
throw new Error(`Validation: unhandled ifPredBody type!\n${JSON.stringify(predBody, null, 2)}`);
238243
}
239244

240-
body(predBody.body[0].children, args?.[2]);
245+
body(predBody.body[0], args?.[2]);
241246
}
242247

243248
export type Declaration = [string | Skip, Type | None | Skip, Expression | None | Skip] | Skip;
244-
export function declaration(decl: DeclarationCstChildren, args?: Declaration) {
249+
export function declaration(node: DeclarationCstNode, args?: Declaration) {
250+
assertEquals(node.name, 'declaration');
251+
const decl = node.children;
245252
const [id, t, e] = args ?? [];
246253
assertEquals(decl.ID.length, 1);
247254
if (id) {
@@ -259,35 +266,37 @@ export function declaration(decl: DeclarationCstChildren, args?: Declaration) {
259266
);
260267
if (decl.type) {
261268
assertEquals(decl.type.length, 1);
262-
type(decl.type[0].children, t || skip);
269+
type(decl.type[0], t || skip);
263270
}
264271
assert(
265272
e === skip || (e ? decl.expression : !decl.expression),
266273
`Declaration > expression: expected ${!!e} but received ${!!decl.expression}`,
267274
);
268275
if (decl.expression) {
269276
assertEquals(decl.expression.length, 1);
270-
expression(decl.expression[0].children, e || skip);
277+
expression(decl.expression[0], e || skip);
271278
}
272279
}
273280

274281
export type Body = Statement[] | None | Skip;
275-
export function body<T extends Body>(node: BodyCstChildren, args?: T) {
276-
assertEquals(node.LCURLY?.at(0)?.image, '{', 'Body: missing {');
282+
export function body<T extends Body>(node: BodyCstNode, args?: T) {
283+
assertEquals(node.name, 'body');
284+
const body = node.children;
285+
assertEquals(body.LCURLY?.at(0)?.image, '{', 'Body: missing {');
277286
// biome-ignore lint/complexity/useOptionalChain: args could be false without being nullish
278287
if (args && args.length) {
279-
assert(node.statement, `Body: expected 1+ statements but received ${node.statement?.length}`);
288+
assert(body.statement, `Body: expected 1+ statements but received ${body.statement?.length}`);
280289
}
281290
if (args === false) {
282291
assert(
283-
!node.statement?.length,
284-
`Body: expected 0 statements but received ${node.statement?.length}`,
292+
!body.statement?.length,
293+
`Body: expected 0 statements but received ${body.statement?.length}`,
285294
);
286295
}
287-
if (node.statement && args !== false) {
288-
statement_list(node.statement, args);
296+
if (body.statement && args !== false) {
297+
statement_list(body.statement, args);
289298
}
290-
assertEquals(node.RCURLY?.at(0)?.image, '}', 'Body: missing }');
299+
assertEquals(body.RCURLY?.at(0)?.image, '}', 'Body: missing }');
291300
}
292301

293302
export type Expression = Parameters<typeof expression>[1];
@@ -305,10 +314,12 @@ export function expression<
305314
T | Skip,
306315
]
307316
| Skip,
308-
>(expr: ExpressionCstChildren, args?: T) {
317+
>(node: ExpressionCstNode, args?: T) {
318+
assertEquals(node.name, 'expression');
319+
const expr = node.children;
309320
const [val, pf, op, rhs] = args ?? [];
310321
assert(expr.value?.at(0)?.children);
311-
value(expr.value[0].children, val);
322+
value(expr.value[0], val);
312323
assert(
313324
pf === skip || (pf ? expr.PostFix : !expr.PostFix),
314325
`Expression > PostFix: expected ${!!pf} but received ${!!expr.PostFix}`,
@@ -347,7 +358,7 @@ export function expression<
347358
);
348359
if (expr.expression) {
349360
assertEquals(expr.expression.length, 1);
350-
expression(expr.expression[0].children, rhs || skip);
361+
expression(expr.expression[0], rhs || skip);
351362
}
352363
}
353364

@@ -361,17 +372,19 @@ export function value<
361372
| ['id', string | Skip]
362373
| ['prefix', string | Skip, T]
363374
| Skip,
364-
>(val: ValueCstChildren, args?: T) {
375+
>(node: ValueCstNode, args?: T) {
376+
assertEquals(node.name, 'value');
377+
const val = node.children;
365378
if (val.expression) {
366379
if (args) {
367380
assertEquals(args[0], 'nested', `Value: expected ${args[0]} but received nested`);
368381
}
369-
expression(val.expression[0].children, args?.at(1) as Expression);
382+
expression(val.expression[0], args?.at(1) as Expression);
370383
} else if (val.constant) {
371384
if (args) {
372385
assertEquals(args[0], 'constant', `Value: expected ${args[0]} but received constant`);
373386
}
374-
constant(val.constant[0].children, args?.at(1) as Constant);
387+
constant(val.constant[0], args?.at(1) as Constant);
375388
} else if (val.ID) {
376389
assertEquals(val.ID.length, 1);
377390
if (args) {
@@ -404,14 +417,16 @@ export function value<
404417
} else {
405418
assertGreater(val.UnOp?.[0].image.length, 0);
406419
}
407-
value(val.value[0].children, args?.at(2) as T);
420+
value(val.value[0], args?.at(2) as T);
408421
} else {
409422
throw new Error(`Validation: unhandled value type!\n${JSON.stringify(val, null, 2)}`);
410423
}
411424
}
412425

413-
export type Constant = [keyof ConstantCstChildren, string] | Skip;
414-
export function constant(c: ConstantCstChildren, args?: Constant) {
426+
export type Constant = [keyof ConstantCstNode['children'], string] | Skip;
427+
export function constant(node: ConstantCstNode, args?: Constant) {
428+
assertEquals(node.name, 'constant');
429+
const c = node.children;
415430
assert(
416431
c.BIN || c.BOOL || c.CMPX || c.INT || c.REAL || c.STRING,
417432
`Constant: unexpected literal type ${Object.keys(c)}`,
@@ -438,7 +453,9 @@ export function constant(c: ConstantCstChildren, args?: Constant) {
438453
}
439454

440455
export type Type = string | Skip;
441-
export function type(t: TypeCstChildren, args?: Type) {
456+
export function type(node: TypeCstNode, args?: Type) {
457+
assertEquals(node.name, 'type');
458+
const t = node.children;
442459
assertEquals(
443460
t.BASIC_TYPE?.length,
444461
1,

apps/parser/test/integration/comments.test.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as TestSubject from '@encode/parser/lib';
22
import { v } from '@encode/parser/lib';
33
import { assertEquals } from '@std/assert';
44
import { performParsingTestCase, useGlobalSettings } from '@/test/utils/mod.ts';
5+
import type { FileCstNode, StatementCstNode } from '../../generated/cst-types.ts';
56

67
Deno.test('Comment parsing #integration', async (t) => {
78
using _globalSettings = useGlobalSettings({ debugTrees: true });
@@ -26,7 +27,8 @@ Deno.test('Comment parsing #integration', async (t) => {
2627

2728
assertEquals(parser.errors.length, 0, 'Parser should not error');
2829

29-
v.file(parserOutput, v.none);
30+
// TODO use dependent types so cast is unnecessary
31+
v.file(parserOutput as FileCstNode, v.none);
3032
});
3133

3234
await t.step('collapsed multiline comment', () => {
@@ -51,23 +53,25 @@ Deno.test('Comment parsing #integration', async (t) => {
5153

5254
assertEquals(parser.errors.length, 0, 'Parser should not error');
5355

54-
v.file(parserOutput, v.none);
56+
v.file(parserOutput as FileCstNode, v.none);
5557
});
5658

5759
await t.step('comments embedded in a string', () => {
5860
const { parserOutput } = performParsingTestCase({
5961
code: "let str = '/*****/ //'; // comments embedded in a string",
6062

6163
parser,
64+
startAt: 'statement',
6265
precedenceHandler,
6366
printer,
6467
typeAnalyzer,
6568
});
6669

6770
assertEquals(parser.errors.length, 0, 'Parser should not error');
6871

69-
v.file(parserOutput, [
70-
['declaration', ['str', v.none, [['constant', ['STRING', "'/*****/ //'"]]]]],
72+
v.statement(parserOutput as StatementCstNode, [
73+
'declaration',
74+
['str', v.none, [['constant', ['STRING', "'/*****/ //'"]]]],
7175
]);
7276
});
7377
});

0 commit comments

Comments
 (0)