From 5f5ba74ace8a956ae48a9f0b182a157052c2546e Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Mon, 20 Mar 2017 16:15:36 +0100 Subject: string.gsub --- README.md | 14 ++++++-------- src/lapi.js | 20 ++++++++++++++++++++ src/lstrlib.js | 40 ++++++++++++++++++++++++++++++++++++++++ tests/lstrlib.js | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6de8cab..2d1e35a 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,6 @@ - [ ] lua_islightuserdata - [ ] lua_isthread - [ ] lua_isuserdata - - [ ] lua_newuserdata - [ ] lua_pcallk - [ ] lua_pushfstring - [ ] lua_pushlightuserdata @@ -57,7 +56,6 @@ - [ ] lua_setlocal - [ ] lua_setuservalue - [ ] lua_tocfunction - - [ ] lua_touserdata - [ ] lua_upvalueid - [ ] lua_upvaluejoin - [ ] Auxiliary library @@ -99,20 +97,20 @@ - [x] string.byte - [x] string.char - [x] string.dump + - [x] string.find - [x] string.format + - [x] string.gsub - [x] string.len - [x] string.lower + - [x] string.match + - [x] string.pack + - [x] string.packsize - [x] string.rep - [x] string.reverse - [x] string.sub + - [x] string.unpack - [x] string.upper - - [ ] string.find - [ ] string.gmatch - - [ ] string.gsub - - [ ] string.match - - [ ] string.pack - - [ ] string.packsize - - [ ] string.unpack - [ ] Package - [ ] os - [ ] io diff --git a/src/lapi.js b/src/lapi.js index a884a97..6840ef9 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -428,6 +428,14 @@ const lua_createtable = function(L, narray, nrec) { assert(L.top <= L.ci.top, "stack overflow"); }; +const lua_newuserdata = function(L, u) { + L.stack[L.top++] = new lobject.TValue(CT.LUA_TUSERDATA, u); + + assert(L.top <= L.ci.top, "stack overflow"); + + return L.stack[L.top - 1].value; +}; + const aux_upvalue = function(fi, n) { switch(fi.ttype()) { case CT.LUAT_TCCL: { /* C closure */ @@ -578,6 +586,16 @@ const lua_tonumber = function(L, idx) { return lvm.tonumber(index2addr(L, idx)); }; +const lua_touserdata = function(L, idx) { + let o = index2addr(L, idx); + switch (o.ttnov()) { + case CT.LUA_TUSERDATA: + case CT.LUA_TLIGHTUSERDATA: + return o.value; + default: return null; + } +}; + const lua_tothread = function(L, idx) { let o = index2addr(L, idx); return o.ttisthread() ? o.value : null; @@ -879,6 +897,7 @@ module.exports.lua_istable = lua_istable; module.exports.lua_len = lua_len; module.exports.lua_load = lua_load; module.exports.lua_newtable = lua_newtable; +module.exports.lua_newuserdata = lua_newuserdata; module.exports.lua_next = lua_next; module.exports.lua_pcall = lua_pcall; module.exports.lua_pcallk = lua_pcallk; @@ -924,6 +943,7 @@ module.exports.lua_tonumber = lua_tonumber; module.exports.lua_topointer = lua_topointer; module.exports.lua_tostring = lua_tostring; module.exports.lua_tothread = lua_tothread; +module.exports.lua_touserdata = lua_touserdata; module.exports.lua_type = lua_type; module.exports.lua_typename = lua_typename; module.exports.lua_version = lua_version; diff --git a/src/lstrlib.js b/src/lstrlib.js index cedb403..9caa3cc 100644 --- a/src/lstrlib.js +++ b/src/lstrlib.js @@ -1221,12 +1221,52 @@ const str_match = function(L) { return str_find_aux(L, 0); }; +/* state for 'gmatch' */ +class GMatchState { + constructor() { + this.src = NaN; /* current position */ + this.p = NaN; /* pattern */ + this.lastmatch = NaN; /* end of last match */ + this.ms = new MatchState(); /* match state */ + } +} + +const gmatch_aux = function(L) { + let gm = lapi.lua_touserdata(L, lua.lua_upvalueindex(3)); + gm.ms.L = L; + for (let src = 0; src < gm.ms.src_end; src++) { + reprepstate(gm.ms); + let e; + if ((e = match(gm.ms, src, gm.p)) !== null && e !== gm.lastmatch) { + gm.src = gm.lastmatch = e; + return push_captures(gm.ms, src, e); + } + } + return 0; /* not found */ +}; + +const str_gmatch = function(L) { + let s = lauxlib.luaL_checkstring(L, 1); + let p = lauxlib.luaL_checkstring(L, 2); + let ls = s.length; + let lp = p.length; + lapi.lua_settop(L, 2); /* keep them on closure to avoid being collected */ + let gm = lapi.lua_newuserdata(L, new GMatchState()); + prepstate(gm.ms, L, s, ls, p, lp); + gm.src = s; + gm.p = p; + gm.lastmatch = null; + lapi.lua_pushcclosure(L, gmatch_aux, 3); + return 1; +}; + const strlib = { "byte": str_byte, "char": str_char, "dump": str_dump, "find": str_find, "format": str_format, + "gmatch": str_gmatch, "len": str_len, "lower": str_lower, "match": str_match, diff --git a/tests/lstrlib.js b/tests/lstrlib.js index aac3851..ce77bdc 100644 --- a/tests/lstrlib.js +++ b/tests/lstrlib.js @@ -621,4 +621,60 @@ test('string.find', function (t) { "123", "Correct element(s) on the stack" ); +}); + + +test('string.gmatch', function (t) { + let luaCode = ` + local s = "hello world from Lua" + local t = {} + + for w in string.gmatch(s, "%a+") do + table.insert(t, w) + end + + return table.unpack(t) + `, L; + + t.plan(6); + + t.doesNotThrow(function () { + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lauxlib.luaL_loadstring(L, luaCode); + + }, "Lua program loaded without error"); + + t.doesNotThrow(function () { + + lapi.lua_call(L, 0, -1); + + }, "Lua program ran without error"); + + t.strictEqual( + lapi.lua_tostring(L, -4), + "hello", + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tostring(L, -3), + "world", + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tostring(L, -2), + "from", + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tostring(L, -1), + "Lua", + "Correct element(s) on the stack" + ); }); \ No newline at end of file -- cgit v1.2.3-70-g09d2