Skip to content

Commit 5d1e48d

Browse files
committed
Simplify and improve enum transform
1 parent f7cb2a2 commit 5d1e48d

4 files changed

Lines changed: 53 additions & 131 deletions

File tree

src/LuaTransformer.ts

Lines changed: 53 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,115 +1654,77 @@ export class LuaTransformer {
16541654
return result;
16551655
}
16561656

1657-
public transformEnumDeclaration(enumDeclaration: ts.EnumDeclaration): StatementVisitResult {
1658-
const type = this.checker.getTypeAtLocation(enumDeclaration);
1659-
1660-
// Const enums should never appear in the resulting code
1661-
if (type.symbol.getFlags() & ts.SymbolFlags.ConstEnum) {
1657+
public transformEnumDeclaration(node: ts.EnumDeclaration): StatementVisitResult {
1658+
if (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Const && !this.options.preserveConstEnums) {
16621659
return undefined;
16631660
}
16641661

1662+
const type = this.checker.getTypeAtLocation(node);
16651663
const membersOnly = tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.CompileMembersOnly);
1666-
16671664
const result: tstl.Statement[] = [];
16681665

16691666
if (!membersOnly) {
1670-
const name = this.transformIdentifier(enumDeclaration.name);
1667+
const name = this.transformIdentifier(node.name);
16711668
const table = tstl.createTableExpression();
1672-
result.push(...this.createLocalOrExportedOrGlobalDeclaration(name, table, enumDeclaration));
1669+
result.push(...this.createLocalOrExportedOrGlobalDeclaration(name, table, node));
16731670
}
16741671

1675-
for (const enumMember of this.computeEnumMembers(enumDeclaration)) {
1676-
const memberName = this.transformPropertyName(enumMember.name);
1677-
if (membersOnly) {
1678-
const enumSymbol = this.checker.getSymbolAtLocation(enumDeclaration.name);
1679-
const exportScope = enumSymbol ? this.getSymbolExportScope(enumSymbol) : undefined;
1672+
const enumReference = this.transformExpression(node.name);
1673+
for (const member of node.members) {
1674+
const memberName = this.transformPropertyName(member.name);
16801675

1681-
if (tstl.isIdentifier(memberName)) {
1682-
result.push(
1683-
...this.createLocalOrExportedOrGlobalDeclaration(
1684-
memberName,
1685-
enumMember.value,
1686-
enumDeclaration,
1687-
undefined,
1688-
exportScope
1689-
)
1690-
);
1691-
} else {
1692-
result.push(
1693-
...this.createLocalOrExportedOrGlobalDeclaration(
1694-
tstl.createIdentifier(enumMember.name.getText(), enumMember.name),
1695-
enumMember.value,
1696-
enumDeclaration,
1697-
undefined,
1698-
exportScope
1699-
)
1700-
);
1676+
let valueExpression: tstl.Expression | undefined;
1677+
const constEnumValue = this.tryGetConstEnumValue(member);
1678+
if (constEnumValue) {
1679+
valueExpression = constEnumValue;
1680+
} else if (member.initializer) {
1681+
if (ts.isIdentifier(member.initializer)) {
1682+
const symbol = this.checker.getSymbolAtLocation(member.initializer);
1683+
if (
1684+
symbol &&
1685+
symbol.valueDeclaration &&
1686+
ts.isEnumMember(symbol.valueDeclaration) &&
1687+
symbol.valueDeclaration.parent === node
1688+
) {
1689+
const otherMemberName = this.transformPropertyName(symbol.valueDeclaration.name);
1690+
valueExpression = tstl.createTableIndexExpression(enumReference, otherMemberName);
1691+
}
17011692
}
1702-
} else {
1703-
const enumTable = this.transformIdentifierExpression(enumDeclaration.name);
1704-
const property = tstl.createTableIndexExpression(enumTable, memberName);
1705-
result.push(tstl.createAssignmentStatement(property, enumMember.value, enumMember.original));
17061693

1707-
const valueIndex = tstl.createTableIndexExpression(enumTable, enumMember.value);
1708-
result.push(tstl.createAssignmentStatement(valueIndex, memberName, enumMember.original));
1709-
}
1710-
}
1711-
1712-
return result;
1713-
}
1714-
1715-
protected computeEnumMembers(
1716-
node: ts.EnumDeclaration
1717-
): Array<{ name: ts.PropertyName; value: tstl.Expression; original: ts.Node }> {
1718-
let numericValue = 0;
1719-
let hasStringInitializers = false;
1720-
1721-
const valueMap = new Map<ts.PropertyName, ExpressionVisitResult>();
1722-
1723-
return node.members.map(member => {
1724-
let valueExpression: ExpressionVisitResult;
1725-
if (member.initializer) {
1726-
if (ts.isNumericLiteral(member.initializer)) {
1727-
numericValue = Number(member.initializer.text);
1728-
valueExpression = this.transformNumericLiteral(member.initializer);
1729-
numericValue++;
1730-
} else if (ts.isStringLiteral(member.initializer)) {
1731-
hasStringInitializers = true;
1732-
valueExpression = this.transformStringLiteral(member.initializer);
1733-
} else {
1734-
if (ts.isIdentifier(member.initializer)) {
1735-
const [isEnumMember, originalName] = tsHelper.isEnumMember(node, member.initializer);
1736-
if (isEnumMember === true && originalName !== undefined) {
1737-
if (valueMap.has(originalName)) {
1738-
valueExpression = valueMap.get(originalName)!;
1739-
} else {
1740-
throw new Error(`Expected valueMap to contain ${originalName}`);
1741-
}
1742-
} else {
1743-
valueExpression = this.transformExpression(member.initializer);
1744-
}
1745-
} else {
1746-
valueExpression = this.transformExpression(member.initializer);
1747-
}
1694+
if (!valueExpression) {
1695+
valueExpression = this.transformExpression(member.initializer);
17481696
}
1749-
} else if (hasStringInitializers) {
1750-
throw TSTLErrors.HeterogeneousEnum(node);
17511697
} else {
1752-
valueExpression = tstl.createNumericLiteral(numericValue);
1753-
numericValue++;
1698+
valueExpression = tstl.createNilLiteral();
17541699
}
17551700

1756-
valueMap.set(member.name, valueExpression);
1701+
if (membersOnly) {
1702+
const enumSymbol = this.checker.getSymbolAtLocation(node.name);
1703+
const exportScope = enumSymbol ? this.getSymbolExportScope(enumSymbol) : undefined;
17571704

1758-
const enumMember = {
1759-
name: member.name,
1760-
original: member,
1761-
value: valueExpression,
1762-
};
1705+
result.push(
1706+
...this.createLocalOrExportedOrGlobalDeclaration(
1707+
tstl.isIdentifier(memberName)
1708+
? memberName
1709+
: tstl.createIdentifier(member.name.getText(), member.name),
1710+
valueExpression,
1711+
node,
1712+
undefined,
1713+
exportScope
1714+
)
1715+
);
1716+
} else {
1717+
const memberAccessor = tstl.createTableIndexExpression(enumReference, memberName);
1718+
result.push(tstl.createAssignmentStatement(memberAccessor, valueExpression, member));
17631719

1764-
return enumMember;
1765-
});
1720+
if (!tstl.isStringLiteral(valueExpression)) {
1721+
const reverseMemberAccessor = tstl.createTableIndexExpression(enumReference, memberAccessor);
1722+
result.push(tstl.createAssignmentStatement(reverseMemberAccessor, memberName, member));
1723+
}
1724+
}
1725+
}
1726+
1727+
return result;
17661728
}
17671729

17681730
protected transformGeneratorFunction(
@@ -4327,7 +4289,7 @@ export class LuaTransformer {
43274289
}
43284290

43294291
private tryGetConstEnumValue(
4330-
node: ts.PropertyAccessExpression | ts.ElementAccessExpression
4292+
node: ts.EnumMember | ts.PropertyAccessExpression | ts.ElementAccessExpression
43314293
): tstl.Expression | undefined {
43324294
const value = this.checker.getConstantValue(node);
43334295
if (typeof value === "string") {

src/TSHelper.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -808,26 +808,6 @@ export function isStandardLibraryType(type: ts.Type, name: string | undefined, p
808808
return isStandardLibraryDeclaration(declaration, program);
809809
}
810810

811-
export function isEnumMember(
812-
enumDeclaration: ts.EnumDeclaration,
813-
value: ts.Expression
814-
): [true, ts.PropertyName] | [false, undefined] {
815-
if (ts.isIdentifier(value)) {
816-
const enumMember = enumDeclaration.members.find(m => ts.isIdentifier(m.name) && m.name.text === value.text);
817-
if (enumMember !== undefined) {
818-
if (enumMember.initializer && ts.isIdentifier(enumMember.initializer)) {
819-
return isEnumMember(enumDeclaration, enumMember.initializer);
820-
} else {
821-
return [true, enumMember.name];
822-
}
823-
} else {
824-
return [false, undefined];
825-
}
826-
} else {
827-
return [false, undefined];
828-
}
829-
}
830-
831811
export function isWithinLiteralAssignmentStatement(node: ts.Node): boolean {
832812
if (!node.parent) {
833813
return false;

src/TSTLErrors.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,6 @@ export const InvalidInstanceOfLuaTable = (node: ts.Node) =>
3434
export const ForbiddenLuaTableUseException = (description: string, node: ts.Node) =>
3535
new TranspileError(`Invalid @luaTable usage: ${description}`, node);
3636

37-
export const HeterogeneousEnum = (node: ts.Node) =>
38-
new TranspileError(
39-
`Invalid heterogeneous enum. Enums should either specify no member values, ` +
40-
`or specify values (of the same type) for all members.`,
41-
node
42-
);
43-
4437
export const InvalidDecoratorArgumentNumber = (name: string, got: number, expected: number, node: ts.Node) =>
4538
new TranspileError(`${name} expects ${expected} argument(s) but got ${got}.`, node);
4639

test/unit/enum.spec.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import * as TSTLErrors from "../../src/TSTLErrors";
21
import * as util from "../util";
32

43
test("Declare const enum", () => {
@@ -54,18 +53,6 @@ test("Const enum without initializer in some values", () => {
5453
expect(util.transpileString(testCode)).toBe(`local valueOne = 4`);
5554
});
5655

57-
test("Invalid heterogeneous enum", () => {
58-
expect(() => {
59-
util.transpileString(`
60-
enum TestEnum {
61-
a,
62-
b = "ok",
63-
c,
64-
}
65-
`);
66-
}).toThrowExactError(TSTLErrors.HeterogeneousEnum(util.nodeStub));
67-
});
68-
6956
test("String literal name in enum", () => {
7057
const code = `
7158
enum TestEnum {

0 commit comments

Comments
 (0)