Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/Compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,11 @@ function emitFilesAndReportErrors(program: ts.Program): number {
}

export function createTranspiler(checker: ts.TypeChecker,
options: ts.CompilerOptions,
options: CompilerOptions,
sourceFile: ts.SourceFile): LuaTranspiler {
let luaTargetTranspiler: LuaTranspiler;
switch (options.luaTarget) {
const target = options.luaTarget ? options.luaTarget.toLowerCase() : "";
switch (target) {
case LuaTarget.Lua51:
luaTargetTranspiler = new LuaTranspiler51(checker, options, sourceFile);
break;
Expand Down
3 changes: 3 additions & 0 deletions src/Errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export class TSTLErrors {
public static InvalidExtensionMetaExtension = (node: ts.Node) =>
new TranspileError(`Cannot use both '!Extension' and '!MetaExtension' decorators on the same class.`, node)

public static InvalidNewExpressionOnExtension = (node: ts.Node) =>
new TranspileError(`Cannot construct classes with decorator '!Extension' or '!MetaExtension'.`, node)

public static InvalidPropertyCall = (node: ts.Node) =>
new TranspileError(`Tried to transpile a non-property call as property call.`, node)

Expand Down
61 changes: 47 additions & 14 deletions src/Transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ export enum LuaTarget {
Lua51 = "5.1",
Lua52 = "5.2",
Lua53 = "5.3",
LuaJIT = "JIT",
LuaJIT = "jit",
}

export enum LuaLibFeature {
ArrayConcat = "ArrayConcat",
ArrayEvery = "ArrayEvery",
ArrayFilter = "ArrayFilter",
ArrayForEach = "ArrayForEach",
Expand Down Expand Up @@ -315,14 +316,25 @@ export abstract class LuaTranspiler {

const imports = node.importClause.namedBindings;

const requireKeyword = "require";

if (ts.isNamedImports(imports)) {
const fileImportTable = path.basename(importPathWithoutQuotes) + this.importCount;
const resolvedImportPath = this.getImportPath(importPathWithoutQuotes);

let result = `local ${fileImportTable} = require(${resolvedImportPath})\n`;
let result = `local ${fileImportTable} = ${requireKeyword}(${resolvedImportPath})\n`;
this.importCount++;

imports.elements.forEach(element => {
const filteredElements = imports.elements.filter(e => {
const decorators = tsHelper.getCustomDecorators(this.checker.getTypeAtLocation(e), this.checker);
return !decorators.has(DecoratorKind.Extension) && !decorators.has(DecoratorKind.MetaExtension);
});

if (filteredElements.length === 0) {
return "";
}

filteredElements.forEach(element => {
const nameText = this.transpileIdentifier(element.name);
if (element.propertyName) {
const propertyText = this.transpileIdentifier(element.propertyName);
Expand All @@ -335,7 +347,7 @@ export abstract class LuaTranspiler {
return result;
} else if (ts.isNamespaceImport(imports)) {
const resolvedImportPath = this.getImportPath(importPathWithoutQuotes);
return `local ${this.transpileIdentifier(imports.name)} = require(${resolvedImportPath})\n`;
return `local ${this.transpileIdentifier(imports.name)} = ${requireKeyword}(${resolvedImportPath})\n`;
} else {
throw TSTLErrors.UnsupportedImportType(imports);
}
Expand Down Expand Up @@ -786,7 +798,9 @@ export abstract class LuaTranspiler {
case ts.SyntaxKind.TypeOfExpression:
return this.transpileTypeOfExpression(node as ts.TypeOfExpression);
case ts.SyntaxKind.EmptyStatement:
return "";
return "";
case ts.SyntaxKind.SpreadElement:
return this.transpileSpreadElement(node as ts.SpreadElement);
default:
throw TSTLErrors.UnsupportedKind("expression", node.kind, node);
}
Expand Down Expand Up @@ -1029,6 +1043,10 @@ export abstract class LuaTranspiler {

this.checkForLuaLibType(type);

if (classDecorators.has(DecoratorKind.Extension) || classDecorators.has(DecoratorKind.MetaExtension)) {
throw TSTLErrors.InvalidNewExpressionOnExtension(node);
}

if (classDecorators.has(DecoratorKind.CustomConstructor)) {
const customDecorator = classDecorators.get(DecoratorKind.CustomConstructor);
if (!customDecorator.args[0]) {
Expand Down Expand Up @@ -1186,6 +1204,8 @@ export abstract class LuaTranspiler {
const caller = this.transpileExpression(expression.expression);
const expressionName = this.transpileIdentifier(expression.name);
switch (expressionName) {
case "concat":
return this.transpileLuaLibFunction(LuaLibFeature.ArrayConcat, caller, params);
case "push":
return this.transpileLuaLibFunction(LuaLibFeature.ArrayPush, caller, params);
case "pop":
Expand Down Expand Up @@ -1236,6 +1256,10 @@ export abstract class LuaTranspiler {
public transpilePropertyAccessExpression(node: ts.PropertyAccessExpression): string {
const property = node.name.text;

if (tsHelper.hasGetAccessor(node, this.checker)) {
return this.transpileGetAccessor(node);
}

// Check for primitive types to override
const type = this.checker.getTypeAtLocation(node.expression);
switch (type.flags) {
Expand All @@ -1245,8 +1269,6 @@ export abstract class LuaTranspiler {
case ts.TypeFlags.Object:
if (tsHelper.isArrayType(type, this.checker)) {
return this.transpileArrayProperty(node);
} else if (tsHelper.hasGetAccessor(node, this.checker)) {
return this.transpileGetAccessor(node);
}
}

Expand Down Expand Up @@ -1366,6 +1388,10 @@ 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 "__";
Expand Down Expand Up @@ -1566,13 +1592,6 @@ export abstract class LuaTranspiler {

let result = "";

if (!isExtension && !isMetaExtension) {
result += this.transpileClassCreationMethods(node, instanceFields, extendsType);
} else {
// export empty table
this.pushExport(className, node, true);
}

// Overwrite the original className with the class we are overriding for extensions
if (isMetaExtension) {
if (!extendsType) {
Expand All @@ -1592,6 +1611,20 @@ export abstract class LuaTranspiler {
}
}

if (!isExtension && !isMetaExtension) {
result += this.transpileClassCreationMethods(node, instanceFields, extendsType);
} else {
for (const f of instanceFields) {
// Get identifier
const fieldIdentifier = f.name as ts.Identifier;
const fieldName = this.transpileIdentifier(fieldIdentifier);

const value = this.transpileExpression(f.initializer);

result += this.indent + `${className}.${fieldName} = ${value}\n`;
}
}

// Add static declarations
for (const field of staticFields) {
const fieldName = this.transpileIdentifier(field.name as ts.Identifier);
Expand Down
22 changes: 22 additions & 0 deletions src/lualib/ArrayConcat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
declare function pcall(func: () => any): any;
declare function type(val: any): string;

function __TS__ArrayConcat(arr1: any[], ...args: any[]): any[] {
const out: any[] = [];
for (const val of arr1) {
out[out.length] = val;
}
for (const arg of args) {
// Hack because we don't have an isArray function
if (pcall(() => (arg as any[]).length) && type(arg) !== "string") {
const argAsArray = (arg as any[]);
for (const val of argAsArray) {
out[out.length] = val;
}
} else {
out[out.length] = arg;
}
}

return out;
}
5 changes: 5 additions & 0 deletions src/targets/Transpiler.52.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,9 @@ 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) + ")";
}
}
4 changes: 4 additions & 0 deletions test/translation/lua/classExtension4.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
MyClass.test = "test"
MyClass.testP = "testP"
function MyClass.myFunction(self)
end
4 changes: 2 additions & 2 deletions test/translation/ts/classExtension1.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** !Extension */
class MyClass {
myFunction() {}
}
public myFunction() {}
}
2 changes: 1 addition & 1 deletion test/translation/ts/classExtension2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ class TestClass {

/** !Extension */
class MyClass extends TestClass {
myFunction() {}
public myFunction() {}
}
4 changes: 2 additions & 2 deletions test/translation/ts/classExtension3.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/** !Extension RenamedTestClass */
class TestClass {
myFunction() {}
public myFunction() {}
}

/** !Extension RenamedMyClass */
class MyClass extends TestClass {
myFunction() {}
public myFunction() {}
}
6 changes: 6 additions & 0 deletions test/translation/ts/classExtension4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** !Extension */
class MyClass {
public test: string = "test";
private testP: string = "testP";
public myFunction() {}
}
18 changes: 17 additions & 1 deletion test/unit/decoratorMetaExtension.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Expect, Test, TestCase } from "alsatian";
import { Expect, Test } from "alsatian";
import * as util from "../src/util";

import { TranspileError } from "../../src/Errors";
Expand Down Expand Up @@ -44,4 +44,20 @@ export class DecoratorMetaExtension {
}).toThrowError(TranspileError,
"!MetaExtension requires the extension of the metatable class.");
}

@Test("DontAllowInstantiation")
public dontAllowInstantiation(): void {
Expect(() => {
util.transpileString(
`
declare class _LOADED;
/** !MetaExtension */
class Ext extends _LOADED {
}
const e = new Ext();
`
);
}).toThrowError(TranspileError,
"Cannot construct classes with decorator '!Extension' or '!MetaExtension'.");
}
}
4 changes: 2 additions & 2 deletions test/unit/expressions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ export class ExpressionTests {
const identifier = ts.createIdentifier("fromCodePoint");
Expect(() => transpiler.transpileStringExpression(identifier))
.toThrowError(TranspileError, "string property fromCodePoint is/are not supported " +
"for target Lua JIT.");
"for target Lua jit.");
}

@Test("Unknown string expression error")
Expand All @@ -331,7 +331,7 @@ export class ExpressionTests {

const identifier = ts.createIdentifier("abcd");
Expect(() => transpiler.transpileStringExpression(identifier))
.toThrowError(TranspileError, "string property abcd is/are not supported for target Lua JIT.");
.toThrowError(TranspileError, "string property abcd is/are not supported for target Lua jit.");
}

@Test("Unsupported array function error")
Expand Down
29 changes: 28 additions & 1 deletion test/unit/lualib/lualib.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,33 @@ export class LuaLibArrayTests {
}
}

@TestCase([], [])
@TestCase([1, 2, 3], [])
@TestCase([1, 2, 3], [4])
@TestCase([1, 2, 3], [4, 5])
@TestCase([1, 2, 3], [4, 5])
@TestCase([1, 2, 3], 4, [5])
@TestCase([1, 2, 3], 4, [5, 6])
@TestCase([1, 2, 3], 4, [5, 6], 7)
@TestCase([1, 2, 3], "test", [5, 6], 7, ["test1", "test2"])
@TestCase([1, 2, "test"], "test", ["test1", "test2"])
@Test("array.concat")
public concat<T>(arr: T[], ...args: T[]) {
const argStr = args.map(arg => JSON.stringify(arg)).join(",");
// Transpile
const lua = util.transpileString(
`let concatTestTable = ${JSON.stringify(arr)};
return JSONStringify(concatTestTable.concat(${argStr}));`
);

// Execute
const result = util.executeLua(lua);

// Assert
const concatArr = arr.concat(...args);
Expect(result).toBe(JSON.stringify(concatArr));
}

@TestCase([], "")
@TestCase(["test1"], "test1")
@TestCase(["test1", "test2"], "test1,test2")
Expand Down Expand Up @@ -308,7 +335,7 @@ export class LuaLibArrayTests {
// Assert
Expect(result).toBe(expected);
}

@TestCase("true", 11)
@TestCase("false", 13)
@TestCase("a < 4", 13)
Expand Down
24 changes: 24 additions & 0 deletions test/unit/spreadElement.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Expect, Test, TestCase } from "alsatian";

import { LuaTarget } from "../../src/Transpiler";
import * as util from "../src/util";

export class SpreadElementTest {

@TestCase([])
@TestCase([1, 2, 3])
@TestCase([1, "test", 3])
@Test("Spread Element Push")
public spreadElementPush(inp: any[]) {
const lua = util.transpileString(`return JSONStringify([].push(...${JSON.stringify(inp)}));`);
const result = util.executeLua(lua);
Expect(result).toBe([].push(...inp));
}

@Test("Spread Element Lua 5.1")
public spreadElement51() {
// Cant test functional because our VM doesn't run on 5.1
const lua = util.transpileString(`[].push(...${JSON.stringify([1, 2, 3])});`, {luaTarget: LuaTarget.Lua51});
Expect(lua).toBe("__TS__ArrayPush({}, unpack({1,2,3}));");
}
}
6 changes: 3 additions & 3 deletions tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
"interface-name": false,
"radix": false,
"typedef": [
true,
"call-signature",
"property-declaration"
true,
"call-signature",
"property-declaration"
]
},
"rulesDirectory": []
Expand Down