From 7c04fa770ac32f785c095d8716609211453fe7a0 Mon Sep 17 00:00:00 2001 From: lolleko Date: Tue, 27 Nov 2018 17:35:56 +0100 Subject: [PATCH 01/14] Addded NS Transformer --- src/TransformHelper.ts | 8 +++----- src/Transformer.ts | 45 +++++++++++++++++++++++++++++++++++++++--- src/Transpiler.ts | 32 ------------------------------ 3 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/TransformHelper.ts b/src/TransformHelper.ts index 34f32dc33..f68b9fd47 100644 --- a/src/TransformHelper.ts +++ b/src/TransformHelper.ts @@ -2,11 +2,9 @@ import * as ts from "typescript"; export class TransformHelper { // Helper to create simple lua variable statement; - public static createLuaVariableStatement( - identifier: ts.Identifier, - expression: ts.Expression, - typeNode?: ts.TypeNode - ): ts.VariableStatement { + public static createLuaVariableStatement(identifier: ts.Identifier, + expression?: ts.Expression, + typeNode?: ts.TypeNode): ts.VariableStatement { const declaration = ts.createVariableDeclaration(identifier, typeNode, expression); const statement = ts.createVariableStatement([], ts.createVariableDeclarationList([declaration])); return statement; diff --git a/src/Transformer.ts b/src/Transformer.ts index 1dda3859b..8cfd7d302 100644 --- a/src/Transformer.ts +++ b/src/Transformer.ts @@ -12,6 +12,9 @@ export class LuaTransformer { private options: CompilerOptions; private context: ts.TransformationContext; private sourceFile: ts.SourceFile; + private isModule: boolean; + + private currentNamespace: ts.ModuleDeclaration; constructor(checker: ts.TypeChecker, options: CompilerOptions) { this.checker = checker; @@ -20,6 +23,7 @@ export class LuaTransformer { public transform(node: ts.SourceFile): ts.SourceFile { this.sourceFile = node; + this.isModule = tsHelper.isFileModule(node); return ts .transform(node, [(ctx: ts.TransformationContext) => { @@ -232,8 +236,44 @@ export class LuaTransformer { if (decorators.has(DecoratorKind.Phantom) && node.body) { return node.body; } - // TODO actual transpilation - return node; + + const result: ts.Node[] = []; + + let declarationNameExpression: ts.Expression; + if (this.currentNamespace) { + const declarationNameExpression = + ts.createPropertyAccess(this.currentNamespace.name, node.name as ts.Identifier); + const declarationAssignment = ts.createAssignment( + declarationNameExpression, ts.createLogicalOr(declarationNameExpression, ts.createObjectLiteral())); + + result.push(declarationAssignment); + // outerNS.innerNS = outerNS.innerNS or {}; + // local innerNS = outerNS.innerNS + } else if (this.isModule && (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export)) { + declarationNameExpression = + ts.createPropertyAccess(ts.createIdentifier("export"), node.name as ts.Identifier); + // exports.NS = exports.NS or {} + // local NS = exports.NS + } else { + declarationNameExpression = node.name; + // NS = NS or {} + // local NS = NS + } + + // Set current namespace for nested NS + // Keep previous currentNS to reset after block transpilation + const previousNamespace = this.currentNamespace; + this.currentNamespace = node; + + // Transform moduleblock to block and transform it + if (ts.isModuleBlock(node.body)) { + const bodyBlock = this.visitBlock(ts.createBlock(node.body.statements)) as ts.Block; + result.push(bodyBlock); + } + + this.currentNamespace = previousNamespace; + + return result; } public visitEnumDeclaration(node: ts.EnumDeclaration): ts.VisitResult { return node; @@ -424,5 +464,4 @@ export class LuaTransformer { private pathToLuaRequirePath(filePath: string): string { return filePath.replace(new RegExp("\\\\|\/", "g"), "."); } - } diff --git a/src/Transpiler.ts b/src/Transpiler.ts index d4f5cf6b4..125d4746b 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -267,8 +267,6 @@ export abstract class LuaTranspiler { switch (node.kind) { case ts.SyntaxKind.ClassDeclaration: return this.transpileClass(node as ts.ClassDeclaration); - case ts.SyntaxKind.ModuleDeclaration: - return this.transpileNamespace(node as ts.ModuleDeclaration); case ts.SyntaxKind.ModuleBlock: return this.transpileBlock(node as ts.Block); case ts.SyntaxKind.EnumDeclaration: @@ -319,36 +317,6 @@ export abstract class LuaTranspiler { return `__TS__${func}(${params.join(", ")})`; } - public transpileNamespace(node: ts.ModuleDeclaration): string { - const decorators = tsHelper.getCustomDecorators(this.checker.getTypeAtLocation(node), this.checker); - // If phantom namespace just transpile the body as normal - if (decorators.has(DecoratorKind.Phantom) && node.body) { - return this.transpileNode(node.body); - } - - const defName = this.definitionName(node.name.text); - // Initialize to pre-existing export if one exists - const prefix = (this.namespace.length === 0 && this.isModule && - (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export)) ? `exports.${node.name.text} or ` : ""; - let result = - this.indent + - this.accessPrefix(node) + - `${node.name.text} = ${prefix}${node.name.text} or {}\n`; - - this.pushExport(node.name.text, node); - // Create closure - result += this.indent + "do\n"; - this.pushIndent(); - this.namespace.push(node.name.text); - if (node.body) { - result += this.transpileNode(node.body); - } - this.namespace.pop(); - this.popIndent(); - result += this.indent + "end\n"; - return result; - } - public transpileEnum(node: ts.EnumDeclaration): string { const type = this.checker.getTypeAtLocation(node); From f90bd1b6bc57f56a1b396b174fd3fd4701595ed7 Mon Sep 17 00:00:00 2001 From: lolleko Date: Tue, 27 Nov 2018 19:08:38 +0100 Subject: [PATCH 02/14] Added block transformation --- src/Transformer.ts | 42 ++++++++++++++++++++++---------- src/Transpiler.ts | 1 + test/runner.ts | 2 +- test/translation/builder.spec.ts | 31 +++++++++++------------ 4 files changed, 47 insertions(+), 29 deletions(-) diff --git a/src/Transformer.ts b/src/Transformer.ts index 8cfd7d302..11f82fb0e 100644 --- a/src/Transformer.ts +++ b/src/Transformer.ts @@ -239,25 +239,41 @@ export class LuaTransformer { const result: ts.Node[] = []; - let declarationNameExpression: ts.Expression; if (this.currentNamespace) { + // outerNS.innerNS = outerNS.innerNS or {}; + // local innerNS = outerNS.innerNS const declarationNameExpression = ts.createPropertyAccess(this.currentNamespace.name, node.name as ts.Identifier); const declarationAssignment = ts.createAssignment( declarationNameExpression, ts.createLogicalOr(declarationNameExpression, ts.createObjectLiteral())); result.push(declarationAssignment); - // outerNS.innerNS = outerNS.innerNS or {}; - // local innerNS = outerNS.innerNS + + const localDeclaration = + transformHelper.createLuaVariableStatement(node.name as ts.Identifier, declarationNameExpression); + + result.push(localDeclaration); } else if (this.isModule && (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export)) { - declarationNameExpression = - ts.createPropertyAccess(ts.createIdentifier("export"), node.name as ts.Identifier); // exports.NS = exports.NS or {} // local NS = exports.NS + const declarationNameExpression = + ts.createPropertyAccess(ts.createIdentifier("exports"), node.name as ts.Identifier); + const declarationAssignment = ts.createAssignment( + declarationNameExpression, ts.createLogicalOr(declarationNameExpression, ts.createObjectLiteral())); + + result.push(declarationAssignment); + + const localDeclaration = + transformHelper.createLuaVariableStatement(node.name as ts.Identifier, declarationNameExpression); + + result.push(localDeclaration); } else { - declarationNameExpression = node.name; - // NS = NS or {} - // local NS = NS + // local NS = NS or {} + const declarationNameExpression = node.name; + const declarationAssignment = ts.createAssignment( + declarationNameExpression, ts.createLogicalOr(declarationNameExpression, ts.createObjectLiteral())); + + result.push(declarationAssignment); } // Set current namespace for nested NS @@ -265,10 +281,10 @@ export class LuaTransformer { const previousNamespace = this.currentNamespace; this.currentNamespace = node; - // Transform moduleblock to block and transform it - if (ts.isModuleBlock(node.body)) { + // Transform moduleblock to block and visit it + if (node.body && ts.isModuleBlock(node.body)) { const bodyBlock = this.visitBlock(ts.createBlock(node.body.statements)) as ts.Block; - result.push(bodyBlock); + // result.push(bodyBlock); } this.currentNamespace = previousNamespace; @@ -430,8 +446,8 @@ export class LuaTransformer { public visitComputedPropertyName(node: ts.ComputedPropertyName): ts.VisitResult { return node; } - public visitBlock(node: ts.Block): ts.VisitResult { - return node; + public visitBlock(node: ts.Block): ts.Block { + return ts.updateBlock(node, node.statements.map(s => this.visitor(s)) as ts.Statement[]); } public visitModuleBlock(node: ts.ModuleBlock): ts.VisitResult { return node; diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 125d4746b..a7d83628b 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -705,6 +705,7 @@ export abstract class LuaTranspiler { this.popIndent(); return ret; default: + console.log(node); throw TSTLErrors.UnsupportedKind("expression", node.kind, node); } } diff --git a/test/runner.ts b/test/runner.ts index 0d5d542fc..4d237f535 100644 --- a/test/runner.ts +++ b/test/runner.ts @@ -23,7 +23,7 @@ fs.copyFileSync( testRunner.outputStream // this will use alsatian's default output if you remove this // you'll get TAP or you can add your favourite TAP reporter in it's place - .pipe(TapBark.create().getPipeable()) + // .pipe(TapBark.create().getPipeable()) // pipe to the console .pipe(process.stdout); diff --git a/test/translation/builder.spec.ts b/test/translation/builder.spec.ts index 70676141c..893ddea93 100644 --- a/test/translation/builder.spec.ts +++ b/test/translation/builder.spec.ts @@ -1,32 +1,32 @@ -import { Expect, Test, TestCases } from "alsatian"; +import { Expect, FocusTest, Test, TestCases } from "alsatian"; import * as util from "../src/util"; import * as fs from "fs"; import * as path from "path"; -let files: string[][] = []; -let fileContents: {[key: string]: Buffer} = {}; +const files: string[][] = []; +const fileContents: {[key: string]: Buffer} = {}; -let tsPath = path.join(__dirname, "./ts/"); -let luaPath = path.join(__dirname, "./lua/") +const tsPath = path.join(__dirname, "./ts/"); +const luaPath = path.join(__dirname, "./lua/"); -let tsFiles = fs.readdirSync(tsPath); -let luaFiles = fs.readdirSync(luaPath); +const tsFiles = fs.readdirSync(tsPath); +const luaFiles = fs.readdirSync(luaPath); tsFiles.forEach( (tsFile, i) => { // ignore non ts files - if (path.extname(tsFile) !== '.ts') { + if (path.extname(tsFile) !== ".ts") { return; } - let luaPart = luaFiles.indexOf(tsFile.replace('.ts', '.lua')); + const luaPart = luaFiles.indexOf(tsFile.replace(".ts", ".lua")); if (luaPart === -1) { - throw new Error("Missing lua counter part for test file: " + tsFile) + throw new Error("Missing lua counter part for test file: " + tsFile); } - let luaFile = luaFiles[luaPart]; - let luaFileAbsolute = path.join(luaPath, luaFile); - let tsFileAbsolute = path.join(tsPath, tsFile); + const luaFile = luaFiles[luaPart]; + const luaFileAbsolute = path.join(luaPath, luaFile); + const tsFileAbsolute = path.join(tsPath, tsFile); files.push([tsFile, luaFile]); fileContents[tsFile] = fs.readFileSync(tsFileAbsolute); fileContents[luaFile] = fs.readFileSync(luaFileAbsolute); @@ -34,14 +34,15 @@ tsFiles.forEach( ); function BufferToTestString(b: Buffer): string { - return b.toString().trim().split("\r\n").join("\n") + return b.toString().trim().split("\r\n").join("\n"); } export class FileTests { @TestCases(files) @Test("Transformation Tests") - public transformationTests(tsFile: string, luaFile:string) { + @FocusTest + public transformationTests(tsFile: string, luaFile: string) { Expect(util.transpileString(BufferToTestString(fileContents[tsFile]))) .toEqual(BufferToTestString(fileContents[luaFile])); } From c62259e332df1e145cabc492f37ce9a77efa208f Mon Sep 17 00:00:00 2001 From: lolleko Date: Tue, 27 Nov 2018 19:29:19 +0100 Subject: [PATCH 03/14] Fixed manual block visit not being flattened --- src/TransformHelper.ts | 5 +++++ src/Transformer.ts | 13 +++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/TransformHelper.ts b/src/TransformHelper.ts index f68b9fd47..e6df838dd 100644 --- a/src/TransformHelper.ts +++ b/src/TransformHelper.ts @@ -16,4 +16,9 @@ export class TransformHelper { ts.createCall(requireIdentifier, [ts.createLiteralTypeNode(moduleSpecifier)], [moduleSpecifier]); return this.createLuaVariableStatement(identifier, requireCall); } + + public static flatten(arr: T[]): T[] { + const flat = [].concat(...arr); + return flat.some(Array.isArray) ? this.flatten(flat) : flat; + } } diff --git a/src/Transformer.ts b/src/Transformer.ts index 11f82fb0e..13d56193e 100644 --- a/src/Transformer.ts +++ b/src/Transformer.ts @@ -283,8 +283,8 @@ export class LuaTransformer { // Transform moduleblock to block and visit it if (node.body && ts.isModuleBlock(node.body)) { - const bodyBlock = this.visitBlock(ts.createBlock(node.body.statements)) as ts.Block; - // result.push(bodyBlock); + const bodyBlock = this.visitModuleBlock(node.body) as ts.Block; + result.push(bodyBlock); } this.currentNamespace = previousNamespace; @@ -446,11 +446,12 @@ export class LuaTransformer { public visitComputedPropertyName(node: ts.ComputedPropertyName): ts.VisitResult { return node; } - public visitBlock(node: ts.Block): ts.Block { - return ts.updateBlock(node, node.statements.map(s => this.visitor(s)) as ts.Statement[]); + public visitBlock(node: ts.Block): ts.VisitResult { + return ts.updateBlock( + node, transformHelper.flatten(node.statements.map(s => this.visitor(s)) as ts.Statement[])); } - public visitModuleBlock(node: ts.ModuleBlock): ts.VisitResult { - return node; + public visitModuleBlock(node: ts.ModuleBlock): ts.VisitResult { + return this.visitBlock(ts.createBlock(node.statements)); } public visitEndOfFileToken(node: ts.EndOfFileToken): ts.VisitResult { return node; From 743912a9c65f078c7ada8a32d37dec16dae04541 Mon Sep 17 00:00:00 2001 From: lolleko Date: Wed, 28 Nov 2018 19:21:47 +0100 Subject: [PATCH 04/14] Flag var statements as static if no module exists --- src/TransformHelper.ts | 5 +++-- src/Transformer.ts | 5 +++++ test/runner.ts | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/TransformHelper.ts b/src/TransformHelper.ts index e6df838dd..959b63fd6 100644 --- a/src/TransformHelper.ts +++ b/src/TransformHelper.ts @@ -4,9 +4,10 @@ export class TransformHelper { // Helper to create simple lua variable statement; public static createLuaVariableStatement(identifier: ts.Identifier, expression?: ts.Expression, - typeNode?: ts.TypeNode): ts.VariableStatement { + typeNode?: ts.TypeNode, + modifiers: ReadonlyArray = []): ts.VariableStatement { const declaration = ts.createVariableDeclaration(identifier, typeNode, expression); - const statement = ts.createVariableStatement([], ts.createVariableDeclarationList([declaration])); + const statement = ts.createVariableStatement(modifiers, ts.createVariableDeclarationList([declaration])); return statement; } diff --git a/src/Transformer.ts b/src/Transformer.ts index 13d56193e..7e5a2ffc2 100644 --- a/src/Transformer.ts +++ b/src/Transformer.ts @@ -304,6 +304,11 @@ export class LuaTransformer { return undefined; } public visitVariableStatement(node: ts.VariableStatement): ts.VisitResult { + // TODO maybe flag as gglobal/local here somehow? + if (!this.isModule && !this.currentNamespace) { + return ts.updateVariableStatement( + node, [ts.createModifier(ts.SyntaxKind.StaticKeyword)], node.declarationList); + } return node; } public visitExpressionStatement(node: ts.ExpressionStatement): ts.VisitResult { diff --git a/test/runner.ts b/test/runner.ts index 4d237f535..0d5d542fc 100644 --- a/test/runner.ts +++ b/test/runner.ts @@ -23,7 +23,7 @@ fs.copyFileSync( testRunner.outputStream // this will use alsatian's default output if you remove this // you'll get TAP or you can add your favourite TAP reporter in it's place - // .pipe(TapBark.create().getPipeable()) + .pipe(TapBark.create().getPipeable()) // pipe to the console .pipe(process.stdout); From cb13fbc0bc2ff65635532079ac0dca01214d3315 Mon Sep 17 00:00:00 2001 From: lolleko Date: Wed, 28 Nov 2018 19:22:46 +0100 Subject: [PATCH 05/14] Revert "Merge branch 'master' into transformer-namespace" This reverts commit 773afae9155ffb91ade2bfec481589f97724ee15, reversing changes made to 743912a9c65f078c7ada8a32d37dec16dae04541. --- README.md | 2 -- package-lock.json | 2 +- package.json | 2 +- src/Transpiler.ts | 37 ++++++++++++++++++------------------- test/unit/class.spec.ts | 22 +--------------------- test/unit/modules.spec.ts | 21 +++++++++------------ 6 files changed, 30 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 903972572..852dae602 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,6 @@ Large projects written in lua can become hard to maintain and make it easy to ma [![Coverage](https://codecov.io/gh/perryvw/typescripttolua/branch/master/graph/badge.svg)](https://codecov.io/gh/perryvw/typescripttolua) [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/TypescriptToLua/Lobby) -You can also find us on Discord: [![Discord](https://img.shields.io/discord/515854149821267971.svg)](https://discord.gg/BWAq58Y) - ## Documentation More detailed documentation and info on writing declarations can be found [on the wiki](https://github.com/Perryvw/TypescriptToLua/wiki). diff --git a/package-lock.json b/package-lock.json index 917dc78de..3f18d09f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "typescript-to-lua", - "version": "0.11.1", + "version": "0.11.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 1f083c6e4..b9c1a6ecc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typescript-to-lua", "license": "MIT", - "version": "0.11.1", + "version": "0.11.0", "repository": "https://github.com/Perryvw/TypescriptToLua", "keywords": [ "typescript", diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 1455dbdf2..a7d83628b 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -213,7 +213,7 @@ export abstract class LuaTranspiler { } // Inline lualib features - if (this.options.luaLibImport === LuaLibImportKind.Inline && this.luaLibFeatureSet.size > 0) { + if (this.options.luaLibImport === LuaLibImportKind.Inline) { result += "\n" + "-- Lua Library Imports\n"; for (const feature of this.luaLibFeatureSet) { const featureFile = path.resolve(__dirname, `../dist/lualib/${feature}.lua`); @@ -1789,30 +1789,29 @@ export abstract class LuaTranspiler { public transpileConstructor(node: ts.ConstructorDeclaration, className: string): string { - // Check for field declarations in constructor - const constructorFieldsDeclarations = node.parameters.filter(p => p.modifiers !== undefined); + const extraInstanceFields = []; - const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters); - - let result = this.indent + `function ${className}.constructor(${["self"].concat(paramNames).join(",")})\n`; + const parameters = ["self"]; + node.parameters.forEach(param => { + // If param has decorators, add extra instance field + if (param.modifiers !== undefined) { + extraInstanceFields.push(this.transpileIdentifier(param.name as ts.Identifier)); + } + // Add to parameter list + parameters.push(this.transpileIdentifier(param.name as ts.Identifier)); + }); - // Transpile constructor body - this.pushIndent(); - this.classStack.push(className); + let result = this.indent + `function ${className}.constructor(${parameters.join(",")})\n`; // Add in instance field declarations - for (const declaration of constructorFieldsDeclarations) { - const declarationName = this.transpileIdentifier(declaration.name as ts.Identifier); - if (declaration.initializer) { - const value = this.transpileExpression(declaration.initializer); - result += this.indent + `self.${declarationName} = ${declarationName} or ${value}\n`; - } else { - result += this.indent + `self.${declarationName} = ${declarationName}\n`; - } + for (const f of extraInstanceFields) { + result += this.indent + ` self.${f} = ${f}\n`; } - result += this.transpileFunctionBody(node.parameters, node.body, spreadIdentifier); - + // Transpile constructor body + this.pushIndent(); + this.classStack.push(className); + result += this.transpileBlock(node.body); this.classStack.pop(); this.popIndent(); diff --git a/test/unit/class.spec.ts b/test/unit/class.spec.ts index de67a5425..d3655034f 100644 --- a/test/unit/class.spec.ts +++ b/test/unit/class.spec.ts @@ -44,7 +44,7 @@ export class ClassTests { @Test("ClassConstructorAssignment") public classConstructorAssignment(): void { - // Transpile + // Transpile const lua = util.transpileString( `class a { constructor(public field: number) {} } return new a(4).field;` @@ -57,26 +57,6 @@ export class ClassTests { Expect(result).toBe(4); } - @Test("ClassConstructorDefaultParameter") - public classConstructorDefaultParameter(): void { - const result = util.transpileAndExecute( - `class a { public field: number; constructor(f: number = 3) { this.field = f; } } - return new a().field;` - ); - - Expect(result).toBe(3); - } - - @Test("ClassConstructorAssignmentDefault") - public classConstructorAssignmentParameterDefault(): void { - const result = util.transpileAndExecute( - `class a { constructor(public field: number = 3) { } } - return new a().field;` - ); - - Expect(result).toBe(3); - } - @Test("ClassNewNoBrackets") public classNewNoBrackets(): void { // Transpile diff --git a/test/unit/modules.spec.ts b/test/unit/modules.spec.ts index 07230485a..0688eeaac 100644 --- a/test/unit/modules.spec.ts +++ b/test/unit/modules.spec.ts @@ -23,6 +23,15 @@ export class LuaModuleTests { Expect(lua.startsWith(`require("lualib_bundle")`)); } + @Test("lualibRequireNoUses") + public lualibRequireNoUses(): void { + // Transpile + const lua = util.transpileString(``, { luaLibImport: LuaLibImportKind.Require, luaTarget: LuaTarget.LuaJIT }); + + // Assert + Expect(lua).toBe(``); + } + @Test("lualibRequireAlways") public lualibRequireAlways(): void { // Transpile @@ -40,16 +49,4 @@ export class LuaModuleTests { Expect(result).toBe(3); } - - @TestCase(LuaLibImportKind.Inline) - @TestCase(LuaLibImportKind.None) - @TestCase(LuaLibImportKind.Require) - @Test("LuaLib no uses? No code") - public lualibNoUsesNoCode(impKind: LuaLibImportKind): void { - // Transpile - const lua = util.transpileString(``, { luaLibImport: impKind }); - - // Assert - Expect(lua).toBe(``); - } } From e4050ca4a977ffd6ed47c3766da7179779f1d921 Mon Sep 17 00:00:00 2001 From: lolleko Date: Thu, 29 Nov 2018 14:03:19 +0100 Subject: [PATCH 06/14] Removed statement static mod --- src/Transformer.ts | 6 +----- test/runner.ts | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Transformer.ts b/src/Transformer.ts index 7e5a2ffc2..6cc7ec244 100644 --- a/src/Transformer.ts +++ b/src/Transformer.ts @@ -24,6 +24,7 @@ export class LuaTransformer { public transform(node: ts.SourceFile): ts.SourceFile { this.sourceFile = node; this.isModule = tsHelper.isFileModule(node); + console.log(this.checker.getTypeAtLocation(this.sourceFile).getSymbol().exports); return ts .transform(node, [(ctx: ts.TransformationContext) => { @@ -304,11 +305,6 @@ export class LuaTransformer { return undefined; } public visitVariableStatement(node: ts.VariableStatement): ts.VisitResult { - // TODO maybe flag as gglobal/local here somehow? - if (!this.isModule && !this.currentNamespace) { - return ts.updateVariableStatement( - node, [ts.createModifier(ts.SyntaxKind.StaticKeyword)], node.declarationList); - } return node; } public visitExpressionStatement(node: ts.ExpressionStatement): ts.VisitResult { diff --git a/test/runner.ts b/test/runner.ts index 0d5d542fc..4d237f535 100644 --- a/test/runner.ts +++ b/test/runner.ts @@ -23,7 +23,7 @@ fs.copyFileSync( testRunner.outputStream // this will use alsatian's default output if you remove this // you'll get TAP or you can add your favourite TAP reporter in it's place - .pipe(TapBark.create().getPipeable()) + // .pipe(TapBark.create().getPipeable()) // pipe to the console .pipe(process.stdout); From 100ead4aacd015f88ecf60ddf9025b8f0dce79c3 Mon Sep 17 00:00:00 2001 From: Lolleko Date: Sat, 1 Dec 2018 16:42:53 +0100 Subject: [PATCH 07/14] Added a lot of transformers for expressions and statements --- src/TSHelper.ts | 10 + src/Transformer.ts | 370 ++++++++++++++++++++++++---------- src/Transpiler.ts | 41 ---- src/targets/Transpiler.52.ts | 5 - src/targets/Transpiler.JIT.ts | 5 - 5 files changed, 273 insertions(+), 158 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index d35973f32..e0d792232 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -56,6 +56,16 @@ export class TSHelper { return false; } + public static isIdentifierExported( + identifier: ts.Identifier, scope: ts.ModuleDeclaration | ts.SourceFile, checker: ts.TypeChecker): boolean { + const identifierSymbol = checker.getTypeAtLocation(scope).getSymbol(); + if (identifierSymbol.exports) { + return identifierSymbol.exports.has(identifier.escapedText); + } + + return false; + } + public static isInDestructingAssignment(node: ts.Node): boolean { return node.parent && ((ts.isVariableDeclaration(node.parent) && ts.isArrayBindingPattern(node.parent.name)) || (ts.isBinaryExpression(node.parent) && ts.isArrayLiteralExpression(node.parent.left))); diff --git a/src/Transformer.ts b/src/Transformer.ts index 6cc7ec244..0ef7d4ff0 100644 --- a/src/Transformer.ts +++ b/src/Transformer.ts @@ -5,6 +5,7 @@ import {CompilerOptions} from "./CompilerOptions"; import {DecoratorKind} from "./Decorator"; import {TSTLErrors} from "./Errors"; import {TransformHelper as transformHelper} from "./TransformHelper"; +import {LuaTarget} from "./Transpiler"; import {TSHelper as tsHelper} from "./TSHelper"; export class LuaTransformer { @@ -24,7 +25,6 @@ export class LuaTransformer { public transform(node: ts.SourceFile): ts.SourceFile { this.sourceFile = node; this.isModule = tsHelper.isFileModule(node); - console.log(this.checker.getTypeAtLocation(this.sourceFile).getSymbol().exports); return ts .transform(node, [(ctx: ts.TransformationContext) => { @@ -68,6 +68,86 @@ export class LuaTransformer { case ts.SyntaxKind.InterfaceDeclaration: return this.visitInterfaceDeclaration(node as ts.InterfaceDeclaration); // Statements + case ts.SyntaxKind.VariableStatement: + case ts.SyntaxKind.ExpressionStatement: + case ts.SyntaxKind.ReturnStatement: + case ts.SyntaxKind.IfStatement: + case ts.SyntaxKind.WhileStatement: + case ts.SyntaxKind.DoStatement: + case ts.SyntaxKind.ForStatement: + case ts.SyntaxKind.ForOfStatement: + case ts.SyntaxKind.ForInStatement: + case ts.SyntaxKind.SwitchStatement: + case ts.SyntaxKind.BreakStatement: + case ts.SyntaxKind.TryStatement: + case ts.SyntaxKind.ThrowStatement: + case ts.SyntaxKind.ContinueStatement: + case ts.SyntaxKind.EmptyStatement: + return this.visitStatement(node as ts.Statement); + // Expressions + case ts.SyntaxKind.BinaryExpression: + case ts.SyntaxKind.ConditionalExpression: + case ts.SyntaxKind.CallExpression: + case ts.SyntaxKind.PropertyAccessExpression: + case ts.SyntaxKind.ElementAccessExpression: + case ts.SyntaxKind.ParenthesizedExpression: + case ts.SyntaxKind.TypeAssertionExpression: + case ts.SyntaxKind.AsExpression: + case ts.SyntaxKind.TypeOfExpression: + case ts.SyntaxKind.SpreadElement: + case ts.SyntaxKind.NonNullExpression: + case ts.SyntaxKind.ClassExpression: + case ts.SyntaxKind.TemplateExpression: + case ts.SyntaxKind.PostfixUnaryExpression: + case ts.SyntaxKind.PrefixUnaryExpression: + case ts.SyntaxKind.ArrayLiteralExpression: + case ts.SyntaxKind.ObjectLiteralExpression: + case ts.SyntaxKind.DeleteExpression: + case ts.SyntaxKind.FunctionExpression: + case ts.SyntaxKind.ArrowFunction: + case ts.SyntaxKind.NewExpression: + case ts.SyntaxKind.Identifier: + return this.visitExpression(node as ts.Expression); + // Literal + case ts.SyntaxKind.StringLiteral: + return this.visitStringLiteral(node as ts.StringLiteral); + case ts.SyntaxKind.NoSubstitutionTemplateLiteral: + return this.visitNoSubstitutionTemplateLiteral(node as ts.NoSubstitutionTemplateLiteral); + case ts.SyntaxKind.NumericLiteral: + return this.visitNumericLiteral(node as ts.NumericLiteral); + // Keywords + case ts.SyntaxKind.TrueKeyword: + return this.visitTrueKeyword(node as ts.BooleanLiteral); + case ts.SyntaxKind.FalseKeyword: + return this.visitFalseKeyword(node as ts.BooleanLiteral); + case ts.SyntaxKind.NullKeyword: + return this.visitNullKeyword(node as ts.KeywordTypeNode); + case ts.SyntaxKind.UndefinedKeyword: + return this.visitUndefinedKeyword(node as ts.KeywordTypeNode); + case ts.SyntaxKind.ThisKeyword: + return this.visitThisKeyword(node as ts.KeywordTypeNode); + case ts.SyntaxKind.SuperKeyword: + return this.visitSuperKeyword(node as ts.KeywordTypeNode); + // ComputedPropertyName + case ts.SyntaxKind.ComputedPropertyName: + return this.visitComputedPropertyName(node as ts.ComputedPropertyName); + // Blocks + case ts.SyntaxKind.Block: + return this.visitBlock(node as ts.Block); + case ts.SyntaxKind.ModuleBlock: + return this.visitModuleBlock(node as ts.ModuleBlock); + // EOF TOKEN + case ts.SyntaxKind.EndOfFileToken: + return this.visitEndOfFileToken(node as ts.EndOfFileToken); + default: + throw TSTLErrors.UnsupportedKind("Node", node.kind, node); + } + } + public visitStatement(node: ts.Statement): ts.Statement { + if (ts.isBlock(node)) { + return this.visitBlock(node); + } + switch (node.kind) { case ts.SyntaxKind.VariableStatement: return this.visitVariableStatement(node as ts.VariableStatement); case ts.SyntaxKind.ExpressionStatement: @@ -98,7 +178,12 @@ export class LuaTransformer { return this.visitContinueStatement(node as ts.ContinueStatement); case ts.SyntaxKind.EmptyStatement: return this.visitEmptyStatement(node as ts.EmptyStatement); - // Expressions + default: + throw TSTLErrors.UnsupportedKind("Statement", node.kind, node); + } + } + public visitExpression(node: ts.Expression): ts.Expression { + switch (node.kind) { case ts.SyntaxKind.BinaryExpression: return this.visitBinaryExpression(node as ts.BinaryExpression); case ts.SyntaxKind.ConditionalExpression: @@ -137,46 +222,14 @@ export class LuaTransformer { return this.visitDeleteExpression(node as ts.DeleteExpression); case ts.SyntaxKind.FunctionExpression: return this.visitFunctionExpression(node as ts.FunctionExpression); - case ts.SyntaxKind.NewExpression: - return this.visitNewExpression(node as ts.NewExpression); case ts.SyntaxKind.ArrowFunction: return this.visitArrowFunction(node as ts.ArrowFunction); - // Identifier + case ts.SyntaxKind.NewExpression: + return this.visitNewExpression(node as ts.NewExpression); case ts.SyntaxKind.Identifier: return this.visitIdentifier(node as ts.Identifier); - // Literal - case ts.SyntaxKind.StringLiteral: - return this.visitStringLiteral(node as ts.StringLiteral); - case ts.SyntaxKind.NoSubstitutionTemplateLiteral: - return this.visitNoSubstitutionTemplateLiteral(node as ts.NoSubstitutionTemplateLiteral); - case ts.SyntaxKind.NumericLiteral: - return this.visitNumericLiteral(node as ts.NumericLiteral); - // Keywords - case ts.SyntaxKind.TrueKeyword: - return this.visitTrueKeyword(node as ts.BooleanLiteral); - case ts.SyntaxKind.FalseKeyword: - return this.visitFalseKeyword(node as ts.BooleanLiteral); - case ts.SyntaxKind.NullKeyword: - return this.visitNullKeyword(node as ts.KeywordTypeNode); - case ts.SyntaxKind.UndefinedKeyword: - return this.visitUndefinedKeyword(node as ts.KeywordTypeNode); - case ts.SyntaxKind.ThisKeyword: - return this.visitThisKeyword(node as ts.KeywordTypeNode); - case ts.SyntaxKind.SuperKeyword: - return this.visitSuperKeyword(node as ts.KeywordTypeNode); - // ComputedPropertyName - case ts.SyntaxKind.ComputedPropertyName: - return this.visitComputedPropertyName(node as ts.ComputedPropertyName); - // Blocks - case ts.SyntaxKind.Block: - return this.visitBlock(node as ts.Block); - case ts.SyntaxKind.ModuleBlock: - return this.visitModuleBlock(node as ts.ModuleBlock); - // EOF TOKEN - case ts.SyntaxKind.EndOfFileToken: - return this.visitEndOfFileToken(node as ts.EndOfFileToken); default: - throw TSTLErrors.UnsupportedKind("Node", node.kind, node); + throw TSTLErrors.UnsupportedKind("Expression", node.kind, node); } } public visitImportDeclaration(node: ts.ImportDeclaration): ts.VisitResult { @@ -304,116 +357,219 @@ export class LuaTransformer { public visitInterfaceDeclaration(node: ts.InterfaceDeclaration): ts.VisitResult { return undefined; } - public visitVariableStatement(node: ts.VariableStatement): ts.VisitResult { - return node; + public visitVariableStatement(node: ts.VariableStatement): ts.VariableStatement { + return ts.updateVariableStatement( + node, node.modifiers, this.visitVariableDeclarationList(node.declarationList)); } - public visitExpressionStatement(node: ts.ExpressionStatement): ts.VisitResult { - return node; + public visitVariableDeclarationList(node: ts.VariableDeclarationList): ts.VariableDeclarationList { + return ts.updateVariableDeclarationList(node, + node.declarations.map(decl => this.visitVariableDeclaration(decl))); } - public visitReturn(node: ts.ReturnStatement): ts.VisitResult { + public visitVariableDeclaration(node: ts.VariableDeclaration): ts.VariableDeclaration { + // TODO return node; } - public visitIfStatement(node: ts.IfStatement): ts.VisitResult { + public visitExpressionStatement(node: ts.ExpressionStatement): ts.ExpressionStatement { return node; } - public visitWhileStatement(node: ts.WhileStatement): ts.VisitResult { - return node; + public visitReturn(node: ts.ReturnStatement): ts.ReturnStatement { + return ts.updateReturn(node, this.visitExpression(node.expression)); } - public visitDoStatement(node: ts.DoStatement): ts.VisitResult { - return node; + public visitIfStatement(node: ts.IfStatement): ts.IfStatement { + return ts.updateIf(node, + this.visitExpression(node.expression), + this.visitStatement(node.thenStatement), + this.visitStatement(node.elseStatement)); } - public visitForStatement(node: ts.ForStatement): ts.VisitResult { - return node; + public visitWhileStatement(node: ts.WhileStatement): ts.WhileStatement { + return ts.updateWhile(node, this.visitExpression(node.expression), this.visitStatement(node.statement)); } - public visitForOfStatement(node: ts.ForOfStatement): ts.VisitResult { - return node; + public visitDoStatement(node: ts.DoStatement): ts.DoStatement { + return ts.updateDo(node, this.visitStatement(node.statement), this.visitExpression(node.expression)); } - public visitForInStatement(node: ts.ForInStatement): ts.VisitResult { - return node; + public visitForStatement(node: ts.ForStatement): ts.ForStatement { + return ts.updateFor(node, + this.visitForInitializer(node.initializer), + this.visitExpression(node.condition), + this.visitExpression(node.incrementor), + this.visitStatement(node.statement)); } - public visitSwitchStatement(node: ts.SwitchStatement): ts.VisitResult { - return node; + public visitForOfStatement(node: ts.ForOfStatement): ts.ForOfStatement { + return ts.updateForOf(node, + node.awaitModifier, + this.visitForInitializer(node.initializer), + this.visitExpression(node.expression), + this.visitStatement(node.statement)); } - public visitBreakStatement(node: ts.BreakStatement): ts.VisitResult { - return node; - } - public visitTryStatement(node: ts.TryStatement): ts.VisitResult { - return node; - } - public visitThrowStatement(node: ts.ThrowStatement): ts.VisitResult { - return node; + public visitForInStatement(node: ts.ForInStatement): ts.ForInStatement { + return ts.updateForIn(node, + this.visitForInitializer(node.initializer), + this.visitExpression(node.expression), + this.visitStatement(node.statement)); } - public visitContinueStatement(node: ts.ContinueStatement): ts.VisitResult { - return node; - } - public visitEmptyStatement(node: ts.EmptyStatement): ts.VisitResult { - return undefined; + public visitForInitializer(node: ts.ForInitializer): ts.ForInitializer { + let updatedInitializer: ts.ForInitializer; + if (ts.isVariableDeclarationList(node)) { + updatedInitializer = this.visitVariableDeclarationList(node); + } else { + updatedInitializer = this.visitExpression(node); + } + return updatedInitializer; } - public visitBinaryExpression(node: ts.BinaryExpression): ts.VisitResult { + public visitSwitchStatement(node: ts.SwitchStatement): ts.SwitchStatement { + // TODO return node; } - public visitConditionalExpression(node: ts.ConditionalExpression): ts.VisitResult { + public visitBreakStatement(node: ts.BreakStatement): ts.BreakStatement { + // TODO return node; } - public visitCallExpression(node: ts.CallExpression): ts.VisitResult { + public visitTryStatement(node: ts.TryStatement): ts.TryStatement { + // TODO return node; } - public visitPropertyAccessExpression(node: ts.PropertyAccessExpression): - ts.VisitResult { + public visitThrowStatement(node: ts.ThrowStatement): ts.ThrowStatement { + // TODO return node; } - public visitElementAccessExpression(node: ts.ElementAccessExpression): ts.VisitResult { + public visitContinueStatement(node: ts.ContinueStatement): ts.ContinueStatement { + // TODO return node; } - public visitParenthesizedExpression(node: ts.ParenthesizedExpression): ts.VisitResult { - throw node; - } - public visitTypeAssertionExpression(node: ts.TypeAssertion): ts.VisitResult { - return node.expression; - } - public visitAsExpression(node: ts.AsExpression): ts.VisitResult { - return node.expression; - } - public visitTypeOfExpression(node: ts.TypeOfExpression): ts.VisitResult { - return node; + public visitEmptyStatement(node: ts.EmptyStatement): ts.EmptyStatement { + return undefined; } - public visitSpreadElement(node: ts.SpreadElement): ts.VisitResult { - return node; + public visitBinaryExpression(node: ts.BinaryExpression): ts.BinaryExpression { + let operatorToken = node.operatorToken; + if (node.operatorToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) { + operatorToken = ts.createToken(ts.SyntaxKind.EqualsEqualsToken); + } + return ts.updateBinary(node, this.visitExpression(node.left), this.visitExpression(node.right), operatorToken); + } + public visitConditionalExpression(node: ts.ConditionalExpression): ts.ConditionalExpression { + return ts.updateConditional(node, + this.visitExpression(node.condition), + this.visitExpression(node.whenTrue), + this.visitExpression(node.whenFalse)); + } + public visitCallExpression(node: ts.CallExpression): ts.CallExpression { + return ts.updateCall(node, + this.visitExpression(node.expression), + node.typeArguments, + node.arguments.map(arg => this.visitExpression(arg))); + } + public visitPropertyAccessExpression(node: ts.PropertyAccessExpression): ts.PropertyAccessExpression { + return ts.updatePropertyAccess(node, this.visitExpression(node.expression), node.name); + } + public visitElementAccessExpression(node: ts.ElementAccessExpression): ts.ElementAccessExpression { + return ts.updateElementAccess( + node, this.visitExpression(node.expression), this.visitExpression(node.argumentExpression)); + } + public visitParenthesizedExpression(node: ts.ParenthesizedExpression): ts.ParenthesizedExpression { + return ts.updateParen(node, this.visitExpression(node.expression)); + } + public visitTypeAssertionExpression(node: ts.TypeAssertion): ts.Expression { + return this.visitExpression(node.expression); + } + public visitAsExpression(node: ts.AsExpression): ts.Expression { + return this.visitExpression(node.expression); + } + public visitTypeOfExpression(node: ts.TypeOfExpression): ts.BinaryExpression { + // ((type(${expression}) == "table" and "object") or type(${expression})) + const expression = this.visitExpression(node.expression); + const typeCall = ts.createCall( + ts.createIdentifier("type"), [ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)], [expression]); + const comapareExpression = + ts.createBinary(typeCall, ts.SyntaxKind.EqualsEqualsToken, ts.createLiteral("table")); + const andExpression = ts.createLogicalAnd(comapareExpression, ts.createLiteral("object")); + const orExpression = ts.createLogicalOr(andExpression, typeCall); + return orExpression; + } + public visitSpreadElement(node: ts.SpreadElement): ts.CallExpression { + // TODO move this to differen targets + // table.unpack(expression) / unpack(expression) + let functionExpresion: ts.Expression; + switch (this.options.luaTarget) { + case LuaTarget.Lua51: + functionExpresion = ts.createIdentifier("unpack"); + break; + case LuaTarget.Lua52: + case LuaTarget.Lua53: + functionExpresion = + ts.createPropertyAccess(ts.createIdentifier("table"), ts.createIdentifier("unpack")); + case LuaTarget.LuaJIT: + functionExpresion = ts.createIdentifier("unpack"); + break; + } + return ts.createCall( + functionExpresion, [ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)], [node.expression]); } - public visitNonNullExpression(node: ts.NonNullExpression): ts.VisitResult { - return node.expression; + public visitNonNullExpression(node: ts.NonNullExpression): ts.Expression { + return this.visitExpression(node.expression); } - public visitClassExpression(node: ts.ClassExpression): ts.VisitResult { + public visitClassExpression(node: ts.ClassExpression): ts.ClassExpression { + // TODO return node; } - public visitTemplateExpression(node: ts.TemplateExpression): ts.VisitResult { - return node; + public visitTemplateExpression(node: ts.TemplateExpression): ts.Expression { + let concatExpression: ts.Expression = ts.createLiteral(node.head.text); + node.templateSpans.forEach(span => { + const expr = ts.createCall(ts.createIdentifier("tostring"), + [ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)], + [this.visitExpression(span.expression)]); + const text = ts.createLiteral(span.literal.text); + + concatExpression = ts.createAdd(concatExpression, ts.createAdd(expr, ts.createLiteral(text))); + }); + return concatExpression; } - public visitPostfixUnaryExpression(node: ts.PostfixUnaryExpression): ts.VisitResult { + public visitPostfixUnaryExpression(node: ts.PostfixUnaryExpression): ts.PostfixUnaryExpression { + // TODO return node; } - public visitPrefixUnaryExpression(node: ts.PrefixUnaryExpression): ts.VisitResult { + public visitPrefixUnaryExpression(node: ts.PrefixUnaryExpression): ts.PrefixUnaryExpression { + // TODO return node; } - public visitArrayLiteralExpression(node: ts.ArrayLiteralExpression): ts.VisitResult { + public visitArrayLiteralExpression(node: ts.ArrayLiteralExpression): ts.ArrayLiteralExpression { return node; } - public visitObjectLiteralExpression(node: ts.ObjectLiteralExpression): ts.VisitResult { + public visitObjectLiteralExpression(node: ts.ObjectLiteralExpression): ts.ObjectLiteralExpression { return node; } - public visitDeleteExpression(node: ts.DeleteExpression): ts.VisitResult { - return node; + public visitDeleteExpression(node: ts.DeleteExpression): ts.Expression { + return ts.createAssignment(this.visitExpression(node.expression), ts.createNull()); } - public visitFunctionExpression(node: ts.FunctionExpression): ts.VisitResult { - return node; + public visitFunctionExpression(node: ts.FunctionExpression): ts.FunctionExpression { + return ts.updateFunctionExpression(node, + node.modifiers, + node.asteriskToken, + node.name, + node.typeParameters, + node.parameters, + node.type, + this.visitBlock(node.body)); } - public visitNewExpression(node: ts.NewExpression): ts.VisitResult { - return node; + public visitArrowFunction(node: ts.ArrowFunction): ts.ArrowFunction { + let newBody: ts.ConciseBody; + if (ts.isBlock(node.body)) { + newBody = this.visitBlock(node.body); + } else { + newBody = this.visitExpression(node.body); + } + return ts.updateArrowFunction(node, node.modifiers, node.typeParameters, node.parameters, node.type, newBody); } - public visitArrowFunction(node: ts.ArrowFunction): ts.VisitResult { + public visitNewExpression(node: ts.NewExpression): ts.NewExpression { return node; } - public visitIdentifier(node: ts.Identifier): ts.VisitResult { + public visitIdentifier(node: ts.Identifier): ts.Identifier| ts.PropertyAccessExpression { + // If we are in a namespace or a sourcefile that is a module check if this identifier is exported + if (this.currentNamespace && tsHelper.isIdentifierExported(node, this.currentNamespace, this.checker)) { + return ts.createPropertyAccess(this.currentNamespace.name, node); + } else if (this.isModule && tsHelper.isIdentifierExported(node, this.sourceFile, this.checker)) { + return ts.createPropertyAccess(ts.createIdentifier("exports"), node); + } + return node; } public visitStringLiteral(node: ts.StringLiteral): ts.VisitResult { @@ -447,9 +603,9 @@ export class LuaTransformer { public visitComputedPropertyName(node: ts.ComputedPropertyName): ts.VisitResult { return node; } - public visitBlock(node: ts.Block): ts.VisitResult { - return ts.updateBlock( - node, transformHelper.flatten(node.statements.map(s => this.visitor(s)) as ts.Statement[])); + public visitBlock(node: ts.Block): ts.Block { + return ts.updateBlock(node, + transformHelper.flatten(node.statements.map(s => this.visitor(s)) as ts.Statement[])); } public visitModuleBlock(node: ts.ModuleBlock): ts.VisitResult { return this.visitBlock(ts.createBlock(node.statements)); diff --git a/src/Transpiler.ts b/src/Transpiler.ts index a7d83628b..84ccbad6c 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -646,8 +646,6 @@ export abstract class LuaTranspiler { case ts.SyntaxKind.NoSubstitutionTemplateLiteral: const text = this.escapeString((node as ts.StringLiteral).text); return `"${text}"`; - case ts.SyntaxKind.TemplateExpression: - return this.transpileTemplateExpression(node as ts.TemplateExpression); case ts.SyntaxKind.NumericLiteral: return (node as ts.NumericLiteral).text; case ts.SyntaxKind.TrueKeyword: @@ -667,8 +665,6 @@ export abstract class LuaTranspiler { return this.transpileArrayLiteral(node as ts.ArrayLiteralExpression); case ts.SyntaxKind.ObjectLiteralExpression: return this.transpileObjectLiteral(node as ts.ObjectLiteralExpression); - case ts.SyntaxKind.DeleteExpression: - return this.transpileExpression((node as ts.DeleteExpression).expression) + "=nil"; case ts.SyntaxKind.FunctionExpression: case ts.SyntaxKind.ArrowFunction: return this.transpileFunctionExpression(node as ts.ArrowFunction); @@ -680,20 +676,8 @@ export abstract class LuaTranspiler { return "(" + this.transpileExpression((node as ts.ParenthesizedExpression).expression) + ")"; case ts.SyntaxKind.SuperKeyword: return "self.__base"; - case ts.SyntaxKind.TypeAssertionExpression: - // Simply ignore the type assertion - return this.transpileExpression((node as ts.TypeAssertion).expression); - case ts.SyntaxKind.AsExpression: - // Also ignore as casts - return this.transpileExpression((node as ts.AsExpression).expression); - case ts.SyntaxKind.TypeOfExpression: - return this.transpileTypeOfExpression(node as ts.TypeOfExpression); case ts.SyntaxKind.EmptyStatement: return ""; - case ts.SyntaxKind.SpreadElement: - return this.transpileSpreadElement(node as ts.SpreadElement); - case ts.SyntaxKind.NonNullExpression: - return this.transpileExpression((node as ts.NonNullExpression).expression); case ts.SyntaxKind.ClassExpression: this.namespace.push(""); const classDeclaration = this.transpileClass(node as ts.ClassExpression, "_"); @@ -705,7 +689,6 @@ export abstract class LuaTranspiler { this.popIndent(); return ret; default: - console.log(node); throw TSTLErrors.UnsupportedKind("expression", node.kind, node); } } @@ -862,21 +845,6 @@ export abstract class LuaTranspiler { throw TSTLErrors.UnsupportedForTarget("Bitwise operations", this.options.luaTarget, node); } - public transpileTemplateExpression(node: ts.TemplateExpression): string { - const parts = [`"${this.escapeString(node.head.text)}"`]; - node.templateSpans.forEach(span => { - const expr = this.transpileExpression(span.expression, true); - const text = this.escapeString(span.literal.text); - - if (ts.isTemplateTail(span.literal)) { - parts.push(`tostring(${expr}).."${text}"`); - } else { - parts.push(`tostring(${expr}).."${text}"`); - } - }); - return parts.join(".."); - } - public transpileConditionalExpression(node: ts.ConditionalExpression, brackets?: boolean): string { const condition = this.transpileExpression(node.condition); const val1 = this.transpileExpression(node.whenTrue); @@ -1428,10 +1396,6 @@ export abstract class LuaTranspiler { return escapedText; } - public transpileSpreadElement(node: ts.SpreadElement): string { - return "unpack(" + this.transpileExpression(node.expression) + ")"; - } - public transpileArrayBindingElement(name: ts.ArrayBindingElement): string { if (ts.isOmittedExpression(name)) { return "__"; @@ -1444,11 +1408,6 @@ export abstract class LuaTranspiler { } } - public transpileTypeOfExpression(node: ts.TypeOfExpression): string { - const expression = this.transpileExpression(node.expression); - return `(type(${expression}) == "table" and "object" or type(${expression}))`; - } - // Transpile a variable statement public transpileVariableStatement(node: ts.VariableStatement): string { let result = ""; diff --git a/src/targets/Transpiler.52.ts b/src/targets/Transpiler.52.ts index 0fcba461f..5e5787973 100644 --- a/src/targets/Transpiler.52.ts +++ b/src/targets/Transpiler.52.ts @@ -136,9 +136,4 @@ export class LuaTranspiler52 extends LuaTranspiler51 { public transpileDestructingAssignmentValue(node: ts.Expression): string { return `table.unpack(${this.transpileExpression(node)})`; } - - /** @override */ - public transpileSpreadElement(node: ts.SpreadElement): string { - return "table.unpack(" + this.transpileExpression(node.expression) + ")"; - } } diff --git a/src/targets/Transpiler.JIT.ts b/src/targets/Transpiler.JIT.ts index a23e3a8e2..76bf2fac2 100644 --- a/src/targets/Transpiler.JIT.ts +++ b/src/targets/Transpiler.JIT.ts @@ -39,9 +39,4 @@ export class LuaTranspilerJIT extends LuaTranspiler52 { public transpileDestructingAssignmentValue(node: ts.Expression): string { return `unpack(${this.transpileExpression(node)})`; } - - /** @override */ - public transpileSpreadElement(node: ts.SpreadElement): string { - return "unpack(" + this.transpileExpression(node.expression) + ")"; - } } From b328760a4714cfe7683bb88c35f7aedeeb982428 Mon Sep 17 00:00:00 2001 From: Lolleko Date: Sat, 1 Dec 2018 21:14:47 +0100 Subject: [PATCH 08/14] Added remaining implementations for transforms Transfrom & Transpile works now but there are still soem feature missing Many tests (especially translation) will fail aswell. --- src/Transformer.ts | 143 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 112 insertions(+), 31 deletions(-) diff --git a/src/Transformer.ts b/src/Transformer.ts index 0ef7d4ff0..d529b98e0 100644 --- a/src/Transformer.ts +++ b/src/Transformer.ts @@ -106,28 +106,20 @@ export class LuaTransformer { case ts.SyntaxKind.FunctionExpression: case ts.SyntaxKind.ArrowFunction: case ts.SyntaxKind.NewExpression: + // Identifier case ts.SyntaxKind.Identifier: - return this.visitExpression(node as ts.Expression); - // Literal + // Literals case ts.SyntaxKind.StringLiteral: - return this.visitStringLiteral(node as ts.StringLiteral); case ts.SyntaxKind.NoSubstitutionTemplateLiteral: - return this.visitNoSubstitutionTemplateLiteral(node as ts.NoSubstitutionTemplateLiteral); case ts.SyntaxKind.NumericLiteral: - return this.visitNumericLiteral(node as ts.NumericLiteral); // Keywords case ts.SyntaxKind.TrueKeyword: - return this.visitTrueKeyword(node as ts.BooleanLiteral); case ts.SyntaxKind.FalseKeyword: - return this.visitFalseKeyword(node as ts.BooleanLiteral); case ts.SyntaxKind.NullKeyword: - return this.visitNullKeyword(node as ts.KeywordTypeNode); case ts.SyntaxKind.UndefinedKeyword: - return this.visitUndefinedKeyword(node as ts.KeywordTypeNode); case ts.SyntaxKind.ThisKeyword: - return this.visitThisKeyword(node as ts.KeywordTypeNode); case ts.SyntaxKind.SuperKeyword: - return this.visitSuperKeyword(node as ts.KeywordTypeNode); + return this.visitExpression(node as ts.Expression); // ComputedPropertyName case ts.SyntaxKind.ComputedPropertyName: return this.visitComputedPropertyName(node as ts.ComputedPropertyName); @@ -228,6 +220,24 @@ export class LuaTransformer { return this.visitNewExpression(node as ts.NewExpression); case ts.SyntaxKind.Identifier: return this.visitIdentifier(node as ts.Identifier); + case ts.SyntaxKind.StringLiteral: + return this.visitStringLiteral(node as ts.StringLiteral); + case ts.SyntaxKind.NoSubstitutionTemplateLiteral: + return this.visitNoSubstitutionTemplateLiteral(node as ts.NoSubstitutionTemplateLiteral); + case ts.SyntaxKind.NumericLiteral: + return this.visitNumericLiteral(node as ts.NumericLiteral); + case ts.SyntaxKind.TrueKeyword: + return this.visitTrueKeyword(node as ts.BooleanLiteral); + case ts.SyntaxKind.FalseKeyword: + return this.visitFalseKeyword(node as ts.BooleanLiteral); + case ts.SyntaxKind.NullKeyword: + return this.visitNullKeyword(node as ts.NullLiteral); + case ts.SyntaxKind.UndefinedKeyword: + return this.visitUndefinedKeyword(node as ts.LiteralExpression); + case ts.SyntaxKind.ThisKeyword: + return this.visitThisKeyword(node as ts.ThisExpression); + case ts.SyntaxKind.SuperKeyword: + return this.visitSuperKeyword(node as ts.SuperExpression); default: throw TSTLErrors.UnsupportedKind("Expression", node.kind, node); } @@ -280,8 +290,62 @@ export class LuaTransformer { throw TSTLErrors.UnsupportedImportType(imports); } } - public visitClassDeclaration(node: ts.ClassDeclaration): ts.VisitResult { - return node; + public visitClassDeclaration(node: ts.ClassDeclaration): ts.ClassDeclaration { + // TODO this should actually be converted to lua nodes + return ts.updateClassDeclaration(node, + node.decorators, + node.modifiers, + node.name, + node.typeParameters, + node.heritageClauses, + node.members.map(elem => this.visitClassElement(elem) as ts.ClassElement)); + // TODO make member visitor more specific + } + public visitClassElement(node: ts.ClassElement): ts.ClassElement { + switch (node.kind) { + case ts.SyntaxKind.PropertyDeclaration: + return this.visitPropertyDeclaration(node as ts.PropertyDeclaration); + case ts.SyntaxKind.MethodDeclaration: + return this.visitMethodDeclaration(node as ts.MethodDeclaration); + case ts.SyntaxKind.Constructor: + return this.visitConstructorDeclaration(node as ts.ConstructorDeclaration); + } + } + public visitPropertyDeclaration(node: ts.PropertyDeclaration): ts.PropertyDeclaration { + let updatedInitializer: ts.Expression; + if (node.initializer) { + updatedInitializer = this.visitExpression(node.initializer); + } + return ts.updateProperty(node, + node.decorators, + node.modifiers, + node.name, + node.questionToken || node.exclamationToken, + node.type, + updatedInitializer); + } + public visitMethodDeclaration(node: ts.MethodDeclaration): ts.MethodDeclaration { + let updatedBody: ts.Block; + if (node.body) { + updatedBody = this.visitBlock(node.body); + } + return ts.updateMethod(node, + node.decorators, + node.modifiers, + node.asteriskToken, + node.name, + node.questionToken, + node.typeParameters, + node.parameters, + node.type, + updatedBody); + } + public visitConstructorDeclaration(node: ts.ConstructorDeclaration): ts.ConstructorDeclaration { + let updatedBody: ts.Block; + if (node.body) { + updatedBody = this.visitBlock(node.body); + } + return ts.updateConstructor(node, node.decorators, node.modifiers, node.parameters, updatedBody); } // previously transpileNamespace public visitModuleDeclaration(node: ts.ModuleDeclaration): ts.VisitResult { @@ -349,7 +413,15 @@ export class LuaTransformer { return node; } public visitFunctionDeclaration(node: ts.FunctionDeclaration): ts.VisitResult { - return node; + return ts.updateFunctionDeclaration(node, + node.decorators, + node.modifiers, + node.asteriskToken, + node.name, + node.typeParameters, + node.parameters, + node.type, + this.visitBlock(node.body)); } public visitTypeAliasDeclaration(node: ts.TypeAliasDeclaration): ts.VisitResult { return undefined; @@ -366,20 +438,29 @@ export class LuaTransformer { node.declarations.map(decl => this.visitVariableDeclaration(decl))); } public visitVariableDeclaration(node: ts.VariableDeclaration): ts.VariableDeclaration { - // TODO - return node; + let initializer: ts.Expression; + if (node.initializer) { + initializer = this.visitExpression(node.initializer); + } + return ts.updateVariableDeclaration(node, node.name, node.type, initializer); } public visitExpressionStatement(node: ts.ExpressionStatement): ts.ExpressionStatement { - return node; + return ts.updateStatement(node, this.visitExpression(node.expression)); } public visitReturn(node: ts.ReturnStatement): ts.ReturnStatement { - return ts.updateReturn(node, this.visitExpression(node.expression)); + let updatedExpression: ts.Expression; + if (node.expression) { + updatedExpression = this.visitExpression(node.expression); + } + return ts.updateReturn(node, updatedExpression); } public visitIfStatement(node: ts.IfStatement): ts.IfStatement { - return ts.updateIf(node, - this.visitExpression(node.expression), - this.visitStatement(node.thenStatement), - this.visitStatement(node.elseStatement)); + let elseStatement: ts.Statement; + if (node.elseStatement) { + elseStatement = this.visitStatement(node.elseStatement); + } + return ts.updateIf( + node, this.visitExpression(node.expression), this.visitStatement(node.thenStatement), elseStatement); } public visitWhileStatement(node: ts.WhileStatement): ts.WhileStatement { return ts.updateWhile(node, this.visitExpression(node.expression), this.visitStatement(node.statement)); @@ -572,32 +653,32 @@ export class LuaTransformer { return node; } - public visitStringLiteral(node: ts.StringLiteral): ts.VisitResult { + public visitStringLiteral(node: ts.StringLiteral): ts.StringLiteral { return node; } public visitNoSubstitutionTemplateLiteral(node: ts.NoSubstitutionTemplateLiteral): - ts.VisitResult { + ts.NoSubstitutionTemplateLiteral { return node; } - public visitNumericLiteral(node: ts.NumericLiteral): ts.VisitResult { + public visitNumericLiteral(node: ts.NumericLiteral): ts.NumericLiteral { return node; } - public visitTrueKeyword(node: ts.BooleanLiteral): ts.VisitResult { + public visitTrueKeyword(node: ts.BooleanLiteral): ts.BooleanLiteral { return node; } - public visitFalseKeyword(node: ts.BooleanLiteral): ts.VisitResult { + public visitFalseKeyword(node: ts.BooleanLiteral): ts.BooleanLiteral { return node; } - public visitNullKeyword(node: ts.KeywordTypeNode): ts.VisitResult { + public visitNullKeyword(node: ts.NullLiteral): ts.NullLiteral { return node; } - public visitUndefinedKeyword(node: ts.KeywordTypeNode): ts.VisitResult { + public visitUndefinedKeyword(node: ts.LiteralExpression): ts.LiteralExpression { return node; } - public visitThisKeyword(node: ts.KeywordTypeNode): ts.VisitResult { + public visitThisKeyword(node: ts.ThisExpression): ts.ThisExpression { return node; } - public visitSuperKeyword(node: ts.KeywordTypeNode): ts.VisitResult { + public visitSuperKeyword(node: ts.SuperExpression): ts.SuperExpression { return node; } public visitComputedPropertyName(node: ts.ComputedPropertyName): ts.VisitResult { From f1839a0d40116e712415b0b685f131bd5fd22821 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 18 Nov 2018 20:49:44 +0100 Subject: [PATCH 09/14] Fixed bug with default values for constructor parameters --- src/Transpiler.ts | 35 ++++++++++++++++++----------------- test/unit/class.spec.ts | 22 +++++++++++++++++++++- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 84ccbad6c..0adcfd7b8 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1748,29 +1748,30 @@ export abstract class LuaTranspiler { public transpileConstructor(node: ts.ConstructorDeclaration, className: string): string { - const extraInstanceFields = []; + // Check for field declarations in constructor + const constructorFieldsDeclarations = node.parameters.filter(p => p.modifiers !== undefined); - const parameters = ["self"]; - node.parameters.forEach(param => { - // If param has decorators, add extra instance field - if (param.modifiers !== undefined) { - extraInstanceFields.push(this.transpileIdentifier(param.name as ts.Identifier)); - } - // Add to parameter list - parameters.push(this.transpileIdentifier(param.name as ts.Identifier)); - }); - - let result = this.indent + `function ${className}.constructor(${parameters.join(",")})\n`; + const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters); - // Add in instance field declarations - for (const f of extraInstanceFields) { - result += this.indent + ` self.${f} = ${f}\n`; - } + let result = this.indent + `function ${className}.constructor(${["self"].concat(paramNames).join(",")})\n`; // Transpile constructor body this.pushIndent(); this.classStack.push(className); - result += this.transpileBlock(node.body); + + // Add in instance field declarations + for (const declaration of constructorFieldsDeclarations) { + const declarationName = this.transpileIdentifier(declaration.name as ts.Identifier); + if (declaration.initializer) { + const value = this.transpileExpression(declaration.initializer); + result += this.indent + `self.${declarationName} = ${declarationName} or ${value}\n`; + } else { + result += this.indent + `self.${declarationName} = ${declarationName}\n`; + } + } + + result += this.transpileFunctionBody(node.parameters, node.body, spreadIdentifier); + this.classStack.pop(); this.popIndent(); diff --git a/test/unit/class.spec.ts b/test/unit/class.spec.ts index d3655034f..de67a5425 100644 --- a/test/unit/class.spec.ts +++ b/test/unit/class.spec.ts @@ -44,7 +44,7 @@ export class ClassTests { @Test("ClassConstructorAssignment") public classConstructorAssignment(): void { - // Transpile + // Transpile const lua = util.transpileString( `class a { constructor(public field: number) {} } return new a(4).field;` @@ -57,6 +57,26 @@ export class ClassTests { Expect(result).toBe(4); } + @Test("ClassConstructorDefaultParameter") + public classConstructorDefaultParameter(): void { + const result = util.transpileAndExecute( + `class a { public field: number; constructor(f: number = 3) { this.field = f; } } + return new a().field;` + ); + + Expect(result).toBe(3); + } + + @Test("ClassConstructorAssignmentDefault") + public classConstructorAssignmentParameterDefault(): void { + const result = util.transpileAndExecute( + `class a { constructor(public field: number = 3) { } } + return new a().field;` + ); + + Expect(result).toBe(3); + } + @Test("ClassNewNoBrackets") public classNewNoBrackets(): void { // Transpile From 43214f54dbcc957d004c388bbc7b57bbf3fb1ebb Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Sun, 11 Nov 2018 18:34:46 +1000 Subject: [PATCH 10/14] Lualib omit when unused (#280) * lualib inline omit header when no features are used * Tests to enforce no lualib text when unused, unless using always --- src/Transpiler.ts | 2 +- test/unit/modules.spec.ts | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 0adcfd7b8..c3e41e65d 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -213,7 +213,7 @@ export abstract class LuaTranspiler { } // Inline lualib features - if (this.options.luaLibImport === LuaLibImportKind.Inline) { + if (this.options.luaLibImport === LuaLibImportKind.Inline && this.luaLibFeatureSet.size > 0) { result += "\n" + "-- Lua Library Imports\n"; for (const feature of this.luaLibFeatureSet) { const featureFile = path.resolve(__dirname, `../dist/lualib/${feature}.lua`); diff --git a/test/unit/modules.spec.ts b/test/unit/modules.spec.ts index 0688eeaac..07230485a 100644 --- a/test/unit/modules.spec.ts +++ b/test/unit/modules.spec.ts @@ -23,15 +23,6 @@ export class LuaModuleTests { Expect(lua.startsWith(`require("lualib_bundle")`)); } - @Test("lualibRequireNoUses") - public lualibRequireNoUses(): void { - // Transpile - const lua = util.transpileString(``, { luaLibImport: LuaLibImportKind.Require, luaTarget: LuaTarget.LuaJIT }); - - // Assert - Expect(lua).toBe(``); - } - @Test("lualibRequireAlways") public lualibRequireAlways(): void { // Transpile @@ -49,4 +40,16 @@ export class LuaModuleTests { Expect(result).toBe(3); } + + @TestCase(LuaLibImportKind.Inline) + @TestCase(LuaLibImportKind.None) + @TestCase(LuaLibImportKind.Require) + @Test("LuaLib no uses? No code") + public lualibNoUsesNoCode(impKind: LuaLibImportKind): void { + // Transpile + const lua = util.transpileString(``, { luaLibImport: impKind }); + + // Assert + Expect(lua).toBe(``); + } } From 396d34366547aed6be844afc6bf96b5faf304a57 Mon Sep 17 00:00:00 2001 From: Lolleko Date: Sat, 1 Dec 2018 21:37:05 +0100 Subject: [PATCH 11/14] Fixed wrong version (git fail) --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f18d09f2..917dc78de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "typescript-to-lua", - "version": "0.11.0", + "version": "0.11.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b9c1a6ecc..1f083c6e4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typescript-to-lua", "license": "MIT", - "version": "0.11.0", + "version": "0.11.1", "repository": "https://github.com/Perryvw/TypescriptToLua", "keywords": [ "typescript", From fadd37ae739da8a6c8ddf64728f83836518bd40c Mon Sep 17 00:00:00 2001 From: lolleko Date: Sun, 2 Dec 2018 02:36:16 +0100 Subject: [PATCH 12/14] Updated alsatian --- package-lock.json | 32 ++++++++++++++++---------------- package.json | 2 +- test/runner.ts | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index 917dc78de..af6ed4063 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,9 +52,9 @@ } }, "alsatian": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/alsatian/-/alsatian-2.2.1.tgz", - "integrity": "sha1-B+qeiU7bnqmX7VcaZOFMyYsj0/g=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/alsatian/-/alsatian-2.3.0.tgz", + "integrity": "sha512-e83K7JpH9Hj1+TUYyZialNKDln5gW56C82CVcd0AMt2whLFtKzYiddE5Dz2biUWjT8KzF11lkwonzf9wOCQyAA==", "dev": true, "requires": { "@types/node": ">=4.0.0", @@ -341,7 +341,7 @@ }, "duplexer": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, @@ -3511,9 +3511,9 @@ "dev": true }, "readable-stream": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz", - "integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==", + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3521,7 +3521,7 @@ "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", + "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, @@ -3689,9 +3689,9 @@ } }, "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3718,7 +3718,7 @@ }, "tap-bark": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tap-bark/-/tap-bark-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/tap-bark/-/tap-bark-1.0.0.tgz", "integrity": "sha1-bAPcUWh/7Xh3+COtSx3dHvXrVnQ=", "dev": true, "requires": { @@ -3751,12 +3751,12 @@ } }, "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "^2.1.5", + "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, diff --git a/package.json b/package.json index 1f083c6e4..a71fd0cb5 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "@types/glob": "^5.0.35", "@types/node": "^9.6.23", "@types/yargs": "^11.1.1", - "alsatian": "^2.2.1", + "alsatian": "^2.3.0", "circular-json": "^0.5.5", "codecov": "3.0.2", "deep-equal": "^1.0.1", diff --git a/test/runner.ts b/test/runner.ts index 4d237f535..0d5d542fc 100644 --- a/test/runner.ts +++ b/test/runner.ts @@ -23,7 +23,7 @@ fs.copyFileSync( testRunner.outputStream // this will use alsatian's default output if you remove this // you'll get TAP or you can add your favourite TAP reporter in it's place - // .pipe(TapBark.create().getPipeable()) + .pipe(TapBark.create().getPipeable()) // pipe to the console .pipe(process.stdout); From 0733f5ab5824b6c26004fc3dc436696f05c52c17 Mon Sep 17 00:00:00 2001 From: lolleko Date: Sun, 2 Dec 2018 14:34:17 +0100 Subject: [PATCH 13/14] Improved empty block handling --- src/LuaLibFeature.ts | 22 ++++++++++++++++++++++ src/Transformer.ts | 15 +++++---------- src/Transpiler.ts | 24 +----------------------- src/tstl.ts | 5 ++++- test/translation/builder.spec.ts | 3 +-- 5 files changed, 33 insertions(+), 36 deletions(-) create mode 100644 src/LuaLibFeature.ts diff --git a/src/LuaLibFeature.ts b/src/LuaLibFeature.ts new file mode 100644 index 000000000..f33236ff1 --- /dev/null +++ b/src/LuaLibFeature.ts @@ -0,0 +1,22 @@ +export enum LuaLibFeature { + ArrayConcat = "ArrayConcat", + ArrayEvery = "ArrayEvery", + ArrayFilter = "ArrayFilter", + ArrayForEach = "ArrayForEach", + ArrayIndexOf = "ArrayIndexOf", + ArrayMap = "ArrayMap", + ArrayPush = "ArrayPush", + ArrayReverse = "ArrayReverse", + ArrayShift = "ArrayShift", + ArrayUnshift = "ArrayUnshift", + ArraySort = "ArraySort", + ArraySlice = "ArraySlice", + ArraySome = "ArraySome", + ArraySplice = "ArraySplice", + InstanceOf = "InstanceOf", + Map = "Map", + Set = "Set", + StringReplace = "StringReplace", + StringSplit = "StringSplit", + Ternary = "Ternary", +} diff --git a/src/Transformer.ts b/src/Transformer.ts index d529b98e0..8dfdf40f3 100644 --- a/src/Transformer.ts +++ b/src/Transformer.ts @@ -325,10 +325,6 @@ export class LuaTransformer { updatedInitializer); } public visitMethodDeclaration(node: ts.MethodDeclaration): ts.MethodDeclaration { - let updatedBody: ts.Block; - if (node.body) { - updatedBody = this.visitBlock(node.body); - } return ts.updateMethod(node, node.decorators, node.modifiers, @@ -338,14 +334,10 @@ export class LuaTransformer { node.typeParameters, node.parameters, node.type, - updatedBody); + this.visitBlock(node.body)); } public visitConstructorDeclaration(node: ts.ConstructorDeclaration): ts.ConstructorDeclaration { - let updatedBody: ts.Block; - if (node.body) { - updatedBody = this.visitBlock(node.body); - } - return ts.updateConstructor(node, node.decorators, node.modifiers, node.parameters, updatedBody); + return ts.updateConstructor(node, node.decorators, node.modifiers, node.parameters, this.visitBlock(node.body)); } // previously transpileNamespace public visitModuleDeclaration(node: ts.ModuleDeclaration): ts.VisitResult { @@ -685,6 +677,9 @@ export class LuaTransformer { return node; } public visitBlock(node: ts.Block): ts.Block { + if (!node) { + return undefined; + } return ts.updateBlock(node, transformHelper.flatten(node.statements.map(s => this.visitor(s)) as ts.Statement[])); } diff --git a/src/Transpiler.ts b/src/Transpiler.ts index c3e41e65d..dead9fca5 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -5,6 +5,7 @@ import * as ts from "typescript"; import { CompilerOptions } from "./CompilerOptions"; import { DecoratorKind } from "./Decorator"; import { TSTLErrors } from "./Errors"; +import { LuaLibFeature } from "./LuaLibFeature"; import { TSHelper as tsHelper } from "./TSHelper"; import { LuaTransformer } from "./Transformer"; @@ -20,29 +21,6 @@ export enum LuaTarget { LuaJIT = "jit", } -export enum LuaLibFeature { - ArrayConcat = "ArrayConcat", - ArrayEvery = "ArrayEvery", - ArrayFilter = "ArrayFilter", - ArrayForEach = "ArrayForEach", - ArrayIndexOf = "ArrayIndexOf", - ArrayMap = "ArrayMap", - ArrayPush = "ArrayPush", - ArrayReverse = "ArrayReverse", - ArrayShift = "ArrayShift", - ArrayUnshift = "ArrayUnshift", - ArraySort = "ArraySort", - ArraySlice = "ArraySlice", - ArraySome = "ArraySome", - ArraySplice = "ArraySplice", - InstanceOf = "InstanceOf", - Map = "Map", - Set = "Set", - StringReplace = "StringReplace", - StringSplit = "StringSplit", - Ternary = "Ternary", -} - export enum LuaLibImportKind { None = "none", Always = "always", diff --git a/src/tstl.ts b/src/tstl.ts index 31174588c..0594900f7 100644 --- a/src/tstl.ts +++ b/src/tstl.ts @@ -19,12 +19,15 @@ export {LuaTranspiler53} from "./targets/Transpiler.53"; export {LuaTranspilerJIT} from "./targets/Transpiler.JIT"; export { - LuaLibFeature, LuaLibImportKind, LuaTarget, LuaTranspiler, } from "./Transpiler"; +export { + LuaLibFeature, +} from "./LuaLibFeature"; + export { createTranspiler } from "./TranspilerFactory"; diff --git a/test/translation/builder.spec.ts b/test/translation/builder.spec.ts index 893ddea93..f667a6fd0 100644 --- a/test/translation/builder.spec.ts +++ b/test/translation/builder.spec.ts @@ -1,4 +1,4 @@ -import { Expect, FocusTest, Test, TestCases } from "alsatian"; +import { Expect, Test, TestCases } from "alsatian"; import * as util from "../src/util"; @@ -41,7 +41,6 @@ export class FileTests { @TestCases(files) @Test("Transformation Tests") - @FocusTest public transformationTests(tsFile: string, luaFile: string) { Expect(util.transpileString(BufferToTestString(fileContents[tsFile]))) .toEqual(BufferToTestString(fileContents[luaFile])); From 3e791a4ad84f806aaaa67a2efb49f69a2157ec68 Mon Sep 17 00:00:00 2001 From: lolleko Date: Sun, 2 Dec 2018 14:44:50 +0100 Subject: [PATCH 14/14] Added empty lines between functions --- src/Transformer.ts | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/Transformer.ts b/src/Transformer.ts index 8dfdf40f3..d9f59173d 100644 --- a/src/Transformer.ts +++ b/src/Transformer.ts @@ -135,6 +135,7 @@ export class LuaTransformer { throw TSTLErrors.UnsupportedKind("Node", node.kind, node); } } + public visitStatement(node: ts.Statement): ts.Statement { if (ts.isBlock(node)) { return this.visitBlock(node); @@ -174,6 +175,7 @@ export class LuaTransformer { throw TSTLErrors.UnsupportedKind("Statement", node.kind, node); } } + public visitExpression(node: ts.Expression): ts.Expression { switch (node.kind) { case ts.SyntaxKind.BinaryExpression: @@ -242,6 +244,7 @@ export class LuaTransformer { throw TSTLErrors.UnsupportedKind("Expression", node.kind, node); } } + public visitImportDeclaration(node: ts.ImportDeclaration): ts.VisitResult { if (!node.importClause || !node.importClause.namedBindings) { throw TSTLErrors.DefaultImportsNotSupported(node); @@ -290,6 +293,7 @@ export class LuaTransformer { throw TSTLErrors.UnsupportedImportType(imports); } } + public visitClassDeclaration(node: ts.ClassDeclaration): ts.ClassDeclaration { // TODO this should actually be converted to lua nodes return ts.updateClassDeclaration(node, @@ -301,6 +305,7 @@ export class LuaTransformer { node.members.map(elem => this.visitClassElement(elem) as ts.ClassElement)); // TODO make member visitor more specific } + public visitClassElement(node: ts.ClassElement): ts.ClassElement { switch (node.kind) { case ts.SyntaxKind.PropertyDeclaration: @@ -311,6 +316,7 @@ export class LuaTransformer { return this.visitConstructorDeclaration(node as ts.ConstructorDeclaration); } } + public visitPropertyDeclaration(node: ts.PropertyDeclaration): ts.PropertyDeclaration { let updatedInitializer: ts.Expression; if (node.initializer) { @@ -324,6 +330,7 @@ export class LuaTransformer { node.type, updatedInitializer); } + public visitMethodDeclaration(node: ts.MethodDeclaration): ts.MethodDeclaration { return ts.updateMethod(node, node.decorators, @@ -336,6 +343,7 @@ export class LuaTransformer { node.type, this.visitBlock(node.body)); } + public visitConstructorDeclaration(node: ts.ConstructorDeclaration): ts.ConstructorDeclaration { return ts.updateConstructor(node, node.decorators, node.modifiers, node.parameters, this.visitBlock(node.body)); } @@ -401,9 +409,11 @@ export class LuaTransformer { return result; } + public visitEnumDeclaration(node: ts.EnumDeclaration): ts.VisitResult { return node; } + public visitFunctionDeclaration(node: ts.FunctionDeclaration): ts.VisitResult { return ts.updateFunctionDeclaration(node, node.decorators, @@ -415,20 +425,25 @@ export class LuaTransformer { node.type, this.visitBlock(node.body)); } + public visitTypeAliasDeclaration(node: ts.TypeAliasDeclaration): ts.VisitResult { return undefined; } + public visitInterfaceDeclaration(node: ts.InterfaceDeclaration): ts.VisitResult { return undefined; } + public visitVariableStatement(node: ts.VariableStatement): ts.VariableStatement { return ts.updateVariableStatement( node, node.modifiers, this.visitVariableDeclarationList(node.declarationList)); } + public visitVariableDeclarationList(node: ts.VariableDeclarationList): ts.VariableDeclarationList { return ts.updateVariableDeclarationList(node, node.declarations.map(decl => this.visitVariableDeclaration(decl))); } + public visitVariableDeclaration(node: ts.VariableDeclaration): ts.VariableDeclaration { let initializer: ts.Expression; if (node.initializer) { @@ -436,9 +451,11 @@ export class LuaTransformer { } return ts.updateVariableDeclaration(node, node.name, node.type, initializer); } + public visitExpressionStatement(node: ts.ExpressionStatement): ts.ExpressionStatement { return ts.updateStatement(node, this.visitExpression(node.expression)); } + public visitReturn(node: ts.ReturnStatement): ts.ReturnStatement { let updatedExpression: ts.Expression; if (node.expression) { @@ -446,6 +463,7 @@ export class LuaTransformer { } return ts.updateReturn(node, updatedExpression); } + public visitIfStatement(node: ts.IfStatement): ts.IfStatement { let elseStatement: ts.Statement; if (node.elseStatement) { @@ -454,12 +472,15 @@ export class LuaTransformer { return ts.updateIf( node, this.visitExpression(node.expression), this.visitStatement(node.thenStatement), elseStatement); } + public visitWhileStatement(node: ts.WhileStatement): ts.WhileStatement { return ts.updateWhile(node, this.visitExpression(node.expression), this.visitStatement(node.statement)); } + public visitDoStatement(node: ts.DoStatement): ts.DoStatement { return ts.updateDo(node, this.visitStatement(node.statement), this.visitExpression(node.expression)); } + public visitForStatement(node: ts.ForStatement): ts.ForStatement { return ts.updateFor(node, this.visitForInitializer(node.initializer), @@ -467,6 +488,7 @@ export class LuaTransformer { this.visitExpression(node.incrementor), this.visitStatement(node.statement)); } + public visitForOfStatement(node: ts.ForOfStatement): ts.ForOfStatement { return ts.updateForOf(node, node.awaitModifier, @@ -474,12 +496,14 @@ export class LuaTransformer { this.visitExpression(node.expression), this.visitStatement(node.statement)); } + public visitForInStatement(node: ts.ForInStatement): ts.ForInStatement { return ts.updateForIn(node, this.visitForInitializer(node.initializer), this.visitExpression(node.expression), this.visitStatement(node.statement)); } + public visitForInitializer(node: ts.ForInitializer): ts.ForInitializer { let updatedInitializer: ts.ForInitializer; if (ts.isVariableDeclarationList(node)) { @@ -489,29 +513,36 @@ export class LuaTransformer { } return updatedInitializer; } + public visitSwitchStatement(node: ts.SwitchStatement): ts.SwitchStatement { // TODO return node; } + public visitBreakStatement(node: ts.BreakStatement): ts.BreakStatement { // TODO return node; } + public visitTryStatement(node: ts.TryStatement): ts.TryStatement { // TODO return node; } + public visitThrowStatement(node: ts.ThrowStatement): ts.ThrowStatement { // TODO return node; } + public visitContinueStatement(node: ts.ContinueStatement): ts.ContinueStatement { // TODO return node; } + public visitEmptyStatement(node: ts.EmptyStatement): ts.EmptyStatement { return undefined; } + public visitBinaryExpression(node: ts.BinaryExpression): ts.BinaryExpression { let operatorToken = node.operatorToken; if (node.operatorToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) { @@ -519,34 +550,42 @@ export class LuaTransformer { } return ts.updateBinary(node, this.visitExpression(node.left), this.visitExpression(node.right), operatorToken); } + public visitConditionalExpression(node: ts.ConditionalExpression): ts.ConditionalExpression { return ts.updateConditional(node, this.visitExpression(node.condition), this.visitExpression(node.whenTrue), this.visitExpression(node.whenFalse)); } + public visitCallExpression(node: ts.CallExpression): ts.CallExpression { return ts.updateCall(node, this.visitExpression(node.expression), node.typeArguments, node.arguments.map(arg => this.visitExpression(arg))); } + public visitPropertyAccessExpression(node: ts.PropertyAccessExpression): ts.PropertyAccessExpression { return ts.updatePropertyAccess(node, this.visitExpression(node.expression), node.name); } + public visitElementAccessExpression(node: ts.ElementAccessExpression): ts.ElementAccessExpression { return ts.updateElementAccess( node, this.visitExpression(node.expression), this.visitExpression(node.argumentExpression)); } + public visitParenthesizedExpression(node: ts.ParenthesizedExpression): ts.ParenthesizedExpression { return ts.updateParen(node, this.visitExpression(node.expression)); } + public visitTypeAssertionExpression(node: ts.TypeAssertion): ts.Expression { return this.visitExpression(node.expression); } + public visitAsExpression(node: ts.AsExpression): ts.Expression { return this.visitExpression(node.expression); } + public visitTypeOfExpression(node: ts.TypeOfExpression): ts.BinaryExpression { // ((type(${expression}) == "table" and "object") or type(${expression})) const expression = this.visitExpression(node.expression); @@ -558,6 +597,7 @@ export class LuaTransformer { const orExpression = ts.createLogicalOr(andExpression, typeCall); return orExpression; } + public visitSpreadElement(node: ts.SpreadElement): ts.CallExpression { // TODO move this to differen targets // table.unpack(expression) / unpack(expression) @@ -577,13 +617,16 @@ export class LuaTransformer { return ts.createCall( functionExpresion, [ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)], [node.expression]); } + public visitNonNullExpression(node: ts.NonNullExpression): ts.Expression { return this.visitExpression(node.expression); } + public visitClassExpression(node: ts.ClassExpression): ts.ClassExpression { // TODO return node; } + public visitTemplateExpression(node: ts.TemplateExpression): ts.Expression { let concatExpression: ts.Expression = ts.createLiteral(node.head.text); node.templateSpans.forEach(span => { @@ -596,23 +639,29 @@ export class LuaTransformer { }); return concatExpression; } + public visitPostfixUnaryExpression(node: ts.PostfixUnaryExpression): ts.PostfixUnaryExpression { // TODO return node; } + public visitPrefixUnaryExpression(node: ts.PrefixUnaryExpression): ts.PrefixUnaryExpression { // TODO return node; } + public visitArrayLiteralExpression(node: ts.ArrayLiteralExpression): ts.ArrayLiteralExpression { return node; } + public visitObjectLiteralExpression(node: ts.ObjectLiteralExpression): ts.ObjectLiteralExpression { return node; } + public visitDeleteExpression(node: ts.DeleteExpression): ts.Expression { return ts.createAssignment(this.visitExpression(node.expression), ts.createNull()); } + public visitFunctionExpression(node: ts.FunctionExpression): ts.FunctionExpression { return ts.updateFunctionExpression(node, node.modifiers, @@ -623,6 +672,7 @@ export class LuaTransformer { node.type, this.visitBlock(node.body)); } + public visitArrowFunction(node: ts.ArrowFunction): ts.ArrowFunction { let newBody: ts.ConciseBody; if (ts.isBlock(node.body)) { @@ -632,9 +682,11 @@ export class LuaTransformer { } return ts.updateArrowFunction(node, node.modifiers, node.typeParameters, node.parameters, node.type, newBody); } + public visitNewExpression(node: ts.NewExpression): ts.NewExpression { return node; } + public visitIdentifier(node: ts.Identifier): ts.Identifier| ts.PropertyAccessExpression { // If we are in a namespace or a sourcefile that is a module check if this identifier is exported if (this.currentNamespace && tsHelper.isIdentifierExported(node, this.currentNamespace, this.checker)) { @@ -645,37 +697,48 @@ export class LuaTransformer { return node; } + public visitStringLiteral(node: ts.StringLiteral): ts.StringLiteral { return node; } + public visitNoSubstitutionTemplateLiteral(node: ts.NoSubstitutionTemplateLiteral): ts.NoSubstitutionTemplateLiteral { return node; } + public visitNumericLiteral(node: ts.NumericLiteral): ts.NumericLiteral { return node; } + public visitTrueKeyword(node: ts.BooleanLiteral): ts.BooleanLiteral { return node; } + public visitFalseKeyword(node: ts.BooleanLiteral): ts.BooleanLiteral { return node; } + public visitNullKeyword(node: ts.NullLiteral): ts.NullLiteral { return node; } + public visitUndefinedKeyword(node: ts.LiteralExpression): ts.LiteralExpression { return node; } + public visitThisKeyword(node: ts.ThisExpression): ts.ThisExpression { return node; } + public visitSuperKeyword(node: ts.SuperExpression): ts.SuperExpression { return node; } + public visitComputedPropertyName(node: ts.ComputedPropertyName): ts.VisitResult { return node; } + public visitBlock(node: ts.Block): ts.Block { if (!node) { return undefined; @@ -683,9 +746,11 @@ export class LuaTransformer { return ts.updateBlock(node, transformHelper.flatten(node.statements.map(s => this.visitor(s)) as ts.Statement[])); } + public visitModuleBlock(node: ts.ModuleBlock): ts.VisitResult { return this.visitBlock(ts.createBlock(node.statements)); } + public visitEndOfFileToken(node: ts.EndOfFileToken): ts.VisitResult { return node; }