Tools and runtimes
You have reached the end of the language. This section is about the things that run it.
The command line
npm run formscript -- lint examples/demo.forms
npm run formscript -- run examples/demo.forms name=Ada age=36
npm run formscript -- ast examples/demo.forms
npm run formscript -- conformance
npm run formscript -- errors
npm run formscript -- level
npm run formscript -- spec-lint
| Command | What it does |
|---|---|
lint <file…> |
read a document and print every diagnostic with its place |
ast <file> |
print the parsed document as JSON |
run <file> [p=v …] |
evaluate a form and print its output document |
conformance [spec.md] |
run the conformance suite of §18 |
spec-lint [spec.md] |
check the specification document itself |
errors |
list the error catalogue |
level |
the conformance level, with its evidence |
Two options matter:
--all— report every diagnostic, not only the earliest stage's errors;--json— print the output document as indented JSON.
lint exits non-zero when a document has errors, so it drops straight into a
CI pipeline.
The playground
The playground runs the reference implementation in your browser: the same lexer, parser, analyser and engine the conformance suite runs, compiled to JavaScript and loaded as a native module.
It is not a mock-up. The diagnostics carry their codes, the stage order decides which of them you are shown, and the output document is the one the specification describes.
The packages
Every part of the pipeline is a package you can use on its own:
| Package | What it does |
|---|---|
@formscript/lexer |
text to tokens |
@formscript/parser |
tokens to an AST |
@formscript/ast |
the AST, and how to walk it |
@formscript/analyzer |
the AST to a semantic model, with diagnostics |
@formscript/scope |
names, scopes and the declarations in them |
@formscript/types |
the type system |
@formscript/decimal |
exact decimals |
@formscript/evaluator |
expressions, and the builtins |
@formscript/engine |
the reactive model, and the output document |
@formscript/validation |
constraints, and satisfiability |
@formscript/render |
the render intents |
@formscript/render-dom |
intents to a DOM |
@formscript/render-html |
intents to HTML |
@formscript/runtime |
the browser runtime |
@formscript/a11y |
the accessibility contract |
@formscript/i18n |
locales and formatting |
@formscript/cli |
the command line |
@formscript/conformance |
the conformance suite |
@formscript/errors |
the error catalogue |
@formscript/property |
the property tests |
A linter needs three of them:
import { tokenize } from '@formscript/lexer';
import { parse } from '@formscript/parser';
import { analyze } from '@formscript/analyzer';
const lexed = tokenize(source, { file: 'form.forms' });
if (lexed.diagnostics.length > 0) {
for (const diagnostic of lexed.diagnostics) {
console.error(`${diagnostic.code} ${diagnostic.message}`);
}
process.exit(1);
}
const parsed = parse(lexed.tokens, { file: 'form.forms' });
if (parsed.document === null) process.exit(1);
const result = analyze(parsed.document, { file: 'form.forms' });
for (const diagnostic of result.reported) {
console.error(`${diagnostic.code} ${diagnostic.message}`);
}
reported is the list to show a person: it is the errors of the earliest
stage that produced any, plus every warning.
The full API of every package is in the packages reference.
A second runtime
FormScript is written down so precisely that a second implementation can be checked against it rather than admired. There is one: a Kotlin implementation of the same specification, in a separate repository, written without looking at the TypeScript one.
Both are held to the same three things:
- the conformance suite of §18 — rows read out of the specification itself;
- the corpus — real questionnaires published by health organisations, converted and run through both runtimes;
- an oracle — a handful of forms whose expected output is written out by hand from the specification, with the statement that gives each answer cited.
The oracle is the one that finds things. It is not the first runtime's answers, so it can disagree with them — and it did: it found a repeat that grew an element without re-reading the guards of the entities it held, a dependency graph that ordered a group's composition before the group's own guard, and a dotted path that two halves of the runtime read two different ways.
Those were defects in the reference implementation, found by asking the specification what the answer was. Where the specification was silent, it was amended, in public, with the amendment written down.
The corpus
The corpus is the other half of the check. It is real forms — published by health organisations — converted into FormScript and run through the engine.
the corpus page reports how many forms there are, how many converted cleanly, what could not be carried over and why, and what the engine produced for the rest. A second corpus does the same for XForms.
Where to go from here
- The language, section by section — the reference version of this course.
- The specification — the normative text.
- Conformance — what is checked, and how.
- The examples — complete documents you can run.