ArkScript
A small, lisp-inspired, functional scripting language
Instructions.hpp
Go to the documentation of this file.
1/**
2 * @file Instructions.hpp
3 * @author Alexandre Plateau (lexplt.dev@gmail.com)
4 * @brief The different instructions used by the compiler and virtual machine
5 * @date 2020-10-27
6 *
7 * @copyright Copyright (c) 2020-2025
8 *
9 */
10
11#ifndef ARK_COMPILER_INSTRUCTIONS_HPP
12#define ARK_COMPILER_INSTRUCTIONS_HPP
13
14#include <array>
15
16namespace Ark::internal
17{
18 /**
19 * @brief The different bytecodes are stored here
20 * @par Adding an operator
21 * It must be referenced as well under include/Ark/Compiler/Common.hpp, in
22 * the operators table. The order of the operators below <code>FIRST_OPERATOR</code>
23 * must be the same as the one in the operators table from the aforementioned file.
24 *
25 */
26 enum Instruction : uint8_t
27 {
28 // @role Does nothing, useful for padding
29 NOP = 0x00,
35 FUNC_TYPE = 0xF3,
38
39 // @args symbol id
40 // @role Load a symbol from its ID onto the stack
42
43 // @args stack index
44 // @role Load a symbol from the locals stack by its index (starting from the end of the current scope)
46
47 // @args constant id
48 // @role Load a constant from its ID onto the stack
49 LOAD_CONST = 0x03,
50
51 // @args absolute address to jump to
52 // @role Jump to the provided address if the last value on the stack was equal to true. Remove the value from the stack no matter what it is
54
55 // @args symbol id
56 // @role Take the value on top of the stack and create a variable in the current scope, named following the given symbol id (cf symbols table)
57 STORE = 0x05,
58
59 // @args symbol id
60 // @role Take the value on top of the stack and put it inside a variable named following the symbol id (cf symbols table), in the nearest scope. Raise an error if it couldn't find a scope where the variable exists
61 SET_VAL = 0x06,
62
63 // @args absolute address to jump to
64 // @role Jump to the provided address if the last value on the stack was equal to false. Remove the value from the stack no matter what it is
66
67 // @args absolute address to jump to
68 // @role Jump to the provided address
69 JUMP = 0x08,
70
71 // @role If in a code segment other than the main one, quit it, and push the value on top of the stack to the new stack; should as well delete the current environment. Otherwise, acts as a `HALT`
72 RET = 0x09,
73
74 // @role Stop the Virtual Machine
75 HALT = 0x0a,
76
77 // @role push pp, then ip on the stack, preparing for a call instruction
79
80 // @args argument count
81 // @role Call function from its symbol id located on top of the stack. Take the given number of arguments from the top of stack and give them to the function (the first argument taken from the stack will be the last one of the function). The stack of the function is now composed of its arguments, from the first to the last one
82 CALL = 0x0c,
83
84 // @args symbol id
85 // @role Tell the Virtual Machine to capture the variable from the current environment. Main goal is to be able to handle closures, which need to save the environment in which they were created
86 CAPTURE = 0x0d,
87
88 // @args builtin id
89 // @role Push the corresponding builtin function object on the stack
90 BUILTIN = 0x0e,
91
92 // @args symbol id
93 // @role Remove a variable/constant named following the given symbol id (cf symbols table)
94 DEL = 0x0f,
95
96 // @args constant id
97 // @role Push a Closure with the page address pointed by the constant, along with the saved scope created by CAPTURE instruction(s)
99
100 // @args symbol id
101 // @role Read the field named following the given symbol id (cf symbols table) of a `Closure` stored in TS. Pop TS and push the value of field read on the stack
102 GET_FIELD = 0x11,
103
104 // @args constant id
105 // @role Load a plugin dynamically, plugin name is stored as a string in the constants table
106 PLUGIN = 0x12,
107
108 // @args number of elements
109 // @role Create a list from the N elements pushed on the stack. Follows the function calling convention
110 LIST = 0x13,
111
112 // @args number of elements
113 // @role Append N elements to a list (TS). Elements are stored in TS(1)..TS(N). Follows the function calling convention
114 APPEND = 0x14,
115
116 // @args number of elements
117 // @role Concatenate N lists to a list (TS). Lists to concat to TS are stored in TS(1)..TS(N). Follows the function calling convention
118 CONCAT = 0x15,
119
120 // @args number of elements
121 // @role Append N elements to a reference to a list (TS), the list is being mutated in-place, no new object created. Elements are stored in TS(1)..TS(N). Follows the function calling convention
123
124 // @args number of elements
125 // @role Concatenate N lists to a reference to a list (TS), the list is being mutated in-place, no new object created. Lists to concat to TS are stored in TS(1)..TS(N). Follows the function calling convention
127
128 // @role Remove an element from a list (TS), given an index (TS1). Push a new list without the removed element to the stack
129 POP_LIST = 0x18,
130
131 // @role Remove an element from a reference to a list (TS), given an index (TS1). The list is mutated in-place, no new object created
133
134 // @role Modify a reference to a list or string (TS) by replacing the element at TS1 (must be a number) by the value in TS2. The object is mutated in-place, no new object created
136
137 // @role Modify a reference to a list (TS) by replacing TS[TS2][TS1] by the value in TS3. TS[TS2] can be a string (if it is, TS3 must be a string). The object is mutated in-place, no new object created
139
140 // @role Remove the top of the stack
141 POP = 0x1c,
142
143 // @role Pop the top of the stack, if it's false, jump to an address
145
146 // @role Pop the top of the stack, if it's true, jump to an address
148
149 // @role Create a new local scope
151
152 // @role Reset the current scope so that it is empty, and jump to a given location
154
155 // @role Destroy the last local scope
156 POP_SCOPE = 0x21,
157
158 // @args symbol id (function name)
159 // @role Push the current page address as a value on the stack
161
163
164 // @role Push `TS1 + TS`
165 ADD = 0x23,
166
167 // @role Push `TS1 - TS`
168 SUB = 0x24,
169
170 // @role Push `TS1 * TS`
171 MUL = 0x25,
172
173 // @role Push `TS1 / TS`
174 DIV = 0x26,
175
176 // @role Push `TS1 > TS`
177 GT = 0x27,
178
179 // @role Push `TS1 < TS`
180 LT = 0x28,
181
182 // @role Push `TS1 <= TS`
183 LE = 0x29,
184
185 // @role Push `TS1 >= TS`
186 GE = 0x2a,
187
188 // @role Push `TS1 != TS`
189 NEQ = 0x2b,
190
191 // @role Push `TS1 == TS`
192 EQ = 0x2c,
193
194 // @role Push `len(TS)`, TS must be a list
195 LEN = 0x2d,
196
197 // @role Push `empty?(TS)`, TS must be a list or string
198 EMPTY = 0x2e,
199
200 // @role Push `tail(TS)`, all the elements of TS except the first one. TS must be a list or string
201 TAIL = 0x2f,
202
203 // @role Push `head(TS)`, the first element of TS or nil if empty. TS must be a list or string
204 HEAD = 0x30,
205
206 // @role Push true if TS is nil, false otherwise
207 ISNIL = 0x31,
208
209 // @role Throw an exception if TS1 is false, and display TS (must be a string). Do not push anything on the stack
210 ASSERT = 0x32,
211
212 // @role Convert TS to number (must be a string)
213 TO_NUM = 0x33,
214
215 // @role Convert TS to string
216 TO_STR = 0x34,
217
218 // @role Push the value at index TS (must be a number) in TS1, which must be a list or string
219 AT = 0x35,
220
221 // @role Push the value at index TS (must be a number), inside the list or string at index TS1 (must be a number) in the list at TS2
222 AT_AT = 0x36,
223
224 // @role Push `TS1 % TS`
225 MOD = 0x37,
226
227 // @role Push the type of TS as a string
228 TYPE = 0x38,
229
230 // @role Check if TS1 is a closure field of TS. TS must be a Closure, TS1 a String
231 HASFIELD = 0x39,
232
233 // @role Push `!TS`
234 NOT = 0x3a,
235
236 // @args constant id, constant id
237 // @role Load two consts (`primary` then `secondary`) on the stack in one instruction
239
240 // @args constant id, symbol id
241 // @role Load const `primary` into the symbol `secondary` (create a variable)
243
244 // @args constant id, symbol id
245 // @role Load const `primary` into the symbol `secondary` (search for the variable with the given symbol id)
247
248 // @args symbol id, symbol id
249 // @role Store the value of the symbol `primary` into a new variable `secondary`
251
252 // @args symbol index, symbol id
253 // @role Store the value of the symbol `primary` into a new variable `secondary`
255
256 // @args symbol id, symbol id
257 // @role Store the value of the symbol `primary` into an existing variable `secondary`
259
260 // @args symbol index, symbol id
261 // @role Store the value of the symbol `primary` into an existing variable `secondary`
263
264 // @args symbol id, count
265 // @role Increment the variable `primary` by `count` and push its value on the stack
266 INCREMENT = 0x42,
267
268 // @args symbol index, count
269 // @role Increment the variable `primary` by `count` and push its value on the stack
271
272 // @args symbol id, count
273 // @role Increment the variable `primary` by `count` and store its value in the given symbol id
275
276 // @args symbol id, count
277 // @role Decrement the variable `primary` by `count` and push its value on the stack
278 DECREMENT = 0x45,
279
280 // @args symbol index, count
281 // @role Decrement the variable `primary` by `count` and push its value on the stack
283
284 // @args symbol id, count
285 // @role Decrement the variable `primary` by `count` and store its value in the given symbol id
287
288 // @args symbol id, symbol id
289 // @role Load the symbol `primary`, compute its tail, store it in a new variable `secondary`
291
292 // @args symbol index, symbol id
293 // @role Load the symbol `primary`, compute its tail, store it in a new variable `secondary`
295
296 // @args symbol id, symbol id
297 // @role Load the symbol `primary`, compute its head, store it in a new variable `secondary`
299
300 // @args symbol index, symbol id
301 // @role Load the symbol `primary`, compute its head, store it in a new variable `secondary`
303
304 // @args number, symbol id
305 // @role Create a list of `number` elements, and store it in a new variable `secondary`
307
308 // @args symbol id, symbol id
309 // @role Load the symbol `primary`, compute its tail, store it in an existing variable `secondary`
311
312 // @args symbol index, symbol id
313 // @role Load the symbol `primary`, compute its tail, store it in an existing variable `secondary`
315
316 // @args symbol id, symbol id
317 // @role Load the symbol `primary`, compute its head, store it in an existing variable `secondary`
319
320 // @args symbol index, symbol id
321 // @role Load the symbol `primary`, compute its head, store it in an existing variable `secondary`
323
324 // @args builtin id, argument count
325 // @role Call a builtin by its id in `primary`, with `secondary` arguments. Bypass the stack size check because we do not push IP/PP since builtins calls do not alter the stack
327
328 // @args builtin id, argument count
329 // @role Call a builtin by its id in `primary`, with `secondary` arguments. Bypass the stack size check because we do not push IP/PP since builtins calls do not alter the stack, as well as the return address removal
331
332 // @args constant id, absolute address to jump to
333 // @role Compare `TS < constant`, if the comparison fails, jump to the given address. Otherwise, does nothing
335
336 // @args constant id, absolute address to jump to
337 // @role Compare `TS < constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
339
340 // @args symbol id, absolute address to jump to
341 // @role Compare `TS < symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing
343
344 // @args constant id, absolute address to jump to
345 // @role Compare `TS > constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
347
348 // @args constant id, absolute address to jump to
349 // @role Compare `TS > constant`, if the comparison fails, jump to the given address. Otherwise, does nothing
351
352 // @args symbol id, absolute address to jump to
353 // @role Compare `TS > symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing
355
356 // @args constant id, absolute address to jump to
357 // @role Compare `TS == constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
359
360 // @args symbol index, absolute address to jump to
361 // @role Compare `TS == symbol`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
363
364 // @args constant id, absolute address to jump to
365 // @role Compare `TS != constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
367
368 // @args symbol id, absolute address to jump to
369 // @role Compare `TS != symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing
371
372 // @args symbol id, argument count
373 // @role Call a symbol by its id in `primary`, with `secondary` arguments
375
376 // @args symbol id (function name), argument count
377 // @role Call the current page with `secondary` arguments
379
380 // @args symbol id, field id in symbols table
381 // @role Push the field of a given symbol (which has to be a closure) on the stack
383
384 // @args symbol index, field id in symbols table
385 // @role Push the field of a given symbol (which has to be a closure) on the stack
387
388 // @args symbol id, symbol id2
389 // @role Push symbol[symbol2]
391
392 // @args symbol index, symbol index2
393 // @role Push symbol[symbol2]
395
396 // @args symbol id, constant id
397 // @role Check that the type of symbol is the given constant, push true if so, false otherwise
399
400 // @args symbol index, constant id
401 // @role Check that the type of symbol is the given constant, push true if so, false otherwise
403
404 // @args symbol id, number of elements
405 // @role Append N elements to a reference to a list (symbol id), the list is being mutated in-place, no new object created. Elements are stored in TS(1)..TS(N). Follows the function calling convention
407
408 // @args symbol index, number of elements
409 // @role Append N elements to a reference to a list (symbol index), the list is being mutated in-place, no new object created. Elements are stored in TS(1)..TS(N). Follows the function calling convention
411
413 };
414
415 constexpr std::array InstructionNames = {
416 "NOP",
417 "LOAD_SYMBOL",
418 "LOAD_SYMBOL_BY_INDEX",
419 "LOAD_CONST",
420 "POP_JUMP_IF_TRUE",
421 "STORE",
422 "SET_VAL",
423 "POP_JUMP_IF_FALSE",
424 "JUMP",
425 "RET",
426 "HALT",
427 "PUSH_RETURN_ADDRESS",
428 "CALL",
429 "CAPTURE",
430 "BUILTIN",
431 "DEL",
432 "MAKE_CLOSURE",
433 "GET_FIELD",
434 "PLUGIN",
435 "LIST",
436 "APPEND",
437 "CONCAT",
438 "APPEND_IN_PLACE",
439 "CONCAT_IN_PLACE",
440 "POP_LIST",
441 "POP_LIST_IN_PLACE",
442 "SET_AT_INDEX",
443 "SET_AT_2_INDEX",
444 "POP",
445 "SHORTCIRCUIT_AND",
446 "SHORTCIRCUIT_OR",
447 "CREATE_SCOPE",
448 "RESET_SCOPE_JUMP",
449 "POP_SCOPE",
450 "GET_CURRENT_PAGE_ADDR",
451 // operators
452 "ADD",
453 "SUB",
454 "MUL",
455 "DIV",
456 "GT",
457 "LT",
458 "LE",
459 "GE",
460 "NEQ",
461 "EQ",
462 "LEN",
463 "EMPTY",
464 "TAIL",
465 "HEAD",
466 "ISNIL",
467 "ASSERT",
468 "TO_NUM",
469 "TO_STR",
470 "AT",
471 "AT_AT",
472 "MOD",
473 "TYPE",
474 "HASFIELD",
475 "NOT",
476 // super instructions
477 "LOAD_CONST_LOAD_CONST",
478 "LOAD_CONST_STORE",
479 "LOAD_CONST_SET_VAL",
480 "STORE_FROM",
481 "STORE_FROM_INDEX",
482 "SET_VAL_FROM",
483 "SET_VAL_FROM_INDEX",
484 "INCREMENT",
485 "INCREMENT_BY_INDEX",
486 "INCREMENT_STORE",
487 "DECREMENT",
488 "DECREMENT_BY_INDEX",
489 "DECREMENT_STORE",
490 "STORE_TAIL",
491 "STORE_TAIL_BY_INDEX",
492 "STORE_HEAD",
493 "STORE_HEAD_BY_INDEX",
494 "STORE_LIST",
495 "SET_VAL_TAIL",
496 "SET_VAL_TAIL_BY_INDEX",
497 "SET_VAL_HEAD",
498 "SET_VAL_HEAD_BY_INDEX",
499 "CALL_BUILTIN",
500 "CALL_BUILTIN_WITHOUT_RETURN_ADDRESS",
501 "LT_CONST_JUMP_IF_FALSE",
502 "LT_CONST_JUMP_IF_TRUE",
503 "LT_SYM_JUMP_IF_FALSE",
504 "GT_CONST_JUMP_IF_TRUE",
505 "GT_CONST_JUMP_IF_FALSE",
506 "GT_SYM_JUMP_IF_FALSE",
507 "EQ_CONST_JUMP_IF_TRUE",
508 "EQ_SYM_INDEX_JUMP_IF_TRUE",
509 "NEQ_CONST_JUMP_IF_TRUE",
510 "NEQ_SYM_JUMP_IF_FALSE",
511 "CALL_SYMBOL",
512 "CALL_CURRENT_PAGE",
513 "GET_FIELD_FROM_SYMBOL",
514 "GET_FIELD_FROM_SYMBOL_INDEX",
515 "AT_SYM_SYM",
516 "AT_SYM_INDEX_SYM_INDEX",
517 "CHECK_TYPE_OF",
518 "CHECK_TYPE_OF_BY_INDEX",
519 "APPEND_IN_PLACE_SYM",
520 "APPEND_IN_PLACE_SYM_INDEX"
521 };
522
523 static_assert(InstructionNames.size() == static_cast<std::size_t>(Instruction::InstructionsCount) && "Some instruction names appear to be missing");
524}
525
526#endif
Instruction
The different bytecodes are stored here.
@ CALL_BUILTIN_WITHOUT_RETURN_ADDRESS
constexpr std::array InstructionNames