/** * lowering.ts — SCP ソース → component-graph IR の **lowering 規則の SSoT**(reader 非依存)。 * * 本体を読むのは AST reader(`commands/ts-compile.ts`)— ソースを TS AST として静的に読む。 * **「本体をどう読むか」と「どう lower するか」は層が違う**(規則を reader 側に散らせば * 「同じソースが読み方次第で違う IR になる」= SSoT 崩壊)。そのため * **「値 → Expression IR」「ノード生成(gate / Wire / outType / portSchemas)」「構造化制御 * (Conditional / Guard / Map / Fanout)」「Component 組み立て」はすべてこのモジュールが所有**し、 * reader は「本体をどう読むか」だけを担ってここの関数を呼ぶ。 * * lowering 先は **既存の component-graph IR そのもの**(behavior.ts / scp-ir-architecture.md §5。 * IR スキーマは変えない)。末尾の検証・plan 導出・正規化・Guard は `builder.ts` の * {@link buildComponentCore} に委譲する(そちらが検証・正規化の SSoT)。 */ import type { BodyNode, CatalogEntry, Component, PortSchema, PortableType } from "./behavior.js"; import { DEFAULT_PLAN_CONCURRENCY } from "./builder.js"; import { type ElementPolicyKind, type PolicyKind } from "./plan.js"; /** * AUTHORING_FAILURE_CODES — compile seam が loud reject に使う失敗コードの**閉集合 SSoT**。 * * 列挙は **runtime 値**(型は下でこの配列から導出する — 宣言を 2 箇所に持たない)。ここが runtime で * 列挙できることがゲートの前提: `ast-equivalence.test.ts` の `[completeness]` が「全コードに最低 1 本の * reject ベクタがある」ことを機械検査するので、**コードを足せばベクタも要る**(#206)。 */ export declare const AUTHORING_FAILURE_CODES: readonly ["NOT_A_BEHAVIOR_CLASS", "NO_PUBLIC_METHOD", "NON_STATIC_MARKER", "COMPUTED_ACCESS", "BAD_PORT_VALUE", "UNKNOWN_PORT", "MISSING_PORT", "NOT_PORTABLE_COMPONENT", "BAD_CONDITION", "BAD_BRANCH", "NESTED_BRANCH", "MAP_IN_BRANCH", "NESTED_MAP", "MAP_NOT_LOWERABLE", "MAP_MULTI_COMPONENT", "FANOUT_NOT_LOWERABLE", "FANOUT_MULTI_COMPONENT", "FANOUT_IN_BRANCH", "BAD_AS_TARGET", "BAD_TYPE_NOTATION", "BAD_OUTPUT", "OUTPUT_TYPE_MISMATCH", "UNMAPPABLE_TS_TYPE", "NOT_STATIC", "PORT_OPTIONALITY_CONFLICT", "CONFLICTING_LEAF_DECLARATION", "BAD_ERROR_POLICY"]; export type AuthoringFailureCode = (typeof AUTHORING_FAILURE_CODES)[number]; export declare class AuthoringFailure extends Error { code: AuthoringFailureCode; constructor(code: AuthoringFailureCode, message: string); } export declare function afail(code: AuthoringFailureCode, message: string): never; /** * EXPR_META — 本体中の「式」を表す抽象値の鍵。recorder reader は Proxy に、AST reader は * 素のオブジェクトにこの鍵で {@link ExprMeta} を載せる。以降の lowering({@link lowerValue} 他)は * **carrier の実装を問わず** meta だけを読むため、両 reader は同じ規則を通る。 */ export declare const EXPR_META: unique symbol; export interface ExprMeta { /** lower 済み Expression IR ノード。 */ node: unknown; /** node が ref/refOpt のときの path(プロパティアクセスで延長可能)。演算結果は null。 */ refPath: string[] | null; refOp: "ref" | "refOpt"; /** `$`(Input)root か(最初のフィールドアクセスで inputPorts に記録する)。 */ isInput: boolean; /** * map/fanout コールバック内の component 結果か(`.as(t)` を pendingMap.outType へ回す * マーカー。この結果は body ノードを持たない — 要素結果は単一 Map/Fanout ノードに畳まれる)。 */ mapResult?: boolean; } /** 式の抽象値なら ExprMeta を返す。 */ export declare function exprMetaOf(v: unknown): ExprMeta | undefined; /** * 式の抽象値の runtime 像。compile-time の型は authoring の関心事(`In<...>` / consumer の * Catalog 型)に委ね、値は構造の記録に徹する。 */ export type Recorded = any; /** carrier 非依存の抽象値(AST reader が使う素のオブジェクト carrier)。 */ export declare function plainExpr(meta: ExprMeta): Recorded; /** 演算結果(ref path を持たない)の meta。 */ export declare function opMeta(node: unknown): ExprMeta; /** ノード結果(`{ref:[]}` root。プロパティアクセスで path 延長可能)の meta。 */ export declare function nodeMeta(id: string): ExprMeta; /** 名前付き入力 port(#191 新形: 各引数=port。`{ref:[name]}` root・isInput)の meta。`opt(name)` が * `markInputPortOptional` を発火できるよう isInput:true・refPath 単一セグメントにする(`$.name` と同像)。 */ export declare function inputPortMeta(name: string): ExprMeta; /** * プロパティアクセス(`$.email` / `row.tagRefs`)を 1 段 lower する。ref path を持たない * (演算結果)値へのアクセスは注記先が無いため fail-closed。 */ export declare function lowerMember(f: GraphFrame, meta: ExprMeta, prop: string): ExprMeta; /** * 本体に現れた値を Expression IR へ lower する。 * 抽象値はその IR ノードに、リテラルはそのまま(配列 → `{arr:[...]}`、オブジェクト → * `{obj:{...}}`、bigint → `{int:"..."}`)に落ちる。関数 / Date / Promise / undefined は * Portability 違反として loud に失敗する(fail-closed)。 */ export declare function lowerValue(v: unknown, at: string): unknown; /** 2 項演算子の閉集合(両 reader が読む唯一の語彙表)。 */ export declare const BINARY_EXPR_OPS: readonly ["add", "sub", "mul", "div", "mod", "eq", "ne", "lt", "le", "gt", "ge", "and", "or", "coalesce"]; /** 単項演算子の閉集合。 */ export declare const UNARY_EXPR_OPS: readonly ["neg", "not", "len"]; export type BinaryExprOp = (typeof BINARY_EXPR_OPS)[number]; export type UnaryExprOp = (typeof UNARY_EXPR_OPS)[number]; export declare function lowerBinOp(op: BinaryExprOp, a: unknown, b: unknown): ExprMeta; export declare function lowerUnOp(op: UnaryExprOp, a: unknown): ExprMeta; /** `+`(文字列連結)。n-ary(min 2)。 */ export declare function lowerConcat(parts: readonly unknown[]): ExprMeta; /** * `opt(x)` — `?.`(optional chaining)の lowering。ref を refOpt に変換する * (以降のプロパティアクセスは中間 null で null を返す — expression-ir.md `refOpt`)。 * * `opt($.x)` は「その input Port は省略可」という**宣言**でもある。宣言(inputPorts)が * optionality の SSoT(runBehavior の束縛規則も native lowering もそこだけを読む)なので、 * ここでその SSoT を実際に立てる — 立てなければ「式は refOpt・宣言は required:true」という * 矛盾した 2 枚の carrier が残り、宣言だけを読む下流は optionality を見失う。 * * 単一セグメント(`opt($.x)`)のときだけ head を optional にする。多段(`opt($.user.name)`)は * 「途中/末端が null を取り得る」の宣言であって head Port 自体の省略可性ではないため触らない。 */ export declare function lowerOpt(f: GraphFrame, x: unknown): ExprMeta; /** * `.map` の opts キーの閉集合(**runtime-enumerable SoT**)。{@link MapOptions} の型はここから * 導出し、新 opts キーを足すときはここに 1 語追加するのが正。matrix の網羅ゲートはこの配列から * 導出するので、SoT にキーを足して matrix にケースが無ければ CI が RED になる。 * 注: 要素ごと skip の `when` は opts キーではなく callback 内 `when(...)` guard 由来 * (`map.when` へ lower)なので、ここには含めない。 */ export declare const MAP_OPTION_KEYS: readonly ["into", "batched", "elementPolicy"]; /** `.map` の 1 opts キー({@link MAP_OPTION_KEYS} から導出)。 */ export type MapOptionKey = (typeof MAP_OPTION_KEYS)[number]; interface MapOptionShapes { /** v2 zip-attach キー(scp-ir-architecture.md §5.1 `map.into`)。 */ into: string; /** v2 バッチ fan-out(`map.batched`)。 */ batched: boolean; /** * Element Error Policy Kind(scp-error.md「The Element Error Policy」)。`error`(既定)=要素の * Failure が map の Component Failure になる / `skip` =失敗要素を結果から落とす(順序は保つ)。 * * `skip` が合法な文脈(batched map では要素ごとの Failure が存在しないので不可)の判定は guard が * 5 言語で持つ単一のルール(`guard.ts` map ノード検査)。ここでは複製しない。 */ elementPolicy: ElementPolicyKind; } /** `.map` の opts。キー集合は {@link MAP_OPTION_KEYS} が SoT(型は shape から取り、キーは SoT に一致)。 */ export type MapOptions = { [K in MapOptionKey]?: MapOptionShapes[K]; }; /** * `refsConnection`(v3 fanout)の opts キーの閉集合(**runtime-enumerable SoT**)。 * {@link FanoutOptions} の型はここから導出。新 opts キーはここに追加するのが正。 */ export declare const FANOUT_OPTION_KEYS: readonly ["dedupeKey", "drop", "implicitSource"]; /** `refsConnection` の 1 opts キー({@link FANOUT_OPTION_KEYS} から導出)。 */ export type FanoutOptionKey = (typeof FANOUT_OPTION_KEYS)[number]; interface FanoutOptionShapes { /** first-seen dedupe の基準となる結果要素の port(必須)。 */ dedupeKey: string; /** dangling(null body)の扱い(既定 "dangling")。 */ drop?: "dangling" | "none"; /** 結果要素から除去する暗黙ソースフィールド名。 */ implicitSource?: string; } /** `refsConnection` の opts(キー集合は {@link FANOUT_OPTION_KEYS} が SoT)。 */ export type FanoutOptions = Pick; /** * `@behavior` の opts キーの閉集合(**runtime-enumerable SoT**)。{@link BehaviorOptions} の型はここから * 導出し、新 opts キーを足すときはここに 1 語追加するのが正。 * * 被覆は `.map` / `refsConnection` の opts と違って IR のノードキーとしては現れない(`onError` の IR 上の * 効果は node の `policy` 注記)ので、corpus 被覆は**注記の値ゲート**(`policy` の Kind が authoring から * produce されること — ast-equivalence.test.ts)が測る。同じ事実に 2 本目のゲートを置かない。 */ export declare const BEHAVIOR_OPTION_KEYS: readonly ["onError"]; /** `@behavior` の 1 opts キー({@link BEHAVIOR_OPTION_KEYS} から導出)。 */ export type BehaviorOptionKey = (typeof BEHAVIOR_OPTION_KEYS)[number]; interface BehaviorOptionShapes { /** Component 名 → Error Policy Kind(scp-error.md §Error Policy — 単位は Component・宣言先は Behavior)。 */ onError: Readonly>; } /** `@behavior` の opts(キー集合は {@link BEHAVIOR_OPTION_KEYS} が SoT)。 */ export type BehaviorOptions = Pick; interface BranchFrame { /** この分岐のローカル条件(else 側は `{not:[c]}`)。 */ cond: unknown; /** 遅延生成される gate(CondNode)の id。分岐内に Component が現れたとき作られる。 */ gateId: string | null; /** 分岐開始時点の nodes.length(in-branch 判定・component 有無判定)。 */ startLen: number; } export interface PendingElement { /** どの構造化制御の要素 capture か(`.map` / `refsConnection`)。要素 capture の規則は共有だが、 * loud reject のコード(MAP_/FANOUT_ 系)は呼び出し元の構文を名指す必要があるので kind で分かれる。 */ kind: "map" | "fanout"; as: string; component: string | null; ports: Record | null; when: unknown; /** callback 内 `.as(t)` が確定させた要素の outType(MapNode/FanoutNode に載る)。 */ outType: unknown; /** callback 内 leaf の catalog `elemType`(base 要素型。`.as` 未指定時の確定型の出所)。 */ baseElemType: PortableType | undefined; /** callback 内 leaf の CatalogEntry(fold 時に `node.map/fanout.portSchemas` を導出する出所)。 */ entry: CatalogEntry | null; } /** 1 メソッド(= 1 root Behavior)の lowering 状態。reader が生成し、共有 lowering が更新する。 */ export interface GraphFrame { method: string; nodes: BodyNode[]; nodeIds: Set; inputPorts: Map; /** `opt($.x)` で **明示的に** 省略可と書かれた input Port 名(上書き schema との整合検査に使う)。 */ optionalInputs: Set; seq: number; mapSeq: number; branchStack: BranchFrame[]; pendingElement: PendingElement | null; /** * この Behavior の Error Policy Kind(Component 名 → Kind)。scp-error.md §Error Policy: policy は * **Behavior に対して Component 単位で**宣言され、DAG とは独立の引数で渡る(native-TS では * `@behavior({onError})`)。ここは宣言の写しで、ノードへ焼くのは {@link componentErrorPolicy}。 */ errorPolicy: ReadonlyMap; /** 実際に Component へ当たった onError キー(当たらなかったキーは終端で loud reject する)。 */ errorPolicyHits: Set; } export declare function newGraphFrame(method: string, errorPolicy?: ReadonlyMap): GraphFrame; /** * componentErrorPolicy — 宣言された Error Policy Kind をこの Component 参照へ解決する(1 箇所)。 * * policy の単位は **Component**(scp-error.md §Standard policies: 「a Component failed」)なので、同じ * Component を 2 度呼ぶ Behavior では両ノードが同じ Kind を得る。既定 `fail` は IR に書かない(注記の * 不在が既定 — 既定を明示的に焼くと同じ意味の IR が 2 通りになる)。 */ export declare function componentErrorPolicy(f: GraphFrame, component: string): PolicyKind | undefined; /** `@behavior({onError})` の宣言を検証して Component 名 → Kind の写しにする(Kind の閉集合は plan.ts)。 */ export declare function readErrorPolicyDeclaration(method: string, onError: unknown): ReadonlyMap; export declare function declareInputPort(f: GraphFrame, name: string): void; /** * markInputPortOptional — `opt($.x)` が宣言する「Port x は省略可」を schema に立てる * (`required:false`)。`$.x` アクセス時点で {@link declareInputPort} が required で登録済みなので、 * ここは既存 schema の更新。型(`type`)には触らない(型は `inputPorts` 上書きの担当)。 */ export declare function markInputPortOptional(f: GraphFrame, name: string): void; /** * derivePortSchemas — node の `portSchemas`(irVersion 2)を機械導出する **唯一の SSoT**。 * `node.outType`(出力型契約)の入力側の双子で、port ごとの型契約を焼く(native emitter の port * 境界がこれを honor し要素 kind 推論を廃す)。各 port の契約は leaf の宣言 `PortSchema` そのもの * (`entry.inputPorts[k]`)。未宣言 port は `checkPortNames` が上流で fail-closed に拒否するので、 * ここへは宣言済み port だけが届く(`{ type: "value" }` は宣言欠落時の不透明 default)。 */ export declare function derivePortSchemas(entry: CatalogEntry, ports: Record): Record; /** * lowerLeafCall — catalog leaf 1 呼び出しの lowering(両 reader 共通の唯一の実装)。 * * ports は catalog の Port schema に対して build-time 検証され(未知 Port / required 欠落 / * 宣言 Port へのスカラリテラル型不一致は fail-closed)、Expression IR へ lower される。 * map/fanout コールバック内の呼びは body ノードを作らず、単一 Map/Fanout ノードへ畳むために * pendingElement へ capture する(戻り値 `null`)。それ以外は componentRef ノードを 1 つ生成し、 * その id を返す。 */ export declare function lowerLeafCall(f: GraphFrame, name: string, entry: CatalogEntry, portsIn: Record): string | null; /** * lowerAs — `.as(t)` を lower する。projection / 合成で base 型(leaf signature 由来)から * **確定型が変わる点**を consumer が inline で確定させる surface。 * * TS の型は実行時に消去されるため BC core は**推論しない**: `.as` は runtime `PortableType` * (可搬型記法の閉集合)だけを読み、それを対象 body ノードの `outType` に書き込む。 * * `.as` は **node 結果(componentRef / map / fanout の結果)** にのみ適用できる。`$` 入力参照・ * 演算結果は body ノードを持たず注記先が無いため `BAD_AS_TARGET` で fail-closed。map/fanout * コールバック内の leaf 結果への `.as` は要素の確定型として pendingElement に記録し、畳まれた * MapNode/FanoutNode の `outType` に載せる。 */ export declare function lowerAs(f: GraphFrame, meta: ExprMeta, t: unknown): ExprMeta; export declare function lowerMap(f: GraphFrame, overMeta: ExprMeta, runCallback: (elementMeta: ExprMeta) => unknown, opts: MapOptions | undefined, intoSourceElem?: PortableType): string; /** * lowerFanout — `ids.refsConnection(el => Leaf({...}), { dedupeKey, drop, implicitSource })` を * 単一 FanoutNode へ lower する。`.map` と同じ single-component capture を使うが、意味は * dedup 済み 1 回の batched dispatch → dedupe(first-seen by dedupeKey)/drop(dangling)/strip → * connection `{items,cursor:null}`。 */ export declare function lowerFanout(f: GraphFrame, overMeta: ExprMeta, runCallback: (elementMeta: ExprMeta) => unknown, opts: FanoutOptions | undefined): string; /** * lowerConditional — Conditional(`?:`)/ Guard(`&&`)の lowering * (semantic-contract-programming.md「構造化制御構文」の Compiler 規則そのもの): * * - 両分岐(else 省略時は then のみ)が**純式のみ** → Expression IR `{cond:[c,t,e]}` へ * inline lower(body ノードは増えない。else 省略は `null`)。 * - 分岐に **Component 宣言を含む** → 分岐ごとに gate(CondNode + null-binding skip)を立て、 * 両分岐が値を持つ場合は Φ(CondNode)ノードで合流する(採用側のみ実行される)。 * Guard 形(else 無し)は then の値をそのまま返す — 条件不成立時は当該ノードが Skip され、 * 下流へは未生成 Port の Skip 伝播規則で連鎖する。 * - `.map` コールバック内では **per-element guard**(v2 `map.when`)として lower される * (guard 形のみ。false の要素は handler 未呼び出しで結果から除外)。 * * `runThen` / `runElse` は reader が供給する分岐本体の読み取り。戻り値は分岐の値(抽象値 or リテラル)。 * 戻り値は「この条件式全体の値」(抽象値 / リテラル / node 結果 meta を包んだ carrier)。 */ export declare function lowerConditional(f: GraphFrame, condition: unknown, runThen: () => unknown, runElse: (() => unknown) | null, wrap: (meta: ExprMeta) => unknown): unknown; /** * assembleComponent — 1 メソッド分の lowering を Component へ畳む(両 reader 共通の末尾)。 * * 上書き schema(`inputPorts`)は **型**の供給点(TS 型はランタイムに残らない reader もある)。 * optionality は `opt($.x)` が本体に書いた**宣言**なので、上書きは型を足すだけで、それを黙って * 取り消さない: * - 上書きが `required:true` を **明示** して `opt($.x)` と矛盾 → LOUD reject(2 枚の carrier が * 食い違ったまま通ると、宣言だけを読む下流 [runBehavior の束縛 / native lowering] が * optionality を見失う — それが silent な誤束縛の入口)。 * - 上書きが `required` を **述べていない**(型だけ)→ 型は上書き・`required:false` は保存。 * 逆向き(上書きが `required:false`・本体は素の `$.x`)は矛盾ではない。 * * 検証・plan 導出・正規化・Guard は builder.ts の {@link buildComponentCore} に委譲し、 * 出力型契約は {@link outputContract} が確定する(宣言戻り型と型確定ゲートの導出型の突き合わせ)。 * * `declaredOutputType` は `@behavior` メソッドの**宣言戻り型**(native-ts-authoring.md §5.1「戻り値型 → * 出力 port 型」)。reader が型解決して渡す(AST reader = `extractMethodReturnType`)。宣言が無い経路は * undefined で、その場合だけ導出型がそのまま契約になる。 */ export declare function assembleComponent(f: GraphFrame, returned: unknown, overrides: Record | undefined, concurrency: number, declaredOutputType: PortableType | undefined): Component; export { DEFAULT_PLAN_CONCURRENCY }; //# sourceMappingURL=lowering.d.ts.map