Add DSL playground with tokenizer and AST inspection

This commit is contained in:
Peter Stockings
2026-09-28 10:31:19 +10:00
parent 834d07d9bc
commit d6c6023093
6 changed files with 144 additions and 12 deletions
+14 -1
View File
@@ -2,13 +2,26 @@ import { test } from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { QuestionnaireEngine, type Answers, type QuestionnaireDefinition, type QuestionDefinition } from "../src/lib/questionnaire/questionnaire-engine";
import { compileExpression, evaluateExpression } from "../src/lib/questionnaire/dsl";
import { compileExpression, evaluateExpression, tokenizeExpression, parseExpression, validateAst } from "../src/lib/questionnaire/dsl";
import { parseResumeDraft } from "../src/lib/questionnaire/resume-draft";
import { SaveEventRecorder, type SaveEvent } from "../src/lib/persistence/save-events";
const claim = JSON.parse(readFileSync(new URL("../src/demo/claim-questionnaire.json", import.meta.url), "utf8"));
const example: QuestionnaireDefinition = JSON.parse(readFileSync(new URL("../src/demo/example-questionnaire.json", import.meta.url), "utf8"));
const today = { today: "2026-09-12" };
test("DSL inspection exposes lexer offsets and keeps parsing separate from validation", () => {
assert.deepEqual(tokenizeExpression(" 'hi' !== null"), [
{ type: "string", value: "hi", position: 2 },
{ type: "operator", value: "!==", position: 7 },
{ type: "identifier", value: "null", position: 11 },
{ type: "eof", value: "", position: 15 },
]);
const ast = parseExpression("unknown(1)");
assert.equal(ast.type, "call");
assert.throws(() => validateAst(ast), /not allowed/);
assert.throws(() => tokenizeExpression("1 @ 2"), /position 2/);
assert.throws(() => tokenizeExpression("1".repeat(4097)), /4096/);
});
const basic = (fields: QuestionDefinition[]): QuestionnaireDefinition => ({ schemaVersion: 1, id: "test", version: "1", title: "Test", pages: [{ id: "first", title: "First", fields }] });
const bool = (id: string): QuestionDefinition => ({ id, type: "boolean", label: id });
const text = (id: string): QuestionDefinition => ({ id, type: "text", label: id });