diff --git a/src/CommandLineParser.ts b/src/CommandLineParser.ts index b3cccea4f..386dab5ba 100644 --- a/src/CommandLineParser.ts +++ b/src/CommandLineParser.ts @@ -19,7 +19,7 @@ export class CLIError extends Error { const optionDeclarations: { [key: string]: yargs.Options } = { addHeader: { - alias: "ah", + alias: ["ah", "header"], default: true, describe: "Specify if a header will be added to compiled files.", type: "boolean", @@ -154,9 +154,9 @@ function runDiagnostics(commandLine: ts.ParsedCommandLine) { } /** Find configFile, function from ts api seems to be broken? */ -function findConfigFile(commandLine: ts.ParsedCommandLine) { +export function findConfigFile(commandLine: ts.ParsedCommandLine) { if (!commandLine.options.project) { - return; + throw new CLIError(`error no base path provided, could not find config.`); } let configPath; if (path.isAbsolute(commandLine.options.project)) { diff --git a/src/ForHelper.ts b/src/ForHelper.ts deleted file mode 100644 index 45056306b..000000000 --- a/src/ForHelper.ts +++ /dev/null @@ -1,98 +0,0 @@ -import * as ts from "typescript"; - -import {LuaTranspiler, TranspileError} from "./Transpiler"; -import {TSHelper as tsEx} from "./TSHelper"; - -export class ForHelper { - - // Get the ending value of a numeric for loop - public static GetForEnd(condition: ts.Expression, transpiler: LuaTranspiler): string { - if (ts.isBinaryExpression(condition)) { - if (ts.isIdentifier(condition.left)) { - // Account for lua 1 indexing - switch (condition.operatorToken.kind) { - case ts.SyntaxKind.LessThanEqualsToken: - case ts.SyntaxKind.GreaterThanEqualsToken: - return transpiler.transpileExpression(condition.right); - case ts.SyntaxKind.LessThanToken: - return transpiler.transpileExpression(condition.right) + "-1"; - case ts.SyntaxKind.GreaterThanToken: - return transpiler.transpileExpression(condition.right) + "+1"; - default: - throw new TranspileError( - "Unsupported for-loop condition operator: " + - tsEx.enumName(condition.operatorToken, ts.SyntaxKind), - condition - ); - } - } else { - // Account for lua 1 indexing - switch (condition.operatorToken.kind) { - case ts.SyntaxKind.LessThanEqualsToken: - case ts.SyntaxKind.GreaterThanEqualsToken: - return transpiler.transpileExpression(condition.left); - case ts.SyntaxKind.LessThanToken: - return transpiler.transpileExpression(condition.left) + "+1"; - case ts.SyntaxKind.GreaterThanToken: - return transpiler.transpileExpression(condition.left) + "-1"; - default: - throw new TranspileError( - "Unsupported for-loop condition operator: " + - tsEx.enumName(condition.operatorToken, ts.SyntaxKind), - condition - ); - } - } - } else { - throw new TranspileError( - "Unsupported for-loop condition type: " + - tsEx.enumName(condition.kind, ts.SyntaxKind), - condition - ); - } - } - - // Get increment step for numeric for loop - public static GetForStep(incrementor: ts.Expression, transpiler: LuaTranspiler): string { - switch (incrementor.kind) { - case ts.SyntaxKind.PostfixUnaryExpression: - case ts.SyntaxKind.PrefixUnaryExpression: - const operator = - (incrementor as ts.PostfixUnaryExpression|ts.PrefixUnaryExpression).operator; - switch (operator) { - case ts.SyntaxKind.PlusPlusToken: - return "1"; - case ts.SyntaxKind.MinusMinusToken: - return "-1"; - default: - throw new TranspileError( - "Unsupported for-loop increment step: " + - tsEx.enumName(incrementor.kind, ts.SyntaxKind), - incrementor - ); - } - case ts.SyntaxKind.BinaryExpression: - const value = ts.isIdentifier((incrementor as ts.BinaryExpression).left) ? - transpiler.transpileExpression((incrementor as ts.BinaryExpression).right) : - transpiler.transpileExpression((incrementor as ts.BinaryExpression).left); - switch ((incrementor as ts.BinaryExpression).operatorToken.kind) { - case ts.SyntaxKind.PlusEqualsToken: - return value; - case ts.SyntaxKind.MinusEqualsToken: - return "-" + value; - default: - throw new TranspileError( - "Unsupported for-loop increment step: " + - tsEx.enumName(incrementor.kind, ts.SyntaxKind), - incrementor - ); - } - default: - throw new TranspileError( - "Unsupported for-loop increment step: " + - tsEx.enumName(incrementor.kind, ts.SyntaxKind), - incrementor - ); - } - } -} diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 7eabc10ff..24da5fbe0 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -97,7 +97,7 @@ export class TSHelper { public static isTupleReturnFunction(type: ts.Type, checker: ts.TypeChecker): boolean { return type.symbol && ((type.symbol.flags & ts.SymbolFlags.Function) !== 0) - && this.hasCustomDecorator(type, checker, "!TupleReturn"); + && this.hasCustomDecorator(type, checker, "!TupleReturn"); } public static hasCustomDecorator(type: ts.Type, checker: ts.TypeChecker, decorator: string): boolean { diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 14644ca20..2b4640268 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1,7 +1,6 @@ import * as ts from "typescript"; import { CompilerOptions } from "./CommandLineParser"; -import { ForHelper } from "./ForHelper"; import { TSHelper as tsEx } from "./TSHelper"; import * as path from "path"; @@ -362,24 +361,22 @@ export class LuaTranspiler { } public transpileFor(node: ts.ForStatement): string { - // Get iterator variable - const variable = (node.initializer as ts.VariableDeclarationList).declarations[0]; - const identifier = variable.name as ts.Identifier; - - // Populate three components of lua numeric for loop: - const start = this.transpileExpression(variable.initializer); - const end = ForHelper.GetForEnd(node.condition, this); - const step = ForHelper.GetForStep(node.incrementor, this); - // Add header - let result = this.indent + `for ${identifier.escapedText}=${start},${end},${step} do\n`; + let result = ""; + for (const variableDeclaration of (node.initializer as ts.VariableDeclarationList).declarations) { + result += this.transpileVariableDeclaration(variableDeclaration); + } + result += this.indent + `while(${this.transpileExpression(node.condition)}) do\n`; // Add body this.pushIndent(); result += this.transpileStatement(node.statement); + result += this.indent + this.transpileExpression(node.incrementor) + "\n"; this.popIndent(); - return result + this.indent + "end\n"; + result += this.indent + "end\n"; + + return result; } public transpileForOf(node: ts.ForOfStatement): string { diff --git a/test/compiler/project.spec.ts b/test/compiler/project.spec.ts index ad8b84a82..902677718 100644 --- a/test/compiler/project.spec.ts +++ b/test/compiler/project.spec.ts @@ -32,6 +32,10 @@ export class CompilerProjectTests { "typescript_lualib.lua", "test_src/test_lib/file.lua", "test_src/main.lua") + @TestCase(".", + "typescript_lualib.lua", + "test_src/test_lib/file.lua", + "test_src/main.lua") @TestCase("test_src/main.ts", "typescript_lualib.lua", "test_src/test_lib/file.lua", diff --git a/test/translation/lua/classExtension.lua b/test/translation/lua/classExtension1.lua similarity index 100% rename from test/translation/lua/classExtension.lua rename to test/translation/lua/classExtension1.lua diff --git a/test/translation/lua/classExtension2.lua b/test/translation/lua/classExtension2.lua new file mode 100644 index 000000000..8d09589c5 --- /dev/null +++ b/test/translation/lua/classExtension2.lua @@ -0,0 +1,2 @@ +function TestClass.myFunction(self) +end diff --git a/test/translation/lua/classStaticMembers.lua b/test/translation/lua/classStaticMembers.lua new file mode 100644 index 000000000..d27e34d5b --- /dev/null +++ b/test/translation/lua/classStaticMembers.lua @@ -0,0 +1,10 @@ +MyClass = MyClass or {} +MyClass.__index = MyClass +function MyClass.new(construct, ...) + local instance = setmetatable({}, MyClass) + if construct and MyClass.constructor then MyClass.constructor(instance, ...) end + return instance +end +MyClass.test = 0 +function MyClass.constructor(self) +end diff --git a/test/translation/lua/typeAssert.lua b/test/translation/lua/typeAssert.lua new file mode 100644 index 000000000..09dc9e405 --- /dev/null +++ b/test/translation/lua/typeAssert.lua @@ -0,0 +1,3 @@ +local test1 = 10 + +local test2 = 10 diff --git a/test/translation/ts/classExtension.ts b/test/translation/ts/classExtension1.ts similarity index 100% rename from test/translation/ts/classExtension.ts rename to test/translation/ts/classExtension1.ts diff --git a/test/translation/ts/classExtension2.ts b/test/translation/ts/classExtension2.ts new file mode 100644 index 000000000..f3c6f31bd --- /dev/null +++ b/test/translation/ts/classExtension2.ts @@ -0,0 +1,9 @@ +/** !Extension */ +class TestClass { +} + + +/** !Extension */ +class MyClass extends TestClass { + myFunction() {} +} diff --git a/test/translation/ts/classStaticMembers.ts b/test/translation/ts/classStaticMembers.ts new file mode 100644 index 000000000..2fb89cfeb --- /dev/null +++ b/test/translation/ts/classStaticMembers.ts @@ -0,0 +1,3 @@ +class MyClass { + public static test = 0; +} diff --git a/test/translation/ts/typeAssert.ts b/test/translation/ts/typeAssert.ts new file mode 100644 index 000000000..95c751a36 --- /dev/null +++ b/test/translation/ts/typeAssert.ts @@ -0,0 +1,2 @@ +const test1 = 10; +const test2 = 10 as number; diff --git a/test/unit/cli.spec.ts b/test/unit/cli.spec.ts index 47ca20195..13b756745 100644 --- a/test/unit/cli.spec.ts +++ b/test/unit/cli.spec.ts @@ -1,6 +1,6 @@ import { Expect, Test, TestCase, Teardown } from "alsatian"; -import { CompilerOptions, parseCommandLine, ParsedCommandLine } from "../../src/CommandLineParser"; +import { CompilerOptions, findConfigFile, parseCommandLine, ParsedCommandLine } from "../../src/CommandLineParser"; export class CLITests { @@ -23,7 +23,7 @@ export class CLITests { Expect(() => parseCommandLine(['--luaTarget', '42'])).toThrow(); } - @Test("InvalidArgument") + @Test("InvalidArgumentTSTL") public invalidArgument() { // Don't check error message because the yargs library messes the message up. Expect(() => parseCommandLine(['--invalidTarget', 'test'])).toThrow(); @@ -52,4 +52,10 @@ export class CLITests { Expect(parsedCommandLine.options['rootDir']).toBe('./testRoot'); } + @Test("Find config no path") + public findConfigNoPath() { + Expect(() => findConfigFile({options: {}, fileNames: [], errors: []})).toThrow(); + + } + } diff --git a/test/unit/enum.spec.ts b/test/unit/enum.spec.ts new file mode 100644 index 000000000..1a25cc171 --- /dev/null +++ b/test/unit/enum.spec.ts @@ -0,0 +1,18 @@ +import { Expect, Test, TestCase } from "alsatian"; +import * as util from "../src/util" + +export class EnumTests { + @Test("Unsuported enum") + public unsuportedEnum() { + // Transpile & Assert + Expect(() => { + let lua = util.transpileString( + `enum TestEnum { + val1 = "test", + val2 = "ok", + val3 = "bye" + }` + ); + }).toThrowError(Error, "Only numeric initializers allowed for enums."); + } +} diff --git a/test/unit/loops.spec.ts b/test/unit/loops.spec.ts index 8c5e17ac7..f3db44381 100644 --- a/test/unit/loops.spec.ts +++ b/test/unit/loops.spec.ts @@ -102,8 +102,7 @@ export class LuaLoopTests { @TestCase([0, 1, 2, 3], [1, 2, 3, 4], "let i = 0; arrTest.length > i; i++") @TestCase([0, 1, 2, 3], [1, 2, 3, 4], "let i = 0; arrTest.length - 1 >= i; i++") @TestCase([0, 1, 2, 3], [1, 1, 3, 3], "let i = 0; i < arrTest.length; i += 2") - @TestCase([0, 1, 2, 3], [1, 2, 3, 4], "let i = arrTest.length - 1; i <= 0; i--") - @TestCase([0, 1, 2, 3], [0, 2, 2, 4], "let i = arrTest.length - 1; i <= 0; i -= 2") + @TestCase([0, 1, 2, 3], [1, 2, 3, 4 ], "let i = arrTest.length - 1; i >= 0; i--") @TestCase([0, 1, 2, 3], [0, 2, 2, 4], "let i = arrTest.length - 1; i >= 0; i -= 2") @TestCase([0, 1, 2, 3], [0, 2, 2, 4], "let i = arrTest.length - 1; i > 0; i -= 2") @Test("forheader") @@ -124,42 +123,6 @@ export class LuaLoopTests { Expect(result).toBe(JSON.stringify(expected)); } - @Test("forstepThrow") - public forstepThrow(inp: number[], expected: number[], header: string) { - // Transpile & Assert - Expect(() => { - let lua = util.transpileString( - `for (let i = 0; i < 30; i = i + 10) { - }` - ); - - // Execute - let result = util.executeLua(lua); - }).toThrowError(Error, "Unsupported for-loop increment step: BinaryExpression") - } - - @TestCase("let i = 0; i + 3; i++") - @TestCase("let i = 0; 3 + i; i++") - @TestCase("let i = 0; i - 3; i++") - @TestCase("let i = 0; i * 3; i++") - @TestCase("let i = 0; i / 3; i++") - @TestCase("let i = 0; i &= 3; i++") - @TestCase("let i = 0; i < 3; !i") - @TestCase("let i = 0; i < 3; i as string") - @Test("forconditionThrow") - public forconditionThrow(header: string) { - // Transpile & Assert - Expect(() => { - let lua = util.transpileString( - `for (${header}) { - }` - ); - - // Execute - let result = util.executeLua(lua); - }).toThrow(); - } - @TestCase({ ['test1']: 0, ['test2']: 1, ['test3']: 2 }, { ['test1']: 1, ['test2']: 2, ['test3']: 3 }) @Test("forin[Object]") public forinObject(inp: any, expected: any) { diff --git a/test/unit/string.spec.ts b/test/unit/string.spec.ts index a0ff8e8ce..18ed59a9e 100644 --- a/test/unit/string.spec.ts +++ b/test/unit/string.spec.ts @@ -3,6 +3,16 @@ import * as util from "../src/util" export class StringTests { + @Test("Unsuported string function") + public stringUnsuportedFunction() { + // Assert + Expect(() => { + util.transpileString( + `return "test".testThisIsNoMember()` + ); + }).toThrowError(Error, "Unsupported string function: testThisIsNoMember"); + } + @TestCase([]) @TestCase([65]) @TestCase([65, 66])