Getting Started
What you need
The compiler. On Windows that is lpsc.exe and the runtime directory beside it. And a browser to open what you wrote.
There is no npm install. A project needs no package.json and no node_modules: what an LPScript program depends on is the runtime that ships with the compiler, and nothing else.
Node 18 or later is used for two things — lpsc run, which runs a program without a browser, and the small server that serves a screen while you work on it.
For editing, a VS Code extension comes with it: diagnostics, hover, go-to-definition and completion. It is a thin client for the language server and has no dependencies of its own.
Getting hold of it
The binaries are handed over free of charge and as-is: compiler, language server, runtime and editor extension, one directory of them on Windows. Evaluate freely; there is no warranty. The terms are at the foot of the front page.
LPScript is not open source. Source is disclosed to partner companies under NDA, bundled with a paid engagement.
The compiler itself is OCaml, built with dune build. It needs OCaml 5, dune and menhir and no other library, and neither the runtime nor the test suite has a package dependency.
The four things the compiler does
lpsc check orders.lps # parse and type-check only
lpsc emit orders.lps # print the entry module's JavaScript
lpsc build orders.lps # write .mjs and .mjs.map per module
lpsc run orders.lps # build and run it with source maps on
A file is the entry of a program, not the whole of it. imports: pulls in sibling modules and the lot is checked together; emission is still per module, so one .lps answers one .mjs and a cross-module reference is an ordinary ES import.
A first program
module Hello.
Object subclass: #Greeting fields: (who : Str).
Greeting class >> to: name = Greeting fields: { who: name }.
Greeting >> line = 'こんにちは、' , who , 'さん'.
(Greeting to: '東海精機') line printNl.
lpsc run prints the line. Values never mutate: with: answers a new one.
A first screen
A screen is an actor that owns a region of a page the designer wrote.
<!-- index.html, written by someone who does not know this language -->
<div id="app">
<p class="count">0</p>
<button class="up">増やす</button>
</div>module Counter page: 'index.html'.
Actor subclass: #Screen fields: (n : Int).
Screen class >> new = Screen fields: { n: 0 }.
Screen >> up = self with: { n: n + 1 }.
Screen >> view = self reply: (
(Html clone: '#app' fill: { '.count' -> n asString })
at: '.up' on: #click send: #up).
App mount: Screen new at: '#app'.
page: tells the compiler which document this screen belongs to, and every selector in it is checked against that file. Rename .count in the HTML and the build stops; it does not silently draw nothing.
Where to look next
Actors is the decision the rest follows from: a handler answers the next state, and the mailbox installs it.
Views is how a screen borrows the designer's markup, why events are names rather than blocks, and what a list of ten thousand rows costs.
Design is the readings this language fixes — money, dates, JSON — and what the type system does and does not promise.
JavaScript is what comes out, and how you reach a browser API that the language does not know about.
Examples walks through the screens that come with the distribution.