@ball-lang/compiler 1.6.1 → 1.6.3

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,QAm3C/B,CAAC"}
1
+ {"version":3,"file":"preamble.d.ts","sourceRoot":"","sources":["../src/preamble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,QA+4C/B,CAAC"}
package/dist/preamble.js CHANGED
@@ -110,7 +110,15 @@ class BallObject extends BallMap {
110
110
  // (e.g. 42.0 not 42). Used by the compiled Dart engine's _toDouble.
111
111
  class BallDouble {
112
112
  readonly value: number;
113
- constructor(v: number) { this.value = v; }
113
+ // Collapse nested wrapping (new BallDouble(new BallDouble(x))) down to the
114
+ // innermost raw number instead of storing a BallDouble-holding-a-BallDouble.
115
+ // This can legitimately arise now that string_to_double's OWN compiled
116
+ // form already wraps its result (#222): the self-hosted engine's Dart
117
+ // source explicitly does BallDouble(double.parse(s)) to make the parse
118
+ // result double-preserving, which — once double.parse (string_to_double)
119
+ // started wrapping on its own — would otherwise double-wrap. Unwrapping
120
+ // here is idempotent for every other caller, since v is a plain number.
121
+ constructor(v: number) { this.value = v instanceof BallDouble ? v.value : v; }
114
122
  valueOf(): number { return this.value; }
115
123
  get isNaN(): boolean { return Number.isNaN(this.value); }
116
124
  get isFinite(): boolean { return Number.isFinite(this.value); }
@@ -244,6 +252,13 @@ function __ball_to_string(v: any): string {
244
252
  }
245
253
  return '{' + parts.join(', ') + '}';
246
254
  }
255
+ if (v instanceof Set) {
256
+ // A Set is a plain object from typeof's perspective (falls through to
257
+ // the generic branch below, which reads Object.keys — always [] for a
258
+ // Set's internal slots), so every Set printed as the empty "{}" no
259
+ // matter its contents until this dedicated case was added (#219).
260
+ return '{' + [...v].map(__ball_to_string).join(', ') + '}';
261
+ }
247
262
  if (typeof v === 'object' && !Array.isArray(v)) {
248
263
  // StringBuffer-like objects
249
264
  if (v['__buffer__'] && Array.isArray(v['__buffer__'])) {
@@ -296,10 +311,13 @@ function __ball_to_int(v: any): any {
296
311
  return t;
297
312
  }
298
313
 
299
- function __ball_parse_double(s: string): number {
314
+ // Returns a BallDouble (not a bare number) so a whole-valued result (e.g.
315
+ // double.parse('7.0')) still prints "7.0", not "7" — JS numbers erase the
316
+ // int/double distinction that the wrapper exists to preserve (#67/#222).
317
+ function __ball_parse_double(s: string): BallDouble {
300
318
  const n = parseFloat(s);
301
319
  if (Number.isNaN(n)) throw new Error('FormatException: ' + s);
302
- return n;
320
+ return new BallDouble(n);
303
321
  }
304
322
 
305
323
  function __ball_double_to_string(n: number): string {
@@ -307,6 +325,16 @@ function __ball_double_to_string(n: number): string {
307
325
  return n.toString();
308
326
  }
309
327
 
328
+ // num.toStringAsFixed(digits). JS Number.prototype.toFixed drops the sign of
329
+ // -0 (returns "0.00" not "-0.00"); Dart's toStringAsFixed keeps it, matching
330
+ // the -0 handling __ball_to_string/BallDouble.toString already do.
331
+ function __ball_to_fixed(v: any, digits: any): string {
332
+ const n = Number(v);
333
+ const s = n.toFixed(digits);
334
+ if (n === 0 && 1 / n === -Infinity && !s.startsWith('-')) return '-' + s;
335
+ return s;
336
+ }
337
+
310
338
  // Polymorphic concat / merge used for std.list_concat. The encoder emits
311
339
  // list_concat both for Dart list concat AND for Map.addAll(...) (encoded as
312
340
  // m = list_concat(m, other)). Arrays concat positionally; plain objects
@@ -805,7 +833,7 @@ function __ball_cascade(target: any, ops: any[]): any {
805
833
  if (!_ballNp.toDouble) _ballNp.toDouble = function () { return Number(this); };
806
834
  if (!_ballNp.clamp) _ballNp.clamp = function (lo: any, hi: any) { const n = Number(this); return n < lo ? lo : n > hi ? hi : n; };
807
835
  if (!_ballNp.compareTo) _ballNp.compareTo = function (other: any) { const a = Number(this), b = Number(other); return a < b ? -1 : a > b ? 1 : 0; };
808
- if (!_ballNp.toStringAsFixed) _ballNp.toStringAsFixed = function (digits: any) { return Number(this).toFixed(digits); };
836
+ if (!_ballNp.toStringAsFixed) _ballNp.toStringAsFixed = function (digits: any) { return __ball_to_fixed(this, digits); };
809
837
  if (!_ballNp.remainder) _ballNp.remainder = function (other: any) { return Number(this) % Number(other); };
810
838
 
811
839
  // Object.prototype polyfills — used by the compiled engine when
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAm3C5C,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+4C5C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ball-lang/compiler",
3
- "version": "1.6.1",
3
+ "version": "1.6.3",
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
@@ -3100,7 +3100,16 @@ function __isUnknownFnError(e: any): boolean {
3100
3100
  const name = this.resolveVarName(rawName);
3101
3101
  // Track this declaration in the current scope.
3102
3102
  this.scopeDeclaredVars.add(name);
3103
- if (stmt.let.value !== undefined) {
3103
+ // The encoder marks "no initializer" with a reference to the sentinel
3104
+ // name __no_init__ (rather than omitting `value`) — e.g. `int? maybe;`.
3105
+ // Left uncaught, this compiles to `let maybe = __no_init__;`, which
3106
+ // resolves to the runtime preamble's OWN __no_init__ Symbol (used
3107
+ // internally by the compiled self-hosted engine) instead of leaving
3108
+ // `maybe` genuinely undefined, so a later `??=` never fires (#225).
3109
+ // Mirrors the same check already applied to for-loop init bindings
3110
+ // in renderForInit.
3111
+ const isNoInit = stmt.let.value?.reference?.name === "__no_init__";
3112
+ if (stmt.let.value !== undefined && !isNoInit) {
3104
3113
  this.writeln(`${kw} ${name} = ${this.expr(stmt.let.value)};`);
3105
3114
  } else {
3106
3115
  this.writeln(`${kw} ${name};`);
@@ -4592,7 +4601,17 @@ function __isUnknownFnError(e: any): boolean {
4592
4601
  } else if (fd.value) {
4593
4602
  elems.push(fd.value);
4594
4603
  }
4595
- } else if (fd.name !== "__type_args__" && fd.name !== "__const__") {
4604
+ } else if (
4605
+ // A `<T>{}` empty-typed-set-literal (e.g. `<int>{}`) carries its
4606
+ // type argument as a field literally named "type_args" (the
4607
+ // encoder's current single-word convention, NOT the older
4608
+ // MessageCreation-level "__type_args__" cosmetic marker checked
4609
+ // elsewhere in this file). Excluding only "__type_args__" left
4610
+ // it falling through to this generic branch and being pushed in
4611
+ // as a bogus set ELEMENT (`<int>{}` -> `new Set(['int'])`
4612
+ // instead of an empty Set, #219).
4613
+ fd.name !== "__type_args__" && fd.name !== "__const__" && fd.name !== "type_args"
4614
+ ) {
4596
4615
  elems.push(fd.value);
4597
4616
  }
4598
4617
  }
@@ -5029,7 +5048,11 @@ function __isUnknownFnError(e: any): boolean {
5029
5048
  }
5030
5049
  // Type ops
5031
5050
  case "type_literal": {
5032
- const v = f.get("value") ?? f.get("name");
5051
+ // The encoder's canonical field is "type" (matches the Dart compiler's
5052
+ // _stringFieldValue(f, 'type')) — a type literal's stored value is
5053
+ // Type.toString() (e.g. 'int', 'List<dynamic>'), so this just needs to
5054
+ // evaluate to that string; == and interpolation follow for free.
5055
+ const v = f.get("type") ?? f.get("value") ?? f.get("name");
5033
5056
  return v ? this.expr(v) : "null";
5034
5057
  }
5035
5058
  default: {
@@ -5099,7 +5122,7 @@ function __isUnknownFnError(e: any): boolean {
5099
5122
  case "to_string_as_fixed": {
5100
5123
  const v = this.expr(fg("value", "arg0")!);
5101
5124
  const d = this.expr(fg("digits", "arg1", "right")!);
5102
- return `(+(${v})).toFixed(${d})`;
5125
+ return `__ball_to_fixed(${v}, ${d})`;
5103
5126
  }
5104
5127
  // num.{round,floor,ceil,truncate}ToDouble() → double (issue #100).
5105
5128
  // Wrap in BallDouble so a whole result renders `4.0`, not `4`.
@@ -6376,8 +6399,14 @@ function dartInitializerToTs(
6376
6399
  return defaultInitializer(tsType, rawDartType);
6377
6400
  }
6378
6401
  const s = dartInit.trim();
6379
- // Already a JS-compatible literal: string, number, bool, null, []
6380
- if (/^(?:null|true|false|'.*'|".*"|-?\d+(?:\.\d+)?|\[\])$/.test(s)) {
6402
+ // Already a JS-compatible literal: string, number, bool, null, list.
6403
+ // A non-empty Dart list literal (e.g. `[10, 20, 30]`) is valid JS array
6404
+ // syntax as-is — this used to only match the EMPTY `[]` case, so a field
6405
+ // like `var widgets = [10, 20, 30];` fell through to defaultInitializer
6406
+ // (which has no type info to fall back on when the field's declared type
6407
+ // was inferred rather than explicit) and silently dropped to no
6408
+ // initializer at all, i.e. undefined/null (#220).
6409
+ if (/^(?:null|true|false|'.*'|".*"|-?\d+(?:\.\d+)?|\[[\s\S]*\])$/.test(s)) {
6381
6410
  return s;
6382
6411
  }
6383
6412
  // Dart `{}` can mean either an empty Map or an empty Set depending
package/src/preamble.ts CHANGED
@@ -110,7 +110,15 @@ class BallObject extends BallMap {
110
110
  // (e.g. 42.0 not 42). Used by the compiled Dart engine's _toDouble.
111
111
  class BallDouble {
112
112
  readonly value: number;
113
- constructor(v: number) { this.value = v; }
113
+ // Collapse nested wrapping (new BallDouble(new BallDouble(x))) down to the
114
+ // innermost raw number instead of storing a BallDouble-holding-a-BallDouble.
115
+ // This can legitimately arise now that string_to_double's OWN compiled
116
+ // form already wraps its result (#222): the self-hosted engine's Dart
117
+ // source explicitly does BallDouble(double.parse(s)) to make the parse
118
+ // result double-preserving, which — once double.parse (string_to_double)
119
+ // started wrapping on its own — would otherwise double-wrap. Unwrapping
120
+ // here is idempotent for every other caller, since v is a plain number.
121
+ constructor(v: number) { this.value = v instanceof BallDouble ? v.value : v; }
114
122
  valueOf(): number { return this.value; }
115
123
  get isNaN(): boolean { return Number.isNaN(this.value); }
116
124
  get isFinite(): boolean { return Number.isFinite(this.value); }
@@ -244,6 +252,13 @@ function __ball_to_string(v: any): string {
244
252
  }
245
253
  return '{' + parts.join(', ') + '}';
246
254
  }
255
+ if (v instanceof Set) {
256
+ // A Set is a plain object from typeof's perspective (falls through to
257
+ // the generic branch below, which reads Object.keys — always [] for a
258
+ // Set's internal slots), so every Set printed as the empty "{}" no
259
+ // matter its contents until this dedicated case was added (#219).
260
+ return '{' + [...v].map(__ball_to_string).join(', ') + '}';
261
+ }
247
262
  if (typeof v === 'object' && !Array.isArray(v)) {
248
263
  // StringBuffer-like objects
249
264
  if (v['__buffer__'] && Array.isArray(v['__buffer__'])) {
@@ -296,10 +311,13 @@ function __ball_to_int(v: any): any {
296
311
  return t;
297
312
  }
298
313
 
299
- function __ball_parse_double(s: string): number {
314
+ // Returns a BallDouble (not a bare number) so a whole-valued result (e.g.
315
+ // double.parse('7.0')) still prints "7.0", not "7" — JS numbers erase the
316
+ // int/double distinction that the wrapper exists to preserve (#67/#222).
317
+ function __ball_parse_double(s: string): BallDouble {
300
318
  const n = parseFloat(s);
301
319
  if (Number.isNaN(n)) throw new Error('FormatException: ' + s);
302
- return n;
320
+ return new BallDouble(n);
303
321
  }
304
322
 
305
323
  function __ball_double_to_string(n: number): string {
@@ -307,6 +325,16 @@ function __ball_double_to_string(n: number): string {
307
325
  return n.toString();
308
326
  }
309
327
 
328
+ // num.toStringAsFixed(digits). JS Number.prototype.toFixed drops the sign of
329
+ // -0 (returns "0.00" not "-0.00"); Dart's toStringAsFixed keeps it, matching
330
+ // the -0 handling __ball_to_string/BallDouble.toString already do.
331
+ function __ball_to_fixed(v: any, digits: any): string {
332
+ const n = Number(v);
333
+ const s = n.toFixed(digits);
334
+ if (n === 0 && 1 / n === -Infinity && !s.startsWith('-')) return '-' + s;
335
+ return s;
336
+ }
337
+
310
338
  // Polymorphic concat / merge used for std.list_concat. The encoder emits
311
339
  // list_concat both for Dart list concat AND for Map.addAll(...) (encoded as
312
340
  // m = list_concat(m, other)). Arrays concat positionally; plain objects
@@ -805,7 +833,7 @@ function __ball_cascade(target: any, ops: any[]): any {
805
833
  if (!_ballNp.toDouble) _ballNp.toDouble = function () { return Number(this); };
806
834
  if (!_ballNp.clamp) _ballNp.clamp = function (lo: any, hi: any) { const n = Number(this); return n < lo ? lo : n > hi ? hi : n; };
807
835
  if (!_ballNp.compareTo) _ballNp.compareTo = function (other: any) { const a = Number(this), b = Number(other); return a < b ? -1 : a > b ? 1 : 0; };
808
- if (!_ballNp.toStringAsFixed) _ballNp.toStringAsFixed = function (digits: any) { return Number(this).toFixed(digits); };
836
+ if (!_ballNp.toStringAsFixed) _ballNp.toStringAsFixed = function (digits: any) { return __ball_to_fixed(this, digits); };
809
837
  if (!_ballNp.remainder) _ballNp.remainder = function (other: any) { return Number(this) % Number(other); };
810
838
 
811
839
  // Object.prototype polyfills — used by the compiled engine when