@tilua/compiler 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-X4DM2BX2.js → chunk-VANZBLCL.js} +62 -36
- package/dist/cli.cjs +85 -60
- package/dist/cli.js +1 -1
- package/dist/index.cjs +92 -67
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/runtime/object.luau +39 -0
|
@@ -14,6 +14,7 @@ import { formatType } from "@tilua/parser";
|
|
|
14
14
|
import { parse as parseLuau, parseExpressionFromSource } from "luau-parser";
|
|
15
15
|
|
|
16
16
|
// src/luau.ts
|
|
17
|
+
import { LUAU_KEYWORDS, isLuauName } from "@tilua/parser";
|
|
17
18
|
var NO_SPAN = { line: { start: 0, end: 0 }, column: { start: 0, end: 0 } };
|
|
18
19
|
var span = () => ({ line: { ...NO_SPAN.line }, column: { ...NO_SPAN.column } });
|
|
19
20
|
var identifier = (name) => ({ type: "Identifier", name, ...span() });
|
|
@@ -74,32 +75,6 @@ var functionBody = (params, statements, hasVarargs = false) => ({
|
|
|
74
75
|
});
|
|
75
76
|
var localFunction = (name, func) => ({ type: "LocalFunctionStatement", name: identifier(name), func, ...span() });
|
|
76
77
|
var program = (statements) => ({ type: "Program", body: block(statements), ...span() });
|
|
77
|
-
var LUAU_KEYWORDS = /* @__PURE__ */ new Set([
|
|
78
|
-
"and",
|
|
79
|
-
"break",
|
|
80
|
-
"do",
|
|
81
|
-
"else",
|
|
82
|
-
"elseif",
|
|
83
|
-
"end",
|
|
84
|
-
"false",
|
|
85
|
-
"for",
|
|
86
|
-
"function",
|
|
87
|
-
"if",
|
|
88
|
-
"in",
|
|
89
|
-
"local",
|
|
90
|
-
"nil",
|
|
91
|
-
"not",
|
|
92
|
-
"or",
|
|
93
|
-
"repeat",
|
|
94
|
-
"return",
|
|
95
|
-
"then",
|
|
96
|
-
"true",
|
|
97
|
-
"until",
|
|
98
|
-
"while"
|
|
99
|
-
]);
|
|
100
|
-
function isLuauName(name) {
|
|
101
|
-
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) && !LUAU_KEYWORDS.has(name);
|
|
102
|
-
}
|
|
103
78
|
|
|
104
79
|
// src/names.ts
|
|
105
80
|
var Names = class _Names {
|
|
@@ -142,6 +117,7 @@ var Names = class _Names {
|
|
|
142
117
|
|
|
143
118
|
// src/language.ts
|
|
144
119
|
import { readFileSync } from "fs";
|
|
120
|
+
import { luauString } from "@tilua/parser";
|
|
145
121
|
var read = (name) => readFileSync(new URL(`../runtime/${name}`, import.meta.url), "utf8");
|
|
146
122
|
var implemented = (source) => new Set([...source.matchAll(/function __NAME__\.(\w+)/g)].map((m) => m[1]));
|
|
147
123
|
var array = read("array.luau");
|
|
@@ -175,7 +151,7 @@ function objectKeys(type) {
|
|
|
175
151
|
orders.push(keys);
|
|
176
152
|
}
|
|
177
153
|
const order = JSON.stringify(orders[0] ?? []);
|
|
178
|
-
const known = orders.length && orders.every((keys) => JSON.stringify(keys) === order) ? `{ ${orders[0].map((key) =>
|
|
154
|
+
const known = orders.length && orders.every((keys) => JSON.stringify(keys) === order) ? `{ ${orders[0].map((key) => luauString(key)).join(", ")} }` : "nil";
|
|
179
155
|
return { known, open: String(open) };
|
|
180
156
|
}
|
|
181
157
|
var plugin = {
|
|
@@ -1439,6 +1415,9 @@ end
|
|
|
1439
1415
|
callValues(node) {
|
|
1440
1416
|
const chain = optionalChain(node);
|
|
1441
1417
|
if (chain) return this.optionalChainExpression(chain);
|
|
1418
|
+
if (node.type === "CallExpression" && (node.callee.type === "SuperExpression" || node.callee.type === "MemberExpression" && node.callee.object.type === "SuperExpression")) {
|
|
1419
|
+
return this.expression(node);
|
|
1420
|
+
}
|
|
1442
1421
|
const object2 = linkObject(node);
|
|
1443
1422
|
if (node.type === "CallExpression" && object2.type === "Identifier") {
|
|
1444
1423
|
return this.link(node, this.reference(object2));
|
|
@@ -2021,11 +2000,13 @@ import {
|
|
|
2021
2000
|
applyDirectives,
|
|
2022
2001
|
UNUSED_EXPECT_ERROR,
|
|
2023
2002
|
ParseError,
|
|
2024
|
-
LexError
|
|
2003
|
+
LexError,
|
|
2004
|
+
luauString as luauString3
|
|
2025
2005
|
} from "@tilua/parser";
|
|
2026
2006
|
import { parse as parseLuau2, parseExpressionFromSource as parseExpressionFromSource2, print } from "luau-parser";
|
|
2027
2007
|
|
|
2028
2008
|
// src/linemap.ts
|
|
2009
|
+
import { luauString as luauString2 } from "@tilua/parser";
|
|
2029
2010
|
function statementsOf(node) {
|
|
2030
2011
|
const out = [];
|
|
2031
2012
|
const visit = (value) => {
|
|
@@ -2058,7 +2039,7 @@ function buildLineMap(emitted, printed, origins) {
|
|
|
2058
2039
|
}
|
|
2059
2040
|
function lineMapSource(map) {
|
|
2060
2041
|
const entries = [...map.entries()].sort((a, b) => a[0] - b[0]);
|
|
2061
|
-
const quoted = (s) =>
|
|
2042
|
+
const quoted = (s) => luauString2(s.replace(/\\/g, "/"));
|
|
2062
2043
|
return `{${entries.map(([line, o]) => `[${line}]={${quoted(o.file)},${o.line}}`).join(",")}}`;
|
|
2063
2044
|
}
|
|
2064
2045
|
|
|
@@ -2185,7 +2166,7 @@ async function bundle(options) {
|
|
|
2185
2166
|
const result = names.fresh("result");
|
|
2186
2167
|
const entryStatements = parseLuau2(`
|
|
2187
2168
|
local ${ok}, ${result} = xpcall(function()
|
|
2188
|
-
return ${G}.require(${
|
|
2169
|
+
return ${G}.require(${luauString3(sources.get(entry).name)})
|
|
2189
2170
|
end, ${G}.fail)
|
|
2190
2171
|
if not ${ok} then
|
|
2191
2172
|
error(${result}, 0)
|
|
@@ -2214,19 +2195,64 @@ ${G} = {
|
|
|
2214
2195
|
-- and a traceback of those places follows. A message that is not a string
|
|
2215
2196
|
-- is left as it is, and a position that is not this bundle's (a library
|
|
2216
2197
|
-- that raised its error with one already mapped) is left alone.
|
|
2198
|
+
--
|
|
2199
|
+
-- It is an \`xpcall\` handler, and a handler that fails loses the error
|
|
2200
|
+
-- altogether: Luau reports only "error in error handling". So the work is
|
|
2201
|
+
-- done under \`pcall\`, leaning on nothing it has not checked is there (an
|
|
2202
|
+
-- executor may lack \`debug.info\`, or refuse it), and an error it cannot
|
|
2203
|
+
-- map is still answered as Luau raised it, with why.
|
|
2217
2204
|
fail = function(message)
|
|
2205
|
+
-- Taken first, before anything that can fail. Level 3 is the frame
|
|
2206
|
+
-- that raised: past \`pcall\` and this handler.
|
|
2207
|
+
local trace
|
|
2208
|
+
if debug ~= nil and debug.traceback ~= nil then
|
|
2209
|
+
local ok, text = pcall(debug.traceback, "", 3)
|
|
2210
|
+
if ok and type(text) == "string" then
|
|
2211
|
+
trace = text
|
|
2212
|
+
end
|
|
2213
|
+
end
|
|
2214
|
+
local ok, described = pcall(${G}.describe, message, trace)
|
|
2215
|
+
if ok then
|
|
2216
|
+
return described
|
|
2217
|
+
end
|
|
2218
|
+
if type(message) ~= "string" then
|
|
2219
|
+
return message
|
|
2220
|
+
end
|
|
2221
|
+
local why = type(described) == "string" and described or "unknown"
|
|
2222
|
+
return message .. "\\n(tilua could not map this error to the project's files: " .. why .. ")" .. (trace or "")
|
|
2223
|
+
end,
|
|
2224
|
+
-- \`fail\`'s work. \`trace\` is a traceback from where the error was
|
|
2225
|
+
-- raised, or nil when none could be taken.
|
|
2226
|
+
describe = function(message, trace)
|
|
2218
2227
|
if type(message) ~= "string" then
|
|
2219
2228
|
return message
|
|
2220
2229
|
end
|
|
2221
2230
|
local lines = ${G}.lines
|
|
2222
2231
|
if lines == nil then
|
|
2223
|
-
return
|
|
2232
|
+
return message .. (trace or "")
|
|
2224
2233
|
end
|
|
2234
|
+
-- The bundle's name, as a position in a message spells it: where this
|
|
2235
|
+
-- function is, asked of the debug library by the function rather
|
|
2236
|
+
-- than by a level, which \`pcall\` would shift. Failing that, a message
|
|
2237
|
+
-- starts with the position it was raised at.
|
|
2225
2238
|
local chunk
|
|
2226
|
-
if debug.info ~= nil then
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2239
|
+
if debug ~= nil and debug.info ~= nil then
|
|
2240
|
+
local found, source = pcall(debug.info, ${G}.describe, "s")
|
|
2241
|
+
if found and type(source) == "string" then
|
|
2242
|
+
chunk = source
|
|
2243
|
+
end
|
|
2244
|
+
end
|
|
2245
|
+
if chunk == nil and debug ~= nil and debug.getinfo ~= nil then
|
|
2246
|
+
local found, info = pcall(debug.getinfo, ${G}.describe, "S")
|
|
2247
|
+
if found and type(info) == "table" and type(info.short_src) == "string" then
|
|
2248
|
+
chunk = info.short_src
|
|
2249
|
+
end
|
|
2250
|
+
end
|
|
2251
|
+
if chunk == nil then
|
|
2252
|
+
chunk = message:match("^(.-):%d+:")
|
|
2253
|
+
end
|
|
2254
|
+
if chunk == nil then
|
|
2255
|
+
return message .. (trace or "")
|
|
2230
2256
|
end
|
|
2231
2257
|
local prefix = chunk:gsub("%p", "%%%0")
|
|
2232
2258
|
local function place(line)
|
|
@@ -2240,7 +2266,7 @@ ${G} = {
|
|
|
2240
2266
|
return place(line)
|
|
2241
2267
|
end)
|
|
2242
2268
|
local frames = {}
|
|
2243
|
-
for frame in
|
|
2269
|
+
for frame in (trace or ""):gmatch("[^\\n]+") do
|
|
2244
2270
|
local line = frame:match(prefix .. ":(%d+)")
|
|
2245
2271
|
local at = line ~= nil and place(line) or nil
|
|
2246
2272
|
if at ~= nil then
|
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 = {
|
|
@@ -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}_`);
|
|
@@ -376,7 +352,7 @@ var Lowerer = class {
|
|
|
376
352
|
const type = this.options.types?.typeOf.get(argument);
|
|
377
353
|
return {
|
|
378
354
|
type,
|
|
379
|
-
typeText: type ? (0,
|
|
355
|
+
typeText: type ? (0, import_parser4.formatType)(type) : void 0,
|
|
380
356
|
source: this.writtenSource(argument),
|
|
381
357
|
declaration: declaration ? this.writtenSource(declaration) : void 0,
|
|
382
358
|
spread: argument.type === "SpreadElement"
|
|
@@ -1455,6 +1431,9 @@ end
|
|
|
1455
1431
|
callValues(node) {
|
|
1456
1432
|
const chain = optionalChain(node);
|
|
1457
1433
|
if (chain) return this.optionalChainExpression(chain);
|
|
1434
|
+
if (node.type === "CallExpression" && (node.callee.type === "SuperExpression" || node.callee.type === "MemberExpression" && node.callee.object.type === "SuperExpression")) {
|
|
1435
|
+
return this.expression(node);
|
|
1436
|
+
}
|
|
1458
1437
|
const object2 = linkObject(node);
|
|
1459
1438
|
if (node.type === "CallExpression" && object2.type === "Identifier") {
|
|
1460
1439
|
return this.link(node, this.reference(object2));
|
|
@@ -1520,7 +1499,7 @@ end
|
|
|
1520
1499
|
...args2
|
|
1521
1500
|
]);
|
|
1522
1501
|
}
|
|
1523
|
-
if (!isLuauName(node.method.name)) {
|
|
1502
|
+
if (!import_parser2.isLuauName(node.method.name)) {
|
|
1524
1503
|
this.report(node.method, `'${node.method.name}' is a Luau keyword and cannot be called with ':'`);
|
|
1525
1504
|
}
|
|
1526
1505
|
return methodCall(object2, node.method.name, args2);
|
|
@@ -1882,7 +1861,7 @@ function topLevelDeclaration(statements, name) {
|
|
|
1882
1861
|
function moduleName(key) {
|
|
1883
1862
|
const last = key.split("/").filter(Boolean).pop() ?? "module";
|
|
1884
1863
|
const cleaned = last.replace(/[^A-Za-z0-9_]/g, "_");
|
|
1885
|
-
return /^[A-Za-z_]/.test(cleaned) && !LUAU_KEYWORDS.has(cleaned) ? cleaned : `module_${cleaned}`;
|
|
1864
|
+
return /^[A-Za-z_]/.test(cleaned) && !import_parser2.LUAU_KEYWORDS.has(cleaned) ? cleaned : `module_${cleaned}`;
|
|
1886
1865
|
}
|
|
1887
1866
|
function alwaysTruthy(value) {
|
|
1888
1867
|
switch (value.type) {
|
|
@@ -1997,6 +1976,7 @@ var CLASS_METAMETHODS = [
|
|
|
1997
1976
|
];
|
|
1998
1977
|
|
|
1999
1978
|
// src/linemap.ts
|
|
1979
|
+
var import_parser5 = require("@tilua/parser");
|
|
2000
1980
|
function statementsOf(node) {
|
|
2001
1981
|
const out2 = [];
|
|
2002
1982
|
const visit = (value) => {
|
|
@@ -2029,7 +2009,7 @@ function buildLineMap(emitted, printed, origins) {
|
|
|
2029
2009
|
}
|
|
2030
2010
|
function lineMapSource(map) {
|
|
2031
2011
|
const entries = [...map.entries()].sort((a, b) => a[0] - b[0]);
|
|
2032
|
-
const quoted = (s) =>
|
|
2012
|
+
const quoted = (s) => (0, import_parser5.luauString)(s.replace(/\\/g, "/"));
|
|
2033
2013
|
return `{${entries.map(([line, o]) => `[${line}]={${quoted(o.file)},${o.line}}`).join(",")}}`;
|
|
2034
2014
|
}
|
|
2035
2015
|
|
|
@@ -2078,13 +2058,13 @@ async function bundle(options) {
|
|
|
2078
2058
|
const parsed = parseFile(file, diagnostics, directives);
|
|
2079
2059
|
if (!parsed) return { modules: [], diagnostics };
|
|
2080
2060
|
const { program: program2, text } = parsed;
|
|
2081
|
-
const scopes = (0,
|
|
2061
|
+
const scopes = (0, import_parser6.analyzeScopes)(program2);
|
|
2082
2062
|
for (const d of scopes.diagnostics) {
|
|
2083
2063
|
diagnostics.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "scope" });
|
|
2084
2064
|
}
|
|
2085
2065
|
sources.set(file, { file, name: nameOf(file), text, program: program2, scopes });
|
|
2086
2066
|
for (const specifier of importedSpecifiers(program2)) {
|
|
2087
|
-
const target = (0,
|
|
2067
|
+
const target = (0, import_parser6.resolveModulePath)(file, specifier, config2);
|
|
2088
2068
|
if (target && !target.endsWith(".d.tilua")) queue.push(target);
|
|
2089
2069
|
}
|
|
2090
2070
|
}
|
|
@@ -2118,7 +2098,7 @@ async function bundle(options) {
|
|
|
2118
2098
|
lines: `${G}.lines`,
|
|
2119
2099
|
fail: `${G}.fail`,
|
|
2120
2100
|
resolve: (specifier) => {
|
|
2121
|
-
const target = (0,
|
|
2101
|
+
const target = (0, import_parser6.resolveModulePath)(file, specifier, config2);
|
|
2122
2102
|
if (!target || target.endsWith(".d.tilua") || !sources.has(target)) return void 0;
|
|
2123
2103
|
pending.push(target);
|
|
2124
2104
|
return sources.get(target).name;
|
|
@@ -2140,14 +2120,14 @@ async function bundle(options) {
|
|
|
2140
2120
|
}
|
|
2141
2121
|
for (const [file, fileDirectives] of directives) {
|
|
2142
2122
|
const semantic = diagnostics.filter((d) => d.file === file && (d.category === "scope" || d.category === "type"));
|
|
2143
|
-
const { kept, unusedExpectErrors } = (0,
|
|
2123
|
+
const { kept, unusedExpectErrors } = (0, import_parser6.applyDirectives)(fileDirectives, semantic, (d) => d.line);
|
|
2144
2124
|
const suppressed = new Set(semantic.filter((d) => !kept.includes(d)));
|
|
2145
2125
|
const remaining = diagnostics.filter((d) => !suppressed.has(d));
|
|
2146
2126
|
diagnostics.length = 0;
|
|
2147
2127
|
diagnostics.push(...remaining);
|
|
2148
2128
|
if (options.typeCheck === false) continue;
|
|
2149
2129
|
for (const d of unusedExpectErrors) {
|
|
2150
|
-
diagnostics.push({ file, message:
|
|
2130
|
+
diagnostics.push({ file, message: import_parser6.UNUSED_EXPECT_ERROR, line: d.line, column: d.column, category: "type" });
|
|
2151
2131
|
}
|
|
2152
2132
|
}
|
|
2153
2133
|
const moduleProblems = new Set(diagnostics.filter((d) => d.category === "module").map((d) => `${d.file}:${d.line}:${d.column}:${d.message}`));
|
|
@@ -2183,7 +2163,7 @@ async function bundle(options) {
|
|
|
2183
2163
|
const result = names.fresh("result");
|
|
2184
2164
|
const entryStatements = (0, import_luau_parser2.parse)(`
|
|
2185
2165
|
local ${ok}, ${result} = xpcall(function()
|
|
2186
|
-
return ${G}.require(${
|
|
2166
|
+
return ${G}.require(${(0, import_parser6.luauString)(sources.get(entry2).name)})
|
|
2187
2167
|
end, ${G}.fail)
|
|
2188
2168
|
if not ${ok} then
|
|
2189
2169
|
error(${result}, 0)
|
|
@@ -2212,19 +2192,64 @@ ${G} = {
|
|
|
2212
2192
|
-- and a traceback of those places follows. A message that is not a string
|
|
2213
2193
|
-- is left as it is, and a position that is not this bundle's (a library
|
|
2214
2194
|
-- that raised its error with one already mapped) is left alone.
|
|
2195
|
+
--
|
|
2196
|
+
-- It is an \`xpcall\` handler, and a handler that fails loses the error
|
|
2197
|
+
-- altogether: Luau reports only "error in error handling". So the work is
|
|
2198
|
+
-- done under \`pcall\`, leaning on nothing it has not checked is there (an
|
|
2199
|
+
-- executor may lack \`debug.info\`, or refuse it), and an error it cannot
|
|
2200
|
+
-- map is still answered as Luau raised it, with why.
|
|
2215
2201
|
fail = function(message)
|
|
2202
|
+
-- Taken first, before anything that can fail. Level 3 is the frame
|
|
2203
|
+
-- that raised: past \`pcall\` and this handler.
|
|
2204
|
+
local trace
|
|
2205
|
+
if debug ~= nil and debug.traceback ~= nil then
|
|
2206
|
+
local ok, text = pcall(debug.traceback, "", 3)
|
|
2207
|
+
if ok and type(text) == "string" then
|
|
2208
|
+
trace = text
|
|
2209
|
+
end
|
|
2210
|
+
end
|
|
2211
|
+
local ok, described = pcall(${G}.describe, message, trace)
|
|
2212
|
+
if ok then
|
|
2213
|
+
return described
|
|
2214
|
+
end
|
|
2215
|
+
if type(message) ~= "string" then
|
|
2216
|
+
return message
|
|
2217
|
+
end
|
|
2218
|
+
local why = type(described) == "string" and described or "unknown"
|
|
2219
|
+
return message .. "\\n(tilua could not map this error to the project's files: " .. why .. ")" .. (trace or "")
|
|
2220
|
+
end,
|
|
2221
|
+
-- \`fail\`'s work. \`trace\` is a traceback from where the error was
|
|
2222
|
+
-- raised, or nil when none could be taken.
|
|
2223
|
+
describe = function(message, trace)
|
|
2216
2224
|
if type(message) ~= "string" then
|
|
2217
2225
|
return message
|
|
2218
2226
|
end
|
|
2219
2227
|
local lines = ${G}.lines
|
|
2220
2228
|
if lines == nil then
|
|
2221
|
-
return
|
|
2229
|
+
return message .. (trace or "")
|
|
2222
2230
|
end
|
|
2231
|
+
-- The bundle's name, as a position in a message spells it: where this
|
|
2232
|
+
-- function is, asked of the debug library by the function rather
|
|
2233
|
+
-- than by a level, which \`pcall\` would shift. Failing that, a message
|
|
2234
|
+
-- starts with the position it was raised at.
|
|
2223
2235
|
local chunk
|
|
2224
|
-
if debug.info ~= nil then
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2236
|
+
if debug ~= nil and debug.info ~= nil then
|
|
2237
|
+
local found, source = pcall(debug.info, ${G}.describe, "s")
|
|
2238
|
+
if found and type(source) == "string" then
|
|
2239
|
+
chunk = source
|
|
2240
|
+
end
|
|
2241
|
+
end
|
|
2242
|
+
if chunk == nil and debug ~= nil and debug.getinfo ~= nil then
|
|
2243
|
+
local found, info = pcall(debug.getinfo, ${G}.describe, "S")
|
|
2244
|
+
if found and type(info) == "table" and type(info.short_src) == "string" then
|
|
2245
|
+
chunk = info.short_src
|
|
2246
|
+
end
|
|
2247
|
+
end
|
|
2248
|
+
if chunk == nil then
|
|
2249
|
+
chunk = message:match("^(.-):%d+:")
|
|
2250
|
+
end
|
|
2251
|
+
if chunk == nil then
|
|
2252
|
+
return message .. (trace or "")
|
|
2228
2253
|
end
|
|
2229
2254
|
local prefix = chunk:gsub("%p", "%%%0")
|
|
2230
2255
|
local function place(line)
|
|
@@ -2238,7 +2263,7 @@ ${G} = {
|
|
|
2238
2263
|
return place(line)
|
|
2239
2264
|
end)
|
|
2240
2265
|
local frames = {}
|
|
2241
|
-
for frame in
|
|
2266
|
+
for frame in (trace or ""):gmatch("[^\\n]+") do
|
|
2242
2267
|
local line = frame:match(prefix .. ":(%d+)")
|
|
2243
2268
|
local at = line ~= nil and place(line) or nil
|
|
2244
2269
|
if at ~= nil then
|
|
@@ -2323,13 +2348,13 @@ function parseFile(file, diagnostics, directives) {
|
|
|
2323
2348
|
return void 0;
|
|
2324
2349
|
}
|
|
2325
2350
|
try {
|
|
2326
|
-
const program = (0,
|
|
2327
|
-
const found = (0,
|
|
2351
|
+
const program = (0, import_parser6.parse)(text);
|
|
2352
|
+
const found = (0, import_parser6.directivesOf)(text);
|
|
2328
2353
|
directives.set(file, found);
|
|
2329
2354
|
if (stamp !== void 0) parsedFiles.set(file, { stamp, program, text, directives: found });
|
|
2330
2355
|
return { program, text };
|
|
2331
2356
|
} catch (error) {
|
|
2332
|
-
if (error instanceof
|
|
2357
|
+
if (error instanceof import_parser6.ParseError || error instanceof import_parser6.LexError) {
|
|
2333
2358
|
const { line, column } = error;
|
|
2334
2359
|
diagnostics.push({ file, message: error.message, line, column, category: "syntax" });
|
|
2335
2360
|
return void 0;
|
|
@@ -2351,7 +2376,7 @@ function parsedLibrary(file) {
|
|
|
2351
2376
|
}
|
|
2352
2377
|
const cached = libraryCache.get(file);
|
|
2353
2378
|
if (cached && cached.stamp === stamp) return cached.program;
|
|
2354
|
-
const program = (0,
|
|
2379
|
+
const program = (0, import_parser6.parse)((0, import_node_fs2.readFileSync)(file, "utf8"));
|
|
2355
2380
|
libraryCache.set(file, { stamp, program });
|
|
2356
2381
|
return program;
|
|
2357
2382
|
}
|
|
@@ -2359,7 +2384,7 @@ var moduleAnalyses = /* @__PURE__ */ new Map();
|
|
|
2359
2384
|
function analyzeModules(modules, config2) {
|
|
2360
2385
|
const out2 = [];
|
|
2361
2386
|
const analyses = /* @__PURE__ */ new Map();
|
|
2362
|
-
const libraries = config2 ? (0,
|
|
2387
|
+
const libraries = config2 ? (0, import_parser6.resolveTypeLibraries)(config2) : { files: [], lowerings: [], problems: [] };
|
|
2363
2388
|
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
2389
|
const libs = libraries.files.map(parsedLibrary);
|
|
2365
2390
|
const globals = libs.flatMap((lib) => lib.body.statements.flatMap((s) => s.type === "DeclareStatement" ? [s.name] : []));
|
|
@@ -2379,14 +2404,14 @@ function analyzeModules(modules, config2) {
|
|
|
2379
2404
|
readWhile.set(file, remembered.read);
|
|
2380
2405
|
return remembered.result;
|
|
2381
2406
|
}
|
|
2382
|
-
const scopes = (0,
|
|
2407
|
+
const scopes = (0, import_parser6.analyzeScopes)(program, { builtinGlobals: globals, reportUndeclared: libs.length > 0 });
|
|
2383
2408
|
const said = [];
|
|
2384
2409
|
for (const d of scopes.diagnostics) {
|
|
2385
2410
|
if (d.kind !== "undeclared") continue;
|
|
2386
2411
|
said.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "type" });
|
|
2387
2412
|
}
|
|
2388
2413
|
const before = partials;
|
|
2389
|
-
const types = (0,
|
|
2414
|
+
const types = (0, import_parser6.analyzeTypes)(program, scopes, {
|
|
2390
2415
|
libs,
|
|
2391
2416
|
resolveModule: resolverFor(file),
|
|
2392
2417
|
reportUnknownTypes: libs.length > 0
|
|
@@ -2409,7 +2434,7 @@ function analyzeModules(modules, config2) {
|
|
|
2409
2434
|
return result;
|
|
2410
2435
|
};
|
|
2411
2436
|
const resolverFor = (file) => (specifier) => {
|
|
2412
|
-
const target = (0,
|
|
2437
|
+
const target = (0, import_parser6.resolveModulePath)(file, specifier, config2);
|
|
2413
2438
|
if (!target) return void 0;
|
|
2414
2439
|
const noteRead = () => {
|
|
2415
2440
|
const read2 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
|
|
@@ -2430,16 +2455,16 @@ function analyzeModules(modules, config2) {
|
|
|
2430
2455
|
let program = byFile.get(target)?.program;
|
|
2431
2456
|
if (!program) {
|
|
2432
2457
|
try {
|
|
2433
|
-
program = (0,
|
|
2458
|
+
program = (0, import_parser6.parse)((0, import_node_fs2.readFileSync)(target, "utf8"));
|
|
2434
2459
|
} catch {
|
|
2435
2460
|
return void 0;
|
|
2436
2461
|
}
|
|
2437
2462
|
}
|
|
2438
2463
|
inProgress.add(target);
|
|
2439
2464
|
try {
|
|
2440
|
-
const scopes = (0,
|
|
2441
|
-
const types = byFile.has(target) ? analyzeModule(target, program).types : (0,
|
|
2442
|
-
const exports2 = (0,
|
|
2465
|
+
const scopes = (0, import_parser6.analyzeScopes)(program, { builtinGlobals: globals });
|
|
2466
|
+
const types = byFile.has(target) ? analyzeModule(target, program).types : (0, import_parser6.analyzeTypes)(program, scopes, { libs, resolveModule: resolverFor(target), diagnostics: false });
|
|
2467
|
+
const exports2 = (0, import_parser6.moduleExports)(program, scopes, types, resolverFor(target));
|
|
2443
2468
|
exportsCache.set(target, exports2);
|
|
2444
2469
|
noteRead();
|
|
2445
2470
|
return exports2;
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -35,7 +35,7 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
|
35
35
|
// src/bundle.ts
|
|
36
36
|
var import_node_fs2 = require("fs");
|
|
37
37
|
var import_node_path2 = require("path");
|
|
38
|
-
var
|
|
38
|
+
var import_parser6 = require("@tilua/parser");
|
|
39
39
|
var import_luau_parser2 = require("luau-parser");
|
|
40
40
|
|
|
41
41
|
// src/config.ts
|
|
@@ -50,10 +50,11 @@ function resolveConfig(input, entry, cwd = process.cwd()) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// src/lower.ts
|
|
53
|
-
var
|
|
53
|
+
var import_parser4 = require("@tilua/parser");
|
|
54
54
|
var import_luau_parser = require("luau-parser");
|
|
55
55
|
|
|
56
56
|
// src/luau.ts
|
|
57
|
+
var import_parser2 = require("@tilua/parser");
|
|
57
58
|
var NO_SPAN = { line: { start: 0, end: 0 }, column: { start: 0, end: 0 } };
|
|
58
59
|
var span = () => ({ line: { ...NO_SPAN.line }, column: { ...NO_SPAN.column } });
|
|
59
60
|
var identifier = (name) => ({ type: "Identifier", name, ...span() });
|
|
@@ -61,7 +62,7 @@ var nil = () => ({ type: "NilLiteral", ...span() });
|
|
|
61
62
|
var boolean = (value) => ({ type: "BooleanLiteral", value, ...span() });
|
|
62
63
|
var number = (value, raw = String(value)) => ({ type: "NumberLiteral", value, raw, ...span() });
|
|
63
64
|
var string = (value) => ({ type: "StringLiteral", value, raw: "", ...span() });
|
|
64
|
-
var member = (object2, property) => isLuauName(property) ? { type: "MemberExpression", object: object2, property: identifier(property), ...span() } : index(object2, string(property));
|
|
65
|
+
var member = (object2, property) => (0, import_parser2.isLuauName)(property) ? { type: "MemberExpression", object: object2, property: identifier(property), ...span() } : index(object2, string(property));
|
|
65
66
|
var index = (object2, key) => ({ type: "IndexExpression", object: object2, index: key, ...span() });
|
|
66
67
|
var call = (callee, args) => ({ type: "CallExpression", callee, arguments: args, ...span() });
|
|
67
68
|
var methodCall = (object2, method, args) => ({ type: "MethodCallExpression", object: object2, method: identifier(method), arguments: args, ...span() });
|
|
@@ -70,7 +71,7 @@ var binary = (operator, left, right) => ({ type: "BinaryExpression", operator, l
|
|
|
70
71
|
var unary = (operator, argument) => ({ type: "UnaryExpression", operator, argument, ...span() });
|
|
71
72
|
var parenthesized = (expression) => ({ type: "ParenthesizedExpression", expression, ...span() });
|
|
72
73
|
var table = (fields) => ({ type: "TableExpression", fields, ...span() });
|
|
73
|
-
var field = (key, value) => isLuauName(key) ? { type: "TableFieldNamed", name: identifier(key), value } : { type: "TableFieldComputed", key: string(key), value };
|
|
74
|
+
var field = (key, value) => (0, import_parser2.isLuauName)(key) ? { type: "TableFieldNamed", name: identifier(key), value } : { type: "TableFieldComputed", key: string(key), value };
|
|
74
75
|
var block = (statements) => ({ type: "Block", statements, ...span() });
|
|
75
76
|
var local = (names, init) => ({
|
|
76
77
|
type: "LocalStatement",
|
|
@@ -114,32 +115,6 @@ var functionBody = (params, statements, hasVarargs = false) => ({
|
|
|
114
115
|
});
|
|
115
116
|
var localFunction = (name, func) => ({ type: "LocalFunctionStatement", name: identifier(name), func, ...span() });
|
|
116
117
|
var program = (statements) => ({ type: "Program", body: block(statements), ...span() });
|
|
117
|
-
var LUAU_KEYWORDS = /* @__PURE__ */ new Set([
|
|
118
|
-
"and",
|
|
119
|
-
"break",
|
|
120
|
-
"do",
|
|
121
|
-
"else",
|
|
122
|
-
"elseif",
|
|
123
|
-
"end",
|
|
124
|
-
"false",
|
|
125
|
-
"for",
|
|
126
|
-
"function",
|
|
127
|
-
"if",
|
|
128
|
-
"in",
|
|
129
|
-
"local",
|
|
130
|
-
"nil",
|
|
131
|
-
"not",
|
|
132
|
-
"or",
|
|
133
|
-
"repeat",
|
|
134
|
-
"return",
|
|
135
|
-
"then",
|
|
136
|
-
"true",
|
|
137
|
-
"until",
|
|
138
|
-
"while"
|
|
139
|
-
]);
|
|
140
|
-
function isLuauName(name) {
|
|
141
|
-
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) && !LUAU_KEYWORDS.has(name);
|
|
142
|
-
}
|
|
143
118
|
|
|
144
119
|
// src/names.ts
|
|
145
120
|
var Names = class _Names {
|
|
@@ -182,6 +157,7 @@ var Names = class _Names {
|
|
|
182
157
|
|
|
183
158
|
// src/language.ts
|
|
184
159
|
var import_node_fs = require("fs");
|
|
160
|
+
var import_parser3 = require("@tilua/parser");
|
|
185
161
|
var read = (name) => (0, import_node_fs.readFileSync)(new URL(`../runtime/${name}`, importMetaUrl), "utf8");
|
|
186
162
|
var implemented = (source) => new Set([...source.matchAll(/function __NAME__\.(\w+)/g)].map((m) => m[1]));
|
|
187
163
|
var array = read("array.luau");
|
|
@@ -215,7 +191,7 @@ function objectKeys(type) {
|
|
|
215
191
|
orders.push(keys);
|
|
216
192
|
}
|
|
217
193
|
const order = JSON.stringify(orders[0] ?? []);
|
|
218
|
-
const known = orders.length && orders.every((keys) => JSON.stringify(keys) === order) ? `{ ${orders[0].map((key) =>
|
|
194
|
+
const known = orders.length && orders.every((keys) => JSON.stringify(keys) === order) ? `{ ${orders[0].map((key) => (0, import_parser3.luauString)(key)).join(", ")} }` : "nil";
|
|
219
195
|
return { known, open: String(open) };
|
|
220
196
|
}
|
|
221
197
|
var plugin = {
|
|
@@ -296,7 +272,7 @@ var Lowerer = class {
|
|
|
296
272
|
// --------------------------------------------------------
|
|
297
273
|
/** A tilua name as Luau can write it: `local` is a keyword there. */
|
|
298
274
|
name(name) {
|
|
299
|
-
if (!LUAU_KEYWORDS.has(name)) return name;
|
|
275
|
+
if (!import_parser2.LUAU_KEYWORDS.has(name)) return name;
|
|
300
276
|
let renamed = this.renamed.get(name);
|
|
301
277
|
if (!renamed) {
|
|
302
278
|
renamed = this.names.fresh(`${name}_`);
|
|
@@ -400,7 +376,7 @@ var Lowerer = class {
|
|
|
400
376
|
const type = this.options.types?.typeOf.get(argument);
|
|
401
377
|
return {
|
|
402
378
|
type,
|
|
403
|
-
typeText: type ? (0,
|
|
379
|
+
typeText: type ? (0, import_parser4.formatType)(type) : void 0,
|
|
404
380
|
source: this.writtenSource(argument),
|
|
405
381
|
declaration: declaration ? this.writtenSource(declaration) : void 0,
|
|
406
382
|
spread: argument.type === "SpreadElement"
|
|
@@ -1479,6 +1455,9 @@ end
|
|
|
1479
1455
|
callValues(node) {
|
|
1480
1456
|
const chain = optionalChain(node);
|
|
1481
1457
|
if (chain) return this.optionalChainExpression(chain);
|
|
1458
|
+
if (node.type === "CallExpression" && (node.callee.type === "SuperExpression" || node.callee.type === "MemberExpression" && node.callee.object.type === "SuperExpression")) {
|
|
1459
|
+
return this.expression(node);
|
|
1460
|
+
}
|
|
1482
1461
|
const object2 = linkObject(node);
|
|
1483
1462
|
if (node.type === "CallExpression" && object2.type === "Identifier") {
|
|
1484
1463
|
return this.link(node, this.reference(object2));
|
|
@@ -1544,7 +1523,7 @@ end
|
|
|
1544
1523
|
...args
|
|
1545
1524
|
]);
|
|
1546
1525
|
}
|
|
1547
|
-
if (!isLuauName(node.method.name)) {
|
|
1526
|
+
if (!import_parser2.isLuauName(node.method.name)) {
|
|
1548
1527
|
this.report(node.method, `'${node.method.name}' is a Luau keyword and cannot be called with ':'`);
|
|
1549
1528
|
}
|
|
1550
1529
|
return methodCall(object2, node.method.name, args);
|
|
@@ -1906,7 +1885,7 @@ function topLevelDeclaration(statements, name) {
|
|
|
1906
1885
|
function moduleName(key) {
|
|
1907
1886
|
const last = key.split("/").filter(Boolean).pop() ?? "module";
|
|
1908
1887
|
const cleaned = last.replace(/[^A-Za-z0-9_]/g, "_");
|
|
1909
|
-
return /^[A-Za-z_]/.test(cleaned) && !LUAU_KEYWORDS.has(cleaned) ? cleaned : `module_${cleaned}`;
|
|
1888
|
+
return /^[A-Za-z_]/.test(cleaned) && !import_parser2.LUAU_KEYWORDS.has(cleaned) ? cleaned : `module_${cleaned}`;
|
|
1910
1889
|
}
|
|
1911
1890
|
function alwaysTruthy(value) {
|
|
1912
1891
|
switch (value.type) {
|
|
@@ -2021,6 +2000,7 @@ var CLASS_METAMETHODS = [
|
|
|
2021
2000
|
];
|
|
2022
2001
|
|
|
2023
2002
|
// src/linemap.ts
|
|
2003
|
+
var import_parser5 = require("@tilua/parser");
|
|
2024
2004
|
function statementsOf(node) {
|
|
2025
2005
|
const out = [];
|
|
2026
2006
|
const visit = (value) => {
|
|
@@ -2053,7 +2033,7 @@ function buildLineMap(emitted, printed, origins) {
|
|
|
2053
2033
|
}
|
|
2054
2034
|
function lineMapSource(map) {
|
|
2055
2035
|
const entries = [...map.entries()].sort((a, b) => a[0] - b[0]);
|
|
2056
|
-
const quoted = (s) =>
|
|
2036
|
+
const quoted = (s) => (0, import_parser5.luauString)(s.replace(/\\/g, "/"));
|
|
2057
2037
|
return `{${entries.map(([line, o]) => `[${line}]={${quoted(o.file)},${o.line}}`).join(",")}}`;
|
|
2058
2038
|
}
|
|
2059
2039
|
|
|
@@ -2102,13 +2082,13 @@ async function bundle(options) {
|
|
|
2102
2082
|
const parsed = parseFile(file, diagnostics, directives);
|
|
2103
2083
|
if (!parsed) return { modules: [], diagnostics };
|
|
2104
2084
|
const { program: program3, text } = parsed;
|
|
2105
|
-
const scopes = (0,
|
|
2085
|
+
const scopes = (0, import_parser6.analyzeScopes)(program3);
|
|
2106
2086
|
for (const d of scopes.diagnostics) {
|
|
2107
2087
|
diagnostics.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "scope" });
|
|
2108
2088
|
}
|
|
2109
2089
|
sources.set(file, { file, name: nameOf(file), text, program: program3, scopes });
|
|
2110
2090
|
for (const specifier of importedSpecifiers(program3)) {
|
|
2111
|
-
const target = (0,
|
|
2091
|
+
const target = (0, import_parser6.resolveModulePath)(file, specifier, config);
|
|
2112
2092
|
if (target && !target.endsWith(".d.tilua")) queue.push(target);
|
|
2113
2093
|
}
|
|
2114
2094
|
}
|
|
@@ -2142,7 +2122,7 @@ async function bundle(options) {
|
|
|
2142
2122
|
lines: `${G}.lines`,
|
|
2143
2123
|
fail: `${G}.fail`,
|
|
2144
2124
|
resolve: (specifier) => {
|
|
2145
|
-
const target = (0,
|
|
2125
|
+
const target = (0, import_parser6.resolveModulePath)(file, specifier, config);
|
|
2146
2126
|
if (!target || target.endsWith(".d.tilua") || !sources.has(target)) return void 0;
|
|
2147
2127
|
pending.push(target);
|
|
2148
2128
|
return sources.get(target).name;
|
|
@@ -2164,14 +2144,14 @@ async function bundle(options) {
|
|
|
2164
2144
|
}
|
|
2165
2145
|
for (const [file, fileDirectives] of directives) {
|
|
2166
2146
|
const semantic = diagnostics.filter((d) => d.file === file && (d.category === "scope" || d.category === "type"));
|
|
2167
|
-
const { kept, unusedExpectErrors } = (0,
|
|
2147
|
+
const { kept, unusedExpectErrors } = (0, import_parser6.applyDirectives)(fileDirectives, semantic, (d) => d.line);
|
|
2168
2148
|
const suppressed = new Set(semantic.filter((d) => !kept.includes(d)));
|
|
2169
2149
|
const remaining = diagnostics.filter((d) => !suppressed.has(d));
|
|
2170
2150
|
diagnostics.length = 0;
|
|
2171
2151
|
diagnostics.push(...remaining);
|
|
2172
2152
|
if (options.typeCheck === false) continue;
|
|
2173
2153
|
for (const d of unusedExpectErrors) {
|
|
2174
|
-
diagnostics.push({ file, message:
|
|
2154
|
+
diagnostics.push({ file, message: import_parser6.UNUSED_EXPECT_ERROR, line: d.line, column: d.column, category: "type" });
|
|
2175
2155
|
}
|
|
2176
2156
|
}
|
|
2177
2157
|
const moduleProblems = new Set(diagnostics.filter((d) => d.category === "module").map((d) => `${d.file}:${d.line}:${d.column}:${d.message}`));
|
|
@@ -2207,7 +2187,7 @@ async function bundle(options) {
|
|
|
2207
2187
|
const result = names.fresh("result");
|
|
2208
2188
|
const entryStatements = (0, import_luau_parser2.parse)(`
|
|
2209
2189
|
local ${ok}, ${result} = xpcall(function()
|
|
2210
|
-
return ${G}.require(${
|
|
2190
|
+
return ${G}.require(${(0, import_parser6.luauString)(sources.get(entry).name)})
|
|
2211
2191
|
end, ${G}.fail)
|
|
2212
2192
|
if not ${ok} then
|
|
2213
2193
|
error(${result}, 0)
|
|
@@ -2236,19 +2216,64 @@ ${G} = {
|
|
|
2236
2216
|
-- and a traceback of those places follows. A message that is not a string
|
|
2237
2217
|
-- is left as it is, and a position that is not this bundle's (a library
|
|
2238
2218
|
-- that raised its error with one already mapped) is left alone.
|
|
2219
|
+
--
|
|
2220
|
+
-- It is an \`xpcall\` handler, and a handler that fails loses the error
|
|
2221
|
+
-- altogether: Luau reports only "error in error handling". So the work is
|
|
2222
|
+
-- done under \`pcall\`, leaning on nothing it has not checked is there (an
|
|
2223
|
+
-- executor may lack \`debug.info\`, or refuse it), and an error it cannot
|
|
2224
|
+
-- map is still answered as Luau raised it, with why.
|
|
2239
2225
|
fail = function(message)
|
|
2226
|
+
-- Taken first, before anything that can fail. Level 3 is the frame
|
|
2227
|
+
-- that raised: past \`pcall\` and this handler.
|
|
2228
|
+
local trace
|
|
2229
|
+
if debug ~= nil and debug.traceback ~= nil then
|
|
2230
|
+
local ok, text = pcall(debug.traceback, "", 3)
|
|
2231
|
+
if ok and type(text) == "string" then
|
|
2232
|
+
trace = text
|
|
2233
|
+
end
|
|
2234
|
+
end
|
|
2235
|
+
local ok, described = pcall(${G}.describe, message, trace)
|
|
2236
|
+
if ok then
|
|
2237
|
+
return described
|
|
2238
|
+
end
|
|
2239
|
+
if type(message) ~= "string" then
|
|
2240
|
+
return message
|
|
2241
|
+
end
|
|
2242
|
+
local why = type(described) == "string" and described or "unknown"
|
|
2243
|
+
return message .. "\\n(tilua could not map this error to the project's files: " .. why .. ")" .. (trace or "")
|
|
2244
|
+
end,
|
|
2245
|
+
-- \`fail\`'s work. \`trace\` is a traceback from where the error was
|
|
2246
|
+
-- raised, or nil when none could be taken.
|
|
2247
|
+
describe = function(message, trace)
|
|
2240
2248
|
if type(message) ~= "string" then
|
|
2241
2249
|
return message
|
|
2242
2250
|
end
|
|
2243
2251
|
local lines = ${G}.lines
|
|
2244
2252
|
if lines == nil then
|
|
2245
|
-
return
|
|
2253
|
+
return message .. (trace or "")
|
|
2246
2254
|
end
|
|
2255
|
+
-- The bundle's name, as a position in a message spells it: where this
|
|
2256
|
+
-- function is, asked of the debug library by the function rather
|
|
2257
|
+
-- than by a level, which \`pcall\` would shift. Failing that, a message
|
|
2258
|
+
-- starts with the position it was raised at.
|
|
2247
2259
|
local chunk
|
|
2248
|
-
if debug.info ~= nil then
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2260
|
+
if debug ~= nil and debug.info ~= nil then
|
|
2261
|
+
local found, source = pcall(debug.info, ${G}.describe, "s")
|
|
2262
|
+
if found and type(source) == "string" then
|
|
2263
|
+
chunk = source
|
|
2264
|
+
end
|
|
2265
|
+
end
|
|
2266
|
+
if chunk == nil and debug ~= nil and debug.getinfo ~= nil then
|
|
2267
|
+
local found, info = pcall(debug.getinfo, ${G}.describe, "S")
|
|
2268
|
+
if found and type(info) == "table" and type(info.short_src) == "string" then
|
|
2269
|
+
chunk = info.short_src
|
|
2270
|
+
end
|
|
2271
|
+
end
|
|
2272
|
+
if chunk == nil then
|
|
2273
|
+
chunk = message:match("^(.-):%d+:")
|
|
2274
|
+
end
|
|
2275
|
+
if chunk == nil then
|
|
2276
|
+
return message .. (trace or "")
|
|
2252
2277
|
end
|
|
2253
2278
|
local prefix = chunk:gsub("%p", "%%%0")
|
|
2254
2279
|
local function place(line)
|
|
@@ -2262,7 +2287,7 @@ ${G} = {
|
|
|
2262
2287
|
return place(line)
|
|
2263
2288
|
end)
|
|
2264
2289
|
local frames = {}
|
|
2265
|
-
for frame in
|
|
2290
|
+
for frame in (trace or ""):gmatch("[^\\n]+") do
|
|
2266
2291
|
local line = frame:match(prefix .. ":(%d+)")
|
|
2267
2292
|
local at = line ~= nil and place(line) or nil
|
|
2268
2293
|
if at ~= nil then
|
|
@@ -2347,13 +2372,13 @@ function parseFile(file, diagnostics, directives) {
|
|
|
2347
2372
|
return void 0;
|
|
2348
2373
|
}
|
|
2349
2374
|
try {
|
|
2350
|
-
const program2 = (0,
|
|
2351
|
-
const found = (0,
|
|
2375
|
+
const program2 = (0, import_parser6.parse)(text);
|
|
2376
|
+
const found = (0, import_parser6.directivesOf)(text);
|
|
2352
2377
|
directives.set(file, found);
|
|
2353
2378
|
if (stamp !== void 0) parsedFiles.set(file, { stamp, program: program2, text, directives: found });
|
|
2354
2379
|
return { program: program2, text };
|
|
2355
2380
|
} catch (error) {
|
|
2356
|
-
if (error instanceof
|
|
2381
|
+
if (error instanceof import_parser6.ParseError || error instanceof import_parser6.LexError) {
|
|
2357
2382
|
const { line, column } = error;
|
|
2358
2383
|
diagnostics.push({ file, message: error.message, line, column, category: "syntax" });
|
|
2359
2384
|
return void 0;
|
|
@@ -2375,7 +2400,7 @@ function parsedLibrary(file) {
|
|
|
2375
2400
|
}
|
|
2376
2401
|
const cached = libraryCache.get(file);
|
|
2377
2402
|
if (cached && cached.stamp === stamp) return cached.program;
|
|
2378
|
-
const program2 = (0,
|
|
2403
|
+
const program2 = (0, import_parser6.parse)((0, import_node_fs2.readFileSync)(file, "utf8"));
|
|
2379
2404
|
libraryCache.set(file, { stamp, program: program2 });
|
|
2380
2405
|
return program2;
|
|
2381
2406
|
}
|
|
@@ -2383,7 +2408,7 @@ var moduleAnalyses = /* @__PURE__ */ new Map();
|
|
|
2383
2408
|
function analyzeModules(modules, config) {
|
|
2384
2409
|
const out = [];
|
|
2385
2410
|
const analyses = /* @__PURE__ */ new Map();
|
|
2386
|
-
const libraries = config ? (0,
|
|
2411
|
+
const libraries = config ? (0, import_parser6.resolveTypeLibraries)(config) : { files: [], lowerings: [], problems: [] };
|
|
2387
2412
|
for (const p of libraries.problems) out.push({ file: p.file, message: p.message, line: p.line ?? 1, column: p.column ?? 1, category: "config" });
|
|
2388
2413
|
const libs = libraries.files.map(parsedLibrary);
|
|
2389
2414
|
const globals = libs.flatMap((lib) => lib.body.statements.flatMap((s) => s.type === "DeclareStatement" ? [s.name] : []));
|
|
@@ -2403,14 +2428,14 @@ function analyzeModules(modules, config) {
|
|
|
2403
2428
|
readWhile.set(file, remembered.read);
|
|
2404
2429
|
return remembered.result;
|
|
2405
2430
|
}
|
|
2406
|
-
const scopes = (0,
|
|
2431
|
+
const scopes = (0, import_parser6.analyzeScopes)(program2, { builtinGlobals: globals, reportUndeclared: libs.length > 0 });
|
|
2407
2432
|
const said = [];
|
|
2408
2433
|
for (const d of scopes.diagnostics) {
|
|
2409
2434
|
if (d.kind !== "undeclared") continue;
|
|
2410
2435
|
said.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "type" });
|
|
2411
2436
|
}
|
|
2412
2437
|
const before = partials;
|
|
2413
|
-
const types = (0,
|
|
2438
|
+
const types = (0, import_parser6.analyzeTypes)(program2, scopes, {
|
|
2414
2439
|
libs,
|
|
2415
2440
|
resolveModule: resolverFor(file),
|
|
2416
2441
|
reportUnknownTypes: libs.length > 0
|
|
@@ -2433,7 +2458,7 @@ function analyzeModules(modules, config) {
|
|
|
2433
2458
|
return result;
|
|
2434
2459
|
};
|
|
2435
2460
|
const resolverFor = (file) => (specifier) => {
|
|
2436
|
-
const target = (0,
|
|
2461
|
+
const target = (0, import_parser6.resolveModulePath)(file, specifier, config);
|
|
2437
2462
|
if (!target) return void 0;
|
|
2438
2463
|
const noteRead = () => {
|
|
2439
2464
|
const read2 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
|
|
@@ -2454,16 +2479,16 @@ function analyzeModules(modules, config) {
|
|
|
2454
2479
|
let program2 = byFile.get(target)?.program;
|
|
2455
2480
|
if (!program2) {
|
|
2456
2481
|
try {
|
|
2457
|
-
program2 = (0,
|
|
2482
|
+
program2 = (0, import_parser6.parse)((0, import_node_fs2.readFileSync)(target, "utf8"));
|
|
2458
2483
|
} catch {
|
|
2459
2484
|
return void 0;
|
|
2460
2485
|
}
|
|
2461
2486
|
}
|
|
2462
2487
|
inProgress.add(target);
|
|
2463
2488
|
try {
|
|
2464
|
-
const scopes = (0,
|
|
2465
|
-
const types = byFile.has(target) ? analyzeModule(target, program2).types : (0,
|
|
2466
|
-
const exports2 = (0,
|
|
2489
|
+
const scopes = (0, import_parser6.analyzeScopes)(program2, { builtinGlobals: globals });
|
|
2490
|
+
const types = byFile.has(target) ? analyzeModule(target, program2).types : (0, import_parser6.analyzeTypes)(program2, scopes, { libs, resolveModule: resolverFor(target), diagnostics: false });
|
|
2491
|
+
const exports2 = (0, import_parser6.moduleExports)(program2, scopes, types, resolverFor(target));
|
|
2467
2492
|
exportsCache.set(target, exports2);
|
|
2468
2493
|
noteRead();
|
|
2469
2494
|
return exports2;
|
|
@@ -2498,35 +2523,35 @@ function allStatements(statements) {
|
|
|
2498
2523
|
// src/compile.ts
|
|
2499
2524
|
var import_node_fs3 = require("fs");
|
|
2500
2525
|
var import_node_path3 = require("path");
|
|
2501
|
-
var
|
|
2526
|
+
var import_parser7 = require("@tilua/parser");
|
|
2502
2527
|
var import_luau_parser3 = require("luau-parser");
|
|
2503
2528
|
async function compile(source, config) {
|
|
2504
2529
|
let program2;
|
|
2505
2530
|
try {
|
|
2506
|
-
program2 = (0,
|
|
2531
|
+
program2 = (0, import_parser7.parse)(source);
|
|
2507
2532
|
} catch (error) {
|
|
2508
|
-
if (error instanceof
|
|
2533
|
+
if (error instanceof import_parser7.ParseError || error instanceof import_parser7.LexError) {
|
|
2509
2534
|
const { line, column } = error;
|
|
2510
2535
|
return { diagnostics: [{ message: error.message, line, column }] };
|
|
2511
2536
|
}
|
|
2512
2537
|
throw error;
|
|
2513
2538
|
}
|
|
2514
|
-
const scopes = (0,
|
|
2539
|
+
const scopes = (0, import_parser7.analyzeScopes)(program2);
|
|
2515
2540
|
const diagnostics = scopes.diagnostics.map((d) => ({
|
|
2516
2541
|
message: d.message,
|
|
2517
2542
|
line: d.node.line.start,
|
|
2518
2543
|
column: d.node.column.start
|
|
2519
2544
|
}));
|
|
2520
2545
|
const { config: resolved, problems } = resolveConfig(config, (0, import_node_path3.resolve)("main.tilua"));
|
|
2521
|
-
const libraries = resolved ? (0,
|
|
2546
|
+
const libraries = resolved ? (0, import_parser7.resolveTypeLibraries)(resolved) : { files: [], lowerings: [], problems: [] };
|
|
2522
2547
|
const { lowerings, problems: loweringProblems } = await loadLowerings(libraries.lowerings);
|
|
2523
2548
|
for (const p of [...problems, ...libraries.problems]) {
|
|
2524
2549
|
diagnostics.push({ message: p.message, line: p.line ?? 1, column: p.column ?? 1 });
|
|
2525
2550
|
}
|
|
2526
2551
|
for (const p of loweringProblems) diagnostics.push({ message: p.message, line: 1, column: 1 });
|
|
2527
|
-
const libs = libraries.files.map((file) => (0,
|
|
2552
|
+
const libs = libraries.files.map((file) => (0, import_parser7.parse)((0, import_node_fs3.readFileSync)(file, "utf8")));
|
|
2528
2553
|
const lowered = lower(program2, scopes, {
|
|
2529
|
-
types: (0,
|
|
2554
|
+
types: (0, import_parser7.analyzeTypes)(program2, scopes, { libs, diagnostics: false }),
|
|
2530
2555
|
lowerings,
|
|
2531
2556
|
target: resolved?.target,
|
|
2532
2557
|
source
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tilua/compiler",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Compiles tilua to Luau: lowers the @tilua/parser AST to a luau-parser AST and prints it",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"type": "module",
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"luau-parser": "^1.0.3",
|
|
41
|
-
"@tilua/parser": "^3.
|
|
41
|
+
"@tilua/parser": "^3.1.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/node": "^22.20.2",
|
package/runtime/object.luau
CHANGED
|
@@ -52,3 +52,42 @@ function __NAME__.entries(known, open, t)
|
|
|
52
52
|
end
|
|
53
53
|
return out
|
|
54
54
|
end
|
|
55
|
+
|
|
56
|
+
-- JavaScript's `Object` functions, as methods of the table they take first.
|
|
57
|
+
-- `known` and `open` are handed to every method here; these need neither.
|
|
58
|
+
|
|
59
|
+
-- The table's own member, not one its metatable's `__index` lends it.
|
|
60
|
+
function __NAME__.hasOwn(known, open, t, key)
|
|
61
|
+
return rawget(t, key) ~= nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
function __NAME__.hasOwnProperty(known, open, t, key)
|
|
65
|
+
return rawget(t, key) ~= nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
-- Each source's members onto `t`, later sources over earlier ones; a `nil`
|
|
69
|
+
-- source is skipped, as JavaScript skips `null`.
|
|
70
|
+
function __NAME__.assign(known, open, t, ...)
|
|
71
|
+
for i = 1, select("#", ...) do
|
|
72
|
+
local source = select(i, ...)
|
|
73
|
+
if source ~= nil then
|
|
74
|
+
for key, value in pairs(source) do
|
|
75
|
+
t[key] = value
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
return t
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
-- Luau freezes a table in place. Lua 5.1 has no way to, and the table is
|
|
83
|
+
-- answered as it is.
|
|
84
|
+
function __NAME__.freeze(known, open, t)
|
|
85
|
+
if table.freeze ~= nil and not table.isfrozen(t) then
|
|
86
|
+
table.freeze(t)
|
|
87
|
+
end
|
|
88
|
+
return t
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
function __NAME__.isFrozen(known, open, t)
|
|
92
|
+
return table.isfrozen ~= nil and table.isfrozen(t)
|
|
93
|
+
end
|