diff options
| author | Benoit Giannangeli <giann008@gmail.com> | 2017-05-03 13:51:20 +0200 | 
|---|---|---|
| committer | Benoit Giannangeli <giann008@gmail.com> | 2017-05-03 15:07:21 +0200 | 
| commit | 278fd3edb6ead6cd65c2293f823887d19e4fbc8e (patch) | |
| tree | 3ad7b42a93bd51d856e0ccef43395fa1b64fe890 | |
| parent | d231ed14d0816eb26856e948a9ab72f64d46c1eb (diff) | |
| download | fengari-278fd3edb6ead6cd65c2293f823887d19e4fbc8e.tar.gz fengari-278fd3edb6ead6cd65c2293f823887d19e4fbc8e.tar.bz2 fengari-278fd3edb6ead6cd65c2293f823887d19e4fbc8e.zip | |
require
| -rw-r--r-- | README.md | 114 | ||||
| -rw-r--r-- | src/defs.js | 6 | ||||
| -rw-r--r-- | src/lauxlib.js | 7 | ||||
| -rw-r--r-- | src/loadlib.js | 75 | ||||
| -rw-r--r-- | src/lua.js | 16 | ||||
| -rw-r--r-- | tests/loadlib.js | 68 | ||||
| -rw-r--r-- | tests/module-hello.lua | 3 | 
7 files changed, 214 insertions, 75 deletions
| @@ -5,7 +5,7 @@  </p>  # fengari -🐺 φεγγάρι - The Lua VM written in JS ES6 targeting the browser +🐺 φεγγάρι - The Lua VM written in JS ES6 for Node and the browser  ## So far @@ -24,10 +24,23 @@      - [x] utf8      - [x] os (~~`os.setlocale()`~~)      - [ ] Package +        - [x] `package.config` +        - [x] `package.cpath` +        - [x] `package.loaded` +        - [x] `package.path` +        - [x] `package.preload` +        - [x] `package.searchers` +        - [x] `require` +        - [ ] `package.loadlib` +        - [ ] `package.searchpath`      - [ ] io +        - [x] `file:__tostring()` +        - [x] `file:write()` +        - [x] `io.close()` +        - [x] `io.stderr`          - [x] `io.stdin`          - [x] `io.stdout` -        - [x] `io.stderr` +        - [x] `io.write()`          - [ ] `io.flush()`          - [ ] `io.input()`          - [ ] `io.lines()` @@ -37,68 +50,63 @@          - [ ] `io.read()`          - [ ] `io.tmpfile()`          - [ ] `io.type()` -        - [x] `io.write()` -        - [x] `io.close()`          - [ ] `file:flush()`          - [ ] `file:lines()`          - [ ] `file:read()`          - [ ] `file:read()`          - [ ] `file:setvbuf()` -        - [x] `file:write()`          - [ ] `file:__gc()` -        - [x] `file:__tostring()`  - [ ] C API      - [x] ... -    - [ ] lua_arith -    - [ ] lua_islightuserdata -    - [ ] lua_pushfstring -    - [ ] lua_pushvfstring -    - [ ] lua_register -    - [ ] lua_setallocf -    - [ ] lua_tocfunction +    - [ ] `lua_arith` +    - [ ] `lua_islightuserdata` +    - [ ] `lua_pushfstring` +    - [ ] `lua_pushvfstring` +    - [ ] `lua_register` +    - [ ] `lua_setallocf` +    - [ ] `lua_tocfunction`  - [ ] Auxiliary library      - [x] ... -    - [ ] luaL_addsize -    - [ ] luaL_checkoption -    - [ ] luaL_checkversion -    - [ ] luaL_gsub -    - [ ] luaL_newlibtable -    - [ ] luaL_optnumber -    - [ ] luaL_prepbuffer -    - [ ] luaL_pushresultsize -    - [ ] luaL_ref -    - [ ] luaL_unref +    - [ ] `luaL_addsize` +    - [ ] `luaL_checkoption` +    - [ ] `luaL_checkversion` +    - [ ] `luaL_newlibtable` +    - [ ] `luaL_optnumber` +    - [ ] `luaL_prepbuffer` +    - [ ] `luaL_pushresultsize` +    - [ ] `luaL_ref` +    - [ ] `luaL_unref`  - [ ] Run [Lua test suite](https://github.com/lua/tests) -    - [x] constructs.lua (`_soft`) -    - [x] locals.lua -    - [x] strings.lua -    - [ ] all.lua -    - [ ] big.lua -    - [ ] checktable.lua -    - [ ] errors.lua -    - [ ] gc.lua -    - [ ] literals.lua -    - [ ] math.lua -    - [ ] sort.lua -    - [ ] utf8.lua -    - [ ] api.lua -    - [ ] bitwise.lua -    - [ ] closure.lua -    - [ ] coroutine.lua -    - [ ] events.lua -    - [ ] goto.lua -    - [ ] nextvar.lua -    - [ ] vararg.lua -    - [ ] attrib.lua -    - [ ] calls.lua -    - [ ] code.lua -    - [ ] db.lua -    - [ ] files.lua -    - [ ] heavy.lua -    - [ ] main.lua -    - [ ] pm.lua -    - [ ] tpack.lua -    - [ ] verybig.lua +    - [x] `constructs.lua (`_soft`) +    - [x] `locals.lua +    - [x] `strings.lua +    - [ ] `all.lua` +    - [ ] `big.lua` +    - [ ] `checktable.lua` +    - [ ] `errors.lua` +    - [ ] `gc.lua` +    - [ ] `literals.lua` +    - [ ] `math.lua` +    - [ ] `sort.lua` +    - [ ] `utf8.lua` +    - [ ] `api.lua` +    - [ ] `bitwise.lua` +    - [ ] `closure.lua` +    - [ ] `coroutine.lua` +    - [ ] `events.lua` +    - [ ] `goto.lua` +    - [ ] `nextvar.lua` +    - [ ] `vararg.lua` +    - [ ] `attrib.lua` +    - [ ] `calls.lua` +    - [ ] `code.lua` +    - [ ] `db.lua` +    - [ ] `files.lua` +    - [ ] `heavy.lua` +    - [ ] `main.lua` +    - [ ] `pm.lua` +    - [ ] `tpack.lua` +    - [ ] `verybig.lua`  - [ ] DOM API binding  ## References diff --git a/src/defs.js b/src/defs.js index c580ee5..3b5a923 100644 --- a/src/defs.js +++ b/src/defs.js @@ -294,6 +294,9 @@ module.exports.LUA_VDIR = LUA_VDIR;  let os = false;  if (typeof require === "function" && (os = require('os')) && typeof os.platform === "function" && os.platform() === 'win32') { +    const LUA_DIRSEP = "\\"; +    module.exports.LUA_DIRSEP = LUA_DIRSEP; +      /*      ** In Windows, any exclamation mark ('!') in the path is replaced by the      ** path of the directory of the executable file of the current process. @@ -320,6 +323,9 @@ if (typeof require === "function" && (os = require('os')) && typeof os.platform          LUA_CDIR + "loadall.dll;.\\?.dll";      module.exports.LUA_CPATH_DEFAULT = LUA_CPATH_DEFAULT;  } else { +    const LUA_DIRSEP = "/"; +    module.exports.LUA_DIRSEP = LUA_DIRSEP; +      const LUA_ROOT = "/usr/local/";      module.exports.LUA_ROOT = LUA_ROOT; diff --git a/src/lauxlib.js b/src/lauxlib.js index d5b69f9..ff8a500 100644 --- a/src/lauxlib.js +++ b/src/lauxlib.js @@ -561,14 +561,13 @@ const find_subarray = function(arr, subarr, from_index) {  const luaL_gsub = function(L, s, p, r) {      let wild; -    let i = 0;      let b = [];      while ((wild = find_subarray(s, p)) >= 0) { -        b.push(...s.slice(i, wild)); /* push prefix */ +        b.push(...s.slice(0, wild)); /* push prefix */          b.push(...r);  /* push replacement in place of pattern */ -        i = wild + p.length;  /* continue after 'p' */ +        s = s.slice(wild + p.length);  /* continue after 'p' */      } -    b.push(s.slice(i));  /* push last suffix */ +    b.push(...s);  /* push last suffix */      lua.lua_pushstring(L, b);      return lua.lua_tostring(L, -1);  }; diff --git a/src/loadlib.js b/src/loadlib.js index c29bc37..5ed4703 100644 --- a/src/loadlib.js +++ b/src/loadlib.js @@ -61,12 +61,16 @@ const noenv = function(L) {  };  const readable = function(filename) { +    let fd = false; +      try { -        fs.openSync(lua.to_jsstring(filename), 'r'); +        fd = fs.openSync(lua.to_jsstring(filename), 'r');      } catch (e) {          return false;      } +    fs.closeSync(fd); +      return true;  }; @@ -157,12 +161,12 @@ const addtoclib = function(L, path, plib) {  };  const pushnexttemplate = function(L, path) { -    while (path[0] === lua.LUA_PATH_SEP.charCodeAt[0]) path = path.slice(1);  /* skip separators */ +    while (path[0] === lua.LUA_PATH_SEP.charCodeAt(0)) path = path.slice(1);  /* skip separators */      if (path.length === 0) return null;  /* no more templates */      let l = path.indexOf(lua.LUA_PATH_SEP.charCodeAt(0));  /* find next separator */      if (l < 0) l = path.length;      lua.lua_pushlstring(L, path, l);  /* template */ -    return l; +    return path.slice(l);  };  const searchpath = function(L, name, path, sep, dirsep) { @@ -170,7 +174,7 @@ const searchpath = function(L, name, path, sep, dirsep) {      if (sep[0] !== 0)  /* non-empty separator? */          name = lauxlib.luaL_gsub(L, name, sep, dirsep);  /* replace it by 'dirsep' */      while ((path = pushnexttemplate(L, path)) !== null) { -        let filename = lauxlib.luaL_gsub(L, lua.lua_tostring(L, -1), lua.LUA_PATH_MARK, name); +        let filename = lauxlib.luaL_gsub(L, lua.lua_tostring(L, -1), lua.to_luastring(lua.LUA_PATH_MARK, true), name);          lua.lua_remove(L, -2);  /* remove path template */          if (readable(filename))  /* does file exist and is readable? */              return filename;  /* return that file name */ @@ -199,7 +203,7 @@ const checkload = function(L, stat, filename) {  const searcher_Lua = function(L) {      let name = lauxlib.luaL_checkstring(L, 1); -    let filename = findfile(L, name, lua.to_luastring("path", true), LUA_LSUBSEP); +    let filename = findfile(L, name, lua.to_luastring("path", true), lua.to_luastring(LUA_LSUBSEP, true));      if (filename === null) return 1;  /* module not found in this path */      return checkload(L, lauxlib.luaL_loadfile(L, filename) === lua.LUA_OK, filename);  }; @@ -229,7 +233,7 @@ const loadfunc = function(L, filename, modname) {  const searcher_C = function(L) {      let name = lauxlib.luaL_checkstring(L, 1); -    let filename = findfile(L, name, "cpath", LUA_CSUBSEP); +    let filename = findfile(L, name, lua.to_luastring("cpath", true), lua.to_luastring(LUA_CSUBSEP, true));      if (filename === null) return 1;  /* module not found in this path */      return checkload(L, (loadfunc(L, filename, name) === 0), filename);  }; @@ -240,7 +244,7 @@ const searcher_Croot = function(L) {      let stat;      if (p < 0) return 0;  /* is root */      lua.lua_pushlstring(L, name, p); -    let filename = findfile(L, lua.lua_tostring(L, -1), lua.to_luastring("cpath", true), LUA_CSUBSEP); +    let filename = findfile(L, lua.lua_tostring(L, -1), lua.to_luastring("cpath", true), lua.to_luastring(LUA_CSUBSEP, true));      if (filename === null) return 1;  /* root not found */      if ((stat = loadfunc(L, filename, name)) !== 0) {          if (stat != ERRFUNC) @@ -256,18 +260,69 @@ const searcher_Croot = function(L) {  const searcher_preload = function(L) {      let name = lauxlib.luaL_checkstring(L, 1); -    lua.lua_getfield(L, lua.LUA_REGISTRYINDEX, lauxlib.LUA_PRELOAD_TABLE); +    lua.lua_getfield(L, lua.LUA_REGISTRYINDEX, lua.to_luastring(lauxlib.LUA_PRELOAD_TABLE, true));      if (lua.lua_getfield(L, -1, name) === lua.LUA_TNIL)  /* not found? */          lua.lua_pushliteral(L, `\n\tno field package.preload['${lua.to_jsstring(name)}']`);      return 1;  }; +const findloader = function(L, name) { +    let msg = [];  /* to build error message */ +    /* push 'package.searchers' to index 3 in the stack */ +    if (lua.lua_getfield(L, lua.lua_upvalueindex(1), lua.to_luastring("searchers", true)) !== lua.LUA_TTABLE) +        lauxlib.luaL_error(L, lua.to_luastring("'package.searchers' must be a table")); +    /*  iterate over available searchers to find a loader */ +    for (let i = 1; ; i++) { +        if (lua.lua_rawgeti(L, 3, i) === lua.LUA_TNIL) {  /* no more searchers? */ +            lua.lua_pop(L, 1);  /* remove nil */ +            lua.lua_pushstring(msg);  /* create error message */ +            lauxlib.luaL_error(L, `module '${lua.to_jsstring(name)}' not found:${lua.lua_tojsstring(L, -1)}`); +        } +        lua.lua_pushstring(L, name); +        lua.lua_call(L, 1, 2);  /* call it */ +        if (lua.lua_isfunction(L, -2))  /* did it find a loader? */ +            return;  /* module loader found */ +        else if (lua.lua_isstring(L, -2)) {  /* searcher returned error message? */ +            lua.lua_pop(L, 1);  /* remove extra return */ +            msg.push(...lua.lua_tostring(L, -1)); +            lua.lua_remove(L, -1);  /* concatenate error message */ +        } +        else +            lua.lua_pop(L, 2);  /* remove both returns */ +    } +}; + +const ll_require = function(L) { +    let name = lauxlib.luaL_checkstring(L, 1); +    lua.lua_settop(L, 1);  /* LOADED table will be at index 2 */ +    lua.lua_getfield(L, lua.LUA_REGISTRYINDEX, lua.to_luastring(lauxlib.LUA_LOADED_TABLE, true)); +    lua.lua_getfield(L, 2, name);  /* LOADED[name] */ +    if (lua.lua_toboolean(L, -1))  /* is it there? */ +      return 1;  /* package is already loaded */ +    /* else must load package */ +    lua.lua_pop(L, 1);  /* remove 'getfield' result */ +    findloader(L, name); +    lua.lua_pushstring(L, name);  /* pass name as argument to module loader */ +    lua.lua_insert(L, -2);  /* name is 1st argument (before search data) */ +    lua.lua_call(L, 2, 1);  /* run loader to load module */ +    if (!lua.lua_isnil(L, -1))  /* non-nil return? */ +      lua.lua_setfield(L, 2, name);  /* LOADED[name] = returned value */ +    if (lua.lua_getfield(L, 2, name) == lua.LUA_TNIL) {   /* module set no value? */ +      lua.lua_pushboolean(L, 1);  /* use true as result */ +      lua.lua_pushvalue(L, -1);  /* extra copy to be returned */ +      lua.lua_setfield(L, 2, name);  /* LOADED[name] = true */ +    } +    return 1; +}; +  const pk_funcs = {}; -const ll_funcs = {}; +const ll_funcs = { +    "require": ll_require +};  const createsearcherstable = function(L) { -    let searchers = {searcher_preload, searcher_Lua, searcher_C, searcher_Croot, null}; +    let searchers = [searcher_preload, searcher_Lua, searcher_C, searcher_Croot, null];      /* create 'searchers' table */      lua.lua_createtable(L);      /* fill it with predefined searchers */ @@ -81,18 +81,17 @@ module.exports.lua_Debug               = defs.lua_Debug;  module.exports.lua_upvalueindex        = defs.lua_upvalueindex;  module.exports.to_jsstring             = defs.to_jsstring;  module.exports.to_luastring            = defs.to_luastring; -module.exports.LUA_VDIR                = defs.LUA_VDIR; -module.exports.LUA_LDIR                = defs.LUA_LDIR;  module.exports.LUA_CDIR                = defs.LUA_CDIR; -module.exports.LUA_SHRDIR              = defs.LUA_SHRDIR; -module.exports.LUA_PATH_DEFAULT        = defs.LUA_PATH_DEFAULT;  module.exports.LUA_CPATH_DEFAULT       = defs.LUA_CPATH_DEFAULT; -module.exports.LUA_ROOT                = defs.LUA_ROOT; +module.exports.LUA_EXEC_DIR            = defs.LUA_EXEC_DIR;  module.exports.LUA_LDIR                = defs.LUA_LDIR; -module.exports.LUA_CDIR                = defs.LUA_CDIR;  module.exports.LUA_PATH_DEFAULT        = defs.LUA_PATH_DEFAULT; -module.exports.LUA_CPATH_DEFAULT       = defs.LUA_CPATH_DEFAULT; - +module.exports.LUA_PATH_MARK           = defs.LUA_PATH_MARK; +module.exports.LUA_PATH_SEP            = defs.LUA_PATH_SEP; +module.exports.LUA_ROOT                = defs.LUA_ROOT; +module.exports.LUA_SHRDIR              = defs.LUA_SHRDIR; +module.exports.LUA_VDIR                = defs.LUA_VDIR; +module.exports.LUA_DIRSEP              = defs.LUA_DIRSEP;  module.exports.lua_absindex            = lapi.lua_absindex;  module.exports.lua_atpanic             = lapi.lua_atpanic;  module.exports.lua_call                = lapi.lua_call; @@ -166,6 +165,7 @@ module.exports.lua_rawget              = lapi.lua_rawget;  module.exports.lua_rawgeti             = lapi.lua_rawgeti;  module.exports.lua_rawgetp             = lapi.lua_rawgetp;  module.exports.lua_rawlen              = lapi.lua_rawlen; +module.exports.lua_rawseti             = lapi.lua_rawseti;  module.exports.lua_rawset              = lapi.lua_rawset;  module.exports.lua_rawsetp             = lapi.lua_rawsetp;  module.exports.lua_remove              = lapi.lua_remove; diff --git a/tests/loadlib.js b/tests/loadlib.js new file mode 100644 index 0000000..933991c --- /dev/null +++ b/tests/loadlib.js @@ -0,0 +1,68 @@ +"use strict"; + +const test     = require('tape'); + +const lauxlib  = require("../src/lauxlib.js"); +const lua      = require('../src/lua.js'); + +test('require an existing module', function (t) { +    let luaCode = ` +        return require('os') +    `, L; +     +    t.plan(3); + +    t.doesNotThrow(function () { + +        L = lauxlib.luaL_newstate(); + +        lauxlib.luaL_openlibs(L); + +        lauxlib.luaL_loadstring(L, lua.to_luastring(luaCode)); + +    }, "Lua program loaded without error"); + +    t.doesNotThrow(function () { + +        lua.lua_call(L, 0, -1); + +    }, "Lua program ran without error"); + +    t.ok( +        lua.lua_istable(L, -1), +        "Correct element(s) on the stack" +    ); + +}); + + +test('require a file', function (t) { +    let luaCode = ` +        return require('tests/module-hello')() +    `, L; +     +    t.plan(3); + +    t.doesNotThrow(function () { + +        L = lauxlib.luaL_newstate(); + +        lauxlib.luaL_openlibs(L); + +        lauxlib.luaL_loadstring(L, lua.to_luastring(luaCode)); + +    }, "Lua program loaded without error"); + +    t.doesNotThrow(function () { + +        lua.lua_call(L, 0, -1); + +    }, "Lua program ran without error"); + +    t.strictEqual( +        lua.lua_tojsstring(L, -1), +        "hello from module", +        "Correct element(s) on the stack" +    ); + +}); diff --git a/tests/module-hello.lua b/tests/module-hello.lua new file mode 100644 index 0000000..4de5890 --- /dev/null +++ b/tests/module-hello.lua @@ -0,0 +1,3 @@ +return function() +    return 'hello from module' +end | 
