/* Resp: A programming language * Copyright (C) 2008-2009 David Robillard * * Resp is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * Resp is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General * Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Resp. If not, see . */ /** @file * @brief Pretty-print AST expressions */ #include #include "resp.hpp" ostream& print_to(ostream& out, const AST* ast, unsigned indent, CEnv* cenv, bool types); static void newline(ostream& out, unsigned indent) { out << endl; for (unsigned i = 0; i < indent; ++i) out << " "; } static inline void print_annotation(ostream& out, const AST* ast, unsigned indent, CEnv* cenv, bool print) { if (is_form(ast, "define")) return; if (print) { const AST* var = cenv->tenv.var(ast); if (var) { out << " :"; print_to(out, cenv->tsubst.apply(var), indent + 2, cenv, false); } } } ostream& print_list_one_line(ostream& out, const ATuple* tup, ATuple::const_iterator i, unsigned indent, CEnv* cenv, bool types, const ATuple* elemsT) { ATuple::const_iterator ti = elemsT ? elemsT->begin() : tup->end(); for (; i != tup->end(); ) { ATuple::const_iterator next = i; ++next; print_to(out, *i, indent, cenv, types); if (elemsT) { out << " :"; print_to(out, *ti++, indent, cenv, false); } if (next != tup->end()) out << " "; i = next; } return (out << ")"); } /** Print list arguments on separate lines, possibly in separated pairs, e.g.: * (elem one) * (elem two) * (elem three) * * or, if split_pairs is true: * * (elem one) * (elem two) * * (elem three) * (elem four) */ ostream& print_list(ostream& out, const ATuple* tup, ATuple::const_iterator i, unsigned indent, CEnv* cenv, bool types, bool split_pairs) { for (; i != tup->end(); ) { ATuple::const_iterator next = i; ++next; print_to(out, *i, indent, cenv, types); if (next != tup->end()) { newline(out, indent); print_to(out, *next++, indent, cenv, types); if (next != tup->end()) { if (split_pairs) newline(out, 0); newline(out, indent); } } i = next; } return out << ")"; } ostream& print_to(ostream& out, const AST* ast, unsigned indent, CEnv* cenv, bool types) { switch (ast->tag()) { case T_UNKNOWN: return out << "?"; case T_TVAR: return out << "?" << AType::var_id(ast); case T_TUPLE: { const ATuple* tup = ast->as_tuple(); out << "("; ATuple::const_iterator i = tup->begin(); if (i == tup->end()) return out << ")"; std::string form = ""; const ASymbol* sym = (*i)->to_symbol(); unsigned head_width = 1; bool head_is_list = (*i)->to_tuple(); if (sym) { form = sym->sym(); head_width = form.length() + 2; } print_to(out, *i++, indent + 1, cenv, types); if (i != tup->end()) { if (head_is_list) newline(out, indent + 1); else out << " "; } if (form == "define") { if (tup->rrst() && is_form(tup->frrst(), "lambda")) { // Abreviate (def (fn (...) ...)) out << "(" << (*i++) << " "; const ATuple* const fn = tup->frrst()->as_tuple(); const ATuple* const prot = fn->frst()->as_tuple(); if (types) { const ATuple* const fnT = cenv->type(fn)->as_tuple(); print_list_one_line(out, prot, prot->begin(), indent + 7, cenv, types, fnT->prot()); print_annotation(out, fn->list_last(), indent + head_width + 1, cenv, types); } else { print_list_one_line(out, prot, prot->begin(), indent + 7, cenv, types, NULL); } newline(out, indent + 2); print_list(out, fn, fn->iter_at(2), indent + 2, cenv, types, false); } else { const unsigned child_indent = indent + 2; out << (*i++); // Print symbol if (types) { print_annotation(out, tup->list_ref(2), indent + head_width + 1, cenv, true); newline(out, child_indent); } else if (tup->frrst()->to_tuple()) { newline(out, child_indent); } else { out << " "; } print_to(out, *i++, child_indent, cenv, types); } out << ")"; } else if (form == "def-type") { out << (*i++); newline(out, indent + 2); print_list(out, tup, i, indent + 2, cenv, types, false); } else if (form == "lambda") { // Print prototype (possibly with parameter type annotations) const ATuple* pat = (*i++)->as_tuple(); out << "("; const ATuple* type = types ? cenv->type(tup)->to_tuple() : NULL; const ATuple* protT = type ? type->prot() : NULL; print_list_one_line(out, pat, pat->begin(), indent + 2, cenv, types, protT); // Print body expression(s) indented on the following lines newline(out, indent + 2); print_list(out, tup, i, indent + 2, cenv, false, false); } else if (form == "if") { print_list(out, tup, i, indent + 4, cenv, types, true); } else if (form == "match") { out << (*i++); newline(out, indent + 2); print_list(out, tup, i, indent + 2, cenv, types, true); } else if (form == "do") { newline(out, indent + 2); print_list(out, tup, i, indent + 2, cenv, types, false); } else { // Print on multiple lines if list contains lists for (auto ti : *tup) { if (ti->to_tuple()) { print_list(out, tup, i, indent + head_width, cenv, types, false); return out; } } // Print on single line if list contains only atoms print_list_one_line(out, tup, i, indent + head_width, cenv, types, NULL); } return out; } case T_BOOL: return out << ((((const ALiteral*)ast)->val) ? "#t" : "#f"); case T_FLOAT: return out << showpoint << ((const ALiteral*)ast)->val; case T_INT32: return out << showpoint << ((const ALiteral*)ast)->val; case T_STRING: return out << '"' << ((const AString*)ast)->cppstr << '"'; case T_SYMBOL: case T_LITSYM: return out << ((const ASymbol*)ast)->sym(); case T_ELLIPSIS: return out << "..."; } return out << "?"; } ostream& operator<<(ostream& out, const AST* ast) { return print_to(out, ast, 0, NULL, false); } /// Pretty-print @a ast to @a out void pprint(ostream& out, const AST* ast, CEnv* cenv, bool types) { print_to(out, ast, 0, cenv, types); print_annotation(out, ast, 0, cenv, types); out << endl; if ((is_form(ast, "define") && is_form(ast->as_tuple()->frrst(), "lambda")) || is_form(ast, "fn-end") || is_form(ast, "def-type")) out << endl; }