@ball-lang/compiler 1.68.6 → 1.69.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/compiler.d.ts.map +1 -1
- package/dist/compiler.js +72 -15
- package/dist/compiler.js.map +1 -1
- package/package.json +1 -1
- package/src/compiler.ts +82 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ball-lang/compiler",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.69.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
|
@@ -5509,20 +5509,65 @@ function __isUnknownFnError(e: any): boolean {
|
|
|
5509
5509
|
if (list) return `${this.expr(list)}.join('')`;
|
|
5510
5510
|
return "''";
|
|
5511
5511
|
}
|
|
5512
|
-
//
|
|
5513
|
-
//
|
|
5514
|
-
//
|
|
5515
|
-
//
|
|
5516
|
-
//
|
|
5517
|
-
//
|
|
5518
|
-
//
|
|
5519
|
-
//
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5512
|
+
// The `std_collections` set family, on native JS `Set`s — the same value
|
|
5513
|
+
// `set_create` above compiles to (`new Set([...])`).
|
|
5514
|
+
//
|
|
5515
|
+
// These used to throw "not implemented", on the reasoning that neither
|
|
5516
|
+
// encoder emits a direct `std_collections.set_*` call (a Set METHOD call
|
|
5517
|
+
// routes through compileCall's generic "self"-field dispatch instead). But
|
|
5518
|
+
// "no encoder emits it" is not "no program contains it": a hand-authored
|
|
5519
|
+
// or tool-generated Ball program can, and every other compiler in the repo
|
|
5520
|
+
// (Dart/C++/Rust/C#/Go/Python) implements the family. Conformance fixture
|
|
5521
|
+
// 459_set_add_remove_bool is exactly such a program, and it made this
|
|
5522
|
+
// throw reachable — so implement the family rather than carve it out.
|
|
5523
|
+
//
|
|
5524
|
+
// `set_add`/`set_remove` mutate in place and answer BOOL (issue #545) —
|
|
5525
|
+
// `Set.add` returns the set in JS, so it is wrapped; `Set.delete` already
|
|
5526
|
+
// reports presence.
|
|
5527
|
+
//
|
|
5528
|
+
// A member whose operand field is MISSING throws (`__setOperand`) rather
|
|
5529
|
+
// than emitting a placeholder like `false` / `0` / `new Set()`: a
|
|
5530
|
+
// `std_collections` set call without its operand is a malformed program,
|
|
5531
|
+
// and quietly compiling it to a constant is precisely the silent
|
|
5532
|
+
// degradation that hid issue #55. Nothing regresses by throwing — until
|
|
5533
|
+
// #545 this whole family threw unconditionally, so any input that reaches
|
|
5534
|
+
// the throw was rejected by the old code too.
|
|
5535
|
+
case "set_add": {
|
|
5536
|
+
const set = __setOperand(fn, "set", f.get("set") ?? f.get("collection"));
|
|
5537
|
+
const value = __setOperand(fn, "value", f.get("value") ?? f.get("element"));
|
|
5538
|
+
return `(() => { const __s = ${this.expr(set)}; const __v = ${this.expr(value)}; if (__s.has(__v)) return false; __s.add(__v); return true; })()`;
|
|
5539
|
+
}
|
|
5540
|
+
case "set_remove": {
|
|
5541
|
+
const set = __setOperand(fn, "set", f.get("set") ?? f.get("collection"));
|
|
5542
|
+
const value = __setOperand(fn, "value", f.get("value") ?? f.get("element"));
|
|
5543
|
+
return `${this.expr(set)}.delete(${this.expr(value)})`;
|
|
5544
|
+
}
|
|
5545
|
+
case "set_contains": {
|
|
5546
|
+
const set = __setOperand(fn, "set", f.get("set") ?? f.get("collection"));
|
|
5547
|
+
const value = __setOperand(fn, "value", f.get("value") ?? f.get("element"));
|
|
5548
|
+
return `${this.expr(set)}.has(${this.expr(value)})`;
|
|
5549
|
+
}
|
|
5550
|
+
case "set_length":
|
|
5551
|
+
return `${this.expr(__setOperand(fn, "set", f.get("set") ?? f.get("collection")))}.size`;
|
|
5552
|
+
case "set_is_empty":
|
|
5553
|
+
return `(${this.expr(__setOperand(fn, "set", f.get("set") ?? f.get("collection")))}.size === 0)`;
|
|
5554
|
+
case "set_to_list":
|
|
5555
|
+
return `[...${this.expr(__setOperand(fn, "set", f.get("set") ?? f.get("collection")))}]`;
|
|
5556
|
+
case "set_union": {
|
|
5557
|
+
const l = __setOperand(fn, "left", f.get("left") ?? f.get("set"));
|
|
5558
|
+
const r = __setOperand(fn, "right", f.get("right") ?? f.get("other"));
|
|
5559
|
+
return `new Set([...${this.expr(l)}, ...${this.expr(r)}])`;
|
|
5560
|
+
}
|
|
5561
|
+
case "set_intersection": {
|
|
5562
|
+
const l = __setOperand(fn, "left", f.get("left") ?? f.get("set"));
|
|
5563
|
+
const r = __setOperand(fn, "right", f.get("right") ?? f.get("other"));
|
|
5564
|
+
return `(() => { const __b = ${this.expr(r)}; return new Set([...${this.expr(l)}].filter((__x: any) => __b.has(__x))); })()`;
|
|
5565
|
+
}
|
|
5566
|
+
case "set_difference": {
|
|
5567
|
+
const l = __setOperand(fn, "left", f.get("left") ?? f.get("set"));
|
|
5568
|
+
const r = __setOperand(fn, "right", f.get("right") ?? f.get("other"));
|
|
5569
|
+
return `(() => { const __b = ${this.expr(r)}; return new Set([...${this.expr(l)}].filter((__x: any) => !__b.has(__x))); })()`;
|
|
5570
|
+
}
|
|
5526
5571
|
// I/O
|
|
5527
5572
|
case "print_error": {
|
|
5528
5573
|
const msg = f.get("message") ?? f.get("value");
|
|
@@ -6400,6 +6445,29 @@ function fieldMap(fields: FieldValuePair[]): Map<string, Expression> {
|
|
|
6400
6445
|
return m;
|
|
6401
6446
|
}
|
|
6402
6447
|
|
|
6448
|
+
/**
|
|
6449
|
+
* Assert that a `std_collections` set operation was handed the operand it needs.
|
|
6450
|
+
*
|
|
6451
|
+
* The set family (issue #545) FAILS LOUD on a missing field rather than
|
|
6452
|
+
* emitting a placeholder (`false` / `0` / `new Set()`). A set call without its
|
|
6453
|
+
* operand is a malformed program, and compiling it to a constant is the silent
|
|
6454
|
+
* degradation CLAUDE.md forbids and issue #55 was made of. Throwing costs
|
|
6455
|
+
* nothing in compatibility: before #545 every member of the family threw
|
|
6456
|
+
* unconditionally, so anything that reaches here was already rejected.
|
|
6457
|
+
*/
|
|
6458
|
+
function __setOperand(
|
|
6459
|
+
fn: string,
|
|
6460
|
+
field: string,
|
|
6461
|
+
value: Expression | undefined,
|
|
6462
|
+
): Expression {
|
|
6463
|
+
if (value === undefined) {
|
|
6464
|
+
throw new Error(
|
|
6465
|
+
`TS compiler: std_collections.${fn} is missing its "${field}" field`,
|
|
6466
|
+
);
|
|
6467
|
+
}
|
|
6468
|
+
return value;
|
|
6469
|
+
}
|
|
6470
|
+
|
|
6403
6471
|
function memberShortName(qualified: string): string {
|
|
6404
6472
|
const dot = qualified.lastIndexOf(".");
|
|
6405
6473
|
return sanitize(dot < 0 ? qualified : qualified.slice(dot + 1));
|