This repository has been archived on 2024-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
pacciani/src/ast-printer.cpp
2018-09-16 14:24:27 +02:00

268 lines
6.7 KiB
C++

// Copyright 2017 the Monicelli project authors. All rights reserved.
// Use of this source code is governed by a GPLv3 license, see LICENSE.txt.
#include "ast-visitor.h"
#include "ast.h"
#include <iostream>
using namespace monicelli;
namespace {
static const char* baseTypeToString(const VarType::BaseType type) {
switch (type) {
#define RETURN_BASE_NAME(NAME, _1, _2, _3, _4, STRING) \
case VarType::NAME: \
return STRING;
BUILTIN_TYPES(RETURN_BASE_NAME)
#undef RETURN_BASE_NAME
default:
UNREACHABLE("Unhandled VarType::BaseType.");
}
}
static std::ostream& operator<<(std::ostream& stream, const VarType& type) {
stream << baseTypeToString(type.getBaseType());
if (type.isPointer()) {
stream << '*';
}
return stream;
}
class AstPrinter final : public ConstAstVisitor<AstPrinter, void> {
public:
AstPrinter(std::ostream& stream) : expression_level_(0), indent_level_(0), stream_(stream) {}
void visitModule(const Module* module) {
for (const Function* function : module->functions()) {
visitFunction(function);
}
if (module->hasEntryPoint()) {
visitFunction(module->getEntryPoint());
}
}
void visitFunction(const Function* function) {
stream() << function->getReturnType() << ' ';
if (function->isEntryPoint()) {
stream() << "main";
} else {
stream() << function->getName();
}
stream() << '(';
bool first = true;
for (const FunctionParam& param : function->params()) {
if (first) {
first = false;
} else {
stream() << ", ";
}
assert(!param.getType().isVoid());
stream() << param.getType() << ' ' << param.getArg().getName();
}
stream() << ')';
if (!function->body_empty()) {
stream(false) << " {\n";
IndentGuard guard(this);
for (const Statement* statement : function->body()) {
visit(statement);
stream() << '\n';
}
stream(false) << "}";
}
stream(false) << "\n\n";
}
void visitAssertStatement(const AssertStatement* s) {
stream() << "assert ";
visit(s->getExpression());
}
void visitInputStatement(const InputStatement* s) {
stream() << "read " << s->getVariable().getName();
}
void visitExpressionStatement(const ExpressionStatement* s) { visit(s->getExpression()); }
void visitAbortStatement(const AbortStatement*) { stream() << "abort"; }
void visitVardeclStatement(const VardeclStatement* s) {
assert(!s->getType().isVoid());
stream() << s->getType() << ' ' << s->getVariable().getName();
if (s->hasInitializer()) {
stream(false) << " = ";
visit(s->getInitializer());
}
}
void visitBranchStatement(const BranchStatement* branch) {
stream() << "branch " << branch->getLeadVariable().getName() << " {\n";
{
IndentGuard guard(this);
for (const BranchCase& c : branch->cases()) {
stream() << "case";
visit(c.getExpression());
stream(false) << " {\n";
{
IndentGuard guard(this);
for (const Statement* s : c.body()) {
visit(s);
stream(false) << '\n';
}
}
stream() << "}\n";
}
if (branch->hasBranchElse()) {
stream() << "else {\n";
{
IndentGuard guard(this);
for (const Statement* s : branch->getBranchElse()->body()) {
visit(s);
stream(false) << '\n';
}
}
stream() << "}\n";
}
}
stream() << "}";
}
void visitLoopStatement(const LoopStatement* s) {
stream() << "do {\n";
{
IndentGuard guard(this);
for (const Statement* is : s->body()) {
visit(is);
stream(false) << '\n';
}
}
stream() << "} while ";
visit(s->getCondition());
}
void visitReturnStatement(const ReturnStatement* s) {
ExpressionNestingGuard guard{this};
stream() << "return";
if (s->hasExpression()) {
stream(false) << ' ';
visit(s->getExpression());
}
}
void visitPrintStatement(const PrintStatement* s) {
ExpressionNestingGuard guard{this};
stream() << "print ";
visit(s->getExpression());
}
void visitAssignStatement(const AssignStatement* s) {
ExpressionNestingGuard guard{this};
stream() << s->getVariable().getName() << " = ";
visit(s->getExpression());
}
void visitAtomicExpression(const AtomicExpression* s) {
ExpressionNestingGuard guard{this};
switch (s->getType()) {
case AtomicExpression::FLOAT:
stream(false) << s->getFloatValue();
break;
case AtomicExpression::INTEGER:
stream(false) << s->getIntValue();
break;
case AtomicExpression::IDENTIFIER:
stream(false) << s->getIdentifierValue().getName();
break;
default:
UNREACHABLE("Unhanlded AtomicExpression type.");
}
}
void visitBinaryExpression(const BinaryExpression* s) {
if (isNestedExpression()) stream(false) << '(';
{
ExpressionNestingGuard guard{this};
if (!s->isSemiExpression()) visit(s->getLeft());
stream(false) << ' ' << s->getOperatorRepresentation() << ' ';
visit(s->getRight());
}
if (isNestedExpression()) stream(false) << ')';
}
void visitFunctionCallExpression(const FunctionCallExpression* s) {
stream(!isNestedExpression()) << s->getFunctionName() << '(';
ExpressionNestingGuard guard{this};
bool first = true;
for (const Expression* arg : s->args()) {
if (first) {
first = false;
} else {
stream(false) << ", ";
}
visit(arg);
}
stream(false) << ')';
}
private:
class IndentGuard final {
public:
IndentGuard(AstPrinter* printer) : printer_(printer) { printer_->increaseIndent(); }
~IndentGuard() { printer_->decreaseIndent(); }
private:
AstPrinter* printer_;
};
class ExpressionNestingGuard final {
public:
ExpressionNestingGuard(AstPrinter* printer) : printer_(printer) {
++printer->expression_level_;
}
~ExpressionNestingGuard() { --printer_->expression_level_; }
private:
AstPrinter* printer_;
};
bool isNestedExpression() const { return expression_level_ > 0; }
void increaseIndent() { indent_level_ += 2; }
void decreaseIndent() {
if (indent_level_ >= 2) indent_level_ -= 2;
}
std::ostream& stream(bool indent = true) {
if (indent) {
static const char* spaces = " ";
for (int i = 0; i < indent_level_ / 32; ++i) {
stream_ << spaces;
}
stream_.write(spaces, indent_level_ % 32);
}
return stream_;
}
int expression_level_;
int indent_level_;
std::ostream& stream_;
};
} // namespace
namespace monicelli {
void printAst(std::ostream& stream, const AstNode* node) {
AstPrinter printer{stream};
printer.visit(node);
}
} // namespace monicelli