text: //===- AST.cpp - Helper for printing out the Toy AST ----------------------===//
  //
  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  // See https://llvm.org/LICENSE.txt for license information.
  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  //
  //===----------------------------------------------------------------------===//
  //
  // This file implements the AST dump for the Toy language.
  //
  //===----------------------------------------------------------------------===//
  
  #include "toy/AST.h"
  
  #include "llvm/ADT/Twine.h"
  #include "llvm/ADT/TypeSwitch.h"
  #include "llvm/Support/raw_ostream.h"
  
  using namespace toy;
  
  namespace {
  
  // RAII helper to manage increasing/decreasing the indentation as we traverse
  // the AST
  struct Indent {
    Indent(int &level) : level(level) { ++level; }
    ~Indent() { --level; }
    int &level;
  };
  
  /// Helper class that implement the AST tree traversal and print the nodes along
  /// the way. The only data member is the current indentation level.
  class ASTDumper {
  public:
    void dump(ModuleAST *node);
  
  private:
    void dump(const VarType &type);
    void dump(VarDeclExprAST *varDecl);
    void dump(ExprAST *expr);
    void dump(ExprASTList *exprList);
    void dump(NumberExprAST *num);
    void dump(LiteralExprAST *node);
    void dump(VariableExprAST *node);
    void dump(ReturnExprAST *node);
    void dump(BinaryExprAST *node);
    void dump(CallExprAST *node);
    void dump(PrintExprAST *node);
    void dump(PrototypeAST *node);
    void dump(FunctionAST *node);
  
    // Actually print spaces matching the current indentation level
    void indent() {
      for (int i = 0; i < curIndent; i++)
        llvm::errs() << "  ";
    }
    int curIndent = 0;
  };
  
  } // namespace
  
  /// Return a formatted string for the location of any node
  template <typename T> static std::string loc(T *node) {
    const auto &loc = node->loc();
    return (llvm::Twine("@") + *loc.file + ":" + llvm::Twine(loc.line) + ":" +
            llvm::Twine(loc.col))
        .str();
  }
  
  // Helper Macro to bump the indentation level and print the leading spaces for
  // the current indentations
  #define INDENT()                                                               \
    Indent level_(curIndent);                                                    \
    indent();
  
  /// Dispatch to a generic expressions to the appropriate subclass using RTTI
  void ASTDumper::dump(ExprAST *expr) {
    llvm::TypeSwitch<ExprAST *>(expr)
        .Case<BinaryExprAST, CallExprAST, LiteralExprAST, NumberExprAST,
              PrintExprAST, ReturnExprAST, VarDeclExprAST, VariableExprAST>(
            [&](auto *node) { this->dump(node); })
        .Default([&](ExprAST *) {
          // No match, fallback to a generic message
          INDENT();
          llvm::errs() << "<unknown Expr, kind " << expr->getKind() << ">\n";
        });
  }
  
  /// A variable declaration is printing the variable name, the type, and then
  /// recurse in the initializer value.
  void ASTDumper::dump(VarDeclExprAST *varDecl) {
    INDENT();
    llvm::errs() << "VarDecl " << varDecl->getName();
    dump(varDecl->getType());
    llvm::errs() << " " << loc(varDecl) << "\n";
    dump(varDecl->getInitVal());
  }
  
  /// A "block", or a list of expression
  void ASTDumper::dump(ExprASTList *exprList) {
    INDENT();
    llvm::errs() << "Block {\n";
    for (auto &expr : *exprList)
      dump(expr.get());
    indent();
    llvm::errs() << "} // Block\n";
  }
  
  /// A literal number, just print the value.
  void ASTDumper::dump(NumberExprAST *num) {
    INDENT();
    llvm::errs() << num->getValue() << " " << loc(num) << "\n";
  }
  
  /// Helper to print recursively a literal. This handles nested array like:
  ///    [ [ 1, 2 ], [ 3, 4 ] ]
  /// We print out such array with the dimensions spelled out at every level:
  ///    <2,2>[<2>[ 1, 2 ], <2>[ 3, 4 ] ]
  void printLitHelper(ExprAST *litOrNum) {
    // Inside a literal expression we can have either a number or another literal
    if (auto num = llvm::dyn_cast<NumberExprAST>(litOrNum)) {
      llvm::errs() << num->getValue();
      return;
    }
    auto *literal = llvm::cast<LiteralExprAST>(litOrNum);
  
    // Print the dimension for this literal first
    llvm::errs() << "<";
    llvm::interleaveComma(literal->getDims(), llvm::errs());
    llvm::errs() << ">";
  
    // Now print the content, recursing on every element of the list
    llvm::errs() << "[ ";
    llvm::interleaveComma(literal->getValues(), llvm::errs(),
                          [&](auto &elt) { printLitHelper(elt.get()); });
    llvm::errs() << "]";
  }
  
  /// Print a literal, see the recursive helper above for the implementation.
  void ASTDumper::dump(LiteralExprAST *node) {
    INDENT();
    llvm::errs() << "Literal: ";
    printLitHelper(node);
    llvm::errs() << " " << loc(node) << "\n";
  }
  
  /// Print a variable reference (just a name).
  void ASTDumper::dump(VariableExprAST *node) {
    INDENT();
    llvm::errs() << "var: " << node->getName() << " " << loc(node) << "\n";
  }
  
  /// Return statement print the return and its (optional) argument.
  void ASTDumper::dump(ReturnExprAST *node) {
    INDENT();
    llvm::errs() << "Return\n";
    if (node->getExpr().hasValue())
      return dump(*node->getExpr());
    {
      INDENT();
      llvm::errs() << "(void)\n";
    }
  }
  
  /// Print a binary operation, first the operator, then recurse into LHS and RHS.
  void ASTDumper::dump(BinaryExprAST *node) {
    INDENT();
    llvm::errs() << "BinOp: " << node->getOp() << " " << loc(node) << "\n";
    dump(node->getLHS());
    dump(node->getRHS());
  }
  
  /// Print a call expression, first the callee name and the list of args by
  /// recursing into each individual argument.
  void ASTDumper::dump(CallExprAST *node) {
    INDENT();
    llvm::errs() << "Call '" << node->getCallee() << "' [ " << loc(node) << "\n";
    for (auto &arg : node->getArgs())
      dump(arg.get());
    indent();
    llvm::errs() << "]\n";
  }
  
  /// Print a builtin print call, first the builtin name and then the argument.
  void ASTDumper::dump(PrintExprAST *node) {
    INDENT();
    llvm::errs() << "Print [ " << loc(node) << "\n";
    dump(node->getArg());
    indent();
    llvm::errs() << "]\n";
  }
  
  /// Print type: only the shape is printed in between '<' and '>'
  void ASTDumper::dump(const VarType &type) {
    llvm::errs() << "<";
    llvm::interleaveComma(type.shape, llvm::errs());
    llvm::errs() << ">";
  }
  
  /// Print a function prototype, first the function name, and then the list of
  /// parameters names.
  void ASTDumper::dump(PrototypeAST *node) {
    INDENT();
    llvm::errs() << "Proto '" << node->getName() << "' " << loc(node) << "\n";
    indent();
    llvm::errs() << "Params: [";
    llvm::interleaveComma(node->getArgs(), llvm::errs(),
                          [](auto &arg) { llvm::errs() << arg->getName(); });
    llvm::errs() << "]\n";
  }
  
  /// Print a function, first the prototype and then the body.
  void ASTDumper::dump(FunctionAST *node) {
    INDENT();
    llvm::errs() << "Function \n";
    dump(node->getProto());
    dump(node->getBody());
  }
  
  /// Print a module, actually loop over the functions and print them in sequence.
  void ASTDumper::dump(ModuleAST *node) {
    INDENT();
    llvm::errs() << "Module:\n";
    for (auto &f : *node)
      dump(&f);
  }
  
  namespace toy {
  
  // Public API
  void dump(ModuleAST &module) { ASTDumper().dump(&module); }
  
  } // namespace toy
  