@ball-lang/compiler 1.63.1 → 1.65.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.
@@ -1 +1 @@
1
- {"version":3,"file":"preamble.d.ts","sourceRoot":"","sources":["../src/preamble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,QA8jD/B,CAAC"}
1
+ {"version":3,"file":"preamble.d.ts","sourceRoot":"","sources":["../src/preamble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,QA4lD/B,CAAC"}
package/dist/preamble.js CHANGED
@@ -1487,6 +1487,36 @@ function __ball_is_type(value: any, typeStr: string): boolean {
1487
1487
  }
1488
1488
  }
1489
1489
 
1490
+ // std.type_of (#489) - the string form of the very discrimination
1491
+ // __ball_is_type performs, i.e. the canonical BASE type name with generic
1492
+ // type arguments dropped and any module prefix stripped. Bare JS typeof
1493
+ // cannot be used: typeof [] is 'object', not 'list', and it cannot tell an
1494
+ // int from a double. Must agree with the Dart reference engine's _typeNameOf.
1495
+ function __ball_type_of(value: any): string {
1496
+ if (value == null) return 'Null';
1497
+ if (typeof value === 'boolean') return 'bool';
1498
+ if (value instanceof BallDouble) return 'double';
1499
+ if (typeof value === 'number') return Number.isInteger(value) ? 'int' : 'double';
1500
+ if (typeof value === 'string') return 'String';
1501
+ if (Array.isArray(value)) return 'List';
1502
+ if (value instanceof Set) return 'Set';
1503
+ if (typeof value === 'function') return 'Function';
1504
+ if (typeof value === 'object') {
1505
+ const tag = value.__type__;
1506
+ if (typeof tag === 'string' && tag.length > 0) {
1507
+ const colon = tag.indexOf(':');
1508
+ return colon >= 0 ? tag.slice(colon + 1) : tag;
1509
+ }
1510
+ // A compiled user class is a real TS class; a Dart Map is a plain object.
1511
+ const ctor = value.constructor?.name;
1512
+ if (typeof ctor === 'string' && ctor.length > 0 && ctor !== 'Object') {
1513
+ return ctor;
1514
+ }
1515
+ return 'Map';
1516
+ }
1517
+ return typeof value;
1518
+ }
1519
+
1490
1520
  // Minimal DateTime / Duration / Future polyfills used by std_time and
1491
1521
  // the round-tripped engine's sleep_ms helper. Wide enough for the
1492
1522
  // conformance suite, narrow enough to stay out of users' way.
@@ -1 +1 @@
1
- {"version":3,"file":"preamble.js","sourceRoot":"","sources":["../src/preamble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8jD5C,CAAC"}
1
+ {"version":3,"file":"preamble.js","sourceRoot":"","sources":["../src/preamble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4lD5C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ball-lang/compiler",
3
- "version": "1.63.1",
3
+ "version": "1.65.0",
4
4
  "description": "Ball → TypeScript compiler. Consumes a Ball protobuf Program and emits idiomatic TypeScript via ts-morph. The canonical TS compiler for Ball — lives in TS land so TS syntax knowledge doesn't leak into other languages.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/compiler.ts CHANGED
@@ -590,36 +590,15 @@ $1_getFieldDefaults(typeName: any): any {
590
590
  $1async _resolveAndCallFunction(`,
591
591
  );
592
592
 
593
- // Post-processing: inject function/method resolution for unresolved
594
- // typeNames in _evalMessageCreation. After the typeDef check, when
595
- // no typeDef is found, try resolving as a function call.
596
- // Find the end of _evalMessageCreation's if(typeName) block and add fallback.
597
- body = body.replace(
598
- /return fields;\s*\}\s*\n\s*_findTypeDef/,
599
- `if (!fields['__type__'] || !this._findTypeDef(msg.typeName)) {
600
- const __fnKey = this._currentModule + '.' + msg.typeName;
601
- const __fnMatch = this._functions[__fnKey];
602
- if (__fnMatch != null && !__fnMatch.isBase && __fnMatch.hasBody()) {
603
- return this._callFunction(this._currentModule, __fnMatch, fields);
604
- }
605
- // Try searching all functions for a matching suffix
606
- // e.g., typeName="main:_gcd" should match "main.main:Fraction._gcd"
607
- const __bareType = String(msg.typeName).indexOf(':') >= 0 ? String(msg.typeName).substring(String(msg.typeName).indexOf(':') + 1) : String(msg.typeName);
608
- for (const __fk of Object.keys(this._functions)) {
609
- if (__fk.endsWith('.' + __bareType) || __fk.endsWith('.' + msg.typeName)) {
610
- const __fm = this._functions[__fk];
611
- if (__fm && !__fm.isBase && __fm.hasBody()) {
612
- return this._callFunction(this._currentModule, __fm, fields);
613
- }
614
- }
615
- }
616
- }
617
- return fields;
618
- }
619
-
620
- _findTypeDef`,
621
- );
622
-
593
+ // NOTE (#489/#499): a former post-processing pass injected a
594
+ // function-resolution fallback into _evalMessageCreation, anchored on the
595
+ // literal text `return fields; } _findTypeDef`. The reference engine's
596
+ // emitted shape moved on, so the regex stopped matching and the pass
597
+ // became a SILENT no-op (verified against the committed compiled_engine.ts
598
+ // on main: neither `__fnKey` nor any of its injected identifiers appears,
599
+ // while the engine is nevertheless at full conformance parity). It was
600
+ // removed rather than left behind as a dead `.replace` that reads as
601
+ // working behaviour.
623
602
  // Post-processing: inject constructor instance building.
624
603
  // When a function has no body and its metadata says kind='constructor',
625
604
  // build an instance object from is_this params instead of returning null.
@@ -2787,6 +2766,17 @@ function __isUnknownFnError(e: any): boolean {
2787
2766
  }
2788
2767
  }
2789
2768
  }
2769
+ // Dart runs a constructor's INITIALIZER LIST before its body, and a
2770
+ // constructor may have both (`Point(int a, int b) : x = a, y = b { … }`).
2771
+ // Only `super(...)` and `this.`-params were emitted here, so every
2772
+ // initializer-list field stayed `undefined` (conformance 438).
2773
+ const rawParamNames = rawParams.map((p) => p.name);
2774
+ for (const init of initializers) {
2775
+ if (init?.kind !== "field" || typeof init.name !== "string") continue;
2776
+ prologueParts.push(
2777
+ `this.${init.name} = ${resolveInitializerValue(init.value, rawParamNames)};`,
2778
+ );
2779
+ }
2790
2780
  const prologue = prologueParts.join("\n");
2791
2781
  // Filter out self-recursive patterns from the constructor body.
2792
2782
  // The encoder emits `let self = messageCreation{typeName:"mod:ClassName"}`
@@ -2892,24 +2882,33 @@ function __isUnknownFnError(e: any): boolean {
2892
2882
  // This directly instantiates with fields set.
2893
2883
  const assignments = initializers
2894
2884
  .filter((i: any) => i?.kind === "field")
2895
- .map((init: any) => {
2896
- const valueStr = typeof init.value === "string" ? init.value : "null";
2897
- let resolvedValue: string;
2898
- if (params.includes(valueStr)) resolvedValue = sanitize(valueStr);
2899
- else if (/^-?\d+(\.\d+)?$/.test(valueStr)) resolvedValue = valueStr.includes(".") ? `new BallDouble(${valueStr})` : valueStr;
2900
- else if (valueStr.startsWith("'") || valueStr.startsWith('"')) resolvedValue = valueStr;
2901
- else {
2902
- const idxMatch = /^(\w+)\[(\d+)\]$/.exec(valueStr);
2903
- if (idxMatch && params.includes(idxMatch[1])) resolvedValue = `${sanitize(idxMatch[1])}[${idxMatch[2]}]`;
2904
- else resolvedValue = valueStr;
2905
- }
2906
- return `__inst.${init.name} = ${resolvedValue};`;
2907
- });
2885
+ .map((init: any) => `__inst.${init.name} = ${resolveInitializerValue(init.value, params)};`);
2908
2886
  // For is_this params, assign them as fields
2909
2887
  const thisAssignments = thisParams.map(p => `__inst.${p.name} = ${sanitize(p.name)};`);
2910
2888
  const allAssignments = [...assignments, ...thisAssignments];
2911
2889
  bodyParts.push(`const __inst = Object.create(${className}.prototype);`);
2912
2890
  for (const a of allAssignments) bodyParts.push(a);
2891
+ // A named constructor may have BOTH an initializer list / `this.`-params
2892
+ // AND a body (`Countdown.pair(int s) : value = s { tail = …; }`, or
2893
+ // `Countdown.from(this.value) { … }`). Dart runs the list first, then the
2894
+ // body; the body used to be dropped entirely by this branch, so every
2895
+ // field the body set stayed unset (conformance 436/438). `.call(__inst)`
2896
+ // — a real `function`, not an arrow — gives the compiled body the
2897
+ // instance as `this`, so its `this.<field>` writes land on `__inst`,
2898
+ // while the constructor's parameters stay in scope by closure.
2899
+ if (fn.body) {
2900
+ const captured = this.withMethodContext(
2901
+ new Set(params),
2902
+ classFields,
2903
+ () =>
2904
+ this.captureInto(() => {
2905
+ this.emitStatementOrExpression(fn.body!, false);
2906
+ }),
2907
+ );
2908
+ if (captured.trim() !== "") {
2909
+ bodyParts.push(`(function () {\n${captured}\n}).call(__inst);`);
2910
+ }
2911
+ }
2913
2912
  bodyParts.push(`return __inst;`);
2914
2913
  } else if (fn.body) {
2915
2914
  // Has an actual body — use it
@@ -4768,6 +4767,14 @@ function __isUnknownFnError(e: any): boolean {
4768
4767
  return `(${this.expr(f.get("value")!)} == null)`;
4769
4768
  }
4770
4769
  case "as": return this.expr(f.get("value")!);
4770
+ // #489 — the value's canonical runtime type NAME. The TS encoder emits
4771
+ // this for JS `typeof`, but bare `typeof` does not match the vocabulary
4772
+ // (`typeof [] === 'object'`), so route through the preamble helper.
4773
+ case "type_of": {
4774
+ const val = f.get("value") ?? f.get("target") ?? f.get("arg0");
4775
+ if (!val) throw new Error("TS compiler: std.type_of is missing its `value` field");
4776
+ return `__ball_type_of(${this.expr(val)})`;
4777
+ }
4771
4778
  case "if": {
4772
4779
  const cond = this.expr(f.get("condition")!);
4773
4780
  const thenExpr = f.get("then")!;
@@ -6112,6 +6119,28 @@ function extractParams(fn: FunctionDef): string[] {
6112
6119
  return out;
6113
6120
  }
6114
6121
 
6122
+ /**
6123
+ * Resolve a constructor initializer-list value. The encoder stores these as
6124
+ * SOURCE TEXT (`x = a` -> "a", `label = 'pt'` -> "'pt'", `x = coords[0]` ->
6125
+ * "coords[0]"), so a param reference, a literal and an indexed read all arrive
6126
+ * as plain strings. Shared by the unnamed and named constructor builders so the
6127
+ * two can never drift.
6128
+ */
6129
+ function resolveInitializerValue(rawValue: unknown, params: string[]): string {
6130
+ const valueStr = typeof rawValue === "string" ? rawValue : "null";
6131
+ if (params.includes(valueStr)) return sanitize(valueStr);
6132
+ if (/^-?\d+(\.\d+)?$/.test(valueStr)) {
6133
+ return valueStr.includes(".") ? `new BallDouble(${valueStr})` : valueStr;
6134
+ }
6135
+ if (valueStr.startsWith("'") || valueStr.startsWith('"')) return valueStr;
6136
+ if (valueStr === "true" || valueStr === "false" || valueStr === "null") return valueStr;
6137
+ const idxMatch = /^(\w+)\[(\d+)\]$/.exec(valueStr);
6138
+ if (idxMatch && params.includes(idxMatch[1])) {
6139
+ return `${sanitize(idxMatch[1])}[${idxMatch[2]}]`;
6140
+ }
6141
+ return valueStr;
6142
+ }
6143
+
6115
6144
  function extractCtorParams(meta: Struct): CtorParam[] {
6116
6145
  const raw = meta["params"];
6117
6146
  if (!Array.isArray(raw)) return [];
package/src/preamble.ts CHANGED
@@ -1487,6 +1487,36 @@ function __ball_is_type(value: any, typeStr: string): boolean {
1487
1487
  }
1488
1488
  }
1489
1489
 
1490
+ // std.type_of (#489) - the string form of the very discrimination
1491
+ // __ball_is_type performs, i.e. the canonical BASE type name with generic
1492
+ // type arguments dropped and any module prefix stripped. Bare JS typeof
1493
+ // cannot be used: typeof [] is 'object', not 'list', and it cannot tell an
1494
+ // int from a double. Must agree with the Dart reference engine's _typeNameOf.
1495
+ function __ball_type_of(value: any): string {
1496
+ if (value == null) return 'Null';
1497
+ if (typeof value === 'boolean') return 'bool';
1498
+ if (value instanceof BallDouble) return 'double';
1499
+ if (typeof value === 'number') return Number.isInteger(value) ? 'int' : 'double';
1500
+ if (typeof value === 'string') return 'String';
1501
+ if (Array.isArray(value)) return 'List';
1502
+ if (value instanceof Set) return 'Set';
1503
+ if (typeof value === 'function') return 'Function';
1504
+ if (typeof value === 'object') {
1505
+ const tag = value.__type__;
1506
+ if (typeof tag === 'string' && tag.length > 0) {
1507
+ const colon = tag.indexOf(':');
1508
+ return colon >= 0 ? tag.slice(colon + 1) : tag;
1509
+ }
1510
+ // A compiled user class is a real TS class; a Dart Map is a plain object.
1511
+ const ctor = value.constructor?.name;
1512
+ if (typeof ctor === 'string' && ctor.length > 0 && ctor !== 'Object') {
1513
+ return ctor;
1514
+ }
1515
+ return 'Map';
1516
+ }
1517
+ return typeof value;
1518
+ }
1519
+
1490
1520
  // Minimal DateTime / Duration / Future polyfills used by std_time and
1491
1521
  // the round-tripped engine's sleep_ms helper. Wide enough for the
1492
1522
  // conformance suite, narrow enough to stay out of users' way.