Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
- re-enabled the AST optimizer, only used for the main `arkscript` executable (not enabled when embedding arkscript, so that one can grab variables from the VM)
- loops have their own scope: variables created inside a loop won't leak outside it
- upgraded fmtlib to 11.1.3-13
- allow capture in nested scope (before it was targeting only the current scope)

### Removed
- removed unused `NodeType::Closure`
Expand Down
4 changes: 2 additions & 2 deletions include/Ark/VM/VM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ namespace Ark
// instruction helpers
// ================================================

inline Value* loadSymbol(uint16_t id, internal::ExecutionContext& context);
inline Value* loadConstAsPtr(uint16_t id) const;
[[nodiscard]] inline Value* loadSymbol(uint16_t id, internal::ExecutionContext& context);
[[nodiscard]] inline Value* loadConstAsPtr(uint16_t id) const;
inline void store(uint16_t id, const Value* val, internal::ExecutionContext& context);
inline void setVal(uint16_t id, const Value* val, internal::ExecutionContext& context);

Expand Down
50 changes: 26 additions & 24 deletions src/arkreactor/VM/VM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ namespace Ark
// value on the stack
else [[likely]]
{
Value* ip;
const Value* ip;
do
{
ip = popAndResolveAsPtr(context);
Expand Down Expand Up @@ -556,7 +556,7 @@ namespace Ark
if (!context.saved_scope)
context.saved_scope = Scope();

Value* ptr = (context.locals.back())[arg];
const Value* ptr = findNearestVariable(arg, context);
if (!ptr)
throwVMError(ErrorKind::Scope, fmt::format("Couldn't capture `{}' as it is currently unbound", m_state.m_symbols[arg]));
else
Expand Down Expand Up @@ -992,49 +992,49 @@ namespace Ark

TARGET(GT)
{
Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
const Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
push((*a != *b && !(*a < *b)) ? Builtins::trueSym : Builtins::falseSym, context);
DISPATCH();
}

TARGET(LT)
{
Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
const Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
push((*a < *b) ? Builtins::trueSym : Builtins::falseSym, context);
DISPATCH();
}

TARGET(LE)
{
Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
const Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
push((((*a < *b) || (*a == *b)) ? Builtins::trueSym : Builtins::falseSym), context);
DISPATCH();
}

TARGET(GE)
{
Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
const Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
push(!(*a < *b) ? Builtins::trueSym : Builtins::falseSym, context);
DISPATCH();
}

TARGET(NEQ)
{
Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
const Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
push((*a != *b) ? Builtins::trueSym : Builtins::falseSym, context);
DISPATCH();
}

TARGET(EQ)
{
Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
const Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
push((*a == *b) ? Builtins::trueSym : Builtins::falseSym, context);
DISPATCH();
}

TARGET(LEN)
{
Value* a = popAndResolveAsPtr(context);
const Value* a = popAndResolveAsPtr(context);

if (a->valueType() == ValueType::List)
push(Value(static_cast<int>(a->constList().size())), context);
Expand All @@ -1051,7 +1051,7 @@ namespace Ark

TARGET(EMPTY)
{
Value* a = popAndResolveAsPtr(context);
const Value* a = popAndResolveAsPtr(context);

if (a->valueType() == ValueType::List)
push(a->constList().empty() ? Builtins::trueSym : Builtins::falseSym, context);
Expand All @@ -1068,28 +1068,29 @@ namespace Ark

TARGET(TAIL)
{
Value* a = popAndResolveAsPtr(context);
Value* const a = popAndResolveAsPtr(context);
push(helper::tail(a), context);
DISPATCH();
}

TARGET(HEAD)
{
Value* a = popAndResolveAsPtr(context);
Value* const a = popAndResolveAsPtr(context);
push(helper::head(a), context);
DISPATCH();
}

TARGET(ISNIL)
{
Value* a = popAndResolveAsPtr(context);
const Value* a = popAndResolveAsPtr(context);
push((*a == Builtins::nil) ? Builtins::trueSym : Builtins::falseSym, context);
DISPATCH();
}

TARGET(ASSERT)
{
Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
Value* const b = popAndResolveAsPtr(context);
Value* const a = popAndResolveAsPtr(context);

if (b->valueType() != ValueType::String)
types::generateError(
Expand All @@ -1104,7 +1105,7 @@ namespace Ark

TARGET(TO_NUM)
{
Value* a = popAndResolveAsPtr(context);
const Value* a = popAndResolveAsPtr(context);

if (a->valueType() != ValueType::String)
types::generateError(
Expand All @@ -1122,15 +1123,15 @@ namespace Ark

TARGET(TO_STR)
{
Value* a = popAndResolveAsPtr(context);
const Value* a = popAndResolveAsPtr(context);
push(Value(a->toString(*this)), context);
DISPATCH();
}

TARGET(AT)
{
{
Value* b = popAndResolveAsPtr(context);
const Value* b = popAndResolveAsPtr(context);
Value a = *popAndResolveAsPtr(context); // be careful, it's not a pointer

if (b->valueType() != ValueType::Number)
Expand Down Expand Up @@ -1173,8 +1174,8 @@ namespace Ark
TARGET(AT_AT)
{
{
Value* x = popAndResolveAsPtr(context);
Value* y = popAndResolveAsPtr(context);
const Value* x = popAndResolveAsPtr(context);
const Value* y = popAndResolveAsPtr(context);
Value list = *popAndResolveAsPtr(context); // be careful, it's not a pointer

if (y->valueType() != ValueType::Number || x->valueType() != ValueType::Number ||
Expand Down Expand Up @@ -1217,7 +1218,7 @@ namespace Ark

TARGET(MOD)
{
Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
const Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context);
if (a->valueType() != ValueType::Number || b->valueType() != ValueType::Number)
types::generateError(
"mod",
Expand All @@ -1229,7 +1230,7 @@ namespace Ark

TARGET(TYPE)
{
Value* a = popAndResolveAsPtr(context);
const Value* a = popAndResolveAsPtr(context);
if (a == &m_undefined_value) [[unlikely]]
types::generateError(
"type",
Expand All @@ -1243,14 +1244,15 @@ namespace Ark
TARGET(HASFIELD)
{
{
Value *field = popAndResolveAsPtr(context), *closure = popAndResolveAsPtr(context);
Value* const field = popAndResolveAsPtr(context);
Value* const closure = popAndResolveAsPtr(context);
if (closure->valueType() != ValueType::Closure || field->valueType() != ValueType::String)
types::generateError(
"hasField",
{ { types::Contract { { types::Typedef("closure", ValueType::Closure), types::Typedef("field", ValueType::String) } } } },
{ *closure, *field });

auto it = std::find(m_state.m_symbols.begin(), m_state.m_symbols.end(), field->stringRef());
auto it = std::ranges::find(m_state.m_symbols, field->stringRef());
if (it == m_state.m_symbols.end())
{
push(Builtins::falseSym, context);
Expand All @@ -1265,7 +1267,7 @@ namespace Ark

TARGET(NOT)
{
Value* a = popAndResolveAsPtr(context);
const Value* a = popAndResolveAsPtr(context);
push(!(*a) ? Builtins::trueSym : Builtins::falseSym, context);
DISPATCH();
}
Expand Down
62 changes: 37 additions & 25 deletions tests/unittests/resources/LangSuite/builtins-tests.ark
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,43 @@
(let closure (fun (&foo) ()))

(test:suite builtin {
(test:eq (type []) "List")
(test:eq (type true) "Bool")
(test:eq (type false) "Bool")
(test:eq (type nil) "Nil")
(test:eq (type 1) "Number")
(test:eq (type "hello") "String")
(test:eq (type print) "CProc")
(test:eq (type foo) "Function")
(test:eq (type closure) "Closure")

(test:expect (not (io:fileExists? "test.txt")))
(io:writeFile "test.txt" "hello, world!")
(test:expect (io:fileExists? "test.txt"))
(test:eq (io:readFile "test.txt") "hello, world!")
(io:appendToFile "test.txt" "bis")
(test:eq (io:readFile "test.txt") "hello, world!bis")
(test:expect (> (len (io:listFiles "./")) 0))
(test:expect (not (io:dir? "test.txt")))
(test:expect (not (io:fileExists? "temp")))
(io:makeDir "temp")
(test:expect (io:fileExists? "temp"))
(test:expect (io:dir? "temp"))
(let old (time))
(sys:sleep 1)
(test:expect (< old (time)))
(test:case "types" {
(test:eq (type []) "List")
(test:eq (type true) "Bool")
(test:eq (type false) "Bool")
(test:eq (type nil) "Nil")
(test:eq (type 1) "Number")
(test:eq (type "hello") "String")
(test:eq (type print) "CProc")
(test:eq (type foo) "Function")
(test:eq (type closure) "Closure") })

(test:case "closures" {
(mut keep true)
(mut foo nil)
(while keep {
(set foo (fun (&keep) keep))
(set keep false) })
(test:expect foo.keep "capture inside a deeper scope works") })

(test:case "files" {
(test:expect (not (io:fileExists? "test.txt")))
(io:writeFile "test.txt" "hello, world!")
(test:expect (io:fileExists? "test.txt"))
(test:eq (io:readFile "test.txt") "hello, world!")
(io:appendToFile "test.txt" "bis")
(test:eq (io:readFile "test.txt") "hello, world!bis")
(test:expect (> (len (io:listFiles "./")) 0))
(test:expect (not (io:dir? "test.txt")))
(test:expect (not (io:fileExists? "temp")))
(io:makeDir "temp")
(test:expect (io:fileExists? "temp"))
(test:expect (io:dir? "temp")) })

(test:case "time" {
(let old (time))
(sys:sleep 1)
(test:expect (< old (time))) })

(mut rands [])
(mut i 0)
Expand Down
Loading