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 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 symbol id
89 // @role Tell the VM to use the given symbol for the next capture
91
92 // @args builtin id
93 // @role Push the corresponding builtin function object on the stack
94 BUILTIN = 0x0f,
95
96 // @args symbol id
97 // @role Remove a variable/constant named following the given symbol id (cf symbols table)
98 DEL = 0x10,
99
100 // @args constant id
101 // @role Push a Closure with the page address pointed by the constant, along with the saved scope created by CAPTURE instruction(s)
103
104 // @args symbol id
105 // @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
106 GET_FIELD = 0x12,
107
108 // @args constant id
109 // @role Load a plugin dynamically, plugin name is stored as a string in the constants table
110 PLUGIN = 0x13,
111
112 // @args number of elements
113 // @role Create a list from the N elements pushed on the stack. Follows the function calling convention
114 LIST = 0x14,
115
116 // @args number of elements
117 // @role Append N elements to a list (TS). Elements are stored in TS(1)..TS(N). Follows the function calling convention
118 APPEND = 0x15,
119
120 // @args number of elements
121 // @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
122 CONCAT = 0x16,
123
124 // @args number of elements
125 // @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
127
128 // @args number of elements
129 // @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
131
132 // @role Remove an element from a list (TS), given an index (TS1). Push a new list without the removed element to the stack
133 POP_LIST = 0x19,
134
135 // @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
137
138 // @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
140
141 // @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
143
144 // @role Remove the top of the stack
145 POP = 0x1d,
146
147 // @role Pop the top of the stack, if it's false, jump to an address
149
150 // @role Pop the top of the stack, if it's true, jump to an address
152
153 // @role Create a new local scope
155
156 // @role Reset the current scope so that it is empty, and jump to a given location
158
159 // @role Destroy the last local scope
160 POP_SCOPE = 0x22,
161
162 // @args symbol id (function name)
163 // @role Push the current page address as a value on the stack
165
167
168 // @role Push `TS1 + TS`
169 ADD = 0x24,
170
171 // @role Push `TS1 - TS`
172 SUB = 0x25,
173
174 // @role Push `TS1 * TS`
175 MUL = 0x26,
176
177 // @role Push `TS1 / TS`
178 DIV = 0x27,
179
180 // @role Push `TS1 > TS`
181 GT = 0x28,
182
183 // @role Push `TS1 < TS`
184 LT = 0x29,
185
186 // @role Push `TS1 <= TS`
187 LE = 0x2a,
188
189 // @role Push `TS1 >= TS`
190 GE = 0x2b,
191
192 // @role Push `TS1 != TS`
193 NEQ = 0x2c,
194
195 // @role Push `TS1 == TS`
196 EQ = 0x2d,
197
198 // @role Push `len(TS)`, TS must be a list
199 LEN = 0x2e,
200
201 // @role Push `empty?(TS)`, TS must be a list or string
202 EMPTY = 0x2f,
203
204 // @role Push `tail(TS)`, all the elements of TS except the first one. TS must be a list or string
205 TAIL = 0x30,
206
207 // @role Push `head(TS)`, the first element of TS or nil if empty. TS must be a list or string
208 HEAD = 0x31,
209
210 // @role Push true if TS is nil, false otherwise
211 ISNIL = 0x32,
212
213 // @role Throw an exception if TS1 is false, and display TS (must be a string). Do not push anything on the stack
214 ASSERT = 0x33,
215
216 // @role Convert TS to number (must be a string)
217 TO_NUM = 0x34,
218
219 // @role Convert TS to string
220 TO_STR = 0x35,
221
222 // @role Push the value at index TS (must be a number) in TS1, which must be a list or string
223 AT = 0x36,
224
225 // @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
226 AT_AT = 0x37,
227
228 // @role Push `TS1 % TS`
229 MOD = 0x38,
230
231 // @role Push the type of TS as a string
232 TYPE = 0x39,
233
234 // @role Check if TS1 is a closure field of TS. TS must be a Closure, TS1 a String
235 HASFIELD = 0x3a,
236
237 // @role Push `!TS`
238 NOT = 0x3b,
239
240 // @args constant id, constant id
241 // @role Load two consts (`primary` then `secondary`) on the stack in one instruction
243
244 // @args constant id, symbol id
245 // @role Load const `primary` into the symbol `secondary` (create a variable)
247
248 // @args constant id, symbol id
249 // @role Load const `primary` into the symbol `secondary` (search for the variable with the given symbol id)
251
252 // @args symbol id, symbol id
253 // @role Store the value of the symbol `primary` into a new variable `secondary`
255
256 // @args symbol index, symbol id
257 // @role Store the value of the symbol `primary` into a new variable `secondary`
259
260 // @args symbol id, symbol id
261 // @role Store the value of the symbol `primary` into an existing variable `secondary`
263
264 // @args symbol index, symbol id
265 // @role Store the value of the symbol `primary` into an existing variable `secondary`
267
268 // @args symbol id, count
269 // @role Increment the variable `primary` by `count` and push its value on the stack
270 INCREMENT = 0x43,
271
272 // @args symbol index, count
273 // @role Increment the variable `primary` by `count` and push its value on the stack
275
276 // @args symbol id, count
277 // @role Increment the variable `primary` by `count` and store its value in the given symbol id
279
280 // @args symbol id, count
281 // @role Decrement the variable `primary` by `count` and push its value on the stack
282 DECREMENT = 0x46,
283
284 // @args symbol index, count
285 // @role Decrement the variable `primary` by `count` and push its value on the stack
287
288 // @args symbol id, count
289 // @role Decrement the variable `primary` by `count` and store its value in the given symbol id
291
292 // @args symbol id, symbol id
293 // @role Load the symbol `primary`, compute its tail, store it in a new variable `secondary`
295
296 // @args symbol index, symbol id
297 // @role Load the symbol `primary`, compute its tail, store it in a new variable `secondary`
299
300 // @args symbol id, symbol id
301 // @role Load the symbol `primary`, compute its head, store it in a new variable `secondary`
303
304 // @args symbol index, symbol id
305 // @role Load the symbol `primary`, compute its head, store it in a new variable `secondary`
307
308 // @args number, symbol id
309 // @role Create a list of `number` elements, and store it in a new variable `secondary`
311
312 // @args symbol id, symbol id
313 // @role Load the symbol `primary`, compute its tail, store it in an existing variable `secondary`
315
316 // @args symbol index, symbol id
317 // @role Load the symbol `primary`, compute its tail, store it in an existing variable `secondary`
319
320 // @args symbol id, symbol id
321 // @role Load the symbol `primary`, compute its head, store it in an existing variable `secondary`
323
324 // @args symbol index, symbol id
325 // @role Load the symbol `primary`, compute its head, store it in an existing variable `secondary`
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
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, as well as the return address removal
335
336 // @args constant id, absolute address to jump to
337 // @role Compare `TS < constant`, if the comparison fails, jump to the given address. Otherwise, does nothing
339
340 // @args constant id, absolute address to jump to
341 // @role Compare `TS < constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
343
344 // @args symbol id, absolute address to jump to
345 // @role Compare `TS < symbol`, if the comparison fails, 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 succeeds, 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 fails, jump to the given address. Otherwise, does nothing
355
356 // @args symbol id, absolute address to jump to
357 // @role Compare `TS > symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing
359
360 // @args constant id, absolute address to jump to
361 // @role Compare `TS == constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
363
364 // @args symbol index, absolute address to jump to
365 // @role Compare `TS == symbol`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
367
368 // @args constant id, absolute address to jump to
369 // @role Compare `TS != constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing
371
372 // @args symbol id, absolute address to jump to
373 // @role Compare `TS != symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing
375
376 // @args symbol id, argument count
377 // @role Call a symbol by its id in `primary`, with `secondary` arguments
379
380 // @args symbol id (function name), argument count
381 // @role Call the current page with `secondary` arguments
383
384 // @args symbol id, 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 index, 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 id, symbol id2
393 // @role Push symbol[symbol2]
395
396 // @args symbol index, symbol index2
397 // @role Push symbol[symbol2]
399
400 // @args symbol index, constant id
401 // @role Push symbol[constant]
403
404 // @args symbol id, constant id
405 // @role Check that the type of symbol is the given constant, push true if so, false otherwise
407
408 // @args symbol index, constant id
409 // @role Check that the type of symbol is the given constant, push true if so, false otherwise
411
412 // @args symbol id, number of elements
413 // @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
415
416 // @args symbol index, number of elements
417 // @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
419
420 // @args symbol index, symbol id
421 // @role Compute the length of the list or string at symbol index, and store it in a variable (symbol id)
422 STORE_LEN = 0x69,
423
424 // @args symbol id, absolute address to jump to
425 // @role Compute the length of a symbol (list or string), and pop TS to compare it, then jump if false
427
429 };
430
431 constexpr std::array InstructionNames = {
432 "NOP",
433 "LOAD_SYMBOL",
434 "LOAD_SYMBOL_BY_INDEX",
435 "LOAD_CONST",
436 "POP_JUMP_IF_TRUE",
437 "STORE",
438 "SET_VAL",
439 "POP_JUMP_IF_FALSE",
440 "JUMP",
441 "RET",
442 "HALT",
443 "PUSH_RETURN_ADDRESS",
444 "CALL",
445 "CAPTURE",
446 "RENAME_NEXT_CAPTURE",
447 "BUILTIN",
448 "DEL",
449 "MAKE_CLOSURE",
450 "GET_FIELD",
451 "PLUGIN",
452 "LIST",
453 "APPEND",
454 "CONCAT",
455 "APPEND_IN_PLACE",
456 "CONCAT_IN_PLACE",
457 "POP_LIST",
458 "POP_LIST_IN_PLACE",
459 "SET_AT_INDEX",
460 "SET_AT_2_INDEX",
461 "POP",
462 "SHORTCIRCUIT_AND",
463 "SHORTCIRCUIT_OR",
464 "CREATE_SCOPE",
465 "RESET_SCOPE_JUMP",
466 "POP_SCOPE",
467 "GET_CURRENT_PAGE_ADDR",
468 // operators
469 "ADD",
470 "SUB",
471 "MUL",
472 "DIV",
473 "GT",
474 "LT",
475 "LE",
476 "GE",
477 "NEQ",
478 "EQ",
479 "LEN",
480 "EMPTY",
481 "TAIL",
482 "HEAD",
483 "ISNIL",
484 "ASSERT",
485 "TO_NUM",
486 "TO_STR",
487 "AT",
488 "AT_AT",
489 "MOD",
490 "TYPE",
491 "HASFIELD",
492 "NOT",
493 // super instructions
494 "LOAD_CONST_LOAD_CONST",
495 "LOAD_CONST_STORE",
496 "LOAD_CONST_SET_VAL",
497 "STORE_FROM",
498 "STORE_FROM_INDEX",
499 "SET_VAL_FROM",
500 "SET_VAL_FROM_INDEX",
501 "INCREMENT",
502 "INCREMENT_BY_INDEX",
503 "INCREMENT_STORE",
504 "DECREMENT",
505 "DECREMENT_BY_INDEX",
506 "DECREMENT_STORE",
507 "STORE_TAIL",
508 "STORE_TAIL_BY_INDEX",
509 "STORE_HEAD",
510 "STORE_HEAD_BY_INDEX",
511 "STORE_LIST",
512 "SET_VAL_TAIL",
513 "SET_VAL_TAIL_BY_INDEX",
514 "SET_VAL_HEAD",
515 "SET_VAL_HEAD_BY_INDEX",
516 "CALL_BUILTIN",
517 "CALL_BUILTIN_WITHOUT_RETURN_ADDRESS",
518 "LT_CONST_JUMP_IF_FALSE",
519 "LT_CONST_JUMP_IF_TRUE",
520 "LT_SYM_JUMP_IF_FALSE",
521 "GT_CONST_JUMP_IF_TRUE",
522 "GT_CONST_JUMP_IF_FALSE",
523 "GT_SYM_JUMP_IF_FALSE",
524 "EQ_CONST_JUMP_IF_TRUE",
525 "EQ_SYM_INDEX_JUMP_IF_TRUE",
526 "NEQ_CONST_JUMP_IF_TRUE",
527 "NEQ_SYM_JUMP_IF_FALSE",
528 "CALL_SYMBOL",
529 "CALL_CURRENT_PAGE",
530 "GET_FIELD_FROM_SYMBOL",
531 "GET_FIELD_FROM_SYMBOL_INDEX",
532 "AT_SYM_SYM",
533 "AT_SYM_INDEX_SYM_INDEX",
534 "AT_SYM_INDEX_CONST",
535 "CHECK_TYPE_OF",
536 "CHECK_TYPE_OF_BY_INDEX",
537 "APPEND_IN_PLACE_SYM",
538 "APPEND_IN_PLACE_SYM_INDEX",
539 "STORE_LEN",
540 "LT_LEN_SYM_JUMP_IF_FALSE"
541 };
542
543 static_assert(InstructionNames.size() == static_cast<std::size_t>(Instruction::InstructionsCount) && "Some instruction names appear to be missing");
544}
545
546#endif
Instruction
The different bytecodes are stored here.
@ CALL_BUILTIN_WITHOUT_RETURN_ADDRESS
constexpr std::array InstructionNames