@tilua/compiler 2.4.0 → 3.0.1
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/{chunk-3ZQ3EITC.js → chunk-X4DM2BX2.js} +123 -53
- package/dist/cli.cjs +131 -57
- package/dist/cli.js +1 -1
- package/dist/index.cjs +131 -57
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +1 -1
- package/package.json +4 -3
- package/runtime/array.luau +179 -0
- package/runtime/object.luau +54 -0
- package/runtime/string.luau +119 -0
|
@@ -21,10 +21,10 @@ var nil = () => ({ type: "NilLiteral", ...span() });
|
|
|
21
21
|
var boolean = (value) => ({ type: "BooleanLiteral", value, ...span() });
|
|
22
22
|
var number = (value, raw = String(value)) => ({ type: "NumberLiteral", value, raw, ...span() });
|
|
23
23
|
var string = (value) => ({ type: "StringLiteral", value, raw: "", ...span() });
|
|
24
|
-
var member = (
|
|
25
|
-
var index = (
|
|
24
|
+
var member = (object2, property) => isLuauName(property) ? { type: "MemberExpression", object: object2, property: identifier(property), ...span() } : index(object2, string(property));
|
|
25
|
+
var index = (object2, key) => ({ type: "IndexExpression", object: object2, index: key, ...span() });
|
|
26
26
|
var call = (callee, args) => ({ type: "CallExpression", callee, arguments: args, ...span() });
|
|
27
|
-
var methodCall = (
|
|
27
|
+
var methodCall = (object2, method, args) => ({ type: "MethodCallExpression", object: object2, method: identifier(method), arguments: args, ...span() });
|
|
28
28
|
var functionExpression = (func) => ({ type: "FunctionExpression", func, ...span() });
|
|
29
29
|
var binary = (operator, left, right) => ({ type: "BinaryExpression", operator, left, right, ...span() });
|
|
30
30
|
var unary = (operator, argument) => ({ type: "UnaryExpression", operator, argument, ...span() });
|
|
@@ -140,6 +140,62 @@ var Names = class _Names {
|
|
|
140
140
|
}
|
|
141
141
|
};
|
|
142
142
|
|
|
143
|
+
// src/language.ts
|
|
144
|
+
import { readFileSync } from "fs";
|
|
145
|
+
var read = (name) => readFileSync(new URL(`../runtime/${name}`, import.meta.url), "utf8");
|
|
146
|
+
var implemented = (source) => new Set([...source.matchAll(/function __NAME__\.(\w+)/g)].map((m) => m[1]));
|
|
147
|
+
var array = read("array.luau");
|
|
148
|
+
var string2 = read("string.luau");
|
|
149
|
+
var object = read("object.luau");
|
|
150
|
+
var arrayMethods = implemented(array);
|
|
151
|
+
var stringMethods = implemented(string2);
|
|
152
|
+
var objectMethods = implemented(object);
|
|
153
|
+
function every(type, is, seen = /* @__PURE__ */ new Set()) {
|
|
154
|
+
if (!type || seen.has(type)) return false;
|
|
155
|
+
seen.add(type);
|
|
156
|
+
if (type.kind === "union") {
|
|
157
|
+
return type.types.filter((m) => !(m.kind === "primitive" && m.name === "nil")).every((m) => every(m, is, seen));
|
|
158
|
+
}
|
|
159
|
+
if (type.kind === "typeParam") return every(type.constraint, is, seen);
|
|
160
|
+
return is(type);
|
|
161
|
+
}
|
|
162
|
+
var isArray = (type) => every(type, (t) => t.kind === "array" || t.kind === "tuple");
|
|
163
|
+
var isString = (type) => every(type, (t) => t.kind === "primitive" && t.name === "string" || t.kind === "literal" && t.base === "string" || t.kind === "templateLiteral");
|
|
164
|
+
function objectKeys(type) {
|
|
165
|
+
const members = type?.kind === "union" ? type.types : type ? [type] : [];
|
|
166
|
+
const orders = [];
|
|
167
|
+
let open = false;
|
|
168
|
+
for (const member2 of members) {
|
|
169
|
+
const keys = [];
|
|
170
|
+
for (const part of member2.kind === "intersection" ? member2.types : [member2]) {
|
|
171
|
+
if (part.kind !== "object") continue;
|
|
172
|
+
for (const key of part.properties.keys()) if (!keys.includes(key)) keys.push(key);
|
|
173
|
+
if (part.indexer) open = true;
|
|
174
|
+
}
|
|
175
|
+
orders.push(keys);
|
|
176
|
+
}
|
|
177
|
+
const order = JSON.stringify(orders[0] ?? []);
|
|
178
|
+
const known = orders.length && orders.every((keys) => JSON.stringify(keys) === order) ? `{ ${orders[0].map((key) => JSON.stringify(key)).join(", ")} }` : "nil";
|
|
179
|
+
return { known, open: String(open) };
|
|
180
|
+
}
|
|
181
|
+
var plugin = {
|
|
182
|
+
runtime: { array, string: string2, object },
|
|
183
|
+
methodCall({ method, receiver, use }) {
|
|
184
|
+
if (isString(receiver)) {
|
|
185
|
+
return stringMethods.has(method) ? { callee: `${use("string")}.${method}` } : void 0;
|
|
186
|
+
}
|
|
187
|
+
if (isArray(receiver)) {
|
|
188
|
+
return arrayMethods.has(method) ? { callee: `${use("array")}.${method}` } : void 0;
|
|
189
|
+
}
|
|
190
|
+
if (objectMethods.has(method)) {
|
|
191
|
+
const keys = objectKeys(receiver);
|
|
192
|
+
return { callee: `${use("object")}.${method}`, prepend: [keys.known, keys.open] };
|
|
193
|
+
}
|
|
194
|
+
return void 0;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
var languageLowering = { plugin, from: "tilua" };
|
|
198
|
+
|
|
143
199
|
// src/lower.ts
|
|
144
200
|
function lower(program2, scopes, options = {}) {
|
|
145
201
|
return new Lowerer(program2, scopes, options).run();
|
|
@@ -323,21 +379,34 @@ var Lowerer = class {
|
|
|
323
379
|
if (id === void 0) return node.name;
|
|
324
380
|
return this.scopes.bindings.get(id)?.kind === "global" ? node.name : void 0;
|
|
325
381
|
}
|
|
326
|
-
/**
|
|
327
|
-
*
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
382
|
+
/** The language's own lowering, then each library's, in load order. A
|
|
383
|
+
* runtime is named by its lowering's place here (`loweringRuntime`). */
|
|
384
|
+
get lowerings() {
|
|
385
|
+
return this.allLowerings ??= [languageLowering, ...this.options.lowerings ?? []];
|
|
386
|
+
}
|
|
387
|
+
allLowerings;
|
|
388
|
+
/** What `receiver:method(...)` becomes, when Luau alone would not run it.
|
|
389
|
+
*
|
|
390
|
+
* A method the receiver's metatable gave it — the analyzer says so, and
|
|
391
|
+
* whose metatable it was (`methodSources`) — is asked of whoever declared
|
|
392
|
+
* that metatable, and only them: the language lowers its own
|
|
393
|
+
* (`names:filter(f)` becomes a call into its runtime), a library its own,
|
|
394
|
+
* and one the program declared is taken at its word. Any other method is
|
|
395
|
+
* a member of the receiver's own, which a library may still claim
|
|
396
|
+
* (`console:log`), the last library loaded first. No answer leaves an
|
|
397
|
+
* ordinary Luau method call: `text:upper()` reaches Lua's own. */
|
|
331
398
|
loweredMethodCall(node) {
|
|
332
|
-
const lowerings = this.
|
|
333
|
-
|
|
399
|
+
const lowerings = this.lowerings;
|
|
400
|
+
const source = this.options.types?.methodSources.get(node);
|
|
401
|
+
const asked = source?.origin === "language" ? [0] : source?.origin === "library" ? lowerings.flatMap((l, i) => l.library === source.library ? [i] : []) : source?.origin === "program" ? [] : lowerings.map((_, i) => i).slice(1).reverse();
|
|
402
|
+
if (!asked.some((i) => lowerings[i].plugin.methodCall)) return void 0;
|
|
334
403
|
const receiver = this.options.types?.typeOf.get(node.object);
|
|
335
404
|
const receiverGlobal = this.globalName(node.object);
|
|
336
405
|
const info = this.lazyArguments(node.arguments);
|
|
337
|
-
for (
|
|
338
|
-
const
|
|
339
|
-
if (!
|
|
340
|
-
const answer =
|
|
406
|
+
for (const i of asked) {
|
|
407
|
+
const plugin2 = lowerings[i].plugin;
|
|
408
|
+
if (!plugin2.methodCall) continue;
|
|
409
|
+
const answer = plugin2.methodCall({
|
|
341
410
|
method: node.method.name,
|
|
342
411
|
receiver,
|
|
343
412
|
receiverGlobal,
|
|
@@ -360,15 +429,15 @@ var Lowerer = class {
|
|
|
360
429
|
}
|
|
361
430
|
/** What a library says a call to a global — `print(x)` — is. */
|
|
362
431
|
loweredGlobalCall(node) {
|
|
363
|
-
const lowerings = this.
|
|
432
|
+
const lowerings = this.lowerings;
|
|
364
433
|
if (!lowerings?.some((l) => l.plugin.globalCall)) return void 0;
|
|
365
434
|
const name = this.globalName(node.callee);
|
|
366
435
|
if (name === void 0) return void 0;
|
|
367
436
|
const info = this.lazyArguments(node.arguments);
|
|
368
437
|
for (let i = lowerings.length - 1; i >= 0; i--) {
|
|
369
|
-
const
|
|
370
|
-
if (!
|
|
371
|
-
const answer =
|
|
438
|
+
const plugin2 = lowerings[i].plugin;
|
|
439
|
+
if (!plugin2.globalCall) continue;
|
|
440
|
+
const answer = plugin2.globalCall({
|
|
372
441
|
name,
|
|
373
442
|
at: this.callSite(node.callee),
|
|
374
443
|
get arguments() {
|
|
@@ -382,7 +451,7 @@ var Lowerer = class {
|
|
|
382
451
|
}
|
|
383
452
|
/** What a library says a global read as a value — `local p = print` — is. */
|
|
384
453
|
loweredGlobalValue(node) {
|
|
385
|
-
const lowerings = this.
|
|
454
|
+
const lowerings = this.lowerings;
|
|
386
455
|
if (!lowerings?.some((l) => l.plugin.globalValue)) return void 0;
|
|
387
456
|
const name = this.globalName(node);
|
|
388
457
|
if (name === void 0) return void 0;
|
|
@@ -412,7 +481,7 @@ var Lowerer = class {
|
|
|
412
481
|
try {
|
|
413
482
|
return parseExpressionFromSource(source);
|
|
414
483
|
} catch (error) {
|
|
415
|
-
this.report(node, `'${this.
|
|
484
|
+
this.report(node, `'${this.lowerings[index2].from}' lowered this to Luau that does not parse: ${JSON.stringify(source)} (${error.message})`);
|
|
416
485
|
return void 0;
|
|
417
486
|
}
|
|
418
487
|
}
|
|
@@ -433,8 +502,8 @@ var Lowerer = class {
|
|
|
433
502
|
], true)));
|
|
434
503
|
}
|
|
435
504
|
for (const { index: index2, runtime: runtime2, name } of this.loweringUsed) {
|
|
436
|
-
const { plugin, from } = this.
|
|
437
|
-
const source =
|
|
505
|
+
const { plugin: plugin2, from } = this.lowerings[index2];
|
|
506
|
+
const source = plugin2.runtime?.[runtime2];
|
|
438
507
|
if (source === void 0) {
|
|
439
508
|
this.diagnostics.push({
|
|
440
509
|
message: `'${from}' asked for a runtime it does not have: '${runtime2}'`,
|
|
@@ -769,6 +838,7 @@ var Lowerer = class {
|
|
|
769
838
|
case "ExportTypeAliasStatement":
|
|
770
839
|
case "DeclareStatement":
|
|
771
840
|
case "DeclareClassStatement":
|
|
841
|
+
case "DeclareMetatableStatement":
|
|
772
842
|
case "ErrorStatement":
|
|
773
843
|
return [];
|
|
774
844
|
case "VariableDeclaration":
|
|
@@ -1369,11 +1439,11 @@ end
|
|
|
1369
1439
|
callValues(node) {
|
|
1370
1440
|
const chain = optionalChain(node);
|
|
1371
1441
|
if (chain) return this.optionalChainExpression(chain);
|
|
1372
|
-
const
|
|
1373
|
-
if (node.type === "CallExpression" &&
|
|
1374
|
-
return this.link(node, this.reference(
|
|
1442
|
+
const object2 = linkObject(node);
|
|
1443
|
+
if (node.type === "CallExpression" && object2.type === "Identifier") {
|
|
1444
|
+
return this.link(node, this.reference(object2));
|
|
1375
1445
|
}
|
|
1376
|
-
return this.link(node, this.expression(
|
|
1446
|
+
return this.link(node, this.expression(object2));
|
|
1377
1447
|
}
|
|
1378
1448
|
/** A call is one value in tilua, and Luau would hand on every value one
|
|
1379
1449
|
* returns in the last place of an argument list or a `return`: it is
|
|
@@ -1412,16 +1482,16 @@ end
|
|
|
1412
1482
|
}))];
|
|
1413
1483
|
}
|
|
1414
1484
|
/** One link of an access chain, read from `object`. */
|
|
1415
|
-
link(node,
|
|
1485
|
+
link(node, object2) {
|
|
1416
1486
|
switch (node.type) {
|
|
1417
1487
|
case "MemberExpression":
|
|
1418
|
-
return member(
|
|
1488
|
+
return member(object2, node.property.name);
|
|
1419
1489
|
case "IndexExpression":
|
|
1420
|
-
return index(
|
|
1490
|
+
return index(object2, this.expression(node.index));
|
|
1421
1491
|
case "CallExpression": {
|
|
1422
1492
|
const lowered = this.loweredGlobalCall(node);
|
|
1423
1493
|
const args = this.arguments(node.arguments);
|
|
1424
|
-
if (!lowered) return call(
|
|
1494
|
+
if (!lowered) return call(object2, args);
|
|
1425
1495
|
return call(calleePath(lowered.callee), [...lowered.prepend, ...args]);
|
|
1426
1496
|
}
|
|
1427
1497
|
case "MethodCallExpression": {
|
|
@@ -1430,14 +1500,14 @@ end
|
|
|
1430
1500
|
if (lowered) {
|
|
1431
1501
|
return call(calleePath(lowered.callee), [
|
|
1432
1502
|
...lowered.prepend,
|
|
1433
|
-
...lowered.passReceiver ? [
|
|
1503
|
+
...lowered.passReceiver ? [object2] : [],
|
|
1434
1504
|
...args
|
|
1435
1505
|
]);
|
|
1436
1506
|
}
|
|
1437
1507
|
if (!isLuauName(node.method.name)) {
|
|
1438
1508
|
this.report(node.method, `'${node.method.name}' is a Luau keyword and cannot be called with ':'`);
|
|
1439
1509
|
}
|
|
1440
|
-
return methodCall(
|
|
1510
|
+
return methodCall(object2, node.method.name, args);
|
|
1441
1511
|
}
|
|
1442
1512
|
}
|
|
1443
1513
|
}
|
|
@@ -1458,8 +1528,8 @@ end
|
|
|
1458
1528
|
}
|
|
1459
1529
|
return out;
|
|
1460
1530
|
}
|
|
1461
|
-
applySegment(segment,
|
|
1462
|
-
return segment.reduce((value, link) => this.link(link, value),
|
|
1531
|
+
applySegment(segment, object2) {
|
|
1532
|
+
return segment.reduce((value, link) => this.link(link, value), object2);
|
|
1463
1533
|
}
|
|
1464
1534
|
/** `a?.b:m()` as a statement:
|
|
1465
1535
|
* `do local ref = a if ref ~= nil then ref.b:m() end end` */
|
|
@@ -1535,9 +1605,9 @@ end
|
|
|
1535
1605
|
const before = [];
|
|
1536
1606
|
let target;
|
|
1537
1607
|
if (node.target.type === "MemberExpression" || node.target.type === "IndexExpression") {
|
|
1538
|
-
const
|
|
1608
|
+
const object2 = this.expression(node.target.object);
|
|
1539
1609
|
const objectName = this.names.fresh("target");
|
|
1540
|
-
before.push(local([objectName], [
|
|
1610
|
+
before.push(local([objectName], [object2]));
|
|
1541
1611
|
if (node.target.type === "MemberExpression") {
|
|
1542
1612
|
target = member(identifier(objectName), node.target.property.name);
|
|
1543
1613
|
} else {
|
|
@@ -1918,15 +1988,15 @@ async function loadLowerings(modules) {
|
|
|
1918
1988
|
for (const module of modules) {
|
|
1919
1989
|
try {
|
|
1920
1990
|
const loaded = await import(pathToFileURL(module.file).href);
|
|
1921
|
-
const
|
|
1922
|
-
if (!
|
|
1991
|
+
const plugin2 = loaded.default;
|
|
1992
|
+
if (!plugin2 || typeof plugin2 !== "object") {
|
|
1923
1993
|
problems.push({
|
|
1924
1994
|
file: module.file,
|
|
1925
1995
|
message: `'${module.from}' lowering module has no default export`
|
|
1926
1996
|
});
|
|
1927
1997
|
continue;
|
|
1928
1998
|
}
|
|
1929
|
-
lowerings.push({ plugin, from: module.from });
|
|
1999
|
+
lowerings.push({ plugin: plugin2, from: module.from, library: module.library });
|
|
1930
2000
|
} catch (error) {
|
|
1931
2001
|
problems.push({
|
|
1932
2002
|
file: module.file,
|
|
@@ -1938,7 +2008,7 @@ async function loadLowerings(modules) {
|
|
|
1938
2008
|
}
|
|
1939
2009
|
|
|
1940
2010
|
// src/bundle.ts
|
|
1941
|
-
import { readFileSync, statSync } from "fs";
|
|
2011
|
+
import { readFileSync as readFileSync2, statSync } from "fs";
|
|
1942
2012
|
import { dirname, relative, resolve as resolve2 } from "path";
|
|
1943
2013
|
import {
|
|
1944
2014
|
parse,
|
|
@@ -2249,7 +2319,7 @@ function parseFile(file, diagnostics, directives) {
|
|
|
2249
2319
|
}
|
|
2250
2320
|
let text;
|
|
2251
2321
|
try {
|
|
2252
|
-
text =
|
|
2322
|
+
text = readFileSync2(file, "utf8");
|
|
2253
2323
|
} catch {
|
|
2254
2324
|
diagnostics.push({ file, message: "Cannot read file", line: 1, column: 1, category: "module" });
|
|
2255
2325
|
return void 0;
|
|
@@ -2283,7 +2353,7 @@ function parsedLibrary(file) {
|
|
|
2283
2353
|
}
|
|
2284
2354
|
const cached = libraryCache.get(file);
|
|
2285
2355
|
if (cached && cached.stamp === stamp) return cached.program;
|
|
2286
|
-
const program2 = parse(
|
|
2356
|
+
const program2 = parse(readFileSync2(file, "utf8"));
|
|
2287
2357
|
libraryCache.set(file, { stamp, program: program2 });
|
|
2288
2358
|
return program2;
|
|
2289
2359
|
}
|
|
@@ -2306,7 +2376,7 @@ function analyzeModules(modules, config) {
|
|
|
2306
2376
|
const done = analyzed.get(file);
|
|
2307
2377
|
if (done) return done;
|
|
2308
2378
|
const remembered = moduleAnalyses.get(file);
|
|
2309
|
-
if (remembered && remembered.setting === settingFor && libs.length === remembered.libs.length && remembered.libs.every((lib, i) => lib === libs[i]) && remembered.program === program2 && [...remembered.read].every(([
|
|
2379
|
+
if (remembered && remembered.setting === settingFor && libs.length === remembered.libs.length && remembered.libs.every((lib, i) => lib === libs[i]) && remembered.program === program2 && [...remembered.read].every(([read2, stamp]) => stampOf(read2) === stamp)) {
|
|
2310
2380
|
analyzed.set(file, remembered.result);
|
|
2311
2381
|
readWhile.set(file, remembered.read);
|
|
2312
2382
|
return remembered.result;
|
|
@@ -2330,12 +2400,12 @@ function analyzeModules(modules, config) {
|
|
|
2330
2400
|
const result = { types, diagnostics: said };
|
|
2331
2401
|
if (partials === before) {
|
|
2332
2402
|
analyzed.set(file, result);
|
|
2333
|
-
const
|
|
2403
|
+
const read2 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
|
|
2334
2404
|
const own = stampOf(file);
|
|
2335
|
-
if (own !== void 0)
|
|
2336
|
-
readWhile.set(file,
|
|
2337
|
-
if ([...
|
|
2338
|
-
moduleAnalyses.set(file, { setting: settingFor, libs: [...libs], program: program2, read, result });
|
|
2405
|
+
if (own !== void 0) read2.set(file, own);
|
|
2406
|
+
readWhile.set(file, read2);
|
|
2407
|
+
if ([...read2.values()].every((stamp) => stamp !== void 0)) {
|
|
2408
|
+
moduleAnalyses.set(file, { setting: settingFor, libs: [...libs], program: program2, read: read2, result });
|
|
2339
2409
|
}
|
|
2340
2410
|
}
|
|
2341
2411
|
return result;
|
|
@@ -2344,11 +2414,11 @@ function analyzeModules(modules, config) {
|
|
|
2344
2414
|
const target = resolveModulePath(file, specifier, config);
|
|
2345
2415
|
if (!target) return void 0;
|
|
2346
2416
|
const noteRead = () => {
|
|
2347
|
-
const
|
|
2348
|
-
readWhile.set(file,
|
|
2417
|
+
const read2 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
|
|
2418
|
+
readWhile.set(file, read2);
|
|
2349
2419
|
const stamp = stampOf(target);
|
|
2350
|
-
if (stamp !== void 0)
|
|
2351
|
-
for (const [below, belowStamp] of readWhile.get(target) ?? [])
|
|
2420
|
+
if (stamp !== void 0) read2.set(target, stamp);
|
|
2421
|
+
for (const [below, belowStamp] of readWhile.get(target) ?? []) read2.set(below, belowStamp);
|
|
2352
2422
|
};
|
|
2353
2423
|
if (inProgress.has(target)) {
|
|
2354
2424
|
partials++;
|
|
@@ -2362,7 +2432,7 @@ function analyzeModules(modules, config) {
|
|
|
2362
2432
|
let program2 = byFile.get(target)?.program;
|
|
2363
2433
|
if (!program2) {
|
|
2364
2434
|
try {
|
|
2365
|
-
program2 = parse(
|
|
2435
|
+
program2 = parse(readFileSync2(target, "utf8"));
|
|
2366
2436
|
} catch {
|
|
2367
2437
|
return void 0;
|
|
2368
2438
|
}
|