FormScript
A form is a program that asks questions. FormScript is the language those programs are written in — and, unusually, the language is defined by a document precise enough that an implementation can be checked against it rather than admired.
form loan_application version = "1.0" {
text {
"loan.title": "Loan application"
"loan.monthly": { "default": "Monthly payment", "ar": "القسط الشهري" }
}
field annual_income {
type = number
label = t("loan.income")
validate { min = 0 }
}
field term {
type = integer
label = t("loan.term")
value = 60
}
field monthly {
type = number
label = t("loan.monthly")
value = pmt(0.05 / 12, term, annual_income)
}
output {
annual_income: number
term: integer
monthly: number
}
}
What the specification says, and what follows from it
Every decision in the reference implementation traces to a numbered statement in the specification. There are 312 of them, and each one is listed with its normative text in the specification reference.
That discipline has teeth. Five defects in the specification were found by building the implementation, and each was repaired in the document itself, with the repair recorded in the specification's own revision history — now 26 rows. Two examples:
- The canonical decimal form was defined for a
numberonly, so anintegerhad no defined text on the wire. Two conforming runtimes could have serialised36as36or"36". The specification now says which. - A
choicewas defined as "a field whose type isenum" while the property table separately listedoptionsandoptionsFromfor it — so achoicecould declare astringtype, and there were two overlapping ways to state a choice set. The specification now has one.
The language in one paragraph
A form is a form block. It holds fields, groups, repeats,
choices, sections and pages. A field declares a type —
string, number, integer, boolean, date, datetime, duration,
enum, array, object — and may declare a value, a default, a label,
constraints under validate, and a guard under when. A section and a
page are presentation only: they introduce neither a scope nor a data
level, so a field inside section address is addressed as address.
Expressions are total. There is no undefined and no exception: an operation
that cannot succeed returns the error value, and a field that has never
been answered holds unknown. Logic is three-valued — true, false, and
unknown — so a question whose answer is not yet known does not silently
become a "no".
The numbers
| Packages | 21, one concern each, no cycles |
| Specification statements | 312, all listed in the reference |
| Unit tests | 776 over 21 files |
| Conformance rows executed | 101, all five sections of §18 |
| Error codes | 78, every one with a stable identity |
| Builtins | 67, checked against §6.4 at build time |
| Conformance level | 3 of 5 — the declaration, and its evidence |
Everything runs in your browser
This site is not a mock-up. The playground loads the reference implementation itself — the same lexer, parser, analyser and engine that the conformance suite runs — compiled from this repository to JavaScript and loaded as a native ES module. Type a form, and it is lexed, parsed, analysed and executed in the page.
What you see is what the specification prescribes: the diagnostics carry their codes, the stage order of §2.5 decides which of them you are shown, and the output document is the one §4.33 describes.
Where to go next
- Getting started — install it, write a form, run it.
- The language — a guided tour, section by section.
- Examples — complete documents you can run.
- The packages — every package, its API and the statements it implements.
- Conformance — the level declaration, and what is checked.