Skip to content

Commit fca36fd

Browse files
committed
Initial proof of concept for string destructuring
Still many edge cases missing
1 parent bfba5b7 commit fca36fd

File tree

3 files changed

+121
-28
lines changed

3 files changed

+121
-28
lines changed

src/transformation/visitors/binary-expression/assignments.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ export function transformAssignmentStatement(
225225

226226
if (ts.isArrayLiteralExpression(expression.right)) {
227227
right = transformExpressionList(context, expression.right.elements);
228+
} else if (ts.isStringLiteral(expression.right)) {
229+
right = Array.from(expression.right.text).map(c => lua.createStringLiteral(c));
228230
} else {
229231
right = context.transformExpression(expression.right);
230232
if (!isMultiReturnCall(context, expression.right) && isArrayType(context, rightType)) {

src/transformation/visitors/variable-declaration.ts

Lines changed: 78 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { transformIdentifier } from "./identifier";
1313
import { isMultiReturnCall } from "./language-extensions/multi";
1414
import { transformPropertyName } from "./literal";
1515
import { moveToPrecedingTemp } from "./expression-list";
16+
import { isStringType } from "../utils/typescript";
1617

1718
export function transformArrayBindingElement(
1819
context: TransformationContext,
@@ -40,7 +41,8 @@ export function transformBindingPattern(
4041
context: TransformationContext,
4142
pattern: ts.BindingPattern,
4243
table: lua.Expression,
43-
propertyAccessStack: ts.PropertyName[] = []
44+
propertyAccessStack: ts.PropertyName[] = [],
45+
isStringBinding = false
4446
): lua.Statement[] {
4547
const result: lua.Statement[] = [];
4648

@@ -125,10 +127,21 @@ export function transformBindingPattern(
125127
);
126128
}
127129
} else {
128-
expression = lua.createTableIndexExpression(
129-
tableExpression,
130-
ts.isObjectBindingPattern(pattern) ? propertyName : lua.createNumericLiteral(index + 1)
131-
);
130+
if (isStringBinding) {
131+
// don't add 1 to index stringaccess already takes care of that
132+
expression = transformLuaLibFunction(
133+
context,
134+
LuaLibFeature.StringAccess,
135+
pattern,
136+
table,
137+
lua.createNumericLiteral(index)
138+
);
139+
} else {
140+
expression = lua.createTableIndexExpression(
141+
tableExpression,
142+
ts.isObjectBindingPattern(pattern) ? propertyName : lua.createNumericLiteral(index + 1)
143+
);
144+
}
132145
}
133146

134147
result.push(...createLocalOrExportedOrGlobalDeclaration(context, variableName, expression));
@@ -153,36 +166,67 @@ export function transformBindingPattern(
153166
return result;
154167
}
155168

156-
export function transformBindingVariableDeclaration(
169+
function requiresComplexBindingVariableDeclaration(
157170
context: TransformationContext,
158171
bindingPattern: ts.BindingPattern,
159172
initializer?: ts.Expression
160-
): lua.Statement[] {
161-
const statements: lua.Statement[] = [];
162-
163-
// For object, nested or rest bindings fall back to transformBindingPattern
173+
): boolean {
174+
// For object, strings, nested or rest bindings fall back to transformBindingPattern
164175
const isComplexBindingElement = (e: ts.ArrayBindingElement) =>
165176
ts.isBindingElement(e) && (!ts.isIdentifier(e.name) || e.dotDotDotToken);
166177

167-
if (ts.isObjectBindingPattern(bindingPattern) || bindingPattern.elements.some(isComplexBindingElement)) {
168-
let table: lua.Expression;
169-
if (initializer) {
170-
// Contain the expression in a temporary variable
171-
let expression = context.transformExpression(initializer);
172-
if (isMultiReturnCall(context, initializer)) {
173-
expression = wrapInTable(expression);
174-
}
175-
const { precedingStatements: moveStatements, result: movedExpr } = transformInPrecedingStatementScope(
176-
context,
177-
() => moveToPrecedingTemp(context, expression, initializer)
178-
);
179-
statements.push(...moveStatements);
180-
table = movedExpr;
181-
} else {
182-
table = lua.createAnonymousIdentifier();
178+
const hasStringInitializer = initializer && isStringType(context, context.checker.getTypeAtLocation(initializer));
179+
180+
return (
181+
ts.isObjectBindingPattern(bindingPattern) ||
182+
bindingPattern.elements.some(isComplexBindingElement) ||
183+
Boolean(hasStringInitializer)
184+
);
185+
}
186+
function transformComplexBindingVariableDeclaration(
187+
context: TransformationContext,
188+
bindingPattern: ts.BindingPattern,
189+
initializer?: ts.Expression
190+
): lua.Statement[] {
191+
const statements: lua.Statement[] = [];
192+
193+
let table: lua.Expression;
194+
if (initializer) {
195+
// Contain the expression in a temporary variable
196+
let expression = context.transformExpression(initializer);
197+
if (isMultiReturnCall(context, initializer)) {
198+
expression = wrapInTable(expression);
183199
}
184-
statements.push(...transformBindingPattern(context, bindingPattern, table));
185-
return statements;
200+
const { precedingStatements: moveStatements, result: movedExpr } = transformInPrecedingStatementScope(
201+
context,
202+
() => moveToPrecedingTemp(context, expression, initializer)
203+
);
204+
statements.push(...moveStatements);
205+
table = movedExpr;
206+
} else {
207+
table = lua.createAnonymousIdentifier();
208+
}
209+
statements.push(
210+
...transformBindingPattern(
211+
context,
212+
bindingPattern,
213+
table,
214+
[],
215+
initializer && isStringType(context, context.checker.getTypeAtLocation(initializer))
216+
)
217+
);
218+
return statements;
219+
}
220+
221+
export function transformBindingVariableDeclaration(
222+
context: TransformationContext,
223+
bindingPattern: ts.BindingPattern,
224+
initializer?: ts.Expression
225+
): lua.Statement[] {
226+
const statements: lua.Statement[] = [];
227+
228+
if (requiresComplexBindingVariableDeclaration(context, bindingPattern, initializer)) {
229+
return transformComplexBindingVariableDeclaration(context, bindingPattern, initializer);
186230
}
187231

188232
const vars =
@@ -208,6 +252,12 @@ export function transformBindingVariableDeclaration(
208252
? initializer.elements.map(e => context.transformExpression(e))
209253
: lua.createNilLiteral();
210254
statements.push(...createLocalOrExportedOrGlobalDeclaration(context, vars, values, initializer));
255+
} else if (ts.isStringLiteral(initializer)) {
256+
const values =
257+
initializer.text.length > 0
258+
? Array.from(initializer.text).map(c => lua.createStringLiteral(c))
259+
: lua.createNilLiteral();
260+
statements.push(...createLocalOrExportedOrGlobalDeclaration(context, vars, values, initializer));
211261
} else {
212262
// local vars = this.transpileDestructingAssignmentValue(node.initializer);
213263
const unpackedInitializer = createUnpackCall(

test/unit/destructuring.spec.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,44 @@ test("no exception from semantically invalid TS", () => {
237237
.disableSemanticCheck()
238238
.expectToHaveDiagnostics([invalidMultiReturnAccess.code, cannotAssignToNodeOfKind.code]);
239239
});
240+
241+
describe("string destructuring", () => {
242+
test("string literal declaration", () => {
243+
util.testFunction`
244+
const [a, b, c] = "test";
245+
return { a, b, c };
246+
`.expectToMatchJsResult();
247+
});
248+
249+
test("string literal assignment", () => {
250+
util.testFunction`
251+
let a = "";
252+
let b = "";
253+
let c = "";
254+
[a, b, c] = "test";
255+
return { a, b, c };
256+
`
257+
.debug()
258+
.expectToMatchJsResult();
259+
});
260+
261+
test("string assignment", () => {
262+
util.testFunction`
263+
const foo = "test";
264+
const [a, b, c] = foo;
265+
return { a, b, c };
266+
`.expectToMatchJsResult();
267+
});
268+
269+
// not wokring right now: send help pls
270+
test("for loop init", () => {
271+
util.testFunction`
272+
const foo = "test";
273+
for (const [a, b, c] of foo) {
274+
return { a, b, c };
275+
}
276+
`
277+
.debug()
278+
.expectToMatchJsResult();
279+
});
280+
});

0 commit comments

Comments
 (0)