From 39f16ad88f4dd7838bea21c69d03c879aeaf32eb Mon Sep 17 00:00:00 2001 From: Perryvw Date: Wed, 27 Jun 2018 22:14:56 +0200 Subject: [PATCH 1/7] Overload spec tests --- test/unit/overloads.spec.ts | 67 +++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 test/unit/overloads.spec.ts diff --git a/test/unit/overloads.spec.ts b/test/unit/overloads.spec.ts new file mode 100644 index 000000000..bd1abd934 --- /dev/null +++ b/test/unit/overloads.spec.ts @@ -0,0 +1,67 @@ +import { Expect, Test, TestCase, FocusTest } from "alsatian"; + +import * as util from "../src/util"; + +export class OverloadTests { + + @TestCase("0") + @TestCase("30") + @TestCase("30_000") + @TestCase("30.00") + @Test("typeof number") + @FocusTest + public typeofNumberTest(inp: string) { + const lua = util.transpileString(`return typeof ${inp};`); + const result = util.executeLua(lua); + + Expect(result).toBe("number"); + } + + @TestCase("\"abc\"") + @TestCase("`abc`") + @Test("typeof string") + public typeofStringTest(inp: string) { + const lua = util.transpileString(`return typeof ${inp};`); + const result = util.executeLua(lua); + + Expect(result).toBe("string"); + } + + @TestCase("false") + @TestCase("true") + @Test("typeof boolean") + public typeofBooleanTest(inp: string) { + const lua = util.transpileString(`return typeof ${inp};`); + const result = util.executeLua(lua); + + Expect(result).toBe("boolean"); + } + + @Test("{}") + @Test("[]") + @Test("typeof object literal") + public typeofObjectLiteral(inp: string) { + const lua = util.transpileString(`return typeof ${inp};`); + const result = util.executeLua(lua); + + Expect(result).toBe("table"); + } + + @Test("typeof class instance") + public typeofClassInstance() { + const lua = util.transpileString(`class myClass {} let inst = new myClass(); return typeof inst;`); + const result = util.executeLua(lua); + + Expect(result).toBe("table"); + } + + @Test("null") + @Test("undefined") + @Test("typeof undefined") + public typeofUndefinedTest(inp: string) { + const lua = util.transpileString(`return typeof ${inp};`); + const result = util.executeLua(lua); + + Expect(result).toBe("nil"); + } +} From 3a4a1e63514cf3388745b45352552b5c7fec3d76 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Fri, 29 Jun 2018 22:38:22 +0200 Subject: [PATCH 2/7] renamed test file --- test/unit/{overloads.spec.ts => typechecking.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/unit/{overloads.spec.ts => typechecking.ts} (100%) diff --git a/test/unit/overloads.spec.ts b/test/unit/typechecking.ts similarity index 100% rename from test/unit/overloads.spec.ts rename to test/unit/typechecking.ts From 63baa4f2a9f102468d08a3c39d6f33f2c827c6ff Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 30 Jun 2018 16:46:15 +0200 Subject: [PATCH 3/7] Define overload tests --- test/unit/overloads.spec.ts | 44 ++++++++++++++ .../{typechecking.ts => typechecking.spec.ts} | 57 ++++++++++++++++--- 2 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 test/unit/overloads.spec.ts rename test/unit/{typechecking.ts => typechecking.spec.ts} (50%) diff --git a/test/unit/overloads.spec.ts b/test/unit/overloads.spec.ts new file mode 100644 index 000000000..60c288d05 --- /dev/null +++ b/test/unit/overloads.spec.ts @@ -0,0 +1,44 @@ +import { Expect, Test, TestCase } from "alsatian"; + +import * as util from "../src/util"; + +export class OverloadTests { + + @Test("overload1") + public overload1() { + const lua = util.transpileString( + `function abc(def: number): string; + function abc(def: string): string; + function abc(def: number | string): string { + if (typeof def == "number") { + return "jkl" + (def * 3); + } else { + return def; + } + } + return abc(3);`); + + const result = util.executeLua(lua); + + Expect(result).toBe("jkl9"); + } + + @Test("overload2") + public overload2() { + const lua = util.transpileString( + `function abc(def: number): string; + function abc(def: string): string; + function abc(def: number | string): string { + if (typeof def == "number") { + return "jkl" + (def * 3); + } else { + return def; + } + } + return abc("ghj");`); + + const result = util.executeLua(lua); + + Expect(result).toBe("ghj"); + } +} diff --git a/test/unit/typechecking.ts b/test/unit/typechecking.spec.ts similarity index 50% rename from test/unit/typechecking.ts rename to test/unit/typechecking.spec.ts index bd1abd934..6218d0f0a 100644 --- a/test/unit/typechecking.ts +++ b/test/unit/typechecking.spec.ts @@ -1,15 +1,14 @@ -import { Expect, Test, TestCase, FocusTest } from "alsatian"; +import { Expect, Test, TestCase, FocusTests } from "alsatian"; import * as util from "../src/util"; +@FocusTests export class OverloadTests { - @TestCase("0") @TestCase("30") @TestCase("30_000") @TestCase("30.00") @Test("typeof number") - @FocusTest public typeofNumberTest(inp: string) { const lua = util.transpileString(`return typeof ${inp};`); const result = util.executeLua(lua); @@ -37,8 +36,8 @@ export class OverloadTests { Expect(result).toBe("boolean"); } - @Test("{}") - @Test("[]") + @TestCase("{}") + @TestCase("[]") @Test("typeof object literal") public typeofObjectLiteral(inp: string) { const lua = util.transpileString(`return typeof ${inp};`); @@ -55,8 +54,8 @@ export class OverloadTests { Expect(result).toBe("table"); } - @Test("null") - @Test("undefined") + @TestCase("null") + @TestCase("undefined") @Test("typeof undefined") public typeofUndefinedTest(inp: string) { const lua = util.transpileString(`return typeof ${inp};`); @@ -64,4 +63,48 @@ export class OverloadTests { Expect(result).toBe("nil"); } + + @Test("instanceof") + public instanceOf() { + const lua = util.transpileString("class myClass {} let inst = new myClass(); return inst instanceof myClass;"); + const result = util.executeLua(lua); + + Expect(result).toBeTruthy(); + } + + @Test("instanceof inheritance") + public instanceOfInheritance() { + const lua = util.transpileString("class myClass {}\n" + + "class childClass extends myClass{}\n" + + "let inst = new childClass(); return inst instanceof myClass;"); + const result = util.executeLua(lua); + + Expect(result).toBeTruthy(); + } + + @Test("instanceof inheritance false") + public instanceOfInheritanceFalse() { + const lua = util.transpileString("class myClass {}\n" + + "class childClass extends myClass{}\n" + + "let inst = new myClass(); return inst instanceof childClass;"); + const result = util.executeLua(lua); + + Expect(result).toBeTruthy(); + } + + @Test("null instanceof Object") + public nullInstanceOf() { + const lua = util.transpileString("return null instanceof Object;"); + const result = util.executeLua(lua); + + Expect(result).toBe(false); + } + + @Test("null instanceof Class") + public nullInstanceOfClass() { + const lua = util.transpileString("class myClass {} return null instanceof myClass;"); + const result = util.executeLua(lua); + + Expect(result).toBe(false); + } } From ca573cbb4ae6e51e4406d344b29eba9741ea6456 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 30 Jun 2018 17:04:05 +0200 Subject: [PATCH 4/7] Implemented typeof and instanceof --- dist/lualib/typescript.lua | 10 ++++++++++ src/Transpiler.ts | 11 +++++++++++ test/unit/typechecking.spec.ts | 5 ++--- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/dist/lualib/typescript.lua b/dist/lualib/typescript.lua index 9b8c8654d..177138e28 100644 --- a/dist/lualib/typescript.lua +++ b/dist/lualib/typescript.lua @@ -126,6 +126,16 @@ function TS_push(list, ...) end end +function TS_instanceof(obj, class) + while obj ~= nil do + if obj.__index == class then + return true + end + obj = obj.__base + end + return false +end + -- Set data structure implementation Set = Set or {} Set.__index = Set diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 24b307447..d04ccab5f 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -597,6 +597,7 @@ export abstract class LuaTranspiler { // Otherwise simply return the name return (node as ts.Identifier).text; case ts.SyntaxKind.StringLiteral: + case ts.SyntaxKind.NoSubstitutionTemplateLiteral: const text = (node as ts.StringLiteral).text; return `"${text}"`; case ts.SyntaxKind.TemplateExpression: @@ -639,6 +640,8 @@ export abstract class LuaTranspiler { 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); default: throw new TranspileError( "Unsupported expression kind: " + tsHelper.enumName(node.kind, ts.SyntaxKind), @@ -755,6 +758,9 @@ export abstract class LuaTranspiler { case ts.SyntaxKind.InKeyword: result = `${rhs}[${lhs}]~=nil`; break; + case ts.SyntaxKind.InstanceOfKeyword: + result = `TS_instanceof(${lhs}, ${rhs})`; + break; default: throw new TranspileError( "Unsupported binary operator kind: " + ts.tokenToString(node.operatorToken.kind), @@ -1124,6 +1130,11 @@ export abstract class LuaTranspiler { } } + public transpileTypeOfExpression(node: ts.TypeOfExpression): string { + const expression = this.transpileExpression(node.expression); + return `type(${expression})`; + } + // Transpile a variable statement public transpileVariableStatement(node: ts.VariableStatement): string { let result = ""; diff --git a/test/unit/typechecking.spec.ts b/test/unit/typechecking.spec.ts index 6218d0f0a..45a1bf077 100644 --- a/test/unit/typechecking.spec.ts +++ b/test/unit/typechecking.spec.ts @@ -1,8 +1,7 @@ -import { Expect, Test, TestCase, FocusTests } from "alsatian"; +import { Expect, Test, TestCase } from "alsatian"; import * as util from "../src/util"; -@FocusTests export class OverloadTests { @TestCase("0") @TestCase("30") @@ -89,7 +88,7 @@ export class OverloadTests { + "let inst = new myClass(); return inst instanceof childClass;"); const result = util.executeLua(lua); - Expect(result).toBeTruthy(); + Expect(result).toBe(false); } @Test("null instanceof Object") From 0d0f81b4349d7fb7608b408745cb7bd96df4581b Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 30 Jun 2018 17:17:37 +0200 Subject: [PATCH 5/7] Added ability to deal with overloads --- src/Transpiler.ts | 6 +++++ test/unit/expressions.spec.ts | 9 ------- test/unit/overloads.spec.ts | 51 +++++++++++++++++++++++++++++++---- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index d04ccab5f..4a2308716 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1185,6 +1185,9 @@ export abstract class LuaTranspiler { } public transpileFunctionDeclaration(node: ts.FunctionDeclaration): string { + // Don't transpile functions without body (overload declarations) + if (!node.body) { return ""; } + let result = ""; const identifier = node.name; const methodName = identifier.escapedText; @@ -1232,6 +1235,9 @@ export abstract class LuaTranspiler { } public transpileMethodDeclaration(node: ts.MethodDeclaration, callPath: string): string { + // Don't transpile methods without body (overload declarations) + if (!node.body) { return ""; } + let result = ""; const identifier = node.name as ts.Identifier; const methodName = identifier.escapedText; diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index e6a72eb17..4da293fc5 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -19,15 +19,6 @@ export class ExpressionTests { Expect(util.transpileString(input)).toBe(lua); } - @TestCase("obj instanceof someClass", "Unsupported binary operator kind: instanceof") - @TestCase("typeof obj", "Unsupported expression kind: TypeOfExpression") - @Test("Prohibted Expressions") - public prohibtedExpressions(input: string, expectedError: string) { - Expect(() => { - util.transpileString(input); - }).toThrowError(Error, expectedError); - } - @TestCase("1+1", "1+1") @TestCase("1-1", "1-1") @TestCase("1*1", "1*1") diff --git a/test/unit/overloads.spec.ts b/test/unit/overloads.spec.ts index 60c288d05..69fa96cc1 100644 --- a/test/unit/overloads.spec.ts +++ b/test/unit/overloads.spec.ts @@ -3,9 +3,8 @@ import { Expect, Test, TestCase } from "alsatian"; import * as util from "../src/util"; export class OverloadTests { - - @Test("overload1") - public overload1() { + @Test("overload function1") + public overloadFunction1() { const lua = util.transpileString( `function abc(def: number): string; function abc(def: string): string; @@ -23,8 +22,8 @@ export class OverloadTests { Expect(result).toBe("jkl9"); } - @Test("overload2") - public overload2() { + @Test("overload function2") + public overloadFunction2() { const lua = util.transpileString( `function abc(def: number): string; function abc(def: string): string; @@ -41,4 +40,46 @@ export class OverloadTests { Expect(result).toBe("ghj"); } + + @Test("overload method1") + public overloadMethod1() { + const lua = util.transpileString( + `class myclass { + static abc(def: number): string; + static abc(def: string): string; + static abc(def: number | string): string { + if (typeof def == "number") { + return "jkl" + (def * 3); + } else { + return def; + } + } + } + return myclass.abc(3);`); + + const result = util.executeLua(lua); + + Expect(result).toBe("jkl9"); + } + + @Test("overload method2") + public overloadMethod2() { + const lua = util.transpileString( + `class myclass { + static abc(def: number): string; + static abc(def: string): string; + static abc(def: number | string): string { + if (typeof def == "number") { + return "jkl" + (def * 3); + } else { + return def; + } + } + } + return myclass.abc("ghj");`); + + const result = util.executeLua(lua); + + Expect(result).toBe("ghj"); + } } From b4d4504af4f821849b110d78b0993c14832c295e Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 1 Jul 2018 10:22:45 +0200 Subject: [PATCH 6/7] Changed typeof returning object instead of table --- src/Transpiler.ts | 2 +- test/unit/typechecking.spec.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 4a2308716..f49606720 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1132,7 +1132,7 @@ export abstract class LuaTranspiler { public transpileTypeOfExpression(node: ts.TypeOfExpression): string { const expression = this.transpileExpression(node.expression); - return `type(${expression})`; + return `type(${expression}) == "table" and "object" or type(${expression})`; } // Transpile a variable statement diff --git a/test/unit/typechecking.spec.ts b/test/unit/typechecking.spec.ts index 45a1bf077..a31b899eb 100644 --- a/test/unit/typechecking.spec.ts +++ b/test/unit/typechecking.spec.ts @@ -42,7 +42,7 @@ export class OverloadTests { const lua = util.transpileString(`return typeof ${inp};`); const result = util.executeLua(lua); - Expect(result).toBe("table"); + Expect(result).toBe("object"); } @Test("typeof class instance") @@ -50,7 +50,15 @@ export class OverloadTests { const lua = util.transpileString(`class myClass {} let inst = new myClass(); return typeof inst;`); const result = util.executeLua(lua); - Expect(result).toBe("table"); + Expect(result).toBe("object"); + } + + @Test("typeof function") + public typeofFunction() { + const lua = util.transpileString(`return typeof (() => 3);`); + const result = util.executeLua(lua); + + Expect(result).toBe("function"); } @TestCase("null") From a4bb7f51ba678c785576eb630fcdf6a495568fc8 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 1 Jul 2018 10:37:43 +0200 Subject: [PATCH 7/7] Added some extra brackets for safety --- src/Transpiler.ts | 4 ++-- test/unit/expressions.spec.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index f49606720..10bf07181 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -824,7 +824,7 @@ export abstract class LuaTranspiler { case ts.SyntaxKind.MinusMinusToken: return `${operand}=${operand}-1`; case ts.SyntaxKind.ExclamationToken: - return `not ${operand}`; + return `(not ${operand})`; case ts.SyntaxKind.MinusToken: return `-${operand}`; default: @@ -1132,7 +1132,7 @@ 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})`; + return `(type(${expression}) == "table" and "object" or type(${expression}))`; } // Transpile a variable statement diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 4da293fc5..c18799d4f 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -10,7 +10,7 @@ export class ExpressionTests { @TestCase("++i", "i=i+1") @TestCase("i--", "i=i-1") @TestCase("--i", "i=i-1") - @TestCase("!a", "not a") + @TestCase("!a", "(not a)") @TestCase("-a", "-a") @TestCase("delete tbl['test']", "tbl[\"test\"]=nil") @TestCase("delete tbl.test", "tbl.test=nil")