Skip to content

Commit c76813f

Browse files
authored
yield* support (#923)
* `yield*` support * Remove `.debug()` call
1 parent 7b6580b commit c76813f

File tree

5 files changed

+100
-10
lines changed

5 files changed

+100
-10
lines changed

src/LuaLib.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export enum LuaLibFeature {
2929
Class = "Class",
3030
ClassExtends = "ClassExtends",
3131
Decorate = "Decorate",
32+
DelegatedYield = "DelegatedYield",
3233
Descriptors = "Descriptors",
3334
Error = "Error",
3435
FunctionBind = "FunctionBind",

src/lualib/DelegatedYield.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
function __TS__DelegatedYield<T>(this: void, iterable: string | GeneratorIterator | Iterable<T> | readonly T[]) {
2+
if (typeof iterable === "string") {
3+
for (const index of forRange(0, iterable.length - 1)) {
4+
coroutine.yield(iterable[index]);
5+
}
6+
} else if ("____coroutine" in iterable) {
7+
const co = iterable.____coroutine;
8+
while (true) {
9+
const [status, value] = coroutine.resume(co);
10+
if (!status) throw value;
11+
if (coroutine.status(co) === "dead") {
12+
return value;
13+
} else {
14+
coroutine.yield(value);
15+
}
16+
}
17+
} else if (iterable[Symbol.iterator]) {
18+
const iterator = iterable[Symbol.iterator]();
19+
while (true) {
20+
const result = iterator.next();
21+
if (result.done) {
22+
return result.value;
23+
} else {
24+
coroutine.yield(result.value);
25+
}
26+
}
27+
} else {
28+
for (const value of iterable as readonly T[]) {
29+
coroutine.yield(value);
30+
}
31+
}
32+
}

src/lualib/Iterator.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ function __TS__IteratorStringStep(this: string, index: number): [number, string]
2626
/** @tupleReturn */
2727
function __TS__Iterator<T>(
2828
this: void,
29-
iterable: Iterable<T> | GeneratorIterator | readonly T[]
29+
iterable: string | GeneratorIterator | Iterable<T> | readonly T[]
3030
): [(...args: any[]) => [any, any] | [], ...any[]] {
31-
if ("____coroutine" in iterable) {
31+
if (typeof iterable === "string") {
32+
return [__TS__IteratorStringStep, iterable, 0];
33+
} else if ("____coroutine" in iterable) {
3234
return [__TS__IteratorGeneratorStep, iterable];
3335
} else if (iterable[Symbol.iterator]) {
3436
const iterator = iterable[Symbol.iterator]();
3537
return [__TS__IteratorIteratorStep, iterator];
36-
} else if (typeof iterable === "string") {
37-
return [__TS__IteratorStringStep, iterable, 0];
3838
} else {
3939
return ipairs(iterable as readonly T[]) as any;
4040
}

src/transformation/visitors/function.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,13 @@ export const transformFunctionDeclaration: FunctionVisitor<ts.FunctionDeclaratio
279279
return createLocalOrExportedOrGlobalDeclaration(context, name, functionExpression, node);
280280
};
281281

282-
export const transformYieldExpression: FunctionVisitor<ts.YieldExpression> = (expression, context) =>
283-
lua.createCallExpression(
284-
lua.createTableIndexExpression(lua.createIdentifier("coroutine"), lua.createStringLiteral("yield")),
285-
expression.expression ? [context.transformExpression(expression.expression)] : [],
286-
expression
287-
);
282+
export const transformYieldExpression: FunctionVisitor<ts.YieldExpression> = (expression, context) => {
283+
const parameters = expression.expression ? [context.transformExpression(expression.expression)] : [];
284+
return expression.asteriskToken
285+
? transformLuaLibFunction(context, LuaLibFeature.DelegatedYield, expression, ...parameters)
286+
: lua.createCallExpression(
287+
lua.createTableIndexExpression(lua.createIdentifier("coroutine"), lua.createStringLiteral("yield")),
288+
parameters,
289+
expression
290+
);
291+
};

test/unit/functions/generators.spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,59 @@ test("for..of", () => {
5151
`.expectToMatchJsResult();
5252
});
5353

54+
describe("yield*", () => {
55+
test("generator", () => {
56+
util.testFunction`
57+
function* subGenerator() {
58+
yield 1;
59+
yield 2;
60+
yield 3;
61+
}
62+
63+
function* generator() {
64+
yield 0;
65+
return yield* subGenerator();
66+
}
67+
68+
const it = generator();
69+
return [it.next(), it.next(), it.next(), it.next(), it.next()];
70+
`.expectToMatchJsResult();
71+
});
72+
73+
test("array", () => {
74+
util.testFunction`
75+
function* generator() {
76+
return yield* [1, 2, 3];
77+
}
78+
79+
const it = generator();
80+
return [it.next(), it.next(), it.next(), it.next()];
81+
`.expectToMatchJsResult();
82+
});
83+
84+
test("string", () => {
85+
util.testFunction`
86+
function* generator() {
87+
return yield* "abc";
88+
}
89+
90+
const it = generator();
91+
return [it.next(), it.next(), it.next(), it.next()];
92+
`.expectToMatchJsResult();
93+
});
94+
95+
test("iterable", () => {
96+
util.testFunction`
97+
function* generator() {
98+
return yield* new Set([1, 2, 3]);
99+
}
100+
101+
const it = generator();
102+
return [it.next(), it.next(), it.next(), it.next()];
103+
`.expectToMatchJsResult();
104+
});
105+
});
106+
54107
test("function expression", () => {
55108
util.testFunction`
56109
const generator = function*() {

0 commit comments

Comments
 (0)