diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c38db3f..61104ebe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ - `STORE_FROM_INDEX` and `SET_VAL_FROM_INDEX` instructions for parity with the super instructions not using load by index - `INCREMENT_BY_INDEX` and `DECREMENT_BY_INDEX` instructions for parity with the super instructions not using load by index - `STORE_TAIL_BY_INDEX`, `STORE_HEAD_BY_INDEX`, `SET_VAL_TAIL_BY_INDEX`, `SET_VAL_HEAD_BY_INDEX` super instructions added for parity with the super instructions not using load by index +- `RESET_SCOPE` instruction emitted at the end of a while loop to reset a scope so that we can create multiple variables and use `LOAD_SYMBOL_BY_INDEX` ### Changed - instructions are on 4 bytes: 1 byte for the instruction, 1 byte of padding, 2 bytes for an immediate argument diff --git a/include/Ark/Compiler/Instructions.hpp b/include/Ark/Compiler/Instructions.hpp index f789a372..d1d4ad72 100644 --- a/include/Ark/Compiler/Instructions.hpp +++ b/include/Ark/Compiler/Instructions.hpp @@ -142,162 +142,165 @@ namespace Ark::internal // @role Create a new local scope CREATE_SCOPE = 0x1d, + // @role Reset the current scope so that it is empty + RESET_SCOPE = 0x1e, + // @role Destroy the last local scope - POP_SCOPE = 0x1e, + POP_SCOPE = 0x1f, - FIRST_OPERATOR = 0x1f, + FIRST_OPERATOR = 0x20, // @role Push #[code TS1 + TS] - ADD = 0x1f, + ADD = 0x20, // @role Push #[code TS1 - TS] - SUB = 0x20, + SUB = 0x21, // @role Push #[code TS1 * TS] - MUL = 0x21, + MUL = 0x22, // @role Push #[code TS1 / TS] - DIV = 0x22, + DIV = 0x23, // @role Push #[code TS1 > TS] - GT = 0x23, + GT = 0x24, // @role Push #[code TS1 < TS] - LT = 0x24, + LT = 0x25, // @role Push #[code TS1 <= TS] - LE = 0x25, + LE = 0x26, // @role Push #[code TS1 >= TS] - GE = 0x26, + GE = 0x27, // @role Push #[code TS1 != TS] - NEQ = 0x27, + NEQ = 0x28, // @role Push #[code TS1 == TS] - EQ = 0x28, + EQ = 0x29, // @role Push #[code len(TS)], TS must be a list - LEN = 0x29, + LEN = 0x2a, // @role Push #[code empty?(TS)], TS must be a list or string - EMPTY = 0x2a, + EMPTY = 0x2b, // @role Push #[code tail(TS)], all the elements of TS except the first one. TS must be a list or string - TAIL = 0x2b, + TAIL = 0x2c, // @role Push #[code head(TS)], the first element of TS or nil if empty. TS must be a list or string - HEAD = 0x2c, + HEAD = 0x2d, // @role Push true if TS is nil, false otherwise - ISNIL = 0x2d, + ISNIL = 0x2e, // @role Throw an exception if TS1 is false, and display TS (must be a string). Do not push anything on the stack - ASSERT = 0x2e, + ASSERT = 0x2f, // @role Convert TS to number (must be a string) - TO_NUM = 0x2f, + TO_NUM = 0x30, // @role Convert TS to string - TO_STR = 0x30, + TO_STR = 0x31, // @role Push the value at index TS (must be a number) in TS1, which must be a list or string - AT = 0x31, + AT = 0x32, // @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 - AT_AT = 0x32, + AT_AT = 0x33, // @role Push #[code TS1 % TS] - MOD = 0x33, + MOD = 0x34, // @role Push the type of TS as a string - TYPE = 0x34, + TYPE = 0x35, // @role Check if TS1 is a closure field of TS. TS must be a Closure, TS1 a String - HASFIELD = 0x35, + HASFIELD = 0x36, // @role Push #[code !TS] - NOT = 0x36, + NOT = 0x37, // @args constant id, constant id // @role Load two consts (#[code primary] then #[code secondary]) on the stack in one instruction - LOAD_CONST_LOAD_CONST = 0x37, + LOAD_CONST_LOAD_CONST = 0x38, // @args constant id, symbol id // @role Load const #[code primary] into the symbol #[code secondary] (create a variable) - LOAD_CONST_STORE = 0x38, + LOAD_CONST_STORE = 0x39, // @args constant id, symbol id // @role Load const #[code primary] into the symbol #[code secondary] (search for the variable with the given symbol id) - LOAD_CONST_SET_VAL = 0x39, + LOAD_CONST_SET_VAL = 0x3a, // @args symbol id, symbol id // @role Store the value of the symbol #[code primary] into a new variable #[code secondary] - STORE_FROM = 0x3a, + STORE_FROM = 0x3b, // @args symbol index, symbol id // @role Store the value of the symbol #[code primary] into a new variable #[code secondary] - STORE_FROM_INDEX = 0x3b, + STORE_FROM_INDEX = 0x3c, // @args symbol id, symbol id // @role Store the value of the symbol #[code primary] into an existing variable #[code secondary] - SET_VAL_FROM = 0x3c, + SET_VAL_FROM = 0x3d, // @args symbol index, symbol id // @role Store the value of the symbol #[code primary] into an existing variable #[code secondary] - SET_VAL_FROM_INDEX = 0x3d, + SET_VAL_FROM_INDEX = 0x3e, // @args symbol id, count // @role Increment the variable #[code primary] by #[code count] and push its value on the stack - INCREMENT = 0x3e, + INCREMENT = 0x3f, // @args symbol index, count // @role Increment the variable #[code primary] by #[code count] and push its value on the stack - INCREMENT_BY_INDEX = 0x3f, + INCREMENT_BY_INDEX = 0x40, // @args symbol id, count // @role Decrement the variable #[code primary] by #[code count] and push its value on the stack - DECREMENT = 0x40, + DECREMENT = 0x41, // @args symbol index, count // @role Decrement the variable #[code primary] by #[code count] and push its value on the stack - DECREMENT_BY_INDEX = 0x41, + DECREMENT_BY_INDEX = 0x42, // @args symbol id, symbol id // @role Load the symbol #[code primary], compute its tail, store it in a new variable #[code secondary] - STORE_TAIL = 0x42, + STORE_TAIL = 0x43, // @args symbol index, symbol id // @role Load the symbol #[code primary], compute its tail, store it in a new variable #[code secondary] - STORE_TAIL_BY_INDEX = 0x43, + STORE_TAIL_BY_INDEX = 0x44, // @args symbol id, symbol id // @role Load the symbol #[code primary], compute its head, store it in a new variable #[code secondary] - STORE_HEAD = 0x44, + STORE_HEAD = 0x45, // @args symbol index, symbol id // @role Load the symbol #[code primary], compute its head, store it in a new variable #[code secondary] - STORE_HEAD_BY_INDEX = 0x45, + STORE_HEAD_BY_INDEX = 0x46, // @args symbol id, symbol id // @role Load the symbol #[code primary], compute its tail, store it in an existing variable #[code secondary] - SET_VAL_TAIL = 0x46, + SET_VAL_TAIL = 0x47, // @args symbol index, symbol id // @role Load the symbol #[code primary], compute its tail, store it in an existing variable #[code secondary] - SET_VAL_TAIL_BY_INDEX = 0x47, + SET_VAL_TAIL_BY_INDEX = 0x48, // @args symbol id, symbol id // @role Load the symbol #[code primary], compute its head, store it in an existing variable #[code secondary] - SET_VAL_HEAD = 0x48, + SET_VAL_HEAD = 0x49, // @args symbol index, symbol id // @role Load the symbol #[code primary], compute its head, store it in an existing variable #[code secondary] - SET_VAL_HEAD_BY_INDEX = 0x49, + SET_VAL_HEAD_BY_INDEX = 0x4a, // @args builtin id, argument count // @role Call a builtin by its id in #[code primary], with #[code secondary] arguments. Bypass the stack size check because we do not push IP/PP since builtins calls do not alter the stack - CALL_BUILTIN = 0x4a, + CALL_BUILTIN = 0x4b, InstructionsCount }; @@ -333,6 +336,7 @@ namespace Ark::internal "POP", "DUP", "CREATE_SCOPE", + "RESET_SCOPE", "POP_SCOPE", // operators "ADD", diff --git a/include/Ark/VM/ScopeView.hpp b/include/Ark/VM/ScopeView.hpp index 909a7880..5d64f9b5 100644 --- a/include/Ark/VM/ScopeView.hpp +++ b/include/Ark/VM/ScopeView.hpp @@ -111,6 +111,11 @@ namespace Ark::internal return m_storage[m_start + m_size - 1 - i]; } + /** + * @brief Reset size, min and max id for the scope, to signify it's empty + */ + void reset() noexcept; + /** * @brief Return the size of the scope * diff --git a/lib/std b/lib/std index 7b86e899..69d782b6 160000 --- a/lib/std +++ b/lib/std @@ -1 +1 @@ -Subproject commit 7b86e899a199bc98713549ef4df5db9f52a075ef +Subproject commit 69d782b69629d19648b0011c843aaac047f1dd49 diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index dd19e1c2..17c4ee2e 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -440,6 +440,10 @@ namespace Ark::internal // push code to page compileExpression(x.constList()[2], p, true, false); + // reset the scope at the end of the loop so that indices are still valid + // otherwise, (while true { (let a 5) (print a) (let b 6) (print b) }) + // would print 5, 6, then only 6 as we emit LOAD_SYMBOL_FROM_INDEX 0 and b is the last in the scope + page(p).emplace_back(RESET_SCOPE); // loop, jump to the condition page(p).emplace_back(IR::Entity::Goto(label_loop)); diff --git a/src/arkreactor/VM/ScopeView.cpp b/src/arkreactor/VM/ScopeView.cpp index 97cfbe24..4ec67f27 100644 --- a/src/arkreactor/VM/ScopeView.cpp +++ b/src/arkreactor/VM/ScopeView.cpp @@ -74,6 +74,13 @@ namespace Ark::internal return std::numeric_limits::max(); } + void ScopeView::reset() noexcept + { + m_size = 0; + m_min_id = std::numeric_limits::max(); + m_max_id = 0; + } + bool operator==(const ScopeView& A, const ScopeView& B) noexcept { // if we have two scopes with the same number of elements and starting at the same position, diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 1a4a8c7c..5ece28f6 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -391,6 +391,7 @@ namespace Ark &&TARGET_POP, &&TARGET_DUP, &&TARGET_CREATE_SCOPE, + &&TARGET_RESET_SCOPE, &&TARGET_POP_SCOPE, &&TARGET_ADD, &&TARGET_SUB, @@ -939,6 +940,12 @@ namespace Ark DISPATCH(); } + TARGET(RESET_SCOPE) + { + context.locals.back().reset(); + DISPATCH(); + } + TARGET(POP_SCOPE) { context.locals.pop_back(); diff --git a/tests/unittests/resources/CompilerSuite/ir/99bottles.expected b/tests/unittests/resources/CompilerSuite/ir/99bottles.expected index ec7a15d1..fa715426 100644 --- a/tests/unittests/resources/CompilerSuite/ir/99bottles.expected +++ b/tests/unittests/resources/CompilerSuite/ir/99bottles.expected @@ -49,6 +49,7 @@ page_0 BUILTIN 9 CALL 1 POP 0 + RESET_SCOPE 0 GOTO L4 .L5: POP_SCOPE 0 diff --git a/tests/unittests/resources/CompilerSuite/ir/factorial.expected b/tests/unittests/resources/CompilerSuite/ir/factorial.expected index 21dc1516..03a0d795 100644 --- a/tests/unittests/resources/CompilerSuite/ir/factorial.expected +++ b/tests/unittests/resources/CompilerSuite/ir/factorial.expected @@ -29,6 +29,7 @@ page_1 LOAD_SYMBOL 3 ADD 0 SET_VAL 3 + RESET_SCOPE 0 GOTO L0 .L1: POP_SCOPE 0 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected index 43cbbc15..3101589d 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected @@ -42,6 +42,7 @@ page_0 CALL_BUILTIN 24, 2 CALL_BUILTIN 9, 1 POP 0 + RESET_SCOPE 0 GOTO L4 .L5: POP_SCOPE 0 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected index 0574a614..757b4947 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected @@ -22,6 +22,7 @@ page_1 SET_VAL 2 INCREMENT 3, 1 SET_VAL 3 + RESET_SCOPE 0 GOTO L0 .L1: POP_SCOPE 0