typescript-to-lua 1.1.1 → 1.2.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.
Files changed (113) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/LuaAST.d.ts +1 -0
  3. package/dist/LuaAST.js +10 -2
  4. package/dist/LuaLib.d.ts +3 -3
  5. package/dist/LuaLib.js +4 -4
  6. package/dist/LuaPrinter.d.ts +11 -4
  7. package/dist/LuaPrinter.js +54 -11
  8. package/dist/lualib/ArrayFlat.lua +1 -1
  9. package/dist/lualib/ArrayFlatMap.lua +1 -1
  10. package/dist/lualib/ArrayIsArray.lua +1 -1
  11. package/dist/lualib/ArrayReduce.lua +7 -1
  12. package/dist/lualib/ArrayReduceRight.lua +7 -1
  13. package/dist/lualib/ArraySetLength.lua +1 -1
  14. package/dist/lualib/ArraySplice.lua +5 -5
  15. package/dist/lualib/Await.lua +14 -10
  16. package/dist/lualib/CloneDescriptor.lua +8 -8
  17. package/dist/lualib/Decorate.lua +1 -1
  18. package/dist/lualib/DelegatedYield.lua +1 -3
  19. package/dist/lualib/Delete.lua +6 -6
  20. package/dist/lualib/Error.lua +47 -44
  21. package/dist/lualib/Generator.lua +1 -5
  22. package/dist/lualib/InstanceOf.lua +1 -1
  23. package/dist/lualib/InstanceOfObject.lua +1 -1
  24. package/dist/lualib/Map.lua +130 -134
  25. package/dist/lualib/Number.lua +1 -1
  26. package/dist/lualib/NumberIsFinite.lua +1 -1
  27. package/dist/lualib/NumberToString.lua +3 -5
  28. package/dist/lualib/ObjectDefineProperty.lua +40 -8
  29. package/dist/lualib/ParseFloat.lua +9 -5
  30. package/dist/lualib/ParseInt.lua +15 -3
  31. package/dist/lualib/Promise.lua +18 -22
  32. package/dist/lualib/Set.lua +119 -123
  33. package/dist/lualib/SetDescriptor.lua +7 -1
  34. package/dist/lualib/SourceMapTraceBack.lua +1 -1
  35. package/dist/lualib/SparseArrayNew.lua +5 -0
  36. package/dist/lualib/SparseArrayPush.lua +9 -0
  37. package/dist/lualib/SparseArraySpread.lua +4 -0
  38. package/dist/lualib/StringAccess.lua +1 -1
  39. package/dist/lualib/StringCharCodeAt.lua +1 -1
  40. package/dist/lualib/StringEndsWith.lua +2 -2
  41. package/dist/lualib/StringPadEnd.lua +2 -2
  42. package/dist/lualib/StringPadStart.lua +2 -2
  43. package/dist/lualib/StringReplace.lua +7 -1
  44. package/dist/lualib/StringSlice.lua +2 -2
  45. package/dist/lualib/StringSplit.lua +3 -3
  46. package/dist/lualib/StringStartsWith.lua +1 -1
  47. package/dist/lualib/StringSubstr.lua +1 -1
  48. package/dist/lualib/StringSubstring.lua +2 -2
  49. package/dist/lualib/Symbol.lua +3 -5
  50. package/dist/lualib/WeakMap.lua +40 -42
  51. package/dist/lualib/WeakSet.lua +36 -38
  52. package/dist/lualib/lualib_bundle.lua +603 -555
  53. package/dist/transformation/builtins/array.js +1 -2
  54. package/dist/transformation/builtins/function.js +1 -2
  55. package/dist/transformation/builtins/index.d.ts +1 -1
  56. package/dist/transformation/builtins/index.js +37 -4
  57. package/dist/transformation/builtins/object.d.ts +2 -1
  58. package/dist/transformation/builtins/object.js +1 -2
  59. package/dist/transformation/builtins/string.js +1 -2
  60. package/dist/transformation/context/context.d.ts +12 -0
  61. package/dist/transformation/context/context.js +102 -3
  62. package/dist/transformation/utils/diagnostics.d.ts +6 -0
  63. package/dist/transformation/utils/diagnostics.js +3 -1
  64. package/dist/transformation/utils/lua-ast.d.ts +0 -1
  65. package/dist/transformation/utils/lua-ast.js +13 -22
  66. package/dist/transformation/utils/preceding-statements.d.ts +3 -0
  67. package/dist/transformation/utils/preceding-statements.js +11 -0
  68. package/dist/transformation/utils/scope.d.ts +1 -0
  69. package/dist/transformation/utils/scope.js +8 -1
  70. package/dist/transformation/utils/typescript/index.d.ts +1 -0
  71. package/dist/transformation/utils/typescript/index.js +16 -1
  72. package/dist/transformation/visitors/access.d.ts +3 -0
  73. package/dist/transformation/visitors/access.js +63 -36
  74. package/dist/transformation/visitors/binary-expression/assignments.d.ts +3 -2
  75. package/dist/transformation/visitors/binary-expression/assignments.js +66 -57
  76. package/dist/transformation/visitors/binary-expression/compound.d.ts +5 -4
  77. package/dist/transformation/visitors/binary-expression/compound.js +66 -68
  78. package/dist/transformation/visitors/binary-expression/destructuring-assignments.d.ts +2 -2
  79. package/dist/transformation/visitors/binary-expression/destructuring-assignments.js +54 -25
  80. package/dist/transformation/visitors/binary-expression/index.d.ts +3 -1
  81. package/dist/transformation/visitors/binary-expression/index.js +64 -20
  82. package/dist/transformation/visitors/call.d.ts +3 -2
  83. package/dist/transformation/visitors/call.js +109 -122
  84. package/dist/transformation/visitors/class/index.js +4 -7
  85. package/dist/transformation/visitors/class/new.js +4 -6
  86. package/dist/transformation/visitors/conditional.js +28 -14
  87. package/dist/transformation/visitors/delete.js +4 -0
  88. package/dist/transformation/visitors/expression-list.d.ts +7 -0
  89. package/dist/transformation/visitors/expression-list.js +120 -0
  90. package/dist/transformation/visitors/function.js +6 -8
  91. package/dist/transformation/visitors/identifier.js +4 -0
  92. package/dist/transformation/visitors/literal.js +69 -16
  93. package/dist/transformation/visitors/loops/do-while.js +34 -8
  94. package/dist/transformation/visitors/loops/for.js +25 -5
  95. package/dist/transformation/visitors/loops/utils.d.ts +1 -0
  96. package/dist/transformation/visitors/loops/utils.js +13 -2
  97. package/dist/transformation/visitors/modules/export.js +1 -1
  98. package/dist/transformation/visitors/optional-chaining.d.ts +16 -0
  99. package/dist/transformation/visitors/optional-chaining.js +172 -0
  100. package/dist/transformation/visitors/sourceFile.js +4 -1
  101. package/dist/transformation/visitors/switch.js +29 -9
  102. package/dist/transformation/visitors/template.js +5 -2
  103. package/dist/transformation/visitors/typeof.js +3 -1
  104. package/dist/transformation/visitors/variable-declaration.js +21 -7
  105. package/dist/transformation/visitors/void.js +4 -7
  106. package/dist/transpilation/transformers.js +4 -1
  107. package/dist/transpilation/utils.js +2 -1
  108. package/package.json +5 -5
  109. package/dist/lualib/OptionalChainAccess.lua +0 -6
  110. package/dist/lualib/OptionalFunctionCall.lua +0 -6
  111. package/dist/lualib/OptionalMethodCall.lua +0 -11
  112. package/dist/transformation/utils/transform.d.ts +0 -8
  113. package/dist/transformation/utils/transform.js +0 -14
@@ -10,6 +10,7 @@ const export_1 = require("../utils/export");
10
10
  const function_context_1 = require("../utils/function-context");
11
11
  const lua_ast_1 = require("../utils/lua-ast");
12
12
  const lualib_1 = require("../utils/lualib");
13
+ const preceding_statements_1 = require("../utils/preceding-statements");
13
14
  const scope_1 = require("../utils/scope");
14
15
  const async_await_1 = require("./async-await");
15
16
  const identifier_1 = require("./identifier");
@@ -34,8 +35,8 @@ function isRestParameterReferenced(identifier, scope) {
34
35
  }
35
36
  function transformFunctionBodyContent(context, body) {
36
37
  if (!ts.isBlock(body)) {
37
- const returnStatement = (0, return_1.transformExpressionBodyToReturnStatement)(context, body);
38
- return [returnStatement];
38
+ const [precedingStatements, returnStatement] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => (0, return_1.transformExpressionBodyToReturnStatement)(context, body));
39
+ return [...precedingStatements, returnStatement];
39
40
  }
40
41
  const bodyStatements = (0, scope_1.performHoisting)(context, context.transformStatements(body.statements));
41
42
  return bodyStatements;
@@ -163,14 +164,11 @@ function transformFunctionLikeDeclaration(node, context) {
163
164
  if (symbol) {
164
165
  // TODO: Not using symbol ids because of https://github.com/microsoft/TypeScript/issues/37131
165
166
  const isReferenced = [...functionScope.referencedSymbols].some(([, nodes]) => nodes.some(n => { var _a; return ((_a = context.checker.getSymbolAtLocation(n)) === null || _a === void 0 ? void 0 : _a.valueDeclaration) === symbol.valueDeclaration; }));
166
- // Only wrap if the name is actually referenced inside the function
167
+ // Only handle if the name is actually referenced inside the function
167
168
  if (isReferenced) {
168
169
  const nameIdentifier = (0, identifier_1.transformIdentifier)(context, node.name);
169
- // We cannot use transformToImmediatelyInvokedFunctionExpression() here because we need to transpile
170
- // the function first to determine if it's self-referencing. Fortunately, this does not cause issues
171
- // with var-arg optimization because the IIFE is just wrapping another function which will already push
172
- // another scope.
173
- return (0, lua_ast_1.createImmediatelyInvokedFunctionExpression)([lua.createVariableDeclarationStatement(nameIdentifier, functionExpression)], lua.cloneIdentifier(nameIdentifier));
170
+ context.addPrecedingStatements(lua.createVariableDeclarationStatement(nameIdentifier, functionExpression));
171
+ return lua.cloneIdentifier(nameIdentifier);
174
172
  }
175
173
  }
176
174
  }
@@ -16,7 +16,11 @@ const operators_1 = require("./language-extensions/operators");
16
16
  const range_1 = require("./language-extensions/range");
17
17
  const table_1 = require("./language-extensions/table");
18
18
  const vararg_1 = require("./language-extensions/vararg");
19
+ const optional_chaining_1 = require("./optional-chaining");
19
20
  function transformIdentifier(context, identifier) {
21
+ if ((0, optional_chaining_1.isOptionalContinuation)(identifier)) {
22
+ return lua.createIdentifier(identifier.text);
23
+ }
20
24
  if ((0, multi_1.isMultiFunctionNode)(context, identifier)) {
21
25
  context.diagnostics.push((0, diagnostics_1.invalidMultiFunctionUse)(identifier));
22
26
  return lua.createAnonymousIdentifier(identifier);
@@ -11,7 +11,7 @@ const safe_names_1 = require("../utils/safe-names");
11
11
  const symbols_1 = require("../utils/symbols");
12
12
  const typescript_1 = require("../utils/typescript");
13
13
  const function_1 = require("./function");
14
- const call_1 = require("./call");
14
+ const expression_list_1 = require("./expression-list");
15
15
  const multi_1 = require("./language-extensions/multi");
16
16
  // TODO: Move to object-literal.ts?
17
17
  function transformPropertyName(context, node) {
@@ -61,13 +61,27 @@ const transformObjectLiteralExpression = (expression, context) => {
61
61
  context.diagnostics.push(...violations.map(e => (0, diagnostics_1.invalidMultiFunctionUse)(e)));
62
62
  return lua.createNilLiteral(expression);
63
63
  }
64
- let properties = [];
65
- const tableExpressions = [];
66
- for (const element of expression.properties) {
64
+ const properties = [];
65
+ const initializers = [];
66
+ const keyPrecedingStatements = [];
67
+ const valuePrecedingStatements = [];
68
+ let lastPrecedingStatementsIndex = -1;
69
+ for (let i = 0; i < expression.properties.length; ++i) {
70
+ const element = expression.properties[i];
71
+ // Transform key and cache preceding statements
72
+ context.pushPrecedingStatements();
67
73
  const name = element.name ? transformPropertyName(context, element.name) : undefined;
74
+ let precedingStatements = context.popPrecedingStatements();
75
+ keyPrecedingStatements.push(precedingStatements);
76
+ if (precedingStatements.length > 0) {
77
+ lastPrecedingStatementsIndex = i;
78
+ }
79
+ // Transform value and cache preceding statements
80
+ context.pushPrecedingStatements();
68
81
  if (ts.isPropertyAssignment(element)) {
69
82
  const expression = context.transformExpression(element.initializer);
70
83
  properties.push(lua.createTableFieldExpression(expression, name, element));
84
+ initializers.push(element.initializer);
71
85
  }
72
86
  else if (ts.isShorthandPropertyAssignment(element)) {
73
87
  const valueSymbol = context.checker.getShorthandAssignmentValueSymbol(element);
@@ -76,19 +90,14 @@ const transformObjectLiteralExpression = (expression, context) => {
76
90
  }
77
91
  const identifier = createShorthandIdentifier(context, valueSymbol, element.name);
78
92
  properties.push(lua.createTableFieldExpression(identifier, name, element));
93
+ initializers.push(element);
79
94
  }
80
95
  else if (ts.isMethodDeclaration(element)) {
81
96
  const expression = (0, function_1.transformFunctionLikeDeclaration)(element, context);
82
97
  properties.push(lua.createTableFieldExpression(expression, name, element));
98
+ initializers.push(element);
83
99
  }
84
100
  else if (ts.isSpreadAssignment(element)) {
85
- // Create a table for preceding properties to preserve property order
86
- // { x: 0, ...{ y: 2 }, y: 1, z: 2 } --> __TS__ObjectAssign({x = 0}, {y = 2}, {y = 1, z = 2})
87
- if (properties.length > 0) {
88
- const tableExpression = lua.createTableExpression(properties, expression);
89
- tableExpressions.push(tableExpression);
90
- properties = [];
91
- }
92
101
  const type = context.checker.getTypeAtLocation(element.expression);
93
102
  let tableExpression;
94
103
  if ((0, typescript_1.isArrayType)(context, type)) {
@@ -97,7 +106,8 @@ const transformObjectLiteralExpression = (expression, context) => {
97
106
  else {
98
107
  tableExpression = context.transformExpression(element.expression);
99
108
  }
100
- tableExpressions.push(tableExpression);
109
+ properties.push(tableExpression);
110
+ initializers.push(element.expression);
101
111
  }
102
112
  else if (ts.isAccessor(element)) {
103
113
  context.diagnostics.push((0, diagnostics_1.unsupportedAccessorInObjectLiteral)(element));
@@ -105,13 +115,56 @@ const transformObjectLiteralExpression = (expression, context) => {
105
115
  else {
106
116
  (0, utils_1.assertNever)(element);
107
117
  }
118
+ precedingStatements = context.popPrecedingStatements();
119
+ valuePrecedingStatements.push(precedingStatements);
120
+ if (precedingStatements.length > 0) {
121
+ lastPrecedingStatementsIndex = i;
122
+ }
123
+ }
124
+ // Expressions referenced before others that produced preceding statements need to be cached in temps
125
+ if (lastPrecedingStatementsIndex >= 0) {
126
+ for (let i = 0; i < properties.length; ++i) {
127
+ const property = properties[i];
128
+ // Bubble up key's preceding statements
129
+ context.addPrecedingStatements(keyPrecedingStatements[i]);
130
+ // Cache computed property name in temp if before the last expression that generated preceding statements
131
+ if (i <= lastPrecedingStatementsIndex && lua.isTableFieldExpression(property) && property.key) {
132
+ property.key = (0, expression_list_1.moveToPrecedingTemp)(context, property.key, expression.properties[i].name);
133
+ }
134
+ // Bubble up value's preceding statements
135
+ context.addPrecedingStatements(valuePrecedingStatements[i]);
136
+ // Cache property value in temp if before the last expression that generated preceding statements
137
+ if (i < lastPrecedingStatementsIndex) {
138
+ if (lua.isTableFieldExpression(property)) {
139
+ property.value = (0, expression_list_1.moveToPrecedingTemp)(context, property.value, initializers[i]);
140
+ }
141
+ else {
142
+ properties[i] = (0, expression_list_1.moveToPrecedingTemp)(context, property, initializers[i]);
143
+ }
144
+ }
145
+ }
146
+ }
147
+ // Sort into field expressions and tables to pass into __TS__ObjectAssign
148
+ let fields = [];
149
+ const tableExpressions = [];
150
+ for (const property of properties) {
151
+ if (lua.isTableFieldExpression(property)) {
152
+ fields.push(property);
153
+ }
154
+ else {
155
+ if (fields.length > 0) {
156
+ tableExpressions.push(lua.createTableExpression(fields));
157
+ }
158
+ tableExpressions.push(property);
159
+ fields = [];
160
+ }
108
161
  }
109
162
  if (tableExpressions.length === 0) {
110
- return lua.createTableExpression(properties, expression);
163
+ return lua.createTableExpression(fields, expression);
111
164
  }
112
165
  else {
113
- if (properties.length > 0) {
114
- const tableExpression = lua.createTableExpression(properties, expression);
166
+ if (fields.length > 0) {
167
+ const tableExpression = lua.createTableExpression(fields, expression);
115
168
  tableExpressions.push(tableExpression);
116
169
  }
117
170
  if (tableExpressions[0].kind !== lua.SyntaxKind.TableExpression) {
@@ -122,7 +175,7 @@ const transformObjectLiteralExpression = (expression, context) => {
122
175
  };
123
176
  const transformArrayLiteralExpression = (expression, context) => {
124
177
  const filteredElements = expression.elements.map(e => ts.isOmittedExpression(e) ? ts.factory.createIdentifier("undefined") : e);
125
- const values = (0, call_1.flattenSpreadExpressions)(context, filteredElements).map(e => lua.createTableFieldExpression(e));
178
+ const values = (0, expression_list_1.transformExpressionList)(context, filteredElements).map(e => lua.createTableFieldExpression(e));
126
179
  return lua.createTableExpression(values, expression);
127
180
  };
128
181
  exports.literalVisitors = {
@@ -2,19 +2,45 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.transformDoStatement = exports.transformWhileStatement = void 0;
4
4
  const lua = require("../../../LuaAST");
5
+ const preceding_statements_1 = require("../../utils/preceding-statements");
5
6
  const utils_1 = require("./utils");
6
- const transformWhileStatement = (statement, context) => lua.createWhileStatement(lua.createBlock((0, utils_1.transformLoopBody)(context, statement)), context.transformExpression(statement.expression), statement);
7
+ const transformWhileStatement = (statement, context) => {
8
+ const body = (0, utils_1.transformLoopBody)(context, statement);
9
+ let [conditionPrecedingStatements, condition] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(statement.expression));
10
+ // If condition has preceding statements, ensure they are executed every iteration by using the form:
11
+ //
12
+ // while true do
13
+ // condition's preceding statements
14
+ // if not condition then
15
+ // break
16
+ // end
17
+ // ...
18
+ // end
19
+ if (conditionPrecedingStatements.length > 0) {
20
+ conditionPrecedingStatements.push(lua.createIfStatement((0, utils_1.invertCondition)(condition), lua.createBlock([lua.createBreakStatement()]), undefined, statement.expression));
21
+ body.unshift(...conditionPrecedingStatements);
22
+ condition = lua.createBooleanLiteral(true);
23
+ }
24
+ return lua.createWhileStatement(lua.createBlock(body), condition, statement);
25
+ };
7
26
  exports.transformWhileStatement = transformWhileStatement;
8
27
  const transformDoStatement = (statement, context) => {
9
28
  const body = lua.createDoStatement((0, utils_1.transformLoopBody)(context, statement));
10
- let condition = context.transformExpression(statement.expression);
11
- if (lua.isUnaryExpression(condition) && condition.operator === lua.SyntaxKind.NotOperator) {
12
- condition = condition.operand;
13
- }
14
- else {
15
- condition = lua.createUnaryExpression(condition, lua.SyntaxKind.NotOperator);
29
+ let [conditionPrecedingStatements, condition] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => (0, utils_1.invertCondition)(context.transformExpression(statement.expression)));
30
+ // If condition has preceding statements, ensure they are executed every iteration by using the form:
31
+ //
32
+ // repeat
33
+ // ...
34
+ // condition's preceding statements
35
+ // if condition then
36
+ // break
37
+ // end
38
+ // end
39
+ if (conditionPrecedingStatements.length > 0) {
40
+ conditionPrecedingStatements.push(lua.createIfStatement(condition, lua.createBlock([lua.createBreakStatement()]), undefined, statement.expression));
41
+ condition = lua.createBooleanLiteral(false);
16
42
  }
17
- return lua.createRepeatStatement(lua.createBlock([body]), condition, statement);
43
+ return lua.createRepeatStatement(lua.createBlock([body, ...conditionPrecedingStatements]), condition, statement);
18
44
  };
19
45
  exports.transformDoStatement = transformDoStatement;
20
46
  //# sourceMappingURL=do-while.js.map
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.transformForStatement = void 0;
4
4
  const ts = require("typescript");
5
5
  const lua = require("../../../LuaAST");
6
+ const preceding_statements_1 = require("../../utils/preceding-statements");
6
7
  const variable_declaration_1 = require("../variable-declaration");
7
8
  const utils_1 = require("./utils");
8
9
  const transformForStatement = (statement, context) => {
@@ -17,16 +18,35 @@ const transformForStatement = (statement, context) => {
17
18
  result.push(...context.transformStatements(ts.factory.createExpressionStatement(statement.initializer)));
18
19
  }
19
20
  }
20
- const condition = statement.condition
21
- ? context.transformExpression(statement.condition)
22
- : lua.createBooleanLiteral(true);
23
- // Add body
24
21
  const body = (0, utils_1.transformLoopBody)(context, statement);
22
+ let condition;
23
+ if (statement.condition) {
24
+ let conditionPrecedingStatements;
25
+ const tsCondition = statement.condition;
26
+ [conditionPrecedingStatements, condition] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(tsCondition));
27
+ // If condition has preceding statements, ensure they are executed every iteration by using the form:
28
+ //
29
+ // while true do
30
+ // condition's preceding statements
31
+ // if not condition then
32
+ // break
33
+ // end
34
+ // ...
35
+ // end
36
+ if (conditionPrecedingStatements.length > 0) {
37
+ conditionPrecedingStatements.push(lua.createIfStatement((0, utils_1.invertCondition)(condition), lua.createBlock([lua.createBreakStatement()]), undefined, statement.condition));
38
+ body.unshift(...conditionPrecedingStatements);
39
+ condition = lua.createBooleanLiteral(true);
40
+ }
41
+ }
42
+ else {
43
+ condition = lua.createBooleanLiteral(true);
44
+ }
25
45
  if (statement.incrementor) {
26
46
  body.push(...context.transformStatements(ts.factory.createExpressionStatement(statement.incrementor)));
27
47
  }
28
48
  // while (condition) do ... end
29
- result.push(lua.createWhileStatement(lua.createBlock(body), condition));
49
+ result.push(lua.createWhileStatement(lua.createBlock(body), condition, statement));
30
50
  return lua.createDoStatement(result, statement);
31
51
  };
32
52
  exports.transformForStatement = transformForStatement;
@@ -4,3 +4,4 @@ import { TransformationContext } from "../../context";
4
4
  export declare function transformLoopBody(context: TransformationContext, loop: ts.WhileStatement | ts.DoStatement | ts.ForStatement | ts.ForOfStatement | ts.ForInOrOfStatement): lua.Statement[];
5
5
  export declare function getVariableDeclarationBinding(context: TransformationContext, node: ts.VariableDeclarationList): ts.BindingName;
6
6
  export declare function transformForInitializer(context: TransformationContext, initializer: ts.ForInitializer, block: lua.Block): lua.Identifier;
7
+ export declare function invertCondition(expression: lua.Expression): lua.Expression;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.transformForInitializer = exports.getVariableDeclarationBinding = exports.transformLoopBody = void 0;
3
+ exports.invertCondition = exports.transformForInitializer = exports.getVariableDeclarationBinding = exports.transformLoopBody = void 0;
4
4
  const ts = require("typescript");
5
5
  const lua = require("../../../LuaAST");
6
6
  const scope_1 = require("../../utils/scope");
@@ -48,10 +48,21 @@ function transformForInitializer(context, initializer, block) {
48
48
  else {
49
49
  // Assignment to existing variable(s)
50
50
  block.statements.unshift(...((0, typescript_1.isAssignmentPattern)(initializer)
51
- ? (0, destructuring_assignments_1.transformAssignmentPattern)(context, initializer, valueVariable)
51
+ ? (0, destructuring_assignments_1.transformAssignmentPattern)(context, initializer, valueVariable, false)
52
52
  : (0, assignments_1.transformAssignment)(context, initializer, valueVariable)));
53
53
  }
54
54
  return valueVariable;
55
55
  }
56
56
  exports.transformForInitializer = transformForInitializer;
57
+ function invertCondition(expression) {
58
+ if (lua.isUnaryExpression(expression) && expression.operator === lua.SyntaxKind.NotOperator) {
59
+ return expression.operand;
60
+ }
61
+ else {
62
+ const notExpression = lua.createUnaryExpression(expression, lua.SyntaxKind.NotOperator);
63
+ lua.setNodePosition(notExpression, lua.getOriginalPos(expression));
64
+ return notExpression;
65
+ }
66
+ }
67
+ exports.invertCondition = invertCondition;
57
68
  //# sourceMappingURL=utils.js.map
@@ -79,7 +79,7 @@ function transformExportSpecifier(context, node) {
79
79
  }
80
80
  function transformExportSpecifiersFrom(context, statement, moduleSpecifier, exportSpecifiers) {
81
81
  // First transpile as import clause
82
- const importClause = ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports(exportSpecifiers.map(s => ts.factory.createImportSpecifier(s.propertyName, s.name))));
82
+ const importClause = ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports(exportSpecifiers.map(s => ts.factory.createImportSpecifier(statement.isTypeOnly, s.propertyName, s.name))));
83
83
  const importDeclaration = ts.factory.createImportDeclaration(statement.decorators, statement.modifiers, importClause, moduleSpecifier);
84
84
  // Wrap in block to prevent imports from hoisting out of `do` statement
85
85
  const [block] = (0, block_1.transformScopeBlock)(context, ts.factory.createBlock([importDeclaration]), scope_1.ScopeType.Block);
@@ -0,0 +1,16 @@
1
+ import * as ts from "typescript";
2
+ import * as lua from "../../LuaAST";
3
+ import { TransformationContext } from "../context";
4
+ export interface ExpressionWithThisValue {
5
+ expression: lua.Expression;
6
+ thisValue?: lua.Expression;
7
+ }
8
+ export declare function captureThisValue(context: TransformationContext, expression: lua.Expression, thisValueCapture: lua.Identifier, tsOriginal: ts.Node): lua.Expression;
9
+ export interface OptionalContinuation {
10
+ contextualCall?: lua.CallExpression;
11
+ }
12
+ export declare function isOptionalContinuation(node: ts.Node): boolean;
13
+ export declare function getOptionalContinuationData(identifier: ts.Identifier): OptionalContinuation | undefined;
14
+ export declare function transformOptionalChain(context: TransformationContext, node: ts.OptionalChain): lua.Expression;
15
+ export declare function transformOptionalChainWithCapture(context: TransformationContext, node: ts.OptionalChain, thisValueCapture: lua.Identifier | undefined, isDelete?: ts.DeleteExpression): ExpressionWithThisValue;
16
+ export declare function transformOptionalDeleteExpression(context: TransformationContext, node: ts.DeleteExpression, innerExpression: ts.OptionalChain): lua.BooleanLiteral;
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transformOptionalDeleteExpression = exports.transformOptionalChainWithCapture = exports.transformOptionalChain = exports.getOptionalContinuationData = exports.isOptionalContinuation = exports.captureThisValue = void 0;
4
+ const ts = require("typescript");
5
+ const lua = require("../../LuaAST");
6
+ const context_1 = require("../context");
7
+ const utils_1 = require("../../utils");
8
+ const preceding_statements_1 = require("../utils/preceding-statements");
9
+ const access_1 = require("./access");
10
+ const expression_list_1 = require("./expression-list");
11
+ function skipNonNullChains(chain) {
12
+ while (ts.isNonNullChain(chain)) {
13
+ chain = chain.expression;
14
+ }
15
+ return chain;
16
+ }
17
+ function flattenChain(chain) {
18
+ chain = skipNonNullChains(chain);
19
+ const links = [chain];
20
+ while (!chain.questionDotToken && !ts.isTaggedTemplateExpression(chain)) {
21
+ const nextLink = chain.expression;
22
+ (0, utils_1.assert)(ts.isOptionalChain(nextLink));
23
+ chain = skipNonNullChains(nextLink);
24
+ links.unshift(chain);
25
+ }
26
+ return { expression: chain.expression, chain: links };
27
+ }
28
+ function transformExpressionWithThisValueCapture(context, node, thisValueCapture) {
29
+ if (ts.isParenthesizedExpression(node)) {
30
+ return transformExpressionWithThisValueCapture(context, node.expression, thisValueCapture);
31
+ }
32
+ if (ts.isPropertyAccessExpression(node)) {
33
+ return (0, access_1.transformPropertyAccessExpressionWithCapture)(context, node, thisValueCapture);
34
+ }
35
+ if (ts.isElementAccessExpression(node)) {
36
+ return (0, access_1.transformElementAccessExpressionWithCapture)(context, node, thisValueCapture);
37
+ }
38
+ return { expression: context.transformExpression(node) };
39
+ }
40
+ // returns thisValueCapture exactly if a temp variable was used.
41
+ function captureThisValue(context, expression, thisValueCapture, tsOriginal) {
42
+ if (!(0, expression_list_1.shouldMoveToTemp)(context, expression, tsOriginal) && !isOptionalContinuation(tsOriginal)) {
43
+ return expression;
44
+ }
45
+ const tempAssignment = lua.createAssignmentStatement(thisValueCapture, expression, tsOriginal);
46
+ context.addPrecedingStatements(tempAssignment);
47
+ return thisValueCapture;
48
+ }
49
+ exports.captureThisValue = captureThisValue;
50
+ const optionalContinuations = new WeakMap();
51
+ // should be translated verbatim to lua
52
+ function createOptionalContinuationIdentifier(text, tsOriginal) {
53
+ const identifier = ts.factory.createIdentifier(text);
54
+ ts.setOriginalNode(identifier, tsOriginal);
55
+ optionalContinuations.set(identifier, {});
56
+ return identifier;
57
+ }
58
+ function isOptionalContinuation(node) {
59
+ return ts.isIdentifier(node) && optionalContinuations.has(node);
60
+ }
61
+ exports.isOptionalContinuation = isOptionalContinuation;
62
+ function getOptionalContinuationData(identifier) {
63
+ return optionalContinuations.get(identifier);
64
+ }
65
+ exports.getOptionalContinuationData = getOptionalContinuationData;
66
+ function transformOptionalChain(context, node) {
67
+ return transformOptionalChainWithCapture(context, node, undefined).expression;
68
+ }
69
+ exports.transformOptionalChain = transformOptionalChain;
70
+ function transformOptionalChainWithCapture(context, node, thisValueCapture, isDelete) {
71
+ var _a;
72
+ const luaTemp = context.createTempNameForNode(node);
73
+ const { expression: tsLeftExpression, chain } = flattenChain(node);
74
+ // build temp.b.c.d
75
+ const tsTemp = createOptionalContinuationIdentifier(luaTemp.text, tsLeftExpression);
76
+ let tsRightExpression = tsTemp;
77
+ for (const link of chain) {
78
+ if (ts.isPropertyAccessExpression(link)) {
79
+ tsRightExpression = ts.factory.createPropertyAccessExpression(tsRightExpression, link.name);
80
+ }
81
+ else if (ts.isElementAccessExpression(link)) {
82
+ tsRightExpression = ts.factory.createElementAccessExpression(tsRightExpression, link.argumentExpression);
83
+ }
84
+ else if (ts.isCallExpression(link)) {
85
+ tsRightExpression = ts.factory.createCallExpression(tsRightExpression, undefined, link.arguments);
86
+ }
87
+ else {
88
+ (0, utils_1.assertNever)(link);
89
+ }
90
+ ts.setOriginalNode(tsRightExpression, link);
91
+ }
92
+ if (isDelete) {
93
+ tsRightExpression = ts.factory.createDeleteExpression(tsRightExpression);
94
+ ts.setOriginalNode(tsRightExpression, isDelete);
95
+ }
96
+ // transform right expression first to check if thisValue capture is needed
97
+ // capture and return thisValue if requested from outside
98
+ let returnThisValue;
99
+ const [rightPrecedingStatements, rightAssignment] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => {
100
+ let result;
101
+ if (thisValueCapture) {
102
+ ({ expression: result, thisValue: returnThisValue } = transformExpressionWithThisValueCapture(context, tsRightExpression, thisValueCapture));
103
+ }
104
+ else {
105
+ result = context.transformExpression(tsRightExpression);
106
+ }
107
+ return lua.createAssignmentStatement(luaTemp, result);
108
+ });
109
+ // transform left expression, handle thisValue if needed by rightExpression
110
+ const thisValueCaptureName = context.createTempName("this");
111
+ const leftThisValueTemp = lua.createIdentifier(thisValueCaptureName, undefined, context_1.tempSymbolId);
112
+ let capturedThisValue;
113
+ const rightContextualCall = (_a = getOptionalContinuationData(tsTemp)) === null || _a === void 0 ? void 0 : _a.contextualCall;
114
+ const [leftPrecedingStatements, leftExpression] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => {
115
+ let result;
116
+ if (rightContextualCall) {
117
+ ({ expression: result, thisValue: capturedThisValue } = transformExpressionWithThisValueCapture(context, tsLeftExpression, leftThisValueTemp));
118
+ }
119
+ else {
120
+ result = context.transformExpression(tsLeftExpression);
121
+ }
122
+ return result;
123
+ });
124
+ // handle context
125
+ if (rightContextualCall) {
126
+ if (capturedThisValue) {
127
+ rightContextualCall.params[0] = capturedThisValue;
128
+ if (capturedThisValue === leftThisValueTemp) {
129
+ context.addPrecedingStatements(lua.createVariableDeclarationStatement(leftThisValueTemp));
130
+ }
131
+ }
132
+ else {
133
+ if (context.isStrict) {
134
+ rightContextualCall.params[0] = lua.createNilLiteral();
135
+ }
136
+ else {
137
+ const identifier = lua.createIdentifier("_G");
138
+ if (rightPrecedingStatements.length === 0) {
139
+ rightContextualCall.params[0] = identifier;
140
+ }
141
+ else {
142
+ const tempContext = context.createTempNameForLuaExpression(identifier);
143
+ rightPrecedingStatements.unshift(lua.createVariableDeclarationStatement(tempContext, identifier));
144
+ rightContextualCall.params[0] = tempContext;
145
+ }
146
+ }
147
+ }
148
+ }
149
+ // <left preceding statements>
150
+ // local temp = <left>
151
+ // if temp ~= nil then
152
+ // <right preceding statements>
153
+ // temp = temp.b.c.d
154
+ // end
155
+ // return temp
156
+ context.addPrecedingStatements([
157
+ ...leftPrecedingStatements,
158
+ lua.createVariableDeclarationStatement(luaTemp, leftExpression),
159
+ lua.createIfStatement(lua.createBinaryExpression(luaTemp, lua.createNilLiteral(), lua.SyntaxKind.InequalityOperator), lua.createBlock([...rightPrecedingStatements, rightAssignment])),
160
+ ]);
161
+ return {
162
+ expression: luaTemp,
163
+ thisValue: returnThisValue,
164
+ };
165
+ }
166
+ exports.transformOptionalChainWithCapture = transformOptionalChainWithCapture;
167
+ function transformOptionalDeleteExpression(context, node, innerExpression) {
168
+ transformOptionalChainWithCapture(context, innerExpression, undefined, node);
169
+ return lua.createBooleanLiteral(true, node);
170
+ }
171
+ exports.transformOptionalDeleteExpression = transformOptionalDeleteExpression;
172
+ //# sourceMappingURL=optional-chaining.js.map
@@ -6,6 +6,7 @@ const lua = require("../../LuaAST");
6
6
  const utils_1 = require("../../utils");
7
7
  const lua_ast_1 = require("../utils/lua-ast");
8
8
  const lualib_1 = require("../utils/lualib");
9
+ const preceding_statements_1 = require("../utils/preceding-statements");
9
10
  const scope_1 = require("../utils/scope");
10
11
  const typescript_1 = require("../utils/typescript");
11
12
  const transformSourceFileNode = (node, context) => {
@@ -15,7 +16,9 @@ const transformSourceFileNode = (node, context) => {
15
16
  const [statement] = node.statements;
16
17
  if (statement) {
17
18
  (0, utils_1.assert)(ts.isExpressionStatement(statement));
18
- statements.push(lua.createReturnStatement([context.transformExpression(statement.expression)]));
19
+ const [precedingStatements, expression] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(statement.expression));
20
+ statements.push(...precedingStatements);
21
+ statements.push(lua.createReturnStatement([expression]));
19
22
  }
20
23
  else {
21
24
  const errorCall = lua.createCallExpression(lua.createIdentifier("error"), [
@@ -3,7 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.transformSwitchStatement = void 0;
4
4
  const ts = require("typescript");
5
5
  const lua = require("../../LuaAST");
6
+ const preceding_statements_1 = require("../utils/preceding-statements");
6
7
  const scope_1 = require("../utils/scope");
8
+ const binary_expression_1 = require("./binary-expression");
7
9
  const containsBreakOrReturn = (nodes) => {
8
10
  for (const s of nodes) {
9
11
  if (ts.isBreakStatement(s) || ts.isReturnStatement(s)) {
@@ -18,13 +20,23 @@ const containsBreakOrReturn = (nodes) => {
18
20
  }
19
21
  return false;
20
22
  };
21
- const coalesceCondition = (condition, switchVariable, expression, context) => {
23
+ const createOrExpression = (context, left, right, rightPrecedingStatements) => {
24
+ if (rightPrecedingStatements.length > 0) {
25
+ return (0, binary_expression_1.createShortCircuitBinaryExpressionPrecedingStatements)(context, left, right, rightPrecedingStatements, ts.SyntaxKind.BarBarToken);
26
+ }
27
+ else {
28
+ return [rightPrecedingStatements, lua.createBinaryExpression(left, right, lua.SyntaxKind.OrOperator)];
29
+ }
30
+ };
31
+ const coalesceCondition = (condition, conditionPrecedingStatements, switchVariable, expression, context) => {
32
+ const [precedingStatements, transformedExpression] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(expression));
22
33
  // Coalesce skipped statements
34
+ const comparison = lua.createBinaryExpression(switchVariable, transformedExpression, lua.SyntaxKind.EqualityOperator);
23
35
  if (condition) {
24
- return lua.createBinaryExpression(condition, lua.createBinaryExpression(switchVariable, context.transformExpression(expression), lua.SyntaxKind.EqualityOperator), lua.SyntaxKind.OrOperator);
36
+ return createOrExpression(context, condition, comparison, precedingStatements);
25
37
  }
26
38
  // Next condition
27
- return lua.createBinaryExpression(switchVariable, context.transformExpression(expression), lua.SyntaxKind.EqualityOperator);
39
+ return [[...conditionPrecedingStatements, ...precedingStatements], comparison];
28
40
  };
29
41
  const transformSwitchStatement = (statement, context) => {
30
42
  const scope = (0, scope_1.pushScope)(context, scope_1.ScopeType.Switch);
@@ -53,6 +65,7 @@ const transformSwitchStatement = (statement, context) => {
53
65
  let defaultTransformed = false;
54
66
  let isInitialCondition = true;
55
67
  let condition = undefined;
68
+ let conditionPrecedingStatements = [];
56
69
  for (let i = 0; i < clauses.length; i++) {
57
70
  const clause = clauses[i];
58
71
  const previousClause = clauses[i - 1];
@@ -64,29 +77,35 @@ const transformSwitchStatement = (statement, context) => {
64
77
  }
65
78
  // Compute the condition for the if statement
66
79
  if (!ts.isDefaultClause(clause)) {
67
- condition = coalesceCondition(condition, switchVariable, clause.expression, context);
80
+ [conditionPrecedingStatements, condition] = coalesceCondition(condition, conditionPrecedingStatements, switchVariable, clause.expression, context);
68
81
  // Skip empty clauses unless final clause (i.e side-effects)
69
82
  if (i !== clauses.length - 1 && clause.statements.length === 0)
70
83
  continue;
71
84
  // Declare or assign condition variable
72
- statements.push(isInitialCondition
73
- ? lua.createVariableDeclarationStatement(conditionVariable, condition)
74
- : lua.createAssignmentStatement(conditionVariable, lua.createBinaryExpression(conditionVariable, condition, lua.SyntaxKind.OrOperator)));
85
+ if (isInitialCondition) {
86
+ statements.push(...conditionPrecedingStatements, lua.createVariableDeclarationStatement(conditionVariable, condition));
87
+ }
88
+ else {
89
+ [conditionPrecedingStatements, condition] = createOrExpression(context, conditionVariable, condition, conditionPrecedingStatements);
90
+ statements.push(...conditionPrecedingStatements, lua.createAssignmentStatement(conditionVariable, condition));
91
+ }
75
92
  isInitialCondition = false;
76
93
  }
77
94
  else {
78
95
  // If the default is proceeded by empty clauses and will be emitted we may need to initialize the condition
79
96
  if (isInitialCondition) {
80
- statements.push(lua.createVariableDeclarationStatement(conditionVariable, condition !== null && condition !== void 0 ? condition : lua.createBooleanLiteral(false)));
97
+ statements.push(...conditionPrecedingStatements, lua.createVariableDeclarationStatement(conditionVariable, condition !== null && condition !== void 0 ? condition : lua.createBooleanLiteral(false)));
81
98
  // Clear condition ot ensure it is not evaluated twice
82
99
  condition = undefined;
100
+ conditionPrecedingStatements = [];
83
101
  isInitialCondition = false;
84
102
  }
85
103
  // Allow default to fallthrough to final default clause
86
104
  if (i === clauses.length - 1) {
87
105
  // Evaluate the final condition that we may be skipping
88
106
  if (condition) {
89
- statements.push(lua.createAssignmentStatement(conditionVariable, lua.createBinaryExpression(conditionVariable, condition, lua.SyntaxKind.OrOperator)));
107
+ [conditionPrecedingStatements, condition] = createOrExpression(context, conditionVariable, condition, conditionPrecedingStatements);
108
+ statements.push(...conditionPrecedingStatements, lua.createAssignmentStatement(conditionVariable, condition));
90
109
  }
91
110
  continue;
92
111
  }
@@ -106,6 +125,7 @@ const transformSwitchStatement = (statement, context) => {
106
125
  statements.push(lua.createIfStatement(conditionVariable, lua.createBlock(clauseStatements)));
107
126
  // Clear condition for next clause
108
127
  condition = undefined;
128
+ conditionPrecedingStatements = [];
109
129
  }
110
130
  // If no conditions above match, we need to create the final default case code-path,
111
131
  // as we only handle fallthrough into defaults in the previous if statement chain