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-exception 6 // 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/Twine.h" 16 #include "llvm/ADT/TypeSwitch.h" 17 #include "llvm/Support/raw_ostream.h" 18 19 using namespace toy; 20 21 namespace { 22 23 // RAII helper to manage increasing/decreasing the indentation as we traverse 24 // the AST 25 struct Indent { 26 Indent(int &level) : level(level) { ++level; } 27 ~Indent() { --level; } 28 int &level; 29 }; 30 31 /// Helper class that implement the AST tree traversal and print the nodes along 32 /// the way. The only data member is the current indentation level. 33 class ASTDumper { 34 public: 35 void dump(ModuleAST *node); 36 37 private: 38 void dump(const VarType &type); 39 void dump(VarDeclExprAST *varDecl); 40 void dump(ExprAST *expr); 41 void dump(ExprASTList *exprList); 42 void dump(NumberExprAST *num); 43 void dump(LiteralExprAST *node); 44 void dump(VariableExprAST *node); 45 void dump(ReturnExprAST *node); 46 void dump(BinaryExprAST *node); 47 void dump(CallExprAST *node); 48 void dump(PrintExprAST *node); 49 void dump(PrototypeAST *node); 50 void dump(FunctionAST *node); 51 52 // Actually print spaces matching the current indentation level 53 void indent() { 54 for (int i = 0; i < curIndent; i++) 55 llvm::errs() << " "; 56 } 57 int curIndent = 0; 58 }; 59 60 } // namespace 61 62 /// Return a formatted string for the location of any node 63 template <typename T> 64 static std::string loc(T *node) { 65 const auto &loc = node->loc(); 66 return (llvm::Twine("@") + *loc.file + ":" + llvm::Twine(loc.line) + ":" + 67 llvm::Twine(loc.col)) 68 .str(); 69 } 70 71 // Helper Macro to bump the indentation level and print the leading spaces for 72 // the current indentations 73 #define INDENT() \ 74 Indent level_(curIndent); \ 75 indent(); 76 77 /// Dispatch to a generic expressions to the appropriate subclass using RTTI 78 void ASTDumper::dump(ExprAST *expr) { 79 llvm::TypeSwitch<ExprAST *>(expr) 80 .Case<BinaryExprAST, CallExprAST, LiteralExprAST, NumberExprAST, 81 PrintExprAST, ReturnExprAST, VarDeclExprAST, VariableExprAST>( 82 [&](auto *node) { this->dump(node); }) 83 .Default([&](ExprAST *) { 84 // No match, fallback to a generic message 85 INDENT(); 86 llvm::errs() << "<unknown Expr, kind " << expr->getKind() << ">\n"; 87 }); 88 } 89 90 /// A variable declaration is printing the variable name, the type, and then 91 /// recurse in the initializer value. 92 void ASTDumper::dump(VarDeclExprAST *varDecl) { 93 INDENT(); 94 llvm::errs() << "VarDecl " << varDecl->getName(); 95 dump(varDecl->getType()); 96 llvm::errs() << " " << loc(varDecl) << "\n"; 97 dump(varDecl->getInitVal()); 98 } 99 100 /// A "block", or a list of expression 101 void ASTDumper::dump(ExprASTList *exprList) { 102 INDENT(); 103 llvm::errs() << "Block {\n"; 104 for (auto &expr : *exprList) 105 dump(expr.get()); 106 indent(); 107 llvm::errs() << "} // Block\n"; 108 } 109 110 /// A literal number, just print the value. 111 void ASTDumper::dump(NumberExprAST *num) { 112 INDENT(); 113 llvm::errs() << num->getValue() << " " << loc(num) << "\n"; 114 } 115 116 /// Helper to print recursively a literal. This handles nested array like: 117 /// [ [ 1, 2 ], [ 3, 4 ] ] 118 /// We print out such array with the dimensions spelled out at every level: 119 /// <2,2>[<2>[ 1, 2 ], <2>[ 3, 4 ] ] 120 void printLitHelper(ExprAST *litOrNum) { 121 // Inside a literal expression we can have either a number or another literal 122 if (auto *num = llvm::dyn_cast<NumberExprAST>(litOrNum)) { 123 llvm::errs() << num->getValue(); 124 return; 125 } 126 auto *literal = llvm::cast<LiteralExprAST>(litOrNum); 127 128 // Print the dimension for this literal first 129 llvm::errs() << "<"; 130 llvm::interleaveComma(literal->getDims(), llvm::errs()); 131 llvm::errs() << ">"; 132 133 // Now print the content, recursing on every element of the list 134 llvm::errs() << "[ "; 135 llvm::interleaveComma(literal->getValues(), llvm::errs(), 136 [&](auto &elt) { printLitHelper(elt.get()); }); 137 llvm::errs() << "]"; 138 } 139 140 /// Print a literal, see the recursive helper above for the implementation. 141 void ASTDumper::dump(LiteralExprAST *node) { 142 INDENT(); 143 llvm::errs() << "Literal: "; 144 printLitHelper(node); 145 llvm::errs() << " " << loc(node) << "\n"; 146 } 147 148 /// Print a variable reference (just a name). 149 void ASTDumper::dump(VariableExprAST *node) { 150 INDENT(); 151 llvm::errs() << "var: " << node->getName() << " " << loc(node) << "\n"; 152 } 153 154 /// Return statement print the return and its (optional) argument. 155 void ASTDumper::dump(ReturnExprAST *node) { 156 INDENT(); 157 llvm::errs() << "Return\n"; 158 if (node->getExpr().hasValue()) 159 return dump(*node->getExpr()); 160 { 161 INDENT(); 162 llvm::errs() << "(void)\n"; 163 } 164 } 165 166 /// Print a binary operation, first the operator, then recurse into LHS and RHS. 167 void ASTDumper::dump(BinaryExprAST *node) { 168 INDENT(); 169 llvm::errs() << "BinOp: " << node->getOp() << " " << loc(node) << "\n"; 170 dump(node->getLHS()); 171 dump(node->getRHS()); 172 } 173 174 /// Print a call expression, first the callee name and the list of args by 175 /// recursing into each individual argument. 176 void ASTDumper::dump(CallExprAST *node) { 177 INDENT(); 178 llvm::errs() << "Call '" << node->getCallee() << "' [ " << loc(node) << "\n"; 179 for (auto &arg : node->getArgs()) 180 dump(arg.get()); 181 indent(); 182 llvm::errs() << "]\n"; 183 } 184 185 /// Print a builtin print call, first the builtin name and then the argument. 186 void ASTDumper::dump(PrintExprAST *node) { 187 INDENT(); 188 llvm::errs() << "Print [ " << loc(node) << "\n"; 189 dump(node->getArg()); 190 indent(); 191 llvm::errs() << "]\n"; 192 } 193 194 /// Print type: only the shape is printed in between '<' and '>' 195 void ASTDumper::dump(const VarType &type) { 196 llvm::errs() << "<"; 197 llvm::interleaveComma(type.shape, llvm::errs()); 198 llvm::errs() << ">"; 199 } 200 201 /// Print a function prototype, first the function name, and then the list of 202 /// parameters names. 203 void ASTDumper::dump(PrototypeAST *node) { 204 INDENT(); 205 llvm::errs() << "Proto '" << node->getName() << "' " << loc(node) << "\n"; 206 indent(); 207 llvm::errs() << "Params: ["; 208 llvm::interleaveComma(node->getArgs(), llvm::errs(), 209 [](auto &arg) { llvm::errs() << arg->getName(); }); 210 llvm::errs() << "]\n"; 211 } 212 213 /// Print a function, first the prototype and then the body. 214 void ASTDumper::dump(FunctionAST *node) { 215 INDENT(); 216 llvm::errs() << "Function \n"; 217 dump(node->getProto()); 218 dump(node->getBody()); 219 } 220 221 /// Print a module, actually loop over the functions and print them in sequence. 222 void ASTDumper::dump(ModuleAST *node) { 223 INDENT(); 224 llvm::errs() << "Module:\n"; 225 for (auto &f : *node) 226 dump(&f); 227 } 228 229 namespace toy { 230 231 // Public API 232 void dump(ModuleAST &module) { ASTDumper().dump(&module); } 233 234 } // namespace toy 235