Skip to content

Commit

Permalink
feat(name resolution): somewhat better handling of closures and captures
Browse files Browse the repository at this point in the history
  • Loading branch information
SuperFola committed Dec 2, 2024
1 parent f15a5b4 commit 4f45458
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 8 deletions.
9 changes: 9 additions & 0 deletions include/Ark/VM/Value/Closure.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ namespace Ark::internal
*/
[[nodiscard]] PageAddr_t pageAddr() const { return m_page_addr; }

/**
* @brief Used when generating error messages in the VM, to see if a symbol might have been wrongly fully qualified
*
* @param end
* @param vm
* @return true if the closure has a field which is the end of 'end'
*/
[[nodiscard]] bool hasFieldEndingWith(const std::string& end, VM& vm) const;

/**
* @brief Print the closure to a string
*
Expand Down
17 changes: 16 additions & 1 deletion src/arkreactor/VM/VM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,22 @@ namespace Ark
push(field, context);
}
else
throwVMError(ErrorKind::Scope, fmt::format("`{}' isn't in the closure environment: {}", m_state.m_symbols[arg], var->refClosure().toString(*this)));
{
if (!var->refClosure().hasFieldEndingWith(m_state.m_symbols[arg], *this))
throwVMError(
ErrorKind::Scope,
fmt::format(
"`{0}' isn't in the closure environment: {1}",
m_state.m_symbols[arg],
var->refClosure().toString(*this)));
throwVMError(
ErrorKind::Scope,
fmt::format(
"`{0}' isn't in the closure environment: {1}. A variable in the package might have the same name as '{0}', "
"and name resolution tried to fully qualify it. Rename either the variable or the capture to solve this",
m_state.m_symbols[arg],
var->refClosure().toString(*this)));
}
DISPATCH();
}

Expand Down
12 changes: 12 additions & 0 deletions src/arkreactor/VM/Value/Closure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <Ark/VM/Value.hpp>
#include <Ark/VM/VM.hpp>

#include <ranges>

namespace Ark::internal
{
Closure::Closure(const Scope& scope, const PageAddr_t pa) noexcept :
Expand All @@ -16,6 +18,16 @@ namespace Ark::internal
m_page_addr(pa)
{}

bool Closure::hasFieldEndingWith(const std::string& end, VM& vm) const
{
for (const auto id : std::ranges::views::keys(m_scope->m_data))
{
if (end.ends_with(":" + vm.m_state.m_symbols[id]))
return true;
}
return false;
}

std::string Closure::toString(VM& vm) const noexcept
{
std::string out = "(";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(import closure_field_wrong_fqn.b)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ScopeError: `b:b' isn't in the closure environment: (.a=hello .b=1). A variable in the package might have the same name as 'b:b', and name resolution tried to fully qualify it. Rename either the variable or the capture to solve this
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
(let tests 0)
(let closure (fun (&tests) ()))

(let a 1)
(let b 2)

(let make (fun (a b)
(fun (&a &b) ())))
(let foo (make "hello" 1))
(let c [foo.a foo.b])
10 changes: 5 additions & 5 deletions tests/unittests/resources/LangSuite/list-tests.ark
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
(let b [4 5 6])

(test:suite list {
(let make (fun (a b)
(fun (&a &b) ())))
(let make (fun (c d)
(fun (&c &d) ())))
(let foo (make "hello" 1))

# if this is failing, this is most likely to be a compiler problem
(test:eq ["hello" 1] [foo.a foo.b])
(test:eq 2 (len [foo.a foo.b]))
(test:eq ["hello"] (append [] foo.a))
(test:eq ["hello" 1] [foo.c foo.d])
(test:eq 2 (len [foo.c foo.d]))
(test:eq ["hello"] (append [] foo.c))

(test:case "append and return a new list" {
(test:eq (append a 4) [1 2 3 4])
Expand Down
4 changes: 2 additions & 2 deletions tests/unittests/resources/LangSuite/vm-tests.ark
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@
(test:eq (type nil) "Nil")
(test:eq (type true) "Bool")
(test:eq (type false) "Bool")
(test:expect (hasField closure "tests"))
(test:expect (hasField closure "vm-tests:tests"))
(test:expect (not (hasField closure "12"))) })

(test:case "closures" {
(test:eq (toString closure) "(.tests=0)")
(test:eq (toString closure) "(.vm-tests:tests=0)")
(test:eq closure_1 closure_1_bis)
(test:eq closure_1 closure_2)
(test:neq closure_1 closure_4)
Expand Down

0 comments on commit 4f45458

Please sign in to comment.