Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/transformation/visitors/binary-expression/assignments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ export function transformAssignmentStatement(

if (ts.isArrayLiteralExpression(expression.right)) {
right = transformExpressionList(context, expression.right.elements);
} else if (ts.isStringLiteral(expression.right)) {
right = Array.from(expression.right.text).map(c => lua.createStringLiteral(c));
} else {
right = context.transformExpression(expression.right);
if (!isMultiReturnCall(context, expression.right) && isArrayType(context, rightType)) {
Expand Down
106 changes: 78 additions & 28 deletions src/transformation/visitors/variable-declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { transformIdentifier } from "./identifier";
import { isMultiReturnCall } from "./language-extensions/multi";
import { transformPropertyName } from "./literal";
import { moveToPrecedingTemp } from "./expression-list";
import { isStringType } from "../utils/typescript";

export function transformArrayBindingElement(
context: TransformationContext,
Expand Down Expand Up @@ -40,7 +41,8 @@ export function transformBindingPattern(
context: TransformationContext,
pattern: ts.BindingPattern,
table: lua.Expression,
propertyAccessStack: ts.PropertyName[] = []
propertyAccessStack: ts.PropertyName[] = [],
isStringBinding = false
): lua.Statement[] {
const result: lua.Statement[] = [];

Expand Down Expand Up @@ -125,10 +127,21 @@ export function transformBindingPattern(
);
}
} else {
expression = lua.createTableIndexExpression(
tableExpression,
ts.isObjectBindingPattern(pattern) ? propertyName : lua.createNumericLiteral(index + 1)
);
if (isStringBinding) {
// don't add 1 to index stringaccess already takes care of that
expression = transformLuaLibFunction(
context,
LuaLibFeature.StringAccess,
pattern,
table,
lua.createNumericLiteral(index)
);
} else {
expression = lua.createTableIndexExpression(
tableExpression,
ts.isObjectBindingPattern(pattern) ? propertyName : lua.createNumericLiteral(index + 1)
);
}
}

result.push(...createLocalOrExportedOrGlobalDeclaration(context, variableName, expression));
Expand All @@ -153,36 +166,67 @@ export function transformBindingPattern(
return result;
}

export function transformBindingVariableDeclaration(
function requiresComplexBindingVariableDeclaration(
context: TransformationContext,
bindingPattern: ts.BindingPattern,
initializer?: ts.Expression
): lua.Statement[] {
const statements: lua.Statement[] = [];

// For object, nested or rest bindings fall back to transformBindingPattern
): boolean {
// For object, strings, nested or rest bindings fall back to transformBindingPattern
const isComplexBindingElement = (e: ts.ArrayBindingElement) =>
ts.isBindingElement(e) && (!ts.isIdentifier(e.name) || e.dotDotDotToken);

if (ts.isObjectBindingPattern(bindingPattern) || bindingPattern.elements.some(isComplexBindingElement)) {
let table: lua.Expression;
if (initializer) {
// Contain the expression in a temporary variable
let expression = context.transformExpression(initializer);
if (isMultiReturnCall(context, initializer)) {
expression = wrapInTable(expression);
}
const { precedingStatements: moveStatements, result: movedExpr } = transformInPrecedingStatementScope(
context,
() => moveToPrecedingTemp(context, expression, initializer)
);
statements.push(...moveStatements);
table = movedExpr;
} else {
table = lua.createAnonymousIdentifier();
const hasStringInitializer = initializer && isStringType(context, context.checker.getTypeAtLocation(initializer));

return (
ts.isObjectBindingPattern(bindingPattern) ||
bindingPattern.elements.some(isComplexBindingElement) ||
Boolean(hasStringInitializer)
);
}
function transformComplexBindingVariableDeclaration(
context: TransformationContext,
bindingPattern: ts.BindingPattern,
initializer?: ts.Expression
): lua.Statement[] {
const statements: lua.Statement[] = [];

let table: lua.Expression;
if (initializer) {
// Contain the expression in a temporary variable
let expression = context.transformExpression(initializer);
if (isMultiReturnCall(context, initializer)) {
expression = wrapInTable(expression);
}
statements.push(...transformBindingPattern(context, bindingPattern, table));
return statements;
const { precedingStatements: moveStatements, result: movedExpr } = transformInPrecedingStatementScope(
context,
() => moveToPrecedingTemp(context, expression, initializer)
);
statements.push(...moveStatements);
table = movedExpr;
} else {
table = lua.createAnonymousIdentifier();
}
statements.push(
...transformBindingPattern(
context,
bindingPattern,
table,
[],
initializer && isStringType(context, context.checker.getTypeAtLocation(initializer))
)
);
return statements;
}

export function transformBindingVariableDeclaration(
context: TransformationContext,
bindingPattern: ts.BindingPattern,
initializer?: ts.Expression
): lua.Statement[] {
const statements: lua.Statement[] = [];

if (requiresComplexBindingVariableDeclaration(context, bindingPattern, initializer)) {
return transformComplexBindingVariableDeclaration(context, bindingPattern, initializer);
}

const vars =
Expand All @@ -208,6 +252,12 @@ export function transformBindingVariableDeclaration(
? initializer.elements.map(e => context.transformExpression(e))
: lua.createNilLiteral();
statements.push(...createLocalOrExportedOrGlobalDeclaration(context, vars, values, initializer));
} else if (ts.isStringLiteral(initializer)) {
const values =
initializer.text.length > 0
? Array.from(initializer.text).map(c => lua.createStringLiteral(c))
: lua.createNilLiteral();
statements.push(...createLocalOrExportedOrGlobalDeclaration(context, vars, values, initializer));
} else {
// local vars = this.transpileDestructingAssignmentValue(node.initializer);
const unpackedInitializer = createUnpackCall(
Expand Down
41 changes: 41 additions & 0 deletions test/unit/destructuring.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,44 @@ test("no exception from semantically invalid TS", () => {
.disableSemanticCheck()
.expectToHaveDiagnostics([invalidMultiReturnAccess.code, cannotAssignToNodeOfKind.code]);
});

describe("string destructuring", () => {
test("string literal declaration", () => {
util.testFunction`
const [a, b, c] = "test";
return { a, b, c };
`.expectToMatchJsResult();
});

test("string literal assignment", () => {
util.testFunction`
let a = "";
let b = "";
let c = "";
[a, b, c] = "test";
return { a, b, c };
`
.debug()
.expectToMatchJsResult();
});

test("string assignment", () => {
util.testFunction`
const foo = "test";
const [a, b, c] = foo;
return { a, b, c };
`.expectToMatchJsResult();
});

// not wokring right now: send help pls
test("for loop init", () => {
util.testFunction`
const foo = "test";
for (const [a, b, c] of foo) {
return { a, b, c };
}
`
.debug()
.expectToMatchJsResult();
});
});