summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md31
-rw-r--r--package.json3
-rw-r--r--src/lapi.js2
-rw-r--r--src/lmathlib.js134
-rw-r--r--src/luaconf.js5
-rw-r--r--src/lvm.js2
-rw-r--r--tests/lmathlib.js419
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"
+ );
+
+});