Skip to content

Commit 9399f10

Browse files
DoctorGesterPerryvw
authored andcommitted
Const enum support (#253)
* Const enum support * Changed string concatenation to interpolation
1 parent 7f25ee5 commit 9399f10

File tree

2 files changed

+126
-26
lines changed

2 files changed

+126
-26
lines changed

src/Transpiler.ts

Lines changed: 69 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,34 @@ export abstract class LuaTranspiler {
184184
return filePath.replace(new RegExp("\\\\|\/", "g"), ".");
185185
}
186186

187+
public computeEnumMembers(node: ts.EnumDeclaration): Array<{ name: string, value: string | number }> {
188+
let val: number | string = 0;
189+
let hasStringInitializers = false;
190+
191+
return node.members.map(member => {
192+
if (member.initializer) {
193+
if (ts.isNumericLiteral(member.initializer)) {
194+
val = parseInt(member.initializer.text);
195+
} else if (ts.isStringLiteral(member.initializer)) {
196+
hasStringInitializers = true;
197+
val = `"${member.initializer.text}"`;
198+
} else {
199+
throw TSTLErrors.InvalidEnumMember(member.initializer);
200+
}
201+
} else if (hasStringInitializers) {
202+
throw TSTLErrors.HeterogeneousEnum(node);
203+
}
204+
205+
const enumMember = { name: this.transpileIdentifier(member.name as ts.Identifier), value: val };
206+
207+
if (typeof val === "number") {
208+
val++;
209+
}
210+
211+
return enumMember;
212+
});
213+
}
214+
187215
// Transpile a source file
188216
public transpileSourceFile(): string {
189217
let header = "";
@@ -393,48 +421,36 @@ export abstract class LuaTranspiler {
393421
}
394422

395423
public transpileEnum(node: ts.EnumDeclaration): string {
396-
let val: number | string = 0;
397-
let result = "";
398-
399424
const type = this.checker.getTypeAtLocation(node);
425+
426+
// Const enums should never appear in the resulting code
427+
if (type.symbol.getFlags() & ts.SymbolFlags.ConstEnum) {
428+
return "";
429+
}
430+
400431
const membersOnly = tsHelper.getCustomDecorators(type, this.checker)
401432
.has(DecoratorKind.CompileMembersOnly);
402433

434+
let result = "";
435+
403436
if (!membersOnly) {
404437
const name = this.transpileIdentifier(node.name);
405438
result += this.indent + this.accessPrefix(node) + `${name}={}\n`;
406439
this.pushExport(name, node);
407440
}
408441

409-
let hasStringInitializers = false;
410-
node.members.forEach(member => {
411-
if (member.initializer) {
412-
if (ts.isNumericLiteral(member.initializer)) {
413-
val = parseInt(member.initializer.text);
414-
} else if (ts.isStringLiteral(member.initializer)) {
415-
hasStringInitializers = true;
416-
val = `"${member.initializer.text}"`;
417-
} else {
418-
throw TSTLErrors.InvalidEnumMember(member.initializer);
419-
}
420-
} else if (hasStringInitializers) {
421-
throw TSTLErrors.HeterogeneousEnum(node);
422-
}
423-
442+
this.computeEnumMembers(node).forEach(enumMember => {
424443
if (membersOnly) {
425-
const defName = this.definitionName(this.transpileIdentifier(member.name as ts.Identifier));
426-
result += this.indent + `${defName}=${val}\n`;
444+
const defName = this.definitionName(enumMember.name);
445+
result += this.indent + `${defName}=${enumMember.value}\n`;
427446
} else {
428447
const defName = this.definitionName(
429-
`${this.transpileIdentifier(node.name)}.${this.transpileIdentifier((member.name as ts.Identifier))}`
448+
`${this.transpileIdentifier(node.name)}.${enumMember.name}`
430449
);
431-
result += this.indent + `${defName}=${val}\n`;
432-
}
433-
434-
if (typeof val === "number") {
435-
val++;
450+
result += this.indent + `${defName}=${enumMember.value}\n`;
436451
}
437452
});
453+
438454
return result;
439455
}
440456

@@ -1408,6 +1424,33 @@ export abstract class LuaTranspiler {
14081424
}
14091425
}
14101426

1427+
if (type.symbol && (type.symbol.flags & ts.SymbolFlags.ConstEnum)) {
1428+
const propertyValueDeclaration = this.checker.getTypeAtLocation(node).symbol.valueDeclaration;
1429+
1430+
if (propertyValueDeclaration && propertyValueDeclaration.kind === ts.SyntaxKind.EnumMember) {
1431+
const enumMember = propertyValueDeclaration as ts.EnumMember;
1432+
1433+
if (enumMember.initializer) {
1434+
return this.transpileExpression(enumMember.initializer);
1435+
} else {
1436+
const enumMembers = this.computeEnumMembers(enumMember.parent);
1437+
const memberPosition = enumMember.parent.members.indexOf(enumMember);
1438+
1439+
if (memberPosition === -1) {
1440+
throw TSTLErrors.UnsupportedProperty(type.symbol.name, property, node);
1441+
}
1442+
1443+
const value = enumMembers[memberPosition].value;
1444+
1445+
if (typeof value === "string") {
1446+
return `"${value}"`;
1447+
}
1448+
1449+
return value.toString();
1450+
}
1451+
}
1452+
}
1453+
14111454
this.checkForLuaLibType(type);
14121455

14131456
const decorators = tsHelper.getCustomDecorators(type, this.checker);

test/unit/enum.spec.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,63 @@ import * as util from "../src/util";
44
import { TranspileError } from "../../src/Errors";
55

66
export class EnumTests {
7+
@Test("Declare const enum")
8+
public declareConstEnum(): void {
9+
const testCode = `
10+
declare const enum TestEnum {
11+
MEMBER_ONE = "test",
12+
MEMBER_TWO = "test2"
13+
}
14+
15+
const valueOne = TestEnum.MEMBER_ONE;
16+
`;
17+
18+
Expect(util.transpileString(testCode)).toBe(`local valueOne = "test";`);
19+
}
20+
21+
@Test("Const enum")
22+
public constEnum(): void {
23+
const testCode = `
24+
const enum TestEnum {
25+
MEMBER_ONE = "test",
26+
MEMBER_TWO = "test2"
27+
}
28+
29+
const valueOne = TestEnum.MEMBER_ONE;
30+
`;
31+
32+
Expect(util.transpileString(testCode)).toBe(`local valueOne = "test";`);
33+
}
34+
35+
@Test("Const enum without initializer")
36+
public constEnumNoInitializer(): void {
37+
const testCode = `
38+
const enum TestEnum {
39+
MEMBER_ONE,
40+
MEMBER_TWO
41+
}
42+
43+
const valueOne = TestEnum.MEMBER_ONE;
44+
`;
45+
46+
Expect(util.transpileString(testCode)).toBe(`local valueOne = 0;`);
47+
}
48+
49+
@Test("Const enum without initializer in some values")
50+
public constEnumNoInitializerInSomeValues(): void {
51+
const testCode = `
52+
const enum TestEnum {
53+
MEMBER_ONE = 3,
54+
MEMBER_TWO,
55+
MEMBER_THREE = 5
56+
}
57+
58+
const valueOne = TestEnum.MEMBER_TWO;
59+
`;
60+
61+
Expect(util.transpileString(testCode)).toBe(`local valueOne = 4;`);
62+
}
63+
764
@Test("Invalid heterogeneous enum")
865
public invalidHeterogeneousEnum(): void {
966
// Transpile & Assert

0 commit comments

Comments
 (0)