@tilua/compiler 2.2.0 → 2.4.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-TBSJRVKA.js → chunk-3ZQ3EITC.js} +102 -93
- package/dist/cli.cjs +102 -93
- package/dist/cli.js +1 -1
- package/dist/index.cjs +102 -93
- package/dist/index.js +1 -1
- package/package.json +2 -2
|
@@ -262,12 +262,10 @@ var Lowerer = class {
|
|
|
262
262
|
for (const node of allNodes(this.source.body)) {
|
|
263
263
|
const statement = node;
|
|
264
264
|
if (statement.type === "VariableDeclaration") {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (binding) this.declarations.set(binding.id, value);
|
|
270
|
-
});
|
|
265
|
+
const target = statement.name;
|
|
266
|
+
if (target.type !== "IdentifierPattern" || !statement.init) continue;
|
|
267
|
+
const binding = this.bindingByDeclaration.get(target);
|
|
268
|
+
if (binding) this.declarations.set(binding.id, statement.init);
|
|
271
269
|
} else if (statement.type === "FunctionDeclaration") {
|
|
272
270
|
const binding = this.bindingByDeclaration.get(statement.name);
|
|
273
271
|
if (binding) this.declarations.set(binding.id, statement);
|
|
@@ -541,7 +539,7 @@ var Lowerer = class {
|
|
|
541
539
|
const declaration = statement.declaration;
|
|
542
540
|
if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") {
|
|
543
541
|
exportBinding(declaration.name, declaration.name.name);
|
|
544
|
-
} else for (const pattern of declaration.
|
|
542
|
+
} else for (const pattern of identifierPatterns(declaration.name)) exportBinding(pattern, pattern.name);
|
|
545
543
|
break;
|
|
546
544
|
}
|
|
547
545
|
case "ExportDefaultStatement":
|
|
@@ -577,7 +575,7 @@ var Lowerer = class {
|
|
|
577
575
|
for (const statement of statements) {
|
|
578
576
|
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
579
577
|
if (declaration.type === "VariableDeclaration") {
|
|
580
|
-
for (const pattern of declaration.
|
|
578
|
+
for (const pattern of identifierPatterns(declaration.name)) {
|
|
581
579
|
if (!this.isRewritten(pattern)) locals.push(this.name(pattern.name));
|
|
582
580
|
}
|
|
583
581
|
} else if ((declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") && !this.isRewritten(declaration.name)) {
|
|
@@ -674,7 +672,7 @@ var Lowerer = class {
|
|
|
674
672
|
continue;
|
|
675
673
|
}
|
|
676
674
|
if (statement.type !== "VariableDeclaration") continue;
|
|
677
|
-
const patterns = statement.
|
|
675
|
+
const patterns = identifierPatterns(statement.name);
|
|
678
676
|
const end = { line: { start: statement.line.end }, column: { start: statement.column.end } };
|
|
679
677
|
const needed = patterns.some((pattern) => {
|
|
680
678
|
const binding = this.bindingByDeclaration.get(pattern);
|
|
@@ -715,7 +713,7 @@ var Lowerer = class {
|
|
|
715
713
|
const body = [];
|
|
716
714
|
for (const statement of statements) {
|
|
717
715
|
if (statement.type === "VariableDeclaration") {
|
|
718
|
-
for (const pattern of statement.
|
|
716
|
+
for (const pattern of identifierPatterns(statement.name)) locals.push(this.name(pattern.name));
|
|
719
717
|
body.push(...this.variableDeclaration(statement, "assign"));
|
|
720
718
|
} else if (statement.type === "FunctionDeclaration") {
|
|
721
719
|
locals.push(this.name(statement.name.name));
|
|
@@ -848,12 +846,13 @@ var Lowerer = class {
|
|
|
848
846
|
}];
|
|
849
847
|
case "GenericForStatement": {
|
|
850
848
|
const prelude = [];
|
|
849
|
+
const variable = declared(node.variable);
|
|
851
850
|
let item;
|
|
852
|
-
if (
|
|
853
|
-
item = this.name(
|
|
851
|
+
if (variable.type === "IdentifierPattern") {
|
|
852
|
+
item = this.name(variable.name);
|
|
854
853
|
} else {
|
|
855
854
|
item = this.names.fresh("item");
|
|
856
|
-
prelude.push(...this.destructure(
|
|
855
|
+
prelude.push(...this.destructure(variable, identifier(item), "declare"));
|
|
857
856
|
}
|
|
858
857
|
const form = this.options.types?.loops.get(node) ?? { walks: "values", viaIter: false };
|
|
859
858
|
let source = this.expression(node.iterator);
|
|
@@ -867,7 +866,7 @@ var Lowerer = class {
|
|
|
867
866
|
)];
|
|
868
867
|
}
|
|
869
868
|
case "ReturnStatement":
|
|
870
|
-
return [returns(node.argument ? [this.expression(node.argument)] : [])];
|
|
869
|
+
return [returns(node.argument ? [this.keptToOne(this.expression(node.argument))] : [])];
|
|
871
870
|
case "BreakStatement":
|
|
872
871
|
return [{ type: "BreakStatement", ...spanOf(node) }];
|
|
873
872
|
case "ContinueStatement":
|
|
@@ -1116,63 +1115,37 @@ end
|
|
|
1116
1115
|
...spanOf(node)
|
|
1117
1116
|
};
|
|
1118
1117
|
}
|
|
1119
|
-
/** `const a
|
|
1120
|
-
* every name is declared up front — nothing is declared,
|
|
1118
|
+
/** `const a = x`, `const { a, b } = x`. In "assign" mode — a module's top
|
|
1119
|
+
* level, where every name is declared up front — nothing is declared,
|
|
1120
|
+
* only assigned. */
|
|
1121
1121
|
variableDeclaration(node, mode) {
|
|
1122
|
-
|
|
1123
|
-
|
|
1122
|
+
const target = declared(node.name);
|
|
1123
|
+
if (!node.init) {
|
|
1124
|
+
const names = identifierPatterns(target);
|
|
1125
|
+
if (mode === "declare") return [local(names.map((p) => this.name(p.name)), [])];
|
|
1126
|
+
const exported = names.filter((p) => this.isRewritten(p));
|
|
1124
1127
|
return exported.length ? [assign(exported.map((p) => this.reference(p)), exported.map(() => nil()))] : [];
|
|
1125
1128
|
}
|
|
1126
|
-
const
|
|
1127
|
-
if (
|
|
1128
|
-
|
|
1129
|
-
return mode === "declare" ? [local(names2.map((n) => this.name(n.name)), init)] : [assign(names2.map((n) => this.reference(n)), init)];
|
|
1130
|
-
}
|
|
1131
|
-
const only = node.names.length === 1 && node.init.length === 1 ? node.names[0] : void 0;
|
|
1132
|
-
if (only && only.type !== "IdentifierPattern") {
|
|
1133
|
-
if (init[0].type === "Identifier") return this.destructure(only, init[0], mode);
|
|
1134
|
-
const temp = this.names.fresh("ref");
|
|
1135
|
-
return [local([temp], init), ...this.destructure(only, identifier(temp), mode)];
|
|
1136
|
-
}
|
|
1137
|
-
const after = [];
|
|
1138
|
-
const names = [];
|
|
1139
|
-
const targets = [];
|
|
1140
|
-
for (const target of node.names) {
|
|
1141
|
-
if (target.type === "IdentifierPattern") {
|
|
1142
|
-
names.push(this.name(target.name));
|
|
1143
|
-
targets.push(this.reference(target));
|
|
1144
|
-
continue;
|
|
1145
|
-
}
|
|
1146
|
-
const temp = this.names.fresh("ref");
|
|
1147
|
-
names.push(temp);
|
|
1148
|
-
targets.push(identifier(temp));
|
|
1149
|
-
after.push(...this.destructure(target, identifier(temp), mode));
|
|
1129
|
+
const value = this.expression(node.init);
|
|
1130
|
+
if (target.type === "IdentifierPattern") {
|
|
1131
|
+
return mode === "declare" ? [local([this.name(target.name)], [value])] : [assign([this.reference(target)], [value])];
|
|
1150
1132
|
}
|
|
1151
|
-
if (
|
|
1152
|
-
const
|
|
1153
|
-
return [
|
|
1133
|
+
if (value.type === "Identifier") return this.destructure(target, value, mode);
|
|
1134
|
+
const temp = this.names.fresh("ref");
|
|
1135
|
+
return [local([temp], [value]), ...this.destructure(target, identifier(temp), mode)];
|
|
1154
1136
|
}
|
|
1155
1137
|
assignment(node) {
|
|
1156
|
-
|
|
1157
|
-
|
|
1138
|
+
const target = node.target;
|
|
1139
|
+
const value = this.expression(node.value);
|
|
1140
|
+
if (target.type !== "ObjectPattern" && target.type !== "ArrayPattern") {
|
|
1141
|
+
return [assign([this.expression(target)], [value])];
|
|
1158
1142
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
if (only && (only.type === "ObjectPattern" || only.type === "ArrayPattern") && values[0].type === "Identifier") {
|
|
1162
|
-
const statements = this.destructure(only, values[0], "assign");
|
|
1143
|
+
if (value.type === "Identifier") {
|
|
1144
|
+
const statements = this.destructure(target, value, "assign");
|
|
1163
1145
|
return statements.some((s) => s.type === "LocalStatement") ? [doBlock(statements)] : statements;
|
|
1164
1146
|
}
|
|
1165
|
-
const
|
|
1166
|
-
|
|
1167
|
-
node.targets.forEach((target, i) => {
|
|
1168
|
-
const value = identifier(temps[i]);
|
|
1169
|
-
if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
|
|
1170
|
-
body.push(...this.destructure(target, value, "assign"));
|
|
1171
|
-
} else {
|
|
1172
|
-
body.push(assign([this.expression(target)], [value]));
|
|
1173
|
-
}
|
|
1174
|
-
});
|
|
1175
|
-
return [doBlock(body)];
|
|
1147
|
+
const temp = this.names.fresh("ref");
|
|
1148
|
+
return [doBlock([local([temp], [value]), ...this.destructure(target, identifier(temp), "assign")])];
|
|
1176
1149
|
}
|
|
1177
1150
|
// --------------------------------------------------------
|
|
1178
1151
|
// Destructuring
|
|
@@ -1188,6 +1161,12 @@ end
|
|
|
1188
1161
|
const reads = [];
|
|
1189
1162
|
const after = [];
|
|
1190
1163
|
const bind = (target, value, fallback) => {
|
|
1164
|
+
if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
1165
|
+
const reference = this.expression(target);
|
|
1166
|
+
reads.push({ name: "", target: reference, value, temp: false });
|
|
1167
|
+
if (fallback) after.push(this.defaultValue(reference, fallback));
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1191
1170
|
if (target.type === "IdentifierPattern") {
|
|
1192
1171
|
const name2 = this.name(target.name);
|
|
1193
1172
|
const reference = mode === "declare" ? identifier(name2) : this.reference(target);
|
|
@@ -1260,6 +1239,11 @@ end
|
|
|
1260
1239
|
* a temporary that a nested pattern then destructures (`then`). The
|
|
1261
1240
|
* statement creating it is pushed onto `after`. */
|
|
1262
1241
|
restTarget(target, initial, mode, after) {
|
|
1242
|
+
if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
1243
|
+
const reference = this.expression(target);
|
|
1244
|
+
after.push(assign([reference], [initial]));
|
|
1245
|
+
return { target: reference, then: [] };
|
|
1246
|
+
}
|
|
1263
1247
|
if (target.type === "IdentifierPattern") {
|
|
1264
1248
|
if (mode === "declare") {
|
|
1265
1249
|
const name2 = this.name(target.name);
|
|
@@ -1307,7 +1291,7 @@ end
|
|
|
1307
1291
|
if (through !== void 0) {
|
|
1308
1292
|
return call(member(this.superClassReference(node), through), [
|
|
1309
1293
|
identifier(this.thisName()),
|
|
1310
|
-
...this.
|
|
1294
|
+
...this.arguments(node.arguments)
|
|
1311
1295
|
]);
|
|
1312
1296
|
}
|
|
1313
1297
|
}
|
|
@@ -1392,35 +1376,29 @@ end
|
|
|
1392
1376
|
return this.link(node, this.expression(object));
|
|
1393
1377
|
}
|
|
1394
1378
|
/** A call is one value in tilua, and Luau would hand on every value one
|
|
1395
|
-
* in the last place of an argument list or
|
|
1396
|
-
*
|
|
1397
|
-
*
|
|
1398
|
-
keptToOne(
|
|
1399
|
-
|
|
1400
|
-
const last = values[values.length - 1];
|
|
1401
|
-
if (!node || !last || !expandsToMany(last)) return values;
|
|
1402
|
-
const type = this.options.types?.typeOf.get(node);
|
|
1403
|
-
if (type && type.kind !== "any" && type.kind !== "unknown") return values;
|
|
1404
|
-
return [...values.slice(0, -1), parenthesized(last)];
|
|
1379
|
+
* returns in the last place of an argument list or a `return`: it is
|
|
1380
|
+
* kept to one there, `f((g()))`. Every value is taken only where the
|
|
1381
|
+
* program asks for them, as an array's last element: `[g()]`. */
|
|
1382
|
+
keptToOne(value) {
|
|
1383
|
+
return expandsToMany(value) ? parenthesized(value) : value;
|
|
1405
1384
|
}
|
|
1406
1385
|
/** Is this `scriptArgs` the language's own, not a name the file declared? */
|
|
1407
1386
|
isLanguageGlobal(node) {
|
|
1408
1387
|
const id = this.bindingIdOf(node);
|
|
1409
1388
|
return id === void 0 || this.scopes.bindings.get(id)?.kind === "global";
|
|
1410
1389
|
}
|
|
1411
|
-
/** A
|
|
1412
|
-
* an assignment's. `f(a, ...xs)` is Lua's own last-value expansion,
|
|
1390
|
+
/** A call's arguments. `f(a, ...xs)` is Lua's own last-value expansion,
|
|
1413
1391
|
* `f(a, table.unpack(xs))`; a spread anywhere else cannot be, since only
|
|
1414
1392
|
* the last value of a list expands, so the whole list is built as an
|
|
1415
|
-
* array first and that is what expands.
|
|
1416
|
-
*
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
values(list, asArguments = false) {
|
|
1393
|
+
* array first and that is what expands. A call written last is one
|
|
1394
|
+
* value — see `keptToOne`. */
|
|
1395
|
+
arguments(written) {
|
|
1396
|
+
const { items: list, many } = packs(written);
|
|
1420
1397
|
const spreads = list.filter((a) => a.type === "SpreadElement");
|
|
1421
1398
|
if (!spreads.length) {
|
|
1422
1399
|
const lowered = list.map((a) => this.expression(a));
|
|
1423
|
-
|
|
1400
|
+
const last2 = list.length - 1;
|
|
1401
|
+
return lowered.length && !many.has(list[last2]) ? [...lowered.slice(0, -1), this.keptToOne(lowered[last2])] : lowered;
|
|
1424
1402
|
}
|
|
1425
1403
|
const unpack = (value) => call(member(this.builtin("table"), "unpack"), [value]);
|
|
1426
1404
|
const last = list[list.length - 1];
|
|
@@ -1442,13 +1420,13 @@ end
|
|
|
1442
1420
|
return index(object, this.expression(node.index));
|
|
1443
1421
|
case "CallExpression": {
|
|
1444
1422
|
const lowered = this.loweredGlobalCall(node);
|
|
1445
|
-
const args = this.
|
|
1423
|
+
const args = this.arguments(node.arguments);
|
|
1446
1424
|
if (!lowered) return call(object, args);
|
|
1447
1425
|
return call(calleePath(lowered.callee), [...lowered.prepend, ...args]);
|
|
1448
1426
|
}
|
|
1449
1427
|
case "MethodCallExpression": {
|
|
1450
1428
|
const lowered = this.loweredMethodCall(node);
|
|
1451
|
-
const args = this.
|
|
1429
|
+
const args = this.arguments(node.arguments);
|
|
1452
1430
|
if (lowered) {
|
|
1453
1431
|
return call(calleePath(lowered.callee), [
|
|
1454
1432
|
...lowered.prepend,
|
|
@@ -1681,33 +1659,55 @@ end
|
|
|
1681
1659
|
/** `[1, ...xs, 2]`. Without a spread it is a Luau sequence; with one, the
|
|
1682
1660
|
* runs of plain elements and the spread arrays are joined by `concat`. */
|
|
1683
1661
|
arrayExpression(node) {
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
return table(
|
|
1662
|
+
const elements = packs(node.elements).items;
|
|
1663
|
+
if (!elements.some((e) => e.type === "SpreadElement")) {
|
|
1664
|
+
return table(elements.map((e) => ({ type: "TableFieldPositional", value: this.expression(e) })));
|
|
1687
1665
|
}
|
|
1688
1666
|
const parts = [];
|
|
1689
1667
|
let current = [];
|
|
1690
|
-
const flush = () => {
|
|
1668
|
+
const flush = (last) => {
|
|
1691
1669
|
if (!current.length) return;
|
|
1692
1670
|
parts.push(table(current.map((e, i) => {
|
|
1693
1671
|
const value = this.expression(e);
|
|
1694
|
-
|
|
1695
|
-
return { type: "TableFieldPositional", value: last && expandsToMany(value) ? parenthesized(value) : value };
|
|
1672
|
+
return { type: "TableFieldPositional", value: last || i < current.length - 1 ? value : this.keptToOne(value) };
|
|
1696
1673
|
})));
|
|
1697
1674
|
current = [];
|
|
1698
1675
|
};
|
|
1699
|
-
for (const element of
|
|
1676
|
+
for (const element of elements) {
|
|
1700
1677
|
if (element.type === "SpreadElement") {
|
|
1701
|
-
flush();
|
|
1678
|
+
flush(false);
|
|
1702
1679
|
parts.push(this.expression(element.argument));
|
|
1703
1680
|
} else {
|
|
1704
1681
|
current.push(element);
|
|
1705
1682
|
}
|
|
1706
1683
|
}
|
|
1707
|
-
flush();
|
|
1684
|
+
flush(true);
|
|
1708
1685
|
return call(this.helper("concat"), parts);
|
|
1709
1686
|
}
|
|
1710
1687
|
};
|
|
1688
|
+
function packs(list) {
|
|
1689
|
+
const items = [];
|
|
1690
|
+
const many = /* @__PURE__ */ new Set();
|
|
1691
|
+
const add = (elements) => {
|
|
1692
|
+
for (const element of elements) {
|
|
1693
|
+
if (element.type === "SpreadElement" && element.argument.type === "ArrayExpression") {
|
|
1694
|
+
const inner = element.argument.elements;
|
|
1695
|
+
add(inner);
|
|
1696
|
+
const last = inner[inner.length - 1];
|
|
1697
|
+
if (last && (last.type === "CallExpression" || last.type === "MethodCallExpression")) many.add(last);
|
|
1698
|
+
} else {
|
|
1699
|
+
items.push(element);
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
};
|
|
1703
|
+
add(list);
|
|
1704
|
+
items.forEach((item, i) => {
|
|
1705
|
+
if (!many.has(item) || i === items.length - 1) return;
|
|
1706
|
+
many.delete(item);
|
|
1707
|
+
items[i] = { type: "SpreadElement", argument: { type: "ArrayExpression", elements: [item], ...spanOf(item) }, ...spanOf(item) };
|
|
1708
|
+
});
|
|
1709
|
+
return { items, many };
|
|
1710
|
+
}
|
|
1711
1711
|
function isLink(node) {
|
|
1712
1712
|
return node.type === "MemberExpression" || node.type === "IndexExpression" || node.type === "CallExpression" || node.type === "MethodCallExpression";
|
|
1713
1713
|
}
|
|
@@ -1756,10 +1756,19 @@ function memberChain(e) {
|
|
|
1756
1756
|
}
|
|
1757
1757
|
return void 0;
|
|
1758
1758
|
}
|
|
1759
|
+
function declared(target) {
|
|
1760
|
+
if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
1761
|
+
throw new Error("a declaration binds names, not a member or an index");
|
|
1762
|
+
}
|
|
1763
|
+
return target;
|
|
1764
|
+
}
|
|
1759
1765
|
function identifierPatterns(target) {
|
|
1760
1766
|
switch (target.type) {
|
|
1761
1767
|
case "IdentifierPattern":
|
|
1762
1768
|
return [target];
|
|
1769
|
+
case "MemberExpression":
|
|
1770
|
+
case "IndexExpression":
|
|
1771
|
+
return [];
|
|
1763
1772
|
case "ObjectPattern":
|
|
1764
1773
|
return [...target.properties.flatMap((p) => identifierPatterns(p.value)), ...target.rest ? identifierPatterns(target.rest) : []];
|
|
1765
1774
|
case "ArrayPattern":
|
|
@@ -1773,7 +1782,7 @@ function topLevelDeclaration(statements, name) {
|
|
|
1773
1782
|
for (const statement of statements) {
|
|
1774
1783
|
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
1775
1784
|
if (declaration.type === "VariableDeclaration") {
|
|
1776
|
-
const pattern = declaration.
|
|
1785
|
+
const pattern = identifierPatterns(declaration.name).find((p) => p.name === name);
|
|
1777
1786
|
if (pattern) return { type: "Declaration", node: pattern };
|
|
1778
1787
|
} else if (declaration.type === "FunctionDeclaration" && declaration.name.name === name) {
|
|
1779
1788
|
return { type: "Declaration", node: declaration.name };
|
package/dist/cli.cjs
CHANGED
|
@@ -274,12 +274,10 @@ var Lowerer = class {
|
|
|
274
274
|
for (const node of allNodes(this.source.body)) {
|
|
275
275
|
const statement = node;
|
|
276
276
|
if (statement.type === "VariableDeclaration") {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if (binding) this.declarations.set(binding.id, value);
|
|
282
|
-
});
|
|
277
|
+
const target = statement.name;
|
|
278
|
+
if (target.type !== "IdentifierPattern" || !statement.init) continue;
|
|
279
|
+
const binding = this.bindingByDeclaration.get(target);
|
|
280
|
+
if (binding) this.declarations.set(binding.id, statement.init);
|
|
283
281
|
} else if (statement.type === "FunctionDeclaration") {
|
|
284
282
|
const binding = this.bindingByDeclaration.get(statement.name);
|
|
285
283
|
if (binding) this.declarations.set(binding.id, statement);
|
|
@@ -553,7 +551,7 @@ var Lowerer = class {
|
|
|
553
551
|
const declaration = statement.declaration;
|
|
554
552
|
if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") {
|
|
555
553
|
exportBinding(declaration.name, declaration.name.name);
|
|
556
|
-
} else for (const pattern of declaration.
|
|
554
|
+
} else for (const pattern of identifierPatterns(declaration.name)) exportBinding(pattern, pattern.name);
|
|
557
555
|
break;
|
|
558
556
|
}
|
|
559
557
|
case "ExportDefaultStatement":
|
|
@@ -589,7 +587,7 @@ var Lowerer = class {
|
|
|
589
587
|
for (const statement of statements) {
|
|
590
588
|
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
591
589
|
if (declaration.type === "VariableDeclaration") {
|
|
592
|
-
for (const pattern of declaration.
|
|
590
|
+
for (const pattern of identifierPatterns(declaration.name)) {
|
|
593
591
|
if (!this.isRewritten(pattern)) locals.push(this.name(pattern.name));
|
|
594
592
|
}
|
|
595
593
|
} else if ((declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") && !this.isRewritten(declaration.name)) {
|
|
@@ -686,7 +684,7 @@ var Lowerer = class {
|
|
|
686
684
|
continue;
|
|
687
685
|
}
|
|
688
686
|
if (statement.type !== "VariableDeclaration") continue;
|
|
689
|
-
const patterns = statement.
|
|
687
|
+
const patterns = identifierPatterns(statement.name);
|
|
690
688
|
const end = { line: { start: statement.line.end }, column: { start: statement.column.end } };
|
|
691
689
|
const needed = patterns.some((pattern) => {
|
|
692
690
|
const binding = this.bindingByDeclaration.get(pattern);
|
|
@@ -727,7 +725,7 @@ var Lowerer = class {
|
|
|
727
725
|
const body = [];
|
|
728
726
|
for (const statement of statements) {
|
|
729
727
|
if (statement.type === "VariableDeclaration") {
|
|
730
|
-
for (const pattern of statement.
|
|
728
|
+
for (const pattern of identifierPatterns(statement.name)) locals.push(this.name(pattern.name));
|
|
731
729
|
body.push(...this.variableDeclaration(statement, "assign"));
|
|
732
730
|
} else if (statement.type === "FunctionDeclaration") {
|
|
733
731
|
locals.push(this.name(statement.name.name));
|
|
@@ -860,12 +858,13 @@ var Lowerer = class {
|
|
|
860
858
|
}];
|
|
861
859
|
case "GenericForStatement": {
|
|
862
860
|
const prelude = [];
|
|
861
|
+
const variable = declared(node.variable);
|
|
863
862
|
let item;
|
|
864
|
-
if (
|
|
865
|
-
item = this.name(
|
|
863
|
+
if (variable.type === "IdentifierPattern") {
|
|
864
|
+
item = this.name(variable.name);
|
|
866
865
|
} else {
|
|
867
866
|
item = this.names.fresh("item");
|
|
868
|
-
prelude.push(...this.destructure(
|
|
867
|
+
prelude.push(...this.destructure(variable, identifier(item), "declare"));
|
|
869
868
|
}
|
|
870
869
|
const form = this.options.types?.loops.get(node) ?? { walks: "values", viaIter: false };
|
|
871
870
|
let source = this.expression(node.iterator);
|
|
@@ -879,7 +878,7 @@ var Lowerer = class {
|
|
|
879
878
|
)];
|
|
880
879
|
}
|
|
881
880
|
case "ReturnStatement":
|
|
882
|
-
return [returns(node.argument ? [this.expression(node.argument)] : [])];
|
|
881
|
+
return [returns(node.argument ? [this.keptToOne(this.expression(node.argument))] : [])];
|
|
883
882
|
case "BreakStatement":
|
|
884
883
|
return [{ type: "BreakStatement", ...spanOf(node) }];
|
|
885
884
|
case "ContinueStatement":
|
|
@@ -1128,63 +1127,37 @@ end
|
|
|
1128
1127
|
...spanOf(node)
|
|
1129
1128
|
};
|
|
1130
1129
|
}
|
|
1131
|
-
/** `const a
|
|
1132
|
-
* every name is declared up front — nothing is declared,
|
|
1130
|
+
/** `const a = x`, `const { a, b } = x`. In "assign" mode — a module's top
|
|
1131
|
+
* level, where every name is declared up front — nothing is declared,
|
|
1132
|
+
* only assigned. */
|
|
1133
1133
|
variableDeclaration(node, mode) {
|
|
1134
|
-
|
|
1135
|
-
|
|
1134
|
+
const target = declared(node.name);
|
|
1135
|
+
if (!node.init) {
|
|
1136
|
+
const names = identifierPatterns(target);
|
|
1137
|
+
if (mode === "declare") return [local(names.map((p) => this.name(p.name)), [])];
|
|
1138
|
+
const exported = names.filter((p) => this.isRewritten(p));
|
|
1136
1139
|
return exported.length ? [assign(exported.map((p) => this.reference(p)), exported.map(() => nil()))] : [];
|
|
1137
1140
|
}
|
|
1138
|
-
const
|
|
1139
|
-
if (
|
|
1140
|
-
|
|
1141
|
-
return mode === "declare" ? [local(names2.map((n) => this.name(n.name)), init)] : [assign(names2.map((n) => this.reference(n)), init)];
|
|
1142
|
-
}
|
|
1143
|
-
const only = node.names.length === 1 && node.init.length === 1 ? node.names[0] : void 0;
|
|
1144
|
-
if (only && only.type !== "IdentifierPattern") {
|
|
1145
|
-
if (init[0].type === "Identifier") return this.destructure(only, init[0], mode);
|
|
1146
|
-
const temp = this.names.fresh("ref");
|
|
1147
|
-
return [local([temp], init), ...this.destructure(only, identifier(temp), mode)];
|
|
1148
|
-
}
|
|
1149
|
-
const after = [];
|
|
1150
|
-
const names = [];
|
|
1151
|
-
const targets = [];
|
|
1152
|
-
for (const target of node.names) {
|
|
1153
|
-
if (target.type === "IdentifierPattern") {
|
|
1154
|
-
names.push(this.name(target.name));
|
|
1155
|
-
targets.push(this.reference(target));
|
|
1156
|
-
continue;
|
|
1157
|
-
}
|
|
1158
|
-
const temp = this.names.fresh("ref");
|
|
1159
|
-
names.push(temp);
|
|
1160
|
-
targets.push(identifier(temp));
|
|
1161
|
-
after.push(...this.destructure(target, identifier(temp), mode));
|
|
1141
|
+
const value = this.expression(node.init);
|
|
1142
|
+
if (target.type === "IdentifierPattern") {
|
|
1143
|
+
return mode === "declare" ? [local([this.name(target.name)], [value])] : [assign([this.reference(target)], [value])];
|
|
1162
1144
|
}
|
|
1163
|
-
if (
|
|
1164
|
-
const
|
|
1165
|
-
return [
|
|
1145
|
+
if (value.type === "Identifier") return this.destructure(target, value, mode);
|
|
1146
|
+
const temp = this.names.fresh("ref");
|
|
1147
|
+
return [local([temp], [value]), ...this.destructure(target, identifier(temp), mode)];
|
|
1166
1148
|
}
|
|
1167
1149
|
assignment(node) {
|
|
1168
|
-
|
|
1169
|
-
|
|
1150
|
+
const target = node.target;
|
|
1151
|
+
const value = this.expression(node.value);
|
|
1152
|
+
if (target.type !== "ObjectPattern" && target.type !== "ArrayPattern") {
|
|
1153
|
+
return [assign([this.expression(target)], [value])];
|
|
1170
1154
|
}
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
if (only && (only.type === "ObjectPattern" || only.type === "ArrayPattern") && values[0].type === "Identifier") {
|
|
1174
|
-
const statements = this.destructure(only, values[0], "assign");
|
|
1155
|
+
if (value.type === "Identifier") {
|
|
1156
|
+
const statements = this.destructure(target, value, "assign");
|
|
1175
1157
|
return statements.some((s) => s.type === "LocalStatement") ? [doBlock(statements)] : statements;
|
|
1176
1158
|
}
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
node.targets.forEach((target, i) => {
|
|
1180
|
-
const value = identifier(temps[i]);
|
|
1181
|
-
if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
|
|
1182
|
-
body.push(...this.destructure(target, value, "assign"));
|
|
1183
|
-
} else {
|
|
1184
|
-
body.push(assign([this.expression(target)], [value]));
|
|
1185
|
-
}
|
|
1186
|
-
});
|
|
1187
|
-
return [doBlock(body)];
|
|
1159
|
+
const temp = this.names.fresh("ref");
|
|
1160
|
+
return [doBlock([local([temp], [value]), ...this.destructure(target, identifier(temp), "assign")])];
|
|
1188
1161
|
}
|
|
1189
1162
|
// --------------------------------------------------------
|
|
1190
1163
|
// Destructuring
|
|
@@ -1200,6 +1173,12 @@ end
|
|
|
1200
1173
|
const reads = [];
|
|
1201
1174
|
const after = [];
|
|
1202
1175
|
const bind = (target, value, fallback) => {
|
|
1176
|
+
if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
1177
|
+
const reference = this.expression(target);
|
|
1178
|
+
reads.push({ name: "", target: reference, value, temp: false });
|
|
1179
|
+
if (fallback) after.push(this.defaultValue(reference, fallback));
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1203
1182
|
if (target.type === "IdentifierPattern") {
|
|
1204
1183
|
const name2 = this.name(target.name);
|
|
1205
1184
|
const reference = mode === "declare" ? identifier(name2) : this.reference(target);
|
|
@@ -1272,6 +1251,11 @@ end
|
|
|
1272
1251
|
* a temporary that a nested pattern then destructures (`then`). The
|
|
1273
1252
|
* statement creating it is pushed onto `after`. */
|
|
1274
1253
|
restTarget(target, initial, mode, after) {
|
|
1254
|
+
if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
1255
|
+
const reference = this.expression(target);
|
|
1256
|
+
after.push(assign([reference], [initial]));
|
|
1257
|
+
return { target: reference, then: [] };
|
|
1258
|
+
}
|
|
1275
1259
|
if (target.type === "IdentifierPattern") {
|
|
1276
1260
|
if (mode === "declare") {
|
|
1277
1261
|
const name2 = this.name(target.name);
|
|
@@ -1319,7 +1303,7 @@ end
|
|
|
1319
1303
|
if (through !== void 0) {
|
|
1320
1304
|
return call(member(this.superClassReference(node), through), [
|
|
1321
1305
|
identifier(this.thisName()),
|
|
1322
|
-
...this.
|
|
1306
|
+
...this.arguments(node.arguments)
|
|
1323
1307
|
]);
|
|
1324
1308
|
}
|
|
1325
1309
|
}
|
|
@@ -1404,35 +1388,29 @@ end
|
|
|
1404
1388
|
return this.link(node, this.expression(object));
|
|
1405
1389
|
}
|
|
1406
1390
|
/** A call is one value in tilua, and Luau would hand on every value one
|
|
1407
|
-
* in the last place of an argument list or
|
|
1408
|
-
*
|
|
1409
|
-
*
|
|
1410
|
-
keptToOne(
|
|
1411
|
-
|
|
1412
|
-
const last = values[values.length - 1];
|
|
1413
|
-
if (!node || !last || !expandsToMany(last)) return values;
|
|
1414
|
-
const type = this.options.types?.typeOf.get(node);
|
|
1415
|
-
if (type && type.kind !== "any" && type.kind !== "unknown") return values;
|
|
1416
|
-
return [...values.slice(0, -1), parenthesized(last)];
|
|
1391
|
+
* returns in the last place of an argument list or a `return`: it is
|
|
1392
|
+
* kept to one there, `f((g()))`. Every value is taken only where the
|
|
1393
|
+
* program asks for them, as an array's last element: `[g()]`. */
|
|
1394
|
+
keptToOne(value) {
|
|
1395
|
+
return expandsToMany(value) ? parenthesized(value) : value;
|
|
1417
1396
|
}
|
|
1418
1397
|
/** Is this `scriptArgs` the language's own, not a name the file declared? */
|
|
1419
1398
|
isLanguageGlobal(node) {
|
|
1420
1399
|
const id = this.bindingIdOf(node);
|
|
1421
1400
|
return id === void 0 || this.scopes.bindings.get(id)?.kind === "global";
|
|
1422
1401
|
}
|
|
1423
|
-
/** A
|
|
1424
|
-
* an assignment's. `f(a, ...xs)` is Lua's own last-value expansion,
|
|
1402
|
+
/** A call's arguments. `f(a, ...xs)` is Lua's own last-value expansion,
|
|
1425
1403
|
* `f(a, table.unpack(xs))`; a spread anywhere else cannot be, since only
|
|
1426
1404
|
* the last value of a list expands, so the whole list is built as an
|
|
1427
|
-
* array first and that is what expands.
|
|
1428
|
-
*
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
values(list, asArguments = false) {
|
|
1405
|
+
* array first and that is what expands. A call written last is one
|
|
1406
|
+
* value — see `keptToOne`. */
|
|
1407
|
+
arguments(written) {
|
|
1408
|
+
const { items: list, many } = packs(written);
|
|
1432
1409
|
const spreads = list.filter((a) => a.type === "SpreadElement");
|
|
1433
1410
|
if (!spreads.length) {
|
|
1434
1411
|
const lowered = list.map((a) => this.expression(a));
|
|
1435
|
-
|
|
1412
|
+
const last2 = list.length - 1;
|
|
1413
|
+
return lowered.length && !many.has(list[last2]) ? [...lowered.slice(0, -1), this.keptToOne(lowered[last2])] : lowered;
|
|
1436
1414
|
}
|
|
1437
1415
|
const unpack = (value) => call(member(this.builtin("table"), "unpack"), [value]);
|
|
1438
1416
|
const last = list[list.length - 1];
|
|
@@ -1454,13 +1432,13 @@ end
|
|
|
1454
1432
|
return index(object, this.expression(node.index));
|
|
1455
1433
|
case "CallExpression": {
|
|
1456
1434
|
const lowered = this.loweredGlobalCall(node);
|
|
1457
|
-
const args2 = this.
|
|
1435
|
+
const args2 = this.arguments(node.arguments);
|
|
1458
1436
|
if (!lowered) return call(object, args2);
|
|
1459
1437
|
return call(calleePath(lowered.callee), [...lowered.prepend, ...args2]);
|
|
1460
1438
|
}
|
|
1461
1439
|
case "MethodCallExpression": {
|
|
1462
1440
|
const lowered = this.loweredMethodCall(node);
|
|
1463
|
-
const args2 = this.
|
|
1441
|
+
const args2 = this.arguments(node.arguments);
|
|
1464
1442
|
if (lowered) {
|
|
1465
1443
|
return call(calleePath(lowered.callee), [
|
|
1466
1444
|
...lowered.prepend,
|
|
@@ -1693,33 +1671,55 @@ end
|
|
|
1693
1671
|
/** `[1, ...xs, 2]`. Without a spread it is a Luau sequence; with one, the
|
|
1694
1672
|
* runs of plain elements and the spread arrays are joined by `concat`. */
|
|
1695
1673
|
arrayExpression(node) {
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
return table(
|
|
1674
|
+
const elements = packs(node.elements).items;
|
|
1675
|
+
if (!elements.some((e) => e.type === "SpreadElement")) {
|
|
1676
|
+
return table(elements.map((e) => ({ type: "TableFieldPositional", value: this.expression(e) })));
|
|
1699
1677
|
}
|
|
1700
1678
|
const parts = [];
|
|
1701
1679
|
let current = [];
|
|
1702
|
-
const flush = () => {
|
|
1680
|
+
const flush = (last) => {
|
|
1703
1681
|
if (!current.length) return;
|
|
1704
1682
|
parts.push(table(current.map((e, i) => {
|
|
1705
1683
|
const value = this.expression(e);
|
|
1706
|
-
|
|
1707
|
-
return { type: "TableFieldPositional", value: last && expandsToMany(value) ? parenthesized(value) : value };
|
|
1684
|
+
return { type: "TableFieldPositional", value: last || i < current.length - 1 ? value : this.keptToOne(value) };
|
|
1708
1685
|
})));
|
|
1709
1686
|
current = [];
|
|
1710
1687
|
};
|
|
1711
|
-
for (const element of
|
|
1688
|
+
for (const element of elements) {
|
|
1712
1689
|
if (element.type === "SpreadElement") {
|
|
1713
|
-
flush();
|
|
1690
|
+
flush(false);
|
|
1714
1691
|
parts.push(this.expression(element.argument));
|
|
1715
1692
|
} else {
|
|
1716
1693
|
current.push(element);
|
|
1717
1694
|
}
|
|
1718
1695
|
}
|
|
1719
|
-
flush();
|
|
1696
|
+
flush(true);
|
|
1720
1697
|
return call(this.helper("concat"), parts);
|
|
1721
1698
|
}
|
|
1722
1699
|
};
|
|
1700
|
+
function packs(list) {
|
|
1701
|
+
const items = [];
|
|
1702
|
+
const many = /* @__PURE__ */ new Set();
|
|
1703
|
+
const add = (elements) => {
|
|
1704
|
+
for (const element of elements) {
|
|
1705
|
+
if (element.type === "SpreadElement" && element.argument.type === "ArrayExpression") {
|
|
1706
|
+
const inner = element.argument.elements;
|
|
1707
|
+
add(inner);
|
|
1708
|
+
const last = inner[inner.length - 1];
|
|
1709
|
+
if (last && (last.type === "CallExpression" || last.type === "MethodCallExpression")) many.add(last);
|
|
1710
|
+
} else {
|
|
1711
|
+
items.push(element);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
};
|
|
1715
|
+
add(list);
|
|
1716
|
+
items.forEach((item, i) => {
|
|
1717
|
+
if (!many.has(item) || i === items.length - 1) return;
|
|
1718
|
+
many.delete(item);
|
|
1719
|
+
items[i] = { type: "SpreadElement", argument: { type: "ArrayExpression", elements: [item], ...spanOf(item) }, ...spanOf(item) };
|
|
1720
|
+
});
|
|
1721
|
+
return { items, many };
|
|
1722
|
+
}
|
|
1723
1723
|
function isLink(node) {
|
|
1724
1724
|
return node.type === "MemberExpression" || node.type === "IndexExpression" || node.type === "CallExpression" || node.type === "MethodCallExpression";
|
|
1725
1725
|
}
|
|
@@ -1768,10 +1768,19 @@ function memberChain(e) {
|
|
|
1768
1768
|
}
|
|
1769
1769
|
return void 0;
|
|
1770
1770
|
}
|
|
1771
|
+
function declared(target) {
|
|
1772
|
+
if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
1773
|
+
throw new Error("a declaration binds names, not a member or an index");
|
|
1774
|
+
}
|
|
1775
|
+
return target;
|
|
1776
|
+
}
|
|
1771
1777
|
function identifierPatterns(target) {
|
|
1772
1778
|
switch (target.type) {
|
|
1773
1779
|
case "IdentifierPattern":
|
|
1774
1780
|
return [target];
|
|
1781
|
+
case "MemberExpression":
|
|
1782
|
+
case "IndexExpression":
|
|
1783
|
+
return [];
|
|
1775
1784
|
case "ObjectPattern":
|
|
1776
1785
|
return [...target.properties.flatMap((p) => identifierPatterns(p.value)), ...target.rest ? identifierPatterns(target.rest) : []];
|
|
1777
1786
|
case "ArrayPattern":
|
|
@@ -1785,7 +1794,7 @@ function topLevelDeclaration(statements, name) {
|
|
|
1785
1794
|
for (const statement of statements) {
|
|
1786
1795
|
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
1787
1796
|
if (declaration.type === "VariableDeclaration") {
|
|
1788
|
-
const pattern = declaration.
|
|
1797
|
+
const pattern = identifierPatterns(declaration.name).find((p) => p.name === name);
|
|
1789
1798
|
if (pattern) return { type: "Declaration", node: pattern };
|
|
1790
1799
|
} else if (declaration.type === "FunctionDeclaration" && declaration.name.name === name) {
|
|
1791
1800
|
return { type: "Declaration", node: declaration.name };
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -298,12 +298,10 @@ var Lowerer = class {
|
|
|
298
298
|
for (const node of allNodes(this.source.body)) {
|
|
299
299
|
const statement = node;
|
|
300
300
|
if (statement.type === "VariableDeclaration") {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if (binding) this.declarations.set(binding.id, value);
|
|
306
|
-
});
|
|
301
|
+
const target = statement.name;
|
|
302
|
+
if (target.type !== "IdentifierPattern" || !statement.init) continue;
|
|
303
|
+
const binding = this.bindingByDeclaration.get(target);
|
|
304
|
+
if (binding) this.declarations.set(binding.id, statement.init);
|
|
307
305
|
} else if (statement.type === "FunctionDeclaration") {
|
|
308
306
|
const binding = this.bindingByDeclaration.get(statement.name);
|
|
309
307
|
if (binding) this.declarations.set(binding.id, statement);
|
|
@@ -577,7 +575,7 @@ var Lowerer = class {
|
|
|
577
575
|
const declaration = statement.declaration;
|
|
578
576
|
if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") {
|
|
579
577
|
exportBinding(declaration.name, declaration.name.name);
|
|
580
|
-
} else for (const pattern of declaration.
|
|
578
|
+
} else for (const pattern of identifierPatterns(declaration.name)) exportBinding(pattern, pattern.name);
|
|
581
579
|
break;
|
|
582
580
|
}
|
|
583
581
|
case "ExportDefaultStatement":
|
|
@@ -613,7 +611,7 @@ var Lowerer = class {
|
|
|
613
611
|
for (const statement of statements) {
|
|
614
612
|
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
615
613
|
if (declaration.type === "VariableDeclaration") {
|
|
616
|
-
for (const pattern of declaration.
|
|
614
|
+
for (const pattern of identifierPatterns(declaration.name)) {
|
|
617
615
|
if (!this.isRewritten(pattern)) locals.push(this.name(pattern.name));
|
|
618
616
|
}
|
|
619
617
|
} else if ((declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") && !this.isRewritten(declaration.name)) {
|
|
@@ -710,7 +708,7 @@ var Lowerer = class {
|
|
|
710
708
|
continue;
|
|
711
709
|
}
|
|
712
710
|
if (statement.type !== "VariableDeclaration") continue;
|
|
713
|
-
const patterns = statement.
|
|
711
|
+
const patterns = identifierPatterns(statement.name);
|
|
714
712
|
const end = { line: { start: statement.line.end }, column: { start: statement.column.end } };
|
|
715
713
|
const needed = patterns.some((pattern) => {
|
|
716
714
|
const binding = this.bindingByDeclaration.get(pattern);
|
|
@@ -751,7 +749,7 @@ var Lowerer = class {
|
|
|
751
749
|
const body = [];
|
|
752
750
|
for (const statement of statements) {
|
|
753
751
|
if (statement.type === "VariableDeclaration") {
|
|
754
|
-
for (const pattern of statement.
|
|
752
|
+
for (const pattern of identifierPatterns(statement.name)) locals.push(this.name(pattern.name));
|
|
755
753
|
body.push(...this.variableDeclaration(statement, "assign"));
|
|
756
754
|
} else if (statement.type === "FunctionDeclaration") {
|
|
757
755
|
locals.push(this.name(statement.name.name));
|
|
@@ -884,12 +882,13 @@ var Lowerer = class {
|
|
|
884
882
|
}];
|
|
885
883
|
case "GenericForStatement": {
|
|
886
884
|
const prelude = [];
|
|
885
|
+
const variable = declared(node.variable);
|
|
887
886
|
let item;
|
|
888
|
-
if (
|
|
889
|
-
item = this.name(
|
|
887
|
+
if (variable.type === "IdentifierPattern") {
|
|
888
|
+
item = this.name(variable.name);
|
|
890
889
|
} else {
|
|
891
890
|
item = this.names.fresh("item");
|
|
892
|
-
prelude.push(...this.destructure(
|
|
891
|
+
prelude.push(...this.destructure(variable, identifier(item), "declare"));
|
|
893
892
|
}
|
|
894
893
|
const form = this.options.types?.loops.get(node) ?? { walks: "values", viaIter: false };
|
|
895
894
|
let source = this.expression(node.iterator);
|
|
@@ -903,7 +902,7 @@ var Lowerer = class {
|
|
|
903
902
|
)];
|
|
904
903
|
}
|
|
905
904
|
case "ReturnStatement":
|
|
906
|
-
return [returns(node.argument ? [this.expression(node.argument)] : [])];
|
|
905
|
+
return [returns(node.argument ? [this.keptToOne(this.expression(node.argument))] : [])];
|
|
907
906
|
case "BreakStatement":
|
|
908
907
|
return [{ type: "BreakStatement", ...spanOf(node) }];
|
|
909
908
|
case "ContinueStatement":
|
|
@@ -1152,63 +1151,37 @@ end
|
|
|
1152
1151
|
...spanOf(node)
|
|
1153
1152
|
};
|
|
1154
1153
|
}
|
|
1155
|
-
/** `const a
|
|
1156
|
-
* every name is declared up front — nothing is declared,
|
|
1154
|
+
/** `const a = x`, `const { a, b } = x`. In "assign" mode — a module's top
|
|
1155
|
+
* level, where every name is declared up front — nothing is declared,
|
|
1156
|
+
* only assigned. */
|
|
1157
1157
|
variableDeclaration(node, mode) {
|
|
1158
|
-
|
|
1159
|
-
|
|
1158
|
+
const target = declared(node.name);
|
|
1159
|
+
if (!node.init) {
|
|
1160
|
+
const names = identifierPatterns(target);
|
|
1161
|
+
if (mode === "declare") return [local(names.map((p) => this.name(p.name)), [])];
|
|
1162
|
+
const exported = names.filter((p) => this.isRewritten(p));
|
|
1160
1163
|
return exported.length ? [assign(exported.map((p) => this.reference(p)), exported.map(() => nil()))] : [];
|
|
1161
1164
|
}
|
|
1162
|
-
const
|
|
1163
|
-
if (
|
|
1164
|
-
|
|
1165
|
-
return mode === "declare" ? [local(names2.map((n) => this.name(n.name)), init)] : [assign(names2.map((n) => this.reference(n)), init)];
|
|
1166
|
-
}
|
|
1167
|
-
const only = node.names.length === 1 && node.init.length === 1 ? node.names[0] : void 0;
|
|
1168
|
-
if (only && only.type !== "IdentifierPattern") {
|
|
1169
|
-
if (init[0].type === "Identifier") return this.destructure(only, init[0], mode);
|
|
1170
|
-
const temp = this.names.fresh("ref");
|
|
1171
|
-
return [local([temp], init), ...this.destructure(only, identifier(temp), mode)];
|
|
1172
|
-
}
|
|
1173
|
-
const after = [];
|
|
1174
|
-
const names = [];
|
|
1175
|
-
const targets = [];
|
|
1176
|
-
for (const target of node.names) {
|
|
1177
|
-
if (target.type === "IdentifierPattern") {
|
|
1178
|
-
names.push(this.name(target.name));
|
|
1179
|
-
targets.push(this.reference(target));
|
|
1180
|
-
continue;
|
|
1181
|
-
}
|
|
1182
|
-
const temp = this.names.fresh("ref");
|
|
1183
|
-
names.push(temp);
|
|
1184
|
-
targets.push(identifier(temp));
|
|
1185
|
-
after.push(...this.destructure(target, identifier(temp), mode));
|
|
1165
|
+
const value = this.expression(node.init);
|
|
1166
|
+
if (target.type === "IdentifierPattern") {
|
|
1167
|
+
return mode === "declare" ? [local([this.name(target.name)], [value])] : [assign([this.reference(target)], [value])];
|
|
1186
1168
|
}
|
|
1187
|
-
if (
|
|
1188
|
-
const
|
|
1189
|
-
return [
|
|
1169
|
+
if (value.type === "Identifier") return this.destructure(target, value, mode);
|
|
1170
|
+
const temp = this.names.fresh("ref");
|
|
1171
|
+
return [local([temp], [value]), ...this.destructure(target, identifier(temp), mode)];
|
|
1190
1172
|
}
|
|
1191
1173
|
assignment(node) {
|
|
1192
|
-
|
|
1193
|
-
|
|
1174
|
+
const target = node.target;
|
|
1175
|
+
const value = this.expression(node.value);
|
|
1176
|
+
if (target.type !== "ObjectPattern" && target.type !== "ArrayPattern") {
|
|
1177
|
+
return [assign([this.expression(target)], [value])];
|
|
1194
1178
|
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
if (only && (only.type === "ObjectPattern" || only.type === "ArrayPattern") && values[0].type === "Identifier") {
|
|
1198
|
-
const statements = this.destructure(only, values[0], "assign");
|
|
1179
|
+
if (value.type === "Identifier") {
|
|
1180
|
+
const statements = this.destructure(target, value, "assign");
|
|
1199
1181
|
return statements.some((s) => s.type === "LocalStatement") ? [doBlock(statements)] : statements;
|
|
1200
1182
|
}
|
|
1201
|
-
const
|
|
1202
|
-
|
|
1203
|
-
node.targets.forEach((target, i) => {
|
|
1204
|
-
const value = identifier(temps[i]);
|
|
1205
|
-
if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
|
|
1206
|
-
body.push(...this.destructure(target, value, "assign"));
|
|
1207
|
-
} else {
|
|
1208
|
-
body.push(assign([this.expression(target)], [value]));
|
|
1209
|
-
}
|
|
1210
|
-
});
|
|
1211
|
-
return [doBlock(body)];
|
|
1183
|
+
const temp = this.names.fresh("ref");
|
|
1184
|
+
return [doBlock([local([temp], [value]), ...this.destructure(target, identifier(temp), "assign")])];
|
|
1212
1185
|
}
|
|
1213
1186
|
// --------------------------------------------------------
|
|
1214
1187
|
// Destructuring
|
|
@@ -1224,6 +1197,12 @@ end
|
|
|
1224
1197
|
const reads = [];
|
|
1225
1198
|
const after = [];
|
|
1226
1199
|
const bind = (target, value, fallback) => {
|
|
1200
|
+
if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
1201
|
+
const reference = this.expression(target);
|
|
1202
|
+
reads.push({ name: "", target: reference, value, temp: false });
|
|
1203
|
+
if (fallback) after.push(this.defaultValue(reference, fallback));
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1227
1206
|
if (target.type === "IdentifierPattern") {
|
|
1228
1207
|
const name2 = this.name(target.name);
|
|
1229
1208
|
const reference = mode === "declare" ? identifier(name2) : this.reference(target);
|
|
@@ -1296,6 +1275,11 @@ end
|
|
|
1296
1275
|
* a temporary that a nested pattern then destructures (`then`). The
|
|
1297
1276
|
* statement creating it is pushed onto `after`. */
|
|
1298
1277
|
restTarget(target, initial, mode, after) {
|
|
1278
|
+
if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
1279
|
+
const reference = this.expression(target);
|
|
1280
|
+
after.push(assign([reference], [initial]));
|
|
1281
|
+
return { target: reference, then: [] };
|
|
1282
|
+
}
|
|
1299
1283
|
if (target.type === "IdentifierPattern") {
|
|
1300
1284
|
if (mode === "declare") {
|
|
1301
1285
|
const name2 = this.name(target.name);
|
|
@@ -1343,7 +1327,7 @@ end
|
|
|
1343
1327
|
if (through !== void 0) {
|
|
1344
1328
|
return call(member(this.superClassReference(node), through), [
|
|
1345
1329
|
identifier(this.thisName()),
|
|
1346
|
-
...this.
|
|
1330
|
+
...this.arguments(node.arguments)
|
|
1347
1331
|
]);
|
|
1348
1332
|
}
|
|
1349
1333
|
}
|
|
@@ -1428,35 +1412,29 @@ end
|
|
|
1428
1412
|
return this.link(node, this.expression(object));
|
|
1429
1413
|
}
|
|
1430
1414
|
/** A call is one value in tilua, and Luau would hand on every value one
|
|
1431
|
-
* in the last place of an argument list or
|
|
1432
|
-
*
|
|
1433
|
-
*
|
|
1434
|
-
keptToOne(
|
|
1435
|
-
|
|
1436
|
-
const last = values[values.length - 1];
|
|
1437
|
-
if (!node || !last || !expandsToMany(last)) return values;
|
|
1438
|
-
const type = this.options.types?.typeOf.get(node);
|
|
1439
|
-
if (type && type.kind !== "any" && type.kind !== "unknown") return values;
|
|
1440
|
-
return [...values.slice(0, -1), parenthesized(last)];
|
|
1415
|
+
* returns in the last place of an argument list or a `return`: it is
|
|
1416
|
+
* kept to one there, `f((g()))`. Every value is taken only where the
|
|
1417
|
+
* program asks for them, as an array's last element: `[g()]`. */
|
|
1418
|
+
keptToOne(value) {
|
|
1419
|
+
return expandsToMany(value) ? parenthesized(value) : value;
|
|
1441
1420
|
}
|
|
1442
1421
|
/** Is this `scriptArgs` the language's own, not a name the file declared? */
|
|
1443
1422
|
isLanguageGlobal(node) {
|
|
1444
1423
|
const id = this.bindingIdOf(node);
|
|
1445
1424
|
return id === void 0 || this.scopes.bindings.get(id)?.kind === "global";
|
|
1446
1425
|
}
|
|
1447
|
-
/** A
|
|
1448
|
-
* an assignment's. `f(a, ...xs)` is Lua's own last-value expansion,
|
|
1426
|
+
/** A call's arguments. `f(a, ...xs)` is Lua's own last-value expansion,
|
|
1449
1427
|
* `f(a, table.unpack(xs))`; a spread anywhere else cannot be, since only
|
|
1450
1428
|
* the last value of a list expands, so the whole list is built as an
|
|
1451
|
-
* array first and that is what expands.
|
|
1452
|
-
*
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
values(list, asArguments = false) {
|
|
1429
|
+
* array first and that is what expands. A call written last is one
|
|
1430
|
+
* value — see `keptToOne`. */
|
|
1431
|
+
arguments(written) {
|
|
1432
|
+
const { items: list, many } = packs(written);
|
|
1456
1433
|
const spreads = list.filter((a) => a.type === "SpreadElement");
|
|
1457
1434
|
if (!spreads.length) {
|
|
1458
1435
|
const lowered = list.map((a) => this.expression(a));
|
|
1459
|
-
|
|
1436
|
+
const last2 = list.length - 1;
|
|
1437
|
+
return lowered.length && !many.has(list[last2]) ? [...lowered.slice(0, -1), this.keptToOne(lowered[last2])] : lowered;
|
|
1460
1438
|
}
|
|
1461
1439
|
const unpack = (value) => call(member(this.builtin("table"), "unpack"), [value]);
|
|
1462
1440
|
const last = list[list.length - 1];
|
|
@@ -1478,13 +1456,13 @@ end
|
|
|
1478
1456
|
return index(object, this.expression(node.index));
|
|
1479
1457
|
case "CallExpression": {
|
|
1480
1458
|
const lowered = this.loweredGlobalCall(node);
|
|
1481
|
-
const args = this.
|
|
1459
|
+
const args = this.arguments(node.arguments);
|
|
1482
1460
|
if (!lowered) return call(object, args);
|
|
1483
1461
|
return call(calleePath(lowered.callee), [...lowered.prepend, ...args]);
|
|
1484
1462
|
}
|
|
1485
1463
|
case "MethodCallExpression": {
|
|
1486
1464
|
const lowered = this.loweredMethodCall(node);
|
|
1487
|
-
const args = this.
|
|
1465
|
+
const args = this.arguments(node.arguments);
|
|
1488
1466
|
if (lowered) {
|
|
1489
1467
|
return call(calleePath(lowered.callee), [
|
|
1490
1468
|
...lowered.prepend,
|
|
@@ -1717,33 +1695,55 @@ end
|
|
|
1717
1695
|
/** `[1, ...xs, 2]`. Without a spread it is a Luau sequence; with one, the
|
|
1718
1696
|
* runs of plain elements and the spread arrays are joined by `concat`. */
|
|
1719
1697
|
arrayExpression(node) {
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
return table(
|
|
1698
|
+
const elements = packs(node.elements).items;
|
|
1699
|
+
if (!elements.some((e) => e.type === "SpreadElement")) {
|
|
1700
|
+
return table(elements.map((e) => ({ type: "TableFieldPositional", value: this.expression(e) })));
|
|
1723
1701
|
}
|
|
1724
1702
|
const parts = [];
|
|
1725
1703
|
let current = [];
|
|
1726
|
-
const flush = () => {
|
|
1704
|
+
const flush = (last) => {
|
|
1727
1705
|
if (!current.length) return;
|
|
1728
1706
|
parts.push(table(current.map((e, i) => {
|
|
1729
1707
|
const value = this.expression(e);
|
|
1730
|
-
|
|
1731
|
-
return { type: "TableFieldPositional", value: last && expandsToMany(value) ? parenthesized(value) : value };
|
|
1708
|
+
return { type: "TableFieldPositional", value: last || i < current.length - 1 ? value : this.keptToOne(value) };
|
|
1732
1709
|
})));
|
|
1733
1710
|
current = [];
|
|
1734
1711
|
};
|
|
1735
|
-
for (const element of
|
|
1712
|
+
for (const element of elements) {
|
|
1736
1713
|
if (element.type === "SpreadElement") {
|
|
1737
|
-
flush();
|
|
1714
|
+
flush(false);
|
|
1738
1715
|
parts.push(this.expression(element.argument));
|
|
1739
1716
|
} else {
|
|
1740
1717
|
current.push(element);
|
|
1741
1718
|
}
|
|
1742
1719
|
}
|
|
1743
|
-
flush();
|
|
1720
|
+
flush(true);
|
|
1744
1721
|
return call(this.helper("concat"), parts);
|
|
1745
1722
|
}
|
|
1746
1723
|
};
|
|
1724
|
+
function packs(list) {
|
|
1725
|
+
const items = [];
|
|
1726
|
+
const many = /* @__PURE__ */ new Set();
|
|
1727
|
+
const add = (elements) => {
|
|
1728
|
+
for (const element of elements) {
|
|
1729
|
+
if (element.type === "SpreadElement" && element.argument.type === "ArrayExpression") {
|
|
1730
|
+
const inner = element.argument.elements;
|
|
1731
|
+
add(inner);
|
|
1732
|
+
const last = inner[inner.length - 1];
|
|
1733
|
+
if (last && (last.type === "CallExpression" || last.type === "MethodCallExpression")) many.add(last);
|
|
1734
|
+
} else {
|
|
1735
|
+
items.push(element);
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
};
|
|
1739
|
+
add(list);
|
|
1740
|
+
items.forEach((item, i) => {
|
|
1741
|
+
if (!many.has(item) || i === items.length - 1) return;
|
|
1742
|
+
many.delete(item);
|
|
1743
|
+
items[i] = { type: "SpreadElement", argument: { type: "ArrayExpression", elements: [item], ...spanOf(item) }, ...spanOf(item) };
|
|
1744
|
+
});
|
|
1745
|
+
return { items, many };
|
|
1746
|
+
}
|
|
1747
1747
|
function isLink(node) {
|
|
1748
1748
|
return node.type === "MemberExpression" || node.type === "IndexExpression" || node.type === "CallExpression" || node.type === "MethodCallExpression";
|
|
1749
1749
|
}
|
|
@@ -1792,10 +1792,19 @@ function memberChain(e) {
|
|
|
1792
1792
|
}
|
|
1793
1793
|
return void 0;
|
|
1794
1794
|
}
|
|
1795
|
+
function declared(target) {
|
|
1796
|
+
if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
1797
|
+
throw new Error("a declaration binds names, not a member or an index");
|
|
1798
|
+
}
|
|
1799
|
+
return target;
|
|
1800
|
+
}
|
|
1795
1801
|
function identifierPatterns(target) {
|
|
1796
1802
|
switch (target.type) {
|
|
1797
1803
|
case "IdentifierPattern":
|
|
1798
1804
|
return [target];
|
|
1805
|
+
case "MemberExpression":
|
|
1806
|
+
case "IndexExpression":
|
|
1807
|
+
return [];
|
|
1799
1808
|
case "ObjectPattern":
|
|
1800
1809
|
return [...target.properties.flatMap((p) => identifierPatterns(p.value)), ...target.rest ? identifierPatterns(target.rest) : []];
|
|
1801
1810
|
case "ArrayPattern":
|
|
@@ -1809,7 +1818,7 @@ function topLevelDeclaration(statements, name) {
|
|
|
1809
1818
|
for (const statement of statements) {
|
|
1810
1819
|
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
1811
1820
|
if (declaration.type === "VariableDeclaration") {
|
|
1812
|
-
const pattern = declaration.
|
|
1821
|
+
const pattern = identifierPatterns(declaration.name).find((p) => p.name === name);
|
|
1813
1822
|
if (pattern) return { type: "Declaration", node: pattern };
|
|
1814
1823
|
} else if (declaration.type === "FunctionDeclaration" && declaration.name.name === name) {
|
|
1815
1824
|
return { type: "Declaration", node: declaration.name };
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tilua/compiler",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.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",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"type": "module",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"luau-parser": "^1.0.3",
|
|
40
|
-
"@tilua/parser": "^2.
|
|
40
|
+
"@tilua/parser": "^2.4.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^22.20.2",
|