@tilua/compiler 3.0.1 → 4.0.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/README.md +24 -3
- package/dist/{chunk-X4DM2BX2.js → chunk-VZ7XC4BG.js} +162 -59
- package/dist/cli.cjs +185 -83
- package/dist/cli.js +1 -1
- package/dist/index.cjs +192 -90
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/runtime/array.luau +5 -5
- package/runtime/object.luau +43 -4
- package/runtime/string.luau +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -12,7 +12,7 @@ var import_node_path3 = require("path");
|
|
|
12
12
|
// src/bundle.ts
|
|
13
13
|
var import_node_fs2 = require("fs");
|
|
14
14
|
var import_node_path2 = require("path");
|
|
15
|
-
var
|
|
15
|
+
var import_parser6 = require("@tilua/parser");
|
|
16
16
|
var import_luau_parser2 = require("luau-parser");
|
|
17
17
|
|
|
18
18
|
// src/config.ts
|
|
@@ -27,10 +27,11 @@ function resolveConfig(input, entry2, cwd = process.cwd()) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
// src/lower.ts
|
|
30
|
-
var
|
|
30
|
+
var import_parser4 = require("@tilua/parser");
|
|
31
31
|
var import_luau_parser = require("luau-parser");
|
|
32
32
|
|
|
33
33
|
// src/luau.ts
|
|
34
|
+
var import_parser2 = require("@tilua/parser");
|
|
34
35
|
var NO_SPAN = { line: { start: 0, end: 0 }, column: { start: 0, end: 0 } };
|
|
35
36
|
var span = () => ({ line: { ...NO_SPAN.line }, column: { ...NO_SPAN.column } });
|
|
36
37
|
var identifier = (name) => ({ type: "Identifier", name, ...span() });
|
|
@@ -38,7 +39,7 @@ var nil = () => ({ type: "NilLiteral", ...span() });
|
|
|
38
39
|
var boolean = (value) => ({ type: "BooleanLiteral", value, ...span() });
|
|
39
40
|
var number = (value, raw = String(value)) => ({ type: "NumberLiteral", value, raw, ...span() });
|
|
40
41
|
var string = (value) => ({ type: "StringLiteral", value, raw: "", ...span() });
|
|
41
|
-
var member = (object2, property) => isLuauName(property) ? { type: "MemberExpression", object: object2, property: identifier(property), ...span() } : index(object2, string(property));
|
|
42
|
+
var member = (object2, property) => (0, import_parser2.isLuauName)(property) ? { type: "MemberExpression", object: object2, property: identifier(property), ...span() } : index(object2, string(property));
|
|
42
43
|
var index = (object2, key) => ({ type: "IndexExpression", object: object2, index: key, ...span() });
|
|
43
44
|
var call = (callee, args2) => ({ type: "CallExpression", callee, arguments: args2, ...span() });
|
|
44
45
|
var methodCall = (object2, method, args2) => ({ type: "MethodCallExpression", object: object2, method: identifier(method), arguments: args2, ...span() });
|
|
@@ -47,7 +48,7 @@ var binary = (operator, left, right) => ({ type: "BinaryExpression", operator, l
|
|
|
47
48
|
var unary = (operator, argument) => ({ type: "UnaryExpression", operator, argument, ...span() });
|
|
48
49
|
var parenthesized = (expression) => ({ type: "ParenthesizedExpression", expression, ...span() });
|
|
49
50
|
var table = (fields) => ({ type: "TableExpression", fields, ...span() });
|
|
50
|
-
var field = (key, value) => isLuauName(key) ? { type: "TableFieldNamed", name: identifier(key), value } : { type: "TableFieldComputed", key: string(key), value };
|
|
51
|
+
var field = (key, value) => (0, import_parser2.isLuauName)(key) ? { type: "TableFieldNamed", name: identifier(key), value } : { type: "TableFieldComputed", key: string(key), value };
|
|
51
52
|
var block = (statements) => ({ type: "Block", statements, ...span() });
|
|
52
53
|
var local = (names, init) => ({
|
|
53
54
|
type: "LocalStatement",
|
|
@@ -90,32 +91,6 @@ var functionBody = (params, statements, hasVarargs = false) => ({
|
|
|
90
91
|
...span()
|
|
91
92
|
});
|
|
92
93
|
var localFunction = (name, func) => ({ type: "LocalFunctionStatement", name: identifier(name), func, ...span() });
|
|
93
|
-
var LUAU_KEYWORDS = /* @__PURE__ */ new Set([
|
|
94
|
-
"and",
|
|
95
|
-
"break",
|
|
96
|
-
"do",
|
|
97
|
-
"else",
|
|
98
|
-
"elseif",
|
|
99
|
-
"end",
|
|
100
|
-
"false",
|
|
101
|
-
"for",
|
|
102
|
-
"function",
|
|
103
|
-
"if",
|
|
104
|
-
"in",
|
|
105
|
-
"local",
|
|
106
|
-
"nil",
|
|
107
|
-
"not",
|
|
108
|
-
"or",
|
|
109
|
-
"repeat",
|
|
110
|
-
"return",
|
|
111
|
-
"then",
|
|
112
|
-
"true",
|
|
113
|
-
"until",
|
|
114
|
-
"while"
|
|
115
|
-
]);
|
|
116
|
-
function isLuauName(name) {
|
|
117
|
-
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) && !LUAU_KEYWORDS.has(name);
|
|
118
|
-
}
|
|
119
94
|
|
|
120
95
|
// src/names.ts
|
|
121
96
|
var Names = class _Names {
|
|
@@ -158,6 +133,7 @@ var Names = class _Names {
|
|
|
158
133
|
|
|
159
134
|
// src/language.ts
|
|
160
135
|
var import_node_fs = require("fs");
|
|
136
|
+
var import_parser3 = require("@tilua/parser");
|
|
161
137
|
var read = (name) => (0, import_node_fs.readFileSync)(new URL(`../runtime/${name}`, importMetaUrl), "utf8");
|
|
162
138
|
var implemented = (source) => new Set([...source.matchAll(/function __NAME__\.(\w+)/g)].map((m) => m[1]));
|
|
163
139
|
var array = read("array.luau");
|
|
@@ -191,7 +167,7 @@ function objectKeys(type) {
|
|
|
191
167
|
orders.push(keys);
|
|
192
168
|
}
|
|
193
169
|
const order = JSON.stringify(orders[0] ?? []);
|
|
194
|
-
const known = orders.length && orders.every((keys) => JSON.stringify(keys) === order) ? `{ ${orders[0].map((key) =>
|
|
170
|
+
const known = orders.length && orders.every((keys) => JSON.stringify(keys) === order) ? `{ ${orders[0].map((key) => (0, import_parser3.luauString)(key)).join(", ")} }` : "nil";
|
|
195
171
|
return { known, open: String(open) };
|
|
196
172
|
}
|
|
197
173
|
var plugin = {
|
|
@@ -250,11 +226,11 @@ var Lowerer = class {
|
|
|
250
226
|
origins = /* @__PURE__ */ new WeakMap();
|
|
251
227
|
run() {
|
|
252
228
|
const body = this.options.module ? this.module(this.options.module) : this.topLevel(this.source.body.statements);
|
|
229
|
+
const scriptArgs = this.usesScriptArgs && !this.options.module ? [local(["scriptArgs"], [this.array(table([{ type: "TableFieldPositional", value: vararg() }]))])] : [];
|
|
253
230
|
const helpers = this.helperDefinitions();
|
|
254
231
|
const captured = [...this.builtins].map(([global, local2]) => local([local2], [identifier(global)]));
|
|
255
|
-
const scriptArgs = this.usesScriptArgs && !this.options.module ? [local(["scriptArgs"], [table([{ type: "TableFieldPositional", value: vararg() }])])] : [];
|
|
256
232
|
return {
|
|
257
|
-
statements: [...
|
|
233
|
+
statements: [...captured, ...helpers, ...scriptArgs, ...this.classRuntimeStatements(), ...body],
|
|
258
234
|
exportsName: this.exportsName,
|
|
259
235
|
module: this.info,
|
|
260
236
|
diagnostics: this.diagnostics,
|
|
@@ -272,7 +248,7 @@ var Lowerer = class {
|
|
|
272
248
|
// --------------------------------------------------------
|
|
273
249
|
/** A tilua name as Luau can write it: `local` is a keyword there. */
|
|
274
250
|
name(name) {
|
|
275
|
-
if (!LUAU_KEYWORDS.has(name)) return name;
|
|
251
|
+
if (!import_parser2.LUAU_KEYWORDS.has(name)) return name;
|
|
276
252
|
let renamed = this.renamed.get(name);
|
|
277
253
|
if (!renamed) {
|
|
278
254
|
renamed = this.names.fresh(`${name}_`);
|
|
@@ -304,11 +280,36 @@ var Lowerer = class {
|
|
|
304
280
|
helper(kind) {
|
|
305
281
|
let name = this.helpers.get(kind);
|
|
306
282
|
if (!name) {
|
|
307
|
-
name = this.names.fresh(`tilua_${kind}`);
|
|
283
|
+
name = this.names.fresh(`tilua_${kind === "array" ? "asArray" : kind}`);
|
|
308
284
|
this.helpers.set(kind, name);
|
|
309
285
|
}
|
|
310
286
|
return identifier(name);
|
|
311
287
|
}
|
|
288
|
+
// `typeof v` tells an array from an object, which Luau cannot: both are
|
|
289
|
+
// tables. So every array the program makes — `[a, b]`, a rest parameter,
|
|
290
|
+
// what `:map` answers — is written down as one, in a table that holds its
|
|
291
|
+
// keys weakly, and `typeof` looks there. A table nothing wrote down (one a
|
|
292
|
+
// Lua library made) is told by its type where that says, and by its shape
|
|
293
|
+
// where it does not.
|
|
294
|
+
/** `value`, a new array, written down as one. */
|
|
295
|
+
array(value) {
|
|
296
|
+
const marked = call(this.helper("array"), [value]);
|
|
297
|
+
this.marked.add(marked);
|
|
298
|
+
return marked;
|
|
299
|
+
}
|
|
300
|
+
/** The calls `array` made: each answers the one table it was given. */
|
|
301
|
+
marked = /* @__PURE__ */ new WeakSet();
|
|
302
|
+
/** Luau reaching the record of arrays: the bundle's, or this file's own. */
|
|
303
|
+
arrays() {
|
|
304
|
+
return this.options.module?.arrays ?? this.helper("arrays").name;
|
|
305
|
+
}
|
|
306
|
+
typeofExpression(node) {
|
|
307
|
+
const known = this.options.types?.typeofTables.get(node);
|
|
308
|
+
let argument = node.argument;
|
|
309
|
+
while (argument.type === "ParenthesizedExpression") argument = argument.expression;
|
|
310
|
+
const value = this.expression(argument);
|
|
311
|
+
return call(this.helper("typeof"), known ? [value, string(known)] : [this.keptToOne(value)]);
|
|
312
|
+
}
|
|
312
313
|
/** The local a library's runtime table gets in this file, emitting it the
|
|
313
314
|
* first time something asks. Keyed by library and by the key the library
|
|
314
315
|
* itself chose, so two libraries' tables never collide. */
|
|
@@ -376,7 +377,7 @@ var Lowerer = class {
|
|
|
376
377
|
const type = this.options.types?.typeOf.get(argument);
|
|
377
378
|
return {
|
|
378
379
|
type,
|
|
379
|
-
typeText: type ? (0,
|
|
380
|
+
typeText: type ? (0, import_parser4.formatType)(type) : void 0,
|
|
380
381
|
source: this.writtenSource(argument),
|
|
381
382
|
declaration: declaration ? this.writtenSource(declaration) : void 0,
|
|
382
383
|
spread: argument.type === "SpreadElement"
|
|
@@ -503,6 +504,46 @@ var Lowerer = class {
|
|
|
503
504
|
}
|
|
504
505
|
helperDefinitions() {
|
|
505
506
|
const out2 = [];
|
|
507
|
+
const runtimes = this.runtimeDefinitions();
|
|
508
|
+
if (this.helpers.has("concat")) this.helper("array");
|
|
509
|
+
const marker = this.helpers.get("array");
|
|
510
|
+
const typeOf = this.helpers.get("typeof");
|
|
511
|
+
if (marker || typeOf) {
|
|
512
|
+
const arrays = this.arrays();
|
|
513
|
+
const own = this.helpers.get("arrays");
|
|
514
|
+
if (own) {
|
|
515
|
+
out2.push(...(0, import_luau_parser.parse)(`local ${own} = ${this.builtin("setmetatable").name}({}, { __mode = "k" })`).body.statements);
|
|
516
|
+
}
|
|
517
|
+
if (marker) {
|
|
518
|
+
out2.push(...(0, import_luau_parser.parse)(`
|
|
519
|
+
local function ${marker}(t)
|
|
520
|
+
${arrays}[t] = true
|
|
521
|
+
return t
|
|
522
|
+
end`).body.statements);
|
|
523
|
+
}
|
|
524
|
+
if (typeOf) {
|
|
525
|
+
out2.push(...(0, import_luau_parser.parse)(`
|
|
526
|
+
local function ${typeOf}(value, known)
|
|
527
|
+
local name = ${this.builtin(this.lua51 ? "type" : "typeof").name}(value)
|
|
528
|
+
if name ~= "table" then
|
|
529
|
+
return name
|
|
530
|
+
end
|
|
531
|
+
if known ~= nil then
|
|
532
|
+
return known
|
|
533
|
+
end
|
|
534
|
+
if ${arrays}[value] then
|
|
535
|
+
return "array"
|
|
536
|
+
end
|
|
537
|
+
if ${this.builtin("getmetatable").name}(value) ~= nil then
|
|
538
|
+
return "object"
|
|
539
|
+
end
|
|
540
|
+
if #value > 0 then
|
|
541
|
+
return "array"
|
|
542
|
+
end
|
|
543
|
+
return "object"
|
|
544
|
+
end`).body.statements);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
506
547
|
const assign2 = this.helpers.get("assign");
|
|
507
548
|
if (assign2) {
|
|
508
549
|
out2.push(localFunction(assign2, functionBody(["target"], [
|
|
@@ -517,25 +558,11 @@ var Lowerer = class {
|
|
|
517
558
|
returns([identifier("target")])
|
|
518
559
|
], true)));
|
|
519
560
|
}
|
|
520
|
-
|
|
521
|
-
const { plugin: plugin2, from } = this.lowerings[index2];
|
|
522
|
-
const source = plugin2.runtime?.[runtime2];
|
|
523
|
-
if (source === void 0) {
|
|
524
|
-
this.diagnostics.push({
|
|
525
|
-
message: `'${from}' asked for a runtime it does not have: '${runtime2}'`,
|
|
526
|
-
line: 1,
|
|
527
|
-
column: 1
|
|
528
|
-
});
|
|
529
|
-
continue;
|
|
530
|
-
}
|
|
531
|
-
const lines = this.options.module?.lines ?? "nil";
|
|
532
|
-
const fail = this.options.module?.fail ?? "nil";
|
|
533
|
-
out2.push(...(0, import_luau_parser.parse)(source.replace(/__NAME__/g, name).replace(/__LINES__/g, lines).replace(/__FAIL__/g, fail)).body.statements);
|
|
534
|
-
}
|
|
561
|
+
out2.push(...runtimes);
|
|
535
562
|
const concat = this.helpers.get("concat");
|
|
536
563
|
if (concat) {
|
|
537
564
|
out2.push(localFunction(concat, functionBody([], [
|
|
538
|
-
local(["result"], [table([])]),
|
|
565
|
+
local(["result"], [this.array(table([]))]),
|
|
539
566
|
numericFor("i", number(1), selectCount(this.builtin("select")), [
|
|
540
567
|
local(["part"], [call(this.builtin("select"), [identifier("i"), vararg()])]),
|
|
541
568
|
callStatement(call(member(this.builtin("table"), "move"), [
|
|
@@ -551,6 +578,26 @@ var Lowerer = class {
|
|
|
551
578
|
}
|
|
552
579
|
return out2;
|
|
553
580
|
}
|
|
581
|
+
/** The runtime tables the file's libraries were asked for. */
|
|
582
|
+
runtimeDefinitions() {
|
|
583
|
+
const out2 = [];
|
|
584
|
+
for (const { index: index2, runtime: runtime2, name } of this.loweringUsed) {
|
|
585
|
+
const { plugin: plugin2, from } = this.lowerings[index2];
|
|
586
|
+
const source = plugin2.runtime?.[runtime2];
|
|
587
|
+
if (source === void 0) {
|
|
588
|
+
this.diagnostics.push({
|
|
589
|
+
message: `'${from}' asked for a runtime it does not have: '${runtime2}'`,
|
|
590
|
+
line: 1,
|
|
591
|
+
column: 1
|
|
592
|
+
});
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
const lines = this.options.module?.lines ?? "nil";
|
|
596
|
+
const fail = this.options.module?.fail ?? "nil";
|
|
597
|
+
out2.push(...(0, import_luau_parser.parse)(source.replace(/__NAME__/g, name).replace(/__LINES__/g, lines).replace(/__FAIL__/g, fail).replace(/__ARRAY__/g, () => this.helper("array").name)).body.statements);
|
|
598
|
+
}
|
|
599
|
+
return out2;
|
|
600
|
+
}
|
|
554
601
|
// --------------------------------------------------------
|
|
555
602
|
// Modules
|
|
556
603
|
// --------------------------------------------------------
|
|
@@ -1303,7 +1350,7 @@ end
|
|
|
1303
1350
|
number(pattern.elements.length + 1),
|
|
1304
1351
|
unary("#", source),
|
|
1305
1352
|
number(1),
|
|
1306
|
-
table([])
|
|
1353
|
+
this.array(table([]))
|
|
1307
1354
|
]);
|
|
1308
1355
|
const rest = this.restTarget(pattern.rest, elements, mode, after);
|
|
1309
1356
|
after.push(...rest.then);
|
|
@@ -1357,7 +1404,7 @@ end
|
|
|
1357
1404
|
const names = [];
|
|
1358
1405
|
for (const p of params) {
|
|
1359
1406
|
if (p.rest) {
|
|
1360
|
-
prelude.push(local([this.name(p.name)], [table([{ type: "TableFieldPositional", value: vararg() }])]));
|
|
1407
|
+
prelude.push(local([this.name(p.name)], [this.array(table([{ type: "TableFieldPositional", value: vararg() }]))]));
|
|
1361
1408
|
continue;
|
|
1362
1409
|
}
|
|
1363
1410
|
const name = p.pattern ? this.names.fresh("arg") : this.name(p.name);
|
|
@@ -1415,6 +1462,7 @@ end
|
|
|
1415
1462
|
return binary(node.operator, left, right);
|
|
1416
1463
|
}
|
|
1417
1464
|
case "UnaryExpression":
|
|
1465
|
+
if (node.operator === "typeof") return this.typeofExpression(node);
|
|
1418
1466
|
return unary(node.operator, this.expression(node.argument));
|
|
1419
1467
|
case "SuperExpression":
|
|
1420
1468
|
return this.superClassReference(node);
|
|
@@ -1455,6 +1503,9 @@ end
|
|
|
1455
1503
|
callValues(node) {
|
|
1456
1504
|
const chain = optionalChain(node);
|
|
1457
1505
|
if (chain) return this.optionalChainExpression(chain);
|
|
1506
|
+
if (node.type === "CallExpression" && (node.callee.type === "SuperExpression" || node.callee.type === "MemberExpression" && node.callee.object.type === "SuperExpression")) {
|
|
1507
|
+
return this.expression(node);
|
|
1508
|
+
}
|
|
1458
1509
|
const object2 = linkObject(node);
|
|
1459
1510
|
if (node.type === "CallExpression" && object2.type === "Identifier") {
|
|
1460
1511
|
return this.link(node, this.reference(object2));
|
|
@@ -1466,7 +1517,7 @@ end
|
|
|
1466
1517
|
* kept to one there, `f((g()))`. Every value is taken only where the
|
|
1467
1518
|
* program asks for them, as an array's last element: `[g()]`. */
|
|
1468
1519
|
keptToOne(value) {
|
|
1469
|
-
return expandsToMany(value) ? parenthesized(value) : value;
|
|
1520
|
+
return expandsToMany(value) && !this.marked.has(value) ? parenthesized(value) : value;
|
|
1470
1521
|
}
|
|
1471
1522
|
/** Is this `scriptArgs` the language's own, not a name the file declared? */
|
|
1472
1523
|
isLanguageGlobal(node) {
|
|
@@ -1520,7 +1571,7 @@ end
|
|
|
1520
1571
|
...args2
|
|
1521
1572
|
]);
|
|
1522
1573
|
}
|
|
1523
|
-
if (!isLuauName(node.method.name)) {
|
|
1574
|
+
if (!import_parser2.isLuauName(node.method.name)) {
|
|
1524
1575
|
this.report(node.method, `'${node.method.name}' is a Luau keyword and cannot be called with ':'`);
|
|
1525
1576
|
}
|
|
1526
1577
|
return methodCall(object2, node.method.name, args2);
|
|
@@ -1747,7 +1798,7 @@ end
|
|
|
1747
1798
|
arrayExpression(node) {
|
|
1748
1799
|
const elements = packs(node.elements).items;
|
|
1749
1800
|
if (!elements.some((e) => e.type === "SpreadElement")) {
|
|
1750
|
-
return table(elements.map((e) => ({ type: "TableFieldPositional", value: this.expression(e) })));
|
|
1801
|
+
return this.array(table(elements.map((e) => ({ type: "TableFieldPositional", value: this.expression(e) }))));
|
|
1751
1802
|
}
|
|
1752
1803
|
const parts = [];
|
|
1753
1804
|
let current = [];
|
|
@@ -1882,7 +1933,7 @@ function topLevelDeclaration(statements, name) {
|
|
|
1882
1933
|
function moduleName(key) {
|
|
1883
1934
|
const last = key.split("/").filter(Boolean).pop() ?? "module";
|
|
1884
1935
|
const cleaned = last.replace(/[^A-Za-z0-9_]/g, "_");
|
|
1885
|
-
return /^[A-Za-z_]/.test(cleaned) && !LUAU_KEYWORDS.has(cleaned) ? cleaned : `module_${cleaned}`;
|
|
1936
|
+
return /^[A-Za-z_]/.test(cleaned) && !import_parser2.LUAU_KEYWORDS.has(cleaned) ? cleaned : `module_${cleaned}`;
|
|
1886
1937
|
}
|
|
1887
1938
|
function alwaysTruthy(value) {
|
|
1888
1939
|
switch (value.type) {
|
|
@@ -1997,6 +2048,7 @@ var CLASS_METAMETHODS = [
|
|
|
1997
2048
|
];
|
|
1998
2049
|
|
|
1999
2050
|
// src/linemap.ts
|
|
2051
|
+
var import_parser5 = require("@tilua/parser");
|
|
2000
2052
|
function statementsOf(node) {
|
|
2001
2053
|
const out2 = [];
|
|
2002
2054
|
const visit = (value) => {
|
|
@@ -2029,7 +2081,7 @@ function buildLineMap(emitted, printed, origins) {
|
|
|
2029
2081
|
}
|
|
2030
2082
|
function lineMapSource(map) {
|
|
2031
2083
|
const entries = [...map.entries()].sort((a, b) => a[0] - b[0]);
|
|
2032
|
-
const quoted = (s) =>
|
|
2084
|
+
const quoted = (s) => (0, import_parser5.luauString)(s.replace(/\\/g, "/"));
|
|
2033
2085
|
return `{${entries.map(([line, o]) => `[${line}]={${quoted(o.file)},${o.line}}`).join(",")}}`;
|
|
2034
2086
|
}
|
|
2035
2087
|
|
|
@@ -2078,13 +2130,13 @@ async function bundle(options) {
|
|
|
2078
2130
|
const parsed = parseFile(file, diagnostics, directives);
|
|
2079
2131
|
if (!parsed) return { modules: [], diagnostics };
|
|
2080
2132
|
const { program: program2, text } = parsed;
|
|
2081
|
-
const scopes = (0,
|
|
2133
|
+
const scopes = (0, import_parser6.analyzeScopes)(program2);
|
|
2082
2134
|
for (const d of scopes.diagnostics) {
|
|
2083
2135
|
diagnostics.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "scope" });
|
|
2084
2136
|
}
|
|
2085
2137
|
sources.set(file, { file, name: nameOf(file), text, program: program2, scopes });
|
|
2086
2138
|
for (const specifier of importedSpecifiers(program2)) {
|
|
2087
|
-
const target = (0,
|
|
2139
|
+
const target = (0, import_parser6.resolveModulePath)(file, specifier, config2);
|
|
2088
2140
|
if (target && !target.endsWith(".d.tilua")) queue.push(target);
|
|
2089
2141
|
}
|
|
2090
2142
|
}
|
|
@@ -2117,8 +2169,9 @@ async function bundle(options) {
|
|
|
2117
2169
|
require: requireExpression,
|
|
2118
2170
|
lines: `${G}.lines`,
|
|
2119
2171
|
fail: `${G}.fail`,
|
|
2172
|
+
arrays: `${G}.arrays`,
|
|
2120
2173
|
resolve: (specifier) => {
|
|
2121
|
-
const target = (0,
|
|
2174
|
+
const target = (0, import_parser6.resolveModulePath)(file, specifier, config2);
|
|
2122
2175
|
if (!target || target.endsWith(".d.tilua") || !sources.has(target)) return void 0;
|
|
2123
2176
|
pending.push(target);
|
|
2124
2177
|
return sources.get(target).name;
|
|
@@ -2140,14 +2193,14 @@ async function bundle(options) {
|
|
|
2140
2193
|
}
|
|
2141
2194
|
for (const [file, fileDirectives] of directives) {
|
|
2142
2195
|
const semantic = diagnostics.filter((d) => d.file === file && (d.category === "scope" || d.category === "type"));
|
|
2143
|
-
const { kept, unusedExpectErrors } = (0,
|
|
2196
|
+
const { kept, unusedExpectErrors } = (0, import_parser6.applyDirectives)(fileDirectives, semantic, (d) => d.line);
|
|
2144
2197
|
const suppressed = new Set(semantic.filter((d) => !kept.includes(d)));
|
|
2145
2198
|
const remaining = diagnostics.filter((d) => !suppressed.has(d));
|
|
2146
2199
|
diagnostics.length = 0;
|
|
2147
2200
|
diagnostics.push(...remaining);
|
|
2148
2201
|
if (options.typeCheck === false) continue;
|
|
2149
2202
|
for (const d of unusedExpectErrors) {
|
|
2150
|
-
diagnostics.push({ file, message:
|
|
2203
|
+
diagnostics.push({ file, message: import_parser6.UNUSED_EXPECT_ERROR, line: d.line, column: d.column, category: "type" });
|
|
2151
2204
|
}
|
|
2152
2205
|
}
|
|
2153
2206
|
const moduleProblems = new Set(diagnostics.filter((d) => d.category === "module").map((d) => `${d.file}:${d.line}:${d.column}:${d.message}`));
|
|
@@ -2162,6 +2215,7 @@ async function bundle(options) {
|
|
|
2162
2215
|
const assignment = program.body.statements[1];
|
|
2163
2216
|
if ([...modules.values()].some((m) => m.usesScriptArgs)) {
|
|
2164
2217
|
program.body.statements.unshift(local(["scriptArgs"], [table([{ type: "TableFieldPositional", value: { type: "VarargExpression" } }])]));
|
|
2218
|
+
program.body.statements.push(...(0, import_luau_parser2.parse)(`${G}.arrays[scriptArgs] = true`).body.statements);
|
|
2165
2219
|
}
|
|
2166
2220
|
const modulesTable = assignment.type === "AssignmentStatement" && assignment.values[0].type === "TableExpression" ? assignment.values[0].fields.find((f) => f.type === "TableFieldNamed" && f.name.name === "modules")?.value : void 0;
|
|
2167
2221
|
if (!modulesTable) throw new Error("@tilua/compiler: the bundle runtime has no modules table");
|
|
@@ -2183,7 +2237,7 @@ async function bundle(options) {
|
|
|
2183
2237
|
const result = names.fresh("result");
|
|
2184
2238
|
const entryStatements = (0, import_luau_parser2.parse)(`
|
|
2185
2239
|
local ${ok}, ${result} = xpcall(function()
|
|
2186
|
-
return ${G}.require(${
|
|
2240
|
+
return ${G}.require(${(0, import_parser6.luauString)(sources.get(entry2).name)})
|
|
2187
2241
|
end, ${G}.fail)
|
|
2188
2242
|
if not ${ok} then
|
|
2189
2243
|
error(${result}, 0)
|
|
@@ -2205,6 +2259,9 @@ function runtime(G) {
|
|
|
2205
2259
|
local ${G}
|
|
2206
2260
|
${G} = {
|
|
2207
2261
|
modules = {},
|
|
2262
|
+
-- Every array the program made, so \`typeof\` can tell one from an object.
|
|
2263
|
+
-- The keys are held weakly: being written down keeps no array alive.
|
|
2264
|
+
arrays = setmetatable({}, { __mode = "k" }),
|
|
2208
2265
|
-- Filled in just before the entry runs: bundle line -> { file, line }.
|
|
2209
2266
|
lines = nil,
|
|
2210
2267
|
-- What an error nothing caught says, in the project's files: the bundle's
|
|
@@ -2212,19 +2269,64 @@ ${G} = {
|
|
|
2212
2269
|
-- and a traceback of those places follows. A message that is not a string
|
|
2213
2270
|
-- is left as it is, and a position that is not this bundle's (a library
|
|
2214
2271
|
-- that raised its error with one already mapped) is left alone.
|
|
2272
|
+
--
|
|
2273
|
+
-- It is an \`xpcall\` handler, and a handler that fails loses the error
|
|
2274
|
+
-- altogether: Luau reports only "error in error handling". So the work is
|
|
2275
|
+
-- done under \`pcall\`, leaning on nothing it has not checked is there (an
|
|
2276
|
+
-- executor may lack \`debug.info\`, or refuse it), and an error it cannot
|
|
2277
|
+
-- map is still answered as Luau raised it, with why.
|
|
2215
2278
|
fail = function(message)
|
|
2279
|
+
-- Taken first, before anything that can fail. Level 3 is the frame
|
|
2280
|
+
-- that raised: past \`pcall\` and this handler.
|
|
2281
|
+
local trace
|
|
2282
|
+
if debug ~= nil and debug.traceback ~= nil then
|
|
2283
|
+
local ok, text = pcall(debug.traceback, "", 3)
|
|
2284
|
+
if ok and type(text) == "string" then
|
|
2285
|
+
trace = text
|
|
2286
|
+
end
|
|
2287
|
+
end
|
|
2288
|
+
local ok, described = pcall(${G}.describe, message, trace)
|
|
2289
|
+
if ok then
|
|
2290
|
+
return described
|
|
2291
|
+
end
|
|
2292
|
+
if type(message) ~= "string" then
|
|
2293
|
+
return message
|
|
2294
|
+
end
|
|
2295
|
+
local why = type(described) == "string" and described or "unknown"
|
|
2296
|
+
return message .. "\\n(tilua could not map this error to the project's files: " .. why .. ")" .. (trace or "")
|
|
2297
|
+
end,
|
|
2298
|
+
-- \`fail\`'s work. \`trace\` is a traceback from where the error was
|
|
2299
|
+
-- raised, or nil when none could be taken.
|
|
2300
|
+
describe = function(message, trace)
|
|
2216
2301
|
if type(message) ~= "string" then
|
|
2217
2302
|
return message
|
|
2218
2303
|
end
|
|
2219
2304
|
local lines = ${G}.lines
|
|
2220
2305
|
if lines == nil then
|
|
2221
|
-
return
|
|
2306
|
+
return message .. (trace or "")
|
|
2222
2307
|
end
|
|
2308
|
+
-- The bundle's name, as a position in a message spells it: where this
|
|
2309
|
+
-- function is, asked of the debug library by the function rather
|
|
2310
|
+
-- than by a level, which \`pcall\` would shift. Failing that, a message
|
|
2311
|
+
-- starts with the position it was raised at.
|
|
2223
2312
|
local chunk
|
|
2224
|
-
if debug.info ~= nil then
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2313
|
+
if debug ~= nil and debug.info ~= nil then
|
|
2314
|
+
local found, source = pcall(debug.info, ${G}.describe, "s")
|
|
2315
|
+
if found and type(source) == "string" then
|
|
2316
|
+
chunk = source
|
|
2317
|
+
end
|
|
2318
|
+
end
|
|
2319
|
+
if chunk == nil and debug ~= nil and debug.getinfo ~= nil then
|
|
2320
|
+
local found, info = pcall(debug.getinfo, ${G}.describe, "S")
|
|
2321
|
+
if found and type(info) == "table" and type(info.short_src) == "string" then
|
|
2322
|
+
chunk = info.short_src
|
|
2323
|
+
end
|
|
2324
|
+
end
|
|
2325
|
+
if chunk == nil then
|
|
2326
|
+
chunk = message:match("^(.-):%d+:")
|
|
2327
|
+
end
|
|
2328
|
+
if chunk == nil then
|
|
2329
|
+
return message .. (trace or "")
|
|
2228
2330
|
end
|
|
2229
2331
|
local prefix = chunk:gsub("%p", "%%%0")
|
|
2230
2332
|
local function place(line)
|
|
@@ -2238,7 +2340,7 @@ ${G} = {
|
|
|
2238
2340
|
return place(line)
|
|
2239
2341
|
end)
|
|
2240
2342
|
local frames = {}
|
|
2241
|
-
for frame in
|
|
2343
|
+
for frame in (trace or ""):gmatch("[^\\n]+") do
|
|
2242
2344
|
local line = frame:match(prefix .. ":(%d+)")
|
|
2243
2345
|
local at = line ~= nil and place(line) or nil
|
|
2244
2346
|
if at ~= nil then
|
|
@@ -2323,13 +2425,13 @@ function parseFile(file, diagnostics, directives) {
|
|
|
2323
2425
|
return void 0;
|
|
2324
2426
|
}
|
|
2325
2427
|
try {
|
|
2326
|
-
const program = (0,
|
|
2327
|
-
const found = (0,
|
|
2428
|
+
const program = (0, import_parser6.parse)(text);
|
|
2429
|
+
const found = (0, import_parser6.directivesOf)(text);
|
|
2328
2430
|
directives.set(file, found);
|
|
2329
2431
|
if (stamp !== void 0) parsedFiles.set(file, { stamp, program, text, directives: found });
|
|
2330
2432
|
return { program, text };
|
|
2331
2433
|
} catch (error) {
|
|
2332
|
-
if (error instanceof
|
|
2434
|
+
if (error instanceof import_parser6.ParseError || error instanceof import_parser6.LexError) {
|
|
2333
2435
|
const { line, column } = error;
|
|
2334
2436
|
diagnostics.push({ file, message: error.message, line, column, category: "syntax" });
|
|
2335
2437
|
return void 0;
|
|
@@ -2351,7 +2453,7 @@ function parsedLibrary(file) {
|
|
|
2351
2453
|
}
|
|
2352
2454
|
const cached = libraryCache.get(file);
|
|
2353
2455
|
if (cached && cached.stamp === stamp) return cached.program;
|
|
2354
|
-
const program = (0,
|
|
2456
|
+
const program = (0, import_parser6.parse)((0, import_node_fs2.readFileSync)(file, "utf8"));
|
|
2355
2457
|
libraryCache.set(file, { stamp, program });
|
|
2356
2458
|
return program;
|
|
2357
2459
|
}
|
|
@@ -2359,7 +2461,7 @@ var moduleAnalyses = /* @__PURE__ */ new Map();
|
|
|
2359
2461
|
function analyzeModules(modules, config2) {
|
|
2360
2462
|
const out2 = [];
|
|
2361
2463
|
const analyses = /* @__PURE__ */ new Map();
|
|
2362
|
-
const libraries = config2 ? (0,
|
|
2464
|
+
const libraries = config2 ? (0, import_parser6.resolveTypeLibraries)(config2) : { files: [], lowerings: [], problems: [] };
|
|
2363
2465
|
for (const p of libraries.problems) out2.push({ file: p.file, message: p.message, line: p.line ?? 1, column: p.column ?? 1, category: "config" });
|
|
2364
2466
|
const libs = libraries.files.map(parsedLibrary);
|
|
2365
2467
|
const globals = libs.flatMap((lib) => lib.body.statements.flatMap((s) => s.type === "DeclareStatement" ? [s.name] : []));
|
|
@@ -2379,14 +2481,14 @@ function analyzeModules(modules, config2) {
|
|
|
2379
2481
|
readWhile.set(file, remembered.read);
|
|
2380
2482
|
return remembered.result;
|
|
2381
2483
|
}
|
|
2382
|
-
const scopes = (0,
|
|
2484
|
+
const scopes = (0, import_parser6.analyzeScopes)(program, { builtinGlobals: globals, reportUndeclared: libs.length > 0 });
|
|
2383
2485
|
const said = [];
|
|
2384
2486
|
for (const d of scopes.diagnostics) {
|
|
2385
2487
|
if (d.kind !== "undeclared") continue;
|
|
2386
2488
|
said.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "type" });
|
|
2387
2489
|
}
|
|
2388
2490
|
const before = partials;
|
|
2389
|
-
const types = (0,
|
|
2491
|
+
const types = (0, import_parser6.analyzeTypes)(program, scopes, {
|
|
2390
2492
|
libs,
|
|
2391
2493
|
resolveModule: resolverFor(file),
|
|
2392
2494
|
reportUnknownTypes: libs.length > 0
|
|
@@ -2409,7 +2511,7 @@ function analyzeModules(modules, config2) {
|
|
|
2409
2511
|
return result;
|
|
2410
2512
|
};
|
|
2411
2513
|
const resolverFor = (file) => (specifier) => {
|
|
2412
|
-
const target = (0,
|
|
2514
|
+
const target = (0, import_parser6.resolveModulePath)(file, specifier, config2);
|
|
2413
2515
|
if (!target) return void 0;
|
|
2414
2516
|
const noteRead = () => {
|
|
2415
2517
|
const read2 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
|
|
@@ -2430,16 +2532,16 @@ function analyzeModules(modules, config2) {
|
|
|
2430
2532
|
let program = byFile.get(target)?.program;
|
|
2431
2533
|
if (!program) {
|
|
2432
2534
|
try {
|
|
2433
|
-
program = (0,
|
|
2535
|
+
program = (0, import_parser6.parse)((0, import_node_fs2.readFileSync)(target, "utf8"));
|
|
2434
2536
|
} catch {
|
|
2435
2537
|
return void 0;
|
|
2436
2538
|
}
|
|
2437
2539
|
}
|
|
2438
2540
|
inProgress.add(target);
|
|
2439
2541
|
try {
|
|
2440
|
-
const scopes = (0,
|
|
2441
|
-
const types = byFile.has(target) ? analyzeModule(target, program).types : (0,
|
|
2442
|
-
const exports2 = (0,
|
|
2542
|
+
const scopes = (0, import_parser6.analyzeScopes)(program, { builtinGlobals: globals });
|
|
2543
|
+
const types = byFile.has(target) ? analyzeModule(target, program).types : (0, import_parser6.analyzeTypes)(program, scopes, { libs, resolveModule: resolverFor(target), diagnostics: false });
|
|
2544
|
+
const exports2 = (0, import_parser6.moduleExports)(program, scopes, types, resolverFor(target));
|
|
2443
2545
|
exportsCache.set(target, exports2);
|
|
2444
2546
|
noteRead();
|
|
2445
2547
|
return exports2;
|