From 1cfdb4fa09ef9715dfa431bea2605a84ba9a4605 Mon Sep 17 00:00:00 2001 From: jyelon Date: Wed, 15 Dec 2021 23:03:43 -0500 Subject: [PATCH] Initial revision of lua 'doc' function --- luprex/core/cpp/animqueue.cpp | 2 +- luprex/core/cpp/globaldb.cpp | 9 +-- luprex/core/cpp/gui.cpp | 2 +- luprex/core/cpp/idalloc.cpp | 2 +- luprex/core/cpp/luastack.cpp | 39 +++++++++- luprex/core/cpp/luastack.hpp | 18 +++-- luprex/core/cpp/planemap.cpp | 2 +- luprex/core/cpp/pprint.cpp | 76 +++++++------------ luprex/core/cpp/pprint.hpp | 10 +-- luprex/core/cpp/printbuffer.cpp | 2 +- luprex/core/cpp/sched.cpp | 2 +- luprex/core/cpp/source.cpp | 117 +++++++++++++++++++++++------ luprex/core/cpp/source.hpp | 3 + luprex/core/cpp/streambuffer.cpp | 2 +- luprex/core/cpp/table.cpp | 72 +++++++----------- luprex/core/cpp/util.cpp | 39 +++++++++- luprex/core/cpp/util.hpp | 6 ++ luprex/core/cpp/world-accessor.cpp | 77 ++++++++----------- luprex/core/cpp/world-core.cpp | 1 + luprex/core/cpp/world-difftab.cpp | 10 ++- luprex/core/cpp/world-testing.cpp | 8 +- luprex/core/lua/login.lua | 1 + 22 files changed, 301 insertions(+), 199 deletions(-) diff --git a/luprex/core/cpp/animqueue.cpp b/luprex/core/cpp/animqueue.cpp index 08c9bc64..80ca40ce 100644 --- a/luprex/core/cpp/animqueue.cpp +++ b/luprex/core/cpp/animqueue.cpp @@ -576,7 +576,7 @@ static bool diff_works(const AnimQueue &master, AnimQueue &sync) { return sync.size_and_steps_equal(master); } -LuaDefine(unittests_animqueue, "c") { +LuaDefine(unittests_animqueue, "", "some unit tests") { // Useful objects. AnimStep stp; AnimQueue aq(util::WORLD_TYPE_MASTER); diff --git a/luprex/core/cpp/globaldb.cpp b/luprex/core/cpp/globaldb.cpp index f7365cd7..e1015316 100644 --- a/luprex/core/cpp/globaldb.cpp +++ b/luprex/core/cpp/globaldb.cpp @@ -2,14 +2,7 @@ #include "globaldb.hpp" -// Get a table from the global database. -// -// GLOBALNAME -// if globalname is already present, and is a table, return it. -// if globalname is already present, and not a table, error. -// if globalname is not present, create and initialize it. -// -LuaDefine(globaldb_global, "f") { +LuaDefine(global, "globalname", "get a table where global data can be stored") { LuaArg globalname; LuaRet globaltab; LuaVar globaldb; diff --git a/luprex/core/cpp/gui.cpp b/luprex/core/cpp/gui.cpp index 796e5ed9..1e7aefb5 100644 --- a/luprex/core/cpp/gui.cpp +++ b/luprex/core/cpp/gui.cpp @@ -52,7 +52,7 @@ std::string Gui::menu_debug_string() const { return oss.str(); } -LuaDefine(gui_menu_item, "c") { +LuaDefine(gui_menu_item, "action,label", "add a menu item to the current gui") { Gui *gui = Gui::fetch_global_pointer(L); LuaArg laction, llabel; LuaStack LS(L, laction, llabel); diff --git a/luprex/core/cpp/idalloc.cpp b/luprex/core/cpp/idalloc.cpp index 0cbf6bc7..2c0d5cc3 100644 --- a/luprex/core/cpp/idalloc.cpp +++ b/luprex/core/cpp/idalloc.cpp @@ -250,7 +250,7 @@ static int64_t nthbatch(int64_t n) { return int64_t(0x0001000000000000) + n*256; } -LuaDefine(unittests_idalloc, "c") { +LuaDefine(unittests_idalloc, "", "some unit tests") { IdGlobalPool gp; IdPlayerPool pp(&gp); IdGlobalPool gpds; diff --git a/luprex/core/cpp/luastack.cpp b/luprex/core/cpp/luastack.cpp index 92a1d1ef..560464b7 100644 --- a/luprex/core/cpp/luastack.cpp +++ b/luprex/core/cpp/luastack.cpp @@ -6,9 +6,10 @@ LuaSpecial LuaRegistry(LUA_REGISTRYINDEX); LuaNilMarker LuaNil; LuaNewTableMarker LuaNewTable; -LuaFunctionReg::LuaFunctionReg(const char *m, const char *n, lua_CFunction f) { - mode_ = m; +LuaFunctionReg::LuaFunctionReg(const char *n, const char *a, const char *d, lua_CFunction f) { name_ = n; + args_ = a; + docs_ = d; func_ = f; next_ = LuaFunctionRegistry; LuaFunctionRegistry = this; @@ -22,6 +23,15 @@ LuaFunctionReg::List LuaFunctionReg::all() { return result; } +const LuaFunctionReg *LuaFunctionReg::lookup(lua_CFunction fn) { + for (const LuaFunctionReg *r = LuaFunctionRegistry; r != 0; r = r->next_) { + if (r->func_ == fn) { + return r; + } + } + return nullptr; +} + LuaFunctionReg *LuaFunctionReg::LuaFunctionRegistry; bool LuaStack::issortablekey(LuaSlot s) const { @@ -350,6 +360,31 @@ int LuaStack::rawlen(LuaSlot obj) const { return lua_rawlen(L_, obj.index()); } +std::string LuaStack::get_function_name(LuaSlot fn) { + LuaVar globals, key, val, skey, sval; + LuaStack LS(L_, globals, key, val, skey, sval); + LS.getglobaltable(globals); + LS.set(key, LuaNil); + while (LS.next(globals, key, val)) { + if (LS.isstring(key)) { + if (LS.rawequal(val, fn)) { + return LS.ckstring(key); + } + if (LS.istable(val)) { + LS.set(skey, LuaNil); + while (LS.next(val, skey, sval)) { + if (LS.isstring(skey) && LS.rawequal(sval, fn)) { + std::string n1 = LS.ckstring(key); + std::string n2 = LS.ckstring(skey); + return n1 + "." + n2; + } + } + } + } + } + return ""; +} + int LuaStack::gettabletype(LuaSlot tab) const { uint16_t bits = lua_getflagbits(L_, tab.index()); return LUA_TT_GENERAL + (bits & 0x000F); diff --git a/luprex/core/cpp/luastack.hpp b/luprex/core/cpp/luastack.hpp index 214aa2c8..a236101d 100644 --- a/luprex/core/cpp/luastack.hpp +++ b/luprex/core/cpp/luastack.hpp @@ -126,7 +126,7 @@ // exposed to lua. It creates a global registry of functions // created with LuaDefine. You use it like so: // -// LuaDefine(function_name, "modebits") { +// LuaDefine(function_name, "arguments", "documentation") { // ... // } // @@ -450,6 +450,9 @@ public: lua_rawseti(L_, tab, key); } + // Get function name + std::string get_function_name(LuaSlot ls); + // Lua flagbits manipulation: Table types. int gettabletype(LuaSlot tab) const; void settabletype(LuaSlot tab, int t) const; @@ -466,8 +469,9 @@ public: class LuaFunctionReg { private: - const char *mode_; const char *name_; + const char *args_; + const char *docs_; lua_CFunction func_; LuaFunctionReg *next_; @@ -476,18 +480,20 @@ private: public: using List = std::vector; - LuaFunctionReg(const char *mode, const char *n, lua_CFunction f); + LuaFunctionReg(const char *name, const char *args, const char *docs, lua_CFunction f); static List all(); + static const LuaFunctionReg *lookup(lua_CFunction fn); - const char *get_mode() const { return mode_; } const char *get_name() const { return name_; } + const char *get_args() const { return args_; } + const char *get_docs() const { return docs_; } lua_CFunction get_func() const { return func_; } }; -#define LuaDefine(name, mode) \ +#define LuaDefine(name, args, docs) \ int lfn_##name(lua_State *L); \ - LuaFunctionReg reg_##name(mode, #name, lfn_##name); \ + LuaFunctionReg reg_##name(#name, args, docs, lfn_##name); \ int lfn_##name(lua_State *L) diff --git a/luprex/core/cpp/planemap.cpp b/luprex/core/cpp/planemap.cpp index 94faf2c9..38b567cc 100644 --- a/luprex/core/cpp/planemap.cpp +++ b/luprex/core/cpp/planemap.cpp @@ -201,7 +201,7 @@ PlaneMap::IdVector PlaneMap::scan_radius(const std::string &plane, float x, floa return result; } -LuaDefine(unittests_planemap, "c") { +LuaDefine(unittests_planemap, "", "some unit tests") { float SC = CELL_SCALE; float E = CELL_SCALE * 0.4; int LO = -CELL_LIMIT; diff --git a/luprex/core/cpp/pprint.cpp b/luprex/core/cpp/pprint.cpp index f5efe242..1f9a1674 100644 --- a/luprex/core/cpp/pprint.cpp +++ b/luprex/core/cpp/pprint.cpp @@ -5,14 +5,19 @@ #include -void atomic_print(LuaStack &LS, LuaSlot val, std::ostream *os) { - switch (LS.type(val)) { +void atomic_print(LuaStack &LS, LuaSlot val, bool quote, std::ostream *os) { + int tt = LS.type(val); + switch (tt) { case LUA_TNIL: (*os) << "nil"; return; case LUA_TSTRING: - // TODO: this could be more efficient. - (*os) << LS.ckstring(val); + if (quote) { + util::quote_string(LS.ckstring(val), os); + } else { + // TODO: this could be more efficient. + (*os) << LS.ckstring(val); + } return; case LUA_TNUMBER: { double value = LS.cknumber(val); @@ -27,38 +32,18 @@ void atomic_print(LuaStack &LS, LuaSlot val, std::ostream *os) { case LUA_TBOOLEAN: (*os) << (LS.ckboolean(val) ? "true" : "false"); return; - case LUA_TTABLE: - (*os) << "table"; - return; - default: - (*os) << "unknown"; - return; - } -} - -static bool string_quote(LuaStack &LS, LuaSlot val, std::ostream *os) { - switch (LS.type(val)) { - case LUA_TNIL: - (*os) << "nil"; - return true; - case LUA_TSTRING: - util::quote_string(LS.ckstring(val), os); - return true; - case LUA_TNUMBER: { - double value = LS.cknumber(val); - int64_t ivalue = int64_t(value); - if (double(ivalue) == value) { - (*os) << ivalue; + case LUA_TFUNCTION: { + std::string name = LS.get_function_name(val); + if (name.empty()) { + (*os) << ""; } else { - (*os) << value; + (*os) << ""; } - return true; + return; } - case LUA_TBOOLEAN: - (*os) << (LS.ckboolean(val) ? "true" : "false"); - return true; default: - return false; + (*os) << "<" << lua_typename(LS.state(), tt) << ">"; + return; } } @@ -105,7 +90,7 @@ static void findtables(LuaStack &LS0, LuaSlot root, LuaSlot tabcount) { LS.result(); } -LuaDefine(table_findtables, "c") { +LuaDefine(table_findtables, "root", "recursively find tables (debugging only)") { LuaArg root; LuaRet tabcount; LuaStack LS(L, root, tabcount); @@ -140,16 +125,9 @@ static void pprint_r(Inspector &insp, int level, LuaSlot root) { LuaVar idv, pairs, key, val; LuaStack LS(insp.L, idv, pairs, key, val); - // If it's a simple type, print it quoted. - if (string_quote(LS, root, insp.stream)) { - LS.result(); - return; - } - - // If it's not a table, just print the typename. - int t = LS.type(root); - if (t != LUA_TTABLE) { - (*insp.stream) << "<" << lua_typename(insp.L, t) << ">"; + // If it's anything but a table, use 'atomic_print'. + if (!LS.istable(root)) { + atomic_print(LS, root, true, insp.stream); LS.result(); return; } @@ -272,7 +250,7 @@ void pprint(LuaStack &LS0, LuaSlot root, bool indent, std::ostream *os) { LS.result(); } -LuaDefine(string_isidentifier, "c") { +LuaDefine(string_isidentifier, "str", "return true if the string is a valid lua identifier") { LuaArg str; LuaRet result; LuaStack LS(L, str, result); @@ -285,17 +263,17 @@ LuaDefine(string_isidentifier, "c") { return LS.result(); } -LuaDefine(string_print, "c") { +LuaDefine(string_print, "obj", "print the specified object into a string") { LuaArg val; LuaRet result; LuaStack LS(L, val, result); std::ostringstream oss; - atomic_print(LS, val, &oss); + atomic_print(LS, val, false, &oss); LS.set(result, oss.str()); return LS.result(); } -LuaDefine(string_pprint, "c") { +LuaDefine(string_pprint, "obj,indent", "pretty-print the specified object into a string") { LuaArg root, indent; LuaRet result; LuaStack LS(L, root, indent, result); @@ -306,12 +284,12 @@ LuaDefine(string_pprint, "c") { return LS.result(); } -LuaDefine(string_tostring, "f") { +LuaDefine(tostring, "obj", "print the specified object into a string") { LuaArg val; LuaRet result; LuaStack LS(L, val, result); std::ostringstream oss; - atomic_print(LS, val, &oss); + atomic_print(LS, val, false, &oss); LS.set(result, oss.str()); return LS.result(); } diff --git a/luprex/core/cpp/pprint.hpp b/luprex/core/cpp/pprint.hpp index f78750a2..a805f5a4 100644 --- a/luprex/core/cpp/pprint.hpp +++ b/luprex/core/cpp/pprint.hpp @@ -24,19 +24,13 @@ // Atomic print to a stream. // // This prints an atomic value to a stream. If you give it a table, -// it just prints "table". This routine is the heart of the lua +// it just prints "". This routine is the heart of the lua // primitives 'print' and 'tostring'. // -void atomic_print(LuaStack &LS, LuaSlot val, std::ostream *os); +void atomic_print(LuaStack &LS, LuaSlot val, bool quote, std::ostream *os); // Pretty print to a stream. // -// If indent is >=0, the output is indented. If indent<0, then -// the output is emitted without newlines or indentation. -// -// Maxlen specifies the maximum number of characters output. If -// this is exceeded, then the printout is truncated. -// void pprint(LuaStack &LS, LuaSlot val, bool indent, std::ostream *os); #endif // PPRINT_HPP \ No newline at end of file diff --git a/luprex/core/cpp/printbuffer.cpp b/luprex/core/cpp/printbuffer.cpp index d23a5ae3..9b355083 100644 --- a/luprex/core/cpp/printbuffer.cpp +++ b/luprex/core/cpp/printbuffer.cpp @@ -189,7 +189,7 @@ Invocation PrintChanneler::invocation(int64_t actor_id) { return Invocation(Invocation::KIND_FLUSH_PRINTS, actor_id, actor_id, std::to_string(line_), InvocationData()); } -LuaDefine(unittests_printbuffer, "c") { +LuaDefine(unittests_printbuffer, "", "some unit tests") { PrintBuffer pbm; PrintBuffer pbs; StreamBuffer sb; diff --git a/luprex/core/cpp/sched.cpp b/luprex/core/cpp/sched.cpp index e061ec50..4dac2674 100644 --- a/luprex/core/cpp/sched.cpp +++ b/luprex/core/cpp/sched.cpp @@ -64,7 +64,7 @@ void Schedule::deserialize(StreamBuffer *sb) { } } -LuaDefine(unittests_scheduler, "c") { +LuaDefine(unittests_scheduler, "", "some unit tests") { Schedule s, xs; StreamBuffer sb; diff --git a/luprex/core/cpp/source.cpp b/luprex/core/cpp/source.cpp index 3b1577b4..e0ecf96b 100644 --- a/luprex/core/cpp/source.cpp +++ b/luprex/core/cpp/source.cpp @@ -15,7 +15,7 @@ #include "source.hpp" #include "luasnap.hpp" -LuaDefine(source_makeclass, "f") { +LuaDefine(makeclass, "classname", "create a class if it doesn't already exist") { LuaArg classname; LuaRet classtab; LuaStack LS(L, classname, classtab); @@ -23,7 +23,7 @@ LuaDefine(source_makeclass, "f") { return LS.result(); } -LuaDefine(source_classname, "f") { +LuaDefine(classname, "classtable", "get the class name from a class table") { LuaArg table; LuaRet result; LuaStack LS(L, table, result); @@ -36,7 +36,7 @@ LuaDefine(source_classname, "f") { return LS.result(); } -LuaDefine(source_maketangible, "f") { +LuaDefine(maketangible, "classname", "create a class if it doesn't already exist, and add tangible features") { LuaArg classname; LuaRet classtab; LuaVar subtab; @@ -46,6 +46,18 @@ LuaDefine(source_maketangible, "f") { return LS.result(); } +static void get_reg_name(const LuaFunctionReg *reg, std::string &classname, std::string &funcname) { + std::string name = reg->get_name(); + size_t upos = name.find('_'); + if (upos == std::string::npos) { + funcname = name; + classname = ""; + } else { + funcname = name.substr(upos + 1); + classname = name.substr(0, upos); + } +} + // the 'luaopen' function creates a new table. // @@ -299,25 +311,16 @@ static void source_load_cfunctions(lua_State *L) { LuaStack LS(L, classobj); auto regs = LuaFunctionReg::all(); for (const LuaFunctionReg *r : regs) { - const std::string &name = r->get_name(); - size_t upos = name.find('_'); + lua_CFunction func = r->get_func(); std::string classname; std::string funcname; - if (upos == std::string::npos) { - continue; - } else { - funcname = name.substr(upos + 1); - classname = name.substr(0, upos); - } - lua_CFunction func = r->get_func(); - std::string mode = r->get_mode(); - if (mode.find('c') != std::string::npos) { // Insert into class - LS.makeclass(classobj, classname); - LS.rawset(classobj, funcname, func); - } - if (mode.find('f') != std::string::npos) { // Make global function + get_reg_name(r, classname, funcname); + if (classname.empty()) { LS.getglobaltable(classobj); LS.rawset(classobj, funcname, func); + } else { + LS.makeclass(classobj, classname); + LS.rawset(classobj, funcname, func); } } LS.result(); @@ -465,8 +468,80 @@ void SourceDB::deserialize_source(util::LuaSourceVec *sv, StreamBuffer *sb) { } } +std::string SourceDB::function_docs(const LuaStack &LS0, LuaSlot fn) { + lua_State *L = LS0.state(); + LuaVar sourcedb, fname, finfo, code; + LuaStack LS(L, sourcedb, fname, finfo, code); + + if (LS.iscfunction(fn)) { + lua_CFunction cfn = lua_tocfunction(L, fn.index()); + const LuaFunctionReg *reg = LuaFunctionReg::lookup(cfn); + if (reg == nullptr) { + return ""; + } + std::string classname; + std::string funcname; + get_reg_name(reg, classname, funcname); + std::ostringstream oss; + util::StringVec docs = util::split_lines(reg->get_docs()); + for (const std::string &line : docs) { + oss << "-- " << line << std::endl; + } + oss << "function "; + if (!classname.empty()) { + oss << classname << "."; + } + oss << funcname << "(" << reg->get_args() << ")"; + return oss.str(); + } else if (LS.isfunction(fn)) { + lua_Debug ar; + lua_pushvalue(L, fn.index()); + int status = lua_getinfo(L, ">S", &ar); + if (status == 0) return ""; + + // Get the source database. + LS.rawget(sourcedb, LuaRegistry, "sourcedb"); + if (!LS.istable(sourcedb)) { + return ""; + } + + // Get the finfo table from the source db. + LS.set(fname, std::string(ar.short_src)); + LS.rawget(finfo, sourcedb, fname); + if (!LS.istable(finfo)) { + return ""; + } + + // Get the code from the finfo table. + LS.rawget(code, finfo, "code"); + if (!LS.isstring(code)) { + return ""; + } + + // Split the code into lines. + util::StringVec lines = util::split_lines(LS.ckstring(code)); + int linehi = ar.linedefined - 1; + if ((linehi < 0) || (linehi >= int(lines.size()))) { + return ""; + } + + // Incorporate the function comment. + int linelo = linehi; + while ((linelo > 0) && (util::is_lua_comment(lines[linelo-1]))) linelo -= 1; + + // Output the docs. + std::ostringstream result; + for (int i = linelo; i <= linehi; i++) { + result << lines[i] << std::endl; + } + return result.str(); + } else { + return ""; + } +} + // These should go away eventually. They're for debugging. -LuaDefine(coroutine_setnextid, "c") { +LuaDefine(coroutine_setnextid, "thread,id", "set the next id of a thread (debugging only)") { LuaArg co, lid; LuaStack LS(L, co, lid); lua_State *CO = LS.ckthread(co); @@ -475,7 +550,7 @@ LuaDefine(coroutine_setnextid, "c") { return LS.result(); } -LuaDefine(coroutine_getnextid, "c") { +LuaDefine(coroutine_getnextid, "thread", "get the next id of a thread (debugging only)") { LuaArg co; LuaRet lid; LuaStack LS(L, co, lid); @@ -484,7 +559,7 @@ LuaDefine(coroutine_getnextid, "c") { return LS.result(); } -LuaDefine(unittests_sourcedb, "c") { +LuaDefine(unittests_sourcedb, "", "some unit tests") { LuaSnap msnap; LuaSnap ssnap; SourceDB mdb; diff --git a/luprex/core/cpp/source.hpp b/luprex/core/cpp/source.hpp index ed7ab57d..8b7c6585 100644 --- a/luprex/core/cpp/source.hpp +++ b/luprex/core/cpp/source.hpp @@ -173,6 +173,9 @@ public: void set(const std::string &fn, const std::string &code, int sequence); std::string get(const std::string &fn); + // Get function documentation. + static std::string function_docs(const LuaStack &LS, LuaSlot slot); + // Serialize and unserialize a source vector. // static void serialize_source(const util::LuaSourceVec &sv, StreamBuffer *sb); diff --git a/luprex/core/cpp/streambuffer.cpp b/luprex/core/cpp/streambuffer.cpp index b6849913..30911718 100644 --- a/luprex/core/cpp/streambuffer.cpp +++ b/luprex/core/cpp/streambuffer.cpp @@ -521,7 +521,7 @@ static void write_ztbytes(StreamBuffer *sb, const char *bytes) { sb->write_bytes(bytes, strlen(bytes)); } -LuaDefine(unittests_streambuffer, "c") { +LuaDefine(unittests_streambuffer, "", "some unit tests") { // An 11-byte fixed-size stream buffer. StreamBuffer sb11(11, true); diff --git a/luprex/core/cpp/table.cpp b/luprex/core/cpp/table.cpp index 6f7e7524..5f8ea9fc 100644 --- a/luprex/core/cpp/table.cpp +++ b/luprex/core/cpp/table.cpp @@ -1,13 +1,6 @@ #include "table.hpp" #include "source.hpp" -LuaDefine(table_getregistry, "f") { - LuaArg key; - LuaRet result; - LuaStack LS(L, key, result); - LS.rawget(result, LuaRegistry, key); - return LS.result(); -} bool table_equal(LuaStack &LS, LuaSlot t1, LuaSlot t2) { lua_State *L = LS.state(); @@ -33,7 +26,7 @@ bool table_equal(LuaStack &LS, LuaSlot t1, LuaSlot t2) { return total1 == total2; } -LuaDefine(table_equal, "c") { +LuaDefine(table_equal, "table1,table2", "return true if two tables contain the same keys and values") { LuaArg t1, t2; LuaRet eql; LuaStack LS(L, t1, t2, eql); @@ -41,7 +34,7 @@ LuaDefine(table_equal, "c") { return LS.result(); } -LuaDefine(table_findremove, "c") { +LuaDefine(table_findremove, "vector,value", "remove all occurrences of value from vector") { luaL_checktype(L, -2, LUA_TTABLE); int src = 1; int dst = 1; @@ -78,7 +71,7 @@ LuaDefine(table_findremove, "c") { } -LuaDefine(table_push, "c") { +LuaDefine(table_push, "vector,value", "push a value onto the end of a vector") { luaL_checktype(L, -2, LUA_TTABLE); int len = lua_rawlen(L, -2); lua_pushinteger(L, len+1); @@ -88,7 +81,7 @@ LuaDefine(table_push, "c") { return 0; } -LuaDefine(table_find, "c") { +LuaDefine(table_find, "vector,value", "find the first occurrence of value in vector") { luaL_checktype(L, -2, LUA_TTABLE); for (int i = 1; ; i++) { lua_pushinteger(L, i); @@ -107,28 +100,21 @@ LuaDefine(table_find, "c") { } } -LuaDefine(table_empty, "c") { +LuaDefine(table_empty, "table", "return true if the table is empty") { luaL_checktype(L, -1, LUA_TTABLE); - lua_pushnil(L); - if (lua_next(L, -2) != 0) { - lua_pop(L, 3); - lua_pushboolean(L, 0); - return 1; - } else { - lua_pop(L, 1); - lua_pushboolean(L, 1); - return 1; - } + int total = lua_nkeys(L, -1); + lua_pushboolean(L, (total == 0)?1:0); + return 1; } -LuaDefine(table_count, "c") { +LuaDefine(table_count, "table", "return the number of keys in table") { luaL_checktype(L, -1, LUA_TTABLE); int total = lua_nkeys(L, -1); lua_pushinteger(L, total); return 1; } -LuaDefine(table_clear, "c") { +LuaDefine(table_clear, "table,metaflag", "clear all keys, and optionally the metatable") { LuaArg tab, clearmeta; LuaVar metatable, metafield; LuaStack LS(L, tab, clearmeta, metatable, metafield); @@ -148,7 +134,7 @@ LuaDefine(table_clear, "c") { return LS.result(); } -LuaDefine(table_getflagbits, "c") { +LuaDefine(table_getflagbits, "table", "get the table's flag bits (debugging only)") { LuaArg tab; LuaRet bits; LuaStack LS(L, tab, bits); @@ -157,7 +143,7 @@ LuaDefine(table_getflagbits, "c") { return LS.result(); } -LuaDefine(table_setflagbits, "c") { +LuaDefine(table_setflagbits, "table,bits", "set the table's flag bits (debugging only)") { LuaArg tab, bits; LuaStack LS(L, tab, bits); uint16_t ubits = LS.ckinteger(bits); @@ -238,7 +224,7 @@ int deque_make_room(lua_State *L, int deque, int left, int fill, int max) { return max; } -LuaDefine(deque_create, "c") { +LuaDefine(deque_create, "", "create a deque") { LuaRet rdeque; LuaVar classobj; LuaStack LS(L, rdeque, classobj); @@ -255,7 +241,7 @@ LuaDefine(deque_create, "c") { return LS.result(); } -LuaDefine(deque_pushl, "c") { +LuaDefine(deque_pushl, "deque,value", "push onto the left end of a deque") { LuaArg deque, elt; LuaStack LS(L, deque, elt); int left, fill, max; @@ -269,7 +255,7 @@ LuaDefine(deque_pushl, "c") { return LS.result(); } -LuaDefine(deque_pushr, "c") { +LuaDefine(deque_pushr, "deque,value", "push onto the right end of a deque") { LuaArg deque, elt; LuaStack LS(L, deque, elt); int left, fill, max; @@ -282,7 +268,7 @@ LuaDefine(deque_pushr, "c") { return LS.result(); } -LuaDefine(deque_popl, "c") { +LuaDefine(deque_popl, "deque", "pop the left end of a deque") { LuaArg deque; LuaRet result; LuaStack LS(L, deque, result); @@ -300,7 +286,7 @@ LuaDefine(deque_popl, "c") { return LS.result(); } -LuaDefine(deque_popr, "c") { +LuaDefine(deque_popr, "deque", "pop the right end of a deque") { LuaArg deque; LuaRet result; LuaStack LS(L, deque, result); @@ -318,7 +304,7 @@ LuaDefine(deque_popr, "c") { return LS.result(); } -LuaDefine(deque_nthl, "c") { +LuaDefine(deque_nthl, "deque,n", "return the nth item from the left end of a deque") { LuaArg deque, nn; LuaRet result; LuaStack LS(L, deque, nn, result); @@ -334,7 +320,7 @@ LuaDefine(deque_nthl, "c") { return LS.result(); } -LuaDefine(deque_nthr, "c") { +LuaDefine(deque_nthr, "deque,n", "return the nth item from the right end of a deque") { LuaArg deque, nn; LuaRet result; LuaStack LS(L, deque, nn, result); @@ -350,7 +336,7 @@ LuaDefine(deque_nthr, "c") { return LS.result(); } -LuaDefine(deque_setl, "c") { +LuaDefine(deque_setl, "deque,n,value", "set the nth item from the left end of a deque") { LuaArg deque, nn, val; LuaStack LS(L, deque, nn, val); int left, fill, max; @@ -365,7 +351,7 @@ LuaDefine(deque_setl, "c") { return LS.result(); } -LuaDefine(deque_setr, "c") { +LuaDefine(deque_setr, "deque,n,value", "set the nth item from the right end of a deque") { LuaArg deque, nn, val; LuaStack LS(L, deque, nn, val); int left, fill, max; @@ -380,7 +366,7 @@ LuaDefine(deque_setr, "c") { return LS.result(); } -LuaDefine(deque_findl, "c") { +LuaDefine(deque_findl, "deque,value", "find the first occurence of value in deque, starting from left") { LuaArg deque, val; LuaRet pos; LuaVar check; @@ -399,7 +385,7 @@ LuaDefine(deque_findl, "c") { return LS.result(); } -LuaDefine(deque_findr, "c") { +LuaDefine(deque_findr, "deque,value", "find the first occurrence of value in deque, starting from right") { LuaArg deque, val; LuaRet pos; LuaVar check; @@ -419,7 +405,7 @@ LuaDefine(deque_findr, "c") { return LS.result(); } -LuaDefine(deque_size, "c") { +LuaDefine(deque_size, "deque", "return the number of items in the deque") { LuaArg deque; LuaRet size; LuaStack LS(L, deque, size); @@ -576,7 +562,7 @@ bool table_getpairs(LuaStack &LS0, LuaSlot tab, LuaSlot pairs, bool sort) { // ///////////////////////////////////////////////////////////// -LuaDefine(table_nextsortedpair, "c") { +LuaDefine(table_nextsortedpair, "sortedpairs,dummy", "next function used by sortedpairs") { if (lua_gettop(L) < 2) { luaL_error(L, "Not enough arguments to nextpair"); } @@ -595,20 +581,20 @@ LuaDefine(table_nextsortedpair, "c") { } } -LuaDefine(table_sortedpairs, "c") { +LuaDefine(table_sortedpairs, "table", "iterate over table, sorting all keys") { LuaArg tab; LuaRet closure, rtab, key; LuaStack LS(L, tab, closure, rtab, key); bool sorted = table_getpairs(LS, tab, rtab, true); if (!sorted) { - luaL_error(L, "Cannot iterate over a table with unsortable keys"); + luaL_error(L, "Cannot sort the table keys"); } LS.set(closure, lfn_table_nextsortedpair); LS.set(key, LuaNil); return LS.result(); } -LuaDefine(table_semisortedpairs, "c") { +LuaDefine(table_semisortedpairs, "table", "iterate over table, sorting those keys that can be sorted") { LuaArg tab; LuaRet closure, rtab, key; LuaStack LS(L, tab, closure, rtab, key); @@ -618,7 +604,7 @@ LuaDefine(table_semisortedpairs, "c") { return LS.result(); } -LuaDefine(table_genlt, "f") { +LuaDefine(genlt, "obj1,obj2", "return true if obj1 is less than obj2 in general ordering") { LuaArg o1,o2; LuaRet lt; LuaStack LS(L, o1, o2, lt); diff --git a/luprex/core/cpp/util.cpp b/luprex/core/cpp/util.cpp index 7cf9be6a..69f7ebd4 100644 --- a/luprex/core/cpp/util.cpp +++ b/luprex/core/cpp/util.cpp @@ -142,6 +142,28 @@ StringVec split(const std::string &s, char sep) { return result; } +static std::string substr_nocr(const std::string &s, int start, int len) { + if ((len > 0) && (s[start + len - 1] == '\r')) { + len -= 1; + } + return s.substr(start, len); +} + +StringVec split_lines(const std::string &s) { + StringVec result; + int start = 0; + for (int i = 0; i < int(s.size()); i++) { + if (s[i]=='\n') { + result.push_back(substr_nocr(s, start, i-start)); + start = i + 1; + } + } + if (start < int(s.size())) { + result.push_back(substr_nocr(s, start, s.size()-start)); + } + return result; +} + std::string join(const StringVec &strs, const std::string &sep) { if (strs.empty()) return ""; std::ostringstream oss; @@ -271,6 +293,12 @@ LuaSourcePtr make_lua_source(const std::string &code) { return result; } +bool is_lua_comment(const std::string &s) { + int start = 0; + while ((start < int(s.size())) && ((s[start]==' ') || (s[start]=='\t'))) start++; + return s.substr(start, 2) == "--"; +} + static std::string get_file_contents(const std::string &fn) { std::ifstream fs(fn); std::stringstream buffer; @@ -329,7 +357,7 @@ std::ostream &operator<<(std::ostream &oss, const util::hex8 &v) { return oss; } -LuaDefine(unittests_util, "c") { +LuaDefine(unittests_util, "", "some unit tests") { // Test the unioning of ID vectors. util::IdVector idv1,idv2; idv1.push_back(1); @@ -358,6 +386,15 @@ LuaDefine(unittests_util, "c") { LuaAssert(L, sv2[2]==""); LuaAssert(L, sv2[3]=="bar"); + // Test the split_lines routine. + util::StringVec sv3 = util::split_lines("foo\n\nbar\r\nbaz\r\n\r\n"); + LuaAssert(L, sv3.size() == 5); + LuaAssert(L, sv3[0] == "foo"); + LuaAssert(L, sv3[1] == ""); + LuaAssert(L, sv3[2] == "bar"); + LuaAssert(L, sv3[3] == "baz"); + LuaAssert(L, sv3[4] == ""); + // Test the repeat string routine. LuaAssertStrEq(L, util::repeat_string("abc", 3), "abcabcabc"); diff --git a/luprex/core/cpp/util.hpp b/luprex/core/cpp/util.hpp index fd0c36f0..ac968505 100644 --- a/luprex/core/cpp/util.hpp +++ b/luprex/core/cpp/util.hpp @@ -64,6 +64,9 @@ std::string hash_to_hex(const HashValue &hash); // Split a string into multiple strings StringVec split(const std::string &s, char sep); +// Split a string into multiple strings using \r or \n +StringVec split_lines(const std::string &s); + // Join multiple strings into one string std::string join(const StringVec &strs, std::string sep); @@ -108,6 +111,9 @@ bool world_type_authoritative(util::WorldType wt); // Make a LuaSourceVec with one element, for unit testing. LuaSourcePtr make_lua_source(const std::string &code); +// Return true if the line of code is a lua comment. +bool is_lua_comment(const std::string &line); + // Remove nullptrs from a vector of unique pointers. template void remove_nullptrs(std::vector> &vec) { diff --git a/luprex/core/cpp/world-accessor.cpp b/luprex/core/cpp/world-accessor.cpp index cd80c925..d30bbe97 100644 --- a/luprex/core/cpp/world-accessor.cpp +++ b/luprex/core/cpp/world-accessor.cpp @@ -17,7 +17,7 @@ static void tangible_getall(LuaStack &LS0, LuaSlot list, const util::IdVector &i LS.result(); } -LuaDefine(tangible_animstate, "c") { +LuaDefine(tangible_animstate, "tan", "get the final animation state of the tangible") { LuaArg tanobj; LuaRet graphic, plane, x, y, z, facing; LuaStack LS(L, tanobj, graphic, plane, x, y, z, facing); @@ -33,7 +33,7 @@ LuaDefine(tangible_animstate, "c") { return LS.result(); } -LuaDefine(tangible_animate, "c") { +LuaDefine(tangible_animate, "tan,configtable", "add an animation step to the tangible") { LuaArg tanobj, config; LuaStack LS(L, tanobj, config); World *w = World::fetch_global_pointer(L); @@ -50,7 +50,7 @@ LuaDefine(tangible_animate, "c") { return LS.result(); } -LuaDefine(tangible_setclass, "c") { +LuaDefine(tangible_setclass, "tan,classname", "set the class of the tangible") { LuaArg tanobj, classname; LuaVar classtab, mt; LuaStack LS(L, tanobj, classname, classtab, mt); @@ -66,7 +66,7 @@ LuaDefine(tangible_setclass, "c") { return LS.result(); } -LuaDefine(tangible_getclass, "c") { +LuaDefine(tangible_getclass, "tan", "get the class of the tangible, if any") { LuaArg tanobj; LuaVar mt, classtab; LuaRet classname; @@ -84,7 +84,7 @@ LuaDefine(tangible_getclass, "c") { return LS.result(); } -LuaDefine(tangible_delete, "c") { +LuaDefine(tangible_delete, "tan", "delete the specified tangible") { LuaArg tanobj; LuaStack LS(L, tanobj); World *w = World::fetch_global_pointer(L); @@ -98,7 +98,7 @@ LuaDefine(tangible_delete, "c") { return LS.result(); } -LuaDefine(tangible_build, "c") { +LuaDefine(tangible_build, "configtable", "build a new tangible object") { LuaArg config; LuaVar classname, classtab, mt; LuaRet database; @@ -148,7 +148,7 @@ LuaDefine(tangible_build, "c") { return LS.result(); } -LuaDefine(tangible_get, "c") { +LuaDefine(tangible_get, "id", "get the tangible with the specified id (debugging only)") { LuaArg id; LuaVar tangibles; LuaRet database; @@ -162,7 +162,7 @@ LuaDefine(tangible_get, "c") { return LS.result(); } -LuaDefine(tangible_redirect, "c") { +LuaDefine(tangible_redirect, "tan1,tan2,bulldozetan1", "redirect is not working yet") { LuaArg actor1, actor2, bldz; LuaStack LS(L, actor1, actor2, bldz); World *w = World::fetch_global_pointer(L); @@ -184,7 +184,7 @@ LuaDefine(tangible_redirect, "c") { return LS.result(); } -LuaDefine(tangible_id, "c") { +LuaDefine(tangible_id, "tan", "return the tangible's id number (debugging only)") { LuaArg tanobj; LuaRet id; LuaStack LS(L, tanobj, id); @@ -196,7 +196,7 @@ LuaDefine(tangible_id, "c") { return LS.result(); } -LuaDefine(tangible_actor, "c") { +LuaDefine(tangible_actor, "", "return the current actor") { LuaRet actor; LuaVar tangibles; LuaStack LS(L, tangibles, actor); @@ -206,7 +206,7 @@ LuaDefine(tangible_actor, "c") { return LS.result(); } -LuaDefine(tangible_place, "c") { +LuaDefine(tangible_place, "", "return the current place") { LuaRet place; LuaVar tangibles; LuaStack LS(L, tangibles, place); @@ -216,7 +216,7 @@ LuaDefine(tangible_place, "c") { return LS.result(); } -LuaDefine(tangible_near, "c") { +LuaDefine(tangible_near, "tan,radius,omit_nowhere,omit_self", "scan near the specified tangible") { LuaArg ltan, lradius, lomit_nowhere, lomit_self; LuaRet list; LuaStack LS(L, ltan, lradius, lomit_nowhere, lomit_self, list); @@ -231,7 +231,7 @@ LuaDefine(tangible_near, "c") { return LS.result(); } -LuaDefine(tangible_scan, "c") { +LuaDefine(tangible_scan, "plane,x,y,radius,omit_nowhere", "scan the specified plane") { LuaArg lplane, lx, ly, lradius, lomit_nowhere; LuaRet list; LuaStack LS(L, lplane, lx, ly, lradius, lomit_nowhere, list); @@ -246,14 +246,14 @@ LuaDefine(tangible_scan, "c") { return LS.result(); } -LuaDefine(world_wait, "f") { +LuaDefine(wait, "nticks", "wait the specified number of ticks") { if ((lua_gettop(L) != 1) || (lua_type(L, -1) != LUA_TNUMBER)) { luaL_error(L, "Argument to wait must be a number."); } return lua_yield(L, 1); } -LuaDefine(tangible_nopredict, "c") { +LuaDefine(tangible_nopredict, "", "stop predictive execution of this thread") { if (lua_gettop(L) != 0) { luaL_error(L, "tangible.nopredict takes no arguments"); } @@ -265,35 +265,7 @@ LuaDefine(tangible_nopredict, "c") { } } -LuaDefine(world_getregistry, "f") { - lua_pushvalue(L, LUA_REGISTRYINDEX); - return 1; -} - -LuaDefine(world_xtype, "f") { - LuaArg tab; - LuaRet rtype; - LuaStack LS(L, tab, rtype); - int xt = LS.xtype(tab); - LS.set(rtype, xt); - return LS.result(); -} - -LuaDefine(world_settabletype, "f") { - LuaArg tab, ttype; - LuaStack LS(L, tab, ttype); - if (!LS.istable(tab)) { - luaL_error(L, "Not a table"); - } - int tt = LS.ckinteger(ttype); - if ((tt < LUA_TT_GENERAL) || (tt > LUA_TT_CLASS)) { - luaL_error(L, "table type out of range"); - } - LS.settabletype(tab, tt); - return LS.result(); -} - -LuaDefine(world_pprint, "f") { +LuaDefine(pprint, "obj1,obj2,...", "pretty-print all the objects") { World *w = World::fetch_global_pointer(L); std::ostream *ostream = w->lthread_print_stream(); LuaStack LS(L); @@ -305,14 +277,27 @@ LuaDefine(world_pprint, "f") { return LS.result(); } -LuaDefine(world_print, "f") { +LuaDefine(print, "obj1,obj2,...", "print all the objects") { World *w = World::fetch_global_pointer(L); std::ostream *ostream = w->lthread_print_stream(); LuaStack LS(L); for (int i = 1; i <= lua_gettop(L); i++) { LuaSpecial root(i); - atomic_print(LS, root, ostream); + atomic_print(LS, root, false, ostream); (*ostream) << std::endl; } return LS.result(); +} + +LuaDefine(doc, "function", "print documentation for specified function") { + World *w = World::fetch_global_pointer(L); + std::ostream *ostream = w->lthread_print_stream(); + LuaArg func; + LuaStack LS(L, func); + std::string doc = SourceDB::function_docs(LS, func); + if (doc == "") { + (*ostream) << "no doc found" << std::endl; + } + (*ostream) << doc; + return LS.result(); } \ No newline at end of file diff --git a/luprex/core/cpp/world-core.cpp b/luprex/core/cpp/world-core.cpp index e361d626..f120f954 100644 --- a/luprex/core/cpp/world-core.cpp +++ b/luprex/core/cpp/world-core.cpp @@ -711,6 +711,7 @@ void World::run_scheduled_threads() { if (LS.ckboolean(print)) { for (int i = 1; i <= lua_gettop(CO); i++) { pprint(LSCO, LuaSpecial(i), true, ostream); + (*ostream) << std::endl; } } LS.rawset(threads, sched.thread_id(), LuaNil); diff --git a/luprex/core/cpp/world-difftab.cpp b/luprex/core/cpp/world-difftab.cpp index 2734ab0f..fca08f80 100644 --- a/luprex/core/cpp/world-difftab.cpp +++ b/luprex/core/cpp/world-difftab.cpp @@ -438,8 +438,8 @@ void World::diff_tangible_databases(const IdVector &basis, lua_State *master, St MLS.result(); } -LuaDefine(table_diffcompare, "c") { - LuaArg mtnmap, mtab, mstnmap, mstab, stnmap, stab; +LuaDefine(table_diffcompare, "mtnmap,mtab,stnmap,stab", "for unit testing only") { + LuaArg mtnmap, mtab, mstnmap, mstab; LuaRet dbgstring; LuaVar tthread; LuaStack MLS(L, mtnmap, mtab, mstnmap, mstab, dbgstring, tthread); @@ -456,6 +456,7 @@ LuaDefine(table_diffcompare, "c") { lua_pushvalue(L, mstnmap.index()); lua_pushvalue(L, mstab.index()); lua_xmove(L, synch, 2); + LuaArg stnmap,stab; LuaStack SLS(synch, stnmap, stab); // Call tablecmp_diff. @@ -467,8 +468,8 @@ LuaDefine(table_diffcompare, "c") { return MLS.result(); } -LuaDefine(table_diffapply, "c") { - LuaArg mtnmap, mtab, mstab, stnmap, stab; +LuaDefine(table_diffapply, "mtnmap,mtab,mstab", "for unit testing only") { + LuaArg mtnmap, mtab, mstab; LuaRet eql, eqlstr, rtab; LuaVar tthread, tangibles, mntmap, key, val; LuaStack MLS(L, mtnmap, mtab, mstab, eql, eqlstr, rtab, tthread, tangibles, mntmap, key, val); @@ -494,6 +495,7 @@ LuaDefine(table_diffapply, "c") { lua_pushvalue(L, mtnmap.index()); lua_pushvalue(L, mstab.index()); lua_xmove(L, synch, 2); + LuaArg stnmap, stab; LuaStack SLS(synch, stnmap, stab); // Call diff_tables and patch_tables diff --git a/luprex/core/cpp/world-testing.cpp b/luprex/core/cpp/world-testing.cpp index 8f3612a3..d127b104 100644 --- a/luprex/core/cpp/world-testing.cpp +++ b/luprex/core/cpp/world-testing.cpp @@ -240,7 +240,7 @@ static bool worlds_identical(const UniqueWorld &w1, const UniqueWorld &w2) { return sbw1.contents_equal(&sbw2); } -LuaDefine(unittests_world1animdiff, "c") { +LuaDefine(unittests_world1animdiff, "", "some unit tests") { UniqueWorld m(new World(util::WORLD_TYPE_MASTER)); UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC)); UniqueWorld cs(new World(util::WORLD_TYPE_C_SYNC)); @@ -310,7 +310,7 @@ LuaDefine(unittests_world1animdiff, "c") { return 0; } -LuaDefine(unittests_world2pairtab, "c") { +LuaDefine(unittests_world2pairtab, "", "some unit tests") { UniqueWorld m(new World(util::WORLD_TYPE_MASTER)); UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC)); StreamBuffer sb; @@ -358,7 +358,7 @@ LuaDefine(unittests_world2pairtab, "c") { return 0; } -LuaDefine(unittests_world3diffluatab, "c") { +LuaDefine(unittests_world3diffluatab, "", "some unit tests") { UniqueWorld m(new World(util::WORLD_TYPE_MASTER)); UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC)); UniqueWorld cs(new World(util::WORLD_TYPE_C_SYNC)); @@ -412,7 +412,7 @@ LuaDefine(unittests_world3diffluatab, "c") { return 0; } -LuaDefine(unittests_world4difftanclass, "c") { +LuaDefine(unittests_world4difftanclass, "", "some unit tests") { UniqueWorld m(new World(util::WORLD_TYPE_MASTER)); UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC)); UniqueWorld cs(new World(util::WORLD_TYPE_C_SYNC)); diff --git a/luprex/core/lua/login.lua b/luprex/core/lua/login.lua index 44daff6f..e39803de 100644 --- a/luprex/core/lua/login.lua +++ b/luprex/core/lua/login.lua @@ -17,6 +17,7 @@ function login.action.p123(actor, place, dialog) print(3) end +-- this is function documentation for setfoo. function setfoo(n) tangible.nopredict() tangible.actor().inventory.foo = n