ArkScript
A small, lisp-inspired, functional scripting language
Instructions.hpp
Go to the documentation of this file.
1/**
2 * @file Instructions.hpp
3 * @author Lex 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 Store a value in a symbol without dereferencing it (used by functions only)
61 STORE_REF = 0x06,
62
63 // @args symbol id
64 // @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
65 SET_VAL = 0x07,
66
67 // @args absolute address to jump to
68 // @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
70
71 // @args absolute address to jump to
72 // @role Jump to the provided address
73 JUMP = 0x09,
74
75 // @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`
76 RET = 0x0a,
77
78 // @role Stop the Virtual Machine
79 HALT = 0x0b,
80
81 // @role push pp, then ip on the stack, preparing for a call instruction
83
84 // @args argument count
85 // @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
86 CALL = 0x0d,
87
88 // @args symbol id
89 // @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
90 CAPTURE = 0x0e,
91
92 // @args symbol id
93 // @role Tell the VM to use the given symbol for the next capture
95
96 // @args builtin id
97 // @role Push the corresponding builtin function object on the stack
98 BUILTIN = 0x10,
99
100 // @args symbol id
101 // @role Remove a variable/constant named following the given symbol id (cf symbols table)
102 DEL = 0x11,
103
104 // @args constant id
105 // @role Push a Closure with the page address pointed by the constant, along with the saved scope created by CAPTURE instruction(s)
107
108 // @args symbol id
109 // @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
110 GET_FIELD = 0x13,
111
112 // @args constant id
113 // @role Load a plugin dynamically, plugin name is stored as a string in the constants table
114 PLUGIN = 0x14,
115
116 // @args number of elements
117 // @role Create a list from the N elements pushed on the stack. Follows the function calling convention
118 LIST = 0x15,
119
120 // @args number of elements
121 // @role Append N elements to a list (TS). Elements are stored in TS(1)..TS(N). Follows the function calling convention
122 APPEND = 0x16,
123
124 // @args number of elements
125 // @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
126 CONCAT = 0x17,
127
128 // @args number of elements
129 // @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
131
132 // @args number of elements
133 // @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
135
136 // @role Remove an element from a list (TS), given an index (TS1). Push a new list without the removed element to the stack
137 POP_LIST = 0x1a,
138
139 // @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
141
142 // @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
144
145 // @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
147
148 // @role Remove the top of the stack
149 POP = 0x1e,
150
151 // @role Pop the top of the stack, if it's false, jump to an address
153
154 // @role Pop the top of the stack, if it's true, jump to an address
156
157 // @role Create a new local scope
159
160 // @role Reset the current scope so that it is empty, and jump to a given location
162
163 // @role Destroy the last local scope
164 POP_SCOPE = 0x23,
165
166 // @args symbol id (function name)
167 // @role Push the current page address as a value on the stack
169
171
172 // @role Push `TS1 + TS`
173 ADD = 0x25,
174
175 // @role Push `TS1 - TS`
176 SUB = 0x26,
177
178 // @role Push `TS1 * TS`
179 MUL = 0x27,
180
181 // @role Push `TS1 / TS`
182 DIV = 0x28,
183
184 // @role Push `TS1 > TS`
185 GT = 0x29,
186
187 // @role Push `TS1 < TS`
188 LT = 0x2a,
189
190 // @role Push `TS1 <= TS`
191 LE = 0x2b,
192
193 // @role Push `TS1 >= TS`
194 GE = 0x2c,
195
196 // @role Push `TS1 != TS`
197 NEQ = 0x2d,
198
199 // @role Push `TS1 == TS`
200 EQ = 0x2e,
201
202 // @role Push `len(TS)`, TS must be a list
203 LEN = 0x2f,
204
205 // @role Push `empty?(TS)`, TS must be a list or string
206 EMPTY = 0x30,
207
208 // @role Push `tail(TS)`, all the elements of TS except the first one. TS must be a list or string
209 TAIL = 0x31,
210
211 // @role Push `head(TS)`, the first element of TS or nil if empty. TS must be a list or string
212 HEAD = 0x32,
213
214 // @role Push true if TS is nil, false otherwise
215 ISNIL = 0x33,
216
217 // @role Throw an exception if TS1 is false, and display TS (must be a string). Do not push anything on the stack
218 ASSERT = 0x34,
219
220 // @role Convert TS to number (must be a string)
221 TO_NUM = 0x35,
222
223 // @role Convert TS to string
224 TO_STR = 0x36,
225
226 // @role Push the value at index TS (must be a number) in TS1, which must be a list or string
227 AT = 0x37,
228
229 // @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
230 AT_AT = 0x38,
231
232 // @role Push `TS1 % TS`
233 MOD = 0x39,
234
235 // @role Push the type of TS as a string
236 TYPE = 0x3a,
237
238 // @role Check if TS1 is a closure field of TS. TS must be a Closure, TS1 a String
239 HASFIELD = 0x3b,
240
241 // @role Push `!TS`
242 NOT = 0x3c,
243
244 // @args constant id, constant id
245 // @role Load two consts (`primary` then `secondary`) on the stack in one instruction
247
248 // @args constant id, symbol id
249 // @role Load const `primary` into the symbol `secondary` (create a variable)
251
252 // @args constant id, symbol id
253 // @role Load const `primary` into the symbol `secondary` (search for the variable with the given symbol id)
255
256 // @args symbol id, symbol id
257 // @role Store the value of the symbol `primary` into a new variable `secondary`
259
260 // @args symbol index, symbol id
261 // @role Store the value of the symbol `primary` into a new variable `secondary`
263
264 // @args symbol id, symbol id
265 // @role Store the value of the symbol `primary` into an existing variable `secondary`
267
268 // @args symbol index, symbol id
269 // @role Store the value of the symbol `primary` into an existing variable `secondary`
271
272 // @args symbol id, count
273 // @role Increment the variable `primary` by `count` and push its value on the stack
274 INCREMENT = 0x44,
275
276 // @args symbol index, count
277 // @role Increment the variable `primary` by `count` and push its value on the stack
279
280 // @args symbol id, count
281 // @role Increment the variable `primary` by `count` and store its value in the given symbol id
283
284 // @args symbol id, count
285 // @role Decrement the variable `primary` by `count` and push its value on the stack
286 DECREMENT = 0x47,
287
288 // @args symbol index, count
289 // @role Decrement the variable `primary` by `count` and push its value on the stack
291
292 // @args symbol id, count
293 // @role Decrement the variable `primary` by `count` and store its value in the given symbol id
295
296 // @args symbol id, symbol id
297 // @role Load the symbol `primary`, compute its tail, store it in a new variable `secondary`
299
300 // @args symbol index, symbol id
301 // @role Load the symbol `primary`, compute its tail, store it in a new variable `secondary`
303
304 // @args symbol id, symbol id
305 // @role Load the symbol `primary`, compute its head, store it in a new variable `secondary`
307
308 // @args symbol index, symbol id
309 // @role Load the symbol `primary`, compute its head, store it in a new variable `secondary`
311
312 // @args number, symbol id
313 // @role Create a list of `number` elements, and store it in a new variable `secondary`
315
316 // @args symbol id, symbol id
317 // @role Load the symbol `primary`, compute its tail, store it in an existing variable `secondary`
319
320 // @args symbol index, symbol id
321 // @role Load the symbol `primary`, compute its tail, store it in an existing variable `secondary`
323
324 // @args symbol id, symbol id
325 // @role Load the symbol `primary`, compute its head, store it in an existing variable `secondary`
327
328 // @args symbol index, symbol id
329 // @role Load the symbol `primary`, compute its head, store it in an existing variable `secondary`
331
332 // @args builtin id, argument count
333 // @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
335
336 // @args builtin id, argument count
337 // @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
339
340 // @args constant id, absolute address to jump to
341 // @role Compare `TS < constant`, 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 symbol id, absolute address to jump to
349 // @role Compare `TS < symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing
351
352 // @args constant id, absolute address to jump to
353 // @role Compare `TS > constant`, if the comparison succeeds, 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 fails, jump to the given address. Otherwise, does nothing
359
360 // @args symbol id, absolute address to jump to
361 // @role Compare `TS > symbol`, if the comparison fails, 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 index, absolute address to jump to
369 // @role Compare `TS == symbol`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
371
372 // @args constant id, absolute address to jump to
373 // @role Compare `TS != constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
375
376 // @args symbol id, absolute address to jump to
377 // @role Compare `TS != symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing
379
380 // @args symbol id, argument count
381 // @role Call a symbol by its id in `primary`, with `secondary` arguments
383
384 // @args symbol id (function name), argument count
385 // @role Call the current page with `secondary` arguments
387
388 // @args symbol id, field id in symbols table
389 // @role Push the field of a given symbol (which has to be a closure) on the stack
391
392 // @args symbol index, field id in symbols table
393 // @role Push the field of a given symbol (which has to be a closure) on the stack
395
396 // @args symbol id, symbol id2
397 // @role Push symbol[symbol2]
399
400 // @args symbol index, symbol index2
401 // @role Push symbol[symbol2]
403
404 // @args symbol index, constant id
405 // @role Push symbol[constant]
407
408 // @args symbol id, constant id
409 // @role Check that the type of symbol is the given constant, push true if so, false otherwise
411
412 // @args symbol index, constant id
413 // @role Check that the type of symbol is the given constant, push true if so, false otherwise
415
416 // @args symbol id, number of elements
417 // @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
419
420 // @args symbol index, number of elements
421 // @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
423
424 // @args symbol index, symbol id
425 // @role Compute the length of the list or string at symbol index, and store it in a variable (symbol id)
426 STORE_LEN = 0x6a,
427
428 // @args symbol id, absolute address to jump to
429 // @role Compute the length of a symbol (list or string), and pop TS to compare it, then jump if false
431
432 // @args symbol id, offset number
433 // @role Multiply the symbol by (offset symbol - 2048), then push it to the stack
434 MUL_BY = 0x6c,
435
436 // @args symbol index, offset number
437 // @role Multiply the symbol by (offset symbol - 2048), then push it to the stack
439
440 // @args symbol id, offset number
441 // @role Multiply the symbol by (offset symbol - 2048), then store the result using the given symbol id
443
444 // @args op1, op2, op3
445 // @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.
447
449 };
450
451 constexpr std::array InstructionNames = {
452 "NOP",
453 "LOAD_SYMBOL",
454 "LOAD_SYMBOL_BY_INDEX",
455 "LOAD_CONST",
456 "POP_JUMP_IF_TRUE",
457 "STORE",
458 "STORE_REF",
459 "SET_VAL",
460 "POP_JUMP_IF_FALSE",
461 "JUMP",
462 "RET",
463 "HALT",
464 "PUSH_RETURN_ADDRESS",
465 "CALL",
466 "CAPTURE",
467 "RENAME_NEXT_CAPTURE",
468 "BUILTIN",
469 "DEL",
470 "MAKE_CLOSURE",
471 "GET_FIELD",
472 "PLUGIN",
473 "LIST",
474 "APPEND",
475 "CONCAT",
476 "APPEND_IN_PLACE",
477 "CONCAT_IN_PLACE",
478 "POP_LIST",
479 "POP_LIST_IN_PLACE",
480 "SET_AT_INDEX",
481 "SET_AT_2_INDEX",
482 "POP",
483 "SHORTCIRCUIT_AND",
484 "SHORTCIRCUIT_OR",
485 "CREATE_SCOPE",
486 "RESET_SCOPE_JUMP",
487 "POP_SCOPE",
488 "GET_CURRENT_PAGE_ADDR",
489 // operators
490 "ADD",
491 "SUB",
492 "MUL",
493 "DIV",
494 "GT",
495 "LT",
496 "LE",
497 "GE",
498 "NEQ",
499 "EQ",
500 "LEN",
501 "EMPTY",
502 "TAIL",
503 "HEAD",
504 "ISNIL",
505 "ASSERT",
506 "TO_NUM",
507 "TO_STR",
508 "AT",
509 "AT_AT",
510 "MOD",
511 "TYPE",
512 "HASFIELD",
513 "NOT",
514 // super instructions
515 "LOAD_CONST_LOAD_CONST",
516 "LOAD_CONST_STORE",
517 "LOAD_CONST_SET_VAL",
518 "STORE_FROM",
519 "STORE_FROM_INDEX",
520 "SET_VAL_FROM",
521 "SET_VAL_FROM_INDEX",
522 "INCREMENT",
523 "INCREMENT_BY_INDEX",
524 "INCREMENT_STORE",
525 "DECREMENT",
526 "DECREMENT_BY_INDEX",
527 "DECREMENT_STORE",
528 "STORE_TAIL",
529 "STORE_TAIL_BY_INDEX",
530 "STORE_HEAD",
531 "STORE_HEAD_BY_INDEX",
532 "STORE_LIST",
533 "SET_VAL_TAIL",
534 "SET_VAL_TAIL_BY_INDEX",
535 "SET_VAL_HEAD",
536 "SET_VAL_HEAD_BY_INDEX",
537 "CALL_BUILTIN",
538 "CALL_BUILTIN_WITHOUT_RETURN_ADDRESS",
539 "LT_CONST_JUMP_IF_FALSE",
540 "LT_CONST_JUMP_IF_TRUE",
541 "LT_SYM_JUMP_IF_FALSE",
542 "GT_CONST_JUMP_IF_TRUE",
543 "GT_CONST_JUMP_IF_FALSE",
544 "GT_SYM_JUMP_IF_FALSE",
545 "EQ_CONST_JUMP_IF_TRUE",
546 "EQ_SYM_INDEX_JUMP_IF_TRUE",
547 "NEQ_CONST_JUMP_IF_TRUE",
548 "NEQ_SYM_JUMP_IF_FALSE",
549 "CALL_SYMBOL",
550 "CALL_CURRENT_PAGE",
551 "GET_FIELD_FROM_SYMBOL",
552 "GET_FIELD_FROM_SYMBOL_INDEX",
553 "AT_SYM_SYM",
554 "AT_SYM_INDEX_SYM_INDEX",
555 "AT_SYM_INDEX_CONST",
556 "CHECK_TYPE_OF",
557 "CHECK_TYPE_OF_BY_INDEX",
558 "APPEND_IN_PLACE_SYM",
559 "APPEND_IN_PLACE_SYM_INDEX",
560 "STORE_LEN",
561 "LT_LEN_SYM_JUMP_IF_FALSE",
562 "MUL_BY",
563 "MUL_BY_INDEX",
564 "MUL_SET_VAL",
565 "FUSED_MATH"
566 };
567
568 static_assert(InstructionNames.size() == static_cast<std::size_t>(Instruction::InstructionsCount) && "Some instruction names appear to be missing");
569}
570
571#endif
Instruction
The different bytecodes are stored here.
@ CALL_BUILTIN_WITHOUT_RETURN_ADDRESS
constexpr std::array InstructionNames