@tilua/compiler 3.1.0 → 5.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 +122 -12
- package/dist/{chunk-VANZBLCL.js → chunk-3J2QMA4W.js} +699 -135
- package/dist/cli.cjs +764 -178
- package/dist/cli.js +32 -9
- package/dist/index.cjs +721 -156
- package/dist/index.d.cts +44 -6
- package/dist/index.d.ts +44 -6
- package/dist/index.js +6 -4
- package/package.json +2 -2
- package/runtime/array.luau +17 -9
- package/runtime/console.luau +227 -0
- package/runtime/object.luau +4 -4
- package/runtime/result.luau +122 -0
- package/runtime/string.luau +2 -2
package/dist/index.cjs
CHANGED
|
@@ -33,9 +33,9 @@ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${_
|
|
|
33
33
|
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
34
34
|
|
|
35
35
|
// src/bundle.ts
|
|
36
|
-
var
|
|
36
|
+
var import_node_fs3 = require("fs");
|
|
37
37
|
var import_node_path2 = require("path");
|
|
38
|
-
var
|
|
38
|
+
var import_parser7 = require("@tilua/parser");
|
|
39
39
|
var import_luau_parser2 = require("luau-parser");
|
|
40
40
|
|
|
41
41
|
// src/config.ts
|
|
@@ -50,7 +50,7 @@ function resolveConfig(input, entry, cwd = process.cwd()) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// src/lower.ts
|
|
53
|
-
var
|
|
53
|
+
var import_parser5 = require("@tilua/parser");
|
|
54
54
|
var import_luau_parser = require("luau-parser");
|
|
55
55
|
|
|
56
56
|
// src/luau.ts
|
|
@@ -212,6 +212,77 @@ var plugin = {
|
|
|
212
212
|
};
|
|
213
213
|
var languageLowering = { plugin, from: "tilua" };
|
|
214
214
|
|
|
215
|
+
// src/standard.ts
|
|
216
|
+
var import_node_fs2 = require("fs");
|
|
217
|
+
var import_parser4 = require("@tilua/parser");
|
|
218
|
+
var read2 = (name) => (0, import_node_fs2.readFileSync)(new URL(`../runtime/${name}`, importMetaUrl), "utf8");
|
|
219
|
+
var console = read2("console.luau");
|
|
220
|
+
var result = read2("result.luau");
|
|
221
|
+
var place = (at) => (0, import_parser4.luauString)(at.file === void 0 ? `line ${at.line}` : `${at.file}:${at.line}`);
|
|
222
|
+
var tag = (at) => (0, import_parser4.luauString)(`[${at.file === void 0 ? `line ${at.line}` : `${at.file.replace(/\.tilua$/, "")}:${at.line}`}]:`);
|
|
223
|
+
function consoleMeta(args) {
|
|
224
|
+
const fields = [];
|
|
225
|
+
args.forEach((argument, index2) => {
|
|
226
|
+
if (argument.spread || argument.typeText === void 0 || !(0, import_parser4.isFunctionType)(argument.type)) return;
|
|
227
|
+
const written = argument.declaration ?? argument.source;
|
|
228
|
+
fields.push(`[${index2 + 1}] = { ${(0, import_parser4.luauString)(argument.typeText)}, ${(0, import_parser4.luauString)(written)} }`);
|
|
229
|
+
});
|
|
230
|
+
return fields.length ? `{ ${fields.join(", ")} }` : "nil";
|
|
231
|
+
}
|
|
232
|
+
var CONSOLE_METHODS = /* @__PURE__ */ new Set(["log", "warn", "error"]);
|
|
233
|
+
var REPLACED = /* @__PURE__ */ new Map([
|
|
234
|
+
["pcall", "pcall"],
|
|
235
|
+
["xpcall", "xpcall"],
|
|
236
|
+
["ypcall", "pcall"],
|
|
237
|
+
["coroutine.resume", "resume"],
|
|
238
|
+
["string.find", "find"],
|
|
239
|
+
["string.match", "match"],
|
|
240
|
+
["string.gsub", "gsub"],
|
|
241
|
+
["string.byte", "byte"],
|
|
242
|
+
["math.modf", "modf"],
|
|
243
|
+
["math.frexp", "frexp"],
|
|
244
|
+
["next", "next"],
|
|
245
|
+
["select", "select"],
|
|
246
|
+
["unpack", "unpack"],
|
|
247
|
+
["table.unpack", "unpack"],
|
|
248
|
+
["loadstring", "loadstring"],
|
|
249
|
+
["pairs", "pairs"],
|
|
250
|
+
["ipairs", "ipairs"],
|
|
251
|
+
["string.gmatch", "gmatch"]
|
|
252
|
+
]);
|
|
253
|
+
var plugin2 = {
|
|
254
|
+
runtime: { console, result },
|
|
255
|
+
methodCall({ method, receiverGlobal, at, arguments: args, use }) {
|
|
256
|
+
if (receiverGlobal === "console" && CONSOLE_METHODS.has(method)) {
|
|
257
|
+
return {
|
|
258
|
+
callee: `${use("console")}.${method}`,
|
|
259
|
+
prepend: [tag(at), consoleMeta(args)],
|
|
260
|
+
passReceiver: false
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return void 0;
|
|
264
|
+
},
|
|
265
|
+
// `print(x)` and `warn(x)` are left exactly as written: the place a line
|
|
266
|
+
// came from is what `console:log` / `console:warn` are for. `error` has to
|
|
267
|
+
// put the place inside the message it raises, so it goes through the
|
|
268
|
+
// runtime.
|
|
269
|
+
globalCall({ name, at, use }) {
|
|
270
|
+
if (name === "error") return { callee: `${use("console")}.raise`, prepend: [place(at)] };
|
|
271
|
+
const replaced = REPLACED.get(name);
|
|
272
|
+
if (replaced) return { callee: `${use("result")}.${replaced}` };
|
|
273
|
+
return void 0;
|
|
274
|
+
},
|
|
275
|
+
// Read as a value there is no call site to write down; the runtime finds
|
|
276
|
+
// one on the stack.
|
|
277
|
+
globalValue({ name, use }) {
|
|
278
|
+
if (name === "error") return `${use("console")}.raiseValue`;
|
|
279
|
+
const replaced = REPLACED.get(name);
|
|
280
|
+
if (replaced) return `${use("result")}.${replaced}`;
|
|
281
|
+
return void 0;
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
var standardLowering = { plugin: plugin2, from: "tilua (standard library)" };
|
|
285
|
+
|
|
215
286
|
// src/lower.ts
|
|
216
287
|
function lower(program2, scopes, options = {}) {
|
|
217
288
|
return new Lowerer(program2, scopes, options).run();
|
|
@@ -250,11 +321,11 @@ var Lowerer = class {
|
|
|
250
321
|
origins = /* @__PURE__ */ new WeakMap();
|
|
251
322
|
run() {
|
|
252
323
|
const body = this.options.module ? this.module(this.options.module) : this.topLevel(this.source.body.statements);
|
|
324
|
+
const scriptArgs = this.usesScriptArgs && !this.options.module ? [local(["scriptArgs"], [this.array(table([{ type: "TableFieldPositional", value: vararg() }]))])] : [];
|
|
253
325
|
const helpers = this.helperDefinitions();
|
|
254
326
|
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
327
|
return {
|
|
257
|
-
statements: [...
|
|
328
|
+
statements: [...captured, ...helpers, ...scriptArgs, ...this.classRuntimeStatements(), ...body],
|
|
258
329
|
exportsName: this.exportsName,
|
|
259
330
|
module: this.info,
|
|
260
331
|
diagnostics: this.diagnostics,
|
|
@@ -301,14 +372,43 @@ var Lowerer = class {
|
|
|
301
372
|
}
|
|
302
373
|
return identifier(local2);
|
|
303
374
|
}
|
|
375
|
+
/** `table.move`. Lua 5.1 has none, and gets a function that does the same. */
|
|
376
|
+
move() {
|
|
377
|
+
return this.lua51 ? this.helper("move") : member(this.builtin("table"), "move");
|
|
378
|
+
}
|
|
304
379
|
helper(kind) {
|
|
305
380
|
let name = this.helpers.get(kind);
|
|
306
381
|
if (!name) {
|
|
307
|
-
name = this.names.fresh(`tilua_${kind}`);
|
|
382
|
+
name = this.names.fresh(`tilua_${kind === "array" ? "asArray" : kind}`);
|
|
308
383
|
this.helpers.set(kind, name);
|
|
309
384
|
}
|
|
310
385
|
return identifier(name);
|
|
311
386
|
}
|
|
387
|
+
// `typeof v` tells an array from an object, which Luau cannot: both are
|
|
388
|
+
// tables. So every array the program makes — `[a, b]`, a rest parameter,
|
|
389
|
+
// what `:map` answers — is written down as one, in a table that holds its
|
|
390
|
+
// keys weakly, and `typeof` looks there. A table nothing wrote down (one a
|
|
391
|
+
// Lua library made) is told by its type where that says, and by its shape
|
|
392
|
+
// where it does not.
|
|
393
|
+
/** `value`, a new array, written down as one. */
|
|
394
|
+
array(value) {
|
|
395
|
+
const marked = call(this.helper("array"), [value]);
|
|
396
|
+
this.marked.add(marked);
|
|
397
|
+
return marked;
|
|
398
|
+
}
|
|
399
|
+
/** The calls `array` made: each answers the one table it was given. */
|
|
400
|
+
marked = /* @__PURE__ */ new WeakSet();
|
|
401
|
+
/** Luau reaching the record of arrays: the bundle's, or this file's own. */
|
|
402
|
+
arrays() {
|
|
403
|
+
return this.options.module?.arrays ?? this.helper("arrays").name;
|
|
404
|
+
}
|
|
405
|
+
typeofExpression(node) {
|
|
406
|
+
const known = this.options.types?.typeofTables.get(node);
|
|
407
|
+
let argument = node.argument;
|
|
408
|
+
while (argument.type === "ParenthesizedExpression") argument = argument.expression;
|
|
409
|
+
const value = this.expression(argument);
|
|
410
|
+
return call(this.helper("typeof"), known ? [value, string(known)] : [this.keptToOne(value)]);
|
|
411
|
+
}
|
|
312
412
|
/** The local a library's runtime table gets in this file, emitting it the
|
|
313
413
|
* first time something asks. Keyed by library and by the key the library
|
|
314
414
|
* itself chose, so two libraries' tables never collide. */
|
|
@@ -376,7 +476,7 @@ var Lowerer = class {
|
|
|
376
476
|
const type = this.options.types?.typeOf.get(argument);
|
|
377
477
|
return {
|
|
378
478
|
type,
|
|
379
|
-
typeText: type ? (0,
|
|
479
|
+
typeText: type ? (0, import_parser5.formatType)(type) : void 0,
|
|
380
480
|
source: this.writtenSource(argument),
|
|
381
481
|
declaration: declaration ? this.writtenSource(declaration) : void 0,
|
|
382
482
|
spread: argument.type === "SpreadElement"
|
|
@@ -398,7 +498,11 @@ var Lowerer = class {
|
|
|
398
498
|
/** The language's own lowering, then each library's, in load order. A
|
|
399
499
|
* runtime is named by its lowering's place here (`loweringRuntime`). */
|
|
400
500
|
get lowerings() {
|
|
401
|
-
return this.allLowerings ??= [
|
|
501
|
+
return this.allLowerings ??= [
|
|
502
|
+
languageLowering,
|
|
503
|
+
...this.options.noLib ? [] : [standardLowering],
|
|
504
|
+
...this.options.lowerings ?? []
|
|
505
|
+
];
|
|
402
506
|
}
|
|
403
507
|
allLowerings;
|
|
404
508
|
/** What `receiver:method(...)` becomes, when Luau alone would not run it.
|
|
@@ -412,22 +516,28 @@ var Lowerer = class {
|
|
|
412
516
|
* (`console:log`), the last library loaded first. No answer leaves an
|
|
413
517
|
* ordinary Luau method call: `text:upper()` reaches Lua's own. */
|
|
414
518
|
loweredMethodCall(node) {
|
|
519
|
+
return this.loweredMethod(node.method.name, this.options.types?.methodSources.get(node), node.object, node.arguments, node.method, node);
|
|
520
|
+
}
|
|
521
|
+
/** `loweredMethodCall`, for a method named `method` of what `object`
|
|
522
|
+
* holds — called with `:` on it, or read off it with `.` and handed its
|
|
523
|
+
* `self` like any other argument. `args` are the arguments after the
|
|
524
|
+
* receiver; a read that is not called has none. */
|
|
525
|
+
loweredMethod(method, source, object2, args, name, node) {
|
|
415
526
|
const lowerings = this.lowerings;
|
|
416
|
-
const source = this.options.types?.methodSources.get(node);
|
|
417
527
|
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();
|
|
418
528
|
if (!asked.some((i) => lowerings[i].plugin.methodCall)) return void 0;
|
|
419
|
-
const receiver = this.options.types?.typeOf.get(
|
|
420
|
-
const receiverGlobal = this.globalName(
|
|
421
|
-
const info = this.lazyArguments(
|
|
529
|
+
const receiver = this.options.types?.typeOf.get(object2);
|
|
530
|
+
const receiverGlobal = this.globalName(object2);
|
|
531
|
+
const info = this.lazyArguments(args);
|
|
422
532
|
for (const i of asked) {
|
|
423
|
-
const
|
|
424
|
-
if (!
|
|
425
|
-
const answer =
|
|
426
|
-
method
|
|
533
|
+
const plugin3 = lowerings[i].plugin;
|
|
534
|
+
if (!plugin3.methodCall) continue;
|
|
535
|
+
const answer = plugin3.methodCall({
|
|
536
|
+
method,
|
|
427
537
|
receiver,
|
|
428
538
|
receiverGlobal,
|
|
429
|
-
argumentCount:
|
|
430
|
-
at: this.callSite(
|
|
539
|
+
argumentCount: args.length,
|
|
540
|
+
at: this.callSite(name),
|
|
431
541
|
get arguments() {
|
|
432
542
|
return info();
|
|
433
543
|
},
|
|
@@ -451,9 +561,9 @@ var Lowerer = class {
|
|
|
451
561
|
if (name === void 0) return void 0;
|
|
452
562
|
const info = this.lazyArguments(node.arguments);
|
|
453
563
|
for (let i = lowerings.length - 1; i >= 0; i--) {
|
|
454
|
-
const
|
|
455
|
-
if (!
|
|
456
|
-
const answer =
|
|
564
|
+
const plugin3 = lowerings[i].plugin;
|
|
565
|
+
if (!plugin3.globalCall) continue;
|
|
566
|
+
const answer = plugin3.globalCall({
|
|
457
567
|
name,
|
|
458
568
|
at: this.callSite(node.callee),
|
|
459
569
|
get arguments() {
|
|
@@ -503,6 +613,114 @@ var Lowerer = class {
|
|
|
503
613
|
}
|
|
504
614
|
helperDefinitions() {
|
|
505
615
|
const out = [];
|
|
616
|
+
const runtimes = this.runtimeDefinitions();
|
|
617
|
+
if (this.helpers.has("concat")) this.helper("array");
|
|
618
|
+
if (this.helpers.has("concat")) this.move();
|
|
619
|
+
const move = this.helpers.get("move");
|
|
620
|
+
if (move) {
|
|
621
|
+
out.push(...(0, import_luau_parser.parse)(`
|
|
622
|
+
local function ${move}(from, first, last, at, to)
|
|
623
|
+
for i = first, last do
|
|
624
|
+
to[at + i - first] = from[i]
|
|
625
|
+
end
|
|
626
|
+
return to
|
|
627
|
+
end`).body.statements);
|
|
628
|
+
}
|
|
629
|
+
const each = this.helpers.get("each");
|
|
630
|
+
if (each) {
|
|
631
|
+
out.push(...(0, import_luau_parser.parse)(`
|
|
632
|
+
local function ${each}(source, ...)
|
|
633
|
+
if ${this.builtin("type").name}(source) == "function" then
|
|
634
|
+
return source, ...
|
|
635
|
+
end
|
|
636
|
+
local meta = ${this.builtin("getmetatable").name}(source)
|
|
637
|
+
if meta ~= nil and meta.__iter ~= nil then
|
|
638
|
+
return meta.__iter(source)
|
|
639
|
+
end
|
|
640
|
+
return ${this.builtin("next").name}, source
|
|
641
|
+
end`).body.statements);
|
|
642
|
+
}
|
|
643
|
+
const marker = this.helpers.get("array");
|
|
644
|
+
const typeOf = this.helpers.get("typeof");
|
|
645
|
+
if (marker || typeOf) {
|
|
646
|
+
const arrays = this.arrays();
|
|
647
|
+
const own = this.helpers.get("arrays");
|
|
648
|
+
if (own) {
|
|
649
|
+
out.push(...(0, import_luau_parser.parse)(`local ${own} = ${this.builtin("setmetatable").name}({}, { __mode = "k" })`).body.statements);
|
|
650
|
+
}
|
|
651
|
+
if (marker) {
|
|
652
|
+
out.push(...(0, import_luau_parser.parse)(`
|
|
653
|
+
local function ${marker}(t)
|
|
654
|
+
${arrays}[t] = true
|
|
655
|
+
return t
|
|
656
|
+
end`).body.statements);
|
|
657
|
+
}
|
|
658
|
+
if (typeOf) {
|
|
659
|
+
out.push(...(0, import_luau_parser.parse)(`
|
|
660
|
+
local function ${typeOf}(value, known)
|
|
661
|
+
local name = ${this.builtin(this.lua51 ? "type" : "typeof").name}(value)
|
|
662
|
+
if name ~= "table" then
|
|
663
|
+
return name
|
|
664
|
+
end
|
|
665
|
+
if known ~= nil then
|
|
666
|
+
return known
|
|
667
|
+
end
|
|
668
|
+
if ${arrays}[value] then
|
|
669
|
+
return "array"
|
|
670
|
+
end
|
|
671
|
+
if ${this.builtin("getmetatable").name}(value) ~= nil then
|
|
672
|
+
return "object"
|
|
673
|
+
end
|
|
674
|
+
if #value > 0 then
|
|
675
|
+
return "array"
|
|
676
|
+
end
|
|
677
|
+
return "object"
|
|
678
|
+
end`).body.statements);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
const instanceOf = this.helpers.get("instanceof");
|
|
682
|
+
if (instanceOf) {
|
|
683
|
+
out.push(...(0, import_luau_parser.parse)(`
|
|
684
|
+
local function ${instanceOf}(value, class)
|
|
685
|
+
if ${this.builtin("type").name}(value) ~= "table" then
|
|
686
|
+
return false
|
|
687
|
+
end
|
|
688
|
+
local current = value.ClassObject
|
|
689
|
+
while current ~= nil do
|
|
690
|
+
if current == class then
|
|
691
|
+
return true
|
|
692
|
+
end
|
|
693
|
+
current = current.ParentClass
|
|
694
|
+
end
|
|
695
|
+
return false
|
|
696
|
+
end`).body.statements);
|
|
697
|
+
}
|
|
698
|
+
const accessors = this.helpers.get("accessors");
|
|
699
|
+
if (accessors) {
|
|
700
|
+
out.push(...(0, import_luau_parser.parse)(`
|
|
701
|
+
local function ${accessors}(target, getters, setters)
|
|
702
|
+
return ${this.builtin("setmetatable").name}(target, {
|
|
703
|
+
__index = function(self, key)
|
|
704
|
+
local get = getters[key]
|
|
705
|
+
if get ~= nil then
|
|
706
|
+
return get(self)
|
|
707
|
+
end
|
|
708
|
+
return nil
|
|
709
|
+
end,
|
|
710
|
+
__newindex = function(self, key, value)
|
|
711
|
+
local set = setters[key]
|
|
712
|
+
if set ~= nil then
|
|
713
|
+
set(self, value)
|
|
714
|
+
return
|
|
715
|
+
end
|
|
716
|
+
if getters[key] ~= nil then
|
|
717
|
+
${this.builtin("error").name}("Cannot assign to '" .. ${this.builtin("tostring").name}(key) .. "': it has a getter and no setter", 2)
|
|
718
|
+
end
|
|
719
|
+
${this.builtin("rawset").name}(self, key, value)
|
|
720
|
+
end,
|
|
721
|
+
})
|
|
722
|
+
end`).body.statements);
|
|
723
|
+
}
|
|
506
724
|
const assign2 = this.helpers.get("assign");
|
|
507
725
|
if (assign2) {
|
|
508
726
|
out.push(localFunction(assign2, functionBody(["target"], [
|
|
@@ -517,28 +735,14 @@ var Lowerer = class {
|
|
|
517
735
|
returns([identifier("target")])
|
|
518
736
|
], true)));
|
|
519
737
|
}
|
|
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
|
-
out.push(...(0, import_luau_parser.parse)(source.replace(/__NAME__/g, name).replace(/__LINES__/g, lines).replace(/__FAIL__/g, fail)).body.statements);
|
|
534
|
-
}
|
|
738
|
+
out.push(...runtimes);
|
|
535
739
|
const concat = this.helpers.get("concat");
|
|
536
740
|
if (concat) {
|
|
537
741
|
out.push(localFunction(concat, functionBody([], [
|
|
538
|
-
local(["result"], [table([])]),
|
|
742
|
+
local(["result"], [this.array(table([]))]),
|
|
539
743
|
numericFor("i", number(1), selectCount(this.builtin("select")), [
|
|
540
744
|
local(["part"], [call(this.builtin("select"), [identifier("i"), vararg()])]),
|
|
541
|
-
callStatement(call(
|
|
745
|
+
callStatement(call(this.move(), [
|
|
542
746
|
identifier("part"),
|
|
543
747
|
number(1),
|
|
544
748
|
unary("#", identifier("part")),
|
|
@@ -551,6 +755,26 @@ var Lowerer = class {
|
|
|
551
755
|
}
|
|
552
756
|
return out;
|
|
553
757
|
}
|
|
758
|
+
/** The runtime tables the file's libraries were asked for. */
|
|
759
|
+
runtimeDefinitions() {
|
|
760
|
+
const out = [];
|
|
761
|
+
for (const { index: index2, runtime: runtime2, name } of this.loweringUsed) {
|
|
762
|
+
const { plugin: plugin3, from } = this.lowerings[index2];
|
|
763
|
+
const source = plugin3.runtime?.[runtime2];
|
|
764
|
+
if (source === void 0) {
|
|
765
|
+
this.diagnostics.push({
|
|
766
|
+
message: `'${from}' asked for a runtime it does not have: '${runtime2}'`,
|
|
767
|
+
line: 1,
|
|
768
|
+
column: 1
|
|
769
|
+
});
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
const lines = this.options.module?.lines ?? "nil";
|
|
773
|
+
const fail = this.options.module?.fail ?? "nil";
|
|
774
|
+
out.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);
|
|
775
|
+
}
|
|
776
|
+
return out;
|
|
777
|
+
}
|
|
554
778
|
// --------------------------------------------------------
|
|
555
779
|
// Modules
|
|
556
780
|
// --------------------------------------------------------
|
|
@@ -631,6 +855,7 @@ var Lowerer = class {
|
|
|
631
855
|
names.push("default");
|
|
632
856
|
break;
|
|
633
857
|
case "ExportNamedStatement": {
|
|
858
|
+
if (statement.typeOnly) break;
|
|
634
859
|
if (statement.source) {
|
|
635
860
|
const key = resolve4(statement.source);
|
|
636
861
|
if (key === void 0) break;
|
|
@@ -714,6 +939,26 @@ var Lowerer = class {
|
|
|
714
939
|
for (let i = 0; i < locals.length; i += 100) declarations.push(local(locals.slice(i, i + 100), []));
|
|
715
940
|
return [...declarations, ...hoisted, ...requires, ...body];
|
|
716
941
|
}
|
|
942
|
+
/** `import("./x")`: the bundle's `import`, called with the module's name.
|
|
943
|
+
* It answers the module's exports table — what `import * as` names. */
|
|
944
|
+
importExpression(node) {
|
|
945
|
+
const context = this.options.module;
|
|
946
|
+
if (!context) {
|
|
947
|
+
this.report(node, "import() needs a bundle: build the project with @tilua/compiler");
|
|
948
|
+
return nil();
|
|
949
|
+
}
|
|
950
|
+
const source = node.source;
|
|
951
|
+
if (source.type !== "StringLiteral") {
|
|
952
|
+
this.report(source, "import() takes a string literal: which module it loads is decided when the project is built");
|
|
953
|
+
return nil();
|
|
954
|
+
}
|
|
955
|
+
const key = context.resolve(source.value, true);
|
|
956
|
+
if (key === void 0) {
|
|
957
|
+
this.report(source, `Cannot find module '${source.value}'`);
|
|
958
|
+
return nil();
|
|
959
|
+
}
|
|
960
|
+
return call(context.import ?? context.require, [string(key)]);
|
|
961
|
+
}
|
|
717
962
|
isRewritten(node) {
|
|
718
963
|
const binding = this.bindingByDeclaration.get(node);
|
|
719
964
|
return binding !== void 0 && this.rewrites.has(binding.id);
|
|
@@ -860,7 +1105,7 @@ var Lowerer = class {
|
|
|
860
1105
|
case "VariableDeclaration":
|
|
861
1106
|
return this.variableDeclaration(node, "declare");
|
|
862
1107
|
case "FunctionDeclaration":
|
|
863
|
-
return [{ ...localFunction(this.name(node.name.name), this.functionBody(node.func)), attributes: node.attributes }];
|
|
1108
|
+
return [{ ...localFunction(this.name(node.name.name), this.functionBody(node.func)), attributes: this.lua51 ? void 0 : node.attributes }];
|
|
864
1109
|
case "FunctionDeclarationStatement":
|
|
865
1110
|
return [this.functionDeclarationStatement(node)];
|
|
866
1111
|
case "ClassDeclaration":
|
|
@@ -891,8 +1136,13 @@ var Lowerer = class {
|
|
|
891
1136
|
// kept — Lua has no expression statement — and it cannot run
|
|
892
1137
|
// anything, since a call is a `CallStatement` instead, so dropping
|
|
893
1138
|
// it changes nothing but the noise.
|
|
894
|
-
case "ExpressionStatement":
|
|
895
|
-
|
|
1139
|
+
case "ExpressionStatement": {
|
|
1140
|
+
let expression = node.expression;
|
|
1141
|
+
while (expression.type === "ParenthesizedExpression") expression = expression.expression;
|
|
1142
|
+
if (expression.type !== "ImportExpression") return [];
|
|
1143
|
+
const call2 = this.importExpression(expression);
|
|
1144
|
+
return call2.type === "CallExpression" ? [this.from(callStatement(call2), node)] : [];
|
|
1145
|
+
}
|
|
896
1146
|
case "DoStatement":
|
|
897
1147
|
return [{ type: "DoStatement", body: this.block(node.body), ...spanOf(node) }];
|
|
898
1148
|
case "WhileStatement":
|
|
@@ -944,7 +1194,11 @@ var Lowerer = class {
|
|
|
944
1194
|
let source = this.expression(node.iterator);
|
|
945
1195
|
if (form.viaIter) source = methodCall(source, "__iter", []);
|
|
946
1196
|
const variables = form.walks === "values" ? [this.names.fresh("_"), item] : [item];
|
|
947
|
-
|
|
1197
|
+
let iterators = form.walks === "iteration" ? [call(this.builtin("unpack"), [source, number(1), number(3)])] : [source];
|
|
1198
|
+
if (this.lua51 && form.walks === "values") {
|
|
1199
|
+
const kind = form.viaIter ? void 0 : this.options.types?.typeOf.get(node.iterator)?.kind;
|
|
1200
|
+
iterators = [call(kind === "array" || kind === "tuple" ? this.builtin("ipairs") : this.helper("each"), [source])];
|
|
1201
|
+
}
|
|
948
1202
|
return [genericFor(
|
|
949
1203
|
variables,
|
|
950
1204
|
iterators,
|
|
@@ -1197,7 +1451,8 @@ end
|
|
|
1197
1451
|
},
|
|
1198
1452
|
isMethod,
|
|
1199
1453
|
func,
|
|
1200
|
-
|
|
1454
|
+
// `@native` and the like are Luau's.
|
|
1455
|
+
attributes: this.lua51 ? void 0 : node.attributes,
|
|
1201
1456
|
...spanOf(node)
|
|
1202
1457
|
};
|
|
1203
1458
|
}
|
|
@@ -1298,12 +1553,12 @@ end
|
|
|
1298
1553
|
if (element) bind(element.value, index(source, number(i + 1)), element.default);
|
|
1299
1554
|
});
|
|
1300
1555
|
if (pattern.rest) {
|
|
1301
|
-
const elements = call(
|
|
1556
|
+
const elements = call(this.move(), [
|
|
1302
1557
|
source,
|
|
1303
1558
|
number(pattern.elements.length + 1),
|
|
1304
1559
|
unary("#", source),
|
|
1305
1560
|
number(1),
|
|
1306
|
-
table([])
|
|
1561
|
+
this.array(table([]))
|
|
1307
1562
|
]);
|
|
1308
1563
|
const rest = this.restTarget(pattern.rest, elements, mode, after);
|
|
1309
1564
|
after.push(...rest.then);
|
|
@@ -1357,7 +1612,7 @@ end
|
|
|
1357
1612
|
const names = [];
|
|
1358
1613
|
for (const p of params) {
|
|
1359
1614
|
if (p.rest) {
|
|
1360
|
-
prelude.push(local([this.name(p.name)], [table([{ type: "TableFieldPositional", value: vararg() }])]));
|
|
1615
|
+
prelude.push(local([this.name(p.name)], [this.array(table([{ type: "TableFieldPositional", value: vararg() }]))]));
|
|
1361
1616
|
continue;
|
|
1362
1617
|
}
|
|
1363
1618
|
const name = p.pattern ? this.names.fresh("arg") : this.name(p.name);
|
|
@@ -1409,12 +1664,20 @@ end
|
|
|
1409
1664
|
case "ArrayExpression":
|
|
1410
1665
|
return this.arrayExpression(node);
|
|
1411
1666
|
case "BinaryExpression": {
|
|
1667
|
+
if (node.operator === "??") return this.nullish(node);
|
|
1412
1668
|
const left = this.expression(node.left);
|
|
1413
1669
|
const right = this.expression(node.right);
|
|
1670
|
+
if (node.operator === "in") {
|
|
1671
|
+
return parenthesized(binary("~=", index(this.keptToOne(right), this.keptToOne(left)), nil()));
|
|
1672
|
+
}
|
|
1673
|
+
if (node.operator === "instanceof") {
|
|
1674
|
+
return call(this.helper("instanceof"), [this.keptToOne(left), this.keptToOne(right)]);
|
|
1675
|
+
}
|
|
1414
1676
|
if (this.lua51 && node.operator === "//") return this.floorDivide(left, right);
|
|
1415
1677
|
return binary(node.operator, left, right);
|
|
1416
1678
|
}
|
|
1417
1679
|
case "UnaryExpression":
|
|
1680
|
+
if (node.operator === "typeof") return this.typeofExpression(node);
|
|
1418
1681
|
return unary(node.operator, this.expression(node.argument));
|
|
1419
1682
|
case "SuperExpression":
|
|
1420
1683
|
return this.superClassReference(node);
|
|
@@ -1436,6 +1699,8 @@ end
|
|
|
1436
1699
|
}
|
|
1437
1700
|
case "ParenthesizedExpression":
|
|
1438
1701
|
return parenthesized(this.expression(node.expression));
|
|
1702
|
+
case "ImportExpression":
|
|
1703
|
+
return this.importExpression(node);
|
|
1439
1704
|
// Types only: the value is the expression itself.
|
|
1440
1705
|
case "TypeAssertionExpression":
|
|
1441
1706
|
case "SatisfiesExpression":
|
|
@@ -1469,7 +1734,7 @@ end
|
|
|
1469
1734
|
* kept to one there, `f((g()))`. Every value is taken only where the
|
|
1470
1735
|
* program asks for them, as an array's last element: `[g()]`. */
|
|
1471
1736
|
keptToOne(value) {
|
|
1472
|
-
return expandsToMany(value) ? parenthesized(value) : value;
|
|
1737
|
+
return expandsToMany(value) && !this.marked.has(value) ? parenthesized(value) : value;
|
|
1473
1738
|
}
|
|
1474
1739
|
/** Is this `scriptArgs` the language's own, not a name the file declared? */
|
|
1475
1740
|
isLanguageGlobal(node) {
|
|
@@ -1489,7 +1754,7 @@ end
|
|
|
1489
1754
|
const last2 = list.length - 1;
|
|
1490
1755
|
return lowered.length && !many.has(list[last2]) ? [...lowered.slice(0, -1), this.keptToOne(lowered[last2])] : lowered;
|
|
1491
1756
|
}
|
|
1492
|
-
const unpack = (value) => call(member(this.builtin("table"), "unpack"), [value]);
|
|
1757
|
+
const unpack = (value) => call(this.lua51 ? this.builtin("unpack") : member(this.builtin("table"), "unpack"), [value]);
|
|
1493
1758
|
const last = list[list.length - 1];
|
|
1494
1759
|
if (spreads.length === 1 && last.type === "SpreadElement") {
|
|
1495
1760
|
return [...list.slice(0, -1).map((a) => this.expression(a)), unpack(this.expression(last.argument))];
|
|
@@ -1503,11 +1768,21 @@ end
|
|
|
1503
1768
|
/** One link of an access chain, read from `object`. */
|
|
1504
1769
|
link(node, object2) {
|
|
1505
1770
|
switch (node.type) {
|
|
1506
|
-
case "MemberExpression":
|
|
1771
|
+
case "MemberExpression": {
|
|
1772
|
+
if (this.options.types?.lengths.has(node)) return unary("#", object2);
|
|
1773
|
+
const source = this.options.types?.methodReads.get(node);
|
|
1774
|
+
const lowered = source && this.loweredMethod(node.property.name, source, node.object, [], node.property, node);
|
|
1775
|
+
if (lowered) {
|
|
1776
|
+
const forwarded = call(calleePath(lowered.callee), [...lowered.prepend, vararg()]);
|
|
1777
|
+
return functionExpression(functionBody(lowered.passReceiver ? [] : ["_"], [returns([forwarded])], true));
|
|
1778
|
+
}
|
|
1507
1779
|
return member(object2, node.property.name);
|
|
1780
|
+
}
|
|
1508
1781
|
case "IndexExpression":
|
|
1509
1782
|
return index(object2, this.expression(node.index));
|
|
1510
1783
|
case "CallExpression": {
|
|
1784
|
+
const method = this.methodCalled(node);
|
|
1785
|
+
if (method) return method;
|
|
1511
1786
|
const lowered = this.loweredGlobalCall(node);
|
|
1512
1787
|
const args = this.arguments(node.arguments);
|
|
1513
1788
|
if (!lowered) return call(object2, args);
|
|
@@ -1530,6 +1805,24 @@ end
|
|
|
1530
1805
|
}
|
|
1531
1806
|
}
|
|
1532
1807
|
}
|
|
1808
|
+
/** `names.filter(names, f)`: a method of the value's metatable, read with
|
|
1809
|
+
* `.` and handed its `self` as the first argument. Written as a call of
|
|
1810
|
+
* the function the method really is — `array.filter(names, f)` — where
|
|
1811
|
+
* whoever declared the metatable says so; `undefined` leaves an ordinary
|
|
1812
|
+
* call, which is right for a string's Lua methods (`text.upper(text)`
|
|
1813
|
+
* reaches Lua's own `string.upper`). */
|
|
1814
|
+
methodCalled(node) {
|
|
1815
|
+
const callee = node.callee;
|
|
1816
|
+
if (callee.type !== "MemberExpression") return void 0;
|
|
1817
|
+
const source = this.options.types?.methodReads.get(callee);
|
|
1818
|
+
if (!source) return void 0;
|
|
1819
|
+
const spreads = node.arguments.map((a, i) => a.type === "SpreadElement" ? i : -1).filter((i) => i >= 0);
|
|
1820
|
+
if (!node.arguments.length || spreads.includes(0) || spreads.some((i) => i !== node.arguments.length - 1)) return void 0;
|
|
1821
|
+
const lowered = this.loweredMethod(callee.property.name, source, callee.object, node.arguments.slice(1), callee.property, node);
|
|
1822
|
+
if (!lowered) return void 0;
|
|
1823
|
+
const [self, ...rest] = this.arguments(node.arguments);
|
|
1824
|
+
return call(calleePath(lowered.callee), [...lowered.prepend, ...lowered.passReceiver ? [self] : [], ...rest]);
|
|
1825
|
+
}
|
|
1533
1826
|
// --------------------------------------------------------
|
|
1534
1827
|
// Optional chains
|
|
1535
1828
|
// --------------------------------------------------------
|
|
@@ -1575,7 +1868,7 @@ end
|
|
|
1575
1868
|
const segments = this.segments(chain);
|
|
1576
1869
|
const base = this.expression(chain.base);
|
|
1577
1870
|
const last = chain.links[chain.links.length - 1];
|
|
1578
|
-
if (base.type === "Identifier" && segments.length === 1 && last.type !== "CallExpression" && last.type !== "MethodCallExpression") {
|
|
1871
|
+
if (!this.lua51 && base.type === "Identifier" && segments.length === 1 && last.type !== "CallExpression" && last.type !== "MethodCallExpression") {
|
|
1579
1872
|
return {
|
|
1580
1873
|
type: "IfElseExpression",
|
|
1581
1874
|
clauses: [{ condition: binary("==", base, nil()), body: nil() }],
|
|
@@ -1596,6 +1889,32 @@ end
|
|
|
1596
1889
|
const func = functionExpression(functionBody([], statements, passesVarargs));
|
|
1597
1890
|
return call(parenthesized(func), passesVarargs ? [vararg()] : []);
|
|
1598
1891
|
}
|
|
1892
|
+
/** `a ?? b`: `a`, or `b` where `a` is nil — read only then. A `false`
|
|
1893
|
+
* stays, which is what sets it apart from `a or b`. A name is tested
|
|
1894
|
+
* in place; anything else is read once, into a local. */
|
|
1895
|
+
nullish(node) {
|
|
1896
|
+
const left = this.keptToOne(this.expression(node.left));
|
|
1897
|
+
const right = this.keptToOne(this.expression(node.right));
|
|
1898
|
+
if (!this.lua51 && left.type === "Identifier") {
|
|
1899
|
+
return {
|
|
1900
|
+
type: "IfElseExpression",
|
|
1901
|
+
clauses: [{ condition: binary("~=", left, nil()), body: left }],
|
|
1902
|
+
alternate: right,
|
|
1903
|
+
line: left.line,
|
|
1904
|
+
column: left.column
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
const ref = this.names.fresh("value");
|
|
1908
|
+
const at = identifier(ref);
|
|
1909
|
+
const statements = [
|
|
1910
|
+
local([ref], [left]),
|
|
1911
|
+
ifThen(binary("~=", at, nil()), [returns([at])]),
|
|
1912
|
+
returns([right])
|
|
1913
|
+
];
|
|
1914
|
+
const passesVarargs = statements.some(usesVararg);
|
|
1915
|
+
const func = functionExpression(functionBody([], statements, passesVarargs));
|
|
1916
|
+
return call(parenthesized(func), passesVarargs ? [vararg()] : []);
|
|
1917
|
+
}
|
|
1599
1918
|
/** `` `${a} any` `` -> `("%s any"):format(tostring(a))` */
|
|
1600
1919
|
interpolatedString(node) {
|
|
1601
1920
|
let format = "";
|
|
@@ -1677,12 +1996,12 @@ end
|
|
|
1677
1996
|
}));
|
|
1678
1997
|
const alternate = this.expression(node.alternate);
|
|
1679
1998
|
if (clauses.every((c) => alwaysTruthy(c.body))) {
|
|
1680
|
-
let
|
|
1999
|
+
let result2 = alternate;
|
|
1681
2000
|
for (let i = clauses.length - 1; i >= 0; i--) {
|
|
1682
2001
|
const { condition, body } = clauses[i];
|
|
1683
|
-
|
|
2002
|
+
result2 = binary("or", binary("and", condition, body), result2);
|
|
1684
2003
|
}
|
|
1685
|
-
return parenthesized(
|
|
2004
|
+
return parenthesized(result2);
|
|
1686
2005
|
}
|
|
1687
2006
|
const statement = {
|
|
1688
2007
|
type: "IfStatement",
|
|
@@ -1739,18 +2058,27 @@ end
|
|
|
1739
2058
|
current = [];
|
|
1740
2059
|
parts.push(this.expression(f.argument));
|
|
1741
2060
|
break;
|
|
2061
|
+
case "TableFieldAccessor":
|
|
2062
|
+
break;
|
|
1742
2063
|
}
|
|
1743
2064
|
}
|
|
1744
|
-
|
|
1745
|
-
if (
|
|
1746
|
-
|
|
2065
|
+
let made;
|
|
2066
|
+
if (parts.length === 0) made = table(current);
|
|
2067
|
+
else {
|
|
2068
|
+
if (current.length) parts.push(table(current));
|
|
2069
|
+
made = call(this.helper("assign"), [table([]), ...parts]);
|
|
2070
|
+
}
|
|
2071
|
+
const accessors = node.fields.filter((f) => f.type === "TableFieldAccessor");
|
|
2072
|
+
if (!accessors.length) return made;
|
|
2073
|
+
const table2 = (kind) => table(accessors.filter((f) => f.kind === kind).map((f) => field(f.key.type === "Identifier" ? f.key.name : f.key.value, functionExpression(this.classFunctionBody(f.func)))));
|
|
2074
|
+
return call(this.helper("accessors"), [made, table2("get"), table2("set")]);
|
|
1747
2075
|
}
|
|
1748
2076
|
/** `[1, ...xs, 2]`. Without a spread it is a Luau sequence; with one, the
|
|
1749
2077
|
* runs of plain elements and the spread arrays are joined by `concat`. */
|
|
1750
2078
|
arrayExpression(node) {
|
|
1751
2079
|
const elements = packs(node.elements).items;
|
|
1752
2080
|
if (!elements.some((e) => e.type === "SpreadElement")) {
|
|
1753
|
-
return table(elements.map((e) => ({ type: "TableFieldPositional", value: this.expression(e) })));
|
|
2081
|
+
return this.array(table(elements.map((e) => ({ type: "TableFieldPositional", value: this.expression(e) }))));
|
|
1754
2082
|
}
|
|
1755
2083
|
const parts = [];
|
|
1756
2084
|
let current = [];
|
|
@@ -2000,7 +2328,7 @@ var CLASS_METAMETHODS = [
|
|
|
2000
2328
|
];
|
|
2001
2329
|
|
|
2002
2330
|
// src/linemap.ts
|
|
2003
|
-
var
|
|
2331
|
+
var import_parser6 = require("@tilua/parser");
|
|
2004
2332
|
function statementsOf(node) {
|
|
2005
2333
|
const out = [];
|
|
2006
2334
|
const visit = (value) => {
|
|
@@ -2033,7 +2361,7 @@ function buildLineMap(emitted, printed, origins) {
|
|
|
2033
2361
|
}
|
|
2034
2362
|
function lineMapSource(map) {
|
|
2035
2363
|
const entries = [...map.entries()].sort((a, b) => a[0] - b[0]);
|
|
2036
|
-
const quoted = (s) => (0,
|
|
2364
|
+
const quoted = (s) => (0, import_parser6.luauString)(s.replace(/\\/g, "/"));
|
|
2037
2365
|
return `{${entries.map(([line, o]) => `[${line}]={${quoted(o.file)},${o.line}}`).join(",")}}`;
|
|
2038
2366
|
}
|
|
2039
2367
|
|
|
@@ -2045,15 +2373,15 @@ async function loadLowerings(modules) {
|
|
|
2045
2373
|
for (const module2 of modules) {
|
|
2046
2374
|
try {
|
|
2047
2375
|
const loaded = await import((0, import_node_url.pathToFileURL)(module2.file).href);
|
|
2048
|
-
const
|
|
2049
|
-
if (!
|
|
2376
|
+
const plugin3 = loaded.default;
|
|
2377
|
+
if (!plugin3 || typeof plugin3 !== "object") {
|
|
2050
2378
|
problems.push({
|
|
2051
2379
|
file: module2.file,
|
|
2052
2380
|
message: `'${module2.from}' lowering module has no default export`
|
|
2053
2381
|
});
|
|
2054
2382
|
continue;
|
|
2055
2383
|
}
|
|
2056
|
-
lowerings.push({ plugin:
|
|
2384
|
+
lowerings.push({ plugin: plugin3, from: module2.from, library: module2.library });
|
|
2057
2385
|
} catch (error) {
|
|
2058
2386
|
problems.push({
|
|
2059
2387
|
file: module2.file,
|
|
@@ -2080,16 +2408,17 @@ async function bundle(options) {
|
|
|
2080
2408
|
const file = queue.shift();
|
|
2081
2409
|
if (sources.has(file)) continue;
|
|
2082
2410
|
const parsed = parseFile(file, diagnostics, directives);
|
|
2083
|
-
if (!parsed) return { modules: [], diagnostics };
|
|
2411
|
+
if (!parsed) return { files: [], modules: [], diagnostics };
|
|
2084
2412
|
const { program: program3, text } = parsed;
|
|
2085
|
-
const scopes = (0,
|
|
2413
|
+
const scopes = (0, import_parser7.analyzeScopes)(program3, { noLib: config?.noLib });
|
|
2086
2414
|
for (const d of scopes.diagnostics) {
|
|
2087
2415
|
diagnostics.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "scope" });
|
|
2088
2416
|
}
|
|
2089
2417
|
sources.set(file, { file, name: nameOf(file), text, program: program3, scopes });
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2418
|
+
const dynamic = (0, import_parser7.importExpressions)(program3).flatMap((node) => node.source.type === "StringLiteral" ? [node.source.value] : []);
|
|
2419
|
+
for (const specifier of [...importedSpecifiers(program3), ...dynamic]) {
|
|
2420
|
+
const target2 = (0, import_parser7.resolveModulePath)(file, specifier, config);
|
|
2421
|
+
if (target2 && !target2.endsWith(".d.tilua")) queue.push(target2);
|
|
2093
2422
|
}
|
|
2094
2423
|
}
|
|
2095
2424
|
const analysis = analyzeModules([...sources.values()], config);
|
|
@@ -2101,6 +2430,9 @@ async function bundle(options) {
|
|
|
2101
2430
|
const names = Names.from(...[...sources.values()].map((s) => s.program));
|
|
2102
2431
|
const G = names.fresh("G");
|
|
2103
2432
|
const requireExpression = member(identifier(G), "require");
|
|
2433
|
+
const importExpression = member(identifier(G), "import");
|
|
2434
|
+
const mode = options.output ?? config?.output ?? "single";
|
|
2435
|
+
const target = options.target ?? config?.target ?? "luau";
|
|
2104
2436
|
const modules = /* @__PURE__ */ new Map();
|
|
2105
2437
|
const origins = /* @__PURE__ */ new WeakMap();
|
|
2106
2438
|
const pending = [entry];
|
|
@@ -2109,106 +2441,235 @@ async function bundle(options) {
|
|
|
2109
2441
|
if (modules.has(file)) continue;
|
|
2110
2442
|
const source = sources.get(file);
|
|
2111
2443
|
const shown = (0, import_node_path2.relative)(root, file).replace(/\\/g, "/");
|
|
2112
|
-
const
|
|
2444
|
+
const imports = /* @__PURE__ */ new Set();
|
|
2445
|
+
const dynamicImports = /* @__PURE__ */ new Set();
|
|
2446
|
+
const lowered2 = lower(source.program, source.scopes, {
|
|
2113
2447
|
names,
|
|
2114
2448
|
types: analysis.types.get(file),
|
|
2115
2449
|
lowerings,
|
|
2116
|
-
target
|
|
2450
|
+
target,
|
|
2451
|
+
noLib: config?.noLib,
|
|
2117
2452
|
source: source.text,
|
|
2118
2453
|
file: shown,
|
|
2119
2454
|
module: {
|
|
2120
2455
|
name: source.name,
|
|
2121
2456
|
require: requireExpression,
|
|
2122
|
-
|
|
2457
|
+
import: importExpression,
|
|
2458
|
+
// One file has one line map. Several have one each, and a
|
|
2459
|
+
// module reads the one of the file it is in.
|
|
2460
|
+
lines: mode === "single" ? `${G}.lines` : `${G}.maps[${(0, import_parser7.luauString)(source.name)}]`,
|
|
2123
2461
|
fail: `${G}.fail`,
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2462
|
+
arrays: `${G}.arrays`,
|
|
2463
|
+
resolve: (specifier, dynamic) => {
|
|
2464
|
+
const found = (0, import_parser7.resolveModulePath)(file, specifier, config);
|
|
2465
|
+
if (!found || found.endsWith(".d.tilua") || !sources.has(found)) return void 0;
|
|
2466
|
+
pending.push(found);
|
|
2467
|
+
const name = sources.get(found).name;
|
|
2468
|
+
(dynamic ? dynamicImports : imports).add(name);
|
|
2469
|
+
return name;
|
|
2129
2470
|
}
|
|
2130
2471
|
}
|
|
2131
2472
|
});
|
|
2132
|
-
for (const d of
|
|
2133
|
-
for (const statement of allStatements(
|
|
2134
|
-
const line =
|
|
2473
|
+
for (const d of lowered2.diagnostics) diagnostics.push({ file, ...d, category: "module" });
|
|
2474
|
+
for (const statement of allStatements(lowered2.statements)) {
|
|
2475
|
+
const line = lowered2.origins.get(statement);
|
|
2135
2476
|
if (line !== void 0) origins.set(statement, { file: shown, line });
|
|
2136
2477
|
}
|
|
2137
2478
|
modules.set(file, {
|
|
2138
2479
|
name: source.name,
|
|
2139
|
-
statements:
|
|
2140
|
-
exportsName:
|
|
2141
|
-
info:
|
|
2142
|
-
usesScriptArgs:
|
|
2480
|
+
statements: lowered2.statements,
|
|
2481
|
+
exportsName: lowered2.exportsName,
|
|
2482
|
+
info: lowered2.module,
|
|
2483
|
+
usesScriptArgs: lowered2.usesScriptArgs,
|
|
2484
|
+
imports,
|
|
2485
|
+
dynamicImports
|
|
2143
2486
|
});
|
|
2144
2487
|
}
|
|
2145
2488
|
for (const [file, fileDirectives] of directives) {
|
|
2146
2489
|
const semantic = diagnostics.filter((d) => d.file === file && (d.category === "scope" || d.category === "type"));
|
|
2147
|
-
const { kept, unusedExpectErrors } = (0,
|
|
2490
|
+
const { kept, unusedExpectErrors } = (0, import_parser7.applyDirectives)(fileDirectives, semantic, (d) => d.line);
|
|
2148
2491
|
const suppressed = new Set(semantic.filter((d) => !kept.includes(d)));
|
|
2149
2492
|
const remaining = diagnostics.filter((d) => !suppressed.has(d));
|
|
2150
2493
|
diagnostics.length = 0;
|
|
2151
2494
|
diagnostics.push(...remaining);
|
|
2152
2495
|
if (options.typeCheck === false) continue;
|
|
2153
2496
|
for (const d of unusedExpectErrors) {
|
|
2154
|
-
diagnostics.push({ file, message:
|
|
2497
|
+
diagnostics.push({ file, message: import_parser7.UNUSED_EXPECT_ERROR, line: d.line, column: d.column, category: "type" });
|
|
2155
2498
|
}
|
|
2156
2499
|
}
|
|
2157
2500
|
const moduleProblems = new Set(diagnostics.filter((d) => d.category === "module").map((d) => `${d.file}:${d.line}:${d.column}:${d.message}`));
|
|
2158
2501
|
const reported = diagnostics.filter((d) => d.category !== "type" || !moduleProblems.has(`${d.file}:${d.line}:${d.column}:${d.message}`));
|
|
2159
2502
|
diagnostics.length = 0;
|
|
2160
2503
|
diagnostics.push(...reported);
|
|
2161
|
-
const
|
|
2504
|
+
const lowered = [...modules.values()];
|
|
2505
|
+
const moduleNames = lowered.map((m) => m.name);
|
|
2162
2506
|
if (diagnostics.some((d) => d.category === "syntax" || d.category === "scope" || d.category === "module")) {
|
|
2163
|
-
return { modules: moduleNames, diagnostics };
|
|
2164
|
-
}
|
|
2165
|
-
const
|
|
2507
|
+
return { files: [], modules: moduleNames, diagnostics };
|
|
2508
|
+
}
|
|
2509
|
+
const several = mode !== "single";
|
|
2510
|
+
const layout = layoutOf(mode, modules.get(entry), lowered, options.outFile);
|
|
2511
|
+
const usesScriptArgs = lowered.some((m) => m.usesScriptArgs);
|
|
2512
|
+
const elsewhere = /* @__PURE__ */ new Map();
|
|
2513
|
+
for (const file of layout.slice(1)) {
|
|
2514
|
+
for (const module2 of file.modules) elsewhere.set(module2.name, loadPath(layout[0].path, file.path, target));
|
|
2515
|
+
}
|
|
2516
|
+
const program2 = (0, import_luau_parser2.parse)(runtime(G, {
|
|
2517
|
+
several,
|
|
2518
|
+
target,
|
|
2519
|
+
files: elsewhere,
|
|
2520
|
+
imports: lowered.some((m) => m.dynamicImports.size > 0)
|
|
2521
|
+
}));
|
|
2166
2522
|
const assignment = program2.body.statements[1];
|
|
2167
|
-
if (
|
|
2523
|
+
if (usesScriptArgs) {
|
|
2168
2524
|
program2.body.statements.unshift(local(["scriptArgs"], [table([{ type: "TableFieldPositional", value: { type: "VarargExpression" } }])]));
|
|
2525
|
+
program2.body.statements.push(...(0, import_luau_parser2.parse)(`${G}.arrays[scriptArgs] = true`).body.statements);
|
|
2526
|
+
if (several) program2.body.statements.push(...(0, import_luau_parser2.parse)(`${G}.scriptArgs = scriptArgs`).body.statements);
|
|
2169
2527
|
}
|
|
2170
2528
|
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;
|
|
2171
2529
|
if (!modulesTable) throw new Error("@tilua/compiler: the bundle runtime has no modules table");
|
|
2172
|
-
for (const module2 of modules
|
|
2173
|
-
|
|
2174
|
-
const fields = [];
|
|
2175
|
-
if (exported.length) fields.push(field("names", table(exported.map((n) => field(n, boolean(true))))));
|
|
2176
|
-
if (links.length) {
|
|
2177
|
-
fields.push(field("links", table(links.map((link) => field(link.name, table([
|
|
2178
|
-
{ type: "TableFieldPositional", value: string(link.module) },
|
|
2179
|
-
...link.imported === void 0 ? [] : [{ type: "TableFieldPositional", value: string(link.imported) }]
|
|
2180
|
-
]))))));
|
|
2181
|
-
}
|
|
2182
|
-
if (stars.length) fields.push(field("stars", table(stars.map((star) => ({ type: "TableFieldPositional", value: string(star) })))));
|
|
2183
|
-
fields.push(field("load", functionExpression(functionBody([module2.exportsName], module2.statements, true))));
|
|
2184
|
-
modulesTable.fields.push({ type: "TableFieldComputed", key: string(module2.name), value: table(fields) });
|
|
2530
|
+
for (const module2 of layout[0].modules) {
|
|
2531
|
+
modulesTable.fields.push({ type: "TableFieldComputed", key: string(module2.name), value: moduleTable(module2) });
|
|
2185
2532
|
}
|
|
2186
2533
|
const ok = names.fresh("ok");
|
|
2187
|
-
const
|
|
2534
|
+
const result2 = names.fresh("result");
|
|
2188
2535
|
const entryStatements = (0, import_luau_parser2.parse)(`
|
|
2189
|
-
local ${ok}, ${
|
|
2190
|
-
return ${G}.require(${(0,
|
|
2536
|
+
local ${ok}, ${result2} = xpcall(function()
|
|
2537
|
+
return ${G}.require(${(0, import_parser7.luauString)(sources.get(entry).name)})
|
|
2191
2538
|
end, ${G}.fail)
|
|
2192
2539
|
if not ${ok} then
|
|
2193
|
-
error(${
|
|
2540
|
+
error(${result2}, 0)
|
|
2194
2541
|
end
|
|
2195
|
-
${modules.get(entry).info.exports ? `return ${
|
|
2542
|
+
${modules.get(entry).info.exports ? `return ${result2}` : ""}
|
|
2196
2543
|
`).body.statements;
|
|
2197
2544
|
program2.body.statements.push(...entryStatements);
|
|
2198
2545
|
const map = buildLineMap(program2, (0, import_luau_parser2.parse)((0, import_luau_parser2.print)(program2)), origins);
|
|
2199
|
-
if (map.size) {
|
|
2200
|
-
|
|
2546
|
+
if (map.size || several) {
|
|
2547
|
+
const mapStatements = [assign(
|
|
2201
2548
|
[member(identifier(G), "lines")],
|
|
2202
2549
|
[(0, import_luau_parser2.parseExpressionFromSource)(lineMapSource(map))]
|
|
2203
|
-
)
|
|
2550
|
+
)];
|
|
2551
|
+
if (several) mapStatements.push(...mapRegistration(G, layout[0], `${G}.lines`));
|
|
2552
|
+
program2.body.statements.splice(program2.body.statements.length - entryStatements.length, 0, ...mapStatements);
|
|
2204
2553
|
}
|
|
2205
|
-
|
|
2554
|
+
const files = [{ path: layout[0].path, code: (0, import_luau_parser2.print)(program2) + "\n", modules: layout[0].modules.map((m) => m.name) }];
|
|
2555
|
+
for (const file of layout.slice(1)) {
|
|
2556
|
+
const body = [];
|
|
2557
|
+
if (file.modules.some((m) => m.usesScriptArgs)) body.push(...(0, import_luau_parser2.parse)(`local scriptArgs = ${G}.scriptArgs`).body.statements);
|
|
2558
|
+
for (const module2 of file.modules) {
|
|
2559
|
+
body.push(assign(
|
|
2560
|
+
[index(member(identifier(G), "modules"), string(module2.name))],
|
|
2561
|
+
[moduleTable(module2)]
|
|
2562
|
+
));
|
|
2563
|
+
}
|
|
2564
|
+
const chunk = program([returns([functionExpression(functionBody([G], body))])]);
|
|
2565
|
+
const lines = buildLineMap(chunk, (0, import_luau_parser2.parse)((0, import_luau_parser2.print)(chunk)), origins);
|
|
2566
|
+
body.push(...mapRegistration(G, file, lineMapSource(lines)));
|
|
2567
|
+
files.push({ path: file.path, code: (0, import_luau_parser2.print)(chunk) + "\n", modules: file.modules.map((m) => m.name) });
|
|
2568
|
+
}
|
|
2569
|
+
return { code: files[0].code, files, modules: moduleNames, diagnostics };
|
|
2570
|
+
}
|
|
2571
|
+
function moduleTable(module2) {
|
|
2572
|
+
const { names: exported, links, stars } = module2.info;
|
|
2573
|
+
const fields = [];
|
|
2574
|
+
if (exported.length) fields.push(field("names", table(exported.map((n) => field(n, boolean(true))))));
|
|
2575
|
+
if (links.length) {
|
|
2576
|
+
fields.push(field("links", table(links.map((link) => field(link.name, table([
|
|
2577
|
+
{ type: "TableFieldPositional", value: string(link.module) },
|
|
2578
|
+
...link.imported === void 0 ? [] : [{ type: "TableFieldPositional", value: string(link.imported) }]
|
|
2579
|
+
]))))));
|
|
2580
|
+
}
|
|
2581
|
+
if (stars.length) fields.push(field("stars", table(stars.map((star) => ({ type: "TableFieldPositional", value: string(star) })))));
|
|
2582
|
+
fields.push(field("load", functionExpression(functionBody([module2.exportsName], module2.statements, true))));
|
|
2583
|
+
return table(fields);
|
|
2584
|
+
}
|
|
2585
|
+
function layoutOf(mode, entry, modules, outFile) {
|
|
2586
|
+
const extension = outFile !== void 0 && (0, import_node_path2.extname)(outFile) || ".luau";
|
|
2587
|
+
const entryStem = outFile !== void 0 ? (0, import_node_path2.basename)(outFile, (0, import_node_path2.extname)(outFile)) : import_node_path2.posix.basename(entry.name);
|
|
2588
|
+
const others = modules.filter((m) => m !== entry);
|
|
2589
|
+
if (mode === "single") return [{ path: `${entryStem}${extension}`, modules: [entry, ...others] }];
|
|
2590
|
+
if (mode === "modules") {
|
|
2591
|
+
const pathOf = (m) => `${m.name.split("/").map((part) => part === ".." ? "__" : part).join("/")}${extension}`;
|
|
2592
|
+
return [entry, ...others].map((m) => ({ path: pathOf(m), modules: [m] }));
|
|
2593
|
+
}
|
|
2594
|
+
const byName = new Map(modules.map((m) => [m.name, m]));
|
|
2595
|
+
const reach = (root) => {
|
|
2596
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2597
|
+
const stack = [root];
|
|
2598
|
+
while (stack.length) {
|
|
2599
|
+
const name = stack.pop();
|
|
2600
|
+
if (seen.has(name)) continue;
|
|
2601
|
+
seen.add(name);
|
|
2602
|
+
stack.push(...byName.get(name)?.imports ?? []);
|
|
2603
|
+
}
|
|
2604
|
+
return seen;
|
|
2605
|
+
};
|
|
2606
|
+
const inEntry = reach(entry.name);
|
|
2607
|
+
const roots = [...new Set(modules.flatMap((m) => [...m.dynamicImports]))].filter((root) => !inEntry.has(root));
|
|
2608
|
+
const reached = new Map(roots.map((root) => [root, reach(root)]));
|
|
2609
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2610
|
+
for (const module2 of others) {
|
|
2611
|
+
if (inEntry.has(module2.name)) continue;
|
|
2612
|
+
const by = roots.filter((root) => reached.get(root).has(module2.name));
|
|
2613
|
+
const key = by.join("\n");
|
|
2614
|
+
if (!groups.has(key)) groups.set(key, { roots: by, modules: [] });
|
|
2615
|
+
groups.get(key).modules.push(module2);
|
|
2616
|
+
}
|
|
2617
|
+
const files = [{ path: `${entryStem}${extension}`, modules: modules.filter((m) => inEntry.has(m.name)) }];
|
|
2618
|
+
const taken = /* @__PURE__ */ new Set([entryStem]);
|
|
2619
|
+
for (const group of groups.values()) {
|
|
2620
|
+
const label = group.roots.length === 1 ? group.roots[0].replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "chunk" : "shared";
|
|
2621
|
+
let stem = `${entryStem}_${label}`;
|
|
2622
|
+
for (let n = 2; taken.has(stem); n++) stem = `${entryStem}_${label}_${n}`;
|
|
2623
|
+
taken.add(stem);
|
|
2624
|
+
files.push({ path: `${stem}${extension}`, modules: group.modules });
|
|
2625
|
+
}
|
|
2626
|
+
return files;
|
|
2627
|
+
}
|
|
2628
|
+
function loadPath(entryPath, file, target) {
|
|
2629
|
+
const path = import_node_path2.posix.relative(import_node_path2.posix.dirname(entryPath), file);
|
|
2630
|
+
if (target === "lua51") return path;
|
|
2631
|
+
const bare = path.slice(0, path.length - import_node_path2.posix.extname(path).length);
|
|
2632
|
+
return bare.startsWith("../") ? bare : `./${bare}`;
|
|
2633
|
+
}
|
|
2634
|
+
function mapRegistration(G, file, map) {
|
|
2635
|
+
const extension = import_node_path2.posix.extname(file.path);
|
|
2636
|
+
const parts = file.path.slice(0, file.path.length - extension.length).split(/[/.]/).filter((part) => part !== "");
|
|
2637
|
+
const entry = `${G}.sources[#${G}.sources + 1]`;
|
|
2638
|
+
return (0, import_luau_parser2.parse)([
|
|
2639
|
+
`${entry} = { { ${parts.map(import_parser7.luauString).join(", ")} }, ${map}, ${(0, import_parser7.luauString)(extension.slice(1))} }`,
|
|
2640
|
+
...file.modules.map((module2) => `${G}.maps[${(0, import_parser7.luauString)(module2.name)}] = ${G}.sources[#${G}.sources][2]`)
|
|
2641
|
+
].join("\n")).body.statements;
|
|
2206
2642
|
}
|
|
2207
|
-
function
|
|
2643
|
+
function loader(G, target) {
|
|
2644
|
+
if (target === "lua51") {
|
|
2645
|
+
return `
|
|
2646
|
+
-- From beside this file, wherever that is: its own path says where.
|
|
2647
|
+
local folder = ""
|
|
2648
|
+
if debug ~= nil and debug.getinfo ~= nil then
|
|
2649
|
+
local at = debug.getinfo(${G}.load, "S").source
|
|
2650
|
+
if at:sub(1, 1) == "@" then
|
|
2651
|
+
folder = at:match("^@(.*[/\\\\])") or ""
|
|
2652
|
+
end
|
|
2653
|
+
end
|
|
2654
|
+
local chunk, problem = loadfile(folder .. file)
|
|
2655
|
+
if chunk == nil then
|
|
2656
|
+
error(problem, 0)
|
|
2657
|
+
end
|
|
2658
|
+
chunk()(${G})`;
|
|
2659
|
+
}
|
|
2660
|
+
return `
|
|
2661
|
+
require(file)(${G})`;
|
|
2662
|
+
}
|
|
2663
|
+
function runtime(G, options) {
|
|
2664
|
+
const { several } = options;
|
|
2665
|
+
const files = [...options.files].map(([name, file]) => `[${(0, import_parser7.luauString)(name)}] = ${(0, import_parser7.luauString)(file)}`).join(", ");
|
|
2208
2666
|
return `
|
|
2209
2667
|
local ${G}
|
|
2210
2668
|
${G} = {
|
|
2211
2669
|
modules = {},
|
|
2670
|
+
-- Every array the program made, so \`typeof\` can tell one from an object.
|
|
2671
|
+
-- The keys are held weakly: being written down keeps no array alive.
|
|
2672
|
+
arrays = setmetatable({}, { __mode = "k" }),
|
|
2212
2673
|
-- Filled in just before the entry runs: bundle line -> { file, line }.
|
|
2213
2674
|
lines = nil,
|
|
2214
2675
|
-- What an error nothing caught says, in the project's files: the bundle's
|
|
@@ -2249,7 +2710,7 @@ ${G} = {
|
|
|
2249
2710
|
return message
|
|
2250
2711
|
end
|
|
2251
2712
|
local lines = ${G}.lines
|
|
2252
|
-
if lines == nil then
|
|
2713
|
+
if lines == nil ${several ? `and #${G}.sources == 0 ` : ""}then
|
|
2253
2714
|
return message .. (trace or "")
|
|
2254
2715
|
end
|
|
2255
2716
|
-- The bundle's name, as a position in a message spells it: where this
|
|
@@ -2269,6 +2730,81 @@ ${G} = {
|
|
|
2269
2730
|
chunk = info.short_src
|
|
2270
2731
|
end
|
|
2271
2732
|
end
|
|
2733
|
+
${several ? `
|
|
2734
|
+
-- A position in this file is read by this file's map. One in another
|
|
2735
|
+
-- of the bundle's files is read by that file's: the one whose name
|
|
2736
|
+
-- the position ends with, by the most folders where several do. A
|
|
2737
|
+
-- message says nothing about which file it starts in, so nothing is
|
|
2738
|
+
-- guessed from it here.
|
|
2739
|
+
local prefix = nil
|
|
2740
|
+
if chunk ~= nil then
|
|
2741
|
+
prefix = chunk:gsub("%p", "%%%0")
|
|
2742
|
+
end
|
|
2743
|
+
local function place(line)
|
|
2744
|
+
local origin = lines ~= nil and lines[tonumber(line)] or nil
|
|
2745
|
+
if origin == nil then
|
|
2746
|
+
return nil
|
|
2747
|
+
end
|
|
2748
|
+
return origin[1] .. ":" .. tostring(origin[2])
|
|
2749
|
+
end
|
|
2750
|
+
local function elsewhere(at, line)
|
|
2751
|
+
-- Already a place in the project.
|
|
2752
|
+
if at:sub(-6) == ".tilua" then
|
|
2753
|
+
return nil
|
|
2754
|
+
end
|
|
2755
|
+
local parts = {}
|
|
2756
|
+
for part in at:gmatch("[^/\\\\%.]+") do
|
|
2757
|
+
parts[#parts + 1] = part
|
|
2758
|
+
end
|
|
2759
|
+
local map, most, tied = nil, 0, false
|
|
2760
|
+
for _, source in ipairs(${G}.sources) do
|
|
2761
|
+
local names = source[1]
|
|
2762
|
+
local last = #parts
|
|
2763
|
+
if parts[last] == source[3] then
|
|
2764
|
+
last = last - 1
|
|
2765
|
+
end
|
|
2766
|
+
local same = 0
|
|
2767
|
+
while same < #names and same < last and names[#names - same] == parts[last - same] do
|
|
2768
|
+
same = same + 1
|
|
2769
|
+
end
|
|
2770
|
+
if same > most then
|
|
2771
|
+
map, most, tied = source[2], same, false
|
|
2772
|
+
elseif same == most then
|
|
2773
|
+
tied = true
|
|
2774
|
+
end
|
|
2775
|
+
end
|
|
2776
|
+
-- Two files it could as well be: a wrong place is worse than none.
|
|
2777
|
+
local origin = map ~= nil and not tied and map[tonumber(line)] or nil
|
|
2778
|
+
if origin == nil then
|
|
2779
|
+
return nil
|
|
2780
|
+
end
|
|
2781
|
+
return origin[1] .. ":" .. tostring(origin[2])
|
|
2782
|
+
end
|
|
2783
|
+
local mapped = message
|
|
2784
|
+
if prefix ~= nil then
|
|
2785
|
+
mapped = mapped:gsub(prefix .. ":(%d+)", function(line)
|
|
2786
|
+
return place(line)
|
|
2787
|
+
end)
|
|
2788
|
+
end
|
|
2789
|
+
mapped = mapped:gsub("(%S+):(%d+)", elsewhere)
|
|
2790
|
+
local frames = {}
|
|
2791
|
+
for frame in (trace or ""):gmatch("[^\\n]+") do
|
|
2792
|
+
local at = nil
|
|
2793
|
+
if prefix ~= nil then
|
|
2794
|
+
local line = frame:match(prefix .. ":(%d+)")
|
|
2795
|
+
if line ~= nil then
|
|
2796
|
+
at = place(line)
|
|
2797
|
+
end
|
|
2798
|
+
end
|
|
2799
|
+
if at == nil then
|
|
2800
|
+
for source, line in frame:gmatch("(%S+):(%d+)") do
|
|
2801
|
+
at = elsewhere(source, line)
|
|
2802
|
+
if at ~= nil then
|
|
2803
|
+
break
|
|
2804
|
+
end
|
|
2805
|
+
end
|
|
2806
|
+
end
|
|
2807
|
+
if at ~= nil then` : `
|
|
2272
2808
|
if chunk == nil then
|
|
2273
2809
|
chunk = message:match("^(.-):%d+:")
|
|
2274
2810
|
end
|
|
@@ -2290,8 +2826,9 @@ ${G} = {
|
|
|
2290
2826
|
for frame in (trace or ""):gmatch("[^\\n]+") do
|
|
2291
2827
|
local line = frame:match(prefix .. ":(%d+)")
|
|
2292
2828
|
local at = line ~= nil and place(line) or nil
|
|
2293
|
-
if at ~= nil then
|
|
2294
|
-
|
|
2829
|
+
if at ~= nil then`}
|
|
2830
|
+
-- Luau writes \`function name\`; Lua 5.1, \`function 'name'\`.
|
|
2831
|
+
local name = frame:match("function ([%w_.:]+)") or frame:match("function '([^']+)'")
|
|
2295
2832
|
frames[#frames + 1] = " at " .. at .. (name ~= nil and (" (" .. name .. ")") or "")
|
|
2296
2833
|
end
|
|
2297
2834
|
end
|
|
@@ -2300,13 +2837,36 @@ ${G} = {
|
|
|
2300
2837
|
end
|
|
2301
2838
|
return mapped .. "\\n" .. table.concat(frames, "\\n")
|
|
2302
2839
|
end,
|
|
2303
|
-
records = {}
|
|
2840
|
+
records = {},${several ? `
|
|
2841
|
+
-- The file each module outside this one is in, as \`load\` is handed it.
|
|
2842
|
+
files = { ${files} },
|
|
2843
|
+
loaded = {},
|
|
2844
|
+
-- Every file's line map: by the module, and by the file's name.
|
|
2845
|
+
maps = {},
|
|
2846
|
+
sources = {},
|
|
2847
|
+
-- Another of the bundle's files: a function that adds its modules here.
|
|
2848
|
+
load = function(file)
|
|
2849
|
+
if ${G}.loaded[file] then
|
|
2850
|
+
return
|
|
2851
|
+
end
|
|
2852
|
+
${G}.loaded[file] = true${loader(G, options.target)}
|
|
2853
|
+
end,` : ""}${options.imports ? `
|
|
2854
|
+
-- \`import("./x")\`: the module's exports, asked for where the code runs
|
|
2855
|
+
-- rather than before its module does. \`require\` does the rest \u2014 and
|
|
2856
|
+
-- loads the file the module is in, if it is not this one.
|
|
2857
|
+
import = function(name)
|
|
2858
|
+
return ${G}.require(name)
|
|
2859
|
+
end,` : ""}
|
|
2304
2860
|
require = function(name)
|
|
2305
2861
|
local record = ${G}.records[name]
|
|
2306
2862
|
if record ~= nil then
|
|
2307
2863
|
return record.exports
|
|
2308
2864
|
end
|
|
2309
|
-
local module = ${G}.modules[name]
|
|
2865
|
+
local module = ${G}.modules[name]${several ? `
|
|
2866
|
+
if module == nil then
|
|
2867
|
+
${G}.load(${G}.files[name])
|
|
2868
|
+
module = ${G}.modules[name]
|
|
2869
|
+
end` : ""}
|
|
2310
2870
|
local initialized = {}
|
|
2311
2871
|
local exports = setmetatable({}, {
|
|
2312
2872
|
__index = function(_, key)
|
|
@@ -2325,7 +2885,7 @@ ${G} = {
|
|
|
2325
2885
|
return source[link[2]]
|
|
2326
2886
|
end
|
|
2327
2887
|
if module.stars ~= nil and key ~= "default" then
|
|
2328
|
-
for _, star in module.stars do
|
|
2888
|
+
for _, star in ipairs(module.stars) do
|
|
2329
2889
|
local value = ${G}.require(star)[key]
|
|
2330
2890
|
if value ~= nil then
|
|
2331
2891
|
return value
|
|
@@ -2350,7 +2910,7 @@ ${G} = {
|
|
|
2350
2910
|
}
|
|
2351
2911
|
function stampOf(file) {
|
|
2352
2912
|
try {
|
|
2353
|
-
const at = (0,
|
|
2913
|
+
const at = (0, import_node_fs3.statSync)(file);
|
|
2354
2914
|
return `${at.size}:${at.mtimeMs}`;
|
|
2355
2915
|
} catch {
|
|
2356
2916
|
return void 0;
|
|
@@ -2366,19 +2926,19 @@ function parseFile(file, diagnostics, directives) {
|
|
|
2366
2926
|
}
|
|
2367
2927
|
let text;
|
|
2368
2928
|
try {
|
|
2369
|
-
text = (0,
|
|
2929
|
+
text = (0, import_node_fs3.readFileSync)(file, "utf8");
|
|
2370
2930
|
} catch {
|
|
2371
2931
|
diagnostics.push({ file, message: "Cannot read file", line: 1, column: 1, category: "module" });
|
|
2372
2932
|
return void 0;
|
|
2373
2933
|
}
|
|
2374
2934
|
try {
|
|
2375
|
-
const program2 = (0,
|
|
2376
|
-
const found = (0,
|
|
2935
|
+
const program2 = (0, import_parser7.parse)(text);
|
|
2936
|
+
const found = (0, import_parser7.directivesOf)(text);
|
|
2377
2937
|
directives.set(file, found);
|
|
2378
2938
|
if (stamp !== void 0) parsedFiles.set(file, { stamp, program: program2, text, directives: found });
|
|
2379
2939
|
return { program: program2, text };
|
|
2380
2940
|
} catch (error) {
|
|
2381
|
-
if (error instanceof
|
|
2941
|
+
if (error instanceof import_parser7.ParseError || error instanceof import_parser7.LexError) {
|
|
2382
2942
|
const { line, column } = error;
|
|
2383
2943
|
diagnostics.push({ file, message: error.message, line, column, category: "syntax" });
|
|
2384
2944
|
return void 0;
|
|
@@ -2393,14 +2953,14 @@ var libraryCache = /* @__PURE__ */ new Map();
|
|
|
2393
2953
|
function parsedLibrary(file) {
|
|
2394
2954
|
let stamp;
|
|
2395
2955
|
try {
|
|
2396
|
-
const at = (0,
|
|
2956
|
+
const at = (0, import_node_fs3.statSync)(file);
|
|
2397
2957
|
stamp = `${at.size}:${at.mtimeMs}`;
|
|
2398
2958
|
} catch {
|
|
2399
2959
|
stamp = "";
|
|
2400
2960
|
}
|
|
2401
2961
|
const cached = libraryCache.get(file);
|
|
2402
2962
|
if (cached && cached.stamp === stamp) return cached.program;
|
|
2403
|
-
const program2 = (0,
|
|
2963
|
+
const program2 = (0, import_parser7.parse)((0, import_node_fs3.readFileSync)(file, "utf8"));
|
|
2404
2964
|
libraryCache.set(file, { stamp, program: program2 });
|
|
2405
2965
|
return program2;
|
|
2406
2966
|
}
|
|
@@ -2408,9 +2968,11 @@ var moduleAnalyses = /* @__PURE__ */ new Map();
|
|
|
2408
2968
|
function analyzeModules(modules, config) {
|
|
2409
2969
|
const out = [];
|
|
2410
2970
|
const analyses = /* @__PURE__ */ new Map();
|
|
2411
|
-
const libraries = config ? (0,
|
|
2971
|
+
const libraries = config ? (0, import_parser7.resolveTypeLibraries)(config) : { files: [], lowerings: [], problems: [] };
|
|
2412
2972
|
for (const p of libraries.problems) out.push({ file: p.file, message: p.message, line: p.line ?? 1, column: p.column ?? 1, category: "config" });
|
|
2413
2973
|
const libs = libraries.files.map(parsedLibrary);
|
|
2974
|
+
const noLib = config?.noLib ?? false;
|
|
2975
|
+
const checked = libs.length > 0 || !noLib;
|
|
2414
2976
|
const globals = libs.flatMap((lib) => lib.body.statements.flatMap((s) => s.type === "DeclareStatement" ? [s.name] : []));
|
|
2415
2977
|
const byFile = new Map(modules.map((m) => [m.file, m]));
|
|
2416
2978
|
const exportsCache = /* @__PURE__ */ new Map();
|
|
@@ -2423,49 +2985,50 @@ function analyzeModules(modules, config) {
|
|
|
2423
2985
|
const done = analyzed.get(file);
|
|
2424
2986
|
if (done) return done;
|
|
2425
2987
|
const remembered = moduleAnalyses.get(file);
|
|
2426
|
-
if (remembered && remembered.setting === settingFor && libs.length === remembered.libs.length && remembered.libs.every((lib, i) => lib === libs[i]) && remembered.program === program2 && [...remembered.read].every(([
|
|
2988
|
+
if (remembered && remembered.setting === settingFor && libs.length === remembered.libs.length && remembered.libs.every((lib, i) => lib === libs[i]) && remembered.program === program2 && [...remembered.read].every(([read3, stamp]) => stampOf(read3) === stamp)) {
|
|
2427
2989
|
analyzed.set(file, remembered.result);
|
|
2428
2990
|
readWhile.set(file, remembered.read);
|
|
2429
2991
|
return remembered.result;
|
|
2430
2992
|
}
|
|
2431
|
-
const scopes = (0,
|
|
2993
|
+
const scopes = (0, import_parser7.analyzeScopes)(program2, { builtinGlobals: globals, reportUndeclared: checked, noLib });
|
|
2432
2994
|
const said = [];
|
|
2433
2995
|
for (const d of scopes.diagnostics) {
|
|
2434
2996
|
if (d.kind !== "undeclared") continue;
|
|
2435
2997
|
said.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "type" });
|
|
2436
2998
|
}
|
|
2437
2999
|
const before = partials;
|
|
2438
|
-
const types = (0,
|
|
3000
|
+
const types = (0, import_parser7.analyzeTypes)(program2, scopes, {
|
|
2439
3001
|
libs,
|
|
2440
3002
|
resolveModule: resolverFor(file),
|
|
2441
|
-
|
|
3003
|
+
noLib,
|
|
3004
|
+
reportUnknownTypes: checked
|
|
2442
3005
|
});
|
|
2443
3006
|
for (const d of types.diagnostics) {
|
|
2444
3007
|
const at = d.node;
|
|
2445
3008
|
said.push({ file, message: d.message, line: at.line.start, column: at.column.start, category: "type" });
|
|
2446
3009
|
}
|
|
2447
|
-
const
|
|
3010
|
+
const result2 = { types, diagnostics: said };
|
|
2448
3011
|
if (partials === before) {
|
|
2449
|
-
analyzed.set(file,
|
|
2450
|
-
const
|
|
3012
|
+
analyzed.set(file, result2);
|
|
3013
|
+
const read3 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
|
|
2451
3014
|
const own = stampOf(file);
|
|
2452
|
-
if (own !== void 0)
|
|
2453
|
-
readWhile.set(file,
|
|
2454
|
-
if ([...
|
|
2455
|
-
moduleAnalyses.set(file, { setting: settingFor, libs: [...libs], program: program2, read:
|
|
3015
|
+
if (own !== void 0) read3.set(file, own);
|
|
3016
|
+
readWhile.set(file, read3);
|
|
3017
|
+
if ([...read3.values()].every((stamp) => stamp !== void 0)) {
|
|
3018
|
+
moduleAnalyses.set(file, { setting: settingFor, libs: [...libs], program: program2, read: read3, result: result2 });
|
|
2456
3019
|
}
|
|
2457
3020
|
}
|
|
2458
|
-
return
|
|
3021
|
+
return result2;
|
|
2459
3022
|
};
|
|
2460
3023
|
const resolverFor = (file) => (specifier) => {
|
|
2461
|
-
const target = (0,
|
|
3024
|
+
const target = (0, import_parser7.resolveModulePath)(file, specifier, config);
|
|
2462
3025
|
if (!target) return void 0;
|
|
2463
3026
|
const noteRead = () => {
|
|
2464
|
-
const
|
|
2465
|
-
readWhile.set(file,
|
|
3027
|
+
const read3 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
|
|
3028
|
+
readWhile.set(file, read3);
|
|
2466
3029
|
const stamp = stampOf(target);
|
|
2467
|
-
if (stamp !== void 0)
|
|
2468
|
-
for (const [below, belowStamp] of readWhile.get(target) ?? [])
|
|
3030
|
+
if (stamp !== void 0) read3.set(target, stamp);
|
|
3031
|
+
for (const [below, belowStamp] of readWhile.get(target) ?? []) read3.set(below, belowStamp);
|
|
2469
3032
|
};
|
|
2470
3033
|
if (inProgress.has(target)) {
|
|
2471
3034
|
partials++;
|
|
@@ -2479,16 +3042,16 @@ function analyzeModules(modules, config) {
|
|
|
2479
3042
|
let program2 = byFile.get(target)?.program;
|
|
2480
3043
|
if (!program2) {
|
|
2481
3044
|
try {
|
|
2482
|
-
program2 = (0,
|
|
3045
|
+
program2 = (0, import_parser7.parse)((0, import_node_fs3.readFileSync)(target, "utf8"));
|
|
2483
3046
|
} catch {
|
|
2484
3047
|
return void 0;
|
|
2485
3048
|
}
|
|
2486
3049
|
}
|
|
2487
3050
|
inProgress.add(target);
|
|
2488
3051
|
try {
|
|
2489
|
-
const scopes = (0,
|
|
2490
|
-
const types = byFile.has(target) ? analyzeModule(target, program2).types : (0,
|
|
2491
|
-
const exports2 = (0,
|
|
3052
|
+
const scopes = (0, import_parser7.analyzeScopes)(program2, { builtinGlobals: globals, noLib });
|
|
3053
|
+
const types = byFile.has(target) ? analyzeModule(target, program2).types : (0, import_parser7.analyzeTypes)(program2, scopes, { libs, noLib, resolveModule: resolverFor(target), diagnostics: false });
|
|
3054
|
+
const exports2 = (0, import_parser7.moduleExports)(program2, scopes, types, resolverFor(target));
|
|
2492
3055
|
exportsCache.set(target, exports2);
|
|
2493
3056
|
noteRead();
|
|
2494
3057
|
return exports2;
|
|
@@ -2521,37 +3084,39 @@ function allStatements(statements) {
|
|
|
2521
3084
|
}
|
|
2522
3085
|
|
|
2523
3086
|
// src/compile.ts
|
|
2524
|
-
var
|
|
3087
|
+
var import_node_fs4 = require("fs");
|
|
2525
3088
|
var import_node_path3 = require("path");
|
|
2526
|
-
var
|
|
3089
|
+
var import_parser8 = require("@tilua/parser");
|
|
2527
3090
|
var import_luau_parser3 = require("luau-parser");
|
|
2528
3091
|
async function compile(source, config) {
|
|
2529
3092
|
let program2;
|
|
2530
3093
|
try {
|
|
2531
|
-
program2 = (0,
|
|
3094
|
+
program2 = (0, import_parser8.parse)(source);
|
|
2532
3095
|
} catch (error) {
|
|
2533
|
-
if (error instanceof
|
|
3096
|
+
if (error instanceof import_parser8.ParseError || error instanceof import_parser8.LexError) {
|
|
2534
3097
|
const { line, column } = error;
|
|
2535
3098
|
return { diagnostics: [{ message: error.message, line, column }] };
|
|
2536
3099
|
}
|
|
2537
3100
|
throw error;
|
|
2538
3101
|
}
|
|
2539
|
-
const
|
|
3102
|
+
const { config: resolved, problems } = resolveConfig(config, (0, import_node_path3.resolve)("main.tilua"));
|
|
3103
|
+
const noLib = resolved?.noLib ?? false;
|
|
3104
|
+
const scopes = (0, import_parser8.analyzeScopes)(program2, { noLib });
|
|
2540
3105
|
const diagnostics = scopes.diagnostics.map((d) => ({
|
|
2541
3106
|
message: d.message,
|
|
2542
3107
|
line: d.node.line.start,
|
|
2543
3108
|
column: d.node.column.start
|
|
2544
3109
|
}));
|
|
2545
|
-
const
|
|
2546
|
-
const libraries = resolved ? (0, import_parser7.resolveTypeLibraries)(resolved) : { files: [], lowerings: [], problems: [] };
|
|
3110
|
+
const libraries = resolved ? (0, import_parser8.resolveTypeLibraries)(resolved) : { files: [], lowerings: [], problems: [] };
|
|
2547
3111
|
const { lowerings, problems: loweringProblems } = await loadLowerings(libraries.lowerings);
|
|
2548
3112
|
for (const p of [...problems, ...libraries.problems]) {
|
|
2549
3113
|
diagnostics.push({ message: p.message, line: p.line ?? 1, column: p.column ?? 1 });
|
|
2550
3114
|
}
|
|
2551
3115
|
for (const p of loweringProblems) diagnostics.push({ message: p.message, line: 1, column: 1 });
|
|
2552
|
-
const libs = libraries.files.map((file) => (0,
|
|
3116
|
+
const libs = libraries.files.map((file) => (0, import_parser8.parse)((0, import_node_fs4.readFileSync)(file, "utf8")));
|
|
2553
3117
|
const lowered = lower(program2, scopes, {
|
|
2554
|
-
types: (0,
|
|
3118
|
+
types: (0, import_parser8.analyzeTypes)(program2, scopes, { libs, noLib, diagnostics: false }),
|
|
3119
|
+
noLib,
|
|
2555
3120
|
lowerings,
|
|
2556
3121
|
target: resolved?.target,
|
|
2557
3122
|
source
|