ArkScript
A small, lisp-inspired, functional scripting language
ASTLowerer.hpp
Go to the documentation of this file.
1/**
2 * @file ASTLowerver.hpp
3 * @author Lexy Plateau (lexplt.dev@gmail.com)
4 * @brief ArkScript compiler is in charge of transforming the AST into IR
5 * @date 2020-10-27
6 *
7 * @copyright Copyright (c) 2020-2026
8 *
9 */
10
11#ifndef ARK_COMPILER_LOWERER_ASTLOWERER_HPP
12#define ARK_COMPILER_LOWERER_ASTLOWERER_HPP
13
14#include <stack>
15#include <vector>
16#include <string>
17#include <cinttypes>
18#include <optional>
19
21#include <Ark/Utils/Logger.hpp>
22#include <Ark/Compiler/Pass.hpp>
28
29namespace Ark
30{
31 class State;
32 class Welder;
33}
34
35namespace Ark::internal
36{
37 /**
38 * @brief The ArkScript AST to IR compiler
39 *
40 */
41 class ARK_API ASTLowerer final : public Pass
42 {
43 public:
44 /**
45 * @brief Construct a new ASTLowerer object
46 *
47 * @param debug the debug level
48 */
49 explicit ASTLowerer(unsigned debug);
50
51 /**
52 * @brief Pre-fill tables (used by the debugger)
53 *
54 * @param symbols
55 * @param constants
56 */
57 void addToTables(const std::vector<std::string>& symbols, const std::vector<ValTableElem>& constants);
58
59 /**
60 * @brief Start bytecode pages at a given offset (by default, 0)
61 *
62 * @param offset
63 */
64 void offsetPagesBy(std::size_t offset);
65
66 /**
67 * @brief Start the compilation
68 *
69 * @param ast
70 */
71 void process(Node& ast);
72
73 /**
74 * @brief Return the IR blocks (one per scope)
75 *
76 * @return const std::vector<Block>&
77 */
78 [[nodiscard]] const std::vector<IR::Block>& intermediateRepresentation() const noexcept;
79
80 /**
81 * @brief Return the symbol table pre-computed
82 *
83 * @return const std::vector<std::string>&
84 */
85 [[nodiscard]] const std::vector<std::string>& symbols() const noexcept;
86
87 /**
88 * @brief Return the value table pre-computed
89 *
90 * @return const std::vector<ValTableElem>&
91 */
92 [[nodiscard]] const std::vector<ValTableElem>& values() const noexcept;
93
94 private:
95 struct Page
96 {
97 std::size_t index;
98 bool is_temp;
99 };
100
102
103 // tables: symbols, values, plugins and codes
104 std::vector<std::string> m_symbols;
105 std::vector<ValTableElem> m_values;
106 std::size_t m_start_page_at_offset = 0; ///< Used to offset the page numbers when compiling code in the debugger
107 std::vector<IR::Block> m_code_pages;
108 std::vector<IR::Block> m_temp_pages; ///< we need temporary code pages for some compilations passes
109 IR::label_t m_current_label = 0;
110 std::stack<std::string> m_opened_vars; ///< stack of vars we are currently declaring
111
112 enum class ErrorKind
113 {
114 InvalidNodeMacro,
115 InvalidNodeNoReturnValue,
116 InvalidNodeInOperatorNoReturnValue,
117 InvalidNodeInTailCallNoReturnValue
118 };
119
120 Page createNewCodePage(const bool temp = false) noexcept
121 {
122 if (!temp)
123 {
124 m_code_pages.emplace_back();
125 return Page { .index = m_start_page_at_offset + m_code_pages.size() - 1u, .is_temp = false };
126 }
127
128 m_temp_pages.emplace_back();
129 return Page { .index = m_temp_pages.size() - 1u, .is_temp = true };
130 }
131
132 /**
133 * @brief helper functions to get a temp or finalized code page
134 *
135 * @param page page descriptor
136 * @return std::vector<IR::Block>&
137 */
138 IR::Block& page(const Page page) noexcept
139 {
140 if (!page.is_temp)
141 return m_code_pages[page.index - m_start_page_at_offset];
142 return m_temp_pages[page.index];
143 }
144
145 /**
146 * @brief Check if we are in a recursive self call
147 *
148 * @param name symbol name being compiled
149 * @return true if the name passed is the name of the last function we entered
150 */
151 [[nodiscard]] bool isFunctionCallingItself(const std::string& name) noexcept
152 {
153 return !m_opened_vars.empty() && m_opened_vars.top() == name;
154 }
155
156 /**
157 * @brief Checking if a symbol is an operator
158 *
159 * @param name symbol name
160 * @return std::optional<Instruction> operator instruction
161 */
162 static std::optional<Instruction> getOperator(const std::string& name) noexcept;
163
164 /**
165 * @brief Checking if a symbol is a builtin
166 *
167 * @param name symbol name
168 * @return std::optional<uint16_t> builtin number
169 */
170 static std::optional<uint16_t> getBuiltin(const std::string& name) noexcept;
171
172 /**
173 * @brief Checking if a symbol is a list instruction
174 *
175 * @param name
176 * @return std::optional<Instruction> list instruction
177 */
178 static std::optional<Instruction> getListInstruction(const std::string& name) noexcept;
179
180 /**
181 * Checks if a node is a list and is a call to 'breakpoint'
182 * @param node node to check
183 * @return true if the node is a 'breakpoint' call: (breakpoint <cond>)
184 * @return false otherwise
185 */
186 static bool isBreakpoint(const Node& node);
187
188 /**
189 * Checks if a node is a list and has a keyboard as its first node, indicating if it's producing a value on the stack or not
190 * @param node node to check
191 * @return true if the node produces an output on the stack (fun, if, begin)
192 * @return false otherwise (let, mut, set, while, import, del)
193 */
194 static bool nodeProducesOutput(const Node& node);
195
196 /**
197 * @brief Check if a given instruction is unary (takes only one argument)
198 *
199 * @param inst
200 * @return true the instruction is unary, false otherwise
201 */
202 static bool isUnaryInst(Instruction inst) noexcept;
203
204 /**
205 * @brief Check if a given instruction is ternary (takes three arguments)
206 *
207 * @param inst
208 * @return true the instruction is ternary, false otherwise
209 */
210 static bool isTernaryInst(Instruction inst) noexcept;
211
212 /**
213 * @brief Check if an operator can be repeated
214 *
215 * @param inst
216 * @return true the instruction can be repeated, eg (+ 1 2 3) compiles to (+ (+ 1 2) 3), false otherwise
217 */
218 static bool isRepeatableOperation(Instruction inst) noexcept;
219
220 /**
221 * @brief Display a warning message
222 *
223 * @param message
224 * @param node
225 */
226 void warning(const std::string& message, const Node& node);
227
228 /**
229 * @brief Throw a nice error message
230 *
231 * @param message
232 * @param node
233 */
234 [[noreturn]] static void buildAndThrowError(const std::string& message, const Node& node);
235
236 /**
237 * @brief Throw a nice error message, using a message builder
238 *
239 * @param kind error kind
240 * @param node erroneous node
241 * @param additional_ctx optional context for the error, e.g. the macro name
242 */
243 static void makeError(ErrorKind kind, const Node& node, const std::string& additional_ctx);
244
245 /**
246 * @brief Compile an expression (a node) recursively
247 *
248 * @param x the Node to compile
249 * @param p the current page number we're on
250 * @param is_result_unused
251 * @param is_terminal
252 */
253 void compileExpression(Node& x, Page p, bool is_result_unused, bool is_terminal);
254
255 void compileSymbol(const Node& x, Page p, bool is_result_unused, bool can_use_ref);
256 void compileListInstruction(Node& x, Page p, bool is_result_unused);
257 void compileApplyInstruction(Node& x, Page p, bool is_result_unused);
258 void compileIf(Node& x, Page p, bool is_result_unused, bool is_terminal);
259 void compileFunction(Node& x, Page p, bool is_result_unused);
260 void compileLetMutSet(Keyword n, Node& x, Page p, bool is_result_unused);
261 void compileWhile(Node& x, Page p);
262 void compilePluginImport(const Node& x, Page p);
263 void pushFunctionCallArguments(Node& call, Page p, bool is_tail_call);
264 void handleCalls(Node& x, Page p, bool is_result_unused, bool is_terminal);
265 void handleShortcircuit(Node& x, Page p);
266 void handleOperator(Node& x, Page p, Instruction op);
267 bool handleFunctionCall(Node& x, Page p, bool is_terminal);
268
269 /**
270 * @brief Register a given node in the symbol table
271 * @details Can throw if the table is full
272 *
273 * @param sym
274 * @return uint16_t
275 */
276 uint16_t addSymbol(const Node& sym);
277
278 /**
279 * @brief Register a given node in the value table
280 * @details Can throw if the table is full
281 *
282 * @param x
283 * @return uint16_t
284 */
285 uint16_t addValue(const Node& x);
286
287 /**
288 * @brief Register a page id (function reference) in the value table
289 * @details Can throw if the table is full
290 *
291 * @param page_id
292 * @param current A reference to the current node, for context
293 * @return std::size_t
294 */
295 uint16_t addValue(std::size_t page_id, const Node& current);
296 };
297}
298
299#endif
An entity in the IR is a bundle of information.
The different instructions used by the compiler and virtual machine.
Track locals at compile.
Internal logger.
#define ARK_API
Definition Module.hpp:22
AST node used by the parser, optimizer and compiler.
Interface for a compiler pass.
ArkScript configuration macros.
The basic value type handled by the compiler.
The ArkScript AST to IR compiler.
std::vector< ValTableElem > m_values
std::vector< IR::Block > m_temp_pages
we need temporary code pages for some compilations passes
Page createNewCodePage(const bool temp=false) noexcept
std::vector< IR::Block > m_code_pages
std::vector< std::string > m_symbols
LocalsLocator m_locals_locator
std::stack< std::string > m_opened_vars
stack of vars we are currently declaring
bool isFunctionCallingItself(const std::string &name) noexcept
Check if we are in a recursive self call.
IR::Block & page(const Page page) noexcept
helper functions to get a temp or finalized code page
A node of an Abstract Syntax Tree for ArkScript.
Definition Node.hpp:32
An interface to describe compiler passes.
Definition Pass.hpp:24
std::vector< Entity > Block
Definition Entity.hpp:94
std::size_t label_t
Definition Entity.hpp:33
Keyword
The different keywords available.
Definition Common.hpp:79
Instruction
The different bytecodes are stored here.