Skip to content

Commit 2ec3234

Browse files
ark120202Perryvw
authored andcommitted
Add NaN, Infinity and related number functions (#557)
* Add NaN, Infinity and related number functions * Add noSelfInFile to global declorations file * Add few number test cases * Remove TODOs
1 parent 3de430d commit 2ec3234

File tree

12 files changed

+230
-35
lines changed

12 files changed

+230
-35
lines changed

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module.exports = {
99
// https://github.com/facebook/jest/issues/5274
1010
"!<rootDir>/src/tstl.ts",
1111
],
12-
watchPathIgnorePatterns: ["cli/watch/[^/]+$"],
12+
watchPathIgnorePatterns: ["cli/watch/[^/]+$", "src/lualib"],
1313

1414
setupFilesAfterEnv: ["<rootDir>/test/setup.ts"],
1515
testEnvironment: "node",

src/LuaLib.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ export enum LuaLibFeature {
3131
Iterator = "Iterator",
3232
Map = "Map",
3333
NewIndex = "NewIndex",
34+
Number = "Number",
35+
NumberIsFinite = "NumberIsFinite",
36+
NumberIsNaN = "NumberIsNaN",
3437
ObjectAssign = "ObjectAssign",
3538
ObjectEntries = "ObjectEntries",
3639
ObjectFromEntries = "ObjectFromEntries",

src/LuaTransformer.ts

Lines changed: 82 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3320,7 +3320,7 @@ export class LuaTransformer {
33203320
const expression = this.expectExpression(this.transformExpression(element.initializer));
33213321
properties.push(tstl.createTableFieldExpression(expression, name, element));
33223322
} else if (ts.isShorthandPropertyAssignment(element)) {
3323-
const identifier = this.transformIdentifier(element.name);
3323+
const identifier = this.transformIdentifierExpression(element.name);
33243324
properties.push(tstl.createTableFieldExpression(identifier, name, element));
33253325
} else if (ts.isMethodDeclaration(element)) {
33263326
const expression = this.expectExpression(this.transformFunctionExpression(element));
@@ -3523,6 +3523,14 @@ export class LuaTransformer {
35233523
);
35243524
}
35253525

3526+
const expressionType = this.checker.getTypeAtLocation(node.expression);
3527+
if (tsHelper.isStandardLibraryType(expressionType, undefined, this.program)) {
3528+
const result = this.transformGlobalFunctionCall(node);
3529+
if (result) {
3530+
return result;
3531+
}
3532+
}
3533+
35263534
const callPath = this.expectExpression(this.transformExpression(node.expression));
35273535
const signatureDeclaration = signature && signature.getDeclaration();
35283536
if (signatureDeclaration
@@ -3534,15 +3542,35 @@ export class LuaTransformer {
35343542
parameters = this.transformArguments(node.arguments, signature, context);
35353543
}
35363544

3537-
const expressionType = this.checker.getTypeAtLocation(node.expression);
3538-
if (tsHelper.isStandardLibraryType(expressionType, "SymbolConstructor", this.program)) {
3539-
return this.transformLuaLibFunction(LuaLibFeature.Symbol, node, ...parameters);
3540-
}
3541-
35423545
const callExpression = tstl.createCallExpression(callPath, parameters, node);
35433546
return wrapResult ? this.wrapInTable(callExpression) : callExpression;
35443547
}
35453548

3549+
private transformGlobalFunctionCall(node: ts.CallExpression): ExpressionVisitResult {
3550+
const signature = this.checker.getResolvedSignature(node);
3551+
const parameters = this.transformArguments(node.arguments, signature);
3552+
3553+
const expressionType = this.checker.getTypeAtLocation(node.expression);
3554+
const name = expressionType.symbol.name;
3555+
switch (name) {
3556+
case "SymbolConstructor":
3557+
return this.transformLuaLibFunction(LuaLibFeature.Symbol, node, ...parameters);
3558+
case "NumberConstructor":
3559+
return this.transformLuaLibFunction(LuaLibFeature.Number, node, ...parameters);
3560+
case "isNaN":
3561+
case "isFinite":
3562+
const numberParameters = tsHelper.isNumberType(expressionType)
3563+
? parameters
3564+
: [this.transformLuaLibFunction(LuaLibFeature.Number, undefined, ...parameters)];
3565+
3566+
return this.transformLuaLibFunction(
3567+
name === "isNaN" ? LuaLibFeature.NumberIsNaN : LuaLibFeature.NumberIsFinite,
3568+
node,
3569+
...numberParameters
3570+
);
3571+
}
3572+
}
3573+
35463574
public transformPropertyCall(node: ts.CallExpression): ExpressionVisitResult {
35473575
let parameters: tstl.Expression[] = [];
35483576

@@ -3580,6 +3608,10 @@ export class LuaTransformer {
35803608
return this.transformSymbolCallExpression(node);
35813609
}
35823610

3611+
if (tsHelper.isStandardLibraryType(ownerType, "NumberConstructor", this.program)) {
3612+
return this.transformNumberCallExpression(node);
3613+
}
3614+
35833615
const classDecorators = tsHelper.getCustomDecorators(ownerType, this.checker);
35843616

35853617
if (classDecorators.has(DecoratorKind.LuaTable)) {
@@ -4290,6 +4322,26 @@ export class LuaTransformer {
42904322
}
42914323
}
42924324

4325+
// Transpile a Number._ property
4326+
private transformNumberCallExpression(expression: ts.CallExpression): tstl.CallExpression {
4327+
const method = expression.expression as ts.PropertyAccessExpression;
4328+
const parameters = this.transformArguments(expression.arguments);
4329+
const methodName = method.name.escapedText;
4330+
4331+
switch (methodName) {
4332+
case "isNaN":
4333+
return this.transformLuaLibFunction(LuaLibFeature.NumberIsNaN, expression, ...parameters);
4334+
case "isFinite":
4335+
return this.transformLuaLibFunction(LuaLibFeature.NumberIsFinite, expression, ...parameters);
4336+
default:
4337+
throw TSTLErrors.UnsupportedForTarget(
4338+
`number property ${methodName}`,
4339+
this.luaTarget,
4340+
expression
4341+
);
4342+
}
4343+
}
4344+
42934345
private validateLuaTableCall(
42944346
expression: ts.CallExpression & { expression: ts.PropertyAccessExpression },
42954347
isWithinExpressionStatement: boolean
@@ -4575,18 +4627,13 @@ export class LuaTransformer {
45754627
}
45764628

45774629
private getIdentifierText(identifier: ts.Identifier): string {
4578-
let escapedText = identifier.escapedText as string;
4579-
const underScoreCharCode = "_".charCodeAt(0);
4580-
if (escapedText.length >= 3 && escapedText.charCodeAt(0) === underScoreCharCode &&
4581-
escapedText.charCodeAt(1) === underScoreCharCode && escapedText.charCodeAt(2) === underScoreCharCode) {
4582-
escapedText = escapedText.substr(1);
4583-
}
4630+
const text = ts.idText(identifier);
45844631

4585-
if (this.luaKeywords.has(escapedText)) {
4632+
if (this.luaKeywords.has(text)) {
45864633
throw TSTLErrors.KeywordIdentifier(identifier);
45874634
}
45884635

4589-
return escapedText;
4636+
return text;
45904637
}
45914638

45924639
public transformIdentifier(expression: ts.Identifier): tstl.Identifier {
@@ -4597,16 +4644,34 @@ export class LuaTransformer {
45974644
// at some point.
45984645
}
45994646

4600-
const escapedText = this.getIdentifierText(expression);
4647+
const text = this.getIdentifierText(expression);
46014648
const symbolId = this.getIdentifierSymbolId(expression);
4602-
return tstl.createIdentifier(escapedText, expression, symbolId);
4649+
return tstl.createIdentifier(text, expression, symbolId);
46034650
}
46044651

4605-
private transformIdentifierExpression(expression: ts.Identifier): tstl.IdentifierOrTableIndexExpression {
4652+
private transformIdentifierExpression(expression: ts.Identifier): tstl.Expression {
46064653
const identifier = this.transformIdentifier(expression);
46074654
if (this.isIdentifierExported(identifier)) {
46084655
return this.createExportedIdentifier(identifier);
46094656
}
4657+
4658+
switch (this.getIdentifierText(expression)) {
4659+
case "NaN":
4660+
return tstl.createParenthesizedExpression(
4661+
tstl.createBinaryExpression(
4662+
tstl.createNumericLiteral(0),
4663+
tstl.createNumericLiteral(0),
4664+
tstl.SyntaxKind.DivisionOperator,
4665+
expression
4666+
)
4667+
);
4668+
4669+
case "Infinity":
4670+
const math = tstl.createIdentifier("math");
4671+
const huge = tstl.createStringLiteral("huge");
4672+
return tstl.createTableIndexExpression(math, huge, expression);
4673+
}
4674+
46104675
return identifier;
46114676
}
46124677

src/TSHelper.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ export class TSHelper {
141141
(type.flags & ts.TypeFlags.StringLiteral) !== 0;
142142
}
143143

144+
public static isNumberType(type: ts.Type): boolean {
145+
return (type.flags & ts.TypeFlags.Number) !== 0 || (type.flags & ts.TypeFlags.NumberLike) !== 0 ||
146+
(type.flags & ts.TypeFlags.NumberLiteral) !== 0;
147+
}
148+
144149
public static isExplicitArrayType(type: ts.Type, checker: ts.TypeChecker, program: ts.Program): boolean {
145150
if (type.isUnionOrIntersection()) {
146151
return type.types.some(t => TSHelper.isExplicitArrayType(t, checker, program));
@@ -731,9 +736,12 @@ export class TSHelper {
731736
return program.isSourceFileDefaultLibrary(source);
732737
}
733738

734-
public static isStandardLibraryType(type: ts.Type, name: string, program: ts.Program): boolean {
735-
const symbol = type.symbol;
736-
if (!symbol || symbol.escapedName !== name) { return false; }
739+
public static isStandardLibraryType(type: ts.Type, name: string | undefined, program: ts.Program): boolean {
740+
const symbol = type.getSymbol();
741+
if (!symbol || (name ? symbol.escapedName !== name : symbol.escapedName === '__type')) {
742+
return false;
743+
}
744+
737745
const declaration = symbol.valueDeclaration;
738746
if(!declaration) { return true; } // assume to be lib function if no valueDeclaration exists
739747
return this.isStandardLibraryDeclaration(declaration, program);

src/lualib/ArraySetLength.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
function __TS__ArraySetLength<T>(this: void, arr: T[], length: number): number {
22
if (length < 0
33
|| length !== length // NaN
4-
|| length === (1 / 0) // Infinity
4+
|| length === Infinity // Infinity
55
|| Math.floor(length) !== length) // non-integer
66
{
77
// tslint:disable-next-line:no-string-throw

src/lualib/Number.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
function __TS__Number(this: void, value: unknown): number {
2+
const valueType = type(value);
3+
if (valueType === "number") {
4+
return value as number;
5+
} else if (valueType === "string") {
6+
const numberValue = tonumber(value);
7+
if (numberValue) return numberValue;
8+
9+
if (value === "Infinity") return Infinity;
10+
if (value === "-Infinity") return -Infinity;
11+
const [stringWithoutSpaces] = string.gsub(value as string, '%s', '');
12+
if (stringWithoutSpaces === "") return 0;
13+
14+
return NaN;
15+
} else if (valueType === "boolean") {
16+
return value ? 1 : 0;
17+
} else {
18+
return NaN;
19+
}
20+
}

src/lualib/NumberIsFinite.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function __TS__NumberIsFinite(this: void, value: unknown): boolean {
2+
return (
3+
typeof value === "number" && value === value && value !== Infinity && value !== -Infinity
4+
);
5+
}

src/lualib/NumberIsNaN.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
function __TS__NumberIsNaN(this: void, value: unknown): boolean {
2+
return value !== value;
3+
}

src/lualib/Symbol.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,18 @@ declare function setmetatable<T extends object>(this: void, obj: T, metatable: a
44
const ____symbolMetatable = {
55
__tostring(): string {
66
if (this.description === undefined) {
7-
return 'Symbol()';
7+
return "Symbol()";
88
} else {
9-
return 'Symbol(' + this.description + ')';
9+
return "Symbol(" + this.description + ")";
1010
}
1111
},
1212
};
1313

14-
function __TS__Symbol(description?: string | number): symbol {
14+
function __TS__Symbol(this: void, description?: string | number): symbol {
1515
return setmetatable({ description }, ____symbolMetatable) as any;
1616
}
1717

1818
Symbol = {
19-
iterator: __TS__Symbol('Symbol.iterator'),
20-
hasInstance: __TS__Symbol('Symbol.hasInstance'),
19+
iterator: __TS__Symbol("Symbol.iterator"),
20+
hasInstance: __TS__Symbol("Symbol.hasInstance"),
2121
} as any;
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
/** @noSelfInFile */
2+
3+
declare function tonumber(value: any, base?: number): number | undefined;
14
declare function type(
2-
this: void,
35
value: any
46
): "nil" | "number" | "string" | "boolean" | "table" | "function" | "thread" | "userdata";

0 commit comments

Comments
 (0)