Skip to content

Commit 714954a

Browse files
authored
Merge pull request #128 from Perryvw/feature/overloads
Implemented typeof, instanceof and overloads
2 parents 29ae00e + a4bb7f5 commit 714954a

File tree

5 files changed

+231
-11
lines changed

5 files changed

+231
-11
lines changed

dist/lualib/typescript.lua

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,16 @@ function TS_push(list, ...)
126126
end
127127
end
128128

129+
function TS_instanceof(obj, class)
130+
while obj ~= nil do
131+
if obj.__index == class then
132+
return true
133+
end
134+
obj = obj.__base
135+
end
136+
return false
137+
end
138+
129139
-- Set data structure implementation
130140
Set = Set or {}
131141
Set.__index = Set

src/Transpiler.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,7 @@ export abstract class LuaTranspiler {
597597
// Otherwise simply return the name
598598
return (node as ts.Identifier).text;
599599
case ts.SyntaxKind.StringLiteral:
600+
case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
600601
const text = (node as ts.StringLiteral).text;
601602
return `"${text}"`;
602603
case ts.SyntaxKind.TemplateExpression:
@@ -639,6 +640,8 @@ export abstract class LuaTranspiler {
639640
case ts.SyntaxKind.AsExpression:
640641
// Also ignore as casts
641642
return this.transpileExpression((node as ts.AsExpression).expression);
643+
case ts.SyntaxKind.TypeOfExpression:
644+
return this.transpileTypeOfExpression(node as ts.TypeOfExpression);
642645
default:
643646
throw new TranspileError(
644647
"Unsupported expression kind: " + tsHelper.enumName(node.kind, ts.SyntaxKind),
@@ -755,6 +758,9 @@ export abstract class LuaTranspiler {
755758
case ts.SyntaxKind.InKeyword:
756759
result = `${rhs}[${lhs}]~=nil`;
757760
break;
761+
case ts.SyntaxKind.InstanceOfKeyword:
762+
result = `TS_instanceof(${lhs}, ${rhs})`;
763+
break;
758764
default:
759765
throw new TranspileError(
760766
"Unsupported binary operator kind: " + ts.tokenToString(node.operatorToken.kind),
@@ -818,7 +824,7 @@ export abstract class LuaTranspiler {
818824
case ts.SyntaxKind.MinusMinusToken:
819825
return `${operand}=${operand}-1`;
820826
case ts.SyntaxKind.ExclamationToken:
821-
return `not ${operand}`;
827+
return `(not ${operand})`;
822828
case ts.SyntaxKind.MinusToken:
823829
return `-${operand}`;
824830
default:
@@ -1124,6 +1130,11 @@ export abstract class LuaTranspiler {
11241130
}
11251131
}
11261132

1133+
public transpileTypeOfExpression(node: ts.TypeOfExpression): string {
1134+
const expression = this.transpileExpression(node.expression);
1135+
return `(type(${expression}) == "table" and "object" or type(${expression}))`;
1136+
}
1137+
11271138
// Transpile a variable statement
11281139
public transpileVariableStatement(node: ts.VariableStatement): string {
11291140
let result = "";
@@ -1174,6 +1185,9 @@ export abstract class LuaTranspiler {
11741185
}
11751186

11761187
public transpileFunctionDeclaration(node: ts.FunctionDeclaration): string {
1188+
// Don't transpile functions without body (overload declarations)
1189+
if (!node.body) { return ""; }
1190+
11771191
let result = "";
11781192
const identifier = node.name;
11791193
const methodName = identifier.escapedText;
@@ -1221,6 +1235,9 @@ export abstract class LuaTranspiler {
12211235
}
12221236

12231237
public transpileMethodDeclaration(node: ts.MethodDeclaration, callPath: string): string {
1238+
// Don't transpile methods without body (overload declarations)
1239+
if (!node.body) { return ""; }
1240+
12241241
let result = "";
12251242
const identifier = node.name as ts.Identifier;
12261243
const methodName = identifier.escapedText;

test/unit/expressions.spec.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export class ExpressionTests {
1010
@TestCase("++i", "i=i+1")
1111
@TestCase("i--", "i=i-1")
1212
@TestCase("--i", "i=i-1")
13-
@TestCase("!a", "not a")
13+
@TestCase("!a", "(not a)")
1414
@TestCase("-a", "-a")
1515
@TestCase("delete tbl['test']", "tbl[\"test\"]=nil")
1616
@TestCase("delete tbl.test", "tbl.test=nil")
@@ -19,15 +19,6 @@ export class ExpressionTests {
1919
Expect(util.transpileString(input)).toBe(lua);
2020
}
2121

22-
@TestCase("obj instanceof someClass", "Unsupported binary operator kind: instanceof")
23-
@TestCase("typeof obj", "Unsupported expression kind: TypeOfExpression")
24-
@Test("Prohibted Expressions")
25-
public prohibtedExpressions(input: string, expectedError: string) {
26-
Expect(() => {
27-
util.transpileString(input);
28-
}).toThrowError(Error, expectedError);
29-
}
30-
3122
@TestCase("1+1", "1+1")
3223
@TestCase("1-1", "1-1")
3324
@TestCase("1*1", "1*1")

test/unit/overloads.spec.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { Expect, Test, TestCase } from "alsatian";
2+
3+
import * as util from "../src/util";
4+
5+
export class OverloadTests {
6+
@Test("overload function1")
7+
public overloadFunction1() {
8+
const lua = util.transpileString(
9+
`function abc(def: number): string;
10+
function abc(def: string): string;
11+
function abc(def: number | string): string {
12+
if (typeof def == "number") {
13+
return "jkl" + (def * 3);
14+
} else {
15+
return def;
16+
}
17+
}
18+
return abc(3);`);
19+
20+
const result = util.executeLua(lua);
21+
22+
Expect(result).toBe("jkl9");
23+
}
24+
25+
@Test("overload function2")
26+
public overloadFunction2() {
27+
const lua = util.transpileString(
28+
`function abc(def: number): string;
29+
function abc(def: string): string;
30+
function abc(def: number | string): string {
31+
if (typeof def == "number") {
32+
return "jkl" + (def * 3);
33+
} else {
34+
return def;
35+
}
36+
}
37+
return abc("ghj");`);
38+
39+
const result = util.executeLua(lua);
40+
41+
Expect(result).toBe("ghj");
42+
}
43+
44+
@Test("overload method1")
45+
public overloadMethod1() {
46+
const lua = util.transpileString(
47+
`class myclass {
48+
static abc(def: number): string;
49+
static abc(def: string): string;
50+
static abc(def: number | string): string {
51+
if (typeof def == "number") {
52+
return "jkl" + (def * 3);
53+
} else {
54+
return def;
55+
}
56+
}
57+
}
58+
return myclass.abc(3);`);
59+
60+
const result = util.executeLua(lua);
61+
62+
Expect(result).toBe("jkl9");
63+
}
64+
65+
@Test("overload method2")
66+
public overloadMethod2() {
67+
const lua = util.transpileString(
68+
`class myclass {
69+
static abc(def: number): string;
70+
static abc(def: string): string;
71+
static abc(def: number | string): string {
72+
if (typeof def == "number") {
73+
return "jkl" + (def * 3);
74+
} else {
75+
return def;
76+
}
77+
}
78+
}
79+
return myclass.abc("ghj");`);
80+
81+
const result = util.executeLua(lua);
82+
83+
Expect(result).toBe("ghj");
84+
}
85+
}

test/unit/typechecking.spec.ts

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { Expect, Test, TestCase } from "alsatian";
2+
3+
import * as util from "../src/util";
4+
5+
export class OverloadTests {
6+
@TestCase("0")
7+
@TestCase("30")
8+
@TestCase("30_000")
9+
@TestCase("30.00")
10+
@Test("typeof number")
11+
public typeofNumberTest(inp: string) {
12+
const lua = util.transpileString(`return typeof ${inp};`);
13+
const result = util.executeLua(lua);
14+
15+
Expect(result).toBe("number");
16+
}
17+
18+
@TestCase("\"abc\"")
19+
@TestCase("`abc`")
20+
@Test("typeof string")
21+
public typeofStringTest(inp: string) {
22+
const lua = util.transpileString(`return typeof ${inp};`);
23+
const result = util.executeLua(lua);
24+
25+
Expect(result).toBe("string");
26+
}
27+
28+
@TestCase("false")
29+
@TestCase("true")
30+
@Test("typeof boolean")
31+
public typeofBooleanTest(inp: string) {
32+
const lua = util.transpileString(`return typeof ${inp};`);
33+
const result = util.executeLua(lua);
34+
35+
Expect(result).toBe("boolean");
36+
}
37+
38+
@TestCase("{}")
39+
@TestCase("[]")
40+
@Test("typeof object literal")
41+
public typeofObjectLiteral(inp: string) {
42+
const lua = util.transpileString(`return typeof ${inp};`);
43+
const result = util.executeLua(lua);
44+
45+
Expect(result).toBe("object");
46+
}
47+
48+
@Test("typeof class instance")
49+
public typeofClassInstance() {
50+
const lua = util.transpileString(`class myClass {} let inst = new myClass(); return typeof inst;`);
51+
const result = util.executeLua(lua);
52+
53+
Expect(result).toBe("object");
54+
}
55+
56+
@Test("typeof function")
57+
public typeofFunction() {
58+
const lua = util.transpileString(`return typeof (() => 3);`);
59+
const result = util.executeLua(lua);
60+
61+
Expect(result).toBe("function");
62+
}
63+
64+
@TestCase("null")
65+
@TestCase("undefined")
66+
@Test("typeof undefined")
67+
public typeofUndefinedTest(inp: string) {
68+
const lua = util.transpileString(`return typeof ${inp};`);
69+
const result = util.executeLua(lua);
70+
71+
Expect(result).toBe("nil");
72+
}
73+
74+
@Test("instanceof")
75+
public instanceOf() {
76+
const lua = util.transpileString("class myClass {} let inst = new myClass(); return inst instanceof myClass;");
77+
const result = util.executeLua(lua);
78+
79+
Expect(result).toBeTruthy();
80+
}
81+
82+
@Test("instanceof inheritance")
83+
public instanceOfInheritance() {
84+
const lua = util.transpileString("class myClass {}\n"
85+
+ "class childClass extends myClass{}\n"
86+
+ "let inst = new childClass(); return inst instanceof myClass;");
87+
const result = util.executeLua(lua);
88+
89+
Expect(result).toBeTruthy();
90+
}
91+
92+
@Test("instanceof inheritance false")
93+
public instanceOfInheritanceFalse() {
94+
const lua = util.transpileString("class myClass {}\n"
95+
+ "class childClass extends myClass{}\n"
96+
+ "let inst = new myClass(); return inst instanceof childClass;");
97+
const result = util.executeLua(lua);
98+
99+
Expect(result).toBe(false);
100+
}
101+
102+
@Test("null instanceof Object")
103+
public nullInstanceOf() {
104+
const lua = util.transpileString("return null instanceof Object;");
105+
const result = util.executeLua(lua);
106+
107+
Expect(result).toBe(false);
108+
}
109+
110+
@Test("null instanceof Class")
111+
public nullInstanceOfClass() {
112+
const lua = util.transpileString("class myClass {} return null instanceof myClass;");
113+
const result = util.executeLua(lua);
114+
115+
Expect(result).toBe(false);
116+
}
117+
}

0 commit comments

Comments
 (0)