Fix util::ostringstream, fix pretty-printing, stub out new globals
This commit is contained in:
@@ -5,13 +5,16 @@
|
|||||||
LuaDefine(global_set, "varname, value",
|
LuaDefine(global_set, "varname, value",
|
||||||
"|Store data in the global data table."
|
"|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"
|
"|You can store global data using global.set, then you can"
|
||||||
"|retrieve it using global.get. You can also retrieve data using"
|
"|retrieve it using global.get. You can also retrieve data using"
|
||||||
"|gv.varname, which is just shorthand for global.get. You may not"
|
"|gv.varname, which is just shorthand for global.get. You may not"
|
||||||
"|store data using gv.varname=value, this yields the error 'Use "
|
"|store data using gv.varname=value, this yields the error 'Use "
|
||||||
"|global.set to store data in the global data table.'"
|
"|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,"
|
"|connected clients immediately. When a new client connects,"
|
||||||
"|he will receive all the global data."
|
"|he will receive all the global data."
|
||||||
"|"
|
"|"
|
||||||
|
|||||||
@@ -474,8 +474,9 @@ public:
|
|||||||
return lua_rawequal(L_, v1, v2);
|
return lua_rawequal(L_, v1, v2);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rawequal(LuaSlot v1, const char *name) const {
|
template<typename VT>
|
||||||
push_any_value(name);
|
bool rawequal(LuaSlot v1, VT value) const {
|
||||||
|
push_any_value(value);
|
||||||
bool result = lua_rawequal(L_, v1, -1);
|
bool result = lua_rawequal(L_, v1, -1);
|
||||||
lua_pop(L_, 1);
|
lua_pop(L_, 1);
|
||||||
return result;
|
return result;
|
||||||
@@ -617,9 +618,11 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define LuaTokenConstant(name, tvalue, docs) \
|
#define LuaTokenConstant(name, tvalue, docs) \
|
||||||
|
LuaToken ltoken_##name(tvalue); \
|
||||||
LuaConstantReg reg_##name(#name, docs, LuaToken(tvalue), 0);
|
LuaConstantReg reg_##name(#name, docs, LuaToken(tvalue), 0);
|
||||||
|
|
||||||
#define LuaNumberConstant(name, nvalue, docs) \
|
#define LuaNumberConstant(name, nvalue, docs) \
|
||||||
|
lua_Number lnumber_##name(nvalue); \
|
||||||
LuaConstantReg reg_##name(#name, docs, LuaToken(), nvalue);
|
LuaConstantReg reg_##name(#name, docs, LuaToken(), nvalue);
|
||||||
|
|
||||||
#define LuaDefine(name, args, docs) \
|
#define LuaDefine(name, args, docs) \
|
||||||
|
|||||||
@@ -7,255 +7,352 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
class PrintMachine {
|
||||||
|
public:
|
||||||
|
LuaVar tabchpos_;
|
||||||
|
LuaStack LS_;
|
||||||
|
int next_id_;
|
||||||
|
bool indent_;
|
||||||
|
std::ostream *output_;
|
||||||
|
eng::map<int, int> chpos_to_tabnum_;
|
||||||
|
|
||||||
void atomic_print(LuaStack &LS, LuaSlot val, bool quote, std::ostream *os) {
|
void atomic_print(int xtype, LuaSlot val, bool quote) {
|
||||||
int tt = LS.type(val);
|
switch (xtype) {
|
||||||
switch (tt) {
|
case LUA_TNIL: {
|
||||||
case LUA_TNIL:
|
(*output_) << "nil";
|
||||||
(*os) << "nil";
|
return;
|
||||||
return;
|
}
|
||||||
case LUA_TSTRING:
|
case LUA_TSTRING: {
|
||||||
if (quote) {
|
if (quote) {
|
||||||
util::quote_string(LS.ckstring(val), os);
|
util::quote_string(LS_.ckstring(val), output_);
|
||||||
} 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;
|
|
||||||
} else {
|
} else {
|
||||||
(*os) << value;
|
// TODO: this could be more efficient.
|
||||||
}
|
(*output_) << LS_.ckstring(val);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
case LUA_TNUMBER: {
|
||||||
}
|
double value = LS_.cknumber(val);
|
||||||
case LUA_TBOOLEAN:
|
if (std::isnan(value)) {
|
||||||
(*os) << (LS.ckboolean(val) ? "true" : "false");
|
(*output_) << "nan";
|
||||||
return;
|
} else {
|
||||||
case LUA_TFUNCTION: {
|
int64_t ivalue = int64_t(value);
|
||||||
(*os) << "<function>";
|
if (double(ivalue) == value) {
|
||||||
return;
|
(*output_) << ivalue;
|
||||||
}
|
} else {
|
||||||
case LUA_TLIGHTUSERDATA: {
|
(*output_) << value;
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LS.getmetatable(val, tab);
|
return;
|
||||||
if (LS.istable(val)) {
|
}
|
||||||
lua_pushvalue(L, val.index());
|
case LUA_TBOOLEAN: {
|
||||||
|
(*output_) << (LS_.ckboolean(val) ? "true" : "false");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TFUNCTION: {
|
||||||
|
(*output_) << "<function>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TTHREAD: {
|
||||||
|
(*output_) << "<thread>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TLIGHTUSERDATA: {
|
||||||
|
LuaToken token = LS_.cktoken(val);
|
||||||
|
(*output_) << "[" << token.str() << "]";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_GENERAL: {
|
||||||
|
(*output_) << "<table>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_TANGIBLE: {
|
||||||
|
(*output_) << "<tangible " << LS_.tanid(val) << ">";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_CLASS: {
|
||||||
|
(*output_) << "<class " << LS_.classname(val) << ">";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_GLOBALENV: {
|
||||||
|
(*output_) << "<global-env>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
(*output_) << "<unknown type #" << xtype << ">";
|
||||||
|
return;
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tabify(int level) {
|
||||||
|
if (indent_) {
|
||||||
|
(*output_) << std::endl;
|
||||||
|
for (int i = 0; i < level; i++) {
|
||||||
|
(*output_) << " ";
|
||||||
}
|
}
|
||||||
} else {
|
} 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_) << "<table " << tabnum << ">";
|
||||||
|
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_) << "<meta> = ";
|
||||||
|
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 <table XX> 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) << "<table " << iter->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();
|
LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaDefine(table_findtables, "root", "recursively find tables (debugging only)") {
|
void atomic_print(LuaStack &LS, LuaSlot val, bool quote, std::ostream *os) {
|
||||||
LuaArg root;
|
PrintMachine pm(LS, val, quote, os);
|
||||||
LuaRet tabcount;
|
}
|
||||||
LuaStack LS(L, root, tabcount);
|
|
||||||
findtables(LS, root, tabcount);
|
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();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Inspector {
|
LuaDefine(string_pprintx, "options",
|
||||||
lua_State *L;
|
"|Pretty-print the specified object into a string, with options"
|
||||||
LuaVar ids;
|
"|"
|
||||||
int nextid;
|
"|Options is a table with these fields:"
|
||||||
bool indent;
|
"|"
|
||||||
int maxlen;
|
"| value - the object to pretty-print"
|
||||||
bool anyindent;
|
"| indent - if false, suppress newlines and indentation (default: true)"
|
||||||
std::ostream *stream;
|
"| level - base level of indentation (default: zero)"
|
||||||
};
|
"| expand - if true, force expansion of top-level table (default: false)"
|
||||||
|
"|"
|
||||||
static void tabify(Inspector &insp, int level) {
|
"|About the expand flag: normally, when you print a class, it just "
|
||||||
if (insp.indent) {
|
"|prints '<class name>', and when you print a tangible, it just"
|
||||||
(*insp.stream) << std::endl;
|
"|prints '<tangible id>'. But sometimes, you want to see the details."
|
||||||
for (int i = 0; i < level; i++) {
|
"|The expand flag forces it to expand the top-level table, even if the"
|
||||||
(*insp.stream) << " ";
|
"|top-level table is a tangible or class."
|
||||||
}
|
"|") {
|
||||||
insp.anyindent = true;
|
LuaArg loptions;
|
||||||
} else {
|
LuaRet result;
|
||||||
(*insp.stream) << " ";
|
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) {
|
LuaDefine(string_print, "obj",
|
||||||
lua_checkstack(insp.L, 20);
|
"|Concise print the specified object into a string"
|
||||||
LuaVar idv, pairs, key, val, nextseq;
|
"|"
|
||||||
LuaStack LS(insp.L, idv, pairs, key, val, nextseq);
|
"|This prints a concise representation of obj into a string. Tables"
|
||||||
|
"|are not expanded: for that, use string.pprintx. The functions"
|
||||||
// If it's anything but a table, use 'atomic_print'.
|
"|tostring and string.print are identical."
|
||||||
if (!LS.istable(root)) {
|
"|") {
|
||||||
atomic_print(LS, root, true, insp.stream);
|
LuaArg val;
|
||||||
LS.result();
|
LuaRet result;
|
||||||
return;
|
LuaStack LS(L, val, result);
|
||||||
}
|
eng::ostringstream oss;
|
||||||
|
atomic_print(LS, val, false, &oss);
|
||||||
// Determine the table's ID, allocating an ID if necessary.
|
LS.set(result, oss.str());
|
||||||
LS.rawget(idv, insp.ids, root);
|
return LS.result();
|
||||||
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) << "<class " << cname << ">";
|
|
||||||
} else {
|
|
||||||
int64_t tid = LS.tanid(root);
|
|
||||||
if (tid > 0) {
|
|
||||||
is_tangible = true;
|
|
||||||
(*insp.stream) << "<tangible " << tid << ">";
|
|
||||||
} else if (id > 0) {
|
|
||||||
(*insp.stream) << "<table " << id << ">";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) << "<meta> = ";
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pprint(LuaStack &LS0, LuaSlot root, bool indent, std::ostream *os) {
|
|
||||||
Inspector insp;
|
LuaDefine(tostring, "obj",
|
||||||
LuaStack LS(LS0.state(), insp.ids);
|
"|Concise print the specified object into a string"
|
||||||
findtables(LS, root, insp.ids);
|
"|"
|
||||||
insp.L = LS0.state();
|
"|This prints a concise representation of obj into a string. Tables"
|
||||||
insp.nextid = 1;
|
"|are not expanded: for that, use string.pprint. The functions"
|
||||||
insp.indent = indent;
|
"|tostring and string.print are identical."
|
||||||
insp.anyindent = false;
|
"|") {
|
||||||
insp.stream = os;
|
LuaArg val;
|
||||||
pprint_r(insp, 0, root);
|
LuaRet result;
|
||||||
LS.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") {
|
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();
|
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();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -22,6 +22,14 @@
|
|||||||
#include "luastack.hpp"
|
#include "luastack.hpp"
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
|
struct PrettyPrintOptions {
|
||||||
|
bool indent;
|
||||||
|
int level;
|
||||||
|
bool expand;
|
||||||
|
PrettyPrintOptions() : indent(true), level(0), expand(true) {}
|
||||||
|
void parse(LuaKeywordParser &kp);
|
||||||
|
};
|
||||||
|
|
||||||
// Atomic print to a stream.
|
// Atomic print to a stream.
|
||||||
//
|
//
|
||||||
// This prints an atomic value to a stream. If you give it a table,
|
// 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.
|
// 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
|
#endif // PPRINT_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);
|
bool table_getpairs(LuaStack &LS0, LuaSlot tab, LuaSlot pairs, bool sort);
|
||||||
|
|
||||||
|
|
||||||
#endif // TABLE_HPP
|
#endif // TABLE_HPP
|
||||||
|
|||||||
@@ -350,7 +350,32 @@ inline eng::string ss(const ARGS & ... args) {
|
|||||||
return oss.str();
|
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<char_type, traits_type, allocator_type> {
|
||||||
|
public:
|
||||||
|
char *eback() const { return std::streambuf::eback(); }
|
||||||
|
char *pptr() const { return std::streambuf::pptr(); }
|
||||||
|
};
|
||||||
|
rstringbuf rstringbuf_;
|
||||||
|
public:
|
||||||
|
ostringstream() {
|
||||||
|
std::basic_ostream<char>::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
|
// dprintf
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -711,21 +711,61 @@ LuaDefine(math_randomstate, "seed",
|
|||||||
|
|
||||||
LuaSandboxBuiltin(math_randomseed, "", "");
|
LuaSandboxBuiltin(math_randomseed, "", "");
|
||||||
|
|
||||||
|
LuaDefine(pprint, "obj1, obj2, ...",
|
||||||
LuaDefine(pprint, "obj1,obj2,...",
|
"|Pretty-print the specified objects."
|
||||||
"|Pretty-print object or 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);
|
World *w = World::fetch_global_pointer(L);
|
||||||
std::ostream *ostream = w->lthread_print_stream();
|
std::ostream *ostream = w->lthread_print_stream();
|
||||||
|
int n = lua_gettop(L);
|
||||||
LuaStack LS(L);
|
LuaStack LS(L);
|
||||||
for (int i = 1; i <= lua_gettop(L); i++) {
|
for (int i = 1; i <= n; i++) {
|
||||||
LuaSpecial root(i);
|
LuaSpecial root(i);
|
||||||
pprint(LS, root, true, ostream);
|
pprint(LS, root, PrettyPrintOptions(), ostream);
|
||||||
(*ostream) << std::endl;
|
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 '<class name>', and when you print a tangible, it just"
|
||||||
|
"|prints '<tangible id>'. 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();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaDefine(print, "obj1,obj2,...",
|
LuaDefine(print, "obj1, obj2, ...",
|
||||||
"|Print object or objects.") {
|
"|Print object or objects.") {
|
||||||
World *w = World::fetch_global_pointer(L);
|
World *w = World::fetch_global_pointer(L);
|
||||||
std::ostream *ostream = w->lthread_print_stream();
|
std::ostream *ostream = w->lthread_print_stream();
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ eng::string World::probe_lua(int64_t actor_id, const eng::string &lua) {
|
|||||||
if (msg.empty()) {
|
if (msg.empty()) {
|
||||||
for (int i = top + 1; i <= lua_gettop(L); i++) {
|
for (int i = top + 1; i <= lua_gettop(L); i++) {
|
||||||
LuaSpecial root(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.
|
// TODO: this endl is unnecessary if we just printed a newline.
|
||||||
(*ostream) << std::endl;
|
(*ostream) << std::endl;
|
||||||
}
|
}
|
||||||
@@ -876,7 +876,7 @@ void World::run_scheduled_threads() {
|
|||||||
LuaStack LSCO(CO);
|
LuaStack LSCO(CO);
|
||||||
if (LS.ckboolean(print)) {
|
if (LS.ckboolean(print)) {
|
||||||
for (int i = 1; i <= lua_gettop(CO); i++) {
|
for (int i = 1; i <= lua_gettop(CO); i++) {
|
||||||
pprint(LSCO, LuaSpecial(i), true, ostream);
|
pprint(LSCO, LuaSpecial(i), PrettyPrintOptions(), ostream);
|
||||||
(*ostream) << std::endl;
|
(*ostream) << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ eng::string World::tangible_pprint(int64_t id) const {
|
|||||||
if (LS.istable(tan)) {
|
if (LS.istable(tan)) {
|
||||||
LS.getmetatable(meta, tan);
|
LS.getmetatable(meta, tan);
|
||||||
LS.clearmetatable(tan);
|
LS.clearmetatable(tan);
|
||||||
pprint(LS, tan, false, &oss);
|
PrettyPrintOptions opts;
|
||||||
|
opts.indent = false;
|
||||||
|
pprint(LS, tan, opts, &oss);
|
||||||
LS.setmetatable(tan, meta);
|
LS.setmetatable(tan, meta);
|
||||||
} else {
|
} else {
|
||||||
oss << "<no such tangible: " << id << ">";
|
oss << "<no such tangible: " << id << ">";
|
||||||
|
|||||||
@@ -158,7 +158,8 @@ class Driver {
|
|||||||
drvutil::ostringstream oss;
|
drvutil::ostringstream oss;
|
||||||
std::string err = drvutil::package_lua_source(".", &oss);
|
std::string err = drvutil::package_lua_source(".", &oss);
|
||||||
if_error_print_and_exit(err);
|
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);
|
if_error_print_and_exit(srcpakerr);
|
||||||
|
|
||||||
// Initialize the engine.
|
// 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);
|
if_error_print_and_exit(engw.error);
|
||||||
|
|
||||||
// Set up listening ports.
|
// Set up listening ports.
|
||||||
|
|||||||
@@ -68,23 +68,27 @@ double get_monotonic_clock();
|
|||||||
// without copying, use oss.size() and oss.c_str()
|
// without copying, use oss.size() and oss.c_str()
|
||||||
//
|
//
|
||||||
class ostringstream : public std::ostringstream {
|
class ostringstream : public std::ostringstream {
|
||||||
class rstringbuf : public std::stringbuf {
|
class rstringbuf : public std::basic_stringbuf<char_type, traits_type, allocator_type> {
|
||||||
public:
|
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:
|
public:
|
||||||
ostringstream() {
|
ostringstream() {
|
||||||
std::basic_ostream<char>::rdbuf(&rsbuf_);
|
std::basic_ostream<char>::rdbuf(&rstringbuf_);
|
||||||
}
|
}
|
||||||
size_t size() {
|
std::string_view view() const {
|
||||||
return tellp();
|
char *p = rstringbuf_.eback();
|
||||||
|
size_t size = rstringbuf_.pptr() - p;
|
||||||
|
return std::string_view(p, size);
|
||||||
}
|
}
|
||||||
const char *c_str() {
|
std::string str() const {
|
||||||
return rsbuf_.eback();
|
return rstringbuf_.str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Remove items from a vector that are marked for deletion.
|
// Remove items from a vector that are marked for deletion.
|
||||||
//
|
//
|
||||||
template<class T>
|
template<class T>
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ class basic_ostringstream : public std::basic_ostringstream<C, T, eng::allocator
|
|||||||
using underlying = std::basic_ostringstream<C, T, eng::allocator<C>>;
|
using underlying = std::basic_ostringstream<C, T, eng::allocator<C>>;
|
||||||
using underlying::basic_ostringstream;
|
using underlying::basic_ostringstream;
|
||||||
};
|
};
|
||||||
|
//template<class C, class T=std::char_traits<C>>
|
||||||
|
//using basic_stringbuf = std::basic_stringbuf<C, T, eng::allocator<C>>;
|
||||||
using ostringstream = basic_ostringstream<char>;
|
using ostringstream = basic_ostringstream<char>;
|
||||||
|
//using stringbuf = basic_stringbuf<char>;
|
||||||
} // namespace eng
|
} // namespace eng
|
||||||
|
|
||||||
#endif // WRAP_SSTREAM_HPP
|
#endif // WRAP_SSTREAM_HPP
|
||||||
|
|||||||
13
luprex/lua/ut-globaldb.lua
Normal file
13
luprex/lua/ut-globaldb.lua
Normal file
@@ -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
|
||||||
|
|
||||||
Reference in New Issue
Block a user