@kumikijs/compiler 0.2.0 → 0.3.0
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 +280 -12
- 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"))`;
|
|
@@ -392,6 +393,18 @@ function jsOfExpr(e, ctx) {
|
|
|
392
393
|
if (e.field === "unique") return `[...new Set((${baseJs}) ?? [])]`;
|
|
393
394
|
if (e.field === "reverse") return `[...((${baseJs}) ?? [])].reverse()`;
|
|
394
395
|
if (e.field === "sort") return `[...((${baseJs}) ?? [])].sort()`;
|
|
396
|
+
if (e.field === "head") return `_s.listHead(${baseJs})`;
|
|
397
|
+
if (e.field === "tail") return `_s.listTail(${baseJs})`;
|
|
398
|
+
if (e.field === "last") return `_s.listLast(${baseJs})`;
|
|
399
|
+
if (e.field === "to-list") return `_s.toList(${baseJs})`;
|
|
400
|
+
if (e.field === "get-err") return `_s.getErr(${baseJs})`;
|
|
401
|
+
if (e.field === "to-option") return `_s.toOption(${baseJs})`;
|
|
402
|
+
if (e.field === "parse-int") return `_s.parseIntOpt(${baseJs})`;
|
|
403
|
+
if (e.field === "parse-float") return `_s.parseFloatOpt(${baseJs})`;
|
|
404
|
+
if (e.field === "abs") return `Math.abs(${baseJs})`;
|
|
405
|
+
if (e.field === "neg") return `(-(${baseJs}))`;
|
|
406
|
+
if (e.field === "to-float") return `(${baseJs})`;
|
|
407
|
+
if (e.field === "to-int") return `Math.trunc(${baseJs})`;
|
|
395
408
|
return `(${baseJs})[${JSON.stringify(e.field)}]`;
|
|
396
409
|
}
|
|
397
410
|
case "Index": return `(${jsOfExpr(e.base, ctx)})[${jsOfExpr(e.index, ctx)}]`;
|
|
@@ -420,6 +433,7 @@ function jsOfExpr(e, ctx) {
|
|
|
420
433
|
const args = e.args.map((a) => jsOfExpr(a, ctx));
|
|
421
434
|
return `_s.fmt ? _s.fmt(${args.join(", ")}) : ${args[0] ?? "\"\""}`;
|
|
422
435
|
}
|
|
436
|
+
if (cn === "panic") return `_s.panic(${e.args[0] ? jsOfExpr(e.args[0], ctx) : "\"\""})`;
|
|
423
437
|
const args = e.args.map((a) => jsOfExpr(a, ctx)).join(", ");
|
|
424
438
|
return `${jsName(cn)}(${args})`;
|
|
425
439
|
}
|
|
@@ -494,8 +508,75 @@ const KNOWN_METHODS = new Set([
|
|
|
494
508
|
"replace",
|
|
495
509
|
"min",
|
|
496
510
|
"max",
|
|
497
|
-
"clamp"
|
|
511
|
+
"clamp",
|
|
512
|
+
"head",
|
|
513
|
+
"tail",
|
|
514
|
+
"last",
|
|
515
|
+
"to-list",
|
|
516
|
+
"get-err",
|
|
517
|
+
"to-option",
|
|
518
|
+
"parse-int",
|
|
519
|
+
"parse-float",
|
|
520
|
+
"abs",
|
|
521
|
+
"neg",
|
|
522
|
+
"to-float",
|
|
523
|
+
"to-int",
|
|
524
|
+
"is-ok",
|
|
525
|
+
"is-err",
|
|
526
|
+
"values",
|
|
527
|
+
"entries",
|
|
528
|
+
"lower",
|
|
529
|
+
"upper",
|
|
530
|
+
"sort",
|
|
531
|
+
"ms"
|
|
498
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",
|
|
571
|
+
"to-int"
|
|
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]);
|
|
499
580
|
function methodCallJs(recv, method, args, ctx) {
|
|
500
581
|
const inner = makeEvalCtx(ctx.gen, ctx.localBinds);
|
|
501
582
|
inner.localBinds.add("$1");
|
|
@@ -558,6 +639,18 @@ function methodCallJs(recv, method, args, ctx) {
|
|
|
558
639
|
case "min": return `Math.min((${recvJs}), (${argRaw(args[0])}))`;
|
|
559
640
|
case "max": return `Math.max((${recvJs}), (${argRaw(args[0])}))`;
|
|
560
641
|
case "clamp": return `Math.min(Math.max((${recvJs}), (${argRaw(args[0])})), (${argRaw(args[1])}))`;
|
|
642
|
+
case "head": return `_s.listHead(${recvJs})`;
|
|
643
|
+
case "tail": return `_s.listTail(${recvJs})`;
|
|
644
|
+
case "last": return `_s.listLast(${recvJs})`;
|
|
645
|
+
case "to-list": return `_s.toList(${recvJs})`;
|
|
646
|
+
case "get-err": return `_s.getErr(${recvJs})`;
|
|
647
|
+
case "to-option": return `_s.toOption(${recvJs})`;
|
|
648
|
+
case "parse-int": return `_s.parseIntOpt(${recvJs})`;
|
|
649
|
+
case "parse-float": return `_s.parseFloatOpt(${recvJs})`;
|
|
650
|
+
case "abs": return `Math.abs(${recvJs})`;
|
|
651
|
+
case "neg": return `(-(${recvJs}))`;
|
|
652
|
+
case "to-float": return `(${recvJs})`;
|
|
653
|
+
case "to-int": return `Math.trunc(${recvJs})`;
|
|
561
654
|
default: return `(${recvJs}).${jsName(method)}(${args.map(argRaw).join(", ")})`;
|
|
562
655
|
}
|
|
563
656
|
}
|
|
@@ -3274,9 +3367,13 @@ function checkSlot(slot, sym, errors) {
|
|
|
3274
3367
|
function checkTile(tile, sym, errors) {
|
|
3275
3368
|
const ctx = {
|
|
3276
3369
|
kind: "tile",
|
|
3277
|
-
localBinds: /* @__PURE__ */ new Set()
|
|
3370
|
+
localBinds: /* @__PURE__ */ new Set(),
|
|
3371
|
+
localTypes: /* @__PURE__ */ new Map()
|
|
3278
3372
|
};
|
|
3279
|
-
if (tile.in)
|
|
3373
|
+
if (tile.in) {
|
|
3374
|
+
ctx.localBinds.add("$1");
|
|
3375
|
+
ctx.localTypes?.set("$1", tile.in);
|
|
3376
|
+
}
|
|
3280
3377
|
checkTileExpr(tile.body, sym, errors, ctx);
|
|
3281
3378
|
}
|
|
3282
3379
|
function checkA11y(t, errors) {
|
|
@@ -3315,7 +3412,8 @@ function checkTileExpr(t, sym, errors, ctx) {
|
|
|
3315
3412
|
checkExpr(t.iter, sym, errors, ctx);
|
|
3316
3413
|
const inner = {
|
|
3317
3414
|
...ctx,
|
|
3318
|
-
localBinds: new Set(ctx.localBinds)
|
|
3415
|
+
localBinds: new Set(ctx.localBinds),
|
|
3416
|
+
localTypes: new Map(ctx.localTypes ?? [])
|
|
3319
3417
|
};
|
|
3320
3418
|
inner.localBinds.add(t.bind);
|
|
3321
3419
|
checkTileExpr(t.body, sym, errors, inner);
|
|
@@ -3335,7 +3433,8 @@ function checkTileExpr(t, sym, errors, ctx) {
|
|
|
3335
3433
|
for (const arm of t.arms) {
|
|
3336
3434
|
const inner = {
|
|
3337
3435
|
...ctx,
|
|
3338
|
-
localBinds: new Set(ctx.localBinds)
|
|
3436
|
+
localBinds: new Set(ctx.localBinds),
|
|
3437
|
+
localTypes: new Map(ctx.localTypes ?? [])
|
|
3339
3438
|
};
|
|
3340
3439
|
if (arm.pattern.kind === "PVariant") for (const b of arm.pattern.binds) inner.localBinds.add(b);
|
|
3341
3440
|
if (arm.pattern.kind === "PBind") inner.localBinds.add(arm.pattern.name);
|
|
@@ -3433,7 +3532,8 @@ function checkStmt(s, sym, errors, ctx, writtenRoots) {
|
|
|
3433
3532
|
checkExpr(s.iter, sym, errors, ctx);
|
|
3434
3533
|
const inner = {
|
|
3435
3534
|
...ctx,
|
|
3436
|
-
localBinds: new Set(ctx.localBinds)
|
|
3535
|
+
localBinds: new Set(ctx.localBinds),
|
|
3536
|
+
localTypes: new Map(ctx.localTypes ?? [])
|
|
3437
3537
|
};
|
|
3438
3538
|
inner.localBinds.add(s.bind);
|
|
3439
3539
|
const bodyWrites = new Set(writtenRoots);
|
|
@@ -3457,7 +3557,8 @@ function checkStmt(s, sym, errors, ctx, writtenRoots) {
|
|
|
3457
3557
|
for (const arm of s.arms) {
|
|
3458
3558
|
const inner = {
|
|
3459
3559
|
...ctx,
|
|
3460
|
-
localBinds: new Set(ctx.localBinds)
|
|
3560
|
+
localBinds: new Set(ctx.localBinds),
|
|
3561
|
+
localTypes: new Map(ctx.localTypes ?? [])
|
|
3461
3562
|
};
|
|
3462
3563
|
if (arm.pattern.kind === "PVariant") {
|
|
3463
3564
|
for (const b of arm.pattern.binds) if (b !== "_") inner.localBinds.add(b);
|
|
@@ -3474,6 +3575,11 @@ function checkStmt(s, sym, errors, ctx, writtenRoots) {
|
|
|
3474
3575
|
if (s.kind === "LetStmt") {
|
|
3475
3576
|
checkExpr(s.rhs, sym, errors, ctx);
|
|
3476
3577
|
ctx.localBinds.add(s.name);
|
|
3578
|
+
const rt = inferType(s.rhs, sym, ctx);
|
|
3579
|
+
if (rt) {
|
|
3580
|
+
if (!ctx.localTypes) ctx.localTypes = /* @__PURE__ */ new Map();
|
|
3581
|
+
ctx.localTypes.set(s.name, rt);
|
|
3582
|
+
}
|
|
3477
3583
|
return;
|
|
3478
3584
|
}
|
|
3479
3585
|
if (s.kind === "Emit") {
|
|
@@ -3579,6 +3685,7 @@ function checkExpr(e, sym, errors, ctx) {
|
|
|
3579
3685
|
return;
|
|
3580
3686
|
case "FieldAccess":
|
|
3581
3687
|
checkExpr(e.base, sym, errors, ctx);
|
|
3688
|
+
classifyFieldAccess(e, sym, errors, ctx);
|
|
3582
3689
|
return;
|
|
3583
3690
|
case "Index":
|
|
3584
3691
|
checkExpr(e.base, sym, errors, ctx);
|
|
@@ -3598,7 +3705,8 @@ function checkExpr(e, sym, errors, ctx) {
|
|
|
3598
3705
|
for (const a of e.args) {
|
|
3599
3706
|
const inner = {
|
|
3600
3707
|
...ctx,
|
|
3601
|
-
localBinds: new Set(ctx.localBinds)
|
|
3708
|
+
localBinds: new Set(ctx.localBinds),
|
|
3709
|
+
localTypes: new Map(ctx.localTypes ?? [])
|
|
3602
3710
|
};
|
|
3603
3711
|
inner.localBinds.add("$1");
|
|
3604
3712
|
inner.localBinds.add("$2");
|
|
@@ -3622,7 +3730,8 @@ function checkExpr(e, sym, errors, ctx) {
|
|
|
3622
3730
|
for (const arm of e.arms) {
|
|
3623
3731
|
const inner = {
|
|
3624
3732
|
...ctx,
|
|
3625
|
-
localBinds: new Set(ctx.localBinds)
|
|
3733
|
+
localBinds: new Set(ctx.localBinds),
|
|
3734
|
+
localTypes: new Map(ctx.localTypes ?? [])
|
|
3626
3735
|
};
|
|
3627
3736
|
if (arm.pattern.kind === "PVariant") {
|
|
3628
3737
|
for (const b of arm.pattern.binds) if (b !== "_") inner.localBinds.add(b);
|
|
@@ -3640,21 +3749,180 @@ function checkExpr(e, sym, errors, ctx) {
|
|
|
3640
3749
|
checkExpr(e.value, sym, errors, ctx);
|
|
3641
3750
|
const inner = {
|
|
3642
3751
|
...ctx,
|
|
3643
|
-
localBinds: new Set(ctx.localBinds)
|
|
3752
|
+
localBinds: new Set(ctx.localBinds),
|
|
3753
|
+
localTypes: new Map(ctx.localTypes ?? [])
|
|
3644
3754
|
};
|
|
3645
3755
|
inner.localBinds.add(e.name);
|
|
3756
|
+
const vt = inferType(e.value, sym, inner);
|
|
3757
|
+
if (vt) inner.localTypes?.set(e.name, vt);
|
|
3646
3758
|
checkExpr(e.body, sym, errors, inner);
|
|
3647
3759
|
return;
|
|
3648
3760
|
}
|
|
3649
3761
|
}
|
|
3650
3762
|
}
|
|
3763
|
+
const SCALAR_PRIMS = new Set([
|
|
3764
|
+
"Int",
|
|
3765
|
+
"Float",
|
|
3766
|
+
"Text",
|
|
3767
|
+
"Bool",
|
|
3768
|
+
"Time",
|
|
3769
|
+
"Bytes"
|
|
3770
|
+
]);
|
|
3771
|
+
const STDLIB_CONTAINERS = new Set([
|
|
3772
|
+
"List",
|
|
3773
|
+
"Map",
|
|
3774
|
+
"Set",
|
|
3775
|
+
"Option",
|
|
3776
|
+
"Result"
|
|
3777
|
+
]);
|
|
3778
|
+
/** Unwrap type aliases (`TypeRef` → its `TypeDef` body) and nominal/refinement wrappers. */
|
|
3779
|
+
function unaliasType(t, sym, seen = /* @__PURE__ */ new Set()) {
|
|
3780
|
+
if (!t) return null;
|
|
3781
|
+
if (t.kind === "TypeRef") {
|
|
3782
|
+
if (seen.has(t.name)) return null;
|
|
3783
|
+
const def = sym.types.get(t.name);
|
|
3784
|
+
if (!def) return t;
|
|
3785
|
+
seen.add(t.name);
|
|
3786
|
+
return unaliasType(def.body, sym, seen);
|
|
3787
|
+
}
|
|
3788
|
+
if (t.kind === "TypeNominal" || t.kind === "TypeRefinement") return unaliasType(t.inner, sym, seen);
|
|
3789
|
+
return t;
|
|
3790
|
+
}
|
|
3791
|
+
function recordFieldType(rec, name) {
|
|
3792
|
+
return rec.fields.find((f) => f.name === name)?.type ?? null;
|
|
3793
|
+
}
|
|
3794
|
+
const unitType = (pos) => ({
|
|
3795
|
+
kind: "TypePrim",
|
|
3796
|
+
name: "Unit",
|
|
3797
|
+
pos
|
|
3798
|
+
});
|
|
3799
|
+
/** Best-effort static type of an expression; `null` = undecidable / dynamic. */
|
|
3800
|
+
function inferType(e, sym, ctx) {
|
|
3801
|
+
switch (e.kind) {
|
|
3802
|
+
case "Num": return {
|
|
3803
|
+
kind: "TypePrim",
|
|
3804
|
+
name: "Int",
|
|
3805
|
+
pos: e.pos
|
|
3806
|
+
};
|
|
3807
|
+
case "Str": return {
|
|
3808
|
+
kind: "TypePrim",
|
|
3809
|
+
name: "Text",
|
|
3810
|
+
pos: e.pos
|
|
3811
|
+
};
|
|
3812
|
+
case "Bool": return {
|
|
3813
|
+
kind: "TypePrim",
|
|
3814
|
+
name: "Bool",
|
|
3815
|
+
pos: e.pos
|
|
3816
|
+
};
|
|
3817
|
+
case "Ref": {
|
|
3818
|
+
const bound = ctx.localTypes?.get(e.name);
|
|
3819
|
+
if (bound) return bound;
|
|
3820
|
+
return sym.slots.get(e.name)?.type ?? null;
|
|
3821
|
+
}
|
|
3822
|
+
case "FieldAccess": {
|
|
3823
|
+
const base = unaliasType(inferType(e.base, sym, ctx), sym);
|
|
3824
|
+
if (!base) return null;
|
|
3825
|
+
if (base.kind === "TypeRecord") return recordFieldType(base, e.field);
|
|
3826
|
+
if (e.field === "get" && base.kind === "TypeApp" && (base.name === "Option" || base.name === "Result")) return base.args[0] ?? null;
|
|
3827
|
+
return null;
|
|
3828
|
+
}
|
|
3829
|
+
case "Index": {
|
|
3830
|
+
const base = unaliasType(inferType(e.base, sym, ctx), sym);
|
|
3831
|
+
if (base?.kind === "TypeApp") {
|
|
3832
|
+
if (base.name === "List" || base.name === "Set") return base.args[0] ?? null;
|
|
3833
|
+
if (base.name === "Map") return base.args[1] ?? null;
|
|
3834
|
+
}
|
|
3835
|
+
return null;
|
|
3836
|
+
}
|
|
3837
|
+
case "MethodCall":
|
|
3838
|
+
if (e.method === "get") {
|
|
3839
|
+
const recv = unaliasType(inferType(e.receiver, sym, ctx), sym);
|
|
3840
|
+
if (recv?.kind === "TypeApp") {
|
|
3841
|
+
if (recv.name === "Map") return recv.args[1] ? {
|
|
3842
|
+
kind: "TypeApp",
|
|
3843
|
+
name: "Option",
|
|
3844
|
+
args: [recv.args[1]],
|
|
3845
|
+
pos: e.pos
|
|
3846
|
+
} : null;
|
|
3847
|
+
if (recv.name === "Option" || recv.name === "Result") return recv.args[0] ?? null;
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
return null;
|
|
3851
|
+
case "RecordLit": return {
|
|
3852
|
+
kind: "TypeRecord",
|
|
3853
|
+
fields: e.fields.map((f) => ({
|
|
3854
|
+
name: f.name,
|
|
3855
|
+
type: inferType(f.value, sym, ctx) ?? unitType(f.value.pos)
|
|
3856
|
+
})),
|
|
3857
|
+
pos: e.pos
|
|
3858
|
+
};
|
|
3859
|
+
case "Variant": {
|
|
3860
|
+
const inner = e.payload[0] ? inferType(e.payload[0], sym, ctx) ?? unitType(e.pos) : unitType(e.pos);
|
|
3861
|
+
if (e.name === "Some" || e.name === "None") return {
|
|
3862
|
+
kind: "TypeApp",
|
|
3863
|
+
name: "Option",
|
|
3864
|
+
args: [inner],
|
|
3865
|
+
pos: e.pos
|
|
3866
|
+
};
|
|
3867
|
+
if (e.name === "Ok") return {
|
|
3868
|
+
kind: "TypeApp",
|
|
3869
|
+
name: "Result",
|
|
3870
|
+
args: [inner],
|
|
3871
|
+
pos: e.pos
|
|
3872
|
+
};
|
|
3873
|
+
return null;
|
|
3874
|
+
}
|
|
3875
|
+
default: return null;
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
/**
|
|
3879
|
+
* Decide whether `recv.field` is a record field read or a method shortcut, and
|
|
3880
|
+
* annotate the node so codegen lowers the right thing (ADR-002). Emits E0108
|
|
3881
|
+
* when the receiver type is KNOWN and `field` is neither a member nor a record
|
|
3882
|
+
* field; stays silent (shortcut) when the type is undecidable.
|
|
3883
|
+
*/
|
|
3884
|
+
function classifyFieldAccess(e, sym, errors, ctx) {
|
|
3885
|
+
const t = unaliasType(inferType(e.base, sym, ctx), sym);
|
|
3886
|
+
if (!t) return;
|
|
3887
|
+
if (t.kind === "TypeRecord") {
|
|
3888
|
+
if (recordFieldType(t, e.field)) {
|
|
3889
|
+
e.accessKind = "field";
|
|
3890
|
+
return;
|
|
3891
|
+
}
|
|
3892
|
+
if (e.field === "show") {
|
|
3893
|
+
e.accessKind = "shortcut";
|
|
3894
|
+
return;
|
|
3895
|
+
}
|
|
3896
|
+
errors.push({
|
|
3897
|
+
code: "E0108",
|
|
3898
|
+
kind: "undef-member",
|
|
3899
|
+
message: `Record type has no field or method ".${e.field}"`,
|
|
3900
|
+
pos: e.pos
|
|
3901
|
+
});
|
|
3902
|
+
return;
|
|
3903
|
+
}
|
|
3904
|
+
if (t.kind === "TypePrim" && SCALAR_PRIMS.has(t.name) || t.kind === "TypeApp" && STDLIB_CONTAINERS.has(t.name)) {
|
|
3905
|
+
if (KNOWN_MEMBERS.has(e.field)) {
|
|
3906
|
+
e.accessKind = "shortcut";
|
|
3907
|
+
return;
|
|
3908
|
+
}
|
|
3909
|
+
const tn = t.kind === "TypeApp" ? t.name : t.name;
|
|
3910
|
+
errors.push({
|
|
3911
|
+
code: "E0108",
|
|
3912
|
+
kind: "undef-member",
|
|
3913
|
+
message: `Type "${tn}" has no member ".${e.field}"`,
|
|
3914
|
+
pos: e.pos
|
|
3915
|
+
});
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3651
3918
|
function currentFnName(ctx) {
|
|
3652
3919
|
return ctx.fnName ?? "<fn>";
|
|
3653
3920
|
}
|
|
3654
3921
|
function checkFn(fn, sym, errors) {
|
|
3655
3922
|
const ctx = {
|
|
3656
3923
|
kind: "fn",
|
|
3657
|
-
localBinds: /* @__PURE__ */ new Set()
|
|
3924
|
+
localBinds: /* @__PURE__ */ new Set(),
|
|
3925
|
+
localTypes: new Map(fn.params.map((p) => [p.name, p.type]))
|
|
3658
3926
|
};
|
|
3659
3927
|
ctx.fnName = fn.name;
|
|
3660
3928
|
for (const p of fn.params) ctx.localBinds.add(p.name);
|
|
@@ -3769,4 +4037,4 @@ function compile(source, opts) {
|
|
|
3769
4037
|
};
|
|
3770
4038
|
}
|
|
3771
4039
|
//#endregion
|
|
3772
|
-
export { ParseError, RUNTIME_HELPERS, STANDARD_CAPABILITIES, check, codegen, compile, inlineRuntime, lex, parse, parseCapabilityManifest };
|
|
4040
|
+
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.0",
|
|
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",
|