From 94a301a27a8a75c4684790a99a898262b8354f68 Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Sun, 26 Feb 2017 15:13:22 +0100 Subject: math.random/randomseed --- README.md | 31 +--- package.json | 3 +- src/lapi.js | 2 +- src/lmathlib.js | 134 +++++++++++++---- src/luaconf.js | 5 +- src/lvm.js | 2 +- tests/lmathlib.js | 419 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 528 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index ded30f7..8468a25 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ - [x] lua_newtable - [x] lua_newthread - [x] lua_next + - [x] lua_numbertointeger - [x] lua_pcall - [x] lua_pop - [x] lua_pushboolean @@ -119,7 +120,6 @@ - [ ] lua_isthread - [ ] lua_isuserdata - [ ] lua_newuserdata - - [ ] lua_numbertointeger - [ ] lua_pcallk - [ ] lua_pushfstring - [ ] lua_pushlightuserdata @@ -206,34 +206,7 @@ - [ ] load - [x] Coroutine - [x] Table - - [ ] Math - - [x] math.abs - - [x] math.acos - - [x] math.asin - - [x] math.atan - - [x] math.cos - - [x] math.sin - - [x] math.tan - - [ ] math.ceil - - [ ] math.deg - - [ ] math.exp - - [ ] math.floor - - [ ] math.fmod - - [ ] math.huge - - [ ] math.log - - [ ] math.max - - [ ] math.maxinteger - - [ ] math.min - - [ ] math.mininteger - - [ ] math.modf - - [ ] math.pi - - [ ] math.rad - - [ ] math.random - - [ ] math.randomseed - - [ ] math.sqrt - - [ ] math.tointeger - - [ ] math.type - - [ ] math.ult + - [x] Math - [ ] Run [Lua test suite](https://github.com/lua/tests) - [ ] DOM API binding - [ ] Parse Lua diff --git a/package.json b/package.json index 390136f..7496921 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "tmp": "0.0.31" }, "dependencies": { - "buffer-dataview": "0.0.2" + "buffer-dataview": "0.0.2", + "seedrandom": "^2.4.2" } } diff --git a/src/lapi.js b/src/lapi.js index 4eb55cd..d0c099f 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -545,7 +545,7 @@ const lua_topointer = function(L, idx) { const lua_compare = function(L, index1, index2, op) { let o1 = index2addr(L, index1); - let o2 = index2addr(L, index1); + let o2 = index2addr(L, index2); return lua_compare_(L, o1, o2, op); }; diff --git a/src/lmathlib.js b/src/lmathlib.js index f4cc978..54bdc46 100644 --- a/src/lmathlib.js +++ b/src/lmathlib.js @@ -1,18 +1,55 @@ /* jshint esversion: 6 */ "use strict"; -const assert = require('assert'); +const assert = require('assert'); +const seedrandom = require('seedrandom'); -const lua = require('./lua.js'); -const lapi = require('./lapi.js'); -const lauxlib = require('./lauxlib.js'); -const lstate = require('./lstate.js'); -const ldo = require('./ldo.js'); -const ldebug = require('./ldebug.js'); -const llimit = require('./llimit.js'); -const CT = lua.constant_types; -const TS = lua.thread_status; +const lua = require('./lua.js'); +const lapi = require('./lapi.js'); +const lauxlib = require('./lauxlib.js'); +const lstate = require('./lstate.js'); +const ldo = require('./ldo.js'); +const ldebug = require('./ldebug.js'); +const llimit = require('./llimit.js'); +const luaconf = require('./luaconf.js'); +const CT = lua.constant_types; +const TS = lua.thread_status; +var RNG = seedrandom(); + +const math_randomseed = function(L) { + RNG = seedrandom(Math.abs(lauxlib.luaL_checknumber(L, 1))); +}; + +const math_random = function(L) { + let low, up; + let r = RNG(); + switch (lapi.lua_gettop(L)) { /* check number of arguments */ + case 0: + lapi.lua_pushnumber(L, r); /* Number between 0 and 1 */ + return 1; + case 1: { + low = 1; + up = lauxlib.luaL_checkinteger(L, 1); + break; + } + case 2: { + low = lauxlib.luaL_checkinteger(L, 1); + up = lauxlib.luaL_checkinteger(L, 2); + break; + } + default: return lauxlib.luaL_error(L, "wrong number of arguments"); + } + + /* random integer in the interval [low, up] */ + lauxlib.luaL_argcheck(L, low <= up, 1, "interval is empty"); + lauxlib.luaL_argcheck(L, low >= 0 || up <= Number.MAX_SAFE_INTEGER + low, 1, + "interval too large"); + + r *= (up - low) + 1; + lapi.lua_pushinteger(L, r + low); + return 1; +}; const math_abs = function(L) { if (lapi.lua_isinteger(L, 1)) @@ -53,7 +90,7 @@ const math_atan = function(L) { }; const math_toint = function(L) { - let n = lapi.lua_tointegerx(L, 1) + let n = lapi.lua_tointegerx(L, 1); if (n !== false) lapi.lua_pushinteger(L, n); else { @@ -91,6 +128,7 @@ const math_ceil = function(L) { const math_sqrt = function(L) { lapi.lua_pushnumber(L, Math.sqrt(lauxlib.luaL_checknumber(L, 1))); + return 1; }; const math_ult = function(L) { @@ -102,6 +140,7 @@ const math_ult = function(L) { const math_log = function(L) { let x = lauxlib.luaL_checknumber(L, 1); + let res; if (lapi.lua_isnoneornil(L, 2)) res = Math.log(x); else { @@ -129,6 +168,7 @@ const math_deg = function(L) { const math_rad = function(L) { lapi.lua_pushnumber(L, lauxlib.luaL_checknumber(L, 1) * (Math.PI / 180)); + return 1; }; const math_min = function(L) { @@ -168,27 +208,59 @@ const math_type = function(L) { return 1; }; +const math_fmod = function(L) { + if (lapi.lua_isinteger(L, 1) && lapi.lua_isinteger(L, 2)) { + let d = lapi.lua_tointeger(L, 2); + if (Math.abs(d) + 1 <= 1) { + lauxlib.luaL_argcheck(L, d !== 0, 2, "zero"); + lapi.lua_pushinteger(L, 0); + } else + lapi.lua_pushinteger(L, lapi.lua_tointeger(L, 1) % d); + } else { + let a = lauxlib.luaL_checknumber(L, 1); + let b = lauxlib.luaL_checknumber(L, 2); + lapi.lua_pushnumber(L, Number((a - (Math.floor(a / b) * b)).toPrecision(8))); + } + return 1; +}; + +const math_modf = function(L) { + if (lapi.lua_isinteger(L, 1)) { + lapi.lua_settop(L, 1); /* number is its own integer part */ + lapi.lua_pushnumber(L, 0); /* no fractional part */ + } else { + let n = lauxlib.luaL_checknumber(L, 1); + let ip = n < 0 ? Math.ceil(n) : Math.floor(n); + pushnumint(L, ip); + lapi.lua_pushnumber(L, n === ip ? 0 : n - ip); + } + return 2; +}; + const mathlib = { - "abs": math_abs, - "acos": math_acos, - "asin": math_asin, - "atan": math_atan, - "ceil": math_ceil, - "cos": math_cos, - "deg": math_deg, - "exp": math_exp, - "floor": math_floor, - "log": math_log, - "max": math_max, - "min": math_min, - "rad": math_rad, - "sin": math_sin, - "sqrt": math_sqrt, - "sqrt": math_sqrt, - "tan": math_tan, - "tointeger": math_toint, - "type": math_type, - "ult": math_ult + "abs": math_abs, + "acos": math_acos, + "asin": math_asin, + "atan": math_atan, + "ceil": math_ceil, + "cos": math_cos, + "deg": math_deg, + "exp": math_exp, + "floor": math_floor, + "fmod": math_fmod, + "log": math_log, + "max": math_max, + "min": math_min, + "modf": math_modf, + "rad": math_rad, + "random": math_random, + "randomseed": math_randomseed, + "sin": math_sin, + "sqrt": math_sqrt, + "tan": math_tan, + "tointeger": math_toint, + "type": math_type, + "ult": math_ult }; const luaopen_math = function(L) { diff --git a/src/luaconf.js b/src/luaconf.js index 7297b28..f0066de 100644 --- a/src/luaconf.js +++ b/src/luaconf.js @@ -20,5 +20,6 @@ const lua_numbertointeger = function(n) { return n|0; }; -module.exports.LUAI_MAXSTACK = LUAI_MAXSTACK; -module.exports.LUA_IDSIZE = LUA_IDSIZE; \ No newline at end of file +module.exports.LUAI_MAXSTACK = LUAI_MAXSTACK; +module.exports.LUA_IDSIZE = LUA_IDSIZE; +module.exports.lua_numbertointeger = lua_numbertointeger; \ No newline at end of file diff --git a/src/lvm.js b/src/lvm.js index d623a72..d987cc1 100644 --- a/src/lvm.js +++ b/src/lvm.js @@ -838,7 +838,7 @@ const luaV_tointeger = function(obj, mode) { } else if (obj.ttisinteger()) { return obj.value|0; } else if (obj.ttisstring()) { - return luaV_tointeger(parseFloat(obj.value), mode); // TODO: luaO_str2num + return luaV_tointeger(new TValue(CT.LUA_TNUMFLT, parseFloat(obj.value)), mode); // TODO: luaO_str2num } return false; diff --git a/tests/lmathlib.js b/tests/lmathlib.js index 4c886f1..ba6853c 100644 --- a/tests/lmathlib.js +++ b/tests/lmathlib.js @@ -20,7 +20,8 @@ const CT = lua.constant_types; test('math.abs, math.sin, math.cos, math.tan, math.asin, math.acos, math.atan', function (t) { let luaCode = ` - return math.abs(-10), math.abs(-10.5), math.cos(10), math.tan(10), math.asin(1), math.acos(0.5), math.atan(10) + return math.abs(-10), math.abs(-10.5), math.cos(10), math.tan(10), + math.asin(1), math.acos(0.5), math.atan(10) `, L; t.plan(8); @@ -33,7 +34,7 @@ test('math.abs, math.sin, math.cos, math.tan, math.asin, math.acos, math.atan', linit.luaL_openlibs(L); - lapi.lua_load(L, bc, "test-math.abs"); + lapi.lua_load(L, bc, "test-math"); lapi.lua_call(L, 0, -1); @@ -80,4 +81,416 @@ test('math.abs, math.sin, math.cos, math.tan, math.asin, math.acos, math.atan', 1.4711276743037347, "Correct element(s) on the stack" ); -}); \ No newline at end of file +}); + + +test('math.ceil, math.floor', function (t) { + let luaCode = ` + return math.ceil(10.5), math.floor(10.5) + `, L; + + t.plan(3); + + t.doesNotThrow(function () { + + let bc = toByteCode(luaCode).dataView; + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lapi.lua_load(L, bc, "test-math"); + + lapi.lua_call(L, 0, -1); + + }, "JS Lua program ran without error"); + + t.strictEqual( + lapi.lua_tointeger(L, -2), + 11, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tointeger(L, -1), + 10, + "Correct element(s) on the stack" + ); + +}); + + +test('math.deg, math.rad', function (t) { + let luaCode = ` + return math.deg(10), math.rad(10) + `, L; + + t.plan(3); + + t.doesNotThrow(function () { + + let bc = toByteCode(luaCode).dataView; + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lapi.lua_load(L, bc, "test-math"); + + lapi.lua_call(L, 0, -1); + + }, "JS Lua program ran without error"); + + t.strictEqual( + lapi.lua_tonumber(L, -2), + 572.9577951308232, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tonumber(L, -1), + 0.17453292519943295, + "Correct element(s) on the stack" + ); + +}); + + +test('math.log', function (t) { + let luaCode = ` + return math.log(10), math.log(10, 2), math.log(10, 10) + `, L; + + t.plan(4); + + t.doesNotThrow(function () { + + let bc = toByteCode(luaCode).dataView; + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lapi.lua_load(L, bc, "test-math"); + + lapi.lua_call(L, 0, -1); + + }, "JS Lua program ran without error"); + + t.strictEqual( + lapi.lua_tonumber(L, -3), + 2.302585092994046, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tonumber(L, -2), + 3.321928094887362, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tonumber(L, -1), + 1, + "Correct element(s) on the stack" + ); + +}); + + +test('math.exp', function (t) { + let luaCode = ` + return math.exp(10) + `, L; + + t.plan(2); + + t.doesNotThrow(function () { + + let bc = toByteCode(luaCode).dataView; + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lapi.lua_load(L, bc, "test-math"); + + lapi.lua_call(L, 0, -1); + + }, "JS Lua program ran without error"); + + t.strictEqual( + lapi.lua_tonumber(L, -1), + 22026.465794806718, + "Correct element(s) on the stack" + ); + +}); + + +test('math.min, math.max', function (t) { + let luaCode = ` + return math.max(10, 5, 23), math.min(10, 5, 23) + `, L; + + t.plan(3); + + t.doesNotThrow(function () { + + let bc = toByteCode(luaCode).dataView; + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lapi.lua_load(L, bc, "test-math"); + + lapi.lua_call(L, 0, -1); + + }, "JS Lua program ran without error"); + + t.strictEqual( + lapi.lua_tonumber(L, -2), + 23, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tonumber(L, -1), + 5, + "Correct element(s) on the stack" + ); + +}); + + +test('math.random', function (t) { + let luaCode = ` + return math.random(), math.random(10, 15) + `, L; + + t.plan(3); + + t.doesNotThrow(function () { + + let bc = toByteCode(luaCode).dataView; + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lapi.lua_load(L, bc, "test-math"); + + lapi.lua_call(L, 0, -1); + + }, "JS Lua program ran without error"); + + t.ok( + 0 <= lapi.lua_tonumber(L, -2) <= 1, + "Correct element(s) on the stack" + ); + + t.ok( + 10 <= lapi.lua_tonumber(L, -1) <= 15, + "Correct element(s) on the stack" + ); + +}); + + +test('math.sqrt', function (t) { + let luaCode = ` + return math.sqrt(10) + `, L; + + t.plan(2); + + t.doesNotThrow(function () { + + let bc = toByteCode(luaCode).dataView; + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lapi.lua_load(L, bc, "test-math"); + + lapi.lua_call(L, 0, -1); + + }, "JS Lua program ran without error"); + + t.strictEqual( + lapi.lua_tonumber(L, -1), + 3.1622776601683795, + "Correct element(s) on the stack" + ); + +}); + + +test('math.tointeger', function (t) { + let luaCode = ` + return math.tointeger('10') + `, L; + + t.plan(2); + + t.doesNotThrow(function () { + + let bc = toByteCode(luaCode).dataView; + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lapi.lua_load(L, bc, "test-math"); + + lapi.lua_call(L, 0, -1); + + }, "JS Lua program ran without error"); + + t.strictEqual( + lapi.lua_tonumber(L, -1), + 10, + "Correct element(s) on the stack" + ); + +}); + + +test('math.type', function (t) { + let luaCode = ` + return math.type(10), math.type(10.5), math.type('hello') + `, L; + + t.plan(4); + + t.doesNotThrow(function () { + + let bc = toByteCode(luaCode).dataView; + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lapi.lua_load(L, bc, "test-math"); + + lapi.lua_call(L, 0, -1); + + }, "JS Lua program ran without error"); + + t.strictEqual( + lapi.lua_tostring(L, -3), + "integer", + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tostring(L, -2), + "float", + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tostring(L, -1), + null, + "Correct element(s) on the stack" + ); + +}); + + +test('math.ult', function (t) { + let luaCode = ` + return math.tointeger('10') + `, L; + + t.plan(2); + + t.doesNotThrow(function () { + + let bc = toByteCode(luaCode).dataView; + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lapi.lua_load(L, bc, "test-math"); + + lapi.lua_call(L, 0, -1); + + }, "JS Lua program ran without error"); + + t.strictEqual( + lapi.lua_toboolean(L, -1), + true, + "Correct element(s) on the stack" + ); + +}); + + +test('math.fmod', function (t) { + let luaCode = ` + return math.fmod(2,5) + `, L; + + t.plan(2); + + t.doesNotThrow(function () { + + let bc = toByteCode(luaCode).dataView; + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lapi.lua_load(L, bc, "test-math"); + + lapi.lua_call(L, 0, -1); + + }, "JS Lua program ran without error"); + + t.strictEqual( + lapi.lua_tonumber(L, -1), + 2, + "Correct element(s) on the stack" + ); + +}); + + +test('math.modf', function (t) { + let luaCode = ` + return math.modf(3.4, 0.6) + `, L; + + t.plan(3); + + t.doesNotThrow(function () { + + let bc = toByteCode(luaCode).dataView; + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lapi.lua_load(L, bc, "test-math"); + + lapi.lua_call(L, 0, -1); + + }, "JS Lua program ran without error"); + + t.strictEqual( + lapi.lua_tonumber(L, -2), + 3, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tonumber(L, -1), + 0.3999999999999999, + "Correct element(s) on the stack" + ); + +}); -- cgit v1.2.3-70-g09d2