@ball-lang/compiler 0.1.0 → 1.3.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.
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ [![npm version](https://img.shields.io/npm/v/@ball-lang/compiler.svg)](https://www.npmjs.com/package/@ball-lang/compiler)
2
+
3
+ # @ball-lang/compiler
4
+
5
+ Compiles [Ball programs](https://github.com/ball-lang/ball) (proto3 JSON / `Program` objects) into idiomatic **TypeScript** source. Runs entirely in-process using [`ts-morph`](https://ts-morph.com) — no Dart subprocess. The Dart compiler (`dart/compiler/`) is the canonical reference implementation; this package mirrors its semantics.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @ball-lang/compiler
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import { compile } from '@ball-lang/compiler';
17
+
18
+ // `program` is a Ball Program — a proto3 JSON object (or a parsed equivalent).
19
+ const program = {
20
+ name: 'hello',
21
+ version: '1.0.0',
22
+ entryModule: 'main',
23
+ entryFunction: 'main',
24
+ modules: [
25
+ {
26
+ name: 'std',
27
+ functions: [{ name: 'print', isBase: true }],
28
+ },
29
+ {
30
+ name: 'main',
31
+ moduleImports: [{ name: 'std' }],
32
+ functions: [
33
+ {
34
+ name: 'main',
35
+ body: {
36
+ call: {
37
+ module: 'std',
38
+ function: 'print',
39
+ input: {
40
+ messageCreation: {
41
+ fields: [
42
+ { name: 'value', value: { literal: { stringValue: 'Hello, World!' } } },
43
+ ],
44
+ },
45
+ },
46
+ },
47
+ },
48
+ },
49
+ ],
50
+ },
51
+ ],
52
+ };
53
+
54
+ const tsSource: string = compile(program);
55
+ console.log(tsSource); // emitted TypeScript
56
+ ```
57
+
58
+ ## API
59
+
60
+ - `compile(program: Program, options?: CompileOptions): string` — compile a whole program to TypeScript source.
61
+ - `compileModule(module: Module, options?: CompileModuleOptions): string` — compile a single module.
62
+ - `BallCompiler` — the underlying class, if you need finer control.
63
+ - `TS_RUNTIME_PREAMBLE` — the Dart-flavored runtime polyfill preamble injected at the top of compiled output.
64
+
65
+ `CompileOptions`:
66
+
67
+ | Option | Type | Description |
68
+ |--------|------|-------------|
69
+ | `includePreamble` | `boolean` | Prepend `TS_RUNTIME_PREAMBLE` to the output. Default `true`. |
70
+ | `fileName` | `string` | Output file path hint (affects `ts-morph`'s internal resolution). |
71
+
72
+ ## License
73
+
74
+ MIT
@@ -39,6 +39,20 @@ function parseArgs(argv) {
39
39
  const args = parseArgs(process.argv.slice(2));
40
40
  const ext = extname(args.input).toLowerCase();
41
41
 
42
+ // Ball files are self-describing google.protobuf.Any envelopes; strip the
43
+ // "@type" key (if present) to recover the bare proto3-JSON Program.
44
+ function unwrapBallFile(json) {
45
+ if (json === null || typeof json !== "object" || Array.isArray(json)) return json;
46
+ const type = json["@type"];
47
+ if (type === undefined) return json;
48
+ const ok = typeof type === "string" &&
49
+ (type.endsWith("/ball.v1.Program") || type.endsWith("/ball.v1.Module"));
50
+ if (!ok) die(`unknown ball file @type: ${JSON.stringify(type)}`);
51
+ const body = {};
52
+ for (const [k, v] of Object.entries(json)) { if (k !== "@type") body[k] = v; }
53
+ return body;
54
+ }
55
+
42
56
  let program;
43
57
  if (ext === ".pb") {
44
58
  die("binary .ball.pb not yet supported in @ball-lang/compiler (use .ball.json)");
@@ -46,6 +60,7 @@ if (ext === ".pb") {
46
60
  const text = readFileSync(args.input, "utf8");
47
61
  try { program = JSON.parse(text); }
48
62
  catch (e) { die(`invalid JSON: ${e.message}`); }
63
+ program = unwrapBallFile(program);
49
64
  }
50
65
 
51
66
  const ts = compile(program, { includePreamble: args.includePreamble });
@@ -1,4 +1,4 @@
1
- import type { Program } from "./types.ts";
1
+ import type { Module, Program } from "./types.ts";
2
2
  export interface CompileOptions {
3
3
  /** Include the runtime preamble at the top of the output. Default true. */
4
4
  includePreamble?: boolean;
@@ -12,22 +12,70 @@ export declare class BallCompiler {
12
12
  private depth;
13
13
  /** Catch-bound variables currently in scope — subject to bracket access. */
14
14
  private readonly catchVars;
15
+ /** Names of user-defined async functions (from metadata is_async). */
16
+ private asyncFnNames;
15
17
  /** Fields of the class currently being emitted (method bodies). */
16
18
  private currentClassFields;
17
19
  /** Parameters of the currently-emitting method (shadow fields). */
18
20
  private currentMethodParams;
19
21
  /** Short method names of the current class (for `this.foo()` routing). */
20
22
  private currentClassMethodNames;
23
+ /** Short names of GETTER accessors. Getters are properties, not methods,
24
+ * so bare references emit `this.name` not `this.name.bind(this)`. */
25
+ private currentClassGetterNames;
26
+ /**
27
+ * Short names of STATIC methods of the current class. Static members are
28
+ * not on `this` in JS, so unqualified same-class calls/tear-offs to these
29
+ * must be emitted as `<ClassName>.<name>` rather than `this.<name>`.
30
+ */
31
+ private currentClassStaticNames;
32
+ /** TS name of the class currently being emitted (for static-call routing). */
33
+ private currentClassName;
34
+ /** Type parameters of the class currently being emitted (erased to `any`). */
35
+ private currentClassTypeParams;
21
36
  /** All function names in the entry module (function vs ctor routing). */
22
37
  private allFunctionNames;
23
38
  /** typeDefs in the entry module (typeName → definition). */
24
39
  private typeDefByName;
40
+ /**
41
+ * Variable names declared (via `let`) in the current function scope.
42
+ * Used to detect shadowing conflicts when hoisting block statements.
43
+ */
44
+ private scopeDeclaredVars;
45
+ /**
46
+ * Stack of rename maps for shadow-renamed variables. When a hoisted
47
+ * block declares a variable that conflicts with an already-declared
48
+ * name, we push a map entry (original → renamed) before emitting
49
+ * the block's statements and pop it afterward.
50
+ */
51
+ private renameStack;
25
52
  constructor(program: Program);
26
53
  /** Compile to TS source. */
27
54
  compile(options?: CompileOptions): string;
28
55
  private emitFreeFunction;
56
+ /** Wrap captured statements in an IIFE, choosing the right kind:
57
+ * - yield present → `yield* (function* () { ... })()`
58
+ * - await present → `await (async () => { ... })()`
59
+ * - neither → `(() => { ... })()`
60
+ */
61
+ private wrapIIFE;
62
+ /** Check if an expression tree references any name in the given set. */
63
+ private bodyReferencesAny;
29
64
  private emitClass;
30
65
  private buildCtor;
66
+ /**
67
+ * Filters a constructor body to remove encoder-generated boilerplate:
68
+ * - `let self = new ClassName()` (self-recursive construction)
69
+ * - `return self` (returning the self variable)
70
+ * - Bare `input;` or `paramName;` statements (no-op expressions)
71
+ */
72
+ private filterCtorBody;
73
+ /**
74
+ * Builds a named constructor as a static factory method.
75
+ * Named constructors (e.g., Point.origin, Point.fromList) create instances
76
+ * from field initializers in metadata.
77
+ */
78
+ private buildNamedCtor;
31
79
  private buildMethod;
32
80
  private buildGetter;
33
81
  private buildSetter;
@@ -37,6 +85,18 @@ export declare class BallCompiler {
37
85
  private withMethodContext;
38
86
  private emitStatementOrExpression;
39
87
  private emitBlock;
88
+ /**
89
+ * Resolve a variable name through the active rename stack.
90
+ * If a rename exists for the sanitized name, return the renamed version.
91
+ */
92
+ private resolveVarName;
93
+ /**
94
+ * Recursively collect all `let`-declared variable names from a block's
95
+ * statements that would be hoisted into the enclosing scope. This includes
96
+ * direct `let` statements and `let` statements inside nested
97
+ * block-expression-as-statement nodes (since those are also hoisted flat).
98
+ */
99
+ private collectHoistedLetNames;
40
100
  private emitStatement;
41
101
  private isControlFlow;
42
102
  private emitControlFlowStatement;
@@ -47,9 +107,59 @@ export declare class BallCompiler {
47
107
  private emitDoWhileStmt;
48
108
  private emitTryStmt;
49
109
  private emitAssignStmt;
110
+ /**
111
+ * Detects `list_concat(list: X, value: Y)` where X is the same reference as
112
+ * [target] and emits in-place `X.push(...Y)` instead of `X = [...X, ...Y]`.
113
+ * Returns the expression string or undefined if the pattern doesn't match.
114
+ */
115
+ private tryEmitInPlaceListMutation;
116
+ /**
117
+ * Like {@link expr}, but produces a valid assignment target (lvalue).
118
+ *
119
+ * The `index` operator (`a[i]`) normally compiles to the bounds-checked
120
+ * `__ball_index(a, i)` runtime helper so out-of-bounds list reads throw a
121
+ * Dart-style RangeError. A function-call expression can't be the left side
122
+ * of an assignment, so when an `index` call is the target of an `assign`
123
+ * (e.g. `map[key] = value`) we emit the raw `a[i]` bracket form instead.
124
+ */
125
+ private lvalueExpr;
50
126
  private typedCatchCondition;
127
+ /**
128
+ * Qualifier for an unqualified same-class member reference. Static members
129
+ * are not on `this` in JS, so they must be addressed via the class name
130
+ * (`<ClassName>.m`); instance members use `this.m`.
131
+ */
132
+ private memberQualifier;
51
133
  private expr;
52
134
  private compileLiteral;
135
+ private isCollectionControlElement;
136
+ /** Build a TS list expression from collection elements. */
137
+ private compileListElements;
138
+ /** Build a TS Set expression from collection elements. */
139
+ private compileSetElements;
140
+ /**
141
+ * Build a TS plain-object map expression from collection map-entry
142
+ * elements. Each element is either an `entry` (key/value messageCreation),
143
+ * a spread/null_spread of another map, or a collection_if/collection_for
144
+ * whose leaves are key/value entries.
145
+ */
146
+ private compileMapElements;
147
+ /** Render a single non-control map entry as `key: value`, or undefined. */
148
+ private mapEntryPair;
149
+ /**
150
+ * Emit statements that push/add/assign one collection element into the
151
+ * accumulator `sink`. `kind` selects the sink operation:
152
+ * list → `sink.push(v)` set → `sink.add(v)` map → `sink[k] = v`.
153
+ */
154
+ private emitCollectionElement;
155
+ /**
156
+ * Render a C-style for-init `block` (a single-let, no-result block) as an
157
+ * inline declaration string like `var i = 0` — NOT an IIFE, so the loop
158
+ * variable stays in scope for the condition/update. Mirrors the Dart
159
+ * compiler's `_renderForInit`. Uses `var` to match Dart's shared-variable
160
+ * loop-capture semantics. Returns "" on an unrecognized shape.
161
+ */
162
+ private renderForInit;
53
163
  private compileFieldAccess;
54
164
  private compileMessageCreation;
55
165
  private extractPositionalAndNamed;
@@ -58,13 +168,49 @@ export declare class BallCompiler {
58
168
  private compileLambda;
59
169
  private compileCall;
60
170
  private compileStdCall;
171
+ private typeRefMetaToString;
172
+ private wrapWithTypeArgs;
61
173
  private emitIsCheck;
62
174
  private wrapIfNeeded;
63
175
  private compileSwitchExpr;
176
+ /**
177
+ * Fold one switch-expression branch into the ternary chain.
178
+ * - No guard: `(cond ? bindBody : rest)`.
179
+ * - With guard: the guard references the pattern's bindings, so bind ONCE in
180
+ * an IIFE and gate the body on the guard, falling through to `rest` when
181
+ * the guard is false: `(cond ? ((binds) => (guard ? body : rest))(args) : rest)`.
182
+ */
183
+ private foldSwitchBranch;
64
184
  private compileThrowValue;
65
185
  private enclosingTypeName;
66
186
  private dartTypeToTs;
67
187
  }
68
188
  /** Convenience: compile a Program directly. */
69
189
  export declare function compile(program: Program, options?: CompileOptions): string;
190
+ export interface CompileModuleOptions {
191
+ /** Include the runtime preamble at the top of the output. Default true. */
192
+ includePreamble?: boolean;
193
+ /** Output file path hint (affects ts-morph's internal resolution). */
194
+ fileName?: string;
195
+ /**
196
+ * Namespace/module name for the output (used in header comments).
197
+ * Defaults to the Module's name.
198
+ */
199
+ moduleName?: string;
200
+ }
201
+ /**
202
+ * Compile a Ball Module (not a Program) to a TypeScript ESM library.
203
+ *
204
+ * This handles the ball_protobuf use case: the input is a Module facade
205
+ * with `moduleImports[].inline.json` containing sub-modules. The output
206
+ * is a single TypeScript file with:
207
+ * - No main() function or top-level invocation
208
+ * - All public functions exported via `export function`
209
+ * - All types/classes exported via `export class`
210
+ *
211
+ * @param module - A Ball Module (with optional inline sub-modules)
212
+ * @param options - Compilation options
213
+ * @returns TypeScript source string with ESM exports
214
+ */
215
+ export declare function compileModule(module: Module, options?: CompileModuleOptions): string;
70
216
  //# sourceMappingURL=compiler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAOV,OAAO,EAIR,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,cAAc;IAC7B,2EAA2E;IAC3E,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAQD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAElC,+CAA+C;IAC/C,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,KAAK,CAAK;IAElB,4EAA4E;IAC5E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAE/C,mEAAmE;IACnE,OAAO,CAAC,kBAAkB,CAA0B;IAEpD,mEAAmE;IACnE,OAAO,CAAC,mBAAmB,CAA0B;IAErD,0EAA0E;IAC1E,OAAO,CAAC,uBAAuB,CAA0B;IAEzD,yEAAyE;IACzE,OAAO,CAAC,gBAAgB,CAA0B;IAElD,4DAA4D;IAC5D,OAAO,CAAC,aAAa,CAA0C;gBAEnD,OAAO,EAAE,OAAO;IAI5B,4BAA4B;IAC5B,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,MAAM;IAyF7C,OAAO,CAAC,gBAAgB;IAwBxB,OAAO,CAAC,SAAS;IAqJjB,OAAO,CAAC,SAAS;IAsDjB,OAAO,CAAC,WAAW;IAwBnB,OAAO,CAAC,WAAW;IAiBnB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,OAAO;IAIf,OAAO,KAAK,GAAG,GAEd;IAED,OAAO,CAAC,iBAAiB;IAmBzB,OAAO,CAAC,yBAAyB;IAoBjC,OAAO,CAAC,SAAS;IAmBjB,OAAO,CAAC,aAAa;IAmCrB,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,wBAAwB;IAyHhC,OAAO,CAAC,UAAU;IAiBlB,OAAO,CAAC,WAAW;IAsBnB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,WAAW;IAmFnB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,mBAAmB;IAc3B,OAAO,CAAC,IAAI;IA0BZ,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,sBAAsB;IAuD9B,OAAO,CAAC,yBAAyB;IAqBjC,OAAO,CAAC,sBAAsB;IAI9B,OAAO,CAAC,sBAAsB;IAe9B,OAAO,CAAC,aAAa;IAoBrB,OAAO,CAAC,WAAW;IA2CnB,OAAO,CAAC,cAAc;IAoRtB,OAAO,CAAC,WAAW;IAuBnB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,iBAAiB;IA6CzB,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,YAAY;CA8CrB;AAqOD,+CAA+C;AAC/C,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,MAAM,CAE1E"}
1
+ {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"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"}