ArkScript
A small, lisp-inspired, functional scripting language
Instructions.hpp
Go to the documentation of this file.
1/**
2 * @file Instructions.hpp
3 * @author Lexy 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-2026
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, as a reference unless it's already one
41 LOAD_FAST = 0x01,
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), as a reference unless it's already one
46
47 // @args symbol id
48 // @role Load a symbol from its ID onto the stack, avoiding the creation of a reference
50
51 // @args constant id
52 // @role Load a constant from its ID onto the stack
53 LOAD_CONST = 0x04,
54
55 // @args absolute address to jump to
56 // @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
58
59 // @args symbol id
60 // @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)
61 STORE = 0x06,
62
63 // @args symbol id
64 // @role Store a value in a symbol without dereferencing it (used by functions only)
65 STORE_REF = 0x07,
66
67 // @args symbol id
68 // @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
69 SET_VAL = 0x08,
70
71 // @args absolute address to jump to
72 // @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
74
75 // @args absolute address to jump to
76 // @role Jump to the provided address
77 JUMP = 0x0a,
78
79 // @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`
80 RET = 0x0b,
81
82 // @role Stop the Virtual Machine
83 HALT = 0x0c,
84
85 // @role push pp, then ip on the stack, preparing for a call instruction
87
88 // @args argument count
89 // @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
90 CALL = 0x0e,
91
92 // @role Jump to the top of the current function and reset the current scope
94
95 // @args symbol id
96 // @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
97 CAPTURE = 0x10,
98
99 // @args symbol id
100 // @role Tell the VM to use the given symbol for the next capture
102
103 // @args builtin id
104 // @role Push the corresponding builtin function object on the stack
105 BUILTIN = 0x12,
106
107 // @args symbol id
108 // @role Remove a variable/constant named following the given symbol id (cf symbols table)
109 DEL = 0x13,
110
111 // @args constant id
112 // @role Push a Closure with the page address pointed by the constant, along with the saved scope created by CAPTURE instruction(s)
114
115 // @args symbol id
116 // @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
117 GET_FIELD = 0x15,
118
119 // @args symbol id
120 // @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, wrapping it in its closure environment
122
123 // @args constant id
124 // @role Load a plugin dynamically, plugin name is stored as a string in the constants table
125 PLUGIN = 0x17,
126
127 // @args number of elements
128 // @role Create a list from the N elements pushed on the stack. Follows the function calling convention
129 LIST = 0x18,
130
131 // @args number of elements
132 // @role Append N elements to a list (TS). Elements are stored in TS(1)..TS(N). Follows the function calling convention
133 APPEND = 0x19,
134
135 // @args number of elements
136 // @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
137 CONCAT = 0x1a,
138
139 // @args number of elements
140 // @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
142
143 // @args number of elements
144 // @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
146
147 // @role Remove an element from a list (TS), given an index (TS1). Push a new list without the removed element to the stack
148 POP_LIST = 0x1d,
149
150 // @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
152
153 // @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
155
156 // @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
158
159 // @role Remove the top of the stack
160 POP = 0x21,
161
162 // @role Pop the top of the stack, if it's false, jump to an address
164
165 // @role Pop the top of the stack, if it's true, jump to an address
167
168 // @role Create a new local scope
170
171 // @role Reset the current scope so that it is empty, and jump to a given location
173
174 // @role Destroy the last local scope
175 POP_SCOPE = 0x26,
176
177 // @role Pop a List from the stack and a function, and call the function with the given list as arguments
178 APPLY = 0x27,
179
181
182 // @role Pop the top of the stack, if it's true, trigger the debugger
184
185 // @role Push `TS1 + TS`
186 ADD = 0x29,
187
188 // @role Push `TS1 - TS`
189 SUB = 0x2a,
190
191 // @role Push `TS1 * TS`
192 MUL = 0x2b,
193
194 // @role Push `TS1 / TS`
195 DIV = 0x2c,
196
197 // @role Push `TS1 > TS`
198 GT = 0x2d,
199
200 // @role Push `TS1 < TS`
201 LT = 0x2e,
202
203 // @role Push `TS1 <= TS`
204 LE = 0x2f,
205
206 // @role Push `TS1 >= TS`
207 GE = 0x30,
208
209 // @role Push `TS1 != TS`
210 NEQ = 0x31,
211
212 // @role Push `TS1 == TS`
213 EQ = 0x32,
214
215 // @role Push `len(TS)`, TS must be a list
216 LEN = 0x33,
217
218 // @role Push `empty?(TS)`, TS must be a list or string
219 IS_EMPTY = 0x34,
220
221 // @role Push `tail(TS)`, all the elements of TS except the first one. TS must be a list or string
222 TAIL = 0x35,
223
224 // @role Push `head(TS)`, the first element of TS or nil if empty. TS must be a list or string
225 HEAD = 0x36,
226
227 // @role Push true if TS is nil, false otherwise
228 IS_NIL = 0x37,
229
230 // @role Convert TS to number (must be a string)
231 TO_NUM = 0x38,
232
233 // @role Convert TS to string
234 TO_STR = 0x39,
235
236 // @role Push the value at index TS (must be a number) in TS1, which must be a list or string
237 AT = 0x3a,
238
239 // @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
240 AT_AT = 0x3b,
241
242 // @role Push `TS1 % TS`
243 MOD = 0x3c,
244
245 // @role Push the type of TS as a string
246 TYPE = 0x3d,
247
248 // @role Check if TS1 is a closure field of TS. TS must be a Closure, TS1 a String
249 HAS_FIELD = 0x3e,
250
251 // @role Push `!TS`
252 NOT = 0x3f,
253
254 // @args constant id, constant id
255 // @role Load two consts (`primary` then `secondary`) on the stack in one instruction
257
258 // @args constant id, symbol id
259 // @role Load const `primary` into the symbol `secondary` (create a variable)
261
262 // @args constant id, symbol id
263 // @role Load const `primary` into the symbol `secondary` (search for the variable with the given symbol id)
265
266 // @args symbol id, symbol id
267 // @role Store the value of the symbol `primary` into a new variable `secondary`
269
270 // @args symbol index, symbol id
271 // @role Store the value of the symbol `primary` into a new variable `secondary`
273
274 // @args symbol id, symbol id
275 // @role Store the value of the symbol `primary` into an existing variable `secondary`
277
278 // @args symbol index, symbol id
279 // @role Store the value of the symbol `primary` into an existing variable `secondary`
281
282 // @args symbol id, count
283 // @role Increment the variable `primary` by `count` and push its value on the stack
284 INCREMENT = 0x47,
285
286 // @args symbol index, count
287 // @role Increment the variable `primary` by `count` and push its value on the stack
289
290 // @args symbol id, count
291 // @role Increment the variable `primary` by `count` and store its value in the given symbol id
293
294 // @args symbol id, count
295 // @role Decrement the variable `primary` by `count` and push its value on the stack
296 DECREMENT = 0x4a,
297
298 // @args symbol index, count
299 // @role Decrement the variable `primary` by `count` and push its value on the stack
301
302 // @args symbol id, count
303 // @role Decrement the variable `primary` by `count` and store its value in the given symbol id
305
306 // @args symbol id, symbol id
307 // @role Load the symbol `primary`, compute its tail, store it in a new variable `secondary`
309
310 // @args symbol index, symbol id
311 // @role Load the symbol `primary`, compute its tail, store it in a new variable `secondary`
313
314 // @args symbol id, symbol id
315 // @role Load the symbol `primary`, compute its head, store it in a new variable `secondary`
317
318 // @args symbol index, symbol id
319 // @role Load the symbol `primary`, compute its head, store it in a new variable `secondary`
321
322 // @args number, symbol id
323 // @role Create a list of `number` elements, and store it in a new variable `secondary`
325
326 // @args symbol id, symbol id
327 // @role Load the symbol `primary`, compute its tail, store it in an existing variable `secondary`
329
330 // @args symbol index, symbol id
331 // @role Load the symbol `primary`, compute its tail, store it in an existing variable `secondary`
333
334 // @args symbol id, symbol id
335 // @role Load the symbol `primary`, compute its head, store it in an existing variable `secondary`
337
338 // @args symbol index, symbol id
339 // @role Load the symbol `primary`, compute its head, store it in an existing variable `secondary`
341
342 // @args builtin id, argument count
343 // @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
345
346 // @args builtin id, argument count
347 // @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
349
350 // @args constant id, absolute address to jump to
351 // @role Compare `TS < constant`, if the comparison fails, jump to the given address. Otherwise, does nothing
353
354 // @args constant id, absolute address to jump to
355 // @role Compare `TS < constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
357
358 // @args symbol id, absolute address to jump to
359 // @role Compare `TS < symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing
361
362 // @args constant id, absolute address to jump to
363 // @role Compare `TS > constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
365
366 // @args constant id, absolute address to jump to
367 // @role Compare `TS > constant`, if the comparison fails, jump to the given address. Otherwise, does nothing
369
370 // @args symbol id, absolute address to jump to
371 // @role Compare `TS > symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing
373
374 // @args constant id, absolute address to jump to
375 // @role Compare `TS == constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
377
378 // @args symbol index, absolute address to jump to
379 // @role Compare `TS == symbol`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
381
382 // @args constant id, absolute address to jump to
383 // @role Compare `TS != constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
385
386 // @args symbol id, absolute address to jump to
387 // @role Compare `TS != symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing
389
390 // @args symbol id, argument count
391 // @role Call a symbol by its id in `primary`, with `secondary` arguments
393
394 // @args symbol index, argument count
395 // @role Call a symbol by its index in the locals in `primary`, with `secondary` arguments
397
398 // @args symbol id (function name), argument count
399 // @role Call the current page with `secondary` arguments
401
402 // @args symbol id, field id in symbols table
403 // @role Push the field of a given symbol (which has to be a closure) on the stack
405
406 // @args symbol index, field id in symbols table
407 // @role Push the field of a given symbol (which has to be a closure) on the stack
409
410 // @args symbol id, symbol id2
411 // @role Push symbol[symbol2]
413
414 // @args symbol index, symbol index2
415 // @role Push symbol[symbol2]
417
418 // @args symbol index, constant id
419 // @role Push symbol[constant]
421
422 // @args symbol id, constant id
423 // @role Check that the type of symbol is the given constant, push true if so, false otherwise
425
426 // @args symbol index, constant id
427 // @role Check that the type of symbol is the given constant, push true if so, false otherwise
429
430 // @args symbol id, number of elements
431 // @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
433
434 // @args symbol index, number of elements
435 // @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
437
438 // @args symbol index, symbol id
439 // @role Compute the length of the list or string at symbol index, and store it in a variable (symbol id)
440 STORE_LEN = 0x6e,
441
442 // @args symbol id, absolute address to jump to
443 // @role Compute the length of a symbol (list or string), and pop TS to compare it, then jump if false
445
446 // @args symbol id, offset number
447 // @role Multiply the symbol by (offset symbol - 2048), then push it to the stack
448 MUL_BY = 0x70,
449
450 // @args symbol index, offset number
451 // @role Multiply the symbol by (offset symbol - 2048), then push it to the stack
453
454 // @args symbol id, offset number
455 // @role Multiply the symbol by (offset symbol - 2048), then store the result using the given symbol id
457
458 // @args op1, op2, op3
459 // @role Pop 3 or 4 values from the stack, and apply the ops sequentially (only ADD, SUB, MUL, and DIV are supported). Push the result to the stack. Only op3 may be NOP.
461
463 };
464
465 constexpr std::array InstructionNames = {
466 "NOP",
467 "LOAD_FAST",
468 "LOAD_FAST_BY_INDEX",
469 "LOAD_SYMBOL",
470 "LOAD_CONST",
471 "POP_JUMP_IF_TRUE",
472 "STORE",
473 "STORE_REF",
474 "SET_VAL",
475 "POP_JUMP_IF_FALSE",
476 "JUMP",
477 "RET",
478 "HALT",
479 "PUSH_RETURN_ADDRESS",
480 "CALL",
481 "TAIL_CALL_SELF",
482 "CAPTURE",
483 "RENAME_NEXT_CAPTURE",
484 "BUILTIN",
485 "DEL",
486 "MAKE_CLOSURE",
487 "GET_FIELD",
488 "GET_FIELD_AS_CLOSURE",
489 "PLUGIN",
490 "LIST",
491 "APPEND",
492 "CONCAT",
493 "APPEND_IN_PLACE",
494 "CONCAT_IN_PLACE",
495 "POP_LIST",
496 "POP_LIST_IN_PLACE",
497 "SET_AT_INDEX",
498 "SET_AT_2_INDEX",
499 "POP",
500 "SHORTCIRCUIT_AND",
501 "SHORTCIRCUIT_OR",
502 "CREATE_SCOPE",
503 "RESET_SCOPE_JUMP",
504 "POP_SCOPE",
505 "APPLY",
506 // operators
507 "BREAKPOINT",
508 "ADD",
509 "SUB",
510 "MUL",
511 "DIV",
512 "GT",
513 "LT",
514 "LE",
515 "GE",
516 "NEQ",
517 "EQ",
518 "LEN",
519 "IS_EMPTY",
520 "TAIL",
521 "HEAD",
522 "IS_NIL",
523 "TO_NUM",
524 "TO_STR",
525 "AT",
526 "AT_AT",
527 "MOD",
528 "TYPE",
529 "HAS_FIELD",
530 "NOT",
531 // super instructions
532 "LOAD_CONST_LOAD_CONST",
533 "LOAD_CONST_STORE",
534 "LOAD_CONST_SET_VAL",
535 "STORE_FROM",
536 "STORE_FROM_INDEX",
537 "SET_VAL_FROM",
538 "SET_VAL_FROM_INDEX",
539 "INCREMENT",
540 "INCREMENT_BY_INDEX",
541 "INCREMENT_STORE",
542 "DECREMENT",
543 "DECREMENT_BY_INDEX",
544 "DECREMENT_STORE",
545 "STORE_TAIL",
546 "STORE_TAIL_BY_INDEX",
547 "STORE_HEAD",
548 "STORE_HEAD_BY_INDEX",
549 "STORE_LIST",
550 "SET_VAL_TAIL",
551 "SET_VAL_TAIL_BY_INDEX",
552 "SET_VAL_HEAD",
553 "SET_VAL_HEAD_BY_INDEX",
554 "CALL_BUILTIN",
555 "CALL_BUILTIN_WITHOUT_RETURN_ADDRESS",
556 "LT_CONST_JUMP_IF_FALSE",
557 "LT_CONST_JUMP_IF_TRUE",
558 "LT_SYM_JUMP_IF_FALSE",
559 "GT_CONST_JUMP_IF_TRUE",
560 "GT_CONST_JUMP_IF_FALSE",
561 "GT_SYM_JUMP_IF_FALSE",
562 "EQ_CONST_JUMP_IF_TRUE",
563 "EQ_SYM_INDEX_JUMP_IF_TRUE",
564 "NEQ_CONST_JUMP_IF_TRUE",
565 "NEQ_SYM_JUMP_IF_FALSE",
566 "CALL_SYMBOL",
567 "CALL_SYMBOL_BY_INDEX",
568 "CALL_CURRENT_PAGE",
569 "GET_FIELD_FROM_SYMBOL",
570 "GET_FIELD_FROM_SYMBOL_INDEX",
571 "AT_SYM_SYM",
572 "AT_SYM_INDEX_SYM_INDEX",
573 "AT_SYM_INDEX_CONST",
574 "CHECK_TYPE_OF",
575 "CHECK_TYPE_OF_BY_INDEX",
576 "APPEND_IN_PLACE_SYM",
577 "APPEND_IN_PLACE_SYM_INDEX",
578 "STORE_LEN",
579 "LT_LEN_SYM_JUMP_IF_FALSE",
580 "MUL_BY",
581 "MUL_BY_INDEX",
582 "MUL_SET_VAL",
583 "FUSED_MATH"
584 };
585
586 static_assert(InstructionNames.size() == static_cast<std::size_t>(Instruction::InstructionsCount) && "Some instruction names appear to be missing");
587}
588
589#endif
Instruction
The different bytecodes are stored here.
@ CALL_BUILTIN_WITHOUT_RETURN_ADDRESS
constexpr std::array InstructionNames