diff --git a/luprex/cpp/core/globaldb.cpp b/luprex/cpp/core/globaldb.cpp index 9a1778ab..2e372a00 100644 --- a/luprex/cpp/core/globaldb.cpp +++ b/luprex/cpp/core/globaldb.cpp @@ -5,13 +5,16 @@ LuaDefine(global_set, "varname, value", "|Store data in the global data table." "|" + "|The variable name must be a string which is a valid" + "|lua identifier." + "|" "|You can store global data using global.set, then you can" "|retrieve it using global.get. You can also retrieve data using" "|gv.varname, which is just shorthand for global.get. You may not" "|store data using gv.varname=value, this yields the error 'Use " "|global.set to store data in the global data table.'" "|" - "|Values stored using global.set are difference transmitted to all" + "|Values stored using global.set are transmitted to all" "|connected clients immediately. When a new client connects," "|he will receive all the global data." "|" diff --git a/luprex/cpp/core/luastack.hpp b/luprex/cpp/core/luastack.hpp index 2460514f..01fae478 100644 --- a/luprex/cpp/core/luastack.hpp +++ b/luprex/cpp/core/luastack.hpp @@ -474,8 +474,9 @@ public: return lua_rawequal(L_, v1, v2); } - bool rawequal(LuaSlot v1, const char *name) const { - push_any_value(name); + template + bool rawequal(LuaSlot v1, VT value) const { + push_any_value(value); bool result = lua_rawequal(L_, v1, -1); lua_pop(L_, 1); return result; @@ -617,9 +618,11 @@ public: }; #define LuaTokenConstant(name, tvalue, docs) \ + LuaToken ltoken_##name(tvalue); \ LuaConstantReg reg_##name(#name, docs, LuaToken(tvalue), 0); #define LuaNumberConstant(name, nvalue, docs) \ + lua_Number lnumber_##name(nvalue); \ LuaConstantReg reg_##name(#name, docs, LuaToken(), nvalue); #define LuaDefine(name, args, docs) \ diff --git a/luprex/cpp/core/pprint.cpp b/luprex/cpp/core/pprint.cpp index 61a2c918..80f74be3 100644 --- a/luprex/cpp/core/pprint.cpp +++ b/luprex/cpp/core/pprint.cpp @@ -7,255 +7,352 @@ #include #include +class PrintMachine { +public: + LuaVar tabchpos_; + LuaStack LS_; + int next_id_; + bool indent_; + std::ostream *output_; + eng::map chpos_to_tabnum_; -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: - 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); - if (std::isnan(value)) { - (*os) << "nan"; - } else { - int64_t ivalue = int64_t(value); - if (double(ivalue) == value) { - (*os) << ivalue; + void atomic_print(int xtype, LuaSlot val, bool quote) { + switch (xtype) { + case LUA_TNIL: { + (*output_) << "nil"; + return; + } + case LUA_TSTRING: { + if (quote) { + util::quote_string(LS_.ckstring(val), output_); } else { - (*os) << value; - } + // TODO: this could be more efficient. + (*output_) << LS_.ckstring(val); + } + return; } - return; - } - case LUA_TBOOLEAN: - (*os) << (LS.ckboolean(val) ? "true" : "false"); - return; - case LUA_TFUNCTION: { - (*os) << ""; - return; - } - case LUA_TLIGHTUSERDATA: { - LuaToken token = LS.cktoken(val); - (*os) << "[" << token.str() << "]"; - return; - } - default: - (*os) << "<" << lua_typename(LS.state(), tt) << ">"; - return; - } -} - - -// Find tables recursively. -// -// Builds a table (tabcount) whose keys are tables. If a table -// is visited exactly once, then tabcount[t]=0. If a table is -// visited more than once, then tabcount[t]=-1. -// -static void findtables(LuaStack &LS0, LuaSlot root, LuaSlot tabcount) { - lua_State *L = LS0.state(); - LuaVar key, val, tab, count; - LuaStack LS(L, key, val, tab, count); - - LS.newtable(tabcount); - int top = lua_gettop(L); - if (LS.istable(root)) { - lua_pushvalue(L, root.index()); - } - while (lua_gettop(L) > top) { - lua_checkstack(L, 20); - lua_replace(L, tab.index()); - LS.rawget(count, tabcount, tab); - if (LS.isnil(count)) { - LS.rawset(tabcount, tab, 0); - LS.set(key, LuaNil); - while (LS.next(tab, key, val)) { - lua_checkstack(L, 20); - if (LS.istable(key)) { - lua_pushvalue(L, key.index()); - } - if (LS.istable(val)) { - lua_pushvalue(L, val.index()); + case LUA_TNUMBER: { + double value = LS_.cknumber(val); + if (std::isnan(value)) { + (*output_) << "nan"; + } else { + int64_t ivalue = int64_t(value); + if (double(ivalue) == value) { + (*output_) << ivalue; + } else { + (*output_) << value; } } - LS.getmetatable(val, tab); - if (LS.istable(val)) { - lua_pushvalue(L, val.index()); + return; + } + case LUA_TBOOLEAN: { + (*output_) << (LS_.ckboolean(val) ? "true" : "false"); + return; + } + case LUA_TFUNCTION: { + (*output_) << ""; + return; + } + case LUA_TTHREAD: { + (*output_) << ""; + return; + } + case LUA_TLIGHTUSERDATA: { + LuaToken token = LS_.cktoken(val); + (*output_) << "[" << token.str() << "]"; + return; + } + case LUA_TT_GENERAL: { + (*output_) << ""; + return; + } + case LUA_TT_TANGIBLE: { + (*output_) << ""; + return; + } + case LUA_TT_CLASS: { + (*output_) << ""; + return; + } + case LUA_TT_GLOBALENV: { + (*output_) << ""; + return; + } + default: { + (*output_) << ""; + return; + }} + } + + void tabify(int level) { + if (indent_) { + (*output_) << std::endl; + for (int i = 0; i < level; i++) { + (*output_) << " "; } } else { - LS.rawset(tabcount, tab, -1); + (*output_) << " "; } } + + void pprint_r(int level, bool expand, LuaSlot value) { + lua_State *L = LS_.state(); + lua_checkstack(L, 20); + LuaVar loffset, pairs, key, val, lchpos, nextseq; + LuaStack LS(L, loffset, pairs, key, val, lchpos, nextseq); + + // Determine the extended type of the object. If the + // expand flag is true, try to coerce it to a general table. + int type = LS.xtype(value); + if (expand && (LS.istable(value))) { + type = LUA_TT_GENERAL; + } + + // If it's anything but a general table, use 'atomic_print' + // and return. + if (type != LUA_TT_GENERAL) { + atomic_print(type, value, true); + LS.result(); + return; + } + + // Find out whether it has a table number. If necessary, + // assign one. + int tabnum = 0; + LS.rawget(lchpos, tabchpos_, value); + if (!LS.isnumber(lchpos)) { + // First time. Record the character position where the + // table first appears in the output stream. + LS.rawset(tabchpos_, value, int((*output_).tellp())); + } else { + int chpos = LS.ckint(lchpos); + tabnum = chpos_to_tabnum_[chpos]; + if (tabnum == 0) { + // Second time. The table is already in the output, + // but it hasn't been assigned a number. Assign one. + tabnum = next_id_++; + chpos_to_tabnum_[chpos] = tabnum; + } + } + + // If the table has a number, that means we're visiting + // it for the second time. Just print an abbreviated version + // and return. + if (tabnum > 0) { + (*output_) << "
"; + if (lua_nkeys(L, value.index())==0) { + (*output_) << "{}"; + } else { + (*output_) << "{...}"; + } + return; + } + + // State variables. + bool needcomma = false; + bool multiline = false; + LS.set(nextseq, 1); + + // Open the brackets. + (*output_) << "{"; + + // Output the table keys. + table_getpairs(LS, value, pairs, true); + for (int i = 2; ; i+=2) { + LS.rawget(key, pairs, i); + if (LS.isnil(key)) break; + LS.rawget(val, pairs, i+1); + if (needcomma) (*output_) << ","; + needcomma = true; + if (LS.rawequal(key, nextseq)) { + (*output_) << " "; + pprint_r(level + 1, false, val); + LS.set(nextseq, LS.ckinteger(nextseq) + 1); + } else { + multiline = true; + tabify(level + 1); + if (LS.isstring(key) && sv::is_lua_id(LS.ckstring(key))) { + (*output_) << LS.ckstring(key); + } else { + (*output_) << "["; + pprint_r(level + 1, false, key); + (*output_) << "]"; + } + if (indent_) { + (*output_) << " = "; + } else { + (*output_) << "="; + } + pprint_r(level + 1, false, val); + } + } + + // Output the metatable. + LS.getmetatable(val, value); + if (LS.istable(val)) { + multiline = true; + if (needcomma) (*output_) << ","; + needcomma = true; + tabify(level + 1); + (*output_) << " = "; + pprint_r(level + 1, false, val); + } + + // Close the brackets. + if (multiline) { + tabify(level); + } else if (LS.ckinteger(nextseq) > 1) { + (*output_) << " "; + } + (*output_) << "}"; + + LS.result(); + } + + // Atomic print interface. + PrintMachine(LuaStack &LS0, LuaSlot root, bool quote, std::ostream *os) : + LS_(LS0.state(), tabchpos_) { + output_ = os; + atomic_print(LS_.xtype(root), root, quote); + LS_.result(); + } + + // Pretty print interface. + PrintMachine(LuaStack &LS0, LuaSlot root, bool indent, int level, bool expand, std::ostream *os) : + LS_(LS0.state(), tabchpos_) { + next_id_ = 1; + indent_ = indent; + LS_.newtable(tabchpos_); + util::ostringstream preoutput; + output_ = &preoutput; + pprint_r(level, expand, root); + std::string_view pre = preoutput.view(); + + // Output the results. We would just copy the characters + // one by one to the target stream, except that we have to + // insert
in front of all tables that got referenced. + chpos_to_tabnum_.emplace(0x7FFFFFFF, 0); + auto iter = chpos_to_tabnum_.begin(); + for (int i = 0; i < int(pre.size()); i++) { + if (i == iter->first) { + (*os) << "
second << ">"; + iter++; + } + (*os).put(pre[i]); + } + LS_.result(); + } +}; + +void PrettyPrintOptions::parse(LuaKeywordParser &kp) { + LuaVar option; + LuaStack LS(kp.state(), option); + if (kp.parse(option, "indent")) { + indent = LS.ckboolean(option); + } + if (kp.parse(option, "level")) { + level = LS.ckint(option); + } + if (kp.parse(option, "expand")) { + expand = LS.ckboolean(option); + } LS.result(); } -LuaDefine(table_findtables, "root", "recursively find tables (debugging only)") { - LuaArg root; - LuaRet tabcount; - LuaStack LS(L, root, tabcount); - findtables(LS, root, tabcount); +void atomic_print(LuaStack &LS, LuaSlot val, bool quote, std::ostream *os) { + PrintMachine pm(LS, val, quote, os); +} + +void pprint(LuaStack &LS, LuaSlot val, const PrettyPrintOptions &opts, std::ostream *os) { + PrintMachine pm(LS, val, opts.indent, opts.level, opts.expand, os); +} + +LuaDefine(string_pprint, "obj1, obj2, ...", + "|Pretty-print the specified objects into a string." + "|" + "|See also: string.pprintx, which has a lot more options." + "|This function uses the default options: pretty print indented," + "|start at indentation level zero, and always expand the" + "|top-level table." + "|") { + int n = lua_gettop(L); + LuaRet result; + LuaStack LS(L, result); + util::ostringstream oss; + for (int i = 1; i <= n; i++) { + LuaSpecial root(i); + pprint(LS, root, PrettyPrintOptions(), &oss); + if (i < n) oss << "\n"; + } + oss << std::endl; + LS.set(result, oss.view()); return LS.result(); } -struct Inspector { - lua_State *L; - LuaVar ids; - int nextid; - bool indent; - int maxlen; - bool anyindent; - std::ostream *stream; -}; - -static void tabify(Inspector &insp, int level) { - if (insp.indent) { - (*insp.stream) << std::endl; - for (int i = 0; i < level; i++) { - (*insp.stream) << " "; - } - insp.anyindent = true; - } else { - (*insp.stream) << " "; +LuaDefine(string_pprintx, "options", + "|Pretty-print the specified object into a string, with options" + "|" + "|Options is a table with these fields:" + "|" + "| value - the object to pretty-print" + "| indent - if false, suppress newlines and indentation (default: true)" + "| level - base level of indentation (default: zero)" + "| expand - if true, force expansion of top-level table (default: false)" + "|" + "|About the expand flag: normally, when you print a class, it just " + "|prints '', and when you print a tangible, it just" + "|prints ''. But sometimes, you want to see the details." + "|The expand flag forces it to expand the top-level table, even if the" + "|top-level table is a tangible or class." + "|") { + LuaArg loptions; + LuaRet result; + LuaVar value; + LuaStack LS(L, loptions, result, value); + PrettyPrintOptions options; + LuaKeywordParser kp(LS, loptions); + options.parse(kp); + if (!kp.parse(value, "value")) { + LS.set(value, LuaNil); } + kp.final_check_throw(); + util::ostringstream oss; + pprint(LS, value, options, &oss); + LS.set(result, oss.view()); + return LS.result(); } -static void pprint_r(Inspector &insp, int level, LuaSlot root) { - lua_checkstack(insp.L, 20); - LuaVar idv, pairs, key, val, nextseq; - LuaStack LS(insp.L, idv, pairs, key, val, nextseq); - - // If it's anything but a table, use 'atomic_print'. - if (!LS.istable(root)) { - atomic_print(LS, root, true, insp.stream); - LS.result(); - return; - } - - // Determine the table's ID, allocating an ID if necessary. - LS.rawget(idv, insp.ids, root); - int id = LS.ckint(idv); - bool new_id = false; - if (id < 0) { - new_id = true; - id = insp.nextid++; - LS.rawset(insp.ids, root, id); - } - - // Print the table's name, if any. - bool is_class = false; - bool is_tangible = false; - eng::string cname = LS.classname(root); - if (cname != "") { - is_class = true; - (*insp.stream) << ""; - } else { - int64_t tid = LS.tanid(root); - if (tid > 0) { - is_tangible = true; - (*insp.stream) << ""; - } else if (id > 0) { - (*insp.stream) << "
"; - } - } - - // If this is a class, and we're not at the top level, truncate. - if ((is_class || is_tangible) && (level > 0)) { - LS.result(); - return; - } - - // If this is a table we've already printed, truncate it. - if ((id > 0) && (!new_id)) { - if (lua_nkeys(insp.L, root.index())==0) { - (*insp.stream) << "{}"; - } else { - (*insp.stream) << "{...}"; - } - LS.result(); - return; - } - - // State variables. - bool needcomma = false; - bool multiline = false; - LS.set(nextseq, 1); - - // Open the brackets. - (*insp.stream) << "{"; - - // Output the table keys. - table_getpairs(LS, root, pairs, true); - for (int i = 2; ; i+=2) { - LS.rawget(key, pairs, i); - if (LS.isnil(key)) break; - LS.rawget(val, pairs, i+1); - if (needcomma) (*insp.stream) << ","; - needcomma = true; - if (LS.rawequal(key, nextseq)) { - (*insp.stream) << " "; - pprint_r(insp, level + 1, val); - LS.set(nextseq, LS.ckinteger(nextseq) + 1); - } else { - multiline = true; - tabify(insp, level + 1); - if (LS.isstring(key) && sv::is_lua_id(LS.ckstring(key))) { - (*insp.stream) << LS.ckstring(key); - } else { - (*insp.stream) << "["; - pprint_r(insp, level + 1, key); - (*insp.stream) << "]"; - } - if (insp.indent) { - (*insp.stream) << " = "; - } else { - (*insp.stream) << "="; - } - pprint_r(insp, level + 1, val); - } - } - - // Output the metatable. - LS.getmetatable(val, root); - if (LS.istable(val)) { - multiline = true; - if (needcomma) (*insp.stream) << ","; - needcomma = true; - tabify(insp, level + 1); - (*insp.stream) << " = "; - pprint_r(insp, level + 1, val); - } - - // Close the brackets. - if (multiline) { - tabify(insp, level); - } else if (LS.ckinteger(nextseq) > 1) { - (*insp.stream) << " "; - } - (*insp.stream) << "}"; - - LS.result(); +LuaDefine(string_print, "obj", + "|Concise print the specified object into a string" + "|" + "|This prints a concise representation of obj into a string. Tables" + "|are not expanded: for that, use string.pprintx. The functions" + "|tostring and string.print are identical." + "|") { + LuaArg val; + LuaRet result; + LuaStack LS(L, val, result); + eng::ostringstream oss; + atomic_print(LS, val, false, &oss); + LS.set(result, oss.str()); + return LS.result(); } -void pprint(LuaStack &LS0, LuaSlot root, bool indent, std::ostream *os) { - Inspector insp; - LuaStack LS(LS0.state(), insp.ids); - findtables(LS, root, insp.ids); - insp.L = LS0.state(); - insp.nextid = 1; - insp.indent = indent; - insp.anyindent = false; - insp.stream = os; - pprint_r(insp, 0, root); - LS.result(); + +LuaDefine(tostring, "obj", + "|Concise print the specified object into a string" + "|" + "|This prints a concise representation of obj into a string. Tables" + "|are not expanded: for that, use string.pprint. The functions" + "|tostring and string.print are identical." + "|") { + LuaArg val; + LuaRet result; + LuaStack LS(L, val, result); + eng::ostringstream oss; + atomic_print(LS, val, false, &oss); + LS.set(result, oss.str()); + return LS.result(); } LuaDefine(string_isidentifier, "str", "return true if the string is a valid lua identifier") { @@ -271,33 +368,5 @@ LuaDefine(string_isidentifier, "str", "return true if the string is a valid lua return LS.result(); } -LuaDefine(string_print, "obj", "print the specified object into a string") { - LuaArg val; - LuaRet result; - LuaStack LS(L, val, result); - eng::ostringstream oss; - atomic_print(LS, val, false, &oss); - LS.set(result, oss.str()); - return LS.result(); -} -LuaDefine(string_pprint, "obj,indent", "pretty-print the specified object into a string") { - LuaArg root, indent; - LuaRet result; - LuaStack LS(L, root, indent, result); - bool ind = LS.ckboolean(indent); - eng::ostringstream oss; - pprint(LS, root, ind, &oss); - LS.set(result, oss.str()); - return LS.result(); -} -LuaDefine(tostring, "obj", "print the specified object into a string") { - LuaArg val; - LuaRet result; - LuaStack LS(L, val, result); - eng::ostringstream oss; - atomic_print(LS, val, false, &oss); - LS.set(result, oss.str()); - return LS.result(); -} diff --git a/luprex/cpp/core/pprint.hpp b/luprex/cpp/core/pprint.hpp index c39a1a82..10ca7066 100644 --- a/luprex/cpp/core/pprint.hpp +++ b/luprex/cpp/core/pprint.hpp @@ -22,6 +22,14 @@ #include "luastack.hpp" #include +struct PrettyPrintOptions { + bool indent; + int level; + bool expand; + PrettyPrintOptions() : indent(true), level(0), expand(true) {} + void parse(LuaKeywordParser &kp); +}; + // Atomic print to a stream. // // This prints an atomic value to a stream. If you give it a table, @@ -32,6 +40,6 @@ void atomic_print(LuaStack &LS, LuaSlot val, bool quote, std::ostream *os); // Pretty print to a stream. // -void pprint(LuaStack &LS, LuaSlot val, bool indent, std::ostream *os); +void pprint(LuaStack &LS, LuaSlot val, const PrettyPrintOptions &opts, std::ostream *os); #endif // PPRINT_HPP \ No newline at end of file diff --git a/luprex/cpp/core/table.hpp b/luprex/cpp/core/table.hpp index a572b43e..dba1ad22 100644 --- a/luprex/cpp/core/table.hpp +++ b/luprex/cpp/core/table.hpp @@ -25,4 +25,5 @@ bool table_equal(LuaStack &LS0, LuaSlot tab1, LuaSlot tab2); // bool table_getpairs(LuaStack &LS0, LuaSlot tab, LuaSlot pairs, bool sort); + #endif // TABLE_HPP diff --git a/luprex/cpp/core/util.hpp b/luprex/cpp/core/util.hpp index 55b57f9f..438c008f 100644 --- a/luprex/cpp/core/util.hpp +++ b/luprex/cpp/core/util.hpp @@ -350,7 +350,32 @@ inline eng::string ss(const ARGS & ... args) { return oss.str(); } - +// util::ostringstream +// +// This is a variant of ostringstream in which it is possible +// to get the contents without copying. To get the contents +// without copying, use oss.size() and oss.c_str() +// +class ostringstream : public eng::ostringstream { + class rstringbuf : public std::basic_stringbuf { + public: + char *eback() const { return std::streambuf::eback(); } + char *pptr() const { return std::streambuf::pptr(); } + }; + rstringbuf rstringbuf_; +public: + ostringstream() { + std::basic_ostream::rdbuf(&rstringbuf_); + } + std::string_view view() const { + char *p = rstringbuf_.eback(); + size_t size = rstringbuf_.pptr() - p; + return std::string_view(p, size); + } + eng::string str() const { + return rstringbuf_.str(); + } +}; // dprintf // diff --git a/luprex/cpp/core/world-accessor.cpp b/luprex/cpp/core/world-accessor.cpp index fe18f595..f41f260e 100644 --- a/luprex/cpp/core/world-accessor.cpp +++ b/luprex/cpp/core/world-accessor.cpp @@ -711,21 +711,61 @@ LuaDefine(math_randomstate, "seed", LuaSandboxBuiltin(math_randomseed, "", ""); - -LuaDefine(pprint, "obj1,obj2,...", - "|Pretty-print object or objects.") { +LuaDefine(pprint, "obj1, obj2, ...", + "|Pretty-print the specified objects." + "|" + "|See also: pprintx, which has a lot more options." + "|This function uses the default options: pretty print indented," + "|start at indentation level zero, and always expand the" + "|top-level table." + "|") { World *w = World::fetch_global_pointer(L); std::ostream *ostream = w->lthread_print_stream(); + int n = lua_gettop(L); LuaStack LS(L); - for (int i = 1; i <= lua_gettop(L); i++) { + for (int i = 1; i <= n; i++) { LuaSpecial root(i); - pprint(LS, root, true, ostream); - (*ostream) << std::endl; + pprint(LS, root, PrettyPrintOptions(), ostream); + if (i < n) (*ostream) << "\n"; } + (*ostream) << std::endl; + return LS.result(); + +} + +LuaDefine(pprintx, "options", + "|Pretty-print the specified object, with options" + "|" + "|Options is a table with these fields:" + "|" + "| value - the object to pretty-print" + "| indent - if false, suppress newlines and indentation (default: true)" + "| level - base level of indentation (default: zero)" + "| expand - if true, force expansion of top-level table (default: false)" + "|" + "|About the expand flag: normally, when you print a class, it just " + "|prints '', and when you print a tangible, it just" + "|prints ''. But sometimes, you want to see the details." + "|The expand flag forces it to expand the top-level table, even if the" + "|top-level table is a tangible or class." + "|") { + World *w = World::fetch_global_pointer(L); + std::ostream *ostream = w->lthread_print_stream(); + LuaArg loptions; + LuaVar value; + LuaStack LS(L, loptions, value); + PrettyPrintOptions options; + LuaKeywordParser kp(LS, loptions); + options.parse(kp); + if (!kp.parse(value, "value")) { + LS.set(value, LuaNil); + } + kp.final_check_throw(); + pprint(LS, value, options, ostream); return LS.result(); } -LuaDefine(print, "obj1,obj2,...", +LuaDefine(print, "obj1, obj2, ...", "|Print object or objects.") { World *w = World::fetch_global_pointer(L); std::ostream *ostream = w->lthread_print_stream(); diff --git a/luprex/cpp/core/world-core.cpp b/luprex/cpp/core/world-core.cpp index c7ec45b7..4b7b27f2 100644 --- a/luprex/cpp/core/world-core.cpp +++ b/luprex/cpp/core/world-core.cpp @@ -297,7 +297,7 @@ eng::string World::probe_lua(int64_t actor_id, const eng::string &lua) { if (msg.empty()) { for (int i = top + 1; i <= lua_gettop(L); i++) { LuaSpecial root(i); - pprint(LS, root, true, ostream); + pprint(LS, root, PrettyPrintOptions(), ostream); // TODO: this endl is unnecessary if we just printed a newline. (*ostream) << std::endl; } @@ -876,7 +876,7 @@ void World::run_scheduled_threads() { LuaStack LSCO(CO); if (LS.ckboolean(print)) { for (int i = 1; i <= lua_gettop(CO); i++) { - pprint(LSCO, LuaSpecial(i), true, ostream); + pprint(LSCO, LuaSpecial(i), PrettyPrintOptions(), ostream); (*ostream) << std::endl; } } diff --git a/luprex/cpp/core/world-testing.cpp b/luprex/cpp/core/world-testing.cpp index 889fb748..4dff9b01 100644 --- a/luprex/cpp/core/world-testing.cpp +++ b/luprex/cpp/core/world-testing.cpp @@ -57,7 +57,9 @@ eng::string World::tangible_pprint(int64_t id) const { if (LS.istable(tan)) { LS.getmetatable(meta, tan); LS.clearmetatable(tan); - pprint(LS, tan, false, &oss); + PrettyPrintOptions opts; + opts.indent = false; + pprint(LS, tan, opts, &oss); LS.setmetatable(tan, meta); } else { oss << ""; diff --git a/luprex/cpp/drv/driver-common.cpp b/luprex/cpp/drv/driver-common.cpp index 934d094f..2affc465 100644 --- a/luprex/cpp/drv/driver-common.cpp +++ b/luprex/cpp/drv/driver-common.cpp @@ -158,7 +158,8 @@ class Driver { drvutil::ostringstream oss; std::string err = drvutil::package_lua_source(".", &oss); if_error_print_and_exit(err); - engw.play_set_lua_source(&engw, oss.size(), oss.c_str()); + std::string_view ossv = oss.view(); + engw.play_set_lua_source(&engw, ossv.size(), ossv.data()); } } @@ -590,7 +591,8 @@ class Driver { if_error_print_and_exit(srcpakerr); // Initialize the engine. - engw.play_initialize(&engw, argc, argv, srcpak.size(), srcpak.c_str(), replaylogfn.c_str()); + std::string_view srcpakv = srcpak.view(); + engw.play_initialize(&engw, argc, argv, srcpakv.size(), srcpakv.data(), replaylogfn.c_str()); if_error_print_and_exit(engw.error); // Set up listening ports. diff --git a/luprex/cpp/drv/drvutil.hpp b/luprex/cpp/drv/drvutil.hpp index 852d1216..eb4eb1e7 100644 --- a/luprex/cpp/drv/drvutil.hpp +++ b/luprex/cpp/drv/drvutil.hpp @@ -68,23 +68,27 @@ double get_monotonic_clock(); // without copying, use oss.size() and oss.c_str() // class ostringstream : public std::ostringstream { - class rstringbuf : public std::stringbuf { + class rstringbuf : public std::basic_stringbuf { public: - char *eback() { return std::streambuf::eback(); } + char *eback() const { return std::streambuf::eback(); } + char *pptr() const { return std::streambuf::pptr(); } }; - rstringbuf rsbuf_; + rstringbuf rstringbuf_; public: ostringstream() { - std::basic_ostream::rdbuf(&rsbuf_); + std::basic_ostream::rdbuf(&rstringbuf_); } - size_t size() { - return tellp(); + std::string_view view() const { + char *p = rstringbuf_.eback(); + size_t size = rstringbuf_.pptr() - p; + return std::string_view(p, size); } - const char *c_str() { - return rsbuf_.eback(); + std::string str() const { + return rstringbuf_.str(); } }; + // Remove items from a vector that are marked for deletion. // template diff --git a/luprex/cpp/wrap/wrap-sstream.hpp b/luprex/cpp/wrap/wrap-sstream.hpp index 785dfcbe..eb60b0ee 100644 --- a/luprex/cpp/wrap/wrap-sstream.hpp +++ b/luprex/cpp/wrap/wrap-sstream.hpp @@ -12,7 +12,10 @@ class basic_ostringstream : public std::basic_ostringstream>; using underlying::basic_ostringstream; }; +//template> +//using basic_stringbuf = std::basic_stringbuf>; using ostringstream = basic_ostringstream; +//using stringbuf = basic_stringbuf; } // namespace eng #endif // WRAP_SSTREAM_HPP diff --git a/luprex/lua/ut-globaldb.lua b/luprex/lua/ut-globaldb.lua new file mode 100644 index 00000000..431ce663 --- /dev/null +++ b/luprex/lua/ut-globaldb.lua @@ -0,0 +1,13 @@ +makeclass("unittests") + +function unittests.globaldb() + local g1a = global.table("unittest-g1") + local g2a = global.table("unittest-g2") + local g1b = global.table("unittest-g1") + local g2b = global.table("unittest-g2") + assert(g1a == g1b) + assert(g2a == g2b) + assert(g1a.__global == "unittest-g1") + assert(g2a.__global == "unittest-g2") +end +