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 {
|
eng::string LuaCoreStack::classname(LuaSlot input) const {
|
||||||
LuaVar lookup, classtab, classname;
|
LuaVar lookup, classtab, classname, metatable;
|
||||||
LuaExtStack LS(L_, lookup, classtab, classname);
|
LuaExtStack LS(L_, lookup, classtab, classname, metatable);
|
||||||
|
|
||||||
int xt = xtype(input);
|
int xt = xtype(input);
|
||||||
if (xt == LUA_TSTRING) {
|
if (xt == LUA_TSTRING) {
|
||||||
@@ -377,6 +377,14 @@ eng::string LuaCoreStack::classname(LuaSlot input) const {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return LS.ckstring(classname);
|
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 {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,12 @@ public:
|
|||||||
int next_id_;
|
int next_id_;
|
||||||
bool indent_;
|
bool indent_;
|
||||||
std::ostream *output_;
|
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) {
|
void atomic_print(int xtype, LuaSlot val, bool quote) {
|
||||||
switch (xtype) {
|
switch (xtype) {
|
||||||
@@ -62,11 +67,21 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case LUA_TT_GENERAL: {
|
case LUA_TT_GENERAL: {
|
||||||
(*output_) << "<table>";
|
eng::string classname = LS_.classname(val);
|
||||||
|
if (classname.empty()) {
|
||||||
|
(*output_) << "<table>";
|
||||||
|
} else {
|
||||||
|
(*output_) << "<" << classname << ">";
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case LUA_TT_TANGIBLE: {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
case LUA_TT_CLASS: {
|
case LUA_TT_CLASS: {
|
||||||
@@ -101,75 +116,114 @@ public:
|
|||||||
void pprint_r(int level, bool expand, LuaSlot value) {
|
void pprint_r(int level, bool expand, LuaSlot value) {
|
||||||
lua_State *L = LS_.state();
|
lua_State *L = LS_.state();
|
||||||
lua_checkstack(L, 20);
|
lua_checkstack(L, 20);
|
||||||
LuaVar loffset, pairs, key, val, lchpos, nextseq;
|
LuaVar loffset, pairs, key, val, lchpos;
|
||||||
LuaExtStack LS(L, loffset, pairs, key, val, lchpos, nextseq);
|
LuaExtStack LS(L, loffset, pairs, key, val, lchpos);
|
||||||
|
|
||||||
// Determine the extended type of the object. If the
|
// Determine the extended type of the object. If the
|
||||||
// expand flag is true, try to coerce it to a general table.
|
// expand flag is true, try to coerce it to a general table.
|
||||||
int xtype = LS.xtype(value);
|
int xtype = LS.xtype(value);
|
||||||
|
eng::string classname = LS.classname(value);
|
||||||
|
|
||||||
// Print the atomic portion.
|
// Print the atomic portion or table header.
|
||||||
if (xtype != LUA_TT_GENERAL) {
|
// Make a decision about whether to print key-value pairs,
|
||||||
|
// if not, then return.
|
||||||
|
if (xtype < LUA_TT_GENERAL) {
|
||||||
atomic_print(xtype, value, true);
|
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;
|
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.
|
// How many keys in the table?
|
||||||
bool needcomma = false;
|
int nkeys = LS.nkeys(value);
|
||||||
bool multiline = false;
|
|
||||||
LS.set(nextseq, 1);
|
|
||||||
|
|
||||||
// Open the brackets.
|
// Decide whether we're going to print a line for the metatable.
|
||||||
(*output_) << "{";
|
// 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.
|
// If it's an array of atomic values, without a visible metatable,
|
||||||
table_getpairs(LS, value, pairs, true);
|
// we're going to print it without newlines. Scan the table to see if
|
||||||
for (int i = 2; ; i+=2) {
|
// it's possible to print it array-style.
|
||||||
LS.rawget(key, pairs, i);
|
bool array_style = false;
|
||||||
if (LS.isnil(key)) break;
|
if (!print_meta) {
|
||||||
LS.rawget(val, pairs, i+1);
|
array_style = true;
|
||||||
if (needcomma) (*output_) << ",";
|
for (int i = 1; i <= nkeys; i++) {
|
||||||
needcomma = true;
|
LS.rawget(val, value, i);
|
||||||
if (LS.rawequal(key, nextseq)) {
|
if (LS.isnil(val) || (LS.type(val) == LUA_TTABLE)) {
|
||||||
(*output_) << " ";
|
array_style = false;
|
||||||
pprint_r(level + 1, false, val);
|
break;
|
||||||
LS.set(nextseq, LS.ckinteger(nextseq) + 1);
|
}
|
||||||
} else {
|
}
|
||||||
multiline = true;
|
}
|
||||||
|
|
||||||
|
// 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);
|
tabify(level + 1);
|
||||||
if (LS.isstring(key) && sv::is_lua_id(LS.ckstring(key))) {
|
if (LS.isstring(key) && sv::is_lua_id(LS.ckstring(key))) {
|
||||||
(*output_) << LS.ckstring(key);
|
(*output_) << LS.ckstring(key);
|
||||||
@@ -187,26 +241,16 @@ public:
|
|||||||
}
|
}
|
||||||
pprint_r(level + 1, false, val);
|
pprint_r(level + 1, false, val);
|
||||||
}
|
}
|
||||||
}
|
if (print_meta) {
|
||||||
|
LS.getmetatable(val, value);
|
||||||
// Output the metatable.
|
if (needcomma) (*output_) << ",";
|
||||||
LS.getmetatable(val, value);
|
tabify(level + 1);
|
||||||
if (LS.istable(val) && (LS.gettabletype(val) != LUA_TT_TANGIBLEMETA)) {
|
(*output_) << "<meta> = ";
|
||||||
multiline = true;
|
pprint_r(level + 1, false, val);
|
||||||
if (needcomma) (*output_) << ",";
|
}
|
||||||
needcomma = true;
|
|
||||||
tabify(level + 1);
|
|
||||||
(*output_) << "<meta> = ";
|
|
||||||
pprint_r(level + 1, false, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the brackets.
|
|
||||||
if (multiline) {
|
|
||||||
tabify(level);
|
tabify(level);
|
||||||
} else if (LS.ckinteger(nextseq) > 1) {
|
(*output_) << "}";
|
||||||
(*output_) << " ";
|
|
||||||
}
|
}
|
||||||
(*output_) << "}";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Atomic print interface.
|
// Atomic print interface.
|
||||||
@@ -229,12 +273,13 @@ public:
|
|||||||
|
|
||||||
// Output the results. We would just copy the characters
|
// Output the results. We would just copy the characters
|
||||||
// one by one to the target stream, except that we have to
|
// one by one to the target stream, except that we have to
|
||||||
// insert <table XX> in front of all tables that got referenced.
|
// insert table headers wherever the chpos_to_header map says
|
||||||
chpos_to_tabnum_.emplace(0x7FFFFFFF, 0);
|
// so.
|
||||||
auto iter = chpos_to_tabnum_.begin();
|
chpos_to_header_.emplace(0x7FFFFFFF, "");
|
||||||
|
auto iter = chpos_to_header_.begin();
|
||||||
for (int i = 0; i < int(pre.size()); i++) {
|
for (int i = 0; i < int(pre.size()); i++) {
|
||||||
if (i == iter->first) {
|
if (i == iter->first) {
|
||||||
(*os) << "<table " << iter->second << ">";
|
(*os) << iter->second;
|
||||||
iter++;
|
iter++;
|
||||||
}
|
}
|
||||||
(*os).put(pre[i]);
|
(*os).put(pre[i]);
|
||||||
|
|||||||
Reference in New Issue
Block a user