はじめに
必要なもの
コンパイラと、ブラウザです。Windows では、実行ファイル lpsc.exe とランタイムのディレクトリが一つのフォルダに入っています。
npm install は不要です。プロジェクトに package.json も node_modules も要りません。LPScript のプログラムが依存するのは、同梱のランタイムだけです。
Node.js は次の二つの場面で使います。lpsc run でブラウザを使わずにプログラムを実行するときと、開発中に画面を配信する簡易サーバを動かすときです。バージョンは 18 以降が必要です。
VS Code 用の拡張が付属しています。診断、ホバー、定義へ移動、補完、参照の検索、名前の変更が使えます。言語サーバへの薄いクライアントで、依存パッケージはありません。
入手について
バイナリ(コンパイラ、言語サーバ、ランタイム、エディタ拡張)は無償・現状有姿で提供します。Windows では一つのディレクトリにまとまっています。保証はありません。詳しくはトップページの「ライセンスとお問い合わせ」を参照してください。
LPScript はオープンソースではありません。ソースの開示は NDA を締結したパートナー企業に限られ、有償案件とセットです。
コンパイラは OCaml で書かれており、dune build でビルドします。必要なのは OCaml 5、dune、menhir だけです。ランタイムとテストにも、依存パッケージはありません。
コンパイラのコマンド
lpsc check orders.lps 構文解析と型検査だけ行う
lpsc emit orders.lps 入口モジュールの JavaScript を標準出力に出す
lpsc build orders.lps モジュールごとに .mjs と .mjs.map を書く
lpsc build orders.lps --minify 同上。局所変数を短くし、空白を落とす
lpsc build orders.lps --no-map 同上。.mjs だけを書く。配置用
lpsc run orders.lps ビルドして、ソースマップ付きで実行する
lpsc version このバイナリをビルドしたときの git の tag
指定するファイルはプログラムの入口です。imports: で指定した他のモジュールも読み込み、全体をまとめて型検査します。出力はモジュール単位で、.lps 一つが .mjs 一つになります。モジュール間の参照は ES の import です。
モジュールの名前はパスで決まります。ファイルの中に宣言は書きません。パスの各段を PascalCase にして :: で繋いだものが名前です。shared/Overlay.lps は Shared::Overlay、net/http_client.lps は Net::HttpClient です。プログラムの入口のファイルは、名前が何であれ Main です。一ファイルのプログラムにはヘッダが要りません。
ヘッダが必要な場合に書くのは、取り込むもの、公開するもの、対になる HTML ページです。
imports: (Shared::Rpc, Shared::Validate).
exports: (Rpc, opaque Session).
page: 'entry.html'.
imports: が指すのはモジュールで、ファイルではありません。コンパイラは取り込む側のディレクトリから上へ順に探し、各段は書いたままか snake_case で照合します。見つけたファイルのパスが名前と一致しなければエラーです。一つのファイルを二つの名前で取り込んだ場合もエラーです。
取り込んだモジュールが同じクラス名を公開しているときは、Sales::Order と修飾します。
exports: (opaque Session) は、型とそのメソッドだけを公開します。fields: copyWith: atField: atField:put: は宣言したモジュールの中に残るので、外からはそのモジュールに頼みます。
Session is exported opaque, so `fields:` belongs to Greeter.lps — ask it for what you need instead
出力ファイルはモジュールの名前になります(Shared_Overlay.mjs)。入口のファイルだけは元の名前のままです。HTML がその名前を書いているためです。
バージョンは v2026.9.8 のような日付で、ビルド時の git の tag をそのまま埋め込みます。tag より後のコミットでビルドしたものは v2026.9.8-13-g1937fa4 のようになるので、リリース版と作業中のビルドを区別できます。
最初のプログラム
Object subclass: #Greeting fields: (who : Str).
Greeting class >> to: name = Greeting fields: { who: name }.
Greeting >> line = 'こんにちは、' , who , 'さん'.
(Greeting to: '東海精機') line displayNl.
lpsc run hello.lps で実行すると、一行表示されます。
値を作るのは fields: で、全フィールドを指定します。もっと短い作り方はクラス側のメソッドとして自分で書きます。上の Greeting to: がそれで、クラスの既定値はそこに置きます。自動では何も生成されません。
値は変更できません。copyWith: は指定したフィールドを差し替えた新しい値を返します。
最初の画面
画面は、デザイナが書いた HTML の一部の領域を担当するアクタです。
<!-- index.html。デザイナが書いたもの -->
<div id="app">
<p class="count">0</p>
<button class="up">増やす</button>
</div>page: 'index.html'.
Object subclass: #Up.
Actor subclass: #Screen fields: (n : Int).
Screen class >> new = Screen fields: { n: 0 }.
Screen >> Up = self copyWith: { n: n + 1 }.
Screen >> view =
(Html clone: '#app' fill: { '.count' -> n asString })
at: '.up' on: #click send: Up.
App mount: Screen new at: '#app'.
page: 'index.html' は、この画面が使う HTML ファイルをコンパイラに指定します。コンパイラはその HTML を読み、プログラムに書いたセレクタ('#app'、'.count'、'.up')が HTML に存在するかを検査します。デザイナが class="count" を別の名前に変えると、コンパイルエラーになります。
Html clone: '#app' fill: {...} は HTML の #app を複製し、.count の中身を n の文字列に置き換えます。at: '.up' on: #click send: Up は、.up がクリックされたら自分に Up メッセージを送る、という指定です。Up はメッセージクラスで、Screen >> Up がその名前を持つハンドラです。ハンドラは n を 1 増やした新しい状態を返し、状態が変わったので画面が描き直されます。
次に読むもの
アクタ —— 状態はアクタが持ち、メッセージが届くたびにハンドラが次の状態を返します。画面の描き直しも、サーバとの通信も、この仕組みの上に載っています。
画面 —— デザイナが書いた HTML を使う方法、イベントをハンドラ名で指定する理由、一万行の一覧の描画コスト。
設計 —— 金額、日付、JSON の扱い。型システムが保証することと、しないこと。
JavaScript —— コンパイラが出力する JavaScript と、ブラウザの機能を呼ぶ方法。
例 —— 同梱の画面の説明。