X Tutup
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
1 change: 1 addition & 0 deletions src/Decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ export enum DecoratorKind {
Phantom = "Phantom",
TupleReturn = "TupleReturn",
NoClassOr = "NoClassOr",
NoContext = "NoContext",
}
38 changes: 25 additions & 13 deletions src/TSHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ export class TSHelper {
return typeNode && this.isArrayTypeNode(typeNode);
}

public static isFunctionType(type: ts.Type, checker: ts.TypeChecker): boolean {
const typeNode = checker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.InTypeAlias);
return typeNode && ts.isFunctionTypeNode(typeNode);
}

public static isTupleReturnCall(node: ts.Node, checker: ts.TypeChecker): boolean {
if (ts.isCallExpression(node)) {
const type = checker.getTypeAtLocation(node.expression);
Expand All @@ -104,22 +109,29 @@ export class TSHelper {
}
}

public static collectCustomDecorators(symbol: ts.Symbol, checker: ts.TypeChecker,
decMap: Map<DecoratorKind, Decorator>): void {
const comments = symbol.getDocumentationComment(checker);
const decorators =
comments.filter(comment => comment.kind === "text")
.map(comment => comment.text.trim().split("\n"))
.reduce((a, b) => a.concat(b), [])
.filter(comment => comment[0] === "!");
decorators.forEach(decStr => {
const dec = new Decorator(decStr);
decMap.set(dec.kind, dec);
});
}

public static getCustomDecorators(type: ts.Type, checker: ts.TypeChecker): Map<DecoratorKind, Decorator> {
const decMap = new Map<DecoratorKind, Decorator>();
if (type.symbol) {
const comments = type.symbol.getDocumentationComment(checker);
const decorators =
comments.filter(comment => comment.kind === "text")
.map(comment => comment.text.trim().split("\n"))
.reduce((a, b) => a.concat(b), [])
.filter(comment => comment[0] === "!");
const decMap = new Map<DecoratorKind, Decorator>();
decorators.forEach(decStr => {
const dec = new Decorator(decStr);
decMap.set(dec.kind, dec);
});
return decMap;
this.collectCustomDecorators(type.symbol, checker, decMap);
}
if (type.aliasSymbol) {
this.collectCustomDecorators(type.aliasSymbol, checker, decMap);
}
return new Map<DecoratorKind, Decorator>();
return decMap;
}

// Search up until finding a node satisfying the callback
Expand Down
113 changes: 68 additions & 45 deletions src/Transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export enum LuaLibFeature {
ArraySlice = "ArraySlice",
ArraySome = "ArraySome",
ArraySplice = "ArraySplice",
FunctionApply = "FunctionApply",
FunctionBind = "FunctionBind",
FunctionCall = "FunctionCall",
InstanceOf = "InstanceOf",
Map = "Map",
Set = "Set",
Expand Down Expand Up @@ -784,8 +787,9 @@ export abstract class LuaTranspiler {
case ts.SyntaxKind.DeleteExpression:
return this.transpileExpression((node as ts.DeleteExpression).expression) + "=nil";
case ts.SyntaxKind.FunctionExpression:
return this.transpileFunctionExpression(node as ts.ArrowFunction, "self");
case ts.SyntaxKind.ArrowFunction:
return this.transpileFunctionExpression(node as ts.ArrowFunction);
return this.transpileFunctionExpression(node as ts.ArrowFunction, "_");
case ts.SyntaxKind.NewExpression:
return this.transpileNewExpression(node as ts.NewExpression);
case ts.SyntaxKind.ComputedPropertyName:
Expand Down Expand Up @@ -1136,7 +1140,7 @@ export abstract class LuaTranspiler {

public transpileNewExpression(node: ts.NewExpression): string {
const name = this.transpileExpression(node.expression);
const params = node.arguments ? this.transpileArguments(node.arguments, ts.createTrue()) : "true";
let params = node.arguments ? this.transpileArguments(node.arguments, ts.createTrue()) : "true";
const type = this.checker.getTypeAtLocation(node);
const classDecorators = tsHelper.getCustomDecorators(type, this.checker);

Expand All @@ -1151,7 +1155,14 @@ export abstract class LuaTranspiler {
if (!customDecorator.args[0]) {
throw TSTLErrors.InvalidDecoratorArgumentNumber("!CustomConstructor", 0, 1, node);
}
return `${customDecorator.args[0]}(${this.transpileArguments(node.arguments)})`;
if (!ts.isPropertyAccessExpression(node.expression)
&& !ts.isElementAccessExpression(node.expression)
&& !tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext)) {
params = this.transpileArguments(node.arguments, ts.createIdentifier("_G"));
} else {
params = this.transpileArguments(node.arguments);
}
return `${customDecorator.args[0]}(${params})`;
}

return `${name}.new(${params})`;
Expand Down Expand Up @@ -1182,7 +1193,14 @@ export abstract class LuaTranspiler {
}

callPath = this.transpileExpression(node.expression);
params = this.transpileArguments(node.arguments);
const type = this.checker.getTypeAtLocation(node.expression);
if (!ts.isPropertyAccessExpression(node.expression)
&& !ts.isElementAccessExpression(node.expression)
&& !tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext)) {
params = this.transpileArguments(node.arguments, ts.createIdentifier("_G"));
} else {
params = this.transpileArguments(node.arguments);
}
return isTupleReturn && !isTupleReturnForward && !isInDestructingAssignment && returnValueIsUsed
? `({ ${callPath}(${params}) })` : `${callPath}(${params})`;
}
Expand Down Expand Up @@ -1220,16 +1238,12 @@ export abstract class LuaTranspiler {
return this.transpileArrayCallExpression(node);
}

if (tsHelper.isFunctionType(ownerType, this.checker)) {
return this.transpileFunctionCallExpression(node);
}

// Get the type of the function
const functionType = this.checker.getTypeAtLocation(node.expression);
// Don't replace . with : for namespaces or functions defined as properties with lambdas
if ((functionType.symbol && !(functionType.symbol.flags & ts.SymbolFlags.Method))
// Check explicitly for method calls on 'this', since they don't have the Method flag set
|| (node.expression.expression.kind === ts.SyntaxKind.ThisType)) {
callPath = this.transpileExpression(node.expression);
params = this.transpileArguments(node.arguments);
return `${callPath}(${params})`;
} else if (node.expression.expression.kind === ts.SyntaxKind.SuperKeyword) {
if (node.expression.expression.kind === ts.SyntaxKind.SuperKeyword) {
// Super calls take the format of super.call(self,...)
params = this.transpileArguments(node.arguments, ts.createNode(ts.SyntaxKind.ThisKeyword) as ts.Expression);
return `${this.transpileExpression(node.expression)}(${params})`;
Expand All @@ -1243,8 +1257,9 @@ export abstract class LuaTranspiler {
params = this.transpileArguments(node.arguments);
return `(rawget(${expr}, ${params} )~=nil)`;
} else {
callPath =
`${this.transpileExpression(node.expression.expression)}:${name}`;
const type = this.checker.getTypeAtLocation(node.expression);
const op = tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext) ? "." : ":";
callPath = `${this.transpileExpression(node.expression.expression)}${op}${name}`;
params = this.transpileArguments(node.arguments);
return `${callPath}(${params})`;
}
Expand Down Expand Up @@ -1364,6 +1379,23 @@ export abstract class LuaTranspiler {
}
}

public transpileFunctionCallExpression(node: ts.CallExpression): string {
const expression = node.expression as ts.PropertyAccessExpression;
const params = this.transpileArguments(node.arguments);
const caller = this.transpileExpression(expression.expression);
const expressionName = this.transpileIdentifier(expression.name);
switch (expressionName) {
case "apply":
return this.transpileLuaLibFunction(LuaLibFeature.FunctionApply, caller, params);
case "bind":
return this.transpileLuaLibFunction(LuaLibFeature.FunctionBind, caller, params);
case "call":
return this.transpileLuaLibFunction(LuaLibFeature.FunctionCall, caller, params);
default:
throw TSTLErrors.UnsupportedProperty("function", expressionName, node);
}
}

public transpileArguments(params: ts.NodeArray<ts.Expression>, context?: ts.Expression): string {
const parameters: string[] = [];

Expand Down Expand Up @@ -1596,7 +1628,9 @@ export abstract class LuaTranspiler {
let result = "";
const methodName = this.transpileIdentifier(node.name);

const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters);
const type = this.checker.getTypeAtLocation(node);
const context = tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext) ? null : "self";
const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters, context);

let prefix = this.accessPrefix(node);

Expand All @@ -1620,9 +1654,13 @@ export abstract class LuaTranspiler {
}

// Transpile a list of parameters, returns a list of transpiled parameters and an optional spread identifier
public transpileParameters(parameters: ts.NodeArray<ts.ParameterDeclaration>): [string[], string] {
public transpileParameters(parameters: ts.NodeArray<ts.ParameterDeclaration>, context: string | null)
: [string[], string] {
// Build parameter string
const paramNames: string[] = [];
if (context) {
paramNames.push(context);
}

let spreadIdentifier = "";

Expand Down Expand Up @@ -1673,12 +1711,12 @@ export abstract class LuaTranspiler {
methodName = "__tostring";
}

const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters);

const selfParamNames = ["self"].concat(paramNames);
const type = this.checker.getTypeAtLocation(node);
const context = tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext) ? null : "self";
const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters, context);

// Build function header
result += this.indent + `function ${callPath}${methodName}(${selfParamNames.join(",")})\n`;
result += this.indent + `function ${callPath}${methodName}(${paramNames.join(",")})\n`;

this.pushIndent();
result += this.transpileFunctionBody(node.parameters, node.body, spreadIdentifier);
Expand Down Expand Up @@ -1932,7 +1970,7 @@ export abstract class LuaTranspiler {
} else if (ts.isShorthandPropertyAssignment(element)) {
properties.push(`${name} = ${name}`);
} else if (ts.isMethodDeclaration(element)) {
const expression = this.transpileFunctionExpression(element);
const expression = this.transpileFunctionExpression(element, "self");
properties.push(`${name} = ${expression}`);
} else {
throw TSTLErrors.UnsupportedKind("object literal element", element.kind, node);
Expand All @@ -1942,30 +1980,15 @@ export abstract class LuaTranspiler {
return "{" + properties.join(",") + "}";
}

public transpileFunctionExpression(node: ts.FunctionLikeDeclaration): string {
public transpileFunctionExpression(node: ts.FunctionLikeDeclaration, context: string | null): string {
// Build parameter string
const paramNames: string[] = [];
if (ts.isMethodDeclaration(node)) {
paramNames.push("self");
}
node.parameters.forEach(param => {
paramNames.push(this.transpileIdentifier(param.name as ts.Identifier));
});

const defaultValueParams = node.parameters.filter(declaration => declaration.initializer !== undefined);

if (ts.isBlock(node.body) || defaultValueParams.length > 0) {
let result = `function(${paramNames.join(",")})\n`;
this.pushIndent();
result += this.transpileParameterDefaultValues(defaultValueParams);
result += this.transpileBlock(node.body as ts.Block);
this.popIndent();
return result + this.indent + "end\n";
} else {
// Transpile as return value
const returnVal = this.transpileReturn(ts.createReturn(node.body));
return `function(${paramNames.join(",")}) ${returnVal} end`;
}
const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters, context);
let result = `function(${paramNames.join(",")})\n`;
this.pushIndent();
const body = ts.isBlock(node.body) ? node.body : ts.createBlock([ts.createReturn(node.body)]);
result += this.transpileFunctionBody(node.parameters, body, spreadIdentifier);
this.popIndent();
return result + this.indent + "end";
}

public transpileParameterDefaultValues(params: ts.ParameterDeclaration[]): string {
Expand Down
3 changes: 3 additions & 0 deletions src/lualib/ArrayConcat.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/** !NoContext */
declare function pcall(func: () => any): any;
/** !NoContext */
declare function type(val: any): string;

/** !NoContext */
function __TS__ArrayConcat(arr1: any[], ...args: any[]): any[] {
const out: any[] = [];
for (const val of arr1) {
Expand Down
1 change: 1 addition & 0 deletions src/lualib/ArrayEvery.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** !NoContext */
function __TS__ArrayEvery<T>(arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean): boolean {
for (let i = 0; i < arr.length; i++) {
if (!callbackfn(arr[i], i, arr)) {
Expand Down
1 change: 1 addition & 0 deletions src/lualib/ArrayFilter.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** !NoContext */
function __TS__ArrayFilter<T>(arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean): T[] {
const result: T[] = [];
for (let i = 0; i < arr.length; i++) {
Expand Down
1 change: 1 addition & 0 deletions src/lualib/ArrayForEach.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** !NoContext */
function __TS__ArrayForEach<T>(arr: T[], callbackFn: (value: T, index?: number, array?: any[]) => any): void {
for (let i = 0; i < arr.length; i++) {
callbackFn(arr[i], i, arr);
Expand Down
1 change: 1 addition & 0 deletions src/lualib/ArrayIndexOf.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** !NoContext */
function __TS__ArrayIndexOf<T>(arr: T[], searchElement: T, fromIndex?: number): number {
const len = arr.length;
if (len === 0) {
Expand Down
1 change: 1 addition & 0 deletions src/lualib/ArrayMap.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** !NoContext */
function __TS__ArrayMap<T, U>(arr: T[], callbackfn: (value: T, index?: number, array?: T[]) => U): U[] {
const newArray: U[] = [];
for (let i = 0; i < arr.length; i++) {
Expand Down
1 change: 1 addition & 0 deletions src/lualib/ArrayPush.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** !NoContext */
function __TS__ArrayPush<T>(arr: T[], ...items: T[]): number {
for (const item of items) {
arr[arr.length] = item;
Expand Down
1 change: 1 addition & 0 deletions src/lualib/ArrayReverse.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** !NoContext */
function __TS__ArrayReverse(arr: any[]): any[] {
let i = 0;
let j = arr.length - 1;
Expand Down
2 changes: 2 additions & 0 deletions src/lualib/ArrayShift.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
declare namespace table {
/** !NoContext */
function remove<T>(arr: T[], idx: number): T;
}
/** !NoContext */
function __TS__ArrayShift<T>(arr: T[]): T {
return table.remove(arr, 1);
}
1 change: 1 addition & 0 deletions src/lualib/ArraySlice.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// https://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf 22.1.3.23
/** !NoContext */
function __TS__ArraySlice<T>(list: T[], first: number, last: number): T[] {
const len = list.length;

Expand Down
1 change: 1 addition & 0 deletions src/lualib/ArraySome.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** !NoContext */
function __TS__ArraySome<T>(arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean): boolean {
for (let i = 0; i < arr.length; i++) {
if (callbackfn(arr[i], i, arr)) {
Expand Down
2 changes: 2 additions & 0 deletions src/lualib/ArraySort.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
declare namespace table {
/** !NoContext */
function sort<T>(arr: T[], compareFn?: (a: T, b: T) => number): void;
}
/** !NoContext */
function __TS__ArraySort<T>(arr: T[], compareFn?: (a: T, b: T) => number): T[] {
table.sort(arr, compareFn);
return arr;
Expand Down
1 change: 1 addition & 0 deletions src/lualib/ArraySplice.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** !NoContext */
function __TS__ArraySplice<T>(list: T[], start: number, deleteCount: number, ...items: T[]): T[] {

const len = list.length;
Expand Down
2 changes: 2 additions & 0 deletions src/lualib/ArrayUnshift.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
declare namespace table {
/** !NoContext */
function insert<T>(arr: T[], idx: number, val: T): void;
}
/** !NoContext */
function __TS__ArrayUnshift<T>(arr: T[], ...items: T[]): number {
for (let i = items.length - 1; i >= 0; --i) {
table.insert(arr, 1, items[i]);
Expand Down
19 changes: 19 additions & 0 deletions src/lualib/FunctionApply.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/** !NoContext */
declare function unpack<T>(list: T[], i?: number, j?: number): T[];

declare namespace table {
/** !NoContext */
export function unpack<T>(list: T[], i?: number, j?: number): T[];
}

/** !NoContext */
type ApplyFn = (...argArray: any[]) => any;

/** !NoContext */
function __TS__FunctionApply(fn: ApplyFn, thisArg: any, argsArray?: any[]): any {
if (argsArray) {
return fn(thisArg, (unpack || table.unpack)(argsArray));
} else {
return fn(thisArg);
}
}
Loading
X Tutup