Improvements to pretty-printer, including code to support printing classnames of tables
This commit is contained in:
@@ -345,8 +345,8 @@ lua_State *LuaCoreStack::newthread(LuaSlot target) const {
|
||||
}
|
||||
|
||||
eng::string LuaCoreStack::classname(LuaSlot input) const {
|
||||
LuaVar lookup, classtab, classname;
|
||||
LuaExtStack LS(L_, lookup, classtab, classname);
|
||||
LuaVar lookup, classtab, classname, metatable;
|
||||
LuaExtStack LS(L_, lookup, classtab, classname, metatable);
|
||||
|
||||
int xt = xtype(input);
|
||||
if (xt == LUA_TSTRING) {
|
||||
@@ -377,6 +377,14 @@ eng::string LuaCoreStack::classname(LuaSlot input) const {
|
||||
return "";
|
||||
}
|
||||
return LS.ckstring(classname);
|
||||
} else if (xt == LUA_TT_GENERAL) {
|
||||
LS.getmetatable(metatable, input);
|
||||
LS.rawget(lookup, LuaRegistry, "classnames");
|
||||
LS.rawget(classname, lookup, metatable);
|
||||
if (!LS.isstring(classname)) {
|
||||
return "";
|
||||
}
|
||||
return LS.ckstring(classname);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -13,7 +13,12 @@ public:
|
||||
int next_id_;
|
||||
bool indent_;
|
||||
std::ostream *output_;
|
||||
eng::map<int, int> chpos_to_tabnum_;
|
||||
|
||||
// When a general table appears in the output, we use the character
|
||||
// position where the table first appeared as a unique ID for the
|
||||
// general table.
|
||||
eng::map<int, int> chpos_to_table_number_;
|
||||
eng::map<int, eng::string> chpos_to_header_;
|
||||
|
||||
void atomic_print(int xtype, LuaSlot val, bool quote) {
|
||||
switch (xtype) {
|
||||
@@ -62,11 +67,21 @@ public:
|
||||
return;
|
||||
}
|
||||
case LUA_TT_GENERAL: {
|
||||
(*output_) << "<table>";
|
||||
eng::string classname = LS_.classname(val);
|
||||
if (classname.empty()) {
|
||||
(*output_) << "<table>";
|
||||
} else {
|
||||
(*output_) << "<" << classname << ">";
|
||||
}
|
||||
return;
|
||||
}
|
||||
case LUA_TT_TANGIBLE: {
|
||||
(*output_) << "<tangible " << LS_.tanid(val) << ">";
|
||||
eng::string classname = LS_.classname(val);
|
||||
if (classname.empty()) {
|
||||
(*output_) << "<tan " << LS_.tanid(val) << ">";
|
||||
} else {
|
||||
(*output_) << "<tan " << classname << " " << LS_.tanid(val) << ">";
|
||||
}
|
||||
return;
|
||||
}
|
||||
case LUA_TT_CLASS: {
|
||||
@@ -101,75 +116,114 @@ public:
|
||||
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;
|
||||
LuaExtStack LS(L, loffset, pairs, key, val, lchpos, nextseq);
|
||||
LuaVar loffset, pairs, key, val, lchpos;
|
||||
LuaExtStack LS(L, loffset, pairs, key, val, lchpos);
|
||||
|
||||
// Determine the extended type of the object. If the
|
||||
// expand flag is true, try to coerce it to a general table.
|
||||
int xtype = LS.xtype(value);
|
||||
eng::string classname = LS.classname(value);
|
||||
|
||||
// Print the atomic portion.
|
||||
if (xtype != LUA_TT_GENERAL) {
|
||||
// Print the atomic portion or table header.
|
||||
// Make a decision about whether to print key-value pairs,
|
||||
// if not, then return.
|
||||
if (xtype < LUA_TT_GENERAL) {
|
||||
atomic_print(xtype, value, true);
|
||||
if ((xtype < LUA_TT_GENERAL) || (!expand)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Find out whether it has a table number. If necessary,
|
||||
// assign one.
|
||||
int tabnum = 0;
|
||||
LS.rawget(lchpos, tabchpos_, value);
|
||||
auto chpos = LS.tryint(lchpos);
|
||||
if (!chpos) {
|
||||
// First time. Record the character position where the
|
||||
// table first appears in the output stream.
|
||||
LS.rawset(tabchpos_, value, int((*output_).tellp()));
|
||||
} else {
|
||||
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;
|
||||
} else if (xtype > LUA_TT_GENERAL) {
|
||||
atomic_print(xtype, value, true);
|
||||
if (!expand) return;
|
||||
} else {
|
||||
LS.rawget(lchpos, tabchpos_, value);
|
||||
int chpos = LS.tryint(lchpos).value_or(-1);
|
||||
if (chpos < 0) {
|
||||
// First time.
|
||||
// * The character position where the table first appears
|
||||
// serves as a unique ID for the table. Record it in the tabchpos
|
||||
// map. Then, generate an initial tentative header for the table,
|
||||
// and insert it into the header map. Do not output the header to
|
||||
// output stream: it will be post-injected into the output stream.
|
||||
chpos = int((*output_).tellp());
|
||||
LS.rawset(tabchpos_, value, chpos);
|
||||
if (!classname.empty()) {
|
||||
chpos_to_header_[chpos] = util::ss("<", classname, ">");
|
||||
}
|
||||
} else {
|
||||
// Second time.
|
||||
// See if the table already has a table number assigned. If not,
|
||||
// assign it one. Update the header for the table.
|
||||
// Do output the header to the output stream, it is only
|
||||
// post-injected at the first table appearance.
|
||||
int tabnum = chpos_to_table_number_[chpos];
|
||||
if (tabnum == 0) {
|
||||
tabnum = next_id_++;
|
||||
chpos_to_table_number_[chpos] = tabnum;
|
||||
if (classname.empty()) {
|
||||
chpos_to_header_[chpos] = util::ss("<table ", tabnum, ">");
|
||||
} else {
|
||||
chpos_to_header_[chpos] = util::ss("<", classname, " ", tabnum, ">");
|
||||
}
|
||||
}
|
||||
(*output_) << chpos_to_header_[chpos];
|
||||
return; // Do not output key-value pairs the second time.
|
||||
}
|
||||
}
|
||||
|
||||
// State variables.
|
||||
bool needcomma = false;
|
||||
bool multiline = false;
|
||||
LS.set(nextseq, 1);
|
||||
// How many keys in the table?
|
||||
int nkeys = LS.nkeys(value);
|
||||
|
||||
// Open the brackets.
|
||||
(*output_) << "{";
|
||||
// Decide whether we're going to print a line for the metatable.
|
||||
// If the table has a classname, we don't need, to, because the
|
||||
// classname (which is in the header) tell the reader what metatable
|
||||
// is being used. Also, tangible metatables are not shown, because
|
||||
// tangibles keep secret stuff in the metatable. (this may change).
|
||||
bool print_meta = false;
|
||||
if (classname.empty()) {
|
||||
LS.getmetatable(val, value);
|
||||
if (LS.istable(val) && (LS.gettabletype(val) != LUA_TT_TANGIBLEMETA)) {
|
||||
print_meta = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
// If it's an array of atomic values, without a visible metatable,
|
||||
// we're going to print it without newlines. Scan the table to see if
|
||||
// it's possible to print it array-style.
|
||||
bool array_style = false;
|
||||
if (!print_meta) {
|
||||
array_style = true;
|
||||
for (int i = 1; i <= nkeys; i++) {
|
||||
LS.rawget(val, value, i);
|
||||
if (LS.isnil(val) || (LS.type(val) == LUA_TTABLE)) {
|
||||
array_style = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print it array-style. This code is simple because
|
||||
// we don't do indentation, and we don't handle any values
|
||||
// that aren't atomic.
|
||||
if (array_style) {
|
||||
(*output_) << "{";
|
||||
for (int i = 1; i <= nkeys; i++) {
|
||||
if (i > 1) (*output_) << ", ";
|
||||
LS.rawget(val, value, i);
|
||||
atomic_print(LS.xtype(val), val, true);
|
||||
}
|
||||
(*output_) << "}";
|
||||
}
|
||||
|
||||
// Print it table-style.
|
||||
if (!array_style) {
|
||||
table_getpairs(LS, value, pairs, true);
|
||||
(*output_) << "{";
|
||||
bool needcomma = false;
|
||||
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;
|
||||
tabify(level + 1);
|
||||
if (LS.isstring(key) && sv::is_lua_id(LS.ckstring(key))) {
|
||||
(*output_) << LS.ckstring(key);
|
||||
@@ -187,26 +241,16 @@ public:
|
||||
}
|
||||
pprint_r(level + 1, false, val);
|
||||
}
|
||||
}
|
||||
|
||||
// Output the metatable.
|
||||
LS.getmetatable(val, value);
|
||||
if (LS.istable(val) && (LS.gettabletype(val) != LUA_TT_TANGIBLEMETA)) {
|
||||
multiline = true;
|
||||
if (needcomma) (*output_) << ",";
|
||||
needcomma = true;
|
||||
tabify(level + 1);
|
||||
(*output_) << "<meta> = ";
|
||||
pprint_r(level + 1, false, val);
|
||||
}
|
||||
|
||||
// Close the brackets.
|
||||
if (multiline) {
|
||||
if (print_meta) {
|
||||
LS.getmetatable(val, value);
|
||||
if (needcomma) (*output_) << ",";
|
||||
tabify(level + 1);
|
||||
(*output_) << "<meta> = ";
|
||||
pprint_r(level + 1, false, val);
|
||||
}
|
||||
tabify(level);
|
||||
} else if (LS.ckinteger(nextseq) > 1) {
|
||||
(*output_) << " ";
|
||||
(*output_) << "}";
|
||||
}
|
||||
(*output_) << "}";
|
||||
}
|
||||
|
||||
// Atomic print interface.
|
||||
@@ -229,12 +273,13 @@ public:
|
||||
|
||||
// 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();
|
||||
// insert table headers wherever the chpos_to_header map says
|
||||
// so.
|
||||
chpos_to_header_.emplace(0x7FFFFFFF, "");
|
||||
auto iter = chpos_to_header_.begin();
|
||||
for (int i = 0; i < int(pre.size()); i++) {
|
||||
if (i == iter->first) {
|
||||
(*os) << "<table " << iter->second << ">";
|
||||
(*os) << iter->second;
|
||||
iter++;
|
||||
}
|
||||
(*os).put(pre[i]);
|
||||
|
||||
Reference in New Issue
Block a user