Joos1W Compiler Framework
All Classes Functions Typedefs Pages
ExprEvaluator.h
1 #pragma once
2 
3 #include <stack>
4 #include <vector>
5 
6 #include "ast/Expr.h"
7 #include "ast/ExprNode.h"
8 #include "diagnostics/Location.h"
9 
10 namespace ast {
11 template <typename T>
12 class ExprEvaluator {
13 protected:
14  using op_array = std::pmr::vector<T>;
15 
16  virtual T mapValue(exprnode::ExprValue& node) const = 0;
17  virtual T evalBinaryOp(exprnode::BinaryOp& op, const T lhs,
18  const T rhs) const = 0;
19  virtual T evalUnaryOp(exprnode::UnaryOp& op, const T rhs) const = 0;
20  virtual T evalMemberAccess(exprnode::MemberAccess& op, const T lhs,
21  const T field) const = 0;
22  virtual T evalMethodCall(exprnode::MethodInvocation& op, const T method,
23  const op_array& args) const = 0;
24  virtual T evalNewObject(exprnode::ClassInstanceCreation& op, const T object,
25  const op_array& args) const = 0;
26  virtual T evalNewArray(exprnode::ArrayInstanceCreation& op, const T type,
27  const T size) const = 0;
28  virtual T evalArrayAccess(exprnode::ArrayAccess& op, const T array,
29  const T index) const = 0;
30  virtual T evalCast(exprnode::Cast& op, const T type, const T value) const = 0;
31 
32  /**
33  * @brief Gets the location of the argument at the given index.
34  *
35  * @param arg_index The index of the argument.
36  * @return SourceLocation
37  */
38  SourceRange argLocation(int arg_index) {
39  // FIXME(kevin): Implement this
40  (void)arg_index;
41  return SourceRange{};
42  }
43 
44 public:
45  /**
46  * @brief Evaluates the given expression.
47  * @param expr The expression to evaluate.
48  */
49  T Evaluate(Expr* expr) { return EvaluateList(expr->list()); }
50 
51  /**
52  * @brief Evaluates the given subexpression.
53  * @param subexpr The list of expression nodes to evaluate.
54  */
55  virtual T EvaluateList(ExprNodeList subexpr) {
56  using namespace ast::exprnode;
57 
58  std::pmr::vector<T> op_args;
59  const auto getArgs = [&op_args, this](int nargs) {
60  op_args.clear();
61  for(int i = 0; i < nargs; ++i) op_args.push_back(popSafe());
62  };
63 
64  // Clear the stack
65  while(!op_stack_.empty()) popSafe();
66 
67  // Lock all the nodes
68  for(auto const* nodes : subexpr.nodes()) {
69  nodes->const_lock();
70  }
71 
72  // Evaluate the RPN expression
73  auto* node = subexpr.mut_head();
74  for(size_t i = 0; i < subexpr.size(); ++i) {
75  // Push on the location of the current node if it's a value
76  if(dyn_cast<ExprValue*>(node)) arg_locs_.push_back(node->location());
77  // We grab the next node because we will unlock the current node
78  auto next_node = node->mut_next();
79  node->const_unlock();
80  cur_op = dyn_cast<ExprOp*>(node);
81  if(auto* value = dyn_cast<ExprValue*>(node)) {
82  op_stack_.push(mapValue(*value));
83  } else if(auto* unary = dyn_cast<UnaryOp*>(node)) {
84  auto rhs = popSafe();
85  op_stack_.push(evalUnaryOp(*unary, rhs));
86  } else if(auto* binary = dyn_cast<BinaryOp*>(node)) {
87  auto rhs = popSafe();
88  auto lhs = popSafe();
89  op_stack_.push(evalBinaryOp(*binary, lhs, rhs));
90  } else if(auto op = dyn_cast<MemberAccess*>(node)) {
91  auto field = popSafe();
92  auto lhs = popSafe();
93  op_stack_.push(evalMemberAccess(*op, lhs, field));
94  } else if(auto* method = dyn_cast<MethodInvocation*>(node)) {
95  op_args.clear();
96  if(method->nargs() > 1) getArgs(method->nargs() - 1);
97  auto method_name = popSafe();
98  op_stack_.push(evalMethodCall(*method, method_name, op_args));
99  } else if(auto* newObj = dyn_cast<ClassInstanceCreation*>(node)) {
100  op_args.clear();
101  if(newObj->nargs() > 1) getArgs(newObj->nargs() - 1);
102  auto type = popSafe();
103  op_stack_.push(evalNewObject(*newObj, type, op_args));
104  } else if(auto op = dyn_cast<ArrayInstanceCreation*>(node)) {
105  auto size = popSafe();
106  auto type = popSafe();
107  op_stack_.push(evalNewArray(*op, type, size));
108  } else if(auto op = dyn_cast<ArrayAccess*>(node)) {
109  auto index = popSafe();
110  auto array = popSafe();
111  op_stack_.push(evalArrayAccess(*op, array, index));
112  } else if(auto op = dyn_cast<Cast*>(node)) {
113  auto value = popSafe();
114  auto type = popSafe();
115  op_stack_.push(evalCast(*op, type, value));
116  }
117  assert(validate(op_stack_.top()));
118  node = next_node;
119  if(cur_op) mergeLocations(cur_op->nargs());
120  }
121 
122  // Return the result
123  auto result = popSafe();
124  assert(op_stack_.empty() && "Stack not empty after evaluation");
125  return result;
126  }
127 
128 protected:
129  virtual bool validate(T const&) const { return true; }
130  virtual bool validatePop(T const&) const { return true; }
131  int opStackSize() const { return op_stack_.size(); }
132  /**
133  * @brief Gets the location of the argument at the given index.
134  * Note the 0th argument is the first argument, not the operator.
135  *
136  * @param argno The index of the argument.
137  * @return SourceRange The location of the argument.
138  */
139  SourceRange argLocation(int argno) const {
140  assert(argno < cur_op->nargs() && argno >= 0);
141  // Arguments are offset from the top of the stack
142  return arg_locs_[arg_locs_.size() - cur_op->nargs() + argno];
143  }
144 
145 private:
146  inline T popSafe() {
147  assert(!op_stack_.empty() && "Stack underflow");
148  T value = op_stack_.top();
149  op_stack_.pop();
150  assert(validatePop(value));
151  return value;
152  }
153  void mergeLocations(int num) {
154  SourceRange loc = arg_locs_.back();
155  arg_locs_.pop_back();
156  for(int i = 1; i < num; i++) {
157  loc = SourceRange::merge(loc, arg_locs_.back());
158  arg_locs_.pop_back();
159  }
160  arg_locs_.push_back(loc);
161  }
162 
163 private:
164  std::stack<T> op_stack_;
165  std::vector<SourceRange> arg_locs_;
166  ast::exprnode::ExprOp* cur_op;
167 };
168 } // namespace ast