@tilua/compiler 2.3.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.
@@ -846,12 +846,13 @@ var Lowerer = class {
846
846
  }];
847
847
  case "GenericForStatement": {
848
848
  const prelude = [];
849
+ const variable = declared(node.variable);
849
850
  let item;
850
- if (node.variable.type === "IdentifierPattern") {
851
- item = this.name(node.variable.name);
851
+ if (variable.type === "IdentifierPattern") {
852
+ item = this.name(variable.name);
852
853
  } else {
853
854
  item = this.names.fresh("item");
854
- prelude.push(...this.destructure(node.variable, identifier(item), "declare"));
855
+ prelude.push(...this.destructure(variable, identifier(item), "declare"));
855
856
  }
856
857
  const form = this.options.types?.loops.get(node) ?? { walks: "values", viaIter: false };
857
858
  let source = this.expression(node.iterator);
@@ -1118,7 +1119,7 @@ end
1118
1119
  * level, where every name is declared up front — nothing is declared,
1119
1120
  * only assigned. */
1120
1121
  variableDeclaration(node, mode) {
1121
- const target = node.name;
1122
+ const target = declared(node.name);
1122
1123
  if (!node.init) {
1123
1124
  const names = identifierPatterns(target);
1124
1125
  if (mode === "declare") return [local(names.map((p) => this.name(p.name)), [])];
@@ -1160,6 +1161,12 @@ end
1160
1161
  const reads = [];
1161
1162
  const after = [];
1162
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
+ }
1163
1170
  if (target.type === "IdentifierPattern") {
1164
1171
  const name2 = this.name(target.name);
1165
1172
  const reference = mode === "declare" ? identifier(name2) : this.reference(target);
@@ -1232,6 +1239,11 @@ end
1232
1239
  * a temporary that a nested pattern then destructures (`then`). The
1233
1240
  * statement creating it is pushed onto `after`. */
1234
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
+ }
1235
1247
  if (target.type === "IdentifierPattern") {
1236
1248
  if (mode === "declare") {
1237
1249
  const name2 = this.name(target.name);
@@ -1380,11 +1392,13 @@ end
1380
1392
  * the last value of a list expands, so the whole list is built as an
1381
1393
  * array first and that is what expands. A call written last is one
1382
1394
  * value — see `keptToOne`. */
1383
- arguments(list) {
1395
+ arguments(written) {
1396
+ const { items: list, many } = packs(written);
1384
1397
  const spreads = list.filter((a) => a.type === "SpreadElement");
1385
1398
  if (!spreads.length) {
1386
1399
  const lowered = list.map((a) => this.expression(a));
1387
- return lowered.length ? [...lowered.slice(0, -1), this.keptToOne(lowered[lowered.length - 1])] : lowered;
1400
+ const last2 = list.length - 1;
1401
+ return lowered.length && !many.has(list[last2]) ? [...lowered.slice(0, -1), this.keptToOne(lowered[last2])] : lowered;
1388
1402
  }
1389
1403
  const unpack = (value) => call(member(this.builtin("table"), "unpack"), [value]);
1390
1404
  const last = list[list.length - 1];
@@ -1645,8 +1659,9 @@ end
1645
1659
  /** `[1, ...xs, 2]`. Without a spread it is a Luau sequence; with one, the
1646
1660
  * runs of plain elements and the spread arrays are joined by `concat`. */
1647
1661
  arrayExpression(node) {
1648
- if (!node.elements.some((e) => e.type === "SpreadElement")) {
1649
- return table(node.elements.map((e) => ({ type: "TableFieldPositional", value: this.expression(e) })));
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) })));
1650
1665
  }
1651
1666
  const parts = [];
1652
1667
  let current = [];
@@ -1658,7 +1673,7 @@ end
1658
1673
  })));
1659
1674
  current = [];
1660
1675
  };
1661
- for (const element of node.elements) {
1676
+ for (const element of elements) {
1662
1677
  if (element.type === "SpreadElement") {
1663
1678
  flush(false);
1664
1679
  parts.push(this.expression(element.argument));
@@ -1670,6 +1685,29 @@ end
1670
1685
  return call(this.helper("concat"), parts);
1671
1686
  }
1672
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
+ }
1673
1711
  function isLink(node) {
1674
1712
  return node.type === "MemberExpression" || node.type === "IndexExpression" || node.type === "CallExpression" || node.type === "MethodCallExpression";
1675
1713
  }
@@ -1718,10 +1756,19 @@ function memberChain(e) {
1718
1756
  }
1719
1757
  return void 0;
1720
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
+ }
1721
1765
  function identifierPatterns(target) {
1722
1766
  switch (target.type) {
1723
1767
  case "IdentifierPattern":
1724
1768
  return [target];
1769
+ case "MemberExpression":
1770
+ case "IndexExpression":
1771
+ return [];
1725
1772
  case "ObjectPattern":
1726
1773
  return [...target.properties.flatMap((p) => identifierPatterns(p.value)), ...target.rest ? identifierPatterns(target.rest) : []];
1727
1774
  case "ArrayPattern":
package/dist/cli.cjs CHANGED
@@ -858,12 +858,13 @@ var Lowerer = class {
858
858
  }];
859
859
  case "GenericForStatement": {
860
860
  const prelude = [];
861
+ const variable = declared(node.variable);
861
862
  let item;
862
- if (node.variable.type === "IdentifierPattern") {
863
- item = this.name(node.variable.name);
863
+ if (variable.type === "IdentifierPattern") {
864
+ item = this.name(variable.name);
864
865
  } else {
865
866
  item = this.names.fresh("item");
866
- prelude.push(...this.destructure(node.variable, identifier(item), "declare"));
867
+ prelude.push(...this.destructure(variable, identifier(item), "declare"));
867
868
  }
868
869
  const form = this.options.types?.loops.get(node) ?? { walks: "values", viaIter: false };
869
870
  let source = this.expression(node.iterator);
@@ -1130,7 +1131,7 @@ end
1130
1131
  * level, where every name is declared up front — nothing is declared,
1131
1132
  * only assigned. */
1132
1133
  variableDeclaration(node, mode) {
1133
- const target = node.name;
1134
+ const target = declared(node.name);
1134
1135
  if (!node.init) {
1135
1136
  const names = identifierPatterns(target);
1136
1137
  if (mode === "declare") return [local(names.map((p) => this.name(p.name)), [])];
@@ -1172,6 +1173,12 @@ end
1172
1173
  const reads = [];
1173
1174
  const after = [];
1174
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
+ }
1175
1182
  if (target.type === "IdentifierPattern") {
1176
1183
  const name2 = this.name(target.name);
1177
1184
  const reference = mode === "declare" ? identifier(name2) : this.reference(target);
@@ -1244,6 +1251,11 @@ end
1244
1251
  * a temporary that a nested pattern then destructures (`then`). The
1245
1252
  * statement creating it is pushed onto `after`. */
1246
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
+ }
1247
1259
  if (target.type === "IdentifierPattern") {
1248
1260
  if (mode === "declare") {
1249
1261
  const name2 = this.name(target.name);
@@ -1392,11 +1404,13 @@ end
1392
1404
  * the last value of a list expands, so the whole list is built as an
1393
1405
  * array first and that is what expands. A call written last is one
1394
1406
  * value — see `keptToOne`. */
1395
- arguments(list) {
1407
+ arguments(written) {
1408
+ const { items: list, many } = packs(written);
1396
1409
  const spreads = list.filter((a) => a.type === "SpreadElement");
1397
1410
  if (!spreads.length) {
1398
1411
  const lowered = list.map((a) => this.expression(a));
1399
- return lowered.length ? [...lowered.slice(0, -1), this.keptToOne(lowered[lowered.length - 1])] : lowered;
1412
+ const last2 = list.length - 1;
1413
+ return lowered.length && !many.has(list[last2]) ? [...lowered.slice(0, -1), this.keptToOne(lowered[last2])] : lowered;
1400
1414
  }
1401
1415
  const unpack = (value) => call(member(this.builtin("table"), "unpack"), [value]);
1402
1416
  const last = list[list.length - 1];
@@ -1657,8 +1671,9 @@ end
1657
1671
  /** `[1, ...xs, 2]`. Without a spread it is a Luau sequence; with one, the
1658
1672
  * runs of plain elements and the spread arrays are joined by `concat`. */
1659
1673
  arrayExpression(node) {
1660
- if (!node.elements.some((e) => e.type === "SpreadElement")) {
1661
- return table(node.elements.map((e) => ({ type: "TableFieldPositional", value: this.expression(e) })));
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) })));
1662
1677
  }
1663
1678
  const parts = [];
1664
1679
  let current = [];
@@ -1670,7 +1685,7 @@ end
1670
1685
  })));
1671
1686
  current = [];
1672
1687
  };
1673
- for (const element of node.elements) {
1688
+ for (const element of elements) {
1674
1689
  if (element.type === "SpreadElement") {
1675
1690
  flush(false);
1676
1691
  parts.push(this.expression(element.argument));
@@ -1682,6 +1697,29 @@ end
1682
1697
  return call(this.helper("concat"), parts);
1683
1698
  }
1684
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
+ }
1685
1723
  function isLink(node) {
1686
1724
  return node.type === "MemberExpression" || node.type === "IndexExpression" || node.type === "CallExpression" || node.type === "MethodCallExpression";
1687
1725
  }
@@ -1730,10 +1768,19 @@ function memberChain(e) {
1730
1768
  }
1731
1769
  return void 0;
1732
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
+ }
1733
1777
  function identifierPatterns(target) {
1734
1778
  switch (target.type) {
1735
1779
  case "IdentifierPattern":
1736
1780
  return [target];
1781
+ case "MemberExpression":
1782
+ case "IndexExpression":
1783
+ return [];
1737
1784
  case "ObjectPattern":
1738
1785
  return [...target.properties.flatMap((p) => identifierPatterns(p.value)), ...target.rest ? identifierPatterns(target.rest) : []];
1739
1786
  case "ArrayPattern":
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  bundle
4
- } from "./chunk-HE6YH7U3.js";
4
+ } from "./chunk-3ZQ3EITC.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { writeFileSync } from "fs";
package/dist/index.cjs CHANGED
@@ -882,12 +882,13 @@ var Lowerer = class {
882
882
  }];
883
883
  case "GenericForStatement": {
884
884
  const prelude = [];
885
+ const variable = declared(node.variable);
885
886
  let item;
886
- if (node.variable.type === "IdentifierPattern") {
887
- item = this.name(node.variable.name);
887
+ if (variable.type === "IdentifierPattern") {
888
+ item = this.name(variable.name);
888
889
  } else {
889
890
  item = this.names.fresh("item");
890
- prelude.push(...this.destructure(node.variable, identifier(item), "declare"));
891
+ prelude.push(...this.destructure(variable, identifier(item), "declare"));
891
892
  }
892
893
  const form = this.options.types?.loops.get(node) ?? { walks: "values", viaIter: false };
893
894
  let source = this.expression(node.iterator);
@@ -1154,7 +1155,7 @@ end
1154
1155
  * level, where every name is declared up front — nothing is declared,
1155
1156
  * only assigned. */
1156
1157
  variableDeclaration(node, mode) {
1157
- const target = node.name;
1158
+ const target = declared(node.name);
1158
1159
  if (!node.init) {
1159
1160
  const names = identifierPatterns(target);
1160
1161
  if (mode === "declare") return [local(names.map((p) => this.name(p.name)), [])];
@@ -1196,6 +1197,12 @@ end
1196
1197
  const reads = [];
1197
1198
  const after = [];
1198
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
+ }
1199
1206
  if (target.type === "IdentifierPattern") {
1200
1207
  const name2 = this.name(target.name);
1201
1208
  const reference = mode === "declare" ? identifier(name2) : this.reference(target);
@@ -1268,6 +1275,11 @@ end
1268
1275
  * a temporary that a nested pattern then destructures (`then`). The
1269
1276
  * statement creating it is pushed onto `after`. */
1270
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
+ }
1271
1283
  if (target.type === "IdentifierPattern") {
1272
1284
  if (mode === "declare") {
1273
1285
  const name2 = this.name(target.name);
@@ -1416,11 +1428,13 @@ end
1416
1428
  * the last value of a list expands, so the whole list is built as an
1417
1429
  * array first and that is what expands. A call written last is one
1418
1430
  * value — see `keptToOne`. */
1419
- arguments(list) {
1431
+ arguments(written) {
1432
+ const { items: list, many } = packs(written);
1420
1433
  const spreads = list.filter((a) => a.type === "SpreadElement");
1421
1434
  if (!spreads.length) {
1422
1435
  const lowered = list.map((a) => this.expression(a));
1423
- return lowered.length ? [...lowered.slice(0, -1), this.keptToOne(lowered[lowered.length - 1])] : lowered;
1436
+ const last2 = list.length - 1;
1437
+ return lowered.length && !many.has(list[last2]) ? [...lowered.slice(0, -1), this.keptToOne(lowered[last2])] : lowered;
1424
1438
  }
1425
1439
  const unpack = (value) => call(member(this.builtin("table"), "unpack"), [value]);
1426
1440
  const last = list[list.length - 1];
@@ -1681,8 +1695,9 @@ end
1681
1695
  /** `[1, ...xs, 2]`. Without a spread it is a Luau sequence; with one, the
1682
1696
  * runs of plain elements and the spread arrays are joined by `concat`. */
1683
1697
  arrayExpression(node) {
1684
- if (!node.elements.some((e) => e.type === "SpreadElement")) {
1685
- return table(node.elements.map((e) => ({ type: "TableFieldPositional", value: this.expression(e) })));
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) })));
1686
1701
  }
1687
1702
  const parts = [];
1688
1703
  let current = [];
@@ -1694,7 +1709,7 @@ end
1694
1709
  })));
1695
1710
  current = [];
1696
1711
  };
1697
- for (const element of node.elements) {
1712
+ for (const element of elements) {
1698
1713
  if (element.type === "SpreadElement") {
1699
1714
  flush(false);
1700
1715
  parts.push(this.expression(element.argument));
@@ -1706,6 +1721,29 @@ end
1706
1721
  return call(this.helper("concat"), parts);
1707
1722
  }
1708
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
+ }
1709
1747
  function isLink(node) {
1710
1748
  return node.type === "MemberExpression" || node.type === "IndexExpression" || node.type === "CallExpression" || node.type === "MethodCallExpression";
1711
1749
  }
@@ -1754,10 +1792,19 @@ function memberChain(e) {
1754
1792
  }
1755
1793
  return void 0;
1756
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
+ }
1757
1801
  function identifierPatterns(target) {
1758
1802
  switch (target.type) {
1759
1803
  case "IdentifierPattern":
1760
1804
  return [target];
1805
+ case "MemberExpression":
1806
+ case "IndexExpression":
1807
+ return [];
1761
1808
  case "ObjectPattern":
1762
1809
  return [...target.properties.flatMap((p) => identifierPatterns(p.value)), ...target.rest ? identifierPatterns(target.rest) : []];
1763
1810
  case "ArrayPattern":
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  lower,
5
5
  program,
6
6
  resolveConfig
7
- } from "./chunk-HE6YH7U3.js";
7
+ } from "./chunk-3ZQ3EITC.js";
8
8
 
9
9
  // src/compile.ts
10
10
  import { readFileSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tilua/compiler",
3
- "version": "2.3.0",
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.3.0"
40
+ "@tilua/parser": "^2.4.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^22.20.2",