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
37 changes: 35 additions & 2 deletions src/Compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@ import * as path from "path";
import * as ts from "typescript";

import { CompilerOptions, parseCommandLine } from "./CommandLineParser";
import { LuaTranspiler, TranspileError } from "./Transpiler";
import { LuaTranspiler51 } from "./targets/Transpiler.51";
import { LuaTranspiler52 } from "./targets/Transpiler.52";
import { LuaTranspiler53 } from "./targets/Transpiler.53";
import { LuaTranspilerJIT } from "./targets/Transpiler.JIT";
import { LuaTarget, LuaTranspiler, TranspileError } from "./Transpiler";
import { TSHelper as tsEx } from "./TSHelper";

export function compile(fileNames: string[], options: CompilerOptions): void {
if (!options.luaTarget) {
options.luaTarget = LuaTarget.LuaJIT;
}

const program = ts.createProgram(fileNames, options);
const checker = program.getTypeChecker();

Expand Down Expand Up @@ -41,7 +49,7 @@ export function compile(fileNames: string[], options: CompilerOptions): void {
const rootDir = options.rootDir;

// Transpile AST
const lua = LuaTranspiler.transpileSourceFile(sourceFile, checker, options);
const lua = createTranspiler(checker, options, sourceFile).transpileSourceFile();

let outPath = sourceFile.fileName;
if (options.outDir !== options.rootDir) {
Expand Down Expand Up @@ -90,6 +98,31 @@ export function compile(fileNames: string[], options: CompilerOptions): void {
);
}

export function createTranspiler(checker: ts.TypeChecker,
options: ts.CompilerOptions,
sourceFile: ts.SourceFile): LuaTranspiler {
let luaTargetTranspiler: LuaTranspiler;
switch (options.luaTarget) {
case LuaTarget.LuaJIT:
luaTargetTranspiler = new LuaTranspilerJIT(checker, options, sourceFile);
break;
case LuaTarget.Lua51:
luaTargetTranspiler = new LuaTranspiler51(checker, options, sourceFile);
break;
case LuaTarget.Lua52:
luaTargetTranspiler = new LuaTranspiler52(checker, options, sourceFile);
break;
case LuaTarget.Lua53:
luaTargetTranspiler = new LuaTranspiler53(checker, options, sourceFile);
break;
default:
// should not happen
throw Error("No luaTarget Specified please ensure a target is set!");
}

return luaTargetTranspiler;
}

export function execCommandLine(argv?: string[]) {
argv = argv ? argv : process.argv.slice(2);
const commandLine = parseCommandLine(argv);
Expand Down
186 changes: 51 additions & 135 deletions src/Transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,15 @@ export class TranspileError extends Error {
}

export enum LuaTarget {
Lua51 = "5.1",
Lua52 = "5.2",
Lua53 = "5.3",
LuaJIT = "JIT",
}

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't needed any more

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It prevents the class from being instantiated.


// Transpile a source file
public static transpileSourceFile(node: ts.SourceFile,
checker: ts.TypeChecker,
options: CompilerOptions): string {
const transpiler = new LuaTranspiler(checker, options, node);

let header = "";
if (options.addHeader) {
header = "-- Generated by TypescriptToLua v" + packageJSON.version + "\n" +
"-- https://github.com/Perryvw/TypescriptToLua\n";
}
let result = header;
if (!options.dontRequireLuaLib) {
// require helper functions
result += `require("typescript_lualib")\n`;
}
if (transpiler.isModule) {
// Shadow exports if it already exists
result += "local exports = exports or {}\n";
}
result += transpiler.transpileBlock(node);
if (transpiler.isModule) {
result += "return exports\n";
}
return result;
}

public indent: string;
public checker: ts.TypeChecker;
public options: ts.CompilerOptions;
Expand Down Expand Up @@ -130,6 +105,29 @@ export class LuaTranspiler {
return `"${relativePath.replace(new RegExp("\\\\|\/", "g"), ".")}"`;
}

// Transpile a source file
public transpileSourceFile(): string {
let header = "";
if (this.options.addHeader) {
header = "-- Generated by TypescriptToLua v" + packageJSON.version + "\n" +
"-- https://github.com/Perryvw/TypescriptToLua\n";
}
let result = header;
if (!this.options.dontRequireLuaLib) {
// require helper functions
result += `require("typescript_lualib")\n`;
}
if (this.isModule) {
// Shadow exports if it already exists
result += "local exports = exports or {}\n";
}
result += this.transpileBlock(this.sourceFile);
if (this.isModule) {
result += "return exports\n";
}
return result;
}

// Transpile a block
public transpileBlock(node: ts.Node): string {
let result = "";
Expand Down Expand Up @@ -655,102 +653,18 @@ export class LuaTranspiler {
let result = "";

// Transpile Bitops
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 (tsHelper.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 (tsHelper.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 (tsHelper.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 (tsHelper.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 (tsHelper.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression,
`bit.rshift(${lhs},${rhs})`);
}
result = `${lhs}=bit.rshift(${lhs},${rhs})`;
break;
}
} else {
switch (node.operatorToken.kind) {
case ts.SyntaxKind.AmpersandToken:
result = `${lhs}&${rhs}`;
break;
case ts.SyntaxKind.AmpersandEqualsToken:
if (tsHelper.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 (tsHelper.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 (tsHelper.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 (tsHelper.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}>>${rhs}`);
}
result = `${lhs}=${lhs}>>${rhs}`;
break;
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
throw new TranspileError("Bitwise operator >>> not supported", node);
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
throw new TranspileError("Bitwise operator >>> not supported", node);
}
switch (node.operatorToken.kind) {
case ts.SyntaxKind.AmpersandToken:
case ts.SyntaxKind.AmpersandEqualsToken:
case ts.SyntaxKind.BarToken:
case ts.SyntaxKind.BarEqualsToken:
case ts.SyntaxKind.LessThanLessThanToken:
case ts.SyntaxKind.LessThanLessThanEqualsToken:
case ts.SyntaxKind.GreaterThanGreaterThanToken:
case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken:
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
result = this.transpileBitOperation(node, lhs, rhs);
}

// Transpile operators
Expand Down Expand Up @@ -862,6 +776,10 @@ export class LuaTranspiler {
}
}

public transpileBitOperation(node: ts.BinaryExpression, lhs: string, rhs: string): string {
throw new TranspileError(`Bit Oeprations are not supported in Lua ${this.options.target}`, node);
}

public transpileTemplateExpression(node: ts.TemplateExpression) {
const parts = [`"${node.head.text}"`];
node.templateSpans.forEach(span => {
Expand Down Expand Up @@ -1015,24 +933,22 @@ export class LuaTranspiler {
}
}

// Transpile a String._ property
public transpileStringExpression(identifier: ts.Identifier): string {
const translation = {
public getValidStringProperties(): {[js: string]: string} {
return {
fromCharCode: "string.char",
fromCodePoint: "utf8.char",
};
}

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
);
}
// Transpile a String._ property
public transpileStringExpression(identifier: ts.Identifier): string {
const translation = this.getValidStringProperties();

if (translation[identifier.escapedText as string]) {
return `${translation[identifier.escapedText as string]}`;
} else {
throw new TranspileError(`Unsupported string property ${identifier.escapedText}.`, identifier);
throw new TranspileError(`Unsupported string property ${identifier.escapedText}, ` +
`is not supported in Lua ${this.options.luaTarget}.`,
identifier);
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/targets/Transpiler.51.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LuaTranspiler, TranspileError } from "../Transpiler";
import { TSHelper as tsHelper } from "../TSHelper";

import * as ts from "typescript";

export class LuaTranspiler51 extends LuaTranspiler {
}
8 changes: 8 additions & 0 deletions src/targets/Transpiler.52.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { TSHelper as tsHelper } from "../TSHelper";
import { LuaTranspiler51 } from "./Transpiler.51";

import * as ts from "typescript";

export class LuaTranspiler52 extends LuaTranspiler51 {

}
51 changes: 51 additions & 0 deletions src/targets/Transpiler.53.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { TranspileError } from "../Transpiler";
import { TSHelper as tsHelper } from "../TSHelper";
import { LuaTranspiler52 } from "./Transpiler.52";

import * as ts from "typescript";

export class LuaTranspiler53 extends LuaTranspiler52 {
public transpileBitOperation(node: ts.BinaryExpression, lhs: string, rhs: string): string {
switch (node.operatorToken.kind) {
case ts.SyntaxKind.AmpersandToken:
return `${lhs}&${rhs}`;
case ts.SyntaxKind.AmpersandEqualsToken:
if (tsHelper.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}&${rhs}`);
}
return `${lhs}=${lhs}&${rhs}`;
case ts.SyntaxKind.BarToken:
return `${lhs}|${rhs}`;
case ts.SyntaxKind.BarEqualsToken:
if (tsHelper.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}|${rhs}`);
}
return `${lhs}=${lhs}|${rhs}`;
case ts.SyntaxKind.LessThanLessThanToken:
return `${lhs}<<${rhs}`;
case ts.SyntaxKind.LessThanLessThanEqualsToken:
if (tsHelper.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}<<${rhs}`);
}
return `${lhs}=${lhs}<<${rhs}`;
case ts.SyntaxKind.GreaterThanGreaterThanToken:
return `${lhs}>>${rhs}`;
case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken:
if (tsHelper.hasSetAccessor(node.left, this.checker)) {
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}>>${rhs}`);
}
return `${lhs}=${lhs}>>${rhs}`;
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
throw new TranspileError("Bitwise operator >>> not supported in Lua 5.3", node);
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
throw new TranspileError("Bitwise operator >>> not supported in Lua 5.3", node);
}
}
public getValidStringProperties(): {[js: string]: string} {
return {
fromCharCode: "string.char",
fromCodePoint: "utf8.char",
};
}

}
Loading