プログラムから建物を読む
.muro を TypeScript から読み、診断を取り、正準JSONに落とすところまでを 20行で通す。CLI が答えることは API も答える — CLI はこの API の一つの入口にすぎない。
前提は Node.js 22 以上だけである。チュートリアルを通していると、読ませる建物が手元にあって都合がよい。
用意する
作業ディレクトリを作り、koyu と tsx を入れる。
mkdir koyu-first && cd koyu-first
npm init -y
npm install @kensnzk/koyu
npm install --save-dev tsx
package.json に "type": "module" を入れておく。読ませる建物として house.muro を置く — 中身はチュートリアル第6段の30行そのままでよい。
koyu 1.0
name 小さな家
grid X 0 3600 5400
grid Y 0 4000
level L1 0 h:2400 slab:150
level L2 2800 h:2400 slab:400
space /L1/ldk ldk X1..X2 Y1..Y2 name:LDK floor:オーク daylight:1
...
20行
read.ts を作る。
import { parseFile } from "@kensnzk/koyu/node";
import { areaM2, checkDiagnostics, toCanonical } from "@kensnzk/koyu";
const model = parseFile(process.argv[2] ?? "house.muro");
console.log(`${model.spaces.size} spaces / ${model.boundaries.length} boundaries`);
for (const space of model.spaces.values()) {
if (space.type === "exterior") continue;
console.log(` ${space.path} ${space.type} ${areaM2(space).toFixed(2)} m2`);
}
const diagnostics = checkDiagnostics(model);
for (const d of diagnostics) {
console.log(` ${d.severity} ${d.code} line ${d.line} — ${d.message}`);
}
console.log(diagnostics.some((d) => d.severity === "error") ? "not consistent" : "consistent");
const canonical = JSON.parse(toCanonical(model));
console.log(canonical.spaces["/L1/ldk"]);
走らせる。
npx tsx read.ts
5 spaces / 7 boundaries
/L1/ldk ldk 14.40 m2
/L1/hall hall 7.20 m2
/L2/bed bedroom 14.40 m2
/L2/hall hall 7.20 m2
consistent
{
type: 'ldk',
at: [ 'X1', 'Y1', 'X2', 'Y2' ],
attrs: { daylight: 1, floor: 'オーク', name: 'LDK' }
}
20行の中身
入口は二つある。@kensnzk/koyu/node の parseFile はファイルシステムから読む入口で、import の相対パスをディスク上で解決する。@kensnzk/koyu 本体は純粋で、ファイルシステムを知らない — ブラウザで動かすときは、そちらの parseFiles に仮想のファイル群を渡す。
parseFile が返す Model が、書かれた構成そのものである。model.spaces はパスをキーにした Map、model.boundaries は配列で、どちらも書かれた宣言をそのまま持っている。判定は入っていない。
面積は areaM2 が答える。壁芯で、単位は㎡である。exterior の空間を飛ばしているのは、外部が領域を持たなくてよいからである。
診断は checkDiagnostics が配列で返す。要素は code (OPN05 のような台帳の記号)、severity ("error" か "warning")、message、line、合成しているときは file を持つ。severity はコードの属性であって、状況では動かない — 同じコードが場合によってエラーになったり警告になったりはしない。だから「エラーが一つでもあるか」は severity を見れば決まる。
toCanonical は文字列を返す。JSON そのものではなく、整形済みの文字列である — バイト列として安定していることに意味があるからで、JSON.parse して使えばよい。書かれた構成だけが入っていて、導出された既定の境界は入っていない。
壊れたファイルを読ませる
house.muro の窓から edge:S を一つ落として broken.muro を作り、同じプログラムに渡す。
npx tsx read.ts broken.muro
5 spaces / 7 boundaries
/L1/ldk ldk 14.40 m2
/L1/hall hall 7.20 m2
/L2/bed bedroom 14.40 m2
/L2/hall hall 7.20 m2
error OPN05 line 29 — There is more than one boundary segment; pick an edge with edge:N/E/S/W (/L2/bed | /out)
not consistent
{
type: 'ldk',
at: [ 'X1', 'Y1', 'X2', 'Y2' ],
attrs: { daylight: 1, floor: 'オーク', name: 'LDK' }
}
診断が出てもモデルは返ってくる。構造整合の診断は解析を止めない — 面積も正準JSONも、そのまま出てくる。
解析そのものが立たないのは、行が読めないときだけである。そのとき parseFile は SourceError を投げる。行番号と、合成しているときは出所のファイルが載っている。型を落とした space /L1/a だけのファイルを読ませてみる。
import { SourceError } from "@kensnzk/koyu";
try {
parseFile("syntaxerr.muro");
} catch (e) {
if (e instanceof SourceError) console.error(e.message);
}
…/syntaxerr.muro:line 4: space /L1/a requires a type (a word from the vocabulary)
この先
- 空間グラフ、動線、採光、敷地など、CLI が答えるものを API から呼ぶ道は TypeScript API にすべて並んでいる。59個の値と 77個の型が公開面である。
- 平面図や立体を SVG で出すなら 図の生成。
- 芯線・厚み・柱・縦動線の立体まで降りるなら 形 — derive(model)。
- 診断コードの意味を引くなら 診断コード索引 — 全65コードが載っている。
- 正準JSONの構造は 正準 JSON。
- LLM エージェントに読み書きさせるなら、同じ導出を12個の道具として出す koyu-mcp がある。