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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- Added new `"luaTarget"` option value - `"universal"`. Choosing this target would make TypeScriptToLua generate code compatible with all supported Lua targets.
- **BREAKING CHANGE:** This is a new default target. If you have been depending on LuaJIT being chosen implicitly, now you have to enable it explicitly with `"luaTarget": "JIT"` in the `tsconfig.json` file.

<!-- TODO: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-9.html doesn't seem to work now -->

- TypeScript has been updated to 3.9. See [release notes](https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/) for details. This update includes some fixes specific to our API usage:
Expand Down
1 change: 1 addition & 0 deletions src/CompilerOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export enum LuaLibImportKind {
}

export enum LuaTarget {
Universal = "universal",
Lua51 = "5.1",
Lua52 = "5.2",
Lua53 = "5.3",
Expand Down
7 changes: 6 additions & 1 deletion src/LuaLib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export enum LuaLibFeature {
InstanceOfObject = "InstanceOfObject",
Iterator = "Iterator",
Map = "Map",
MathAtan2 = "MathAtan2",
New = "New",
Number = "Number",
NumberIsFinite = "NumberIsFinite",
Expand Down Expand Up @@ -67,12 +68,16 @@ export enum LuaLibFeature {
Symbol = "Symbol",
SymbolRegistry = "SymbolRegistry",
TypeOf = "TypeOf",
Unpack = "Unpack",
}

const luaLibDependencies: Partial<Record<LuaLibFeature, LuaLibFeature[]>> = {
ArrayFlat: [LuaLibFeature.ArrayConcat],
ArrayFlatMap: [LuaLibFeature.ArrayConcat],
Error: [LuaLibFeature.New, LuaLibFeature.Class, LuaLibFeature.FunctionCall],
FunctionApply: [LuaLibFeature.Unpack],
FunctionBind: [LuaLibFeature.Unpack],
FunctionCall: [LuaLibFeature.Unpack],
Generator: [LuaLibFeature.Symbol],
InstanceOf: [LuaLibFeature.Symbol],
Iterator: [LuaLibFeature.Symbol],
Expand All @@ -81,7 +86,7 @@ const luaLibDependencies: Partial<Record<LuaLibFeature, LuaLibFeature[]>> = {
Set: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol],
WeakMap: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol],
WeakSet: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol],
Spread: [LuaLibFeature.Iterator],
Spread: [LuaLibFeature.Iterator, LuaLibFeature.Unpack],
SymbolRegistry: [LuaLibFeature.Symbol],
};

Expand Down
2 changes: 1 addition & 1 deletion src/lualib/FunctionApply.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
function __TS__FunctionApply(this: void, fn: (this: void, ...args: any[]) => any, thisArg: any, args?: any[]): any {
if (args) {
return fn(thisArg, (unpack || table.unpack)(args));
return fn(thisArg, ...args);
} else {
return fn(thisArg);
}
Expand Down
2 changes: 1 addition & 1 deletion src/lualib/FunctionBind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ function __TS__FunctionBind(
for (let i = 0; i < boundArgs.length; ++i) {
table.insert(args, i + 1, boundArgs[i]);
}
return fn(thisArg, (unpack || table.unpack)(args));
return fn(thisArg, ...args);
};
}
2 changes: 1 addition & 1 deletion src/lualib/FunctionCall.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
function __TS__FunctionCall(this: void, fn: (this: void, ...args: any[]) => any, thisArg: any, ...args: any[]): any {
return fn(thisArg, (unpack || table.unpack)(args));
return fn(thisArg, ...args);
}
1 change: 1 addition & 0 deletions src/lualib/MathAtan2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const __TS__MathAtan2 = math.atan2 || math.atan;
2 changes: 1 addition & 1 deletion src/lualib/Spread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ function __TS__Spread<T>(this: void, iterable: string | Iterable<T>): T[] {
arr[arr.length] = item;
}
}
return (table.unpack || unpack)(arr);
return __TS__Unpack(arr);
}
1 change: 1 addition & 0 deletions src/lualib/Unpack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const __TS__Unpack = table.unpack || unpack;
6 changes: 6 additions & 0 deletions src/lualib/declarations/math.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@
declare namespace math {
/** @tupleReturn */
function modf(x: number): [number, number];

function atan(x: number): number;
// eslint-disable-next-line @typescript-eslint/unified-signatures
function atan(y: number, x?: number): number;

function atan2(y: number, x: number): number;
}
1 change: 0 additions & 1 deletion src/lualib/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
},
"tstl": {
"luaLibImport": "none",
"luaTarget": "5.1",
"noHeader": true
}
}
16 changes: 8 additions & 8 deletions src/transformation/builtins/math.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as ts from "typescript";
import * as lua from "../../LuaAST";
import { LuaTarget } from "../../CompilerOptions";
import * as lua from "../../LuaAST";
import { TransformationContext } from "../context";
import { unsupportedProperty } from "../utils/diagnostics";
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
import { PropertyCallExpression, transformArguments } from "../visitors/call";

export function transformMathProperty(
Expand Down Expand Up @@ -37,22 +38,24 @@ export function transformMathCall(
const expression = node.expression;
const signature = context.checker.getResolvedSignature(node);
const params = transformArguments(context, node.arguments, signature);
const math = lua.createIdentifier("math");

const expressionName = expression.name.text;
switch (expressionName) {
// Lua 5.3: math.atan(y, x)
// Otherwise: math.atan2(y, x)
case "atan2": {
const math = lua.createIdentifier("math");
const methodName = context.options.luaTarget === LuaTarget.Lua53 ? "atan" : expressionName;
const method = lua.createStringLiteral(methodName);
if (context.luaTarget === LuaTarget.Universal) {
return transformLuaLibFunction(context, LuaLibFeature.MathAtan2, node, ...params);
}

const method = lua.createStringLiteral(context.luaTarget === LuaTarget.Lua53 ? "atan" : "atan2");
return lua.createCallExpression(lua.createTableIndexExpression(math, method), params, node);
}

// (math.log(x) / Math.LNe)
case "log10":
case "log2": {
const math = lua.createIdentifier("math");
const log1 = lua.createTableIndexExpression(math, lua.createStringLiteral("log"));
const logCall1 = lua.createCallExpression(log1, params);
const e = lua.createNumericLiteral(expressionName === "log10" ? Math.LN10 : Math.LN2);
Expand All @@ -61,7 +64,6 @@ export function transformMathCall(

// math.log(1 + x)
case "log1p": {
const math = lua.createIdentifier("math");
const log = lua.createStringLiteral("log");
const one = lua.createNumericLiteral(1);
const add = lua.createBinaryExpression(one, params[0], lua.SyntaxKind.AdditionOperator);
Expand All @@ -70,7 +72,6 @@ export function transformMathCall(

// math.floor(x + 0.5)
case "round": {
const math = lua.createIdentifier("math");
const floor = lua.createStringLiteral("floor");
const half = lua.createNumericLiteral(0.5);
const add = lua.createBinaryExpression(params[0], half, lua.SyntaxKind.AdditionOperator);
Expand All @@ -93,7 +94,6 @@ export function transformMathCall(
case "sin":
case "sqrt":
case "tan": {
const math = lua.createIdentifier("math");
const method = lua.createStringLiteral(expressionName);
return lua.createCallExpression(lua.createTableIndexExpression(math, method), params, node);
}
Expand Down
2 changes: 1 addition & 1 deletion src/transformation/context/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class TransformationContext {
public readonly resolver: EmitResolver;

public readonly options: CompilerOptions = this.program.getCompilerOptions();
public readonly luaTarget = this.options.luaTarget ?? LuaTarget.LuaJIT;
public readonly luaTarget = this.options.luaTarget ?? LuaTarget.Universal;
public readonly isModule = ts.isExternalModule(this.sourceFile);
public readonly isStrict =
(this.options.alwaysStrict ?? this.options.strict) ||
Expand Down
2 changes: 1 addition & 1 deletion src/transformation/utils/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export const unsupportedRightShiftOperator = createDiagnosticFactory(

const getLuaTargetName = (version: LuaTarget) => (version === LuaTarget.LuaJIT ? "LuaJIT" : `Lua ${version}`);
export const unsupportedForTarget = createDiagnosticFactory(
(functionality: string, version: LuaTarget) =>
(functionality: string, version: Exclude<LuaTarget, LuaTarget.Universal>) =>
`${functionality} is/are not supported for target ${getLuaTargetName(version)}.`
);

Expand Down
6 changes: 6 additions & 0 deletions src/transformation/utils/lua-ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { TransformationContext } from "../context";
import { createExportedIdentifier, getIdentifierExportScope } from "./export";
import { peekScope, ScopeType } from "./scope";
import { isFunctionType } from "./typescript";
import { transformLuaLibFunction } from "./lualib";
import { LuaLibFeature } from "../../LuaLib";

export type OneToManyVisitorResult<T extends lua.Node> = T | T[] | undefined;
export function unwrapVisitorResult<T extends lua.Node>(result: OneToManyVisitorResult<T>): T[] {
Expand Down Expand Up @@ -56,6 +58,10 @@ export function createUnpackCall(
expression: lua.Expression,
tsOriginal?: ts.Node
): lua.Expression {
if (context.luaTarget === LuaTarget.Universal) {
return transformLuaLibFunction(context, LuaLibFeature.Unpack, tsOriginal, expression);
}

const unpack =
context.luaTarget === LuaTarget.Lua51 || context.luaTarget === LuaTarget.LuaJIT
? lua.createIdentifier("unpack")
Expand Down
2 changes: 2 additions & 0 deletions src/transformation/visitors/binary-expression/bit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export function transformBinaryBitOperation(
operator: BitOperator
): lua.Expression {
switch (context.luaTarget) {
case LuaTarget.Universal:
case LuaTarget.Lua51:
context.diagnostics.push(unsupportedForTarget(node, "Bitwise operations", LuaTarget.Lua51));

Expand Down Expand Up @@ -105,6 +106,7 @@ export function transformUnaryBitOperation(
operator: lua.UnaryBitwiseOperator
): lua.Expression {
switch (context.luaTarget) {
case LuaTarget.Universal:
case LuaTarget.Lua51:
context.diagnostics.push(unsupportedForTarget(node, "Bitwise operations", LuaTarget.Lua51));

Expand Down
2 changes: 1 addition & 1 deletion src/transformation/visitors/break-continue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const transformBreakStatement: FunctionVisitor<ts.BreakStatement> = (brea
};

export const transformContinueStatement: FunctionVisitor<ts.ContinueStatement> = (statement, context) => {
if (context.luaTarget === LuaTarget.Lua51) {
if (context.luaTarget === LuaTarget.Universal || context.luaTarget === LuaTarget.Lua51) {
context.diagnostics.push(unsupportedForTarget(statement, "Continue statement", LuaTarget.Lua51));
}

Expand Down
2 changes: 1 addition & 1 deletion src/transformation/visitors/switch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { unsupportedForTarget } from "../utils/diagnostics";
import { performHoisting, popScope, pushScope, ScopeType } from "../utils/scope";

export const transformSwitchStatement: FunctionVisitor<ts.SwitchStatement> = (statement, context) => {
if (context.luaTarget === LuaTarget.Lua51) {
if (context.luaTarget === LuaTarget.Universal || context.luaTarget === LuaTarget.Lua51) {
context.diagnostics.push(unsupportedForTarget(statement, "Switch statements", LuaTarget.Lua51));
}

Expand Down
2 changes: 2 additions & 0 deletions test/cli/parse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ describe("command line", () => {
["luaLibImport", "inline", { luaLibImport: tstl.LuaLibImportKind.Inline }],
["luaLibImport", "require", { luaLibImport: tstl.LuaLibImportKind.Require }],

["luaTarget", "universal", { luaTarget: tstl.LuaTarget.Universal }],
["luaTarget", "5.1", { luaTarget: tstl.LuaTarget.Lua51 }],
["luaTarget", "5.2", { luaTarget: tstl.LuaTarget.Lua52 }],
["luaTarget", "5.3", { luaTarget: tstl.LuaTarget.Lua53 }],
Expand Down Expand Up @@ -216,6 +217,7 @@ describe("tsconfig", () => {
["luaLibImport", "inline", { luaLibImport: tstl.LuaLibImportKind.Inline }],
["luaLibImport", "require", { luaLibImport: tstl.LuaLibImportKind.Require }],

["luaTarget", "universal", { luaTarget: tstl.LuaTarget.Universal }],
["luaTarget", "5.1", { luaTarget: tstl.LuaTarget.Lua51 }],
["luaTarget", "5.2", { luaTarget: tstl.LuaTarget.Lua52 }],
["luaTarget", "5.3", { luaTarget: tstl.LuaTarget.Lua53 }],
Expand Down
59 changes: 59 additions & 0 deletions test/unit/__snapshots__/loops.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ until not false"

exports[`loop continue (do { continue; } while (false)) [5.1]: diagnostics 1`] = `"main.ts(1,6): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;

exports[`loop continue (do { continue; } while (false)) [universal]: code 1`] = `
"repeat
do
do
goto __continue2
end
::__continue2::
end
until not false"
`;

exports[`loop continue (do { continue; } while (false)) [universal]: diagnostics 1`] = `"main.ts(1,6): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;

exports[`loop continue (for (;;) { continue; }) [5.1]: code 1`] = `
"do
while true do
Expand All @@ -38,6 +51,19 @@ end"

exports[`loop continue (for (;;) { continue; }) [5.1]: diagnostics 1`] = `"main.ts(1,12): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;

exports[`loop continue (for (;;) { continue; }) [universal]: code 1`] = `
"do
while true do
do
goto __continue2
end
::__continue2::
end
end"
`;

exports[`loop continue (for (;;) { continue; }) [universal]: diagnostics 1`] = `"main.ts(1,12): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;

exports[`loop continue (for (const a in {}) { continue; }) [5.1]: code 1`] = `
"for a in pairs({}) do
do
Expand All @@ -49,6 +75,17 @@ end"

exports[`loop continue (for (const a in {}) { continue; }) [5.1]: diagnostics 1`] = `"main.ts(1,23): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;

exports[`loop continue (for (const a in {}) { continue; }) [universal]: code 1`] = `
"for a in pairs({}) do
do
goto __continue2
end
::__continue2::
end"
`;

exports[`loop continue (for (const a in {}) { continue; }) [universal]: diagnostics 1`] = `"main.ts(1,23): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;

exports[`loop continue (for (const a of []) { continue; }) [5.1]: code 1`] = `
"for ____, a in ipairs({}) do
do
Expand All @@ -60,6 +97,17 @@ end"

exports[`loop continue (for (const a of []) { continue; }) [5.1]: diagnostics 1`] = `"main.ts(1,23): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;

exports[`loop continue (for (const a of []) { continue; }) [universal]: code 1`] = `
"for ____, a in ipairs({}) do
do
goto __continue2
end
::__continue2::
end"
`;

exports[`loop continue (for (const a of []) { continue; }) [universal]: diagnostics 1`] = `"main.ts(1,23): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;

exports[`loop continue (while (false) { continue; }) [5.1]: code 1`] = `
"while false do
do
Expand All @@ -70,3 +118,14 @@ end"
`;

exports[`loop continue (while (false) { continue; }) [5.1]: diagnostics 1`] = `"main.ts(1,17): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;

exports[`loop continue (while (false) { continue; }) [universal]: code 1`] = `
"while false do
do
goto __continue2
end
::__continue2::
end"
`;

exports[`loop continue (while (false) { continue; }) [universal]: diagnostics 1`] = `"main.ts(1,17): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;
3 changes: 3 additions & 0 deletions test/unit/builtins/math.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ test.each(["E", "LN10", "LN2", "LOG10E", "LOG2E", "SQRT1_2", "SQRT2"])("Math.%s"

const expectMathAtan2: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toContain("math.atan2(");
const expectMathAtan: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toContain("math.atan(");
const expectLualibMathAtan2: util.TapCallback = builder =>
expect(builder.getMainLuaCodeChunk()).toContain("__TS__MathAtan2(");

util.testEachVersion("Math.atan2", () => util.testExpression`Math.atan2(4, 5)`, {
[tstl.LuaTarget.Universal]: builder => builder.tap(expectLualibMathAtan2),
[tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectMathAtan2),
[tstl.LuaTarget.Lua51]: builder => builder.tap(expectMathAtan2),
[tstl.LuaTarget.Lua52]: builder => builder.tap(expectMathAtan2),
Expand Down
1 change: 1 addition & 0 deletions test/unit/loops.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ for (const testCase of [
expect(builder.getMainLuaCodeChunk()).toMatch("::__continue2::");

util.testEachVersion(`loop continue (${testCase})`, () => util.testModule(testCase), {
[tstl.LuaTarget.Universal]: builder => builder.expectDiagnosticsToMatchSnapshot([unsupportedForTarget.code]),
[tstl.LuaTarget.Lua51]: builder => builder.expectDiagnosticsToMatchSnapshot([unsupportedForTarget.code]),
[tstl.LuaTarget.Lua52]: expectContinueGotoLabel,
[tstl.LuaTarget.Lua53]: expectContinueGotoLabel,
Expand Down
3 changes: 3 additions & 0 deletions test/unit/spread.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { formatCode } from "../util";
// TODO: Make some utils for testing other targets
const expectUnpack: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toMatch(/[^.]unpack\(/);
const expectTableUnpack: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toContain("table.unpack");
const expectLualibUnpack: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toContain("__TS__Unpack");

const arrayLiteralCases = [
"1, 2, ...[3, 4, 5]",
Expand Down Expand Up @@ -72,6 +73,7 @@ describe("in function call", () => {
return foo(...array);
`,
{
[tstl.LuaTarget.Universal]: builder => builder.tap(expectLualibUnpack),
[tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectUnpack),
[tstl.LuaTarget.Lua51]: builder => builder.tap(expectUnpack),
[tstl.LuaTarget.Lua52]: builder => builder.tap(expectTableUnpack),
Expand All @@ -82,6 +84,7 @@ describe("in function call", () => {

describe("in array literal", () => {
util.testEachVersion(undefined, () => util.testExpression`[...[0, 1, 2]]`, {
[tstl.LuaTarget.Universal]: builder => builder.tap(expectLualibUnpack),
[tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectUnpack),
[tstl.LuaTarget.Lua51]: builder => builder.tap(expectUnpack),
[tstl.LuaTarget.Lua52]: builder => builder.tap(expectTableUnpack),
Expand Down
X Tutup