Joos1W Compiler Framework
All Classes Functions Typedefs Pages
Expr.cc
1 #include <ast/AST.h>
2 #include <ast/Expr.h>
3 
4 #include <cctype>
5 #include <ostream>
6 
7 #include "ast/ExprNode.h"
8 
9 namespace ast {
10 
11 std::ostream& Expr::print(std::ostream& os, int indentation) const {
12  if(indentation >= 0) {
13  os << AstNode::indent(indentation);
14  os << "Expr {\n";
15  }
16  os << AstNode::indent(indentation + 1);
17  char state = '(';
18  for(const auto op : rpn_ops.nodes()) {
19  if(indentation >= 0) {
20  std::ostringstream oss;
21  op->print(oss);
22  char cur = (oss.str().begin()[0] == '(') ? '(' : '.';
23  if(cur == state) {
24  os << oss.str() << " ";
25  } else {
26  os << "\n" << AstNode::indent(indentation + 1) << oss.str() << " ";
27  }
28  state = cur;
29  } else {
30  op->print(os);
31  os << "\n";
32  }
33  }
34  if(indentation >= 0) {
35  os << "\n" << AstNode::indent(indentation) << "}\n";
36  }
37  return os;
38 }
39 
40 int Expr::printDotNode(DotPrinter& dp) const {
41  std::ostringstream os;
42  for(const auto op : rpn_ops.nodes()) {
43  op->print(os);
44  if(os.str().ends_with(")")) {
45  os << "\n";
46  } else {
47  os << " ";
48  }
49  }
50  dp.sanitize(os.str());
51  return -1;
52 }
53 
54 void Expr::dump() const { this->print(std::cerr, 0); }
55 
56 /* ===--------------------------------------------------------------------=== */
57 // ExprNode Subclasses
58 /* ===--------------------------------------------------------------------=== */
59 
60 void ExprNode::dump() const { this->print(std::cerr) << "\n"; }
61 
62 void ExprNodeList::dump() const { print(std::cerr); }
63 
64 std::ostream& ExprNodeList::print(std::ostream& os) const {
65  for(auto node : nodes()) {
66  node->print(os);
67  os << " ";
68  }
69  os << "\n";
70  return os;
71 }
72 
73 void ExprNodeList::check_invariants() const {
74  assert((!tail_ || tail_->next() == nullptr) &&
75  "Tail node should not have a next node");
76  assert(((head_ != nullptr) == (tail_ != nullptr)) &&
77  "Head is null if and only if tail is null");
78  assert(((head_ == nullptr) == (size_ == 0)) &&
79  "Size should be 0 if and only if head is null");
80 }
81 
82 namespace exprnode {
83 
84 std::ostream& MemberName::print(std::ostream& os) const {
85  return os << "(MemberName " << name_ << ")";
86 }
87 
88 std::ostream& MethodName::print(std::ostream& os) const {
89  return os << "(MethodName " << name() << ")";
90 }
91 
92 std::ostream& ThisNode::print(std::ostream& os) const { return os << "(THIS)"; }
93 
94 std::ostream& TypeNode::print(std::ostream& os) const {
95  os << "(Type ";
96  if(type())
97  type()->print(os);
98  else
99  os << "unresolved ❌";
100  return os << ")";
101 }
102 
103 static uint8_t parseChar(std::string_view value) {
104  // Consume ' first, next value is either \ or a character
105  // If it is a character, just return that character
106  if(value.at(1) != '\\') return (uint8_t)value.at(1);
107  // Here, we have an escape sequence, let's first handle the octal case
108  if(isdigit(value.at(2))) {
109  // We have an octal escape sequence of 1 to 3 digits
110  uint8_t octal[3] = {0, 0, 0};
111  // 1 digit '\0
112  octal[0] = value.at(2) - '0';
113  // 2 digits '\00
114  if(value.length() >= 4) octal[1] = value.at(3) - '0';
115  // 3 digits '\000
116  if(value.length() >= 5) octal[2] = value.at(4) - '0';
117  // Must consume ' then return the character
118  return (uint8_t)((octal[0] << 6) | (octal[1] << 3) | octal[2]);
119  }
120  // Here, we have a non-octal escape sequence
121  switch(value.at(2)) {
122  case 'n':
123  return '\n';
124  case 't':
125  return '\t';
126  case 'r':
127  return '\r';
128  case 'b':
129  return '\b';
130  case 'f':
131  return '\f';
132  case '\\':
133  return '\\';
134  case '\'':
135  return '\'';
136  case '\"':
137  return '\"';
138  default:
139  assert(false && "Invalid escape sequence");
140  }
141 }
142 
143 static void unescapeString(std::string_view in, std::pmr::string& out) {
144  // FIXME(kevin): String literals are broken for now :)
145  for(size_t i = 1; i < in.length(); i++) {
146  char c = in.at(i);
147  if(c == '\"') {
148  break;
149  } else {
150  out.push_back(c);
151  }
152  }
153 }
154 
155 LiteralNode::LiteralNode(BumpAllocator& alloc, parsetree::Literal const* node,
156  ast::BuiltInType* type, SourceRange loc)
157  : ExprValue{loc, reinterpret_cast<ast::Type*>(type)} {
158  auto str = node->get_value();
159 
160  // 1. Check if the type is numeric
161  if(type->isNumeric()) {
162  uint32_t value = 0;
163  if(type->getKind() == ast::BuiltInType::Kind::Char) {
164  value = parseChar(str);
165  } else {
166  // Convert the string to an integer
167  try {
168  if(node->isNegative())
169  value = std::stoi("-" + std::string(str));
170  else
171  value = std::stoi(std::string(str));
172  } catch(std::invalid_argument& e) {
173  assert(false && "Invalid integer literal");
174  }
175  }
176  value_ = value;
177  }
178  // 2. Otherwise, check if the type is boolean
179  else if(type->isBoolean()) {
180  if(str == "true") {
181  value_ = 1U;
182  } else if(str == "false") {
183  value_ = 0U;
184  } else {
185  assert(false && "Invalid boolean literal");
186  }
187  }
188  // 3. Otherwise, its a string
189  else if(type->isString()) {
190  // Unescape the string
191  value_ = std::pmr::string{alloc};
192  unescapeString(str, std::get<std::pmr::string>(value_));
193  }
194  // 4. Maybe it's a NoneType (i.e., NULL)
195  else if(type->getKind() == ast::BuiltInType::Kind::NoneType) {
196  value_ = 0U;
197  }
198  // 5. Otherwise, it's an invalid type
199  else {
200  assert(false && "Invalid type for literal node");
201  }
202 }
203 
204 std::ostream& LiteralNode::print(std::ostream& os) const {
205  // TODO(kevin): re-implement this
206  os << "(Literal ";
207  builtinType()->print(os) << ")";
208  return os;
209 }
210 
211 ast::BuiltInType const* LiteralNode::builtinType() const {
212  return cast<ast::BuiltInType>(type());
213 }
214 
215 std::ostream& MemberAccess::print(std::ostream& os) const {
216  return os << "MemberAccess";
217 }
218 
219 std::ostream& MethodInvocation::print(std::ostream& os) const {
220  return os << "MethodInvocation(" << nargs() - 1 << ")";
221 }
222 
223 std::ostream& ClassInstanceCreation::print(std::ostream& os) const {
224  return os << "(ClassInstanceCreation args: " << std::to_string(nargs()) << ")";
225 }
226 
227 std::ostream& ArrayInstanceCreation::print(std::ostream& os) const {
228  return os << "ArrayInstanceCreation";
229 }
230 
231 std::ostream& ArrayAccess::print(std::ostream& os) const {
232  return os << "ArrayAccess";
233 }
234 
235 std::ostream& Cast::print(std::ostream& os) const { return os << "Cast"; }
236 
237 std::ostream& UnaryOp::print(std::ostream& os) const {
238  return os << OpType_to_string(type, "(Unknown unary op)");
239 }
240 
241 std::ostream& BinaryOp::print(std::ostream& os) const {
242  return os << OpType_to_string(type, "(Unknown binary op)");
243 }
244 
245 } // namespace exprnode
246 
247 } // namespace ast