mirror of
https://github.com/open-goal/jak-project
synced 2026-07-09 23:01:56 -04:00
Replace Form with goos::Object (#64)
* add pretty printing to goos form
* temp
* fix format
* fix format
* fix windows build
* again
* windows
* Revert "windows"
This reverts commit b7d448e733.
* windows again
* give up and just add a getter function
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
add_library(goos SHARED Object.cpp TextDB.cpp Reader.cpp Interpreter.cpp InterpreterEval.cpp)
|
||||
add_library(goos SHARED Object.cpp TextDB.cpp Reader.cpp Interpreter.cpp InterpreterEval.cpp PrettyPrinter.cpp)
|
||||
target_link_libraries(goos common_util fmt)
|
||||
@@ -4,6 +4,9 @@
|
||||
namespace goos {
|
||||
|
||||
std::shared_ptr<EmptyListObject> gEmptyList = nullptr;
|
||||
std::shared_ptr<EmptyListObject>& get_empty_list() {
|
||||
return gEmptyList;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert type to string (name in brackets)
|
||||
|
||||
@@ -319,7 +319,7 @@ class Object {
|
||||
|
||||
// There is a single heap allocated EmptyListObject.
|
||||
class EmptyListObject;
|
||||
extern std::shared_ptr<EmptyListObject> gEmptyList;
|
||||
std::shared_ptr<EmptyListObject>& get_empty_list();
|
||||
|
||||
class EmptyListObject : public HeapObject {
|
||||
public:
|
||||
@@ -327,10 +327,10 @@ class EmptyListObject : public HeapObject {
|
||||
static Object make_new() {
|
||||
Object obj;
|
||||
obj.type = ObjectType::EMPTY_LIST;
|
||||
if (!gEmptyList) {
|
||||
gEmptyList = std::make_shared<EmptyListObject>();
|
||||
if (!get_empty_list()) {
|
||||
get_empty_list() = std::make_shared<EmptyListObject>();
|
||||
}
|
||||
obj.heap_obj = gEmptyList;
|
||||
obj.heap_obj = get_empty_list();
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,567 @@
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include "PrettyPrinter.h"
|
||||
#include "Reader.h"
|
||||
|
||||
namespace pretty_print {
|
||||
|
||||
/*!
|
||||
* A single token which cannot be split between lines.
|
||||
*/
|
||||
struct FormToken {
|
||||
enum class TokenKind {
|
||||
WHITESPACE,
|
||||
STRING,
|
||||
OPEN_PAREN,
|
||||
DOT,
|
||||
CLOSE_PAREN,
|
||||
EMPTY_PAIR,
|
||||
SPECIAL_STRING // has different alignment rules than STRING
|
||||
} kind;
|
||||
explicit FormToken(TokenKind _kind, std::string _str = "") : kind(_kind), str(std::move(_str)) {}
|
||||
|
||||
std::string str;
|
||||
|
||||
std::string toString() const {
|
||||
std::string s;
|
||||
switch (kind) {
|
||||
case TokenKind::WHITESPACE:
|
||||
s.push_back(' ');
|
||||
break;
|
||||
case TokenKind::STRING:
|
||||
s.append(str);
|
||||
break;
|
||||
case TokenKind::OPEN_PAREN:
|
||||
s.push_back('(');
|
||||
break;
|
||||
case TokenKind::DOT:
|
||||
s.push_back('.');
|
||||
break;
|
||||
case TokenKind::CLOSE_PAREN:
|
||||
s.push_back(')');
|
||||
break;
|
||||
case TokenKind::EMPTY_PAIR:
|
||||
s.append("()");
|
||||
break;
|
||||
case TokenKind::SPECIAL_STRING:
|
||||
s.append(str);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("toString unknown token kind");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Convert a GOOS object to tokens and add it to the list.
|
||||
* This is the main function which recursively builds a list of tokens out of an s-expression.
|
||||
*
|
||||
* Note that not all GOOS objects can be pretty printed. Only the ones that can be directly
|
||||
* generated by the reader.
|
||||
*/
|
||||
void add_to_token_list(const goos::Object& obj, std::vector<FormToken>* tokens) {
|
||||
switch (obj.type) {
|
||||
case goos::ObjectType::EMPTY_LIST:
|
||||
tokens->emplace_back(FormToken::TokenKind::EMPTY_PAIR);
|
||||
break;
|
||||
// all of these can just be printed to a string and turned into a 'symbol'
|
||||
case goos::ObjectType::INTEGER:
|
||||
case goos::ObjectType::FLOAT:
|
||||
case goos::ObjectType::CHAR:
|
||||
case goos::ObjectType::SYMBOL:
|
||||
case goos::ObjectType::STRING:
|
||||
tokens->emplace_back(FormToken::TokenKind::STRING, obj.print());
|
||||
break;
|
||||
|
||||
// it's important to break the pair up into smaller tokens which can then be split
|
||||
// across lines.
|
||||
case goos::ObjectType::PAIR: {
|
||||
tokens->emplace_back(FormToken::TokenKind::OPEN_PAREN);
|
||||
auto* to_print = &obj;
|
||||
for (;;) {
|
||||
if (to_print->is_pair()) {
|
||||
// first print the car into our token list:
|
||||
add_to_token_list(to_print->as_pair()->car, tokens);
|
||||
// then load up the cdr as the next thing to print
|
||||
to_print = &to_print->as_pair()->cdr;
|
||||
if (to_print->is_empty_list()) {
|
||||
// we're done, add a close paren and finish
|
||||
tokens->emplace_back(FormToken::TokenKind::CLOSE_PAREN);
|
||||
return;
|
||||
} else {
|
||||
// more to print, add whitespace
|
||||
tokens->emplace_back(FormToken::TokenKind::WHITESPACE);
|
||||
}
|
||||
} else {
|
||||
// got an improper list.
|
||||
// add a dot, space
|
||||
tokens->emplace_back(FormToken::TokenKind::DOT);
|
||||
tokens->emplace_back(FormToken::TokenKind::WHITESPACE);
|
||||
// then the thing and a close paren.
|
||||
add_to_token_list(*to_print, tokens);
|
||||
tokens->emplace_back(FormToken::TokenKind::CLOSE_PAREN);
|
||||
return; // and we're done with this list.
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
// these are unsupported by the pretty printer.
|
||||
case goos::ObjectType::ARRAY: // todo, we should probably handle arrays.
|
||||
case goos::ObjectType::LAMBDA:
|
||||
case goos::ObjectType::MACRO:
|
||||
case goos::ObjectType::ENVIRONMENT:
|
||||
throw std::runtime_error("tried to pretty print a goos object kind which is not supported.");
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Linked list node representing a token in the output (whitespace, paren, newline, etc)
|
||||
*/
|
||||
struct PrettyPrinterNode {
|
||||
FormToken* tok = nullptr; // if we aren't a newline, we will have a token.
|
||||
int line = -1; // line that token occurs on. undef for newlines
|
||||
int lineIndent = -1; // indent of line. only valid for first token in the line
|
||||
int offset = -1; // offset of beginning of token from left margin
|
||||
int specialIndentDelta = 0;
|
||||
bool is_line_separator = false; // true if line separator (not a token)
|
||||
PrettyPrinterNode *next = nullptr, *prev = nullptr; // linked list
|
||||
PrettyPrinterNode* paren =
|
||||
nullptr; // pointer to open paren if in parens. open paren points to close and vice versa
|
||||
explicit PrettyPrinterNode(FormToken* _tok) { tok = _tok; }
|
||||
PrettyPrinterNode() = default;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Container to track and cleanup all nodes after use.
|
||||
*/
|
||||
struct NodePool {
|
||||
std::vector<PrettyPrinterNode*> nodes;
|
||||
PrettyPrinterNode* allocate(FormToken* x) {
|
||||
auto result = new PrettyPrinterNode(x);
|
||||
nodes.push_back(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
PrettyPrinterNode* allocate() {
|
||||
auto result = new PrettyPrinterNode;
|
||||
nodes.push_back(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
NodePool() = default;
|
||||
|
||||
~NodePool() {
|
||||
for (auto& x : nodes) {
|
||||
delete x;
|
||||
}
|
||||
}
|
||||
|
||||
// so we don't accidentally copy this.
|
||||
NodePool& operator=(const NodePool&) = delete;
|
||||
NodePool(const NodePool&) = delete;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Splice in a line break after the given node, it there isn't one already and if it isn't the last
|
||||
* node.
|
||||
*/
|
||||
void insertNewlineAfter(NodePool& pool, PrettyPrinterNode* node, int specialIndentDelta) {
|
||||
if (node->next && !node->next->is_line_separator) {
|
||||
auto* nl = pool.allocate();
|
||||
auto* next = node->next;
|
||||
node->next = nl;
|
||||
nl->prev = node;
|
||||
nl->next = next;
|
||||
next->prev = nl;
|
||||
nl->is_line_separator = true;
|
||||
nl->specialIndentDelta = specialIndentDelta;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Splice in a line break before the given node, if there isn't one already and if it isn't the
|
||||
* first node.
|
||||
*/
|
||||
void insertNewlineBefore(NodePool& pool, PrettyPrinterNode* node, int specialIndentDelta) {
|
||||
if (node->prev && !node->prev->is_line_separator) {
|
||||
auto* nl = pool.allocate();
|
||||
auto* prev = node->prev;
|
||||
prev->next = nl;
|
||||
nl->prev = prev;
|
||||
nl->next = node;
|
||||
node->prev = nl;
|
||||
nl->is_line_separator = true;
|
||||
nl->specialIndentDelta = specialIndentDelta;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Break a list across multiple lines. This is how line lengths are decreased.
|
||||
* This does not compute the proper indentation and leaves the list in a bad state.
|
||||
* After this has been called, the entire selection should be reformatted with propagate_pretty
|
||||
*/
|
||||
void breakList(NodePool& pool, PrettyPrinterNode* leftParen) {
|
||||
assert(!leftParen->is_line_separator);
|
||||
assert(leftParen->tok->kind == FormToken::TokenKind::OPEN_PAREN);
|
||||
auto* rp = leftParen->paren;
|
||||
assert(rp->tok->kind == FormToken::TokenKind::CLOSE_PAREN);
|
||||
|
||||
for (auto* n = leftParen->next; n && n != rp; n = n->next) {
|
||||
if (!n->is_line_separator) {
|
||||
if (n->tok->kind == FormToken::TokenKind::OPEN_PAREN) {
|
||||
n = n->paren;
|
||||
assert(n->tok->kind == FormToken::TokenKind::CLOSE_PAREN);
|
||||
insertNewlineAfter(pool, n, 0);
|
||||
} else if (n->tok->kind != FormToken::TokenKind::WHITESPACE) {
|
||||
assert(n->tok->kind != FormToken::TokenKind::CLOSE_PAREN);
|
||||
insertNewlineAfter(pool, n, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Compute proper line numbers, offsets, and indents for a list of tokens with newlines
|
||||
* Will add newlines for close parens if needed.
|
||||
*/
|
||||
static PrettyPrinterNode* propagatePretty(NodePool& pool,
|
||||
PrettyPrinterNode* list,
|
||||
int line_length) {
|
||||
// propagate line numbers
|
||||
PrettyPrinterNode* rv = nullptr;
|
||||
int line = list->line;
|
||||
for (auto* n = list; n; n = n->next) {
|
||||
if (n->is_line_separator) {
|
||||
line++;
|
||||
} else {
|
||||
n->line = line;
|
||||
// add the weird newline.
|
||||
if (n->tok->kind == FormToken::TokenKind::CLOSE_PAREN) {
|
||||
if (n->line != n->paren->line) {
|
||||
if (n->prev && !n->prev->is_line_separator) {
|
||||
insertNewlineBefore(pool, n, 0);
|
||||
line++;
|
||||
}
|
||||
if (n->next && !n->next->is_line_separator) {
|
||||
insertNewlineAfter(pool, n, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compute offsets and indents
|
||||
std::vector<int> indentStack;
|
||||
indentStack.push_back(0);
|
||||
int offset = 0;
|
||||
PrettyPrinterNode* line_start = list;
|
||||
bool previous_line_sep = false;
|
||||
for (auto* n = list; n; n = n->next) {
|
||||
if (n->is_line_separator) {
|
||||
previous_line_sep = true;
|
||||
offset = indentStack.back() += n->specialIndentDelta;
|
||||
} else {
|
||||
if (previous_line_sep) {
|
||||
line_start = n;
|
||||
n->lineIndent = offset;
|
||||
previous_line_sep = false;
|
||||
}
|
||||
|
||||
n->offset = offset;
|
||||
offset += n->tok->toString().length();
|
||||
if (offset > line_length && !rv)
|
||||
rv = line_start;
|
||||
if (n->tok->kind == FormToken::TokenKind::OPEN_PAREN) {
|
||||
if (!n->prev || n->prev->is_line_separator) {
|
||||
indentStack.push_back(offset + 1);
|
||||
} else {
|
||||
indentStack.push_back(offset - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (n->tok->kind == FormToken::TokenKind::CLOSE_PAREN) {
|
||||
indentStack.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the token on the start of the next line. nullptr if we're the last line.
|
||||
*/
|
||||
PrettyPrinterNode* getNextLine(PrettyPrinterNode* start) {
|
||||
assert(!start->is_line_separator);
|
||||
int line = start->line;
|
||||
for (;;) {
|
||||
if (start->is_line_separator || start->line == line) {
|
||||
if (start->next)
|
||||
start = start->next;
|
||||
else
|
||||
return nullptr;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the next open paren on the current line (can start in the middle of line, not inclusive of
|
||||
* start) nullptr if there's no open parens on the rest of this line.
|
||||
*/
|
||||
PrettyPrinterNode* getNextListOnLine(PrettyPrinterNode* start) {
|
||||
int line = start->line;
|
||||
assert(!start->is_line_separator);
|
||||
if (!start->next || start->next->is_line_separator)
|
||||
return nullptr;
|
||||
start = start->next;
|
||||
while (!start->is_line_separator && start->line == line) {
|
||||
if (start->tok->kind == FormToken::TokenKind::OPEN_PAREN)
|
||||
return start;
|
||||
if (!start->next)
|
||||
return nullptr;
|
||||
start = start->next;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the first open paren on the current line (can start in the middle of line, inclusive of
|
||||
* start) nullptr if there's no open parens on the rest of this line
|
||||
*/
|
||||
PrettyPrinterNode* getFirstListOnLine(PrettyPrinterNode* start) {
|
||||
int line = start->line;
|
||||
assert(!start->is_line_separator);
|
||||
while (!start->is_line_separator && start->line == line) {
|
||||
if (start->tok->kind == FormToken::TokenKind::OPEN_PAREN)
|
||||
return start;
|
||||
if (!start->next)
|
||||
return nullptr;
|
||||
start = start->next;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the first token on the first line which exceeds the max length
|
||||
*/
|
||||
PrettyPrinterNode* getFirstBadLine(PrettyPrinterNode* start, int line_length) {
|
||||
assert(!start->is_line_separator);
|
||||
int currentLine = start->line;
|
||||
auto* currentLineNode = start;
|
||||
for (;;) {
|
||||
if (start->is_line_separator) {
|
||||
assert(start->next);
|
||||
start = start->next;
|
||||
} else {
|
||||
if (start->line != currentLine) {
|
||||
currentLine = start->line;
|
||||
currentLineNode = start;
|
||||
}
|
||||
if (start->offset > line_length) {
|
||||
return currentLineNode;
|
||||
}
|
||||
if (!start->next) {
|
||||
return nullptr;
|
||||
}
|
||||
start = start->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Break insertion algorithm.
|
||||
*/
|
||||
void insertBreaksAsNeeded(NodePool& pool, PrettyPrinterNode* head, int line_length) {
|
||||
PrettyPrinterNode* last_line_complete = nullptr;
|
||||
PrettyPrinterNode* line_to_start_line_search = head;
|
||||
|
||||
// loop over lines
|
||||
for (;;) {
|
||||
// compute lines as needed
|
||||
propagatePretty(pool, head, line_length);
|
||||
|
||||
// search for a bad line starting at the last line we fixed
|
||||
PrettyPrinterNode* candidate_line = getFirstBadLine(line_to_start_line_search, line_length);
|
||||
// if we got the same line we started on, this means we couldn't fix it.
|
||||
if (candidate_line == last_line_complete) {
|
||||
candidate_line = nullptr; // so we say our candidate was bad and try to find another
|
||||
PrettyPrinterNode* next_line = getNextLine(line_to_start_line_search);
|
||||
if (next_line) {
|
||||
candidate_line = getFirstBadLine(next_line, line_length);
|
||||
}
|
||||
}
|
||||
if (!candidate_line)
|
||||
break;
|
||||
|
||||
// okay, we have a line which needs fixing.
|
||||
assert(!candidate_line->prev || candidate_line->prev->is_line_separator);
|
||||
PrettyPrinterNode* form_to_start = getFirstListOnLine(candidate_line);
|
||||
for (;;) {
|
||||
if (!form_to_start) {
|
||||
// this means we failed to hit the desired line length...
|
||||
break;
|
||||
}
|
||||
breakList(pool, form_to_start);
|
||||
propagatePretty(pool, head, line_length);
|
||||
if (getFirstBadLine(candidate_line, line_length) != candidate_line) {
|
||||
break;
|
||||
}
|
||||
|
||||
form_to_start = getNextListOnLine(form_to_start);
|
||||
if (!form_to_start)
|
||||
break;
|
||||
}
|
||||
|
||||
last_line_complete = candidate_line;
|
||||
line_to_start_line_search = candidate_line;
|
||||
}
|
||||
}
|
||||
|
||||
void insertSpecialBreaks(NodePool& pool, PrettyPrinterNode* node) {
|
||||
for (; node; node = node->next) {
|
||||
if (!node->is_line_separator && node->tok->kind == FormToken::TokenKind::STRING) {
|
||||
std::string& name = node->tok->str;
|
||||
if (name == "deftype") { // todo!
|
||||
auto* parent_type_dec = getNextListOnLine(node);
|
||||
if (parent_type_dec) {
|
||||
insertNewlineAfter(pool, parent_type_dec->paren, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (name == "defun" || name == "defmethod") {
|
||||
auto* parent_type_dec = getNextListOnLine(node);
|
||||
if (parent_type_dec) {
|
||||
insertNewlineAfter(pool, parent_type_dec->paren, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (name.at(0) == '"') {
|
||||
insertNewlineAfter(pool, node, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_string(const goos::Object& obj, int line_length) {
|
||||
NodePool pool;
|
||||
std::vector<FormToken> tokens;
|
||||
add_to_token_list(obj, &tokens);
|
||||
assert(!tokens.empty());
|
||||
std::string pretty;
|
||||
|
||||
// build linked list of nodes
|
||||
PrettyPrinterNode* head = pool.allocate(&tokens[0]);
|
||||
PrettyPrinterNode* node = head;
|
||||
head->line = 0;
|
||||
head->offset = 0;
|
||||
head->lineIndent = 0;
|
||||
int offset = head->tok->toString().length();
|
||||
for (size_t i = 1; i < tokens.size(); i++) {
|
||||
node->next = pool.allocate(&tokens[i]);
|
||||
node->next->prev = node;
|
||||
node = node->next;
|
||||
node->line = 0;
|
||||
node->offset = offset;
|
||||
offset += node->tok->toString().length();
|
||||
node->lineIndent = 0;
|
||||
}
|
||||
|
||||
// attach parens.
|
||||
std::vector<PrettyPrinterNode*> parenStack;
|
||||
parenStack.push_back(nullptr);
|
||||
for (PrettyPrinterNode* n = head; n; n = n->next) {
|
||||
if (n->tok->kind == FormToken::TokenKind::OPEN_PAREN) {
|
||||
parenStack.push_back(n);
|
||||
} else if (n->tok->kind == FormToken::TokenKind::CLOSE_PAREN) {
|
||||
n->paren = parenStack.back();
|
||||
parenStack.back()->paren = n;
|
||||
parenStack.pop_back();
|
||||
} else {
|
||||
n->paren = parenStack.back();
|
||||
}
|
||||
}
|
||||
assert(parenStack.size() == 1);
|
||||
assert(!parenStack.back());
|
||||
|
||||
insertSpecialBreaks(pool, head);
|
||||
propagatePretty(pool, head, line_length);
|
||||
insertBreaksAsNeeded(pool, head, line_length);
|
||||
|
||||
// write to string
|
||||
bool newline_prev = true;
|
||||
for (PrettyPrinterNode* n = head; n; n = n->next) {
|
||||
if (n->is_line_separator) {
|
||||
pretty.push_back('\n');
|
||||
newline_prev = true;
|
||||
} else {
|
||||
if (newline_prev) {
|
||||
pretty.append(n->lineIndent, ' ');
|
||||
newline_prev = false;
|
||||
if (n->tok->kind == FormToken::TokenKind::WHITESPACE)
|
||||
continue;
|
||||
}
|
||||
pretty.append(n->tok->toString());
|
||||
}
|
||||
}
|
||||
|
||||
return pretty;
|
||||
}
|
||||
|
||||
goos::Reader pretty_printer_reader;
|
||||
|
||||
goos::Reader& get_pretty_printer_reader() {
|
||||
return pretty_printer_reader;
|
||||
}
|
||||
|
||||
goos::Object to_symbol(const std::string& str) {
|
||||
return goos::SymbolObject::make_new(pretty_printer_reader.symbolTable, str);
|
||||
}
|
||||
|
||||
goos::Object build_list(const std::string& str) {
|
||||
return build_list(to_symbol(str));
|
||||
}
|
||||
|
||||
goos::Object build_list(const goos::Object& obj) {
|
||||
return goos::PairObject::make_new(obj, goos::EmptyListObject::make_new());
|
||||
}
|
||||
|
||||
goos::Object build_list(const std::vector<goos::Object>& objects) {
|
||||
if (objects.empty()) {
|
||||
return goos::EmptyListObject::make_new();
|
||||
} else {
|
||||
return build_list(objects.data(), objects.size());
|
||||
}
|
||||
}
|
||||
|
||||
// build a list out of an array of forms
|
||||
goos::Object build_list(const goos::Object* objects, int count) {
|
||||
assert(count);
|
||||
auto car = objects[0];
|
||||
goos::Object cdr;
|
||||
if (count - 1) {
|
||||
cdr = build_list(objects + 1, count - 1);
|
||||
} else {
|
||||
cdr = goos::EmptyListObject::make_new();
|
||||
}
|
||||
return goos::PairObject::make_new(car, cdr);
|
||||
}
|
||||
|
||||
// build a list out of a vector of strings that are converted to symbols
|
||||
goos::Object build_list(const std::vector<std::string>& symbols) {
|
||||
if (symbols.empty()) {
|
||||
return goos::EmptyListObject::make_new();
|
||||
}
|
||||
std::vector<goos::Object> f;
|
||||
f.reserve(symbols.size());
|
||||
for (auto& x : symbols) {
|
||||
f.push_back(to_symbol(x));
|
||||
}
|
||||
return build_list(f.data(), f.size());
|
||||
}
|
||||
} // namespace pretty_print
|
||||
@@ -0,0 +1,49 @@
|
||||
/*!
|
||||
* @file PrettyPrinter.h
|
||||
* A Pretty Printer for GOOS.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/goos/Object.h"
|
||||
#include "common/goos/Reader.h"
|
||||
|
||||
namespace pretty_print {
|
||||
// main pretty print function
|
||||
std::string to_string(const goos::Object& obj, int line_length = 80);
|
||||
|
||||
// string -> object (as a symbol)
|
||||
goos::Object to_symbol(const std::string& str);
|
||||
|
||||
// list with a single symbol from a string
|
||||
goos::Object build_list(const std::string& str);
|
||||
|
||||
// wrap an object in a list
|
||||
goos::Object build_list(const goos::Object& obj);
|
||||
|
||||
// build a list out of a vector of forms
|
||||
goos::Object build_list(const std::vector<goos::Object>& objects);
|
||||
|
||||
// build a list out of an array of forms
|
||||
goos::Object build_list(const goos::Object* objects, int count);
|
||||
|
||||
// build a list out of a vector of strings that are converted to symbols
|
||||
goos::Object build_list(const std::vector<std::string>& symbols);
|
||||
|
||||
// fancy wrapper functions. Due to template magic these can call each other
|
||||
// and accept mixed arguments!
|
||||
|
||||
template <typename... Args>
|
||||
goos::Object build_list(const std::string& str, Args... rest) {
|
||||
return goos::PairObject::make_new(to_symbol(str), build_list(rest...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
goos::Object build_list(const goos::Object& car, Args... rest) {
|
||||
return goos::PairObject::make_new(car, build_list(rest...));
|
||||
}
|
||||
|
||||
goos::Reader& get_pretty_printer_reader();
|
||||
|
||||
} // namespace pretty_print
|
||||
@@ -1,5 +1,4 @@
|
||||
add_executable(decompiler
|
||||
util/LispPrint.cpp
|
||||
main.cpp
|
||||
ObjectFile/ObjectFileDB.cpp
|
||||
Disasm/Instruction.cpp
|
||||
@@ -11,7 +10,6 @@ add_executable(decompiler
|
||||
Function/Function.cpp
|
||||
util/FileIO.cpp
|
||||
config.cpp
|
||||
util/LispPrint.cpp
|
||||
util/DecompilerTypeSystem.cpp
|
||||
Function/BasicBlocks.cpp
|
||||
Disasm/InstructionMatching.cpp
|
||||
@@ -20,6 +18,7 @@ add_executable(decompiler
|
||||
IR/IR.cpp)
|
||||
|
||||
target_link_libraries(decompiler
|
||||
goos
|
||||
minilzo
|
||||
common_util
|
||||
type_system
|
||||
|
||||
@@ -140,8 +140,8 @@ std::string BlockVtx::to_string() {
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> BlockVtx::to_form() {
|
||||
return toForm("b" + std::to_string(block_id));
|
||||
goos::Object BlockVtx::to_form() {
|
||||
return pretty_print::to_symbol("b" + std::to_string(block_id));
|
||||
}
|
||||
|
||||
std::string SequenceVtx::to_string() {
|
||||
@@ -152,117 +152,117 @@ std::string SequenceVtx::to_string() {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> SequenceVtx::to_form() {
|
||||
std::vector<std::shared_ptr<Form>> forms;
|
||||
forms.push_back(toForm("seq"));
|
||||
goos::Object SequenceVtx::to_form() {
|
||||
std::vector<goos::Object> forms;
|
||||
forms.push_back(pretty_print::to_symbol("seq"));
|
||||
for (auto* x : seq) {
|
||||
forms.push_back(x->to_form());
|
||||
}
|
||||
return buildList(forms);
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
std::string EntryVtx::to_string() {
|
||||
return "ENTRY";
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> EntryVtx::to_form() {
|
||||
return toForm("entry");
|
||||
goos::Object EntryVtx::to_form() {
|
||||
return pretty_print::to_symbol("entry");
|
||||
}
|
||||
|
||||
std::string ExitVtx::to_string() {
|
||||
return "EXIT";
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> ExitVtx::to_form() {
|
||||
return toForm("exit");
|
||||
goos::Object ExitVtx::to_form() {
|
||||
return pretty_print::to_symbol("exit");
|
||||
}
|
||||
|
||||
std::string CondWithElse::to_string() {
|
||||
return "CONDWE" + std::to_string(uid);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> CondWithElse::to_form() {
|
||||
std::vector<std::shared_ptr<Form>> forms;
|
||||
forms.push_back(toForm("cond"));
|
||||
goos::Object CondWithElse::to_form() {
|
||||
std::vector<goos::Object> forms;
|
||||
forms.push_back(pretty_print::to_symbol("cond"));
|
||||
for (const auto& x : entries) {
|
||||
std::vector<std::shared_ptr<Form>> e = {x.condition->to_form(), x.body->to_form()};
|
||||
forms.push_back(buildList(e));
|
||||
std::vector<goos::Object> e = {x.condition->to_form(), x.body->to_form()};
|
||||
forms.push_back(pretty_print::build_list(e));
|
||||
}
|
||||
std::vector<std::shared_ptr<Form>> e = {toForm("else"), else_vtx->to_form()};
|
||||
forms.push_back(buildList(e));
|
||||
return buildList(forms);
|
||||
std::vector<goos::Object> e = {pretty_print::to_symbol("else"), else_vtx->to_form()};
|
||||
forms.push_back(pretty_print::build_list(e));
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
std::string CondNoElse::to_string() {
|
||||
return "CONDNE" + std::to_string(uid);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> CondNoElse::to_form() {
|
||||
std::vector<std::shared_ptr<Form>> forms;
|
||||
forms.push_back(toForm("cond"));
|
||||
goos::Object CondNoElse::to_form() {
|
||||
std::vector<goos::Object> forms;
|
||||
forms.push_back(pretty_print::to_symbol("cond"));
|
||||
for (const auto& x : entries) {
|
||||
std::vector<std::shared_ptr<Form>> e = {x.condition->to_form(), x.body->to_form()};
|
||||
forms.push_back(buildList(e));
|
||||
std::vector<goos::Object> e = {x.condition->to_form(), x.body->to_form()};
|
||||
forms.push_back(pretty_print::build_list(e));
|
||||
}
|
||||
return buildList(forms);
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
std::string WhileLoop::to_string() {
|
||||
return "WHL" + std::to_string(uid);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> WhileLoop::to_form() {
|
||||
std::vector<std::shared_ptr<Form>> forms = {toForm("while"), condition->to_form(),
|
||||
body->to_form()};
|
||||
return buildList(forms);
|
||||
goos::Object WhileLoop::to_form() {
|
||||
std::vector<goos::Object> forms = {pretty_print::to_symbol("while"), condition->to_form(),
|
||||
body->to_form()};
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
std::string UntilLoop::to_string() {
|
||||
return "UNTL" + std::to_string(uid);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> UntilLoop::to_form() {
|
||||
std::vector<std::shared_ptr<Form>> forms = {toForm("until"), condition->to_form(),
|
||||
body->to_form()};
|
||||
return buildList(forms);
|
||||
goos::Object UntilLoop::to_form() {
|
||||
std::vector<goos::Object> forms = {pretty_print::to_symbol("until"), condition->to_form(),
|
||||
body->to_form()};
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
std::string UntilLoop_single::to_string() {
|
||||
return "UNTLS" + std::to_string(uid);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> UntilLoop_single::to_form() {
|
||||
std::vector<std::shared_ptr<Form>> forms = {toForm("until1"), block->to_form()};
|
||||
return buildList(forms);
|
||||
goos::Object UntilLoop_single::to_form() {
|
||||
std::vector<goos::Object> forms = {pretty_print::to_symbol("until1"), block->to_form()};
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
std::string InfiniteLoopBlock::to_string() {
|
||||
return "INFL" + std::to_string(uid);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> InfiniteLoopBlock::to_form() {
|
||||
std::vector<std::shared_ptr<Form>> forms = {toForm("inf-loop"), block->to_form()};
|
||||
return buildList(forms);
|
||||
goos::Object InfiniteLoopBlock::to_form() {
|
||||
std::vector<goos::Object> forms = {pretty_print::to_symbol("inf-loop"), block->to_form()};
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
std::string ShortCircuit::to_string() {
|
||||
return "SC" + std::to_string(uid);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> ShortCircuit::to_form() {
|
||||
std::vector<std::shared_ptr<Form>> forms;
|
||||
forms.push_back(toForm("sc"));
|
||||
goos::Object ShortCircuit::to_form() {
|
||||
std::vector<goos::Object> forms;
|
||||
forms.push_back(pretty_print::to_symbol("sc"));
|
||||
for (const auto& x : entries) {
|
||||
forms.push_back(x->to_form());
|
||||
}
|
||||
return buildList(forms);
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
/*
|
||||
std::shared_ptr<Form> IfElseVtx::to_form() {
|
||||
std::vector<std::shared_ptr<Form>> forms = {toForm("if"), condition->to_form(),
|
||||
goos::Object IfElseVtx::to_form() {
|
||||
std::vector<goos::Object> forms = {pretty_print::to_symbol("if"), condition->to_form(),
|
||||
true_case->to_form(), false_case->to_form()};
|
||||
return buildList(forms);
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
std::string IfElseVtx::to_string() {
|
||||
@@ -274,10 +274,10 @@ std::string GotoEnd::to_string() {
|
||||
return "goto_end" + std::to_string(uid);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> GotoEnd::to_form() {
|
||||
std::vector<std::shared_ptr<Form>> forms = {toForm("return-from-function"), body->to_form(),
|
||||
unreachable_block->to_form()};
|
||||
return buildList(forms);
|
||||
goos::Object GotoEnd::to_form() {
|
||||
std::vector<goos::Object> forms = {pretty_print::to_symbol("return-from-function"),
|
||||
body->to_form(), unreachable_block->to_form()};
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
ControlFlowGraph::ControlFlowGraph() {
|
||||
@@ -361,17 +361,17 @@ CfgVtx* ControlFlowGraph::get_single_top_level() {
|
||||
* Turn into a form. If fully resolved, prints the nested control flow. Otherwise puts all the
|
||||
* ungrouped stuff into an "(ungrouped ...)" form and prints that.
|
||||
*/
|
||||
std::shared_ptr<Form> ControlFlowGraph::to_form() {
|
||||
goos::Object ControlFlowGraph::to_form() {
|
||||
if (get_top_level_vertices_count() == 1) {
|
||||
return get_single_top_level()->to_form();
|
||||
} else {
|
||||
std::vector<std::shared_ptr<Form>> forms = {toForm("ungrouped")};
|
||||
std::vector<goos::Object> forms = {pretty_print::to_symbol("ungrouped")};
|
||||
for (auto* x : m_node_pool) {
|
||||
if (!x->parent && x != entry() && x != exit()) {
|
||||
forms.push_back(x->to_form());
|
||||
}
|
||||
}
|
||||
return buildList(forms);
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,8 +380,7 @@ std::shared_ptr<Form> ControlFlowGraph::to_form() {
|
||||
* ungrouped stuff into an "(ungrouped ...)" form and prints that.
|
||||
*/
|
||||
std::string ControlFlowGraph::to_form_string() {
|
||||
// todo - fix bug in pretty printing and reduce this to 80!
|
||||
return to_form()->toStringPretty(0, 140);
|
||||
return pretty_print::to_string(to_form());
|
||||
}
|
||||
|
||||
// bool ControlFlowGraph::compact_top_level() {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include "decompiler/util/LispPrint.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
|
||||
/*!
|
||||
* In v, find an item equal to old, and replace it with replace.
|
||||
@@ -61,8 +61,8 @@ void replace_exactly_one_in(std::vector<T>& v, T old, T replace) {
|
||||
*/
|
||||
class CfgVtx {
|
||||
public:
|
||||
virtual std::string to_string() = 0; // convert to a single line string for debugging
|
||||
virtual std::shared_ptr<Form> to_form() = 0; // recursive print as LISP form.
|
||||
virtual std::string to_string() = 0; // convert to a single line string for debugging
|
||||
virtual goos::Object to_form() = 0; // recursive print as LISP form.
|
||||
virtual ~CfgVtx() = default;
|
||||
|
||||
CfgVtx* parent = nullptr; // parent structure, or nullptr if top level
|
||||
@@ -125,7 +125,7 @@ class CfgVtx {
|
||||
class EntryVtx : public CfgVtx {
|
||||
public:
|
||||
EntryVtx() = default;
|
||||
std::shared_ptr<Form> to_form() override;
|
||||
goos::Object to_form() override;
|
||||
std::string to_string() override;
|
||||
};
|
||||
|
||||
@@ -135,7 +135,7 @@ class EntryVtx : public CfgVtx {
|
||||
class ExitVtx : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() override;
|
||||
std::shared_ptr<Form> to_form() override;
|
||||
goos::Object to_form() override;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -145,7 +145,7 @@ class BlockVtx : public CfgVtx {
|
||||
public:
|
||||
explicit BlockVtx(int id) : block_id(id) {}
|
||||
std::string to_string() override;
|
||||
std::shared_ptr<Form> to_form() override;
|
||||
goos::Object to_form() override;
|
||||
int block_id = -1; // which block are we?
|
||||
bool is_early_exit_block = false; // are we an empty block at the end for early exits to jump to?
|
||||
};
|
||||
@@ -157,7 +157,7 @@ class BlockVtx : public CfgVtx {
|
||||
class SequenceVtx : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() override;
|
||||
std::shared_ptr<Form> to_form() override;
|
||||
goos::Object to_form() override;
|
||||
std::vector<CfgVtx*> seq;
|
||||
};
|
||||
|
||||
@@ -169,7 +169,7 @@ class SequenceVtx : public CfgVtx {
|
||||
class CondWithElse : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() override;
|
||||
std::shared_ptr<Form> to_form() override;
|
||||
goos::Object to_form() override;
|
||||
|
||||
struct Entry {
|
||||
Entry() = default;
|
||||
@@ -190,7 +190,7 @@ class CondWithElse : public CfgVtx {
|
||||
class CondNoElse : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() override;
|
||||
std::shared_ptr<Form> to_form() override;
|
||||
goos::Object to_form() override;
|
||||
|
||||
struct Entry {
|
||||
Entry() = default;
|
||||
@@ -205,7 +205,7 @@ class CondNoElse : public CfgVtx {
|
||||
class WhileLoop : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() override;
|
||||
std::shared_ptr<Form> to_form() override;
|
||||
goos::Object to_form() override;
|
||||
|
||||
CfgVtx* condition = nullptr;
|
||||
CfgVtx* body = nullptr;
|
||||
@@ -214,7 +214,7 @@ class WhileLoop : public CfgVtx {
|
||||
class UntilLoop : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() override;
|
||||
std::shared_ptr<Form> to_form() override;
|
||||
goos::Object to_form() override;
|
||||
|
||||
CfgVtx* condition = nullptr;
|
||||
CfgVtx* body = nullptr;
|
||||
@@ -223,7 +223,7 @@ class UntilLoop : public CfgVtx {
|
||||
class UntilLoop_single : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() override;
|
||||
std::shared_ptr<Form> to_form() override;
|
||||
goos::Object to_form() override;
|
||||
|
||||
CfgVtx* block = nullptr;
|
||||
};
|
||||
@@ -231,21 +231,21 @@ class UntilLoop_single : public CfgVtx {
|
||||
class ShortCircuit : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() override;
|
||||
std::shared_ptr<Form> to_form() override;
|
||||
goos::Object to_form() override;
|
||||
std::vector<CfgVtx*> entries;
|
||||
};
|
||||
|
||||
class InfiniteLoopBlock : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() override;
|
||||
std::shared_ptr<Form> to_form() override;
|
||||
goos::Object to_form() override;
|
||||
CfgVtx* block;
|
||||
};
|
||||
|
||||
class GotoEnd : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() override;
|
||||
std::shared_ptr<Form> to_form() override;
|
||||
goos::Object to_form() override;
|
||||
CfgVtx* body = nullptr;
|
||||
CfgVtx* unreachable_block = nullptr;
|
||||
};
|
||||
@@ -260,7 +260,7 @@ class ControlFlowGraph {
|
||||
ControlFlowGraph();
|
||||
~ControlFlowGraph();
|
||||
|
||||
std::shared_ptr<Form> to_form();
|
||||
goos::Object to_form();
|
||||
std::string to_form_string();
|
||||
std::string to_dot();
|
||||
int get_top_level_vertices_count();
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
|
||||
namespace {
|
||||
|
||||
struct ConvertState {
|
||||
void reset() {}
|
||||
};
|
||||
|
||||
std::shared_ptr<IR_Set> make_set(IR_Set::Kind kind,
|
||||
const std::shared_ptr<IR>& dst,
|
||||
const std::shared_ptr<IR>& src) {
|
||||
@@ -1122,6 +1118,7 @@ std::shared_ptr<IR> try_lwu(Instruction& i0,
|
||||
Instruction& i3,
|
||||
Instruction& i4,
|
||||
int idx) {
|
||||
(void)idx;
|
||||
auto s6 = make_gpr(Reg::S6);
|
||||
if (i0.kind == InstructionKind::LWU && i0.get_dst(0).is_reg(s6) &&
|
||||
i0.get_src(0).get_imm() == 44 && i0.get_src(1).is_reg(s6) &&
|
||||
@@ -1138,8 +1135,6 @@ std::shared_ptr<IR> try_lwu(Instruction& i0,
|
||||
} // namespace
|
||||
|
||||
void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjectFile* file) {
|
||||
ConvertState state;
|
||||
|
||||
for (int instr = block.start_word; instr < block.end_word; instr++) {
|
||||
auto& i = func->instructions.at(instr);
|
||||
|
||||
@@ -1400,7 +1395,6 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
|
||||
|
||||
// everything failed
|
||||
if (!result) {
|
||||
state.reset();
|
||||
// temp hack for debug:
|
||||
printf("Instruction -> BasicOp failed on %s\n", i.to_string(*file).c_str());
|
||||
func->add_basic_op(std::make_shared<IR_Failed>(), instr, instr + 1);
|
||||
|
||||
+59
-48
@@ -2,19 +2,21 @@
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
|
||||
std::string IR::print(const LinkedObjectFile& file) const {
|
||||
return to_form(file)->toStringPretty();
|
||||
// return to_form(file)->toStringPretty();
|
||||
return pretty_print::to_string(to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Register::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_Register::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return toForm(reg.to_charp());
|
||||
return pretty_print::to_symbol(reg.to_charp());
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Set::to_form(const LinkedObjectFile& file) const {
|
||||
return buildList(toForm("set!"), dst->to_form(file), src->to_form(file));
|
||||
goos::Object IR_Set::to_form(const LinkedObjectFile& file) const {
|
||||
return pretty_print::build_list(pretty_print::to_symbol("set!"), dst->to_form(file),
|
||||
src->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Store::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_Store::to_form(const LinkedObjectFile& file) const {
|
||||
std::string store_operator;
|
||||
switch (kind) {
|
||||
case FLOAT:
|
||||
@@ -45,30 +47,31 @@ std::shared_ptr<Form> IR_Store::to_form(const LinkedObjectFile& file) const {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return buildList(toForm(store_operator), dst->to_form(file), src->to_form(file));
|
||||
return pretty_print::build_list(pretty_print::to_symbol(store_operator), dst->to_form(file),
|
||||
src->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Failed::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_Failed::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return buildList("INVALID-OPERATION");
|
||||
return pretty_print::build_list("INVALID-OPERATION");
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Symbol::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_Symbol::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return toForm("'" + name);
|
||||
return pretty_print::to_symbol("'" + name);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_SymbolValue::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_SymbolValue::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return toForm(name);
|
||||
return pretty_print::to_symbol(name);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_StaticAddress::to_form(const LinkedObjectFile& file) const {
|
||||
// return buildList(toForm("&"), file.get_label_name(label_id));
|
||||
return toForm(file.get_label_name(label_id));
|
||||
goos::Object IR_StaticAddress::to_form(const LinkedObjectFile& file) const {
|
||||
// return pretty_print::build_list(pretty_print::to_symbol("&"), file.get_label_name(label_id));
|
||||
return pretty_print::to_symbol(file.get_label_name(label_id));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Load::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_Load::to_form(const LinkedObjectFile& file) const {
|
||||
std::string load_operator;
|
||||
switch (kind) {
|
||||
case FLOAT:
|
||||
@@ -113,10 +116,10 @@ std::shared_ptr<Form> IR_Load::to_form(const LinkedObjectFile& file) const {
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return buildList(toForm(load_operator), location->to_form(file));
|
||||
return pretty_print::build_list(pretty_print::to_symbol(load_operator), location->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_FloatMath2::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_FloatMath2::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
case DIV:
|
||||
@@ -141,10 +144,11 @@ std::shared_ptr<Form> IR_FloatMath2::to_form(const LinkedObjectFile& file) const
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return buildList(toForm(math_operator), arg0->to_form(file), arg1->to_form(file));
|
||||
return pretty_print::build_list(pretty_print::to_symbol(math_operator), arg0->to_form(file),
|
||||
arg1->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_IntMath2::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_IntMath2::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
case ADD:
|
||||
@@ -195,10 +199,11 @@ std::shared_ptr<Form> IR_IntMath2::to_form(const LinkedObjectFile& file) const {
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return buildList(toForm(math_operator), arg0->to_form(file), arg1->to_form(file));
|
||||
return pretty_print::build_list(pretty_print::to_symbol(math_operator), arg0->to_form(file),
|
||||
arg1->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_IntMath1::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_IntMath1::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
case NOT:
|
||||
@@ -207,10 +212,10 @@ std::shared_ptr<Form> IR_IntMath1::to_form(const LinkedObjectFile& file) const {
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return buildList(toForm(math_operator), arg->to_form(file));
|
||||
return pretty_print::build_list(pretty_print::to_symbol(math_operator), arg->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_FloatMath1::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_FloatMath1::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
case FLOAT_TO_INT:
|
||||
@@ -231,40 +236,43 @@ std::shared_ptr<Form> IR_FloatMath1::to_form(const LinkedObjectFile& file) const
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return buildList(toForm(math_operator), arg->to_form(file));
|
||||
return pretty_print::build_list(pretty_print::to_symbol(math_operator), arg->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Call::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_Call::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return buildList("call!");
|
||||
return pretty_print::build_list("call!");
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_IntegerConstant::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_IntegerConstant::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return toForm(std::to_string(value));
|
||||
return pretty_print::to_symbol(std::to_string(value));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> BranchDelay::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object BranchDelay::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
switch (kind) {
|
||||
case NOP:
|
||||
return buildList("nop");
|
||||
return pretty_print::build_list("nop");
|
||||
case SET_REG_FALSE:
|
||||
return buildList(toForm("set!"), destination->to_form(file), "'#f");
|
||||
return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file),
|
||||
"'#f");
|
||||
case SET_REG_TRUE:
|
||||
return buildList(toForm("set!"), destination->to_form(file), "'#t");
|
||||
return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file),
|
||||
"'#t");
|
||||
case SET_REG_REG:
|
||||
return buildList(toForm("set!"), destination->to_form(file), source->to_form(file));
|
||||
return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file),
|
||||
source->to_form(file));
|
||||
case UNKNOWN:
|
||||
return buildList("unknown-branch-delay");
|
||||
return pretty_print::build_list("unknown-branch-delay");
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Nop::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_Nop::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return buildList("nop!");
|
||||
return pretty_print::build_list("nop!");
|
||||
}
|
||||
|
||||
int Condition::num_args() const {
|
||||
@@ -296,7 +304,7 @@ int Condition::num_args() const {
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> Condition::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object Condition::to_form(const LinkedObjectFile& file) const {
|
||||
int nargs = num_args();
|
||||
std::string condtion_operator;
|
||||
switch (kind) {
|
||||
@@ -362,30 +370,33 @@ std::shared_ptr<Form> Condition::to_form(const LinkedObjectFile& file) const {
|
||||
}
|
||||
|
||||
if (nargs == 2) {
|
||||
return buildList(toForm(condtion_operator), src0->to_form(file), src1->to_form(file));
|
||||
return pretty_print::build_list(pretty_print::to_symbol(condtion_operator), src0->to_form(file),
|
||||
src1->to_form(file));
|
||||
} else if (nargs == 1) {
|
||||
if (condtion_operator.empty()) {
|
||||
return src0->to_form(file);
|
||||
} else {
|
||||
return buildList(toForm(condtion_operator), src0->to_form(file));
|
||||
return pretty_print::build_list(pretty_print::to_symbol(condtion_operator),
|
||||
src0->to_form(file));
|
||||
}
|
||||
} else if (nargs == 0) {
|
||||
return toForm(condtion_operator);
|
||||
return pretty_print::to_symbol(condtion_operator);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Branch::to_form(const LinkedObjectFile& file) const {
|
||||
return buildList(toForm(likely ? "bl!" : "b!"), condition.to_form(file),
|
||||
toForm(file.get_label_name(dest_label_idx)), branch_delay.to_form(file));
|
||||
goos::Object IR_Branch::to_form(const LinkedObjectFile& file) const {
|
||||
return pretty_print::build_list(
|
||||
pretty_print::to_symbol(likely ? "bl!" : "b!"), condition.to_form(file),
|
||||
pretty_print::to_symbol(file.get_label_name(dest_label_idx)), branch_delay.to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Compare::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_Compare::to_form(const LinkedObjectFile& file) const {
|
||||
return condition.to_form(file);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Suspend::to_form(const LinkedObjectFile& file) const {
|
||||
goos::Object IR_Suspend::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return buildList("suspend!");
|
||||
return pretty_print::build_list("suspend!");
|
||||
}
|
||||
+22
-22
@@ -3,13 +3,13 @@
|
||||
|
||||
#include <cassert>
|
||||
#include "decompiler/Disasm/Register.h"
|
||||
#include "decompiler/util/LispPrint.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
|
||||
class LinkedObjectFile;
|
||||
|
||||
class IR {
|
||||
public:
|
||||
virtual std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const = 0;
|
||||
virtual goos::Object to_form(const LinkedObjectFile& file) const = 0;
|
||||
std::string print(const LinkedObjectFile& file) const;
|
||||
|
||||
bool is_basic_op = false;
|
||||
@@ -18,13 +18,13 @@ class IR {
|
||||
class IR_Failed : public IR {
|
||||
public:
|
||||
IR_Failed() = default;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Register : public IR {
|
||||
public:
|
||||
IR_Register(Register _reg, int _instr_idx) : reg(_reg), instr_idx(_instr_idx) {}
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
Register reg;
|
||||
int instr_idx = -1;
|
||||
};
|
||||
@@ -44,7 +44,7 @@ class IR_Set : public IR {
|
||||
} kind;
|
||||
IR_Set(Kind _kind, std::shared_ptr<IR> _dst, std::shared_ptr<IR> _src)
|
||||
: kind(_kind), dst(std::move(_dst)), src(std::move(_src)) {}
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
std::shared_ptr<IR> dst, src;
|
||||
std::shared_ptr<IR> clobber = nullptr;
|
||||
};
|
||||
@@ -55,28 +55,28 @@ class IR_Store : public IR_Set {
|
||||
IR_Store(Kind _kind, std::shared_ptr<IR> _dst, std::shared_ptr<IR> _src, int _size)
|
||||
: IR_Set(IR_Set::LOAD, std::move(_dst), std::move(_src)), kind(_kind), size(_size) {}
|
||||
int size;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Symbol : public IR {
|
||||
public:
|
||||
IR_Symbol(std::string _name) : name(std::move(_name)) {}
|
||||
std::string name;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_SymbolValue : public IR {
|
||||
public:
|
||||
IR_SymbolValue(std::string _name) : name(std::move(_name)) {}
|
||||
std::string name;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_StaticAddress : public IR {
|
||||
public:
|
||||
IR_StaticAddress(int _label_id) : label_id(_label_id) {}
|
||||
int label_id = -1;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Load : public IR {
|
||||
@@ -87,7 +87,7 @@ class IR_Load : public IR {
|
||||
: kind(_kind), size(_size), location(_location) {}
|
||||
int size;
|
||||
std::shared_ptr<IR> location;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_FloatMath2 : public IR {
|
||||
@@ -96,7 +96,7 @@ class IR_FloatMath2 : public IR {
|
||||
IR_FloatMath2(Kind _kind, std::shared_ptr<IR> _arg0, std::shared_ptr<IR> _arg1)
|
||||
: kind(_kind), arg0(std::move(_arg0)), arg1(std::move(_arg1)) {}
|
||||
std::shared_ptr<IR> arg0, arg1;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_FloatMath1 : public IR {
|
||||
@@ -104,7 +104,7 @@ class IR_FloatMath1 : public IR {
|
||||
enum Kind { FLOAT_TO_INT, INT_TO_FLOAT, ABS, NEG, SQRT } kind;
|
||||
IR_FloatMath1(Kind _kind, std::shared_ptr<IR> _arg) : kind(_kind), arg(std::move(_arg)) {}
|
||||
std::shared_ptr<IR> arg;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_IntMath2 : public IR {
|
||||
@@ -129,7 +129,7 @@ class IR_IntMath2 : public IR {
|
||||
IR_IntMath2(Kind _kind, std::shared_ptr<IR> _arg0, std::shared_ptr<IR> _arg1)
|
||||
: kind(_kind), arg0(std::move(_arg0)), arg1(std::move(_arg1)) {}
|
||||
std::shared_ptr<IR> arg0, arg1;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_IntMath1 : public IR {
|
||||
@@ -137,27 +137,27 @@ class IR_IntMath1 : public IR {
|
||||
enum Kind { NOT } kind;
|
||||
IR_IntMath1(Kind _kind, std::shared_ptr<IR> _arg) : kind(_kind), arg(std::move(_arg)) {}
|
||||
std::shared_ptr<IR> arg;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Call : public IR {
|
||||
public:
|
||||
IR_Call() = default;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_IntegerConstant : public IR {
|
||||
public:
|
||||
int64_t value;
|
||||
explicit IR_IntegerConstant(int64_t _value) : value(_value) {}
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
struct BranchDelay {
|
||||
enum Kind { NOP, SET_REG_FALSE, SET_REG_TRUE, SET_REG_REG, UNKNOWN } kind;
|
||||
std::shared_ptr<IR> destination = nullptr, source = nullptr;
|
||||
BranchDelay(Kind _kind) : kind(_kind) {}
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const;
|
||||
};
|
||||
|
||||
struct Condition {
|
||||
@@ -199,7 +199,7 @@ struct Condition {
|
||||
}
|
||||
|
||||
int num_args() const;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const;
|
||||
std::shared_ptr<IR> src0, src1, clobber;
|
||||
};
|
||||
|
||||
@@ -216,7 +216,7 @@ class IR_Branch : public IR {
|
||||
BranchDelay branch_delay;
|
||||
bool likely;
|
||||
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Compare : public IR {
|
||||
@@ -225,19 +225,19 @@ class IR_Compare : public IR {
|
||||
|
||||
Condition condition;
|
||||
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Nop : public IR {
|
||||
public:
|
||||
IR_Nop() = default;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Suspend : public IR {
|
||||
public:
|
||||
IR_Suspend() = default;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
#endif // JAK_IR_H
|
||||
|
||||
@@ -714,7 +714,9 @@ std::string LinkedObjectFile::print_scripts() {
|
||||
if (label_id != -1) {
|
||||
auto& label = labels.at(label_id);
|
||||
if ((label.offset & 7) == 2) {
|
||||
result += to_form_script(seg, word_idx, already_printed)->toStringPretty(0, 100) + "\n";
|
||||
// result += to_form_script(seg, word_idx, already_printed)->toStringPretty(0, 100) +
|
||||
// "\n";
|
||||
result += pretty_print::to_string(to_form_script(seg, word_idx, already_printed)) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -736,16 +738,14 @@ bool LinkedObjectFile::is_empty_list(int seg, int byte_idx) {
|
||||
* Note : this takes the address of the car of the pair. which is perhaps a bit confusing
|
||||
* (in GOAL, this would be (&-> obj car))
|
||||
*/
|
||||
std::shared_ptr<Form> LinkedObjectFile::to_form_script(int seg,
|
||||
int word_idx,
|
||||
std::vector<bool>& seen) {
|
||||
goos::Object LinkedObjectFile::to_form_script(int seg, int word_idx, std::vector<bool>& seen) {
|
||||
// the object to currently print. to start off, create pair from the car address we've been given.
|
||||
int goal_print_obj = word_idx * 4 + 2;
|
||||
|
||||
// resulting form. we can't have a totally empty list (as an empty list looks like a symbol,
|
||||
// so it wouldn't be flagged), so it's safe to make this a pair.
|
||||
auto result = std::make_shared<Form>();
|
||||
result->kind = FormKind::PAIR;
|
||||
auto result = goos::PairObject::make_new(goos::EmptyListObject::make_new(),
|
||||
goos::EmptyListObject::make_new());
|
||||
|
||||
// the current pair to fill out.
|
||||
auto fill = result;
|
||||
@@ -755,14 +755,14 @@ std::shared_ptr<Form> LinkedObjectFile::to_form_script(int seg,
|
||||
// check the thing to print is a a pair.
|
||||
if ((goal_print_obj & 7) == 2) {
|
||||
// first convert the car (again, with (&-> obj car))
|
||||
fill->pair[0] = to_form_script_object(seg, goal_print_obj - 2, seen);
|
||||
fill.as_pair()->car = to_form_script_object(seg, goal_print_obj - 2, seen);
|
||||
seen.at(goal_print_obj / 4) = true;
|
||||
|
||||
auto cdr_addr = goal_print_obj + 2;
|
||||
|
||||
if (is_empty_list(seg, cdr_addr)) {
|
||||
// the list has ended!
|
||||
fill->pair[1] = gSymbolTable.getEmptyPair();
|
||||
fill.as_pair()->cdr = goos::EmptyListObject::make_new();
|
||||
return result;
|
||||
} else {
|
||||
// cdr object should be aligned.
|
||||
@@ -772,12 +772,12 @@ std::shared_ptr<Form> LinkedObjectFile::to_form_script(int seg,
|
||||
if (cdr_word.kind == LinkedWord::PTR && (labels.at(cdr_word.label_id).offset & 7) == 2) {
|
||||
// yes, proper list. add another pair and link it in to the list.
|
||||
goal_print_obj = labels.at(cdr_word.label_id).offset;
|
||||
fill->pair[1] = std::make_shared<Form>();
|
||||
fill->pair[1]->kind = FormKind::PAIR;
|
||||
fill = fill->pair[1];
|
||||
fill.as_pair()->cdr = goos::PairObject::make_new(goos::EmptyListObject::make_new(),
|
||||
goos::EmptyListObject::make_new());
|
||||
fill = fill.as_pair()->cdr;
|
||||
} else {
|
||||
// improper list, put the last thing in and end
|
||||
fill->pair[1] = to_form_script_object(seg, cdr_addr, seen);
|
||||
fill.as_pair()->cdr = to_form_script_object(seg, cdr_addr, seen);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -809,10 +809,10 @@ bool LinkedObjectFile::is_string(int seg, int byte_idx) {
|
||||
/*!
|
||||
* Convert a (pointer object) to some nice representation.
|
||||
*/
|
||||
std::shared_ptr<Form> LinkedObjectFile::to_form_script_object(int seg,
|
||||
int byte_idx,
|
||||
std::vector<bool>& seen) {
|
||||
std::shared_ptr<Form> result;
|
||||
goos::Object LinkedObjectFile::to_form_script_object(int seg,
|
||||
int byte_idx,
|
||||
std::vector<bool>& seen) {
|
||||
goos::Object result;
|
||||
|
||||
switch (byte_idx & 7) {
|
||||
case 0:
|
||||
@@ -820,10 +820,10 @@ std::shared_ptr<Form> LinkedObjectFile::to_form_script_object(int seg,
|
||||
auto& word = words_by_seg.at(seg).at(byte_idx / 4);
|
||||
if (word.kind == LinkedWord::SYM_PTR) {
|
||||
// .symbol xxxx
|
||||
result = toForm(word.symbol_name);
|
||||
result = pretty_print::to_symbol(word.symbol_name);
|
||||
} else if (word.kind == LinkedWord::PLAIN_DATA) {
|
||||
// .word xxxxx
|
||||
result = toForm(std::to_string(word.data));
|
||||
result = pretty_print::to_symbol(std::to_string(word.data));
|
||||
} else if (word.kind == LinkedWord::PTR) {
|
||||
// might be a sub-list, or some other random pointer
|
||||
auto offset = labels.at(word.label_id).offset;
|
||||
@@ -832,14 +832,14 @@ std::shared_ptr<Form> LinkedObjectFile::to_form_script_object(int seg,
|
||||
result = to_form_script(seg, offset / 4, seen);
|
||||
} else {
|
||||
if (is_string(seg, offset)) {
|
||||
result = toForm(get_goal_string(seg, offset / 4 - 1));
|
||||
result = pretty_print::to_symbol(get_goal_string(seg, offset / 4 - 1));
|
||||
} else {
|
||||
// some random pointer, just print the label.
|
||||
result = toForm(labels.at(word.label_id).name);
|
||||
result = pretty_print::to_symbol(labels.at(word.label_id).name);
|
||||
}
|
||||
}
|
||||
} else if (word.kind == LinkedWord::EMPTY_PTR) {
|
||||
result = gSymbolTable.getEmptyPair();
|
||||
result = goos::EmptyListObject::make_new();
|
||||
} else {
|
||||
std::string debug;
|
||||
append_word_to_string(debug, word);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <unordered_set>
|
||||
#include "LinkedWord.h"
|
||||
#include "decompiler/Function/Function.h"
|
||||
#include "decompiler/util/LispPrint.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
|
||||
/*!
|
||||
* A label to a location in this object file.
|
||||
@@ -124,8 +124,8 @@ class LinkedObjectFile {
|
||||
std::vector<Label> labels;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Form> to_form_script(int seg, int word_idx, std::vector<bool>& seen);
|
||||
std::shared_ptr<Form> to_form_script_object(int seg, int byte_idx, std::vector<bool>& seen);
|
||||
goos::Object to_form_script(int seg, int word_idx, std::vector<bool>& seen);
|
||||
goos::Object to_form_script_object(int seg, int byte_idx, std::vector<bool>& seen);
|
||||
bool is_empty_list(int seg, int byte_idx);
|
||||
bool is_string(int seg, int byte_idx);
|
||||
std::string get_goal_string(int seg, int word_idx);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"write_hexdump_on_v3_only":true,
|
||||
|
||||
// to write out "scripts", which are currently just all the linked lists found
|
||||
"write_scripts":false,
|
||||
"write_scripts":true,
|
||||
|
||||
// Experimental Stuff
|
||||
"find_basic_blocks":true,
|
||||
|
||||
@@ -1,527 +0,0 @@
|
||||
#include "LispPrint.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
//////// HACK - symbol table now looks up by string, which makes it really stupid and store
|
||||
// all strings twice.
|
||||
// should probably just remove it
|
||||
|
||||
/*!
|
||||
* String interning
|
||||
*/
|
||||
std::string* SymbolTable::intern(const std::string& str) {
|
||||
if (map.find(str) == map.end()) {
|
||||
auto* new_string = new std::string(str);
|
||||
map[str] = new_string;
|
||||
return new_string;
|
||||
} else {
|
||||
return map[str];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Global interned string table
|
||||
*/
|
||||
SymbolTable gSymbolTable;
|
||||
|
||||
SymbolTable::SymbolTable() {
|
||||
empty_pair = std::make_shared<Form>();
|
||||
empty_pair->kind = FormKind::EMPTY_LIST;
|
||||
}
|
||||
|
||||
SymbolTable::~SymbolTable() {
|
||||
for (const auto& kv : map)
|
||||
delete kv.second;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert a form to a one-line string.
|
||||
*/
|
||||
std::string Form::toStringSimple() {
|
||||
std::string result;
|
||||
buildStringSimple(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Form::buildStringSimple(std::string& str) {
|
||||
std::vector<FormToken> tokens;
|
||||
toTokenList(tokens);
|
||||
for (auto& token : tokens) {
|
||||
switch (token.kind) {
|
||||
case TokenKind::WHITESPACE:
|
||||
str.push_back(' ');
|
||||
break;
|
||||
case TokenKind::SYMBOL:
|
||||
str.append(*token.str);
|
||||
break;
|
||||
case TokenKind::OPEN_PAREN:
|
||||
str.push_back('(');
|
||||
break;
|
||||
case TokenKind::DOT:
|
||||
str.push_back('.');
|
||||
break;
|
||||
case TokenKind::CLOSE_PAREN:
|
||||
str.push_back(')');
|
||||
break;
|
||||
case TokenKind::EMPTY_PAIR:
|
||||
str.append("()");
|
||||
break;
|
||||
case TokenKind::SPECIAL_SYMBOL:
|
||||
str.append(*token.str);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("buildStringSimple unknown token kind");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Form::toTokenList(std::vector<FormToken>& tokens) {
|
||||
switch (kind) {
|
||||
case FormKind::SYMBOL:
|
||||
tokens.emplace_back(TokenKind::SYMBOL, symbol);
|
||||
break;
|
||||
case FormKind::PAIR: {
|
||||
tokens.emplace_back(TokenKind::OPEN_PAREN);
|
||||
Form* toPrint = this;
|
||||
for (;;) {
|
||||
if (toPrint->kind == FormKind::PAIR) {
|
||||
toPrint->pair[0]->toTokenList(tokens); // print CAR
|
||||
toPrint = toPrint->pair[1].get();
|
||||
if (toPrint->kind == FormKind::EMPTY_LIST) {
|
||||
tokens.emplace_back(TokenKind::CLOSE_PAREN);
|
||||
return;
|
||||
} else {
|
||||
tokens.emplace_back(TokenKind::WHITESPACE);
|
||||
}
|
||||
} else { // not a proper list!
|
||||
tokens.emplace_back(TokenKind::DOT);
|
||||
tokens.emplace_back(TokenKind::WHITESPACE);
|
||||
toPrint->toTokenList(tokens);
|
||||
tokens.emplace_back(TokenKind::CLOSE_PAREN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case FormKind::EMPTY_LIST:
|
||||
tokens.emplace_back(TokenKind::EMPTY_PAIR);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("unhandled form type in buildSimpleString");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////
|
||||
// Pretty Printer
|
||||
///////////////////
|
||||
|
||||
/*!
|
||||
* Linked list node representing a token in the output (whitespace, paren, newline, etc)
|
||||
*/
|
||||
struct PrettyPrinterNode {
|
||||
FormToken* tok = nullptr; // if we aren't a newline, we will have a token.
|
||||
int line = -1; // line that token occurs on. undef for newlines
|
||||
int lineIndent = -1; // indent of line. only valid for first token in the line
|
||||
int offset = -1; // offset of beginning of token from left margin
|
||||
int specialIndentDelta = 0;
|
||||
bool is_line_separator = false; // true if line separator (not a token)
|
||||
PrettyPrinterNode *next = nullptr, *prev = nullptr; // linked list
|
||||
PrettyPrinterNode* paren =
|
||||
nullptr; // pointer to open paren if in parens. open paren points to close and vice versa
|
||||
explicit PrettyPrinterNode(FormToken& _tok) { tok = &_tok; }
|
||||
PrettyPrinterNode() = default;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Splice in a line break after the given node, it there isn't one already and if it isn't the last
|
||||
* node.
|
||||
*/
|
||||
static void insertNewlineAfter(PrettyPrinterNode* node, int specialIndentDelta) {
|
||||
if (node->next && !node->next->is_line_separator) {
|
||||
auto* nl = new PrettyPrinterNode;
|
||||
auto* next = node->next;
|
||||
node->next = nl;
|
||||
nl->prev = node;
|
||||
nl->next = next;
|
||||
next->prev = nl;
|
||||
nl->is_line_separator = true;
|
||||
nl->specialIndentDelta = specialIndentDelta;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Splice in a line break before the given node, if there isn't one already and if it isn't the
|
||||
* first node.
|
||||
*/
|
||||
static void insertNewlineBefore(PrettyPrinterNode* node, int specialIndentDelta) {
|
||||
if (node->prev && !node->prev->is_line_separator) {
|
||||
auto* nl = new PrettyPrinterNode;
|
||||
auto* prev = node->prev;
|
||||
prev->next = nl;
|
||||
nl->prev = prev;
|
||||
nl->next = node;
|
||||
node->prev = nl;
|
||||
nl->is_line_separator = true;
|
||||
nl->specialIndentDelta = specialIndentDelta;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Break a list across multiple lines. This is the fundamental reducing operation of this algorithm
|
||||
*/
|
||||
static void breakList(PrettyPrinterNode* leftParen) {
|
||||
assert(!leftParen->is_line_separator);
|
||||
assert(leftParen->tok->kind == TokenKind::OPEN_PAREN);
|
||||
auto* rp = leftParen->paren;
|
||||
assert(rp->tok->kind == TokenKind::CLOSE_PAREN);
|
||||
|
||||
for (auto* n = leftParen->next; n && n != rp; n = n->next) {
|
||||
if (!n->is_line_separator) {
|
||||
if (n->tok->kind == TokenKind::OPEN_PAREN) {
|
||||
n = n->paren;
|
||||
assert(n->tok->kind == TokenKind::CLOSE_PAREN);
|
||||
insertNewlineAfter(n, 0);
|
||||
} else if (n->tok->kind != TokenKind::WHITESPACE) {
|
||||
assert(n->tok->kind != TokenKind::CLOSE_PAREN);
|
||||
insertNewlineAfter(n, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Compute proper line numbers, offsets, and indents for a list of tokens with newlines
|
||||
* Will add newlines for close parens if needed.
|
||||
*/
|
||||
static PrettyPrinterNode* propagatePretty(PrettyPrinterNode* list, int line_length) {
|
||||
// propagate line numbers
|
||||
PrettyPrinterNode* rv = nullptr;
|
||||
int line = list->line;
|
||||
for (auto* n = list; n; n = n->next) {
|
||||
if (n->is_line_separator) {
|
||||
line++;
|
||||
} else {
|
||||
n->line = line;
|
||||
// add the weird newline.
|
||||
if (n->tok->kind == TokenKind::CLOSE_PAREN) {
|
||||
if (n->line != n->paren->line) {
|
||||
if (n->prev && !n->prev->is_line_separator) {
|
||||
insertNewlineBefore(n, 0);
|
||||
line++;
|
||||
}
|
||||
if (n->next && !n->next->is_line_separator) {
|
||||
insertNewlineAfter(n, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compute offsets and indents
|
||||
std::vector<int> indentStack;
|
||||
indentStack.push_back(0);
|
||||
int offset = 0;
|
||||
PrettyPrinterNode* line_start = list;
|
||||
bool previous_line_sep = false;
|
||||
for (auto* n = list; n; n = n->next) {
|
||||
if (n->is_line_separator) {
|
||||
previous_line_sep = true;
|
||||
offset = indentStack.back() += n->specialIndentDelta;
|
||||
} else {
|
||||
if (previous_line_sep) {
|
||||
line_start = n;
|
||||
n->lineIndent = offset;
|
||||
previous_line_sep = false;
|
||||
}
|
||||
|
||||
n->offset = offset;
|
||||
offset += n->tok->toString().length();
|
||||
if (offset > line_length && !rv)
|
||||
rv = line_start;
|
||||
if (n->tok->kind == TokenKind::OPEN_PAREN) {
|
||||
if (!n->prev || n->prev->is_line_separator) {
|
||||
indentStack.push_back(offset + 1);
|
||||
} else {
|
||||
indentStack.push_back(offset - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (n->tok->kind == TokenKind::CLOSE_PAREN) {
|
||||
indentStack.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the token on the start of the next line. nullptr if we're the last line.
|
||||
*/
|
||||
static PrettyPrinterNode* getNextLine(PrettyPrinterNode* start) {
|
||||
assert(!start->is_line_separator);
|
||||
int line = start->line;
|
||||
for (;;) {
|
||||
if (start->is_line_separator || start->line == line) {
|
||||
if (start->next)
|
||||
start = start->next;
|
||||
else
|
||||
return nullptr;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the next open paren on the current line (can start in the middle of line, not inclusive of
|
||||
* start) nullptr if there's no open parens on the rest of this line.
|
||||
*/
|
||||
static PrettyPrinterNode* getNextListOnLine(PrettyPrinterNode* start) {
|
||||
int line = start->line;
|
||||
assert(!start->is_line_separator);
|
||||
if (!start->next || start->next->is_line_separator)
|
||||
return nullptr;
|
||||
start = start->next;
|
||||
while (!start->is_line_separator && start->line == line) {
|
||||
if (start->tok->kind == TokenKind::OPEN_PAREN)
|
||||
return start;
|
||||
if (!start->next)
|
||||
return nullptr;
|
||||
start = start->next;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the first open paren on the current line (can start in the middle of line, inclusive of
|
||||
* start) nullptr if there's no open parens on the rest of this line
|
||||
*/
|
||||
static PrettyPrinterNode* getFirstListOnLine(PrettyPrinterNode* start) {
|
||||
int line = start->line;
|
||||
assert(!start->is_line_separator);
|
||||
while (!start->is_line_separator && start->line == line) {
|
||||
if (start->tok->kind == TokenKind::OPEN_PAREN)
|
||||
return start;
|
||||
if (!start->next)
|
||||
return nullptr;
|
||||
start = start->next;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the first token on the first line which exceeds the max length
|
||||
*/
|
||||
static PrettyPrinterNode* getFirstBadLine(PrettyPrinterNode* start, int line_length) {
|
||||
assert(!start->is_line_separator);
|
||||
int currentLine = start->line;
|
||||
auto* currentLineNode = start;
|
||||
for (;;) {
|
||||
if (start->is_line_separator) {
|
||||
assert(start->next);
|
||||
start = start->next;
|
||||
} else {
|
||||
if (start->line != currentLine) {
|
||||
currentLine = start->line;
|
||||
currentLineNode = start;
|
||||
}
|
||||
if (start->offset > line_length) {
|
||||
return currentLineNode;
|
||||
}
|
||||
if (!start->next) {
|
||||
return nullptr;
|
||||
}
|
||||
start = start->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Break insertion algorithm.
|
||||
*/
|
||||
static void insertBreaksAsNeeded(PrettyPrinterNode* head, int line_length) {
|
||||
PrettyPrinterNode* last_line_complete = nullptr;
|
||||
PrettyPrinterNode* line_to_start_line_search = head;
|
||||
|
||||
// loop over lines
|
||||
for (;;) {
|
||||
// compute lines as needed
|
||||
propagatePretty(head, line_length);
|
||||
|
||||
// search for a bad line starting at the last line we fixed
|
||||
PrettyPrinterNode* candidate_line = getFirstBadLine(line_to_start_line_search, line_length);
|
||||
// if we got the same line we started on, this means we couldn't fix it.
|
||||
if (candidate_line == last_line_complete) {
|
||||
candidate_line = nullptr; // so we say our candidate was bad and try to find another
|
||||
PrettyPrinterNode* next_line = getNextLine(line_to_start_line_search);
|
||||
if (next_line) {
|
||||
candidate_line = getFirstBadLine(next_line, line_length);
|
||||
}
|
||||
}
|
||||
if (!candidate_line)
|
||||
break;
|
||||
|
||||
// okay, we have a line which needs fixing.
|
||||
assert(!candidate_line->prev || candidate_line->prev->is_line_separator);
|
||||
PrettyPrinterNode* form_to_start = getFirstListOnLine(candidate_line);
|
||||
for (;;) {
|
||||
if (!form_to_start) {
|
||||
printf("pretty printer has failed. Fix the bug or increase the the line length.\n");
|
||||
assert(false);
|
||||
}
|
||||
breakList(form_to_start);
|
||||
propagatePretty(head, line_length);
|
||||
if (getFirstBadLine(candidate_line, line_length) != candidate_line) {
|
||||
break;
|
||||
}
|
||||
|
||||
form_to_start = getNextListOnLine(form_to_start);
|
||||
if (!form_to_start)
|
||||
break;
|
||||
}
|
||||
|
||||
last_line_complete = candidate_line;
|
||||
line_to_start_line_search = candidate_line;
|
||||
}
|
||||
}
|
||||
|
||||
static void insertSpecialBreaks(PrettyPrinterNode* node) {
|
||||
for (; node; node = node->next) {
|
||||
if (!node->is_line_separator && node->tok->kind == TokenKind::SYMBOL) {
|
||||
std::string& name = *node->tok->str;
|
||||
if (name == "deftype") {
|
||||
auto* parent_type_dec = getNextListOnLine(node);
|
||||
if (parent_type_dec) {
|
||||
insertNewlineAfter(parent_type_dec->paren, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string Form::toStringPretty(int indent, int line_length) {
|
||||
(void)indent;
|
||||
(void)line_length;
|
||||
std::vector<FormToken> tokens;
|
||||
toTokenList(tokens);
|
||||
assert(!tokens.empty());
|
||||
std::string pretty;
|
||||
|
||||
// build linked list of nodes
|
||||
PrettyPrinterNode* head = new PrettyPrinterNode(tokens[0]);
|
||||
PrettyPrinterNode* node = head;
|
||||
head->line = 0;
|
||||
head->offset = 0;
|
||||
head->lineIndent = 0;
|
||||
int offset = head->tok->toString().length();
|
||||
for (size_t i = 1; i < tokens.size(); i++) {
|
||||
node->next = new PrettyPrinterNode(tokens[i]);
|
||||
node->next->prev = node;
|
||||
node = node->next;
|
||||
node->line = 0;
|
||||
node->offset = offset;
|
||||
offset += node->tok->toString().length();
|
||||
node->lineIndent = 0;
|
||||
}
|
||||
|
||||
// attach parens.
|
||||
std::vector<PrettyPrinterNode*> parenStack;
|
||||
parenStack.push_back(nullptr);
|
||||
for (PrettyPrinterNode* n = head; n; n = n->next) {
|
||||
if (n->tok->kind == TokenKind::OPEN_PAREN) {
|
||||
parenStack.push_back(n);
|
||||
} else if (n->tok->kind == TokenKind::CLOSE_PAREN) {
|
||||
n->paren = parenStack.back();
|
||||
parenStack.back()->paren = n;
|
||||
parenStack.pop_back();
|
||||
} else {
|
||||
n->paren = parenStack.back();
|
||||
}
|
||||
}
|
||||
assert(parenStack.size() == 1);
|
||||
assert(!parenStack.back());
|
||||
|
||||
insertSpecialBreaks(head);
|
||||
propagatePretty(head, line_length);
|
||||
insertBreaksAsNeeded(head, line_length);
|
||||
|
||||
// write to string
|
||||
bool newline_prev = true;
|
||||
for (PrettyPrinterNode* n = head; n; n = n->next) {
|
||||
if (n->is_line_separator) {
|
||||
pretty.push_back('\n');
|
||||
newline_prev = true;
|
||||
} else {
|
||||
if (newline_prev) {
|
||||
pretty.append(n->lineIndent, ' ');
|
||||
newline_prev = false;
|
||||
if (n->tok->kind == TokenKind::WHITESPACE)
|
||||
continue;
|
||||
}
|
||||
pretty.append(n->tok->toString());
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (!head)
|
||||
break;
|
||||
auto* next = head->next;
|
||||
delete head;
|
||||
head = next;
|
||||
}
|
||||
|
||||
return pretty;
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> toForm(const std::string& str) {
|
||||
auto f = std::make_shared<Form>();
|
||||
f->kind = FormKind::SYMBOL;
|
||||
f->symbol = gSymbolTable.intern(str);
|
||||
return f;
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> buildList(std::shared_ptr<Form> form) {
|
||||
auto f = std::make_shared<Form>();
|
||||
f->kind = FormKind::PAIR;
|
||||
f->pair[0] = form;
|
||||
f->pair[1] = gSymbolTable.getEmptyPair();
|
||||
return f;
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> buildList(const std::string& str) {
|
||||
return buildList(toForm(str));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> buildList(std::shared_ptr<Form>* forms, int count) {
|
||||
auto f = std::make_shared<Form>();
|
||||
f->kind = FormKind::PAIR;
|
||||
f->pair[0] = forms[0];
|
||||
if (count - 1) {
|
||||
f->pair[1] = buildList(forms + 1, count - 1);
|
||||
} else {
|
||||
f->pair[1] = gSymbolTable.getEmptyPair();
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> buildList(std::vector<std::shared_ptr<Form>>& forms) {
|
||||
if (forms.empty()) {
|
||||
return gSymbolTable.getEmptyPair();
|
||||
}
|
||||
return buildList(forms.data(), forms.size());
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> buildList(std::vector<std::string>& forms) {
|
||||
if (forms.empty()) {
|
||||
return gSymbolTable.getEmptyPair();
|
||||
}
|
||||
std::vector<std::shared_ptr<Form>> f;
|
||||
for (auto& x : forms) {
|
||||
f.push_back(toForm(x));
|
||||
}
|
||||
return buildList(f.data(), f.size());
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef JAK2_DISASSEMBLER_LISPPRINT_H
|
||||
#define JAK2_DISASSEMBLER_LISPPRINT_H
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
/*!
|
||||
* What type of thing is it?
|
||||
*/
|
||||
enum class FormKind {
|
||||
SYMBOL,
|
||||
HEX_NUMBER,
|
||||
DECIMAL_NUMBER,
|
||||
BINARY_NUMBER,
|
||||
SIGNED_NUMBER,
|
||||
STRING,
|
||||
EMPTY_LIST,
|
||||
PAIR
|
||||
};
|
||||
|
||||
/*!
|
||||
* Tokens in a textual representation
|
||||
*/
|
||||
enum class TokenKind {
|
||||
WHITESPACE,
|
||||
SYMBOL,
|
||||
OPEN_PAREN,
|
||||
DOT,
|
||||
CLOSE_PAREN,
|
||||
EMPTY_PAIR,
|
||||
SPECIAL_SYMBOL
|
||||
};
|
||||
|
||||
/*!
|
||||
* Token in a text representation
|
||||
*/
|
||||
struct FormToken {
|
||||
explicit FormToken(TokenKind _kind, std::string* _str = nullptr) : kind(_kind), str(_str) {}
|
||||
|
||||
TokenKind kind;
|
||||
union {
|
||||
std::string* str;
|
||||
};
|
||||
|
||||
std::string toString() {
|
||||
std::string s;
|
||||
switch (kind) {
|
||||
case TokenKind::WHITESPACE:
|
||||
s.push_back(' ');
|
||||
break;
|
||||
case TokenKind::SYMBOL:
|
||||
s.append(*str);
|
||||
break;
|
||||
case TokenKind::OPEN_PAREN:
|
||||
s.push_back('(');
|
||||
break;
|
||||
case TokenKind::DOT:
|
||||
s.push_back('.');
|
||||
break;
|
||||
case TokenKind::CLOSE_PAREN:
|
||||
s.push_back(')');
|
||||
break;
|
||||
case TokenKind::EMPTY_PAIR:
|
||||
s.append("()");
|
||||
break;
|
||||
case TokenKind::SPECIAL_SYMBOL:
|
||||
s.append(*str);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("toString unknown token kind");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* S-Expression Form
|
||||
*/
|
||||
class Form {
|
||||
public:
|
||||
FormKind kind;
|
||||
|
||||
std::string* symbol;
|
||||
std::shared_ptr<Form> pair[2];
|
||||
|
||||
std::string toStringSimple();
|
||||
std::string toStringPretty(int indent = 0, int line_length = 80);
|
||||
void toTokenList(std::vector<FormToken>& tokens);
|
||||
|
||||
private:
|
||||
void buildStringSimple(std::string& str);
|
||||
};
|
||||
|
||||
/*!
|
||||
* Symbol table to reduce the number of strings everywhere.
|
||||
*/
|
||||
class SymbolTable {
|
||||
public:
|
||||
SymbolTable();
|
||||
std::string* intern(const std::string& str);
|
||||
~SymbolTable();
|
||||
std::shared_ptr<Form> getEmptyPair() { return empty_pair; }
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::string*> map;
|
||||
std::shared_ptr<Form> empty_pair;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Global symbol table used for the compiler/decompiler
|
||||
*/
|
||||
extern SymbolTable gSymbolTable;
|
||||
|
||||
std::shared_ptr<Form> toForm(const std::string& str); //
|
||||
|
||||
std::shared_ptr<Form> buildList(const std::string& str);
|
||||
std::shared_ptr<Form> buildList(std::shared_ptr<Form> form);
|
||||
std::shared_ptr<Form> buildList(std::vector<std::shared_ptr<Form>>& forms);
|
||||
std::shared_ptr<Form> buildList(std::shared_ptr<Form>* forms, int count);
|
||||
std::shared_ptr<Form> buildList(std::vector<std::string>& forms);
|
||||
|
||||
template <typename... Args>
|
||||
std::shared_ptr<Form> buildList(const std::string& str, Args... rest) {
|
||||
auto f = std::make_shared<Form>();
|
||||
f->kind = FormKind::PAIR;
|
||||
f->pair[0] = toForm(str);
|
||||
f->pair[1] = buildList(rest...);
|
||||
return f;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::shared_ptr<Form> buildList(std::shared_ptr<Form> car, Args... rest) {
|
||||
auto f = std::make_shared<Form>();
|
||||
f->kind = FormKind::PAIR;
|
||||
f->pair[0] = car;
|
||||
f->pair[1] = buildList(rest...);
|
||||
return f;
|
||||
}
|
||||
|
||||
#endif // JAK2_DISASSEMBLER_LISPPRINT_H
|
||||
@@ -60,8 +60,8 @@ void find_basic_blocks(RegAllocCache* cache, const AllocationInput& in) {
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
printf("[RegAlloc Error] couldn't find basic block beginning with instr %d of %lld\n", instr,
|
||||
in.instructions.size());
|
||||
printf("[RegAlloc Error] couldn't find basic block beginning with instr %d of %d\n", instr,
|
||||
int(in.instructions.size()));
|
||||
}
|
||||
assert(found);
|
||||
return result;
|
||||
|
||||
@@ -15,6 +15,7 @@ add_executable(goalc-test
|
||||
test_common_util.cpp
|
||||
test_compiler_and_runtime.cpp
|
||||
test_deftype.cpp
|
||||
test_pretty_print.cpp
|
||||
)
|
||||
|
||||
enable_testing()
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "common/goos/Reader.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
|
||||
using namespace goos;
|
||||
|
||||
namespace {
|
||||
Object read(const std::string& str) {
|
||||
auto body = pretty_print::get_pretty_printer_reader().read_from_string(str).as_pair()->cdr;
|
||||
EXPECT_TRUE(body.as_pair()->cdr.is_empty_list());
|
||||
return body.as_pair()->car;
|
||||
}
|
||||
|
||||
std::string pprint(const Object& o, int len = 80) {
|
||||
return pretty_print::to_string(o, len);
|
||||
}
|
||||
|
||||
// read then pretty print a string.
|
||||
std::string ppr(const std::string& in, int len = 80) {
|
||||
return pprint(read(in), len);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(PrettyPrinter, Basics) {
|
||||
EXPECT_EQ(ppr("test"), "test");
|
||||
EXPECT_EQ(ppr("(l 12 asdf)"), "(l 12 asdf)");
|
||||
|
||||
// force it to break
|
||||
EXPECT_EQ(ppr("(thing 12 asd asfd sdfjk)", 10), "(thing\n 12\n asd\n asfd\n sdfjk\n )");
|
||||
}
|
||||
|
||||
TEST(PrettyPrinter, ReadAgain) {
|
||||
// first read the gcommon file
|
||||
auto gcommon_code = pretty_print::get_pretty_printer_reader().read_from_file(
|
||||
{"goal_src", "kernel", "gcommon.gc"});
|
||||
// pretty print it
|
||||
auto printed_gcommon = pretty_print::to_string(gcommon_code);
|
||||
auto gcommon_code2 = pretty_print::get_pretty_printer_reader()
|
||||
.read_from_string(printed_gcommon)
|
||||
.as_pair()
|
||||
->cdr.as_pair()
|
||||
->car;
|
||||
auto printed_gcommon2 = pretty_print::to_string(gcommon_code);
|
||||
EXPECT_TRUE(gcommon_code == gcommon_code2);
|
||||
}
|
||||
|
||||
TEST(PrettyPrinter, ReadAgainVeryShortLines) {
|
||||
// first read the gcommon file
|
||||
auto gcommon_code = pretty_print::get_pretty_printer_reader().read_from_file(
|
||||
{"goal_src", "kernel", "gcommon.gc"});
|
||||
// pretty print it but with a very short line length. This looks terrible but will hopefully
|
||||
// hit many of the cases for line breaking.
|
||||
auto printed_gcommon = pretty_print::to_string(gcommon_code, 5);
|
||||
auto gcommon_code2 = pretty_print::get_pretty_printer_reader()
|
||||
.read_from_string(printed_gcommon)
|
||||
.as_pair()
|
||||
->cdr.as_pair()
|
||||
->car;
|
||||
auto printed_gcommon2 = pretty_print::to_string(gcommon_code);
|
||||
EXPECT_TRUE(gcommon_code == gcommon_code2);
|
||||
}
|
||||
Reference in New Issue
Block a user