275 lines · cpp
1//===- AST.cpp - Helper for printing out the Toy AST ----------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the AST dump for the Toy language.10//11//===----------------------------------------------------------------------===//12 13#include "toy/AST.h"14 15#include "llvm/ADT/STLExtras.h"16#include "llvm/ADT/Twine.h"17#include "llvm/ADT/TypeSwitch.h"18#include "llvm/Support/Casting.h"19#include "llvm/Support/raw_ostream.h"20#include <string>21 22using namespace toy;23 24namespace {25 26// RAII helper to manage increasing/decreasing the indentation as we traverse27// the AST28struct Indent {29 Indent(int &level) : level(level) { ++level; }30 ~Indent() { --level; }31 int &level;32};33 34/// Helper class that implement the AST tree traversal and print the nodes along35/// the way. The only data member is the current indentation level.36class ASTDumper {37public:38 void dump(ModuleAST *node);39 40private:41 void dump(const VarType &type);42 void dump(VarDeclExprAST *varDecl);43 void dump(ExprAST *expr);44 void dump(ExprASTList *exprList);45 void dump(NumberExprAST *num);46 void dump(LiteralExprAST *node);47 void dump(StructLiteralExprAST *node);48 void dump(VariableExprAST *node);49 void dump(ReturnExprAST *node);50 void dump(BinaryExprAST *node);51 void dump(CallExprAST *node);52 void dump(PrintExprAST *node);53 void dump(PrototypeAST *node);54 void dump(FunctionAST *node);55 void dump(StructAST *node);56 57 // Actually print spaces matching the current indentation level58 void indent() {59 for (int i = 0; i < curIndent; i++)60 llvm::errs() << " ";61 }62 int curIndent = 0;63};64 65} // namespace66 67/// Return a formatted string for the location of any node68template <typename T>69static std::string loc(T *node) {70 const auto &loc = node->loc();71 return (llvm::Twine("@") + *loc.file + ":" + llvm::Twine(loc.line) + ":" +72 llvm::Twine(loc.col))73 .str();74}75 76// Helper Macro to bump the indentation level and print the leading spaces for77// the current indentations78#define INDENT() \79 Indent level_(curIndent); \80 indent();81 82/// Dispatch to a generic expressions to the appropriate subclass using RTTI83void ASTDumper::dump(ExprAST *expr) {84 llvm::TypeSwitch<ExprAST *>(expr)85 .Case<BinaryExprAST, CallExprAST, LiteralExprAST, NumberExprAST,86 PrintExprAST, ReturnExprAST, StructLiteralExprAST, VarDeclExprAST,87 VariableExprAST>([&](auto *node) { this->dump(node); })88 .Default([&](ExprAST *) {89 // No match, fallback to a generic message90 INDENT();91 llvm::errs() << "<unknown Expr, kind " << expr->getKind() << ">\n";92 });93}94 95/// A variable declaration is printing the variable name, the type, and then96/// recurse in the initializer value.97void ASTDumper::dump(VarDeclExprAST *varDecl) {98 INDENT();99 llvm::errs() << "VarDecl " << varDecl->getName();100 dump(varDecl->getType());101 llvm::errs() << " " << loc(varDecl) << "\n";102 if (auto *initVal = varDecl->getInitVal())103 dump(initVal);104}105 106/// A "block", or a list of expression107void ASTDumper::dump(ExprASTList *exprList) {108 INDENT();109 llvm::errs() << "Block {\n";110 for (auto &expr : *exprList)111 dump(expr.get());112 indent();113 llvm::errs() << "} // Block\n";114}115 116/// A literal number, just print the value.117void ASTDumper::dump(NumberExprAST *num) {118 INDENT();119 llvm::errs() << num->getValue() << " " << loc(num) << "\n";120}121 122/// Helper to print recursively a literal. This handles nested array like:123/// [ [ 1, 2 ], [ 3, 4 ] ]124/// We print out such array with the dimensions spelled out at every level:125/// <2,2>[<2>[ 1, 2 ], <2>[ 3, 4 ] ]126static void printLitHelper(ExprAST *litOrNum) {127 // Inside a literal expression we can have either a number or another literal128 if (auto *num = llvm::dyn_cast<NumberExprAST>(litOrNum)) {129 llvm::errs() << num->getValue();130 return;131 }132 auto *literal = llvm::cast<LiteralExprAST>(litOrNum);133 134 // Print the dimension for this literal first135 llvm::errs() << "<";136 llvm::interleaveComma(literal->getDims(), llvm::errs());137 llvm::errs() << ">";138 139 // Now print the content, recursing on every element of the list140 llvm::errs() << "[ ";141 llvm::interleaveComma(literal->getValues(), llvm::errs(),142 [&](auto &elt) { printLitHelper(elt.get()); });143 llvm::errs() << "]";144}145 146/// Print a literal, see the recursive helper above for the implementation.147void ASTDumper::dump(LiteralExprAST *node) {148 INDENT();149 llvm::errs() << "Literal: ";150 printLitHelper(node);151 llvm::errs() << " " << loc(node) << "\n";152}153 154/// Print a struct literal.155void ASTDumper::dump(StructLiteralExprAST *node) {156 INDENT();157 llvm::errs() << "Struct Literal: ";158 for (auto &value : node->getValues())159 dump(value.get());160 indent();161 llvm::errs() << " " << loc(node) << "\n";162}163 164/// Print a variable reference (just a name).165void ASTDumper::dump(VariableExprAST *node) {166 INDENT();167 llvm::errs() << "var: " << node->getName() << " " << loc(node) << "\n";168}169 170/// Return statement print the return and its (optional) argument.171void ASTDumper::dump(ReturnExprAST *node) {172 INDENT();173 llvm::errs() << "Return\n";174 if (node->getExpr().has_value())175 return dump(*node->getExpr());176 {177 INDENT();178 llvm::errs() << "(void)\n";179 }180}181 182/// Print a binary operation, first the operator, then recurse into LHS and RHS.183void ASTDumper::dump(BinaryExprAST *node) {184 INDENT();185 llvm::errs() << "BinOp: " << node->getOp() << " " << loc(node) << "\n";186 dump(node->getLHS());187 dump(node->getRHS());188}189 190/// Print a call expression, first the callee name and the list of args by191/// recursing into each individual argument.192void ASTDumper::dump(CallExprAST *node) {193 INDENT();194 llvm::errs() << "Call '" << node->getCallee() << "' [ " << loc(node) << "\n";195 for (auto &arg : node->getArgs())196 dump(arg.get());197 indent();198 llvm::errs() << "]\n";199}200 201/// Print a builtin print call, first the builtin name and then the argument.202void ASTDumper::dump(PrintExprAST *node) {203 INDENT();204 llvm::errs() << "Print [ " << loc(node) << "\n";205 dump(node->getArg());206 indent();207 llvm::errs() << "]\n";208}209 210/// Print type: only the shape is printed in between '<' and '>'211void ASTDumper::dump(const VarType &type) {212 llvm::errs() << "<";213 if (!type.name.empty())214 llvm::errs() << type.name;215 else216 llvm::interleaveComma(type.shape, llvm::errs());217 llvm::errs() << ">";218}219 220/// Print a function prototype, first the function name, and then the list of221/// parameters names.222void ASTDumper::dump(PrototypeAST *node) {223 INDENT();224 llvm::errs() << "Proto '" << node->getName() << "' " << loc(node) << "\n";225 indent();226 llvm::errs() << "Params: [";227 llvm::interleaveComma(node->getArgs(), llvm::errs(),228 [](auto &arg) { llvm::errs() << arg->getName(); });229 llvm::errs() << "]\n";230}231 232/// Print a function, first the prototype and then the body.233void ASTDumper::dump(FunctionAST *node) {234 INDENT();235 llvm::errs() << "Function \n";236 dump(node->getProto());237 dump(node->getBody());238}239 240/// Print a struct.241void ASTDumper::dump(StructAST *node) {242 INDENT();243 llvm::errs() << "Struct: " << node->getName() << " " << loc(node) << "\n";244 245 {246 INDENT();247 llvm::errs() << "Variables: [\n";248 for (auto &variable : node->getVariables())249 dump(variable.get());250 indent();251 llvm::errs() << "]\n";252 }253}254 255/// Print a module, actually loop over the functions and print them in sequence.256void ASTDumper::dump(ModuleAST *node) {257 INDENT();258 llvm::errs() << "Module:\n";259 for (auto &record : *node) {260 if (FunctionAST *function = llvm::dyn_cast<FunctionAST>(record.get()))261 dump(function);262 else if (StructAST *str = llvm::dyn_cast<StructAST>(record.get()))263 dump(str);264 else265 llvm::errs() << "<unknown Record, kind " << record->getKind() << ">\n";266 }267}268 269namespace toy {270 271// Public API272void dump(ModuleAST &module) { ASTDumper().dump(&module); }273 274} // namespace toy275