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
95 changes: 69 additions & 26 deletions src/Transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,34 @@ export abstract class LuaTranspiler {
return filePath.replace(new RegExp("\\\\|\/", "g"), ".");
}

public computeEnumMembers(node: ts.EnumDeclaration): Array<{ name: string, value: string | number }> {
let val: number | string = 0;
let hasStringInitializers = false;

return node.members.map(member => {
if (member.initializer) {
if (ts.isNumericLiteral(member.initializer)) {
val = parseInt(member.initializer.text);
} else if (ts.isStringLiteral(member.initializer)) {
hasStringInitializers = true;
val = `"${member.initializer.text}"`;
} else {
throw TSTLErrors.InvalidEnumMember(member.initializer);
}
} else if (hasStringInitializers) {
throw TSTLErrors.HeterogeneousEnum(node);
}

const enumMember = { name: this.transpileIdentifier(member.name as ts.Identifier), value: val };

if (typeof val === "number") {
val++;
}

return enumMember;
});
}

// Transpile a source file
public transpileSourceFile(): string {
let header = "";
Expand Down Expand Up @@ -393,48 +421,36 @@ export abstract class LuaTranspiler {
}

public transpileEnum(node: ts.EnumDeclaration): string {
let val: number | string = 0;
let result = "";

const type = this.checker.getTypeAtLocation(node);

// Const enums should never appear in the resulting code
if (type.symbol.getFlags() & ts.SymbolFlags.ConstEnum) {
return "";
}

const membersOnly = tsHelper.getCustomDecorators(type, this.checker)
.has(DecoratorKind.CompileMembersOnly);

let result = "";

if (!membersOnly) {
const name = this.transpileIdentifier(node.name);
result += this.indent + this.accessPrefix(node) + `${name}={}\n`;
this.pushExport(name, node);
}

let hasStringInitializers = false;
node.members.forEach(member => {
if (member.initializer) {
if (ts.isNumericLiteral(member.initializer)) {
val = parseInt(member.initializer.text);
} else if (ts.isStringLiteral(member.initializer)) {
hasStringInitializers = true;
val = `"${member.initializer.text}"`;
} else {
throw TSTLErrors.InvalidEnumMember(member.initializer);
}
} else if (hasStringInitializers) {
throw TSTLErrors.HeterogeneousEnum(node);
}

this.computeEnumMembers(node).forEach(enumMember => {
if (membersOnly) {
const defName = this.definitionName(this.transpileIdentifier(member.name as ts.Identifier));
result += this.indent + `${defName}=${val}\n`;
const defName = this.definitionName(enumMember.name);
result += this.indent + `${defName}=${enumMember.value}\n`;
} else {
const defName = this.definitionName(
`${this.transpileIdentifier(node.name)}.${this.transpileIdentifier((member.name as ts.Identifier))}`
`${this.transpileIdentifier(node.name)}.${enumMember.name}`
);
result += this.indent + `${defName}=${val}\n`;
}

if (typeof val === "number") {
val++;
result += this.indent + `${defName}=${enumMember.value}\n`;
}
});

return result;
}

Expand Down Expand Up @@ -1398,6 +1414,33 @@ export abstract class LuaTranspiler {
}
}

if (type.symbol && (type.symbol.flags & ts.SymbolFlags.ConstEnum)) {
const propertyValueDeclaration = this.checker.getTypeAtLocation(node).symbol.valueDeclaration;

if (propertyValueDeclaration && propertyValueDeclaration.kind === ts.SyntaxKind.EnumMember) {
const enumMember = propertyValueDeclaration as ts.EnumMember;

if (enumMember.initializer) {
return this.transpileExpression(enumMember.initializer);
} else {
const enumMembers = this.computeEnumMembers(enumMember.parent);
const memberPosition = enumMember.parent.members.indexOf(enumMember);

if (memberPosition === -1) {
throw TSTLErrors.UnsupportedProperty(type.symbol.name, property, node);
}

const value = enumMembers[memberPosition].value;

if (typeof value === "string") {
return `"${value}"`;
}

return value.toString();
}
}
}

this.checkForLuaLibType(type);

const decorators = tsHelper.getCustomDecorators(type, this.checker);
Expand Down
57 changes: 57 additions & 0 deletions test/unit/enum.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,63 @@ import * as util from "../src/util";
import { TranspileError } from "../../src/Errors";

export class EnumTests {
@Test("Declare const enum")
public declareConstEnum(): void {
const testCode = `
declare const enum TestEnum {
MEMBER_ONE = "test",
MEMBER_TWO = "test2"
}

const valueOne = TestEnum.MEMBER_ONE;
`;

Expect(util.transpileString(testCode)).toBe(`local valueOne = "test";`);
}

@Test("Const enum")
public constEnum(): void {
const testCode = `
const enum TestEnum {
MEMBER_ONE = "test",
MEMBER_TWO = "test2"
}

const valueOne = TestEnum.MEMBER_ONE;
`;

Expect(util.transpileString(testCode)).toBe(`local valueOne = "test";`);
}

@Test("Const enum without initializer")
public constEnumNoInitializer(): void {
const testCode = `
const enum TestEnum {
MEMBER_ONE,
MEMBER_TWO
}

const valueOne = TestEnum.MEMBER_ONE;
`;

Expect(util.transpileString(testCode)).toBe(`local valueOne = 0;`);
}

@Test("Const enum without initializer in some values")
public constEnumNoInitializerInSomeValues(): void {
const testCode = `
const enum TestEnum {
MEMBER_ONE = 3,
MEMBER_TWO,
MEMBER_THREE = 5
}

const valueOne = TestEnum.MEMBER_TWO;
`;

Expect(util.transpileString(testCode)).toBe(`local valueOne = 4;`);
}

@Test("Invalid heterogeneous enum")
public invalidHeterogeneousEnum(): void {
// Transpile & Assert
Expand Down