@ball-lang/compiler 1.7.3 → 1.7.5

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,QAq7C/B,CAAC"}
1
+ {"version":3,"file":"preamble.d.ts","sourceRoot":"","sources":["../src/preamble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,QA29C/B,CAAC"}
package/dist/preamble.js CHANGED
@@ -994,14 +994,11 @@ function __ball_cascade(target: any, ops: any[]): any {
994
994
  },
995
995
  });
996
996
  }
997
- defDartGetter('entries', function (this: any) {
998
- if (this instanceof Map) return [..._nativeMapEntries.call(this)].map(([k, v]: any) => ({ key: k, value: v }));
999
- if (this == null || typeof this !== 'object') return [];
1000
- return Object.entries(this).map(([k, v]: any) => ({ key: k, value: v }));
1001
- });
1002
- // .keys/.values on a non-Map must FAIL LOUD (throw a catchable error), not
1003
- // silently return [] — the silent-degradation class of bug that hid issue
1004
- // #55 (mirrors the fix already applied to the Dart/C++ compilers).
997
+ // .entries/.keys/.values on a non-Map must FAIL LOUD (throw a catchable
998
+ // error), not silently return [] the silent-degradation class of bug
999
+ // that hid issue #55 (mirrors the fix already applied to the Dart/C++
1000
+ // compilers). .entries used to be the odd one out here, silently
1001
+ // returning [] instead of throwing — same bug family as #218.
1005
1002
  //
1006
1003
  // A getter installed on Object.prototype is invoked in "sloppy" (non-strict)
1007
1004
  // script contexts with this auto-boxed to a Number/String/Boolean WRAPPER
@@ -1014,6 +1011,13 @@ function __ball_cascade(target: any, ops: any[]): any {
1014
1011
  typeof v === 'object' && v !== null && !Array.isArray(v) &&
1015
1012
  !(v instanceof BallDouble) && !(v instanceof Set) &&
1016
1013
  !(v instanceof Number) && !(v instanceof String) && !(v instanceof Boolean);
1014
+ defDartGetter('entries', function (this: any) {
1015
+ if (this instanceof Map) return [..._nativeMapEntries.call(this)].map(([k, v]: any) => ({ key: k, value: v }));
1016
+ if (!__isGenuineMap(this)) {
1017
+ throw new Error('type \'' + __ball_to_string(this) + '\' has no .entries getter (not a Map)');
1018
+ }
1019
+ return Object.entries(this).map(([k, v]: any) => ({ key: k, value: v }));
1020
+ });
1017
1021
  defDartGetter('keys', function (this: any) {
1018
1022
  if (this instanceof Map) return [..._nativeMapKeys.call(this)];
1019
1023
  if (!__isGenuineMap(this)) {
@@ -1076,6 +1080,40 @@ function __ball_cascade(target: any, ops: any[]): any {
1076
1080
  });
1077
1081
  })();
1078
1082
 
1083
+ // std.map_keys/std.map_values/std.map_entries (the base-function-call form,
1084
+ // as opposed to the .keys/.values/.entries DART-GETTER-STYLE property
1085
+ // access the defDartGetter block above already guards) must ALSO fail loud
1086
+ // on a non-Map receiver instead of silently returning [] — same "genuine
1087
+ // Map" check, exposed as top-level helpers so compileStdCall's emitted code
1088
+ // can call them (#218).
1089
+ function __ball_map_keys(m: any): any {
1090
+ if (m instanceof Map) return [...m.keys()];
1091
+ if (typeof m !== 'object' || m === null || Array.isArray(m) ||
1092
+ m instanceof BallDouble || m instanceof Set ||
1093
+ m instanceof Number || m instanceof String || m instanceof Boolean) {
1094
+ throw new Error('type \'' + __ball_to_string(m) + '\' has no .keys getter (not a Map)');
1095
+ }
1096
+ return Object.keys(m);
1097
+ }
1098
+ function __ball_map_values(m: any): any {
1099
+ if (m instanceof Map) return [...m.values()];
1100
+ if (typeof m !== 'object' || m === null || Array.isArray(m) ||
1101
+ m instanceof BallDouble || m instanceof Set ||
1102
+ m instanceof Number || m instanceof String || m instanceof Boolean) {
1103
+ throw new Error('type \'' + __ball_to_string(m) + '\' has no .values getter (not a Map)');
1104
+ }
1105
+ return Object.values(m);
1106
+ }
1107
+ function __ball_map_entries(m: any): any {
1108
+ if (m instanceof Map) return [...m.entries()].map(([k, v]) => ({ key: k, value: v }));
1109
+ if (typeof m !== 'object' || m === null || Array.isArray(m) ||
1110
+ m instanceof BallDouble || m instanceof Set ||
1111
+ m instanceof Number || m instanceof String || m instanceof Boolean) {
1112
+ throw new Error('type \'' + __ball_to_string(m) + '\' has no .entries getter (not a Map)');
1113
+ }
1114
+ return Object.entries(m).map(([k, v]) => ({ key: k, value: v }));
1115
+ }
1116
+
1079
1117
  // ── Protobuf Struct/Value compatibility ─────────────────────────
1080
1118
  //
1081
1119
  // Dart's protobuf runtime wraps google.protobuf.Struct as a class
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAq7C5C,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA29C5C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ball-lang/compiler",
3
- "version": "1.7.3",
3
+ "version": "1.7.5",
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
@@ -4451,7 +4451,27 @@ function __isUnknownFnError(e: any): boolean {
4451
4451
  case "math_infinity": return "Infinity";
4452
4452
  case "math_nan": return "NaN";
4453
4453
  case "print": return `console.log(__ball_to_string(${this.expr(f.get("message")!)}))`;
4454
- case "index": return `__ball_index(${this.expr(f.get("target")!)}, ${this.expr(f.get("index")!)})`;
4454
+ case "index": {
4455
+ const target = f.get("target")!;
4456
+ const idx = f.get("index")!;
4457
+ // Bracket-invoking a string-literal-named operator method (e.g.
4458
+ // `a['+'](b)` — the only way to call one, since TS has no `operator`
4459
+ // keyword) indexes with the RAW lexeme, but the compiled class
4460
+ // exposes the operator under its canonical __op_*__ name (#248) —
4461
+ // translate the lexeme so the access finds the real method instead
4462
+ // of a nonexistent literal '+' property (#252). "-" is ambiguous
4463
+ // (unary __op_neg__ vs binary __op_sub__ — a class can only define
4464
+ // ONE '-'-named method, so exactly one of the two properties ever
4465
+ // actually exists; ?? picks whichever it is without needing type
4466
+ // info to disambiguate at the call site).
4467
+ const lexeme = idx.literal?.stringValue;
4468
+ if (lexeme !== undefined && Object.prototype.hasOwnProperty.call(operatorIndexNames, lexeme)) {
4469
+ const targetStr = this.expr(target);
4470
+ if (lexeme === "-") return `(${targetStr}.__op_sub__ ?? ${targetStr}.__op_neg__)`;
4471
+ return `${targetStr}.${operatorIndexNames[lexeme]}`;
4472
+ }
4473
+ return `__ball_index(${this.expr(target)}, ${this.expr(idx)})`;
4474
+ }
4455
4475
  case "null_coalesce": return `(${this.expr(f.get("left")!)} ?? ${this.expr(f.get("right")!)})`;
4456
4476
  case "null_check": return this.expr(f.get("value")!);
4457
4477
  case "is": {
@@ -4941,15 +4961,23 @@ function __isUnknownFnError(e: any): boolean {
4941
4961
  }
4942
4962
  case "map_keys": {
4943
4963
  const map = f.get("map") ?? f.get("value");
4944
- return map ? `Object.keys(${this.expr(map)})` : "[]";
4964
+ // Fail loud on a non-Map receiver instead of silently returning []
4965
+ // that silent degradation is the class of bug that hid issue #55
4966
+ // (mirrors the .keys DART-GETTER guard already installed in
4967
+ // preamble.ts's defDartGetter block, and the engine/C++-compiler
4968
+ // guards for the same base function, #218).
4969
+ return map ? `__ball_map_keys(${this.expr(map)})` : "[]";
4945
4970
  }
4946
4971
  case "map_values": {
4947
4972
  const map = f.get("map") ?? f.get("value");
4948
- return map ? `Object.values(${this.expr(map)})` : "[]";
4973
+ return map ? `__ball_map_values(${this.expr(map)})` : "[]";
4949
4974
  }
4950
4975
  case "map_entries": {
4951
4976
  const map = f.get("map") ?? f.get("value");
4952
- return map ? `Object.entries(${this.expr(map)}).map(([k, v]) => ({key: k, value: v}))` : "[]";
4977
+ // Same silent-degradation class as map_keys/map_values (#218): a
4978
+ // bare Object.entries(...) on a non-Map silently returns garbage
4979
+ // instead of failing loud.
4980
+ return map ? `__ball_map_entries(${this.expr(map)})` : "[]";
4953
4981
  }
4954
4982
  case "map_length": {
4955
4983
  const map = f.get("map") ?? f.get("value");
@@ -6493,6 +6521,36 @@ function unwrapLambda(e: Expression): Expression {
6493
6521
  return e;
6494
6522
  }
6495
6523
 
6524
+ /** Maps an operator lexeme to the canonical __op_*__ name a compiled class
6525
+ * exposes it under (mirrors ts/encoder's OPERATOR_CANONICAL_NAMES exactly
6526
+ * — double-trailing-underscore; NOT sanitize()'s single-underscore
6527
+ * operatorMap below, which is a separate, legacy short-name convention).
6528
+ * Used by std."index" to translate `a['+']` (the only way to bracket-
6529
+ * invoke a string-literal-named operator method) to the real property. */
6530
+ const operatorIndexNames: Record<string, string> = {
6531
+ "[]=": "__op_set_index__",
6532
+ "[]": "__op_get_index__",
6533
+ "==": "__op_eq__",
6534
+ "<": "__op_lt__",
6535
+ "<=": "__op_le__",
6536
+ ">": "__op_gt__",
6537
+ ">=": "__op_ge__",
6538
+ "<<": "__op_shl__",
6539
+ ">>": "__op_shr__",
6540
+ ">>>": "__op_ushr__",
6541
+ "+": "__op_add__",
6542
+ "*": "__op_mul__",
6543
+ "/": "__op_div__",
6544
+ "~/": "__op_idiv__",
6545
+ "%": "__op_mod__",
6546
+ "&": "__op_band__",
6547
+ "|": "__op_bor__",
6548
+ "^": "__op_bxor__",
6549
+ "~": "__op_bnot__",
6550
+ // "-" handled specially at the call site (unary vs binary ambiguity).
6551
+ "-": "__op_sub__",
6552
+ };
6553
+
6496
6554
  function sanitize(name: string): string {
6497
6555
  let out = name;
6498
6556
  const colon = out.indexOf(":");
package/src/preamble.ts CHANGED
@@ -994,14 +994,11 @@ function __ball_cascade(target: any, ops: any[]): any {
994
994
  },
995
995
  });
996
996
  }
997
- defDartGetter('entries', function (this: any) {
998
- if (this instanceof Map) return [..._nativeMapEntries.call(this)].map(([k, v]: any) => ({ key: k, value: v }));
999
- if (this == null || typeof this !== 'object') return [];
1000
- return Object.entries(this).map(([k, v]: any) => ({ key: k, value: v }));
1001
- });
1002
- // .keys/.values on a non-Map must FAIL LOUD (throw a catchable error), not
1003
- // silently return [] — the silent-degradation class of bug that hid issue
1004
- // #55 (mirrors the fix already applied to the Dart/C++ compilers).
997
+ // .entries/.keys/.values on a non-Map must FAIL LOUD (throw a catchable
998
+ // error), not silently return [] the silent-degradation class of bug
999
+ // that hid issue #55 (mirrors the fix already applied to the Dart/C++
1000
+ // compilers). .entries used to be the odd one out here, silently
1001
+ // returning [] instead of throwing — same bug family as #218.
1005
1002
  //
1006
1003
  // A getter installed on Object.prototype is invoked in "sloppy" (non-strict)
1007
1004
  // script contexts with this auto-boxed to a Number/String/Boolean WRAPPER
@@ -1014,6 +1011,13 @@ function __ball_cascade(target: any, ops: any[]): any {
1014
1011
  typeof v === 'object' && v !== null && !Array.isArray(v) &&
1015
1012
  !(v instanceof BallDouble) && !(v instanceof Set) &&
1016
1013
  !(v instanceof Number) && !(v instanceof String) && !(v instanceof Boolean);
1014
+ defDartGetter('entries', function (this: any) {
1015
+ if (this instanceof Map) return [..._nativeMapEntries.call(this)].map(([k, v]: any) => ({ key: k, value: v }));
1016
+ if (!__isGenuineMap(this)) {
1017
+ throw new Error('type \'' + __ball_to_string(this) + '\' has no .entries getter (not a Map)');
1018
+ }
1019
+ return Object.entries(this).map(([k, v]: any) => ({ key: k, value: v }));
1020
+ });
1017
1021
  defDartGetter('keys', function (this: any) {
1018
1022
  if (this instanceof Map) return [..._nativeMapKeys.call(this)];
1019
1023
  if (!__isGenuineMap(this)) {
@@ -1076,6 +1080,40 @@ function __ball_cascade(target: any, ops: any[]): any {
1076
1080
  });
1077
1081
  })();
1078
1082
 
1083
+ // std.map_keys/std.map_values/std.map_entries (the base-function-call form,
1084
+ // as opposed to the .keys/.values/.entries DART-GETTER-STYLE property
1085
+ // access the defDartGetter block above already guards) must ALSO fail loud
1086
+ // on a non-Map receiver instead of silently returning [] — same "genuine
1087
+ // Map" check, exposed as top-level helpers so compileStdCall's emitted code
1088
+ // can call them (#218).
1089
+ function __ball_map_keys(m: any): any {
1090
+ if (m instanceof Map) return [...m.keys()];
1091
+ if (typeof m !== 'object' || m === null || Array.isArray(m) ||
1092
+ m instanceof BallDouble || m instanceof Set ||
1093
+ m instanceof Number || m instanceof String || m instanceof Boolean) {
1094
+ throw new Error('type \'' + __ball_to_string(m) + '\' has no .keys getter (not a Map)');
1095
+ }
1096
+ return Object.keys(m);
1097
+ }
1098
+ function __ball_map_values(m: any): any {
1099
+ if (m instanceof Map) return [...m.values()];
1100
+ if (typeof m !== 'object' || m === null || Array.isArray(m) ||
1101
+ m instanceof BallDouble || m instanceof Set ||
1102
+ m instanceof Number || m instanceof String || m instanceof Boolean) {
1103
+ throw new Error('type \'' + __ball_to_string(m) + '\' has no .values getter (not a Map)');
1104
+ }
1105
+ return Object.values(m);
1106
+ }
1107
+ function __ball_map_entries(m: any): any {
1108
+ if (m instanceof Map) return [...m.entries()].map(([k, v]) => ({ key: k, value: v }));
1109
+ if (typeof m !== 'object' || m === null || Array.isArray(m) ||
1110
+ m instanceof BallDouble || m instanceof Set ||
1111
+ m instanceof Number || m instanceof String || m instanceof Boolean) {
1112
+ throw new Error('type \'' + __ball_to_string(m) + '\' has no .entries getter (not a Map)');
1113
+ }
1114
+ return Object.entries(m).map(([k, v]) => ({ key: k, value: v }));
1115
+ }
1116
+
1079
1117
  // ── Protobuf Struct/Value compatibility ─────────────────────────
1080
1118
  //
1081
1119
  // Dart's protobuf runtime wraps google.protobuf.Struct as a class