Skip to content

Commit b5ad882

Browse files
authored
Support spreading lua iterables (#1323)
1 parent 96edec4 commit b5ad882

File tree

5 files changed

+63
-0
lines changed

5 files changed

+63
-0
lines changed

src/LuaLib.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export enum LuaLibFeature {
4444
InstanceOf = "InstanceOf",
4545
InstanceOfObject = "InstanceOfObject",
4646
Iterator = "Iterator",
47+
LuaIteratorSpread = "LuaIteratorSpread",
4748
Map = "Map",
4849
MathAtan2 = "MathAtan2",
4950
MathSign = "MathSign",

src/lualib/LuaIteratorSpread.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export function __TS__LuaIteratorSpread<TKey, TValue, TState>(
2+
this: (this: void, state: TState, key: TKey) => LuaMultiReturn<[TKey, TValue]>,
3+
state: TState,
4+
firstKey: TKey
5+
): LuaMultiReturn<Array<[TKey, TValue]>> {
6+
const results = [];
7+
let [key, value] = this(state, firstKey);
8+
while (key) {
9+
results.push([key, value]);
10+
[key, value] = this(state, key);
11+
}
12+
return $multi(...results);
13+
}

src/transformation/utils/language-extensions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ export enum IterableExtensionKind {
9898
PairsKey = "PairsKey",
9999
}
100100

101+
export function isLuaIterable(context: TransformationContext, type: ts.Type): boolean {
102+
return getPropertyValue(context, type, "__tstlIterable") !== undefined;
103+
}
104+
101105
export function getIterableExtensionTypeForType(
102106
context: TransformationContext,
103107
type: ts.Type

src/transformation/visitors/spread.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as ts from "typescript";
22
import * as lua from "../../LuaAST";
33
import { FunctionVisitor, TransformationContext } from "../context";
4+
import { isLuaIterable } from "../utils/language-extensions";
45
import { createUnpackCall } from "../utils/lua-ast";
56
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
67
import {
@@ -73,6 +74,10 @@ export const transformSpreadElement: FunctionVisitor<ts.SpreadElement> = (node,
7374
if (isMultiReturnCall(context, tsInnerExpression)) return innerExpression;
7475

7576
const type = context.checker.getTypeAtLocation(node.expression); // not ts-inner expression, in case of casts
77+
if (isLuaIterable(context, type)) {
78+
return transformLuaLibFunction(context, LuaLibFeature.LuaIteratorSpread, node, innerExpression);
79+
}
80+
7681
if (isArrayType(context, type)) {
7782
return createUnpackCall(context, innerExpression, node);
7883
}

test/unit/spread.spec.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,3 +478,43 @@ describe("vararg spread in IIFE", () => {
478478
`.expectToMatchJsResult();
479479
});
480480
});
481+
482+
// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1244
483+
test.each(["pairs", "ipairs"])("can spread %s (#1244)", func => {
484+
util.testFunction`
485+
const arr = ["a", "b", "c"];
486+
return [...${func}(arr)];
487+
`
488+
.withLanguageExtensions()
489+
.setTsHeader(
490+
`
491+
declare function ipairs<T>(this: void, t: T): LuaIterable<LuaMultiReturn<[number, NonNullable<T[keyof T]>]>>;
492+
declare function pairs<T>(this: void, t: T): LuaIterable<LuaMultiReturn<[keyof T, NonNullable<T[keyof T]>]>>;
493+
`
494+
)
495+
.expectToEqual([
496+
[1, "a"],
497+
[2, "b"],
498+
[3, "c"],
499+
]);
500+
});
501+
502+
// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1244
503+
test.each(["LuaTable", "LuaMap"])("can spread %s (#1244)", type => {
504+
const result: Array<[string, string]> = util.testFunction`
505+
const tbl = new ${type}();
506+
tbl.set("foo", "bar");
507+
tbl.set("fizz", "buzz");
508+
return [...pairs(tbl)];
509+
`
510+
.withLanguageExtensions()
511+
.setTsHeader(
512+
"declare function pairs<T>(this: void, t: T): LuaIterable<LuaMultiReturn<[keyof T, NonNullable<T[keyof T]>]>>;"
513+
)
514+
.getLuaExecutionResult();
515+
516+
// We don't know the order so match like this
517+
expect(result).toHaveLength(2);
518+
expect(result.some(([k, v]) => k === "foo" && v === "bar")).toBe(true);
519+
expect(result.some(([k, v]) => k === "fizz" && v === "buzz")).toBe(true);
520+
});

0 commit comments

Comments
 (0)