Joos1W Compiler Framework
All Classes Functions Typedefs Pages
AstNode.h
1 #pragma once
2 
3 #include <iostream>
4 #include <ranges>
5 #include <string>
6 #include <vector>
7 
8 #include "diagnostics/Location.h"
9 #include "parsetree/ParseTree.h"
10 #include "utils/BumpAllocator.h"
11 #include "utils/DotPrinter.h"
12 #include "utils/Generator.h"
13 #include "utils/Utils.h"
14 
15 namespace ast {
16 
17 using utils::DotPrinter;
18 
19 template <typename T>
20 using pmr_vector = std::pmr::vector<T>;
21 template <typename T>
22 using array_ref = std::pmr::vector<T>&;
23 using std::string_view;
24 
25 class Type;
26 class BuiltInType;
27 class Decl;
28 class DeclContext;
29 class Stmt;
30 class ClassDecl;
31 
32 class Expr;
33 
34 /* ===--------------------------------------------------------------------=== */
35 // AstNode
36 /* ===--------------------------------------------------------------------=== */
37 
38 /// @brief Base class for all AST nodes. Helps unify printing and dot printing.
39 class AstNode {
40  friend class Expr;
41 
42 public:
43  AstNode() = default;
44  AstNode(const AstNode&) = delete;
45  AstNode(AstNode&&) = delete;
46  AstNode& operator=(const AstNode&) = delete;
47  AstNode& operator=(AstNode&&) = delete;
48 
49 public:
50  std::ostream& printDot(std::ostream& os) const {
51  DotPrinter dp{os};
52  dp.startGraph();
53  dp.print("compound=true;");
54  printDotNode(dp);
55  dp.endGraph();
56  return os;
57  }
58 
59  virtual std::ostream& print(std::ostream& os, int indentation = 0) const = 0;
60  virtual ~AstNode() = default;
61  virtual int printDotNode(DotPrinter& dp) const = 0;
62  void dump() const;
63  /// @brief Returns a generator for the children of this node.
64  virtual utils::Generator<AstNode const*> children() const = 0;
65  /// @brief Returns a generator for the mutable children of this node.
67  for(auto const* child : children()) co_yield const_cast<AstNode*>(child);
68  }
69 
70 protected:
71  /**
72  * @brief Get a string of spaces for indentation
73  *
74  * @param indentation The level of indentation
75  * @return std::string String of spaces
76  */
77  static std::string indent(int indentation) {
78  return std::string(indentation * 2, ' ');
79  }
80 };
81 
82 /* ===--------------------------------------------------------------------=== */
83 // Decl
84 /* ===--------------------------------------------------------------------=== */
85 
86 /// @brief Base class for all declarations.
87 class Decl : public virtual AstNode {
88 public:
89  Decl(BumpAllocator& alloc, std::string_view name) noexcept
90  : canonicalName_{alloc}, name_{name, alloc}, parent_{nullptr} {}
91 
92  /// @brief Gets the simple name of this declaration.
93  std::string_view name() const { return name_; }
94  /// @brief Gets the context in which this declaration is declared.
95  DeclContext* parent() const { return parent_; }
96  /// @brief Sets the parent. See parent().
97  virtual void setParent(DeclContext* parent) {
98  assert(parent_ == nullptr);
99  parent_ = parent;
100  }
101  /// @brief Gets the fully qualified name of this declaration. Returns
102  /// undefined value if the declaration does not have a canonical name.
103  std::string_view getCanonicalName() const {
104  assert(hasCanonicalName() && "Does not have a canonical name.");
105  assert(parent_ != nullptr && "Canonical name requires a non-null parent.");
106  return canonicalName_;
107  }
108  /// @brief Returns if the declaration has a canonical name.
109  virtual bool hasCanonicalName() const = 0;
110  /// @brief Returns the location of the declaration. This is an abstract
111  /// method to allow abstract classes of Decl without location.
112  virtual SourceRange location() const = 0;
113  /// @brief
114  virtual DeclContext const* asDeclContext() const { return nullptr; }
115 
116 protected:
117  std::pmr::string canonicalName_;
118 
119 private:
120  std::pmr::string name_;
121  DeclContext* parent_;
122 };
123 
124 /* ===--------------------------------------------------------------------=== */
125 // DeclContext
126 /* ===--------------------------------------------------------------------=== */
127 
128 /// @brief Base class for all declaration contexts (i.e., methods).
129 class DeclContext : public virtual AstNode {
130 public:
131  /// @brief Generator to yield all children decls of the context.
132  virtual utils::Generator<ast::Decl const*> decls() const {
133  for(auto child : children()) {
134  if(auto decl = dyn_cast_or_null<Decl>(child)) {
135  assert(decl->parent() == this &&
136  "child declaration of this context has wrong the parent!");
137  co_yield decl;
138  }
139  }
140  }
141 
142  /// @brief
143  virtual Decl const* asDecl() const { return nullptr; }
144 };
145 
146 /* ===--------------------------------------------------------------------=== */
147 // Type
148 /* ===--------------------------------------------------------------------=== */
149 
150 class UnresolvedType;
151 
152 /// @brief Abstract base representing a (stateful) class used to resolve types.
154 public:
155  virtual void ResolveType(UnresolvedType* type) = 0;
156 };
157 
158 /// @brief Base class for all types.
159 class Type : public virtual AstNode {
160 public:
161  Type(SourceRange loc) : loc_{loc} {}
162  virtual string_view toString() const = 0;
163  virtual std::ostream& print(std::ostream& os,
164  int indentation = 0) const override = 0;
165  int printDotNode(DotPrinter& dp) const override {
166  int id = dp.id();
167  dp.printLabel(id, toString());
168  return id;
169  }
170  SourceRange location() const { return loc_; }
171  /// @brief Resolves the type based on the condition of isResolved()
172  virtual void resolve(TypeResolver&) {}
173  /// @brief Returns if the type is resolved
174  virtual bool isResolved() const = 0;
175  virtual bool operator==(const Type&) const = 0;
176  virtual bool operator!=(const Type& other) { return !(*this == other); }
177  /// @brief Since there is no child, this returns an empty generator.
178  /// Note: We don't count the cross-reference to the declaration as a child
179  /// as that would violate the "tree" part of AST.
180  utils::Generator<AstNode const*> children() const override final {
181  co_yield nullptr;
182  }
183 
184  virtual bool isInvalid() const { return false; }
185  virtual bool isNumeric() const { return false; }
186  virtual bool isBoolean() const { return false; }
187  virtual bool isNull() const { return false; }
188  virtual bool isString() const { return false; }
189  virtual bool isArray() const { return false; }
190  virtual bool isPrimitive() const { return false; }
191  virtual bool isReference() const { return false; }
192 
193  virtual ast::Decl const* getAsDecl() const { return nullptr; }
194 
195 private:
196  SourceRange loc_;
197 };
198 
199 /* ===--------------------------------------------------------------------=== */
200 // Stmt
201 /* ===--------------------------------------------------------------------=== */
202 
203 /// @brief Base class for all statements.
204 class Stmt : public virtual AstNode {
205 public:
206  /// @brief By default, returns an empty generator for the statement.
207  virtual utils::Generator<AstNode const*> children() const override {
208  co_yield nullptr;
209  }
210  /// @brief Returns all the expressions in the statement.
211  virtual utils::Generator<Expr const*> exprs() const = 0;
212  utils::Generator<Expr*> mut_exprs() {
213  for(auto const* expr : exprs()) co_yield const_cast<Expr*>(expr);
214  }
215 };
216 
217 /* ===--------------------------------------------------------------------=== */
218 // Misc functions
219 /* ===--------------------------------------------------------------------=== */
220 
221 /// @brief Overload the << operator for AstNode to print the node
222 std::ostream& operator<<(std::ostream& os, const AstNode& astNode);
223 
224 /**
225  * @brief Prints the dot node for each item in the range. The connections
226  * are then formed as first -> second -> third -> fourth -> ...
227  * and the ID of the first node is returned.
228  *
229  * @tparam Range This is inferred
230  * @param dp The DotPrinter
231  * @param range The range must be an iterable of ast::AstNode*
232  * @return The ID of the first node
233  */
234 template <std::ranges::range Range>
235  requires std::is_convertible_v<std::ranges::range_value_t<Range>, ast::AstNode*>
236 int printDotNodeList(DotPrinter& dp, Range&& range) {
237  int childIdFirst = -1;
238  int childIdLast = -1;
239  for(auto p : range) {
240  int childId = p->printDotNode(dp);
241  if(childIdLast != -1)
242  dp.printConnection(childIdLast, childId);
243  else
244  childIdFirst = childId;
245  childIdLast = childId;
246  }
247  return childIdFirst;
248 }
249 
250 /**
251  * @brief Draws either a single statement node or a subgraph of statements
252  * if the statement is a block statement. Returns the ID of the first node
253  * and the ID of the subgraph if it is a block statement.
254  *
255  * @param dp The DotPrinter
256  * @param stmt The statement to draw
257  * @return std::pair<int, int> Returns -1 if stmt is not a block statement
258  */
259 std::pair<int, int> printStmtSubgraph(utils::DotPrinter& dp, ast::Stmt* stmt);
260 
261 /* ===--------------------------------------------------------------------=== */
262 // Modifiers
263 /* ===--------------------------------------------------------------------=== */
264 
265 class Modifiers {
266 public:
267  enum class Type {
268  Public = 0,
269  Protected = 1,
270  Static = 2,
271  Final = 3,
272  Abstract = 4,
273  Native = 5,
274  NumModifiers = 6
275  };
276 
277 public:
278  /// @brief Will clear + set the modifier given a parsetree::Modifier
279  /// Will also set the location of the modifier.
280  /// @param modifier The modifier to assign to this Modifiers object
281  void set(parsetree::Modifier target);
282 
283  /// @brief Will union the modifier with the current modifiers
284  /// @param modifier The modifier to union with this Modifiers object
285  /// @return True if the modifier was already set
286  bool set(ast::Modifiers::Type target) {
287  bool wasSet = test(modifiers, target);
288  modifiers |= (1 << (uint8_t)target);
289  return wasSet;
290  }
291 
292  /// @brief Will union the modifiers with the current modifiers
293  /// @param target The set of modifiers to union
294  /// @return True if any of the modifiers were already set
295  bool set(ast::Modifiers target) {
296  bool wasSet = false;
297  for(int i = 0; i < (int)Type::NumModifiers; i++) {
298  if(test(target.modifiers, (Type)i)) {
299  wasSet |= set((Type)i);
300  }
301  }
302  return wasSet;
303  }
304 
305  /// @brief Returns an iterator over the locations of the modifiers that
306  /// are set in both this Modifiers object and the given Modifiers object.
307  /// @param target The set of modifiers to intersect
308  auto getLocationsMasked(Modifiers target) const {
309  auto masked = target.modifiers & modifiers;
310  return std::views::iota(0, (int)Type::NumModifiers) |
311  std::views::filter(
312  [masked](int i) { return (masked & (1 << i)) != 0; }) |
313  std::views::transform([this](int i) { return modifierLocations[i]; });
314  }
315 
316  /// @brief Returns the location of the given modifier. Returns an
317  /// undefined location if the modifier is not set.
318  auto getLocation(Type modifier) const {
319  return modifierLocations[(int)modifier];
320  }
321 
322  bool isPublic() const { return test(modifiers, Type::Public); }
323  bool isProtected() const { return test(modifiers, Type::Protected); }
324  bool isStatic() const { return test(modifiers, Type::Static); }
325  bool isFinal() const { return test(modifiers, Type::Final); }
326  bool isAbstract() const { return test(modifiers, Type::Abstract); }
327  bool isNative() const { return test(modifiers, Type::Native); }
328 
329  std::string toString() const;
330 
331 private:
332  SourceRange modifierLocations[(int)Type::NumModifiers];
333  uint8_t modifiers = 0;
334 
335  static constexpr int test(uint8_t value, Type bit) {
336  return (value & (1 << (uint8_t)bit)) != 0;
337  }
338 };
339 
340 /* ===--------------------------------------------------------------------=== */
341 // ScopeID
342 /* ===--------------------------------------------------------------------=== */
343 
344 /**
345  * @brief Immutable struct that represents a unique identifier for a scope.
346  * This captures the position of the lexical scope in the AST to be used
347  * after AST construction, when lexical information has been lost.
348  */
349 class ScopeID final {
350 private:
351  ScopeID(ScopeID const* parent, int pos) : parent_{parent}, pos_{pos} {}
352 
353 public:
354  /**
355  * @brief Move on to the next declaration in the given scope.
356  *
357  * @param alloc The allocator to allocate the new scope
358  * @param parent The parent scope
359  * @return ScopeID const* The ScopeID for this declaration
360  */
361  ScopeID const* next(BumpAllocator& alloc, ScopeID const* parent) const {
362  void* mem = alloc.allocate_bytes(sizeof(ScopeID), alignof(ScopeID));
363  return new(mem) ScopeID{parent, pos_ + 1};
364  }
365 
366  /**
367  * @brief Returns true if we can view the "other" scope from this scope.
368  *
369  * @param other The other scope we want to view
370  */
371  bool canView(ScopeID const* other) const;
372 
373  const ScopeID* parent() const { return parent_; }
374 
375  static const ScopeID* New(BumpAllocator& alloc) {
376  void* mem = alloc.allocate_bytes(sizeof(ScopeID), alignof(ScopeID));
377  return new(mem) ScopeID{nullptr, 0};
378  }
379 
380 public: // Printing functions
381  std::string toString() const;
382  std::ostream& print(std::ostream& os) const;
383  void dump() const;
384  friend std::ostream& operator<<(std::ostream& os, const ScopeID& id) {
385  return os << id.toString();
386  }
387 
388 private:
389  const ScopeID* const parent_;
390  const int pos_;
391 };
392 
393 } // namespace ast