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
26 changes: 26 additions & 0 deletions src/TSHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,30 @@ export class TSHelper {
}
return null;
}

public static hasGetAccessor(node: ts.Node, checker: ts.TypeChecker): boolean {
if (ts.isPropertyAccessExpression(node)) {
const name = node.name.escapedText;
const type = checker.getTypeAtLocation(node.expression);

if (type && type.symbol && type.symbol.members) {
const field = type.symbol.members.get(name);
return field && (field.flags & ts.SymbolFlags.GetAccessor) !== 0;
}
}
return false;
}

public static hasSetAccessor(node: ts.Node, checker: ts.TypeChecker): boolean {
if (ts.isPropertyAccessExpression(node)) {
const name = node.name.escapedText;
const type = checker.getTypeAtLocation(node.expression);

if (type && type.symbol && type.symbol.members) {
const field = type.symbol.members.get(name);
return field && (field.flags & ts.SymbolFlags.SetAccessor) !== 0;
}
}
return false;
}
}
115 changes: 111 additions & 4 deletions src/Transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ export class TranspileError extends Error {
}
}

export enum Target {
export enum LuaTarget {
Lua53 = "5.3",
LuaJIT = "JIT",
}

export class LuaTranspiler {
public static AvailableLuaTargets = [Target.LuaJIT, Target.Lua53];
public static AvailableLuaTargets = [LuaTarget.LuaJIT, LuaTarget.Lua53];

// Transpile a source file
public static transpileSourceFile(node: ts.SourceFile,
Expand Down Expand Up @@ -627,36 +627,56 @@ export class LuaTranspiler {
let result = "";

// Transpile Bitops
if (this.options.luaTarget === Target.LuaJIT) {
if (this.options.luaTarget === LuaTarget.LuaJIT) {
switch (node.operatorToken.kind) {
case ts.SyntaxKind.AmpersandToken:
result = `bit.band(${lhs},${rhs})`;
break;
case ts.SyntaxKind.AmpersandEqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression,
`bit.band(${lhs},${rhs})`);
}
result = `${lhs}=bit.band(${lhs},${rhs})`;
break;
case ts.SyntaxKind.BarToken:
result = `bit.bor(${lhs},${rhs})`;
break;
case ts.SyntaxKind.BarEqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression,
`bit.bor(${lhs},${rhs})`);
}
result = `${lhs}=bit.bor(${lhs},${rhs})`;
break;
case ts.SyntaxKind.LessThanLessThanToken:
result = `bit.lshift(${lhs},${rhs})`;
break;
case ts.SyntaxKind.LessThanLessThanEqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression,
`bit.lshift(${lhs},${rhs})`);
}
result = `${lhs}=bit.lshift(${lhs},${rhs})`;
break;
case ts.SyntaxKind.GreaterThanGreaterThanToken:
result = `bit.arshift(${lhs},${rhs})`;
break;
case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression,
`bit.arshift(${lhs},${rhs})`);
}
result = `${lhs}=bit.arshift(${lhs},${rhs})`;
break;
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
result = `bit.rshift(${lhs},${rhs})`;
break;
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression,
`bit.rshift(${lhs},${rhs})`);
}
result = `${lhs}=bit.rshift(${lhs},${rhs})`;
break;
}
Expand All @@ -666,30 +686,45 @@ export class LuaTranspiler {
result = `${lhs}&${rhs}`;
break;
case ts.SyntaxKind.AmpersandEqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}&${rhs}`);
}
result = `${lhs}=${lhs}&${rhs}`;
break;
case ts.SyntaxKind.BarToken:
result = `${lhs}|${rhs}`;
break;
case ts.SyntaxKind.BarEqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}|${rhs}`);
}
result = `${lhs}=${lhs}|${rhs}`;
break;
case ts.SyntaxKind.LessThanLessThanToken:
result = `${lhs}<<${rhs}`;
break;
case ts.SyntaxKind.LessThanLessThanEqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}<<${rhs}`);
}
result = `${lhs}=${lhs}<<${rhs}`;
break;
case ts.SyntaxKind.GreaterThanGreaterThanToken:
result = `${lhs}>>${rhs}`;
break;
case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}>>${rhs}`);
}
result = `${lhs}=${lhs}>>${rhs}`;
break;
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
result = `${lhs}>>>${rhs}`;
break;
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}>>>${rhs}`);
}
result = `${lhs}=${lhs}>>>${rhs}`;
break;
}
Expand All @@ -699,15 +734,27 @@ export class LuaTranspiler {
if (result === "") {
switch (node.operatorToken.kind) {
case ts.SyntaxKind.PlusEqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}+${rhs}`);
}
result = `${lhs}=${lhs}+${rhs}`;
break;
case ts.SyntaxKind.MinusEqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}-${rhs}`);
}
result = `${lhs}=${lhs}-${rhs}`;
break;
case ts.SyntaxKind.AsteriskEqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}*${rhs}`);
}
result = `${lhs}=${lhs}*${rhs}`;
break;
case ts.SyntaxKind.SlashEqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}/${rhs}`);
}
result = `${lhs}=${lhs}/${rhs}`;
break;
case ts.SyntaxKind.AmpersandAmpersandToken:
Expand Down Expand Up @@ -751,6 +798,9 @@ export class LuaTranspiler {
result = `${lhs}<=${rhs}`;
break;
case ts.SyntaxKind.EqualsToken:
if (tsEx.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, rhs);
}
result = `${lhs}=${rhs}`;
break;
case ts.SyntaxKind.EqualsEqualsToken:
Expand Down Expand Up @@ -931,7 +981,7 @@ export class LuaTranspiler {
fromCodePoint: "utf8.char",
};

if (identifier.escapedText as string === "fromCodePoint" && this.options.luaTarget !== Target.Lua53) {
if (identifier.escapedText as string === "fromCodePoint" && this.options.luaTarget !== LuaTarget.Lua53) {
throw new TranspileError(
`Unsupported string property ${identifier.escapedText} is only supported for lua 5.3.`,
identifier
Expand Down Expand Up @@ -1007,6 +1057,8 @@ export class LuaTranspiler {
case ts.TypeFlags.Object:
if (tsEx.isArrayType(type, this.checker)) {
return this.transpileArrayProperty(node);
} else if (tsEx.hasGetAccessor(node, this.checker)) {
return this.transpileGetAccessor(node);
}
}

Expand All @@ -1024,6 +1076,18 @@ export class LuaTranspiler {
return `${callPath}.${property}`;
}

public transpileGetAccessor(node: ts.PropertyAccessExpression): string {
const name = node.name.escapedText;
const expression = this.transpileExpression(node.expression);
return `${expression}:get__${name}()`;
}

public transpileSetAccessor(node: ts.PropertyAccessExpression, value: string): string {
const name = node.name.escapedText;
const expression = this.transpileExpression(node.expression);
return `${expression}:set__${name}(${value})`;
}

// Transpile a Math._ property
public transpileMathExpression(identifier: ts.Identifier): string {
const translation = {
Expand Down Expand Up @@ -1322,6 +1386,16 @@ export class LuaTranspiler {
);
}

// Transpile get accessors
node.members.filter(ts.isGetAccessor).forEach((getAccessor) => {
result += this.transpileGetAccessorDeclaration(getAccessor, className);
});

// Transpile set accessors
node.members.filter(ts.isSetAccessor).forEach((setAccessor) => {
result += this.transpileSetAccessorDeclaration(setAccessor, className);
});

// Transpile methods
node.members.filter(ts.isMethodDeclaration).forEach((method) => {
result += this.transpileMethodDeclaration(method, `${className}.`);
Expand All @@ -1330,6 +1404,39 @@ export class LuaTranspiler {
return result;
}

public transpileGetAccessorDeclaration(getAccessor: ts.GetAccessorDeclaration, className: string): string {
const name = (getAccessor.name as ts.Identifier).escapedText;

let result = this.indent + `function ${className}.get__${name}(self)\n`;

this.pushIndent();
result += this.transpileBlock(getAccessor.body);
this.popIndent();

result += this.indent + `end\n`;

return result;
}

public transpileSetAccessorDeclaration(setAccessor: ts.SetAccessorDeclaration, className: string): string {
const name = (setAccessor.name as ts.Identifier).escapedText;

const paramNames: string[] = ["self"];
setAccessor.parameters.forEach((param) => {
paramNames.push((param.name as ts.Identifier).escapedText as string);
});

let result = this.indent + `function ${className}.set__${name}(${paramNames.join(",")})\n`;

this.pushIndent();
result += this.transpileBlock(setAccessor.body);
this.popIndent();

result += this.indent + `end\n`;

return result;
}

public transpileConstructor(node: ts.ConstructorDeclaration,
className: string,
instanceFields: ts.PropertyDeclaration[]): string {
Expand Down
4 changes: 2 additions & 2 deletions test/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import * as path from "path";

import { Expect } from "alsatian";

import { LuaTranspiler, TranspileError } from "../../src/Transpiler";
import { LuaTranspiler, TranspileError, LuaTarget } from "../../src/Transpiler";
import { CompilerOptions } from "../../src/CommandLineParser";

const LuaVM = require("lua.vm.js");
const fs = require("fs");

const libSource = fs.readFileSync(path.join(path.dirname(require.resolve('typescript')), 'lib.d.ts')).toString();

export function transpileString(str: string, options: CompilerOptions = { dontRequireLuaLib: true }): string {
export function transpileString(str: string, options: CompilerOptions = { dontRequireLuaLib: true, luaTarget: LuaTarget.LuaJIT }): string {
let compilerHost = {
getSourceFile: (filename, languageVersion) => {
if (filename === "file.ts") {
Expand Down
21 changes: 21 additions & 0 deletions test/translation/lua/getSetAccessors.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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
function MyClass.constructor(self)
end
function MyClass.get__field(self)
return self._field+4
end
function MyClass.set__field(self,v)
self._field=(v*2)
end
local instance = MyClass.new(true)

instance:set__field(4)
local b = instance:get__field()

local c = (4+instance:get__field())*3
14 changes: 14 additions & 0 deletions test/translation/ts/getSetAccessors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class MyClass {
private _field: number;
public get field(): number {
return this._field + 4;
}
public set field(v: number) {
this._field = v*2;
}
}

var instance = new MyClass();
instance.field = 4;
const b = instance.field;
const c = (4 + instance.field)*3;
Loading