Files
plainleaf/client/space_lua/numeric.ts
T
Matouš Jan FialkaandGitHub 27339c98f6 Space Lua: Align arithmetic model with standard Lua (#1611)
* Align Space Lua arithmetic model with standard Lua
* Add missing __unm and __bnot metamethods
- add new basic tests
- add helper for negative tests
* Add comments for ASCII numeric constants

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2025-10-16 08:41:47 +02:00

137 lines
2.6 KiB
TypeScript

import type { NumericType } from "./ast.ts";
import { luaToNumberDetailed } from "./tonumber.ts";
export const ZeroBoxKind = Symbol("ZeroBox");
export function isBoxedZero(x: any): boolean {
return x instanceof Number && Number(x) === 0 && !Object.is(Number(x), -0);
}
export function getZeroBoxKind(x: any): NumericType | undefined {
return isBoxedZero(x)
? (x as any)[ZeroBoxKind] as NumericType | undefined
: undefined;
}
export function boxZero(kind: NumericType): number {
const z = new Number(0);
(z as any)[ZeroBoxKind] = kind;
return z as unknown as number;
}
export type OpHints = {
leftKind?: NumericType;
rightKind?: NumericType;
};
export function coerceNumeric(
val: unknown,
): {
n: number;
isInt: boolean;
zeroKind?: NumericType;
} {
if (isBoxedZero(val)) {
return { n: 0, isInt: true, zeroKind: getZeroBoxKind(val)! };
}
if (typeof val === "number") {
const n = val;
if (n === 0) {
if (Object.is(n, -0)) {
return {
n,
isInt: true,
zeroKind: "float",
};
}
return {
n,
isInt: true,
};
}
return {
n,
isInt: Number.isInteger(n),
};
}
if (val instanceof Number) {
const n = Number(val);
if (n === 0 && !Object.is(n, -0)) {
return {
n,
isInt: true,
zeroKind: getZeroBoxKind(val),
};
}
return {
n,
isInt: Number.isInteger(n),
};
}
if (typeof val === "string") {
const det = luaToNumberDetailed(val);
if (!det) {
throw new Error(`attempt to perform arithmetic on a non-number`);
}
const n = det.value;
const isInt = det.numericType === "int";
if (n === 0) {
if (Object.is(n, -0)) {
return {
n,
isInt,
zeroKind: "float",
};
}
return {
n,
isInt,
zeroKind: det.numericType,
};
}
return {
n,
isInt,
};
}
throw new Error(`attempt to perform arithmetic on a non-number`);
}
export function coerceNumericPair(
a: unknown,
b: unknown,
hints?: OpHints,
): {
ax: number;
bx: number;
bothInt: boolean;
aZeroKind?: NumericType;
bZeroKind?: NumericType;
} {
const A = coerceNumeric(a);
const B = coerceNumeric(b);
const aIsInt = hints?.leftKind
? hints.leftKind === "int"
: (A.zeroKind ? A.zeroKind === "int" : A.isInt);
const bIsInt = hints?.rightKind
? hints.rightKind === "int"
: (B.zeroKind ? B.zeroKind === "int" : B.isInt);
return {
ax: A.n,
bx: B.n,
bothInt: aIsInt && bIsInt,
aZeroKind: A.zeroKind,
bZeroKind: B.zeroKind,
};
}