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
22using namespace toy;
23
24namespace {
25
26// RAII helper to manage increasing/decreasing the indentation as we traverse
27// the AST
28struct 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.
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 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
68template <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 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
83void 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.
97void 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
107void 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.
117void 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 ] ]
126void 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.
147void 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.
155void 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).
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(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.
183void 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.
192void 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.
202void 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 '>'
211void 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.
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(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.
233void 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.
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(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.
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>(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
269namespace toy {
270
271// Public API
272void dump(ModuleAST &module) { ASTDumper().dump(node: &module); }
273
274} // namespace toy
275

source code of mlir/examples/toy/Ch7/parser/AST.cpp