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
292 changes: 202 additions & 90 deletions src/LuaTransformer.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/TSHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,10 @@ export class TSHelper {
return match !== undefined && match !== null && match[0] === str;
}

public static fixInvalidLuaIdentifier(name: string): string {
return name.replace(/[^a-zA-Z0-9_]/g, c => `_${c.charCodeAt(0).toString(16).toUpperCase()}`);
}

// Checks that a name is valid for use in lua function declaration syntax:
// 'foo.bar' => passes ('function foo.bar()' is valid)
// 'getFoo().bar' => fails ('function getFoo().bar()' would be illegal)
Expand Down
4 changes: 2 additions & 2 deletions src/TSTLErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ export class TSTLErrors {
public static InvalidThrowExpression = (node: ts.Node) =>
new TranspileError(`Invalid throw expression, only strings can be thrown.`, node);

public static KeywordIdentifier = (node: ts.Identifier) =>
new TranspileError(`Cannot use Lua keyword ${node.escapedText} as identifier.`, node);
public static ForbiddenStaticClassPropertyName = (node: ts.Node, name: string) =>
new TranspileError(`Cannot use "${name}" as a static class property or method name.`, node);

public static MissingClassName = (node: ts.Node) =>
new TranspileError(`Class declarations must have a name.`, node);
Expand Down
40 changes: 20 additions & 20 deletions test/translation/__snapshots__/transformation.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -391,16 +391,16 @@ local TestClass = __TSTL_test.TestClass"
`;

exports[`Transformation (modulesImportNamedSpecialChars) 1`] = `
"local __TSTL_kebab_module = require(\\"kebab-module\\")
local TestClass = __TSTL_kebab_module.TestClass
local __TSTL_dollar_module = require(\\"dollar$module\\")
local TestClass = __TSTL_dollar_module.TestClass
local __TSTL_singlequote_module = require(\\"singlequote'module\\")
local TestClass = __TSTL_singlequote_module.TestClass
local __TSTL_hash_module = require(\\"hash#module\\")
local TestClass = __TSTL_hash_module.TestClass
local __TSTL_space_module = require(\\"space module\\")
local TestClass = __TSTL_space_module.TestClass"
"local __TSTL_kebab_2Dmodule = require(\\"kebab-module\\")
local TestClass = __TSTL_kebab_2Dmodule.TestClass
local __TSTL_dollar_24module = require(\\"dollar$module\\")
local TestClass = __TSTL_dollar_24module.TestClass
local __TSTL_singlequote_27module = require(\\"singlequote'module\\")
local TestClass = __TSTL_singlequote_27module.TestClass
local __TSTL_hash_23module = require(\\"hash#module\\")
local TestClass = __TSTL_hash_23module.TestClass
local __TSTL_space_20module = require(\\"space module\\")
local TestClass = __TSTL_space_20module.TestClass"
`;

exports[`Transformation (modulesImportRenamed) 1`] = `
Expand All @@ -409,16 +409,16 @@ local RenamedClass = __TSTL_test.TestClass"
`;

exports[`Transformation (modulesImportRenamedSpecialChars) 1`] = `
"local __TSTL_kebab_module = require(\\"kebab-module\\")
local RenamedClass = __TSTL_kebab_module.TestClass
local __TSTL_dollar_module = require(\\"dollar$module\\")
local RenamedClass = __TSTL_dollar_module.TestClass
local __TSTL_singlequote_module = require(\\"singlequote'module\\")
local RenamedClass = __TSTL_singlequote_module.TestClass
local __TSTL_hash_module = require(\\"hash#module\\")
local RenamedClass = __TSTL_hash_module.TestClass
local __TSTL_space_module = require(\\"space module\\")
local RenamedClass = __TSTL_space_module.TestClass"
"local __TSTL_kebab_2Dmodule = require(\\"kebab-module\\")
local RenamedClass = __TSTL_kebab_2Dmodule.TestClass
local __TSTL_dollar_24module = require(\\"dollar$module\\")
local RenamedClass = __TSTL_dollar_24module.TestClass
local __TSTL_singlequote_27module = require(\\"singlequote'module\\")
local RenamedClass = __TSTL_singlequote_27module.TestClass
local __TSTL_hash_23module = require(\\"hash#module\\")
local RenamedClass = __TSTL_hash_23module.TestClass
local __TSTL_space_20module = require(\\"space module\\")
local RenamedClass = __TSTL_space_20module.TestClass"
`;

exports[`Transformation (modulesImportWithoutFromClause) 1`] = `"require(\\"test\\")"`;
Expand Down
9 changes: 0 additions & 9 deletions test/unit/assignments/assignments.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,15 +176,6 @@ test("TupleReturn in expression", () => {
expect(result).toBe("a3");
});

test.each(["and", "local", "nil", "not", "or", "repeat", "then", "until"])(
"Keyword identifier error (%p)",
identifier => {
expect(() => util.transpileString(`const ${identifier} = 3;`)).toThrowExactError(
TSTLErrors.KeywordIdentifier(ts.createIdentifier(identifier)),
);
},
);

test("String table access", () => {
const code = `
const dict : {[key:string]:any} = {};
Expand Down
40 changes: 40 additions & 0 deletions test/unit/class.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -823,3 +823,43 @@ test("Class field override in subclass with constructors", () => {
return (new Foo()).field + (new Bar()).field;`;
expect(util.transpileAndExecute(code)).toBe("foobar");
});

test("Class cannot have static new method", () => {
const code = `
class Foo {
static new() {}
}`;
expect(() => util.transpileAndExecute(code)).toThrow(
TSTLErrors.ForbiddenStaticClassPropertyName(ts.createEmptyStatement(), "new").message,
);
});

test("Class cannot have static new property", () => {
const code = `
class Foo {
static new = "foobar";
}`;
expect(() => util.transpileAndExecute(code)).toThrow(
TSTLErrors.ForbiddenStaticClassPropertyName(ts.createEmptyStatement(), "new").message,
);
});

test("Class cannot have static new get accessor", () => {
const code = `
class Foo {
static get new() { return "foobar" }
}`;
expect(() => util.transpileAndExecute(code)).toThrow(
TSTLErrors.ForbiddenStaticClassPropertyName(ts.createEmptyStatement(), "new").message,
);
});

test("Class cannot have static new set accessor", () => {
const code = `
class Foo {
static set new(value: string) {}
}`;
expect(() => util.transpileAndExecute(code)).toThrow(
TSTLErrors.ForbiddenStaticClassPropertyName(ts.createEmptyStatement(), "new").message,
);
});
Loading