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(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 level |
58 | void indent() { |
59 | for (int i = 0; i < curIndent; i++) |
60 | llvm::errs() << " " ; |
61 | } |
62 | int curIndent = 0; |
63 | }; |
64 | |
65 | } // namespace |
66 | |
67 | /// Return a formatted string for the location of any node |
68 | template <typename T> |
69 | static 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 for |
77 | // the current indentations |
78 | #define INDENT() \ |
79 | Indent level_(curIndent); \ |
80 | indent(); |
81 | |
82 | /// Dispatch to a generic expressions to the appropriate subclass using RTTI |
83 | void ASTDumper::dump(ExprAST *expr) { |
84 | llvm::TypeSwitch<ExprAST *>(expr) |
85 | .Case<BinaryExprAST, CallExprAST, LiteralExprAST, NumberExprAST, |
86 | PrintExprAST, ReturnExprAST, StructLiteralExprAST, VarDeclExprAST, |
87 | VariableExprAST>(caseFn: [&](auto *node) { this->dump(node); }) |
88 | .Default(defaultFn: [&](ExprAST *) { |
89 | // No match, fallback to a generic message |
90 | 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 then |
96 | /// recurse in the initializer value. |
97 | void ASTDumper::dump(VarDeclExprAST *varDecl) { |
98 | INDENT(); |
99 | llvm::errs() << "VarDecl " << varDecl->getName(); |
100 | dump(type: varDecl->getType()); |
101 | llvm::errs() << " " << loc(node: varDecl) << "\n" ; |
102 | if (auto *initVal = varDecl->getInitVal()) |
103 | dump(expr: initVal); |
104 | } |
105 | |
106 | /// A "block", or a list of expression |
107 | void ASTDumper::dump(ExprASTList *exprList) { |
108 | INDENT(); |
109 | llvm::errs() << "Block {\n" ; |
110 | for (auto &expr : *exprList) |
111 | dump(expr: expr.get()); |
112 | indent(); |
113 | llvm::errs() << "} // Block\n" ; |
114 | } |
115 | |
116 | /// A literal number, just print the value. |
117 | void ASTDumper::dump(NumberExprAST *num) { |
118 | INDENT(); |
119 | llvm::errs() << num->getValue() << " " << loc(node: 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 ] ] |
126 | void printLitHelper(ExprAST *litOrNum) { |
127 | // Inside a literal expression we can have either a number or another literal |
128 | if (auto *num = llvm::dyn_cast<NumberExprAST>(Val: litOrNum)) { |
129 | llvm::errs() << num->getValue(); |
130 | return; |
131 | } |
132 | auto *literal = llvm::cast<LiteralExprAST>(Val: litOrNum); |
133 | |
134 | // Print the dimension for this literal first |
135 | llvm::errs() << "<" ; |
136 | llvm::interleaveComma(c: literal->getDims(), os&: llvm::errs()); |
137 | llvm::errs() << ">" ; |
138 | |
139 | // Now print the content, recursing on every element of the list |
140 | llvm::errs() << "[ " ; |
141 | llvm::interleaveComma(c: literal->getValues(), os&: llvm::errs(), |
142 | each_fn: [&](auto &elt) { printLitHelper(elt.get()); }); |
143 | llvm::errs() << "]" ; |
144 | } |
145 | |
146 | /// Print a literal, see the recursive helper above for the implementation. |
147 | void ASTDumper::dump(LiteralExprAST *node) { |
148 | INDENT(); |
149 | llvm::errs() << "Literal: " ; |
150 | printLitHelper(litOrNum: node); |
151 | llvm::errs() << " " << loc(node) << "\n" ; |
152 | } |
153 | |
154 | /// Print a struct literal. |
155 | void ASTDumper::dump(StructLiteralExprAST *node) { |
156 | INDENT(); |
157 | llvm::errs() << "Struct Literal: " ; |
158 | for (auto &value : node->getValues()) |
159 | dump(expr: value.get()); |
160 | indent(); |
161 | llvm::errs() << " " << loc(node) << "\n" ; |
162 | } |
163 | |
164 | /// Print a variable reference (just a name). |
165 | void 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. |
171 | void ASTDumper::dump(ReturnExprAST *node) { |
172 | INDENT(); |
173 | llvm::errs() << "Return\n" ; |
174 | if (node->getExpr().has_value()) |
175 | return dump(expr: *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. |
183 | void ASTDumper::dump(BinaryExprAST *node) { |
184 | INDENT(); |
185 | llvm::errs() << "BinOp: " << node->getOp() << " " << loc(node) << "\n" ; |
186 | dump(expr: node->getLHS()); |
187 | dump(expr: node->getRHS()); |
188 | } |
189 | |
190 | /// Print a call expression, first the callee name and the list of args by |
191 | /// recursing into each individual argument. |
192 | void ASTDumper::dump(CallExprAST *node) { |
193 | INDENT(); |
194 | llvm::errs() << "Call '" << node->getCallee() << "' [ " << loc(node) << "\n" ; |
195 | for (auto &arg : node->getArgs()) |
196 | dump(expr: arg.get()); |
197 | indent(); |
198 | llvm::errs() << "]\n" ; |
199 | } |
200 | |
201 | /// Print a builtin print call, first the builtin name and then the argument. |
202 | void ASTDumper::dump(PrintExprAST *node) { |
203 | INDENT(); |
204 | llvm::errs() << "Print [ " << loc(node) << "\n" ; |
205 | dump(expr: node->getArg()); |
206 | indent(); |
207 | llvm::errs() << "]\n" ; |
208 | } |
209 | |
210 | /// Print type: only the shape is printed in between '<' and '>' |
211 | void ASTDumper::dump(const VarType &type) { |
212 | llvm::errs() << "<" ; |
213 | if (!type.name.empty()) |
214 | llvm::errs() << type.name; |
215 | else |
216 | llvm::interleaveComma(c: type.shape, os&: llvm::errs()); |
217 | llvm::errs() << ">" ; |
218 | } |
219 | |
220 | /// Print a function prototype, first the function name, and then the list of |
221 | /// parameters names. |
222 | void ASTDumper::dump(PrototypeAST *node) { |
223 | INDENT(); |
224 | llvm::errs() << "Proto '" << node->getName() << "' " << loc(node) << "\n" ; |
225 | indent(); |
226 | llvm::errs() << "Params: [" ; |
227 | llvm::interleaveComma(c: node->getArgs(), os&: llvm::errs(), |
228 | each_fn: [](auto &arg) { llvm::errs() << arg->getName(); }); |
229 | llvm::errs() << "]\n" ; |
230 | } |
231 | |
232 | /// Print a function, first the prototype and then the body. |
233 | void ASTDumper::dump(FunctionAST *node) { |
234 | INDENT(); |
235 | llvm::errs() << "Function \n" ; |
236 | dump(node: node->getProto()); |
237 | dump(exprList: node->getBody()); |
238 | } |
239 | |
240 | /// Print a struct. |
241 | void 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(varDecl: 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. |
256 | void ASTDumper::dump(ModuleAST *node) { |
257 | INDENT(); |
258 | llvm::errs() << "Module:\n" ; |
259 | for (auto &record : *node) { |
260 | if (FunctionAST *function = llvm::dyn_cast<FunctionAST>(Val: record.get())) |
261 | dump(node: function); |
262 | else if (StructAST *str = llvm::dyn_cast<StructAST>(Val: record.get())) |
263 | dump(node: str); |
264 | else |
265 | llvm::errs() << "<unknown Record, kind " << record->getKind() << ">\n" ; |
266 | } |
267 | } |
268 | |
269 | namespace toy { |
270 | |
271 | // Public API |
272 | void dump(ModuleAST &module) { ASTDumper().dump(node: &module); } |
273 | |
274 | } // namespace toy |
275 | |