From fbda8cf4d15fe6f2f251436ca3d23daa4fce2b91 Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 8 Feb 2019 20:02:04 +0200 Subject: [PATCH 01/14] -added generateAnnonymousIdentifier --- src/LuaAST.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/LuaAST.ts b/src/LuaAST.ts index 31ab9975d..0dd154026 100644 --- a/src/LuaAST.ts +++ b/src/LuaAST.ts @@ -803,6 +803,12 @@ export function createIdentifier(text: string | ts.__String, tsOriginal?: ts.Nod return expression; } +export function createAnnonymousIdentifier(tsOriginal?: ts.Node, parent?: Node): Identifier { + const expression = createNode(SyntaxKind.Identifier, tsOriginal, parent) as Identifier; + expression.text = "___"; + return expression; +} + export interface TableIndexExpression extends Expression { kind: SyntaxKind.TableIndexExpression; table: Expression; From 76fcf9eac9943ed23f5c614e9c81bdccd3185851 Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 8 Feb 2019 20:02:24 +0200 Subject: [PATCH 02/14] -added support to transpile generator functions and yield calls --- src/LuaTransformer.ts | 85 +++++++++++++++++++++++++++++++++++++++---- src/TSHelper.ts | 19 ++++++++++ 2 files changed, 97 insertions(+), 7 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 961c4a422..281668d26 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -10,7 +10,6 @@ import {TSTLErrors} from "./TSTLErrors"; export type StatementVisitResult = tstl.Statement | tstl.Statement[] | undefined; export type ExpressionVisitResult = tstl.Expression | undefined; - export enum ScopeType { Function, Switch, @@ -937,9 +936,39 @@ export class LuaTransformer { const [params, dotsLiteral, restParamName] = this.transformParameters(functionDeclaration.parameters, context); const name = this.transformIdentifier(functionDeclaration.name); - const body = tstl.createBlock( - this.transformFunctionBody(functionDeclaration.parameters, functionDeclaration.body, restParamName) - ); + let body: tstl.Block; + if (functionDeclaration.asteriskToken) + { + this.importLuaLibFeature(LuaLibFeature.Symbol); + const functionBody = this.transformFunctionBody( + functionDeclaration.parameters, functionDeclaration.body, restParamName); + functionBody.push(tstl.createReturnStatement([ + tstl.createTableExpression([tstl.createTableFieldExpression( + tstl.createBooleanLiteral(true), tstl.createStringLiteral("done") + )])])); + const wrappedFunction = tstl.createCallExpression( + tstl.createTableIndexExpression(tstl.createIdentifier("coroutine"), + tstl.createStringLiteral("wrap")), + [tstl.createFunctionExpression( + tstl.createBlock(functionBody), params, dotsLiteral, restParamName)]); + const itIdentifier = tstl.createIdentifier("__it"); + const symbolIterator = tstl.createTableIndexExpression( + tstl.createIdentifier("Symbol"),tstl.createStringLiteral("iterator")); + body = tstl.createBlock( + [tstl.createAssignmentStatement(itIdentifier, tstl.createTableExpression([ + tstl.createTableFieldExpression(wrappedFunction, tstl.createStringLiteral("next"))])), + tstl.createAssignmentStatement( + tstl.createTableIndexExpression(itIdentifier, symbolIterator), + tstl.createFunctionExpression(tstl.createBlock([tstl.createReturnStatement([itIdentifier])]))), + tstl.createReturnStatement([itIdentifier])] + ); + } + else + { + body = tstl.createBlock( + this.transformFunctionBody(functionDeclaration.parameters, functionDeclaration.body, restParamName) + ); + } const functionExpression = tstl.createFunctionExpression(body, params, dotsLiteral, restParamName); return this.createLocalOrExportedOrGlobalDeclaration(name, functionExpression, functionDeclaration); @@ -967,8 +996,15 @@ export class LuaTransformer { // Find variable identifier const identifierName = this.transformIdentifier(statement.name); if (statement.initializer) { - const value = this.transformExpression(statement.initializer); - return this.createLocalOrExportedOrGlobalDeclaration(identifierName, value, statement); + if(ts.isYieldExpression(statement.initializer)) + { + const value = this.transformExpression(statement.initializer); + return this.createLocalOrExportedOrGlobalDeclaration( + [tstl.createAnnonymousIdentifier(), identifierName], value, statement); + } else { + const value = this.transformExpression(statement.initializer); + return this.createLocalOrExportedOrGlobalDeclaration(identifierName, value, statement); + } } else { return this.createLocalOrExportedOrGlobalDeclaration( identifierName, @@ -1080,7 +1116,19 @@ export class LuaTransformer { return tstl.createExpressionStatement(this.transformExpression(expression)); } + public transformYield(expression: ts.YieldExpression): tstl.Expression { + const yieldValue = [tstl.createTableFieldExpression( + tstl.createBooleanLiteral(false),tstl.createStringLiteral("done"))]; + if(expression.expression){ + yieldValue.push(tstl.createTableFieldExpression(this.transformExpression(expression.expression), + tstl.createStringLiteral("value"))); + } + + return tstl.createCallExpression( + tstl.createTableIndexExpression(tstl.createIdentifier("coroutine"), tstl.createStringLiteral("yield")), + [tstl.createTableExpression(yieldValue)], expression); + } public transformReturn(statement: ts.ReturnStatement): tstl.Statement { if (statement.expression) { const returnType = tsHelper.getContainingFunctionReturnType(statement, this.checker); @@ -1088,6 +1136,13 @@ export class LuaTransformer { const expressionType = this.checker.getTypeAtLocation(statement.expression); this.validateFunctionAssignment(statement, expressionType, returnType); } + if (tsHelper.isInGeneratorFunction(statement, this.checker)) { + return tstl.createReturnStatement([tstl.createTableExpression([ + tstl.createTableFieldExpression(tstl.createBooleanLiteral(true), + tstl.createStringLiteral("done")), + tstl.createTableFieldExpression(this.transformExpression(statement.expression), + tstl.createStringLiteral("value"))])]); + } if (tsHelper.isInTupleReturnFunction(statement, this.checker)) { // Parent function is a TupleReturn function if (ts.isArrayLiteralExpression(statement.expression)) { @@ -1584,6 +1639,8 @@ export class LuaTransformer { return this.transformSpreadElement(expression as ts.SpreadElement); case ts.SyntaxKind.NonNullExpression: return this.transformExpression((expression as ts.NonNullExpression).expression); + case ts.SyntaxKind.YieldExpression: + return this.transformYield(expression as ts.YieldExpression); case ts.SyntaxKind.EmptyStatement: return undefined; case ts.SyntaxKind.NotEmittedStatement: @@ -1717,7 +1774,21 @@ export class LuaTransformer { throw TSTLErrors.UnsupportedUnionAccessor(lhs); } } - + let originalRhs: ts.Expression | undefined; + if(ts.isBinaryExpression(lhs.parent)) + { + originalRhs = lhs.parent.right; + } + if (originalRhs && tsHelper.isGeneratorYieldCall(originalRhs, this.checker)) { + //a = yield(); can only be called on an iterator, which will generate code like it:resume(). + //we need to ignore the implicit self in the resume call + return tstl.createAssignmentStatement( + [tstl.createAnnonymousIdentifier(), + this.transformExpression(lhs) as tstl.IdentifierOrTableIndexExpression], + right, + lhs.parent + ); + } return tstl.createAssignmentStatement( this.transformExpression(lhs) as tstl.IdentifierOrTableIndexExpression, right, diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 89e57f575..5fa130068 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -178,6 +178,25 @@ export class TSHelper { return false; } } + public static isInGeneratorFunction(node: ts.Node, checker: ts.TypeChecker): boolean { + const declaration = this.findFirstNodeAbove( + node, + (n): n is ts.Node => ts.isFunctionDeclaration(n) || ts.isMethodDeclaration(n) + ); + if (declaration && ts.isFunctionDeclaration(declaration) && declaration.asteriskToken) { + return true; + } else { + return false; + } + } + + public static isGeneratorYieldCall(node: ts.Node, checker: ts.TypeChecker): boolean { + if (ts.isYieldExpression(node) ) { + return true; + } else { + return false; + } + } public static getContainingFunctionReturnType(node: ts.Node, checker: ts.TypeChecker): ts.Type { const declaration = this.findFirstNodeAbove(node, ts.isFunctionLike); From ad7833862ae9dfcbaf941fc44a2a82b7fe17af5d Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 8 Feb 2019 20:02:40 +0200 Subject: [PATCH 03/14] -added tests --- test/unit/functions.spec.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index a0aee62cb..ad2013d75 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -388,4 +388,35 @@ export class FunctionTests { const result = util.transpileAndExecute(code); Expect(result).toBe("foobar"); } + @Test("Generator functions") + public generatorFunction(): void { + const fct = `function* seq() { + let a = yield 1; + return a; + } + const gen = seq(); + `; + { + const code = fct + `return gen.next().done;`; + const result = util.transpileAndExecute(code); + Expect(result).toBe(false); + } + { + const code = fct + `return gen.next().value;`; + const result = util.transpileAndExecute(code); + Expect(result).toBe(1); + } + { + const code = fct + `gen.next(); + return gen.next(42).done;`; + const result = util.transpileAndExecute(code); + Expect(result).toBe(true); + } + { + const code = fct + `gen.next(); + return gen.next(42).value;`; + const result = util.transpileAndExecute(code); + Expect(result).toBe(42); + } + } } From 431caba312d728d94612d7f9a510329943ed615a Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 8 Feb 2019 20:28:50 +0200 Subject: [PATCH 04/14] -fixed hoisting for generator functions --- src/LuaTransformer.ts | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 6744fcd4e..1f9167af0 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -1043,11 +1043,13 @@ export class LuaTransformer { const [params, dotsLiteral, restParamName] = this.transformParameters(functionDeclaration.parameters, context); const name = this.transformIdentifier(functionDeclaration.name); - let body: tstl.Block; + let block: tstl.Block; + let functionScope: Scope; if (functionDeclaration.asteriskToken) { this.importLuaLibFeature(LuaLibFeature.Symbol); - const [functionBody, _scope] = this.transformFunctionBody( + let functionBody: tstl.Statement[]; + [functionBody, functionScope] = this.transformFunctionBody( functionDeclaration.parameters, functionDeclaration.body, restParamName); functionBody.push(tstl.createReturnStatement([ tstl.createTableExpression([tstl.createTableFieldExpression( @@ -1061,7 +1063,7 @@ export class LuaTransformer { const itIdentifier = tstl.createIdentifier("__it"); const symbolIterator = tstl.createTableIndexExpression( tstl.createIdentifier("Symbol"),tstl.createStringLiteral("iterator")); - body = tstl.createBlock( + block = tstl.createBlock( [tstl.createAssignmentStatement(itIdentifier, tstl.createTableExpression([ tstl.createTableFieldExpression(wrappedFunction, tstl.createStringLiteral("next"))])), tstl.createAssignmentStatement( @@ -1072,22 +1074,23 @@ export class LuaTransformer { } else { - const [body, functionScope] = this.transformFunctionBody( + let body: tstl.Statement[]; + [body, functionScope] = this.transformFunctionBody( functionDeclaration.parameters, functionDeclaration.body, restParamName ); - const block = tstl.createBlock(body); - const functionExpression = tstl.createFunctionExpression(block, params, dotsLiteral, restParamName); - // Remember symbols referenced in this function for hoisting later - if (!this.options.noHoisting && name.symbolId !== undefined) { - const scope = this.peekScope(); - if (!scope.functionDefinitions) { scope.functionDefinitions = new Map(); } - const functionInfo = {referencedSymbols: functionScope.referencedSymbols || new Set()}; - scope.functionDefinitions.set(name.symbolId, functionInfo); - } - return this.createLocalOrExportedOrGlobalDeclaration(name, functionExpression, functionDeclaration); + block = tstl.createBlock(body); + } + const functionExpression = tstl.createFunctionExpression(block, params, dotsLiteral, restParamName); + // Remember symbols referenced in this function for hoisting later + if (!this.options.noHoisting && name.symbolId !== undefined) { + const scope = this.peekScope(); + if (!scope.functionDefinitions) { scope.functionDefinitions = new Map(); } + const functionInfo = {referencedSymbols: functionScope.referencedSymbols || new Set()}; + scope.functionDefinitions.set(name.symbolId, functionInfo); } + return this.createLocalOrExportedOrGlobalDeclaration(name, functionExpression, functionDeclaration); } public transformTypeAliasDeclaration(statement: ts.TypeAliasDeclaration): undefined { From 38ea83982fa4a2cf61f1e62f281a99d91ee329f2 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 9 Feb 2019 16:09:07 +0200 Subject: [PATCH 05/14] -cleaned up formatting and moved the return logic in the generator itself, instead of yield/return --- src/LuaTransformer.ts | 213 +++++++++++++++++++++++++++--------------- 1 file changed, 138 insertions(+), 75 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 1f9167af0..7fca6640c 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -1030,6 +1030,131 @@ export class LuaTransformer { }); } + private transformGeneratorFunction( + parameters: ts.NodeArray, + body: ts.Block, + transformedParameters: tstl.Identifier[], + dotsLiteral: tstl.DotsLiteral, + spreadIdentifier?: tstl.Identifier + ): [tstl.Statement[], Scope] + { + this.importLuaLibFeature(LuaLibFeature.Symbol); + const [functionBody, functionScope] = this.transformFunctionBody( + parameters, + body, + spreadIdentifier + ); + + const coroutineIdentifier = tstl.createIdentifier("__co"); + const valueIdentifier = tstl.createIdentifier("__value"); + const errIdentifier = tstl.createIdentifier("__err"); + const itIdentifier = tstl.createIdentifier("__it"); + + //local __co = coroutine.create(originalFunction) + const coroutine = + tstl.createVariableDeclarationStatement(coroutineIdentifier, + tstl.createCallExpression( + tstl.createTableIndexExpression(tstl.createIdentifier("coroutine"), + tstl.createStringLiteral("create") + ), + [tstl.createFunctionExpression( + tstl.createBlock(functionBody), + transformedParameters, + dotsLiteral, + spreadIdentifier), + ] + ) + ); + + const nextBody = []; + // coroutine.resume(__co, ...) + const resumeCall = tstl.createCallExpression( + tstl.createTableIndexExpression( + tstl.createIdentifier("coroutine"), + tstl.createStringLiteral("resume") + ), + [coroutineIdentifier, tstl.createDotsLiteral()] + ); + + // __err, __value = coroutine.resume(__co, ...) + nextBody.push(tstl.createVariableDeclarationStatement( + [errIdentifier, valueIdentifier], + resumeCall) + ); + + //coroutine.status(__co) ~= "dead"; + const coStatus = tstl.createCallExpression( + tstl.createTableIndexExpression( + tstl.createIdentifier("coroutine"), + tstl.createStringLiteral("status") + ), + [coroutineIdentifier] + ); + const status = tstl.createBinaryExpression( + coStatus, + tstl.createStringLiteral("dead"), + tstl.SyntaxKind.EqualityOperator + ); + nextBody.push(status); + + //{done = coroutine.status(__co) ~= "dead"; value = not __err and __value} + const iteratorResult = tstl.createTableExpression([ + tstl.createTableFieldExpression( + status, + tstl.createStringLiteral("done") + ), + tstl.createTableFieldExpression( + tstl.createBinaryExpression( + errIdentifier, + valueIdentifier, + tstl.SyntaxKind.AndOperator), + tstl.createStringLiteral("value") + ), + ]); + nextBody.push(tstl.createReturnStatement([iteratorResult])); + + //function(__, ...) + const nextFunctionDeclaration = tstl.createFunctionExpression( + tstl.createBlock(nextBody), + [tstl.createAnnonymousIdentifier()], + tstl.createDotsLiteral()); + + //__it = {next = function(__, ...)} + const iterator = tstl.createAssignmentStatement( + itIdentifier, + tstl.createTableExpression([ + tstl.createTableFieldExpression( + nextFunctionDeclaration, + tstl.createStringLiteral("next") + ), + ]) + ); + + const symbolIterator = tstl.createTableIndexExpression( + tstl.createIdentifier("Symbol"), + tstl.createStringLiteral("iterator") + ); + + const block = [ + coroutine, + iterator, + //__it[Symbol.iterator] = {return __it} + tstl.createAssignmentStatement( + tstl.createTableIndexExpression( + itIdentifier, + symbolIterator + ), + tstl.createFunctionExpression( + tstl.createBlock( + [tstl.createReturnStatement([itIdentifier])] + ) + ) + ), + //return __it + tstl.createReturnStatement([itIdentifier]), + ]; + return [block, functionScope]; + } public transformFunctionDeclaration(functionDeclaration: ts.FunctionDeclaration): StatementVisitResult { // Don't transform functions without body (overload declarations) if (!functionDeclaration.body) { @@ -1043,45 +1168,20 @@ export class LuaTransformer { const [params, dotsLiteral, restParamName] = this.transformParameters(functionDeclaration.parameters, context); const name = this.transformIdentifier(functionDeclaration.name); - let block: tstl.Block; - let functionScope: Scope; - if (functionDeclaration.asteriskToken) - { - this.importLuaLibFeature(LuaLibFeature.Symbol); - let functionBody: tstl.Statement[]; - [functionBody, functionScope] = this.transformFunctionBody( - functionDeclaration.parameters, functionDeclaration.body, restParamName); - functionBody.push(tstl.createReturnStatement([ - tstl.createTableExpression([tstl.createTableFieldExpression( - tstl.createBooleanLiteral(true), tstl.createStringLiteral("done") - )])])); - const wrappedFunction = tstl.createCallExpression( - tstl.createTableIndexExpression(tstl.createIdentifier("coroutine"), - tstl.createStringLiteral("wrap")), - [tstl.createFunctionExpression( - tstl.createBlock(functionBody), params, dotsLiteral, restParamName)]); - const itIdentifier = tstl.createIdentifier("__it"); - const symbolIterator = tstl.createTableIndexExpression( - tstl.createIdentifier("Symbol"),tstl.createStringLiteral("iterator")); - block = tstl.createBlock( - [tstl.createAssignmentStatement(itIdentifier, tstl.createTableExpression([ - tstl.createTableFieldExpression(wrappedFunction, tstl.createStringLiteral("next"))])), - tstl.createAssignmentStatement( - tstl.createTableIndexExpression(itIdentifier, symbolIterator), - tstl.createFunctionExpression(tstl.createBlock([tstl.createReturnStatement([itIdentifier])]))), - tstl.createReturnStatement([itIdentifier])] - ); - } - else - { - let body: tstl.Statement[]; - [body, functionScope] = this.transformFunctionBody( + const [body, functionScope] = functionDeclaration.asteriskToken + ? this.transformGeneratorFunction( + functionDeclaration.parameters, + functionDeclaration.body, + params, + dotsLiteral, + restParamName + ) + : this.transformFunctionBody( functionDeclaration.parameters, functionDeclaration.body, restParamName ); - block = tstl.createBlock(body); - } + const block = tstl.createBlock(body); const functionExpression = tstl.createFunctionExpression(block, params, dotsLiteral, restParamName); // Remember symbols referenced in this function for hoisting later if (!this.options.noHoisting && name.symbolId !== undefined) { @@ -1115,15 +1215,8 @@ export class LuaTransformer { // Find variable identifier const identifierName = this.transformIdentifier(statement.name); if (statement.initializer) { - if(ts.isYieldExpression(statement.initializer)) - { - const value = this.transformExpression(statement.initializer); - return this.createLocalOrExportedOrGlobalDeclaration( - [tstl.createAnnonymousIdentifier(), identifierName], value, statement); - } else { - const value = this.transformExpression(statement.initializer); - return this.createLocalOrExportedOrGlobalDeclaration(identifierName, value, statement); - } + const value = this.transformExpression(statement.initializer); + return this.createLocalOrExportedOrGlobalDeclaration(identifierName, value, statement); } else { return this.createLocalOrExportedOrGlobalDeclaration( identifierName, @@ -1256,17 +1349,9 @@ export class LuaTransformer { return tstl.createExpressionStatement(this.transformExpression(expression)); } public transformYield(expression: ts.YieldExpression): tstl.Expression { - const yieldValue = [tstl.createTableFieldExpression( - tstl.createBooleanLiteral(false),tstl.createStringLiteral("done"))]; - - if(expression.expression){ - yieldValue.push(tstl.createTableFieldExpression(this.transformExpression(expression.expression), - tstl.createStringLiteral("value"))); - } - return tstl.createCallExpression( tstl.createTableIndexExpression(tstl.createIdentifier("coroutine"), tstl.createStringLiteral("yield")), - [tstl.createTableExpression(yieldValue)], expression); + expression.expression?[this.transformExpression(expression.expression)]:[], expression); } public transformReturn(statement: ts.ReturnStatement): tstl.Statement { if (statement.expression) { @@ -1275,13 +1360,6 @@ export class LuaTransformer { const expressionType = this.checker.getTypeAtLocation(statement.expression); this.validateFunctionAssignment(statement, expressionType, returnType); } - if (tsHelper.isInGeneratorFunction(statement, this.checker)) { - return tstl.createReturnStatement([tstl.createTableExpression([ - tstl.createTableFieldExpression(tstl.createBooleanLiteral(true), - tstl.createStringLiteral("done")), - tstl.createTableFieldExpression(this.transformExpression(statement.expression), - tstl.createStringLiteral("value"))])]); - } if (tsHelper.isInTupleReturnFunction(statement, this.checker)) { // Parent function is a TupleReturn function if (ts.isArrayLiteralExpression(statement.expression)) { @@ -1929,21 +2007,6 @@ export class LuaTransformer { throw TSTLErrors.UnsupportedUnionAccessor(lhs); } } - let originalRhs: ts.Expression | undefined; - if(ts.isBinaryExpression(lhs.parent)) - { - originalRhs = lhs.parent.right; - } - if (originalRhs && tsHelper.isGeneratorYieldCall(originalRhs, this.checker)) { - //a = yield(); can only be called on an iterator, which will generate code like it:resume(). - //we need to ignore the implicit self in the resume call - return tstl.createAssignmentStatement( - [tstl.createAnnonymousIdentifier(), - this.transformExpression(lhs) as tstl.IdentifierOrTableIndexExpression], - right, - lhs.parent - ); - } return tstl.createAssignmentStatement( this.transformExpression(lhs) as tstl.IdentifierOrTableIndexExpression, right, From 63299b2971078baaf22eff9898d35fc58a1b2e29 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 9 Feb 2019 16:48:51 +0200 Subject: [PATCH 06/14] -added error check --- src/LuaTransformer.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 7fca6640c..f45685730 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -1096,7 +1096,22 @@ export class LuaTransformer { tstl.SyntaxKind.EqualityOperator ); nextBody.push(status); - + //if(not __err){error(__value)} + const errorCheck = tstl.createIfStatement( + tstl.createUnaryExpression( + errIdentifier, + tstl.SyntaxKind.NotOperator + ), + tstl.createBlock([ + tstl.createExpressionStatement( + tstl.createCallExpression( + tstl.createIdentifier("error"), + [valueIdentifier] + ) + ), + ]) + ); + nextBody.push(errorCheck); //{done = coroutine.status(__co) ~= "dead"; value = not __err and __value} const iteratorResult = tstl.createTableExpression([ tstl.createTableFieldExpression( From fba776165399b573d27aedb6b81013d428823766 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 9 Feb 2019 16:59:19 +0200 Subject: [PATCH 07/14] -removed superfluous check --- src/LuaTransformer.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index f45685730..1be562ca3 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -1112,18 +1112,15 @@ export class LuaTransformer { ]) ); nextBody.push(errorCheck); - //{done = coroutine.status(__co) ~= "dead"; value = not __err and __value} + //{done = coroutine.status(__co) ~= "dead"; value = __value} const iteratorResult = tstl.createTableExpression([ tstl.createTableFieldExpression( status, tstl.createStringLiteral("done") ), tstl.createTableFieldExpression( - tstl.createBinaryExpression( - errIdentifier, - valueIdentifier, - tstl.SyntaxKind.AndOperator), - tstl.createStringLiteral("value") + valueIdentifier, + tstl.createStringLiteral("value") ), ]); nextBody.push(tstl.createReturnStatement([iteratorResult])); From 872002a6d0dfbacb9573ecc379b5adcabadf02fe Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 9 Feb 2019 18:01:27 +0200 Subject: [PATCH 08/14] -underscodes++ --- src/LuaAST.ts | 2 +- src/LuaTransformer.ts | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/LuaAST.ts b/src/LuaAST.ts index bfbbce965..2639dbd85 100644 --- a/src/LuaAST.ts +++ b/src/LuaAST.ts @@ -815,7 +815,7 @@ export function createIdentifier( export function createAnnonymousIdentifier(tsOriginal?: ts.Node, parent?: Node): Identifier { const expression = createNode(SyntaxKind.Identifier, tsOriginal, parent) as Identifier; - expression.text = "___"; + expression.text = "____"; return expression; } diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 1be562ca3..ec7dddaf2 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -1045,12 +1045,12 @@ export class LuaTransformer { spreadIdentifier ); - const coroutineIdentifier = tstl.createIdentifier("__co"); - const valueIdentifier = tstl.createIdentifier("__value"); - const errIdentifier = tstl.createIdentifier("__err"); - const itIdentifier = tstl.createIdentifier("__it"); + const coroutineIdentifier = tstl.createIdentifier("____co"); + const valueIdentifier = tstl.createIdentifier("____value"); + const errIdentifier = tstl.createIdentifier("____err"); + const itIdentifier = tstl.createIdentifier("____it"); - //local __co = coroutine.create(originalFunction) + //local ____co = coroutine.create(originalFunction) const coroutine = tstl.createVariableDeclarationStatement(coroutineIdentifier, tstl.createCallExpression( @@ -1076,13 +1076,13 @@ export class LuaTransformer { [coroutineIdentifier, tstl.createDotsLiteral()] ); - // __err, __value = coroutine.resume(__co, ...) + // ____err, ____value = coroutine.resume(____co, ...) nextBody.push(tstl.createVariableDeclarationStatement( [errIdentifier, valueIdentifier], resumeCall) ); - //coroutine.status(__co) ~= "dead"; + //coroutine.status(____co) ~= "dead"; const coStatus = tstl.createCallExpression( tstl.createTableIndexExpression( tstl.createIdentifier("coroutine"), @@ -1096,7 +1096,7 @@ export class LuaTransformer { tstl.SyntaxKind.EqualityOperator ); nextBody.push(status); - //if(not __err){error(__value)} + //if(not ____err){error(____value)} const errorCheck = tstl.createIfStatement( tstl.createUnaryExpression( errIdentifier, @@ -1112,7 +1112,7 @@ export class LuaTransformer { ]) ); nextBody.push(errorCheck); - //{done = coroutine.status(__co) ~= "dead"; value = __value} + //{done = coroutine.status(____co) ~= "dead"; value = ____value} const iteratorResult = tstl.createTableExpression([ tstl.createTableFieldExpression( status, @@ -1125,13 +1125,13 @@ export class LuaTransformer { ]); nextBody.push(tstl.createReturnStatement([iteratorResult])); - //function(__, ...) + //function(____, ...) const nextFunctionDeclaration = tstl.createFunctionExpression( tstl.createBlock(nextBody), [tstl.createAnnonymousIdentifier()], tstl.createDotsLiteral()); - //__it = {next = function(__, ...)} + //____it = {next = function(____, ...)} const iterator = tstl.createAssignmentStatement( itIdentifier, tstl.createTableExpression([ @@ -1150,7 +1150,7 @@ export class LuaTransformer { const block = [ coroutine, iterator, - //__it[Symbol.iterator] = {return __it} + //____it[Symbol.iterator] = {return ____it} tstl.createAssignmentStatement( tstl.createTableIndexExpression( itIdentifier, @@ -1162,7 +1162,7 @@ export class LuaTransformer { ) ) ), - //return __it + //return ____it tstl.createReturnStatement([itIdentifier]), ]; return [block, functionScope]; From d17cb35274d0c7fe22469b7638e09eba66791bee Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 9 Feb 2019 18:06:16 +0200 Subject: [PATCH 09/14] -removed unused function --- src/TSHelper.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 1fffe9156..7a712d257 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -178,17 +178,6 @@ export class TSHelper { return false; } } - public static isInGeneratorFunction(node: ts.Node, checker: ts.TypeChecker): boolean { - const declaration = this.findFirstNodeAbove( - node, - (n): n is ts.Node => ts.isFunctionDeclaration(n) || ts.isMethodDeclaration(n) - ); - if (declaration && ts.isFunctionDeclaration(declaration) && declaration.asteriskToken) { - return true; - } else { - return false; - } - } public static isGeneratorYieldCall(node: ts.Node, checker: ts.TypeChecker): boolean { if (ts.isYieldExpression(node) ) { From 5ed80e28e39d97796e950e2c425be11613b3d034 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 9 Feb 2019 18:08:36 +0200 Subject: [PATCH 10/14] -missing local --- src/LuaTransformer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index ec7dddaf2..6327213fc 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -1132,7 +1132,7 @@ export class LuaTransformer { tstl.createDotsLiteral()); //____it = {next = function(____, ...)} - const iterator = tstl.createAssignmentStatement( + const iterator = tstl.createVariableDeclarationStatement( itIdentifier, tstl.createTableExpression([ tstl.createTableFieldExpression( @@ -1167,6 +1167,7 @@ export class LuaTransformer { ]; return [block, functionScope]; } + public transformFunctionDeclaration(functionDeclaration: ts.FunctionDeclaration): StatementVisitResult { // Don't transform functions without body (overload declarations) if (!functionDeclaration.body) { From e26e15b07c9ed023ac3ddaa1b260f71f8ee9ba03 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 9 Feb 2019 18:21:22 +0200 Subject: [PATCH 11/14] -added generator for...of test --- test/unit/functions.spec.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index ad2013d75..1606d5f12 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -419,4 +419,21 @@ export class FunctionTests { Expect(result).toBe(42); } } + @Test("Generator for..of") + public generatorFunctionForOf(): void { + const code = `function* seq() { + yield(1); + yield(2); + yield(3); + return 4; + } + let result = 0; + for(let i of seq()) + { + result = result * 10 + i; + } + return result`; + const result = util.transpileAndExecute(code); + Expect(result).toBe(123); + } } From 261e3b0dcd04194b973d5148f9cda1526a2b8e8a Mon Sep 17 00:00:00 2001 From: = <=> Date: Sun, 10 Feb 2019 15:43:42 +0200 Subject: [PATCH 12/14] -removed unused function --- src/TSHelper.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 7a712d257..f4d847e0e 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -179,14 +179,6 @@ export class TSHelper { } } - public static isGeneratorYieldCall(node: ts.Node, checker: ts.TypeChecker): boolean { - if (ts.isYieldExpression(node) ) { - return true; - } else { - return false; - } - } - public static getContainingFunctionReturnType(node: ts.Node, checker: ts.TypeChecker): ts.Type { const declaration = this.findFirstNodeAbove(node, ts.isFunctionLike); if (declaration) { From b18eee1741ed8bfbd77abc2ead549cb0bb7faea6 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sun, 10 Feb 2019 15:43:52 +0200 Subject: [PATCH 13/14] -cleaned up tests --- test/unit/functions.spec.ts | 58 ++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index 1606d5f12..57a653ebe 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -388,37 +388,47 @@ export class FunctionTests { const result = util.transpileAndExecute(code); Expect(result).toBe("foobar"); } - @Test("Generator functions") - public generatorFunction(): void { - const fct = `function* seq() { - let a = yield 1; - return a; - } - const gen = seq(); - `; - { - const code = fct + `return gen.next().done;`; - const result = util.transpileAndExecute(code); - Expect(result).toBe(false); + + @TestCase(1, 1) + @TestCase(2, 42) + @Test("Generator functions value") + public generatorFunctionValue(iterations: number, expectedResult: number): void { + const code = `function* seq(value: number) { + let a = yield value + 1; + return 42; } + const gen = seq(0); + let ret: number; + for(let i = 0; i < ${iterations}; ++i) { - const code = fct + `return gen.next().value;`; - const result = util.transpileAndExecute(code); - Expect(result).toBe(1); + ret = gen.next(i).value; } - { - const code = fct + `gen.next(); - return gen.next(42).done;`; - const result = util.transpileAndExecute(code); - Expect(result).toBe(true); + return ret; + `; + const result = util.transpileAndExecute(code); + Expect(result).toBe(expectedResult); + } + + @TestCase(1, false) + @TestCase(2, true) + @Test("Generator functions done") + public generatorFunctionDone(iterations: number, expectedResult: boolean): void { + const code = `function* seq(value: number) { + let a = yield value + 1; + return 42; } + const gen = seq(0); + let ret: boolean; + for(let i = 0; i < ${iterations}; ++i) { - const code = fct + `gen.next(); - return gen.next(42).value;`; - const result = util.transpileAndExecute(code); - Expect(result).toBe(42); + ret = gen.next(i).done; } + return ret; + `; + const result = util.transpileAndExecute(code); + Expect(result).toBe(expectedResult); } + @Test("Generator for..of") public generatorFunctionForOf(): void { const code = `function* seq() { From 7813ca8d234db2a1632b656c80351bfaf8c85321 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sun, 10 Feb 2019 16:26:29 +0200 Subject: [PATCH 14/14] -spacing --- src/LuaTransformer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 0193d4d38..5aa73cde1 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -1361,11 +1361,13 @@ export class LuaTransformer { return tstl.createExpressionStatement(this.transformExpression(expression)); } + public transformYield(expression: ts.YieldExpression): tstl.Expression { return tstl.createCallExpression( tstl.createTableIndexExpression(tstl.createIdentifier("coroutine"), tstl.createStringLiteral("yield")), expression.expression?[this.transformExpression(expression.expression)]:[], expression); } + public transformReturn(statement: ts.ReturnStatement): tstl.Statement { if (statement.expression) { const returnType = tsHelper.getContainingFunctionReturnType(statement, this.checker);