@kumikijs/compiler 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +31 -1
- package/dist/index.js +244 -11
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -286,6 +286,14 @@ type Expr = {
|
|
|
286
286
|
base: Expr;
|
|
287
287
|
field: string;
|
|
288
288
|
pos: Pos;
|
|
289
|
+
/**
|
|
290
|
+
* Dispatch decision filled in by the type checker (ADR-002): `"field"`
|
|
291
|
+
* means the receiver is a record with this field, so codegen lowers a
|
|
292
|
+
* field read instead of a method shortcut (kills the #23 shadow). Absent /
|
|
293
|
+
* `"shortcut"` keeps the name-based shortcut dispatch (back-compat — also
|
|
294
|
+
* the case when codegen runs without `check()`).
|
|
295
|
+
*/
|
|
296
|
+
accessKind?: "field" | "shortcut";
|
|
289
297
|
} | {
|
|
290
298
|
kind: "Index";
|
|
291
299
|
base: Expr;
|
|
@@ -463,6 +471,28 @@ type CodegenOptions = {
|
|
|
463
471
|
includeTests?: boolean;
|
|
464
472
|
};
|
|
465
473
|
declare function codegen(program: Program, opts: CodegenOptions): string;
|
|
474
|
+
/**
|
|
475
|
+
* Methods the code generator actually implements (the `methodCallJs` switch
|
|
476
|
+
* cases below). This is the single source of truth for what `obj.method(...)`
|
|
477
|
+
* calls are runnable; the typechecker uses it to flag unimplemented methods
|
|
478
|
+
* (E0801) at `check` time instead of letting them throw or misbehave at runtime.
|
|
479
|
+
* Keep this in exact sync with the `switch (method)` cases.
|
|
480
|
+
*/
|
|
481
|
+
declare const KNOWN_METHODS: ReadonlySet<string>;
|
|
482
|
+
/**
|
|
483
|
+
* The method names codegen lowers in the parenthesis-free `recv.m` (FieldAccess)
|
|
484
|
+
* form — kept in sync with the `if (e.field === …)` chain in jsOfExpr's
|
|
485
|
+
* FieldAccess case. A subset of KNOWN_METHODS (enforced by a test): every
|
|
486
|
+
* no-paren shortcut must also accept the `recv.m()` shape.
|
|
487
|
+
*/
|
|
488
|
+
declare const FIELD_ACCESS_SHORTCUTS: ReadonlySet<string>;
|
|
489
|
+
/**
|
|
490
|
+
* Every member name the runtime understands on a stdlib receiver — the union of
|
|
491
|
+
* the method-call methods and the no-paren shortcuts. Used by the type checker
|
|
492
|
+
* (ADR-002) to decide whether `recv.m` on a *known* receiver type is a real
|
|
493
|
+
* member (→ shortcut) or an unknown one (→ E0108). Flat, not per-type.
|
|
494
|
+
*/
|
|
495
|
+
declare const KNOWN_MEMBERS: ReadonlySet<string>;
|
|
466
496
|
declare const RUNTIME_HELPERS = "\nfunction _setPath(obj, path, value) {\n if (path.length === 0) return value;\n const [head, ...rest] = path;\n const cur = obj ?? {};\n return { ...cur, [head]: _setPath(cur[head], rest, value) };\n}\nfunction _children(...xs) {\n const out = [];\n for (const x of xs) {\n if (x === null || x === undefined) continue;\n if (Array.isArray(x)) {\n for (const y of x) if (y !== null && y !== undefined) out.push(y);\n } else {\n out.push(x);\n }\n }\n return out;\n}\nfunction _attachProps(node, props) {\n if (!node || !props) return node;\n return { ...node, props: { ...(node.props || {}), ...props } };\n}\n";
|
|
467
497
|
//#endregion
|
|
468
498
|
//#region src/typecheck.d.ts
|
|
@@ -519,4 +549,4 @@ declare class ParseError extends Error {
|
|
|
519
549
|
}
|
|
520
550
|
declare function parse(tokens: Token[]): Program;
|
|
521
551
|
//#endregion
|
|
522
|
-
export { type AppDef, type BinOp, type CapabilityManifest, type CodegenOptions, type CompileFail, type CompileOk, type CompileResult, type Def, type EffectDef, type EventPattern, type Expr, type ExtendedCodegenOptions, type FnDef, type KumikiError, type Lvalue, type ManifestResult, type MatchArm, type MotionDef, ParseError, type Pattern, type PolicyExpr, type Pos, type Program, RUNTIME_HELPERS, type ReducerDef, type Refinement, type RetryExpr, STANDARD_CAPABILITIES, type SlotDef, type Statement, type TestDef, type ThemeDef, type ThemeValue, type TileArg, type TileDef, type TileExpr, type TileMatchArm, type TileProp, type Token, type TypeDef, type TypeExpr, type UiEventKind, check, codegen, compile, inlineRuntime, lex, parse, parseCapabilityManifest };
|
|
552
|
+
export { type AppDef, type BinOp, type CapabilityManifest, type CodegenOptions, type CompileFail, type CompileOk, type CompileResult, type Def, type EffectDef, type EventPattern, type Expr, type ExtendedCodegenOptions, FIELD_ACCESS_SHORTCUTS, type FnDef, KNOWN_MEMBERS, KNOWN_METHODS, type KumikiError, type Lvalue, type ManifestResult, type MatchArm, type MotionDef, ParseError, type Pattern, type PolicyExpr, type Pos, type Program, RUNTIME_HELPERS, type ReducerDef, type Refinement, type RetryExpr, STANDARD_CAPABILITIES, type SlotDef, type Statement, type TestDef, type ThemeDef, type ThemeValue, type TileArg, type TileDef, type TileExpr, type TileMatchArm, type TileProp, type Token, type TypeDef, type TypeExpr, type UiEventKind, check, codegen, compile, inlineRuntime, lex, parse, parseCapabilityManifest };
|
package/dist/index.js
CHANGED
|
@@ -373,6 +373,7 @@ function jsOfExpr(e, ctx) {
|
|
|
373
373
|
case "UnaryOp": return `(${e.op === "!" ? "!" : "-"}${jsOfExpr(e.rhs, ctx)})`;
|
|
374
374
|
case "FieldAccess": {
|
|
375
375
|
const baseJs = jsOfExpr(e.base, ctx);
|
|
376
|
+
if (e.accessKind === "field") return `(${baseJs})[${JSON.stringify(e.field)}]`;
|
|
376
377
|
if (e.field === "get") return `_s.unwrap(${baseJs})`;
|
|
377
378
|
if (e.field === "is-some") return `(_s.variantIs(${baseJs}, "Some"))`;
|
|
378
379
|
if (e.field === "is-none") return `(_s.variantIs(${baseJs}, "None"))`;
|
|
@@ -432,6 +433,7 @@ function jsOfExpr(e, ctx) {
|
|
|
432
433
|
const args = e.args.map((a) => jsOfExpr(a, ctx));
|
|
433
434
|
return `_s.fmt ? _s.fmt(${args.join(", ")}) : ${args[0] ?? "\"\""}`;
|
|
434
435
|
}
|
|
436
|
+
if (cn === "panic") return `_s.panic(${e.args[0] ? jsOfExpr(e.args[0], ctx) : "\"\""})`;
|
|
435
437
|
const args = e.args.map((a) => jsOfExpr(a, ctx)).join(", ");
|
|
436
438
|
return `${jsName(cn)}(${args})`;
|
|
437
439
|
}
|
|
@@ -518,8 +520,63 @@ const KNOWN_METHODS = new Set([
|
|
|
518
520
|
"abs",
|
|
519
521
|
"neg",
|
|
520
522
|
"to-float",
|
|
523
|
+
"to-int",
|
|
524
|
+
"is-ok",
|
|
525
|
+
"is-err",
|
|
526
|
+
"values",
|
|
527
|
+
"entries",
|
|
528
|
+
"lower",
|
|
529
|
+
"upper",
|
|
530
|
+
"sort",
|
|
531
|
+
"ms"
|
|
532
|
+
]);
|
|
533
|
+
/**
|
|
534
|
+
* The method names codegen lowers in the parenthesis-free `recv.m` (FieldAccess)
|
|
535
|
+
* form — kept in sync with the `if (e.field === …)` chain in jsOfExpr's
|
|
536
|
+
* FieldAccess case. A subset of KNOWN_METHODS (enforced by a test): every
|
|
537
|
+
* no-paren shortcut must also accept the `recv.m()` shape.
|
|
538
|
+
*/
|
|
539
|
+
const FIELD_ACCESS_SHORTCUTS = new Set([
|
|
540
|
+
"get",
|
|
541
|
+
"is-some",
|
|
542
|
+
"is-none",
|
|
543
|
+
"is-ok",
|
|
544
|
+
"is-err",
|
|
545
|
+
"keys",
|
|
546
|
+
"values",
|
|
547
|
+
"entries",
|
|
548
|
+
"size",
|
|
549
|
+
"to-ms",
|
|
550
|
+
"ms",
|
|
551
|
+
"show",
|
|
552
|
+
"length",
|
|
553
|
+
"is-empty",
|
|
554
|
+
"lower",
|
|
555
|
+
"upper",
|
|
556
|
+
"trim",
|
|
557
|
+
"unique",
|
|
558
|
+
"reverse",
|
|
559
|
+
"sort",
|
|
560
|
+
"head",
|
|
561
|
+
"tail",
|
|
562
|
+
"last",
|
|
563
|
+
"to-list",
|
|
564
|
+
"get-err",
|
|
565
|
+
"to-option",
|
|
566
|
+
"parse-int",
|
|
567
|
+
"parse-float",
|
|
568
|
+
"abs",
|
|
569
|
+
"neg",
|
|
570
|
+
"to-float",
|
|
521
571
|
"to-int"
|
|
522
572
|
]);
|
|
573
|
+
/**
|
|
574
|
+
* Every member name the runtime understands on a stdlib receiver — the union of
|
|
575
|
+
* the method-call methods and the no-paren shortcuts. Used by the type checker
|
|
576
|
+
* (ADR-002) to decide whether `recv.m` on a *known* receiver type is a real
|
|
577
|
+
* member (→ shortcut) or an unknown one (→ E0108). Flat, not per-type.
|
|
578
|
+
*/
|
|
579
|
+
const KNOWN_MEMBERS = new Set([...KNOWN_METHODS, ...FIELD_ACCESS_SHORTCUTS]);
|
|
523
580
|
function methodCallJs(recv, method, args, ctx) {
|
|
524
581
|
const inner = makeEvalCtx(ctx.gen, ctx.localBinds);
|
|
525
582
|
inner.localBinds.add("$1");
|
|
@@ -2726,6 +2783,7 @@ var Parser = class {
|
|
|
2726
2783
|
}
|
|
2727
2784
|
const tok0 = this.peek();
|
|
2728
2785
|
if (tok0.kind === "ident") {
|
|
2786
|
+
if (parentTakesValueArg) return this.parseExpr();
|
|
2729
2787
|
const name = tok0.value;
|
|
2730
2788
|
const p1 = this.peek(1);
|
|
2731
2789
|
const looksLikeTileCall = p1.kind === "op" && (p1.value === "(" || p1.value === "{");
|
|
@@ -3310,9 +3368,13 @@ function checkSlot(slot, sym, errors) {
|
|
|
3310
3368
|
function checkTile(tile, sym, errors) {
|
|
3311
3369
|
const ctx = {
|
|
3312
3370
|
kind: "tile",
|
|
3313
|
-
localBinds: /* @__PURE__ */ new Set()
|
|
3371
|
+
localBinds: /* @__PURE__ */ new Set(),
|
|
3372
|
+
localTypes: /* @__PURE__ */ new Map()
|
|
3314
3373
|
};
|
|
3315
|
-
if (tile.in)
|
|
3374
|
+
if (tile.in) {
|
|
3375
|
+
ctx.localBinds.add("$1");
|
|
3376
|
+
ctx.localTypes?.set("$1", tile.in);
|
|
3377
|
+
}
|
|
3316
3378
|
checkTileExpr(tile.body, sym, errors, ctx);
|
|
3317
3379
|
}
|
|
3318
3380
|
function checkA11y(t, errors) {
|
|
@@ -3351,7 +3413,8 @@ function checkTileExpr(t, sym, errors, ctx) {
|
|
|
3351
3413
|
checkExpr(t.iter, sym, errors, ctx);
|
|
3352
3414
|
const inner = {
|
|
3353
3415
|
...ctx,
|
|
3354
|
-
localBinds: new Set(ctx.localBinds)
|
|
3416
|
+
localBinds: new Set(ctx.localBinds),
|
|
3417
|
+
localTypes: new Map(ctx.localTypes ?? [])
|
|
3355
3418
|
};
|
|
3356
3419
|
inner.localBinds.add(t.bind);
|
|
3357
3420
|
checkTileExpr(t.body, sym, errors, inner);
|
|
@@ -3371,7 +3434,8 @@ function checkTileExpr(t, sym, errors, ctx) {
|
|
|
3371
3434
|
for (const arm of t.arms) {
|
|
3372
3435
|
const inner = {
|
|
3373
3436
|
...ctx,
|
|
3374
|
-
localBinds: new Set(ctx.localBinds)
|
|
3437
|
+
localBinds: new Set(ctx.localBinds),
|
|
3438
|
+
localTypes: new Map(ctx.localTypes ?? [])
|
|
3375
3439
|
};
|
|
3376
3440
|
if (arm.pattern.kind === "PVariant") for (const b of arm.pattern.binds) inner.localBinds.add(b);
|
|
3377
3441
|
if (arm.pattern.kind === "PBind") inner.localBinds.add(arm.pattern.name);
|
|
@@ -3469,7 +3533,8 @@ function checkStmt(s, sym, errors, ctx, writtenRoots) {
|
|
|
3469
3533
|
checkExpr(s.iter, sym, errors, ctx);
|
|
3470
3534
|
const inner = {
|
|
3471
3535
|
...ctx,
|
|
3472
|
-
localBinds: new Set(ctx.localBinds)
|
|
3536
|
+
localBinds: new Set(ctx.localBinds),
|
|
3537
|
+
localTypes: new Map(ctx.localTypes ?? [])
|
|
3473
3538
|
};
|
|
3474
3539
|
inner.localBinds.add(s.bind);
|
|
3475
3540
|
const bodyWrites = new Set(writtenRoots);
|
|
@@ -3493,7 +3558,8 @@ function checkStmt(s, sym, errors, ctx, writtenRoots) {
|
|
|
3493
3558
|
for (const arm of s.arms) {
|
|
3494
3559
|
const inner = {
|
|
3495
3560
|
...ctx,
|
|
3496
|
-
localBinds: new Set(ctx.localBinds)
|
|
3561
|
+
localBinds: new Set(ctx.localBinds),
|
|
3562
|
+
localTypes: new Map(ctx.localTypes ?? [])
|
|
3497
3563
|
};
|
|
3498
3564
|
if (arm.pattern.kind === "PVariant") {
|
|
3499
3565
|
for (const b of arm.pattern.binds) if (b !== "_") inner.localBinds.add(b);
|
|
@@ -3510,6 +3576,11 @@ function checkStmt(s, sym, errors, ctx, writtenRoots) {
|
|
|
3510
3576
|
if (s.kind === "LetStmt") {
|
|
3511
3577
|
checkExpr(s.rhs, sym, errors, ctx);
|
|
3512
3578
|
ctx.localBinds.add(s.name);
|
|
3579
|
+
const rt = inferType(s.rhs, sym, ctx);
|
|
3580
|
+
if (rt) {
|
|
3581
|
+
if (!ctx.localTypes) ctx.localTypes = /* @__PURE__ */ new Map();
|
|
3582
|
+
ctx.localTypes.set(s.name, rt);
|
|
3583
|
+
}
|
|
3513
3584
|
return;
|
|
3514
3585
|
}
|
|
3515
3586
|
if (s.kind === "Emit") {
|
|
@@ -3615,6 +3686,7 @@ function checkExpr(e, sym, errors, ctx) {
|
|
|
3615
3686
|
return;
|
|
3616
3687
|
case "FieldAccess":
|
|
3617
3688
|
checkExpr(e.base, sym, errors, ctx);
|
|
3689
|
+
classifyFieldAccess(e, sym, errors, ctx);
|
|
3618
3690
|
return;
|
|
3619
3691
|
case "Index":
|
|
3620
3692
|
checkExpr(e.base, sym, errors, ctx);
|
|
@@ -3634,7 +3706,8 @@ function checkExpr(e, sym, errors, ctx) {
|
|
|
3634
3706
|
for (const a of e.args) {
|
|
3635
3707
|
const inner = {
|
|
3636
3708
|
...ctx,
|
|
3637
|
-
localBinds: new Set(ctx.localBinds)
|
|
3709
|
+
localBinds: new Set(ctx.localBinds),
|
|
3710
|
+
localTypes: new Map(ctx.localTypes ?? [])
|
|
3638
3711
|
};
|
|
3639
3712
|
inner.localBinds.add("$1");
|
|
3640
3713
|
inner.localBinds.add("$2");
|
|
@@ -3658,7 +3731,8 @@ function checkExpr(e, sym, errors, ctx) {
|
|
|
3658
3731
|
for (const arm of e.arms) {
|
|
3659
3732
|
const inner = {
|
|
3660
3733
|
...ctx,
|
|
3661
|
-
localBinds: new Set(ctx.localBinds)
|
|
3734
|
+
localBinds: new Set(ctx.localBinds),
|
|
3735
|
+
localTypes: new Map(ctx.localTypes ?? [])
|
|
3662
3736
|
};
|
|
3663
3737
|
if (arm.pattern.kind === "PVariant") {
|
|
3664
3738
|
for (const b of arm.pattern.binds) if (b !== "_") inner.localBinds.add(b);
|
|
@@ -3676,21 +3750,180 @@ function checkExpr(e, sym, errors, ctx) {
|
|
|
3676
3750
|
checkExpr(e.value, sym, errors, ctx);
|
|
3677
3751
|
const inner = {
|
|
3678
3752
|
...ctx,
|
|
3679
|
-
localBinds: new Set(ctx.localBinds)
|
|
3753
|
+
localBinds: new Set(ctx.localBinds),
|
|
3754
|
+
localTypes: new Map(ctx.localTypes ?? [])
|
|
3680
3755
|
};
|
|
3681
3756
|
inner.localBinds.add(e.name);
|
|
3757
|
+
const vt = inferType(e.value, sym, inner);
|
|
3758
|
+
if (vt) inner.localTypes?.set(e.name, vt);
|
|
3682
3759
|
checkExpr(e.body, sym, errors, inner);
|
|
3683
3760
|
return;
|
|
3684
3761
|
}
|
|
3685
3762
|
}
|
|
3686
3763
|
}
|
|
3764
|
+
const SCALAR_PRIMS = new Set([
|
|
3765
|
+
"Int",
|
|
3766
|
+
"Float",
|
|
3767
|
+
"Text",
|
|
3768
|
+
"Bool",
|
|
3769
|
+
"Time",
|
|
3770
|
+
"Bytes"
|
|
3771
|
+
]);
|
|
3772
|
+
const STDLIB_CONTAINERS = new Set([
|
|
3773
|
+
"List",
|
|
3774
|
+
"Map",
|
|
3775
|
+
"Set",
|
|
3776
|
+
"Option",
|
|
3777
|
+
"Result"
|
|
3778
|
+
]);
|
|
3779
|
+
/** Unwrap type aliases (`TypeRef` → its `TypeDef` body) and nominal/refinement wrappers. */
|
|
3780
|
+
function unaliasType(t, sym, seen = /* @__PURE__ */ new Set()) {
|
|
3781
|
+
if (!t) return null;
|
|
3782
|
+
if (t.kind === "TypeRef") {
|
|
3783
|
+
if (seen.has(t.name)) return null;
|
|
3784
|
+
const def = sym.types.get(t.name);
|
|
3785
|
+
if (!def) return t;
|
|
3786
|
+
seen.add(t.name);
|
|
3787
|
+
return unaliasType(def.body, sym, seen);
|
|
3788
|
+
}
|
|
3789
|
+
if (t.kind === "TypeNominal" || t.kind === "TypeRefinement") return unaliasType(t.inner, sym, seen);
|
|
3790
|
+
return t;
|
|
3791
|
+
}
|
|
3792
|
+
function recordFieldType(rec, name) {
|
|
3793
|
+
return rec.fields.find((f) => f.name === name)?.type ?? null;
|
|
3794
|
+
}
|
|
3795
|
+
const unitType = (pos) => ({
|
|
3796
|
+
kind: "TypePrim",
|
|
3797
|
+
name: "Unit",
|
|
3798
|
+
pos
|
|
3799
|
+
});
|
|
3800
|
+
/** Best-effort static type of an expression; `null` = undecidable / dynamic. */
|
|
3801
|
+
function inferType(e, sym, ctx) {
|
|
3802
|
+
switch (e.kind) {
|
|
3803
|
+
case "Num": return {
|
|
3804
|
+
kind: "TypePrim",
|
|
3805
|
+
name: "Int",
|
|
3806
|
+
pos: e.pos
|
|
3807
|
+
};
|
|
3808
|
+
case "Str": return {
|
|
3809
|
+
kind: "TypePrim",
|
|
3810
|
+
name: "Text",
|
|
3811
|
+
pos: e.pos
|
|
3812
|
+
};
|
|
3813
|
+
case "Bool": return {
|
|
3814
|
+
kind: "TypePrim",
|
|
3815
|
+
name: "Bool",
|
|
3816
|
+
pos: e.pos
|
|
3817
|
+
};
|
|
3818
|
+
case "Ref": {
|
|
3819
|
+
const bound = ctx.localTypes?.get(e.name);
|
|
3820
|
+
if (bound) return bound;
|
|
3821
|
+
return sym.slots.get(e.name)?.type ?? null;
|
|
3822
|
+
}
|
|
3823
|
+
case "FieldAccess": {
|
|
3824
|
+
const base = unaliasType(inferType(e.base, sym, ctx), sym);
|
|
3825
|
+
if (!base) return null;
|
|
3826
|
+
if (base.kind === "TypeRecord") return recordFieldType(base, e.field);
|
|
3827
|
+
if (e.field === "get" && base.kind === "TypeApp" && (base.name === "Option" || base.name === "Result")) return base.args[0] ?? null;
|
|
3828
|
+
return null;
|
|
3829
|
+
}
|
|
3830
|
+
case "Index": {
|
|
3831
|
+
const base = unaliasType(inferType(e.base, sym, ctx), sym);
|
|
3832
|
+
if (base?.kind === "TypeApp") {
|
|
3833
|
+
if (base.name === "List" || base.name === "Set") return base.args[0] ?? null;
|
|
3834
|
+
if (base.name === "Map") return base.args[1] ?? null;
|
|
3835
|
+
}
|
|
3836
|
+
return null;
|
|
3837
|
+
}
|
|
3838
|
+
case "MethodCall":
|
|
3839
|
+
if (e.method === "get") {
|
|
3840
|
+
const recv = unaliasType(inferType(e.receiver, sym, ctx), sym);
|
|
3841
|
+
if (recv?.kind === "TypeApp") {
|
|
3842
|
+
if (recv.name === "Map") return recv.args[1] ? {
|
|
3843
|
+
kind: "TypeApp",
|
|
3844
|
+
name: "Option",
|
|
3845
|
+
args: [recv.args[1]],
|
|
3846
|
+
pos: e.pos
|
|
3847
|
+
} : null;
|
|
3848
|
+
if (recv.name === "Option" || recv.name === "Result") return recv.args[0] ?? null;
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
return null;
|
|
3852
|
+
case "RecordLit": return {
|
|
3853
|
+
kind: "TypeRecord",
|
|
3854
|
+
fields: e.fields.map((f) => ({
|
|
3855
|
+
name: f.name,
|
|
3856
|
+
type: inferType(f.value, sym, ctx) ?? unitType(f.value.pos)
|
|
3857
|
+
})),
|
|
3858
|
+
pos: e.pos
|
|
3859
|
+
};
|
|
3860
|
+
case "Variant": {
|
|
3861
|
+
const inner = e.payload[0] ? inferType(e.payload[0], sym, ctx) ?? unitType(e.pos) : unitType(e.pos);
|
|
3862
|
+
if (e.name === "Some" || e.name === "None") return {
|
|
3863
|
+
kind: "TypeApp",
|
|
3864
|
+
name: "Option",
|
|
3865
|
+
args: [inner],
|
|
3866
|
+
pos: e.pos
|
|
3867
|
+
};
|
|
3868
|
+
if (e.name === "Ok") return {
|
|
3869
|
+
kind: "TypeApp",
|
|
3870
|
+
name: "Result",
|
|
3871
|
+
args: [inner],
|
|
3872
|
+
pos: e.pos
|
|
3873
|
+
};
|
|
3874
|
+
return null;
|
|
3875
|
+
}
|
|
3876
|
+
default: return null;
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
/**
|
|
3880
|
+
* Decide whether `recv.field` is a record field read or a method shortcut, and
|
|
3881
|
+
* annotate the node so codegen lowers the right thing (ADR-002). Emits E0108
|
|
3882
|
+
* when the receiver type is KNOWN and `field` is neither a member nor a record
|
|
3883
|
+
* field; stays silent (shortcut) when the type is undecidable.
|
|
3884
|
+
*/
|
|
3885
|
+
function classifyFieldAccess(e, sym, errors, ctx) {
|
|
3886
|
+
const t = unaliasType(inferType(e.base, sym, ctx), sym);
|
|
3887
|
+
if (!t) return;
|
|
3888
|
+
if (t.kind === "TypeRecord") {
|
|
3889
|
+
if (recordFieldType(t, e.field)) {
|
|
3890
|
+
e.accessKind = "field";
|
|
3891
|
+
return;
|
|
3892
|
+
}
|
|
3893
|
+
if (e.field === "show") {
|
|
3894
|
+
e.accessKind = "shortcut";
|
|
3895
|
+
return;
|
|
3896
|
+
}
|
|
3897
|
+
errors.push({
|
|
3898
|
+
code: "E0108",
|
|
3899
|
+
kind: "undef-member",
|
|
3900
|
+
message: `Record type has no field or method ".${e.field}"`,
|
|
3901
|
+
pos: e.pos
|
|
3902
|
+
});
|
|
3903
|
+
return;
|
|
3904
|
+
}
|
|
3905
|
+
if (t.kind === "TypePrim" && SCALAR_PRIMS.has(t.name) || t.kind === "TypeApp" && STDLIB_CONTAINERS.has(t.name)) {
|
|
3906
|
+
if (KNOWN_MEMBERS.has(e.field)) {
|
|
3907
|
+
e.accessKind = "shortcut";
|
|
3908
|
+
return;
|
|
3909
|
+
}
|
|
3910
|
+
const tn = t.kind === "TypeApp" ? t.name : t.name;
|
|
3911
|
+
errors.push({
|
|
3912
|
+
code: "E0108",
|
|
3913
|
+
kind: "undef-member",
|
|
3914
|
+
message: `Type "${tn}" has no member ".${e.field}"`,
|
|
3915
|
+
pos: e.pos
|
|
3916
|
+
});
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3687
3919
|
function currentFnName(ctx) {
|
|
3688
3920
|
return ctx.fnName ?? "<fn>";
|
|
3689
3921
|
}
|
|
3690
3922
|
function checkFn(fn, sym, errors) {
|
|
3691
3923
|
const ctx = {
|
|
3692
3924
|
kind: "fn",
|
|
3693
|
-
localBinds: /* @__PURE__ */ new Set()
|
|
3925
|
+
localBinds: /* @__PURE__ */ new Set(),
|
|
3926
|
+
localTypes: new Map(fn.params.map((p) => [p.name, p.type]))
|
|
3694
3927
|
};
|
|
3695
3928
|
ctx.fnName = fn.name;
|
|
3696
3929
|
for (const p of fn.params) ctx.localBinds.add(p.name);
|
|
@@ -3805,4 +4038,4 @@ function compile(source, opts) {
|
|
|
3805
4038
|
};
|
|
3806
4039
|
}
|
|
3807
4040
|
//#endregion
|
|
3808
|
-
export { ParseError, RUNTIME_HELPERS, STANDARD_CAPABILITIES, check, codegen, compile, inlineRuntime, lex, parse, parseCapabilityManifest };
|
|
4041
|
+
export { FIELD_ACCESS_SHORTCUTS, KNOWN_MEMBERS, KNOWN_METHODS, ParseError, RUNTIME_HELPERS, STANDARD_CAPABILITIES, check, codegen, compile, inlineRuntime, lex, parse, parseCapabilityManifest };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kumikijs/compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Kumiki compiler — lexer, parser, typechecker, and code generator.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"provenance": true
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@kumikijs/runtime": "0.
|
|
44
|
+
"@kumikijs/runtime": "0.3.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/node": "^25.9.1",
|