diff options
| -rw-r--r-- | README.md | 31 | ||||
| -rw-r--r-- | package.json | 3 | ||||
| -rw-r--r-- | src/lapi.js | 2 | ||||
| -rw-r--r-- | src/lmathlib.js | 134 | ||||
| -rw-r--r-- | src/luaconf.js | 5 | ||||
| -rw-r--r-- | src/lvm.js | 2 | ||||
| -rw-r--r-- | tests/lmathlib.js | 419 | 
7 files changed, 528 insertions, 68 deletions
| @@ -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 @@ -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" +    ); + +}); | 
