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