@ball-lang/compiler 1.63.1 → 1.64.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 +69 -44
- package/dist/compiler.js.map +1 -1
- package/dist/preamble.d.ts.map +1 -1
- package/dist/preamble.js +30 -0
- package/dist/preamble.js.map +1 -1
- package/package.json +1 -1
- package/src/compiler.ts +72 -43
- package/src/preamble.ts +30 -0
package/dist/preamble.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preamble.d.ts","sourceRoot":"","sources":["../src/preamble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,
|
|
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.
|
package/dist/preamble.js.map
CHANGED
|
@@ -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
|
|
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.
|
|
3
|
+
"version": "1.64.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
|
-
//
|
|
594
|
-
//
|
|
595
|
-
//
|
|
596
|
-
//
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
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.
|