はじめに
必要なもの
lp9 の実行ファイル一つと、標準ライブラリのディレクトリです。解釈器、ビルド、言語サーバ、デバッガは全部この一つが受け持ちます。
lp9 run には、ほかに何も要りません。 Windows では SDL3 の DLL が lp9 と一緒に入っています。
lp9 build には Zig のツールチェインが要ります。 LP-9 のプログラムを Zig のソースにして、Zig にネイティブの実行ファイルを作らせるからです。できた実行ファイルは単独で動きます。Windows では SDL3 の DLL を横に置きます。
Linux では SDL3 と日本語のフォントをディストリビューションから入れます。
sudo apt install libsdl3-dev fonts-noto-cjk
動くのは x86_64 だけです。回収がレジスタを読むところと、スタックを切り替えるところが x86_64 向けに書かれています。
入手について
バイナリ(lp9、標準ライブラリ、VS Code の拡張)は無償・現状有姿で提供します。保証はありません。詳しくはトップページの「ライセンスとお問い合わせ」を参照してください。
LP-9 はオープンソースではありません。ソースの開示は NDA を締結したパートナー企業に限られ、有償案件とセットです。本番評価をご検討の際は、事前に お問い合わせ ください。
コマンド
lp9 run hello.lp9 構文木をたどって実行する
lp9 build hello.lp9 Zig を吐いてネイティブの実行ファイルを作る
lp9 build hello.lp9 --out hello.exe 同上。置く場所を指定する
lp9 bundle --out all.exe a.lp9 b.lp9 いくつかのプログラムを一つの実行ファイルにまとめる
lp9 lsp エディタに答える言語サーバ
lp9 dap エディタから使うデバッガ
lp9 api 全部のクラスとセレクタの一覧を書く
lp9 version この lp9 の版(日付の tag。v2026.10.14 など)
開発中は run、配るときは build です。二つは同じ答えを出します。違うのは速さと、深い再帰がどこで止まるかだけです。
lp9 build は、画面を持つプログラムを Windows ではウィンドウのアプリとして作ります。エクスプローラやタスクバーから起動してもコンソールの窓は開きません。画面を持たないプログラムはコンソールのプログラムのままです。
最初のプログラム
Object subclass: #Point fields: (x, y).
Point class >> x: a y: b = Point fields: { x: a, y: b }.
Point >> x = x.
Point >> y = y.
Point >> + other = Point x: x + other x y: y + other y.
Point >> printString = '(', x asString, ', ', y asString, ')'.
p := Point x: 3 y: 4.
q := Point x: 10 y: 20.
(p + q) displayNl. "(13, 24)"
"copyWith: は一部のフィールドを替えた新しい値を返す。元は変わらない。"
(p copyWith: { x: 100 }) displayNl. "(100, 4)"
p displayNl. "(3, 4)"
クラスはフィールドを宣言し、値は fields: で全部のフィールドを与えて作ります。nil が無いので、一つも省けません。値は変わりません。変えたいときは copyWith: で新しい値を作ります。
lp9 run hello.lp9最初のウィンドウ
class >> start と view を持つアクタがあると、ランタイムがウィンドウを開いて、そのアクタを画面として動かします。
imports: (Ui, Theme).
Object subclass: #Bump fields: ().
Actor subclass: #Counter fields: (n).
Counter class >> start = Counter fields: { n: 0 }.
Counter class >> title = 'Counter'.
Counter class >> width = 320.
Counter class >> height = 160.
Counter >> theme = Theme day.
Counter >> Bump = self copyWith: { n: n + 1 }.
Counter >> view = Paint group: #[
Paint rect: 0 y: 0 w: 320 h: 160 color: #paper,
Paint move: (Paint column: #[
W label: ('pressed ' , n asString , ' times'),
W gap: 20,
(W button: 'Press' wide: 120) on: #click send: Bump ] gap: 0) x: 24 y: 28 ].
ボタンを一度押したところ。ウィンドウを開かずに描いたもの
view は描きません。「何を描くか」の記述(Paint)を返すだけで、前のコマと違うところをランタイムが描きます。ボタンを押すと Bump クラスのメッセージが届き、Counter >> Bump が次の状態を返します。色は #paper のように名前で書き、名前が何色かは theme が答えます。
ウィンドウを開かずに確かめる
画面のプログラムは、ウィンドウを開かずに一コマを PNG に描けます。クリック、キー、打った文字、日本語入力の途中の状態も、引数で渡せます。このサイトの画像は全部この方法で LP-9 自身が描いたものです。
lp9 run counter.lp9 --click 70,105 --shot out.png
lp9 run entry.lp9 --click 120,40 --text 受注伝票 --key Return --shot out.png
同梱の検査は、画面に操作を与えて描いたものを比べる形で 29 本あります。
モジュール
一つのファイルが一つのモジュールで、ファイルの中に名前は書きません。プログラムの入口のファイルは Main、取り込まれるファイルはパスで名前が決まります。ui.lp9 は Ui、date_picker.lp9 は DatePicker、net/http_client.lp9 は Net::HttpClient です。
ヘッダに書くのは、取り込むものと公開するものだけです。どちらも省けます。
imports: (Ui, Theme).
exports: (Board, opaque Ticket).
exports: に書いたクラスだけが外から見えます。opaque を付けたクラスは、型とメソッドは公開しますが、fields: で作ることと copyWith: で替えることはそのモジュールの中に残します。
取り込みは、取り込む側のファイルのディレクトリ、LP9_PATH に並べたディレクトリ、標準ライブラリの順に探します。見つからなければ、探した場所を全部並べたエラーになります。
クラスはモジュールと名前の組で区別されます。二つのモジュールが同じ名前のクラスを公開していれば、Sales::Order と修飾します。プログラムが自分の Dict を宣言しても、言語の Dict とはぶつかりません。
標準ライブラリ
LP-9 で書かれています。
Ui | ウィジェット(W)、一行の入力欄(Edit)、複数行の入力欄(Memo)、ツリー(Tree) |
Theme | 色の名前と色の組。昼の Theme day と夜の Theme night |
Table | 一度だけ定義する表の列(見出し、幅、右寄せ) |
Comparable | < だけ書けば >、<=、max: などが付くプロトコル |
Enumerable | inject:into: だけ書けば collect:、select:、detect:、sum などが付くプロトコル |
OrderedDict | 入れた順に歩く辞書 |