A compiler that turns structured contract logic into legally formatted agreements โ with mathematical guarantees of completeness.
- What Is This? (Plain English)
- The Core Problem It Solves
- How It Works โ For Lawyers
- How It Works โ For Engineers
- Quick Start
- The DSL โ Writing a Contract
- DSL Language Reference
- CLI Reference
- What the Compiler Checks
- Project Structure
- Architecture Deep Dive
- Roadmap
- Design Decisions & Constraints
LexScript is a tool that lets you write the logic of a legal agreement in a simple, structured format โ and then automatically produces a professionally formatted, legally worded contract document from it.
Think of it like a spreadsheet formula for contracts: instead of a lawyer typing out lengthy paragraphs describing what happens when Party A doesn't pay, you write the rule once in a compact, unambiguous form, and the tool generates the full legal language for you โ identically, every time.
The output is a standard Markdown (.md) document or a formatted PDF (.pdf) โ both produced from the same fixed templates. Any word processor, PDF viewer, or legal drafting tool can open them.
No artificial intelligence is involved. Every word in the output was written by a human legal drafter and is stored as a fixed template. The compiler simply matches your contract logic to the right template and fills in the blanks. This means:
- The same input always produces the exact same output.
- No hallucinated clauses. No invented obligations.
- The legal text is fully auditable and human-reviewable.
A standard written contract is essentially a description of a process: what happens first, what triggers the next step, what happens if someone fails to perform, and how the agreement eventually ends. But natural language is fundamentally ambiguous. Two readers can interpret the same clause differently.
This creates three expensive failure modes:
- Ambiguity โ a clause can be argued to mean two opposite things in court.
- Incompleteness โ a scenario occurs that the contract simply does not address, leaving parties without recourse.
- Inconsistency โ two clauses in the same document contradict each other.
This compiler treats a contract as a finite state machine โ a concept from computer science and mathematics. Every contract has:
- A set of states (e.g., "Awaiting Payment", "Active", "Fulfilled", "Breached")
- Transitions between states, triggered by specific events (e.g., payment received, deadline passed, breach occurred)
- Terminal states โ points where the contract definitively ends
By forcing the contract author to express the agreement in this form, the compiler can mathematically verify properties that are impossible to check in plain text:
| Property | What It Means | How It's Checked |
|---|---|---|
| No dead ends | Every situation has a defined outcome | Every state either ends the contract or leads to another state |
| No unreachable clauses | No clause exists that can never apply | Graph traversal from the starting state visits all states |
| Type safety | "$50,000" is money; "30 days" is time โ they cannot be confused | The compiler validates monetary and temporal primitives |
| Turing-incompleteness | The contract process always terminates; it cannot loop forever | Looping constructs are forbidden at the language level |
You don't need to know how to program to understand and use this tool. Here is the full process in plain terms.
Instead of writing paragraphs, you describe the contract as a series of phases (called states), obligations, and what causes the phase to change (called transitions).
Here is a rental agreement expressed in the LexScript language:
contract RentalAgreement {
party Landlord;
party Tenant;
amount rent = 1500.00 USD;
amount security_deposit = 3000.00 USD;
time_limit lease_duration = 12 months;
time_limit payment_grace = 5 business_days;
state AwaitingDeposit {
require Tenant pays security_deposit;
require Tenant pays rent;
transition PaymentReceived -> Active;
transition time_limit(payment_grace) -> Expired;
}
state Active {
require Landlord provides property_access;
require Tenant pays rent;
transition breach(Tenant) -> TenantBreached;
transition breach(Landlord) -> LandlordBreached;
transition time_limit(lease_duration) -> Fulfilled;
}
state Fulfilled { terminate fulfilled; }
state TenantBreached { terminate breached; }
state LandlordBreached { terminate breached; }
state Expired { terminate expired; }
}
Reading this requires no technical knowledge. Each line has a direct plain-language meaning:
| Line | Plain English |
|---|---|
party Landlord; |
There is a party named Landlord. |
amount rent = 1500.00 USD; |
"Rent" means $1,500.00 USD. |
time_limit lease_duration = 12 months; |
"Lease Duration" means 12 months. |
state AwaitingDeposit { ... } |
There is a phase called "Awaiting Deposit". |
require Tenant pays security_deposit; |
The Tenant is obligated to pay the Security Deposit during this phase. |
transition PaymentReceived -> Active; |
When payment is received, the contract moves to the "Active" phase. |
transition time_limit(payment_grace) -> Expired; |
If the 5-day grace period expires first, the contract moves to "Expired". |
transition breach(Tenant) -> TenantBreached; |
If the Tenant materially breaches the agreement, the contract moves to "Tenant Breached". |
terminate fulfilled; |
This is the end of the contract โ successfully completed. |
One command (Markdown output, the default):
lexs compile RentalAgreement.lxs
Or generate a PDF directly:
lexs compile RentalAgreement.lxs -f pdf
The compiler produces RentalAgreement.md (or .pdf) โ a formatted agreement containing:
- Title, date, and governing law statement
- Party identification block
- Definitions section (all named amounts and time limits)
- Obligations and Performance section (one clause per phase, per obligation)
- Term and Termination section (one clause per terminal state)
- General Provisions (entire agreement, severability, force majeure, notices, etc.)
- Representations and Warranties
- Signature blocks for each party
Important: The compiler does not replace a lawyer. The output templates were designed to reflect standard common-law structures. A qualified attorney should review any document before execution, particularly for jurisdiction-specific requirements.
| Component | Technology |
|---|---|
| Host language | Go 1.24 |
| Parser generator | participle/v2 โ grammar expressed as Go struct tags, no codegen step |
| CLI framework | cobra |
| Template engine | Go standard library text/template |
| Template embedding | Go //go:embed directive โ binary is fully self-contained |
| Graph analysis | gonum/graph โ Tarjan SCC + BFS for cycle detection and reachability |
| PDF backend | go-pdf/fpdf โ A4 PDF output with inline bold rendering |
| LSP transport | sourcegraph/jsonrpc2 โ JSON-RPC 2.0 over stdin/stdout |
| VS Code extension | TypeScript + vscode-languageclient |
| Web playground | Go net/http + embedded static files + viz.js for FSM rendering |
Source (.lxs)
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ FRONTEND โ
โ 1. Pre-scan (forbidden kw.) โ โ hard error on while/for/loop/goto/...
โ 2. Lexer (participle/v2) โ โ token stream
โ 3. Parser (participle/v2) โ โ AST (*ast.Contract)
โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MIDDLE-END (pkg/semantic) โ
โ Pass 1: Symbol table + dupes โ duplicate party/state/var detection
โ Pass 2: Type checking โ currency codes, durations (REQ-2.3)
โ Pass 3: Reference resolution โ all names must be declared
โ Pass 4: State body completenessโ no dead-end states (REQ-2.2)
โ Pass 5: Tarjan SCC + BFS โ cycle detection + reachability (REQ-2.1)
โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ BACKEND (pkg/codegen) โ
โ AST โ ContractData model โ
โ text/template render โ deterministic, no AI (REQ-3.1)
โ Write .md / .pdf output โ
โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ
โผ
Output (.md or .pdf)
Contract
โโโ []Declaration
โโโ PartyDecl { Name string }
โโโ AmountDecl { Name, Value float64, Currency string }
โโโ TimeLimitDecl { Name, Value int, Unit string }
โโโ StateDecl { Name, []StateBody }
โโโ StateBody (union)
โโโ RequireStmt { Party, Action, Object string }
โโโ TransitionStmt { Trigger, Target string }
โ โโโ Trigger (union)
โ โโโ TimeLimitTrigger { Ref string }
โ โโโ BreachTrigger { Party string }
โ โโโ EventName string
โโโ TerminateStmt { Kind string } // fulfilled|breached|expired
- No JDK/Java dependency required โ the grammar is pure Go struct tags
- The parser struct is the AST โ no separate tree-building visitor needed
- 2-token lookahead (
UseLookahead(2)) resolves theTriggeralternative without full backtracking - Sufficient for this LL-style DSL; ANTLR would add complexity without benefit here
The DSL is intentionally not Turing-complete. This is a hard design requirement:
- The lexer does not tokenise looping/jump keywords โ they remain as raw identifiers
- A pre-scan pass runs before the parser and scans the raw source bytes for whole-word occurrences of:
while,for,loop,goto,repeat,until,do,foreach,recurse - Any match produces a compilation error with line number
- Because no cycles can be expressed in the grammar itself (no loop constructs, no recursive references), every FSM is a Directed Acyclic Graph โ guaranteed to halt
- Go 1.24 or later (golang.org)
# Clone the repository
git clone <repo-url>
cd project
# Download dependencies
go mod tidy
# Build the compiler binary
go build -o bin/lexs .On Windows (PowerShell):
go build -o bin\lexs.exe .# Compile the included rental example (Markdown)
./bin/lexs compile examples/rental.lxs
# Output: examples/rental.md
# Compile to PDF
./bin/lexs compile examples/rental.lxs -f pdf
# Output: examples/rental.pdf# Print canonical formatting to stdout
./bin/lexs fmt examples/rental.lxs
# Overwrite in-place
./bin/lexs fmt --write examples/rental.lxs./bin/lexs visualize examples/rental.lxs -o examples/rental.dot
# Render with Graphviz: dot -Tpng examples/rental.dot -o examples/rental.png./bin/lexs validate examples/rental.lxs
# โ examples/rental.lxs: no errors./bin/lexs parse examples/rental.lxs
# Outputs the full Abstract Syntax Tree as JSONA .lxs file has one top-level block: contract <Name> { ... }.
Inside, you declare parties, define named values, then describe states.
contract SimplePayment {
party Payer;
party Payee;
amount invoice = 5000.00 USD;
time_limit due_date = 30 days;
state AwaitingPayment {
require Payer pays invoice;
transition PaymentMade -> Done;
transition time_limit(due_date) -> Overdue;
transition breach(Payer) -> Defaulted;
}
state Done { terminate fulfilled; }
state Overdue { terminate expired; }
state Defaulted { terminate breached; }
}
| Syntax | When it fires | Generated legal text |
|---|---|---|
transition EventName -> TargetState; |
On a named event signal | "Upon the occurrence of Event Name, this Agreement shall transition to..." |
transition time_limit(varName) -> TargetState; |
When a declared duration elapses | "Upon the expiration of the Var Name period without fulfillment..." |
transition breach(PartyName) -> TargetState; |
On material breach by a named party | "In the event of a material breach by Party, this Agreement shall immediately transition to..." |
Append cpi_adjusted to any amount declaration to generate a Consumer Price Index
adjustment clause in ยง2 Definitions:
amount base_salary = 95000.00 USD cpi_adjusted;
Generated: "Base Salary" means the amount of 95000.00 USD, subject to annual adjustment in accordance with the Consumer Price Index (CPI) as published by the relevant national statistical authority.
Declare named calendar dates at the top level of a contract:
date commencement_date = 2026-04-01;
date contract_expiry = 2028-04-01;
Dates appear in ยง2 Definitions with both human-readable and ISO 8601 representation: "Commencement Date" means April 1, 2026 (2026-04-01).
Dates are validated as real calendar dates (the semantic pass rejects 2026-02-30).
| Keyword | Meaning | Legal effect |
|---|---|---|
terminate fulfilled; |
Successful completion | All obligations discharged; no further liability |
terminate breached; |
Material breach | Non-breaching party entitled to all remedies at law |
terminate expired; |
Time ran out without performance | Agreement lapses; written extension required to revive |
// This is a comment โ ignored by the compiler
| Keyword | Purpose |
|---|---|
contract |
Opens the contract block |
party |
Declares a named actor |
amount |
Declares a named monetary value |
time_limit |
Declares a named duration |
date |
Declares a named calendar date (ISO 8601) โ Phase 3 |
state |
Declares a FSM state / contract phase |
require |
Declares an obligation within a state |
transition |
Declares a directed edge to another state |
terminate |
Marks a terminal state |
breach |
Breach trigger in a transition |
time_limit(...) |
Time-limit trigger in a transition |
cpi_adjusted |
Optional modifier on amount โ generates CPI-indexed clause โ Phase 3 |
pays ยท provides ยท delivers ยท signs ยท returns ยท transfers ยท notifies
USD ยท EUR ยท GBP ยท JPY ยท CAD ยท AUD ยท CHF
days ยท business_days ยท weeks ยท months ยท years ยท hours
The following words cause an immediate compilation error if they appear anywhere in a .lxs file:
while ยท for ยท loop ยท goto ยท repeat ยท until ยท do ยท foreach ยท recurse
lexs <command> [flags] <input.lxs>
Runs the full pipeline: parse โ validate โ generate.
lexs compile <input.lxs> [-f md|pdf] [-j common|delaware|california|uk] [-o <output>]
| Flag | Default | Description |
|---|---|---|
-f, --format |
md |
Output format: md (Markdown) or pdf |
-j, --jurisdiction |
common |
Jurisdiction clause library: common, delaware, california, uk |
-o, --output |
<input>.md or <input>.pdf |
Explicit output path |
Jurisdiction variants (Phase 3):
| Value | Governing Law | Dispute Resolution |
|---|---|---|
common |
Common Law | General arbitration / competent jurisdiction |
delaware |
State of Delaware | Delaware Court of Chancery / Superior Court |
california |
State of California | JAMS arbitration (mediation first) |
uk |
England and Wales | LCIA arbitration, London seat |
pakistan |
Pakistan (Contract Act 1872) | Arbitration Act 1940 / Karachi seat |
Exit codes: 0 = success ยท 1 = compilation error (full error list printed to stderr)
Pretty-prints a .lxs source file in canonical style. By default writes to stdout; use --write to overwrite in-place.
lexs fmt [--write] <input.lxs>
| Flag | Default | Description |
|---|---|---|
-w, --write |
false | Overwrite the source file in-place |
Exports the contract's finite state machine as a Graphviz DOT file.
lexs visualize <input.lxs> [-o <output.dot>] [--stdin]
| Flag | Default | Description |
|---|---|---|
-o, --output |
<input>.dot |
Output path for the .dot file |
--stdin |
false | Read .lxs source from stdin (used by IDE integrations) |
Render with: dot -Tpng output.dot -o output.png
Starts the Language Server Protocol server over stdin/stdout. This is intended to be spawned by an editor extension (e.g. the VS Code LexScript extension), not run manually.
lexs lsp
Capabilities:
textDocument/publishDiagnosticsโ errors + warnings on every edittextDocument/hoverโ keyword and symbol descriptionstextDocument/completionโ keywords, declared names, currencies, time unitstextDocument/definitionโ go-to-declaration for parties, amounts, states, dates
Starts the web playground HTTP server.
lexs serve [-a <addr>]
| Flag | Default | Description |
|---|---|---|
-a, --addr |
:8080 |
Address to listen on (e.g. :3000) |
Open http://localhost:8080 in a browser to use the playground.
Runs the frontend and middle-end only. Produces no output file. Useful for pre-commit hooks or CI checks.
lexs validate <input.lxs>
Parses the input and dumps the full Abstract Syntax Tree as formatted JSON to stdout. Useful for debugging grammar issues or inspecting parsed values.
lexs parse <input.lxs>
Prints help for any command:
lexs --help
lexs compile --help
All errors are collected and reported together โ the compiler does not stop at the first error.
Scans raw source bytes before the parser runs. Reports line number of any forbidden keyword.
error: line 7: forbidden keyword "for" โ this DSL is Turing-incomplete and does
not support looping or jump constructs (REQ-1.3)
Reports any party, amount, time_limit, or state declared more than once.
error: 12:5: duplicate party "Tenant" (previously declared at 9:5)
- Currency codes must be one of the seven valid ISO codes
- Duration values must be positive integers
- Duration units must be one of the six valid unit names
- Monetary values must be non-negative
error: 14:5: unknown currency "DOGE" in amount "fee"; valid codes: USD EUR GBP JPY CAD AUD CHF
error: 15:5: time_limit "grace" has non-positive value 0; durations must be positive
Every name used must be declared:
error: 28:9: transition to undefined state "Payed" (state "Active")
โ did you mean "Paid"?
error: 29:9: time_limit trigger references undeclared variable "deadline" (state "Active")
error: 30:9: breach trigger references undeclared party "Renter" (state "Active")
Every state must either contain terminate or at least one transition. A state with neither is a dead end โ execution would be trapped there forever.
error: 22:5: state "Limbo" is a dead end: has no terminate statement and no transitions;
all execution paths must eventually reach a terminate node (REQ-2.2)
Pass 5 builds a directed graph of all states using gonum/graph. It then runs two analyses:
Tarjan's Strongly Connected Components to detect deadlock cycles โ groups of states that can never reach a terminate node:
error: 4:5: states [A B] form a deadlock cycle; no execution path from this
group can reach a terminate node (REQ-2.1 โ Tarjan SCC cycle detection)
Breadth-First Search from the first declared state to flag unreachable states:
error: 35:5: state "GhostState" is unreachable from the initial state "AwaitingPayment" (REQ-2.1)
Every date declaration must be ISO 8601 format (YYYY-MM-DD) representing a real calendar date.
error: 8:5: invalid date value "2026-02-30" in date "expiry"; expected ISO 8601 format
YYYY-MM-DD with a real calendar date
error: 9:5: duplicate date "effective_date" (previously declared at 7:5)
project/
โ
โโโ main.go Entry point โ delegates to cmd.Execute()
โโโ go.mod Go module definition (lexscript, Go 1.24)
โโโ go.sum Dependency checksums
โโโ Makefile Build, test, lint, and example targets
โ
โโโ grammar/
โ โโโ grammar.ebnf Formal EBNF grammar specification
โ
โโโ cmd/ CLI layer (Cobra) โ no business logic here
โ โโโ root.go Root command and Execute() entry point
โ โโโ compile.go `lexs compile` โ full pipeline + forbidden-kw scan
โ โโโ parse.go `lexs parse` โ AST JSON dump (debug)
โ โโโ validate.go `lexs validate` โ semantic checks only (debug/CI)
โ โโโ fmt.go `lexs fmt` โ canonical source formatter
โ โโโ visualize.go `lexs visualize` โ Graphviz DOT export
โ โโโ lsp.go `lexs lsp` โ start LSP server (Phase 4)
โ โโโ serve.go `lexs serve` โ start web playground (Phase 4)
โ
โโโ pkg/
โ โโโ ast/
โ โ โโโ ast.go Lexer definition + all AST node structs + Parser
โ โ
โ โโโ semantic/
โ โ โโโ validate.go 5-pass semantic validator (all errors accumulated)
โ โ
โ โโโ format/
โ โ โโโ format.go AST pretty-printer (canonical .lxs formatting)
โ โ
โ โโโ visualize/
โ โ โโโ visualize.go Graphviz DOT emitter (state machine diagram)
โ โ
โ โโโ codegen/
โ โ โโโ emitter.go AST โ ContractData model + text/template renderer
โ โ โโโ pdf_emitter.go AST โ PDF document via go-pdf/fpdf
โ โ โโโ jurisdiction.go Jurisdiction-specific boilerplate clauses
โ โ โโโ templates/
โ โ โโโ contract.md.tmpl Master Markdown template (embedded in binary)
โ โ
โ โโโ lsp/ Language Server Protocol implementation (Phase 4)
โ โ โโโ protocol.go LSP type definitions (self-contained, no framework)
โ โ โโโ server.go JSON-RPC 2.0 server over stdin/stdout
โ โ โโโ diagnostics.go Compiler pipeline โ LSP Diagnostics bridge
โ โ โโโ hover.go textDocument/hover โ keyword & symbol info
โ โ โโโ completion.go textDocument/completion โ keywords + declared names
โ โ โโโ definition.go textDocument/definition โ go-to-declaration
โ โ
โ โโโ playground/ Web playground HTTP server (Phase 4)
โ โโโ server.go HTTP API: /api/compile, /api/visualize
โ โโโ static/
โ โโโ index.html Single-page playground UI
โ โโโ app.js Client-side compilation & FSM rendering
โ
โโโ vscode-extension/ VS Code extension (Phase 4)
โ โโโ package.json Extension manifest + language contribution
โ โโโ tsconfig.json TypeScript configuration
โ โโโ language-configuration.json Comment/bracket/folding rules
โ โโโ syntaxes/
โ โ โโโ lexscript.tmLanguage.json TextMate grammar for syntax highlighting
โ โโโ src/
โ โโโ extension.ts Extension entry point โ activates LSP + commands
โ โโโ client.ts LanguageClient โ spawns `lexs lsp` over stdio
โ โโโ fsmPreview.ts Webview panel โ live FSM graph rendering
โ
โโโ examples/
โ โโโ rental.lxs Residential rental agreement example
โ โโโ rental.md Compiled Markdown output
โ โโโ rental.pdf Compiled PDF output
โ โโโ rental.dot Graphviz DOT state machine
โ โโโ software_dev.lxs Software development agreement example
โ โโโ software_dev.md Compiled Markdown output
โ โโโ software_dev.pdf Compiled PDF output
โ โโโ software_dev.dot Graphviz DOT state machine
โ
โโโ bin/
โโโ lexs (or lexs.exe on Windows) Compiled binary (git-ignored)
The lexer is defined using participle/v2's lexer.MustSimple with seven ordered rules:
| Rule | Pattern | Notes |
|---|---|---|
Comment |
//[^\n]* |
Elided before parser sees tokens |
Whitespace |
\s+ |
Elided before parser sees tokens |
Arrow |
\-> |
Must precede any - rule to avoid greedy mismatch |
Float |
[0-9]+\.[0-9]+ |
Must precede Int to avoid splitting 1.5 into 1 and .5 |
Int |
[0-9]+ |
|
Ident |
[a-zA-Z_][a-zA-Z0-9_]* |
Covers all keywords; parser disambiguates via literal string matching |
Punct |
[{}();=,] |
Single-character punctuation |
Keywords are not reserved in the lexer โ they are matched as string literals in the grammar. This is deliberate: it avoids a common pitfall in participle where keyword tokens conflict with identifier tokens.
The parser is built with UseLookahead(2), which enables unambiguous selection of Trigger alternatives (time_limit(...) vs breach(...) vs bare EventName) without requiring full backtracking.
Validation errors carry source positions from participle's lexer.Position (filename + line + column). All five passes run regardless of earlier failures โ the programmer sees the complete error list in one compilation attempt.
Pass 5 builds a gonum/graph/simple.DirectedGraph from the state transition graph, then runs two analyses:
topo.TarjanSCCโ detects deadlock cycles (SCCs with more than one node, or single-node SCCs with a self-loop). A cycle means a group of states can never reach aterminatenode.traverse.BreadthFirstโ BFS from the first declared state flags any state not reachable from the contract's entry point.
The template (contract.md.tmpl) is embedded into the binary at compile time via //go:embed. This means the binary is fully self-contained โ no template files need to ship alongside it.
AST-to-legal-text mappings:
| AST construct | Legal clause produced |
|---|---|
AmountDecl |
Defined term in ยง2 Definitions |
TimeLimitDecl |
Defined duration in ยง2 Definitions |
RequireStmt |
Obligation paragraph in ยง3 |
TransitionStmt (event) |
Condition precedent clause in ยง3 |
TransitionStmt (time_limit) |
Term and Termination clause in ยง3 |
TransitionStmt (breach) |
Breach and Remedy clause in ยง3 |
TerminateStmt (fulfilled) |
Fulfillment termination clause in ยง4 |
TerminateStmt (breached) |
Breach termination clause in ยง4 |
TerminateStmt (expired) |
Expiry termination clause in ยง4 |
The template also injects boilerplate ยง5โยง7 sections (General Provisions, Representations & Warranties, Signatures) that are jurisdiction-agnostic and constant across all contracts.
The PDF backend (pdf_emitter.go) reuses the same ContractData model and renders it using go-pdf/fpdf with A4 layout, inline bold text via mixed-font Write() calls, and Windows-1252 bullet characters. Both backends produce identical clause content โ only the presentation format differs.
- Graph-based cycle detection:
gonum/graph+ Tarjan's SCC replaces BFS for full deadlock cycle detection โ REQ-2.1 full implementation - PDF output:
go-pdf/fpdfbackend;-f pdfflag oncompilecommand lexs fmt: Canonical auto-formatter for.lxssource fileslexs visualize: Graphviz.dotstate machine export with colour-coded terminal nodes
- Jurisdiction variants:
--jurisdiction common|delaware|california|uk|pakistanselects a jurisdiction-specific boilerplate clause library per REQ-3.2. Each jurisdiction provides a distinct governing law statement, severability clause, dispute resolution section (ยง7), and any additional jurisdiction-specific provisions in ยง5. dateprimitive: Newdate <name> = YYYY-MM-DD;top-level declaration. Dates are validated as real ISO 8601 calendar dates by the semantic pass and appear in ยง2 Definitions with human-readable display (e.g., April 1, 2026).- CPI-adjusted amounts: Optional
cpi_adjustedmodifier on anyamountdeclaration generates an annual Consumer Price Index adjustment clause in ยง2 Definitions.
- LSP server (
lexs lsp): Full Language Server Protocol implementation over stdin/stdout usingsourcegraph/jsonrpc2. Provides real-time diagnostics (error squiggles on every keystroke), hover information (keyword/symbol descriptions), autocompletion (keywords, currencies, time units, declared names), and go-to-definition (jump to party/amount/state/date declarations). Works with any LSP-compatible editor. - VS Code extension (
vscode-extension/): First-class IDE experience for.lxsfiles โ TextMate grammar for syntax highlighting (keywords, currencies, time units, forbidden keywords, dates, numbers, comments), language configuration (bracket matching, folding, indentation), and an FSM Preview command that renders the contract's state machine as an interactive graph in a side panel. The extension spawnslexs lspautomatically for diagnostics, hover, completion, and go-to-definition. - Web playground (
lexs serve): Browser-based editor athttp://localhost:8080with a split-pane UI โ code editor on the left, Markdown output or FSM graph on the right. Supports jurisdiction selection (common/delaware/california/uk), real-time error diagnostics, and FSM visualization via viz.js. All static assets are embedded in the binary via//go:embed.
| Decision | Rationale |
|---|---|
| Turing-incomplete by design | A contract that loops forever is not a contract โ it is a program. Forbidding loops guarantees the FSM halts, which is a legal requirement (parties need certainty). |
| No generative AI in the backend | Legal text must be deterministic and auditable. AI-generated clauses introduce non-determinism and cannot be legally reviewed as a fixed artefact (REQ-3.1). |
participle/v2 over ANTLR |
Avoids a JDK/Java runtime dependency. Grammar is co-located with AST types in a single .go file. Easier for academic review. |
text/template over third-party engines |
Zero dependencies. Output is fully predictable. Custom FuncMap provides all necessary formatting helpers. |
| Errors are accumulated, not short-circuited | A compiler that stops at the first error is frustrating. All five semantic passes run; the programmer sees every problem in one go. |
Template embedded with //go:embed |
The binary is self-contained. No runtime file path resolution required. Deployment is a single executable. |
| Go as host language | Strong typing for AST structs, excellent compilation speed, single-binary deployment, first-class support in participle/v2. |
gonum/graph for cycle detection |
Tarjan's SCC via a well-tested graph library is safer than a hand-rolled DFS. The directed-graph model also makes the BFS reachability pass a natural extension. |
go-pdf/fpdf for PDF output |
Pure-Go, zero CGO, no external binary dependencies. Core font WinAnsi encoding is handled by using correct byte values for bullet characters rather than UTF-8. |
Contributions are welcome! See CONTRIBUTING.md for setup instructions, code style guidelines, and pull request conventions.
LexScript is released under the MIT License.
LexScript v0.4 โ Compiler Construction Project