From bf62b972eb3c7f74ec7da87ff3a10bc6f0dbbdfb Mon Sep 17 00:00:00 2001 From: Lennart Ochel Date: Wed, 7 May 2025 18:54:44 +0200 Subject: [PATCH] Add missing case to math.random and check arguments (#1372) --- common/space_lua/stdlib/math.ts | 64 +++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/common/space_lua/stdlib/math.ts b/common/space_lua/stdlib/math.ts index 8ad44235..5447ea63 100644 --- a/common/space_lua/stdlib/math.ts +++ b/common/space_lua/stdlib/math.ts @@ -5,18 +5,68 @@ import { } from "$common/space_lua/runtime.ts"; export const mathApi = new LuaTable({ - // Random number generation + /** + * When called without arguments, returns a pseudo-random float with + * uniform distribution in the range [0,1). When called with two + * integers m and n, math.random returns a pseudo-random integer + * with uniform distribution in the range [m, n]. The call + * math.random(n), for a positive n, is equivalent to + * math.random(1,n). The call math.random(0) produces an integer + * with all bits (pseudo)random. + */ random: new LuaBuiltinFunction((_sf, m?: number, n?: number) => { if (m === undefined && n === undefined) { // random() returns [0,1) return Math.random(); - } else if (n === undefined) { - // random(m) returns [1,m] - return Math.floor(Math.random() * m!) + 1; - } else { - // random(m,n) returns [m,n] - return Math.floor(Math.random() * (n - m! + 1)) + m!; } + + if (!Number.isInteger(m)) { + throw new LuaRuntimeError( + "bad argument #1 to 'math.random' (integer expected)", + _sf, + ); + } + + if (n === undefined) { + if (m! == 0) { + // random(0) returns a random integer + const high = Math.floor(Math.random() * 0x100000000); + const low = Math.floor(Math.random() * 0x100000000); + + let result = (BigInt(high) << 32n) | BigInt(low); + + if (result & (1n << 63n)) { + result -= 1n << 64n; + } + + return result; + } else { + // random(m) returns [1,m] + if (m! < 1) { + throw new LuaRuntimeError( + "bad argument #1 to 'math.random' (interval is empty)", + _sf, + ); + } + return Math.floor(Math.random() * m!) + 1; + } + } + + if (!Number.isInteger(n!)) { + throw new LuaRuntimeError( + "bad argument #2 to 'math.random' (integer expected)", + _sf, + ); + } + + // random(m,n) returns [m,n] + if (n! < m!) { + throw new LuaRuntimeError( + "bad argument #1 to 'math.random' (interval is empty)", + _sf, + ); + } + return Math.floor(Math.random() * (n! - m! + 1)) + m!; }), // Basic functions