Skip to content

Commit

Permalink
feat(list modifying in place): adding @= and @@= to modify lists in p…
Browse files Browse the repository at this point in the history
…lace
  • Loading branch information
SuperFola committed Dec 10, 2024
1 parent 707f3e0 commit 9cfde62
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 49 deletions.
11 changes: 8 additions & 3 deletions include/Ark/Compiler/Common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,26 @@ namespace Ark::internal
constexpr std::string_view AppendInPlace = "append!";
constexpr std::string_view ConcatInPlace = "concat!";
constexpr std::string_view PopInPlace = "pop!";
constexpr std::string_view SetAtInPlace = "@=";
constexpr std::string_view SetAt2InPlace = "@@=";
/// All the builtins that modify in place a variable
constexpr std::array UpdateRef = {
AppendInPlace, ConcatInPlace, PopInPlace
AppendInPlace, ConcatInPlace, PopInPlace,
SetAtInPlace, SetAt2InPlace
};

// This list is related to include/Ark/Compiler/Instructions.hpp
// The order is very important
constexpr std::array<std::string_view, 7> listInstructions = {
constexpr std::array<std::string_view, 9> listInstructions = {
"list",
"append",
"concat",
AppendInPlace,
ConcatInPlace,
"pop",
PopInPlace
PopInPlace,
SetAtInPlace,
SetAt2InPlace
};

constexpr std::string_view SysArgs = "sys:args";
Expand Down
80 changes: 42 additions & 38 deletions include/Ark/Compiler/Instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,46 +57,48 @@ namespace Ark::internal
CONCAT_IN_PLACE = 0x15,
POP_LIST = 0x16,
POP_LIST_IN_PLACE = 0x17,
POP = 0x18,
DUP = 0x19,
SET_AT_INDEX = 0x18,
SET_AT_2_INDEX = 0x19,
POP = 0x1a,
DUP = 0x1b,

FIRST_OPERATOR = 0x1a,
ADD = 0x1a,
SUB = 0x1b,
MUL = 0x1c,
DIV = 0x1d,
GT = 0x1e,
LT = 0x1f,
LE = 0x20,
GE = 0x21,
NEQ = 0x22,
EQ = 0x23,
LEN = 0x24,
EMPTY = 0x25,
TAIL = 0x26,
HEAD = 0x27,
ISNIL = 0x28,
ASSERT = 0x29,
TO_NUM = 0x2a,
TO_STR = 0x2b,
AT = 0x2c,
MOD = 0x2d,
TYPE = 0x2e,
HASFIELD = 0x2f,
NOT = 0x30,
FIRST_OPERATOR = 0x1c,
ADD = 0x1c,
SUB = 0x1d,
MUL = 0x1e,
DIV = 0x1f,
GT = 0x20,
LT = 0x21,
LE = 0x22,
GE = 0x23,
NEQ = 0x24,
EQ = 0x25,
LEN = 0x26,
EMPTY = 0x27,
TAIL = 0x28,
HEAD = 0x29,
ISNIL = 0x2a,
ASSERT = 0x2b,
TO_NUM = 0x2c,
TO_STR = 0x2d,
AT = 0x2e,
MOD = 0x2f,
TYPE = 0x30,
HASFIELD = 0x31,
NOT = 0x32,

LOAD_CONST_LOAD_CONST = 0x31,
LOAD_CONST_STORE = 0x32,
LOAD_CONST_SET_VAL = 0x33,
STORE_FROM = 0x34,
SET_VAL_FROM = 0x35,
INCREMENT = 0x36,
DECREMENT = 0x37,
STORE_TAIL = 0x38,
STORE_HEAD = 0x39,
SET_VAL_TAIL = 0x3a,
SET_VAL_HEAD = 0x3b,
CALL_BUILTIN = 0x3c
LOAD_CONST_LOAD_CONST = 0x33,
LOAD_CONST_STORE = 0x34,
LOAD_CONST_SET_VAL = 0x35,
STORE_FROM = 0x36,
SET_VAL_FROM = 0x37,
INCREMENT = 0x38,
DECREMENT = 0x39,
STORE_TAIL = 0x3a,
STORE_HEAD = 0x3b,
SET_VAL_TAIL = 0x3c,
SET_VAL_HEAD = 0x3d,
CALL_BUILTIN = 0x3e
};

constexpr std::array InstructionNames = {
Expand Down Expand Up @@ -124,6 +126,8 @@ namespace Ark::internal
"CONCAT_IN_PLACE",
"POP_LIST",
"POP_LIST_IN_PLACE",
"SET_AT_INDEX",
"SET_AT_2_INDEX",
"POP",
"DUP",
// operators
Expand Down
9 changes: 6 additions & 3 deletions include/Ark/VM/Value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,12 @@ namespace Ark
[[nodiscard]] ValueType valueType() const noexcept { return m_type; }
[[nodiscard]] bool isFunction() const noexcept
{
const auto type = valueType();
return type == ValueType::PageAddr || type == ValueType::Closure || type == ValueType::CProc ||
(type == ValueType::Reference && reference()->isFunction());
return m_type == ValueType::PageAddr || m_type == ValueType::Closure || m_type == ValueType::CProc ||
(m_type == ValueType::Reference && reference()->isFunction());
}
[[nodiscard]] bool isIndexable() const noexcept
{
return m_type == ValueType::List || m_type == ValueType::String;
}

[[nodiscard]] double number() const { return std::get<double>(m_value); }
Expand Down
18 changes: 13 additions & 5 deletions src/arkreactor/Compiler/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,14 @@ namespace Ark::internal
// length of at least 1 since we got a symbol name
const auto argc = x.constList().size() - 1u;
// error, can not use append/concat/pop (and their in place versions) with a <2 length argument list
if (argc < 2 && inst != LIST)
if (argc < 2 && APPEND <= inst && inst <= POP)
throwCompilerError(fmt::format("Can not use {} with less than 2 arguments", name), c0);
if (std::cmp_greater(argc, std::numeric_limits<uint16_t>::max()))
if (inst <= POP && std::cmp_greater(argc, std::numeric_limits<uint16_t>::max()))
throwCompilerError(fmt::format("Too many arguments ({}), exceeds 65'535", argc), x);
if (argc != 3 && inst == SET_AT_INDEX)
throwCompilerError(fmt::format("Expected 3 arguments for {}, got {}", name, argc), c0);
if (argc != 4 && inst == SET_AT_2_INDEX)
throwCompilerError(fmt::format("Expected 4 arguments for {}, got {}", name, argc), c0);

// compile arguments in reverse order
for (std::size_t i = x.constList().size() - 1u; i > 0; --i)
Expand All @@ -259,7 +263,7 @@ namespace Ark::internal
}

// put inst and number of arguments
std::size_t inst_argc;
std::size_t inst_argc = 0;
switch (inst)
{
case LIST:
Expand All @@ -273,13 +277,17 @@ namespace Ark::internal
inst_argc = argc - 1;
break;

default:
case POP_LIST:
case POP_LIST_IN_PLACE:
inst_argc = 0;
break;

default:
break;
}
page(p).emplace_back(inst, static_cast<uint16_t>(inst_argc));

if (is_result_unused && name.back() != '!') // in-place functions never push a value
if (is_result_unused && name.back() != '!' && inst <= POP_LIST_IN_PLACE) // in-place functions never push a value
{
compilerWarning("Ignoring return value of function", x);
page(p).emplace_back(POP);
Expand Down
100 changes: 100 additions & 0 deletions src/arkreactor/VM/VM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,8 @@ namespace Ark
&&TARGET_CONCAT_IN_PLACE,
&&TARGET_POP_LIST,
&&TARGET_POP_LIST_IN_PLACE,
&&TARGET_SET_AT_INDEX,
&&TARGET_SET_AT_2_INDEX,
&&TARGET_POP,
&&TARGET_DUP,
&&TARGET_ADD,
Expand Down Expand Up @@ -798,6 +800,104 @@ namespace Ark
DISPATCH();
}

TARGET(SET_AT_INDEX)
{
{
Value* list = popAndResolveAsPtr(context);
Value number = *popAndResolveAsPtr(context);
Value new_value = *popAndResolveAsPtr(context);

if (!list->isIndexable() || number.valueType() != ValueType::Number || (list->valueType() == ValueType::String && new_value.valueType() != ValueType::String))
types::generateError(
"@=",
{ { types::Contract {
{ types::Typedef("list", ValueType::List),
types::Typedef("index", ValueType::Number),
types::Typedef("new_value", ValueType::Any) } } },
{ types::Contract {
{ types::Typedef("string", ValueType::String),
types::Typedef("index", ValueType::Number),
types::Typedef("char", ValueType::String) } } } },
{ *list, number });

const std::size_t size = list->valueType() == ValueType::List ? list->list().size() : list->stringRef().size();
long idx = static_cast<long>(number.number());
idx = idx < 0 ? static_cast<long>(size) + idx : idx;
if (std::cmp_greater_equal(idx, size))
throwVMError(
ErrorKind::Index,
fmt::format("@= index ({}) out of range (indexable size: {})", idx, size));

if (list->valueType() == ValueType::List)
list->list()[static_cast<std::size_t>(idx)] = new_value;
else
list->stringRef()[static_cast<std::size_t>(idx)] = new_value.string()[0];
}
DISPATCH();
}

TARGET(SET_AT_2_INDEX)
{
{
Value* list = popAndResolveAsPtr(context);
Value x = *popAndResolveAsPtr(context);
Value y = *popAndResolveAsPtr(context);
Value new_value = *popAndResolveAsPtr(context);

if (list->valueType() != ValueType::List || x.valueType() != ValueType::Number || y.valueType() != ValueType::Number)
types::generateError(
"@@=",
{ { types::Contract {
{ types::Typedef("list", ValueType::List),
types::Typedef("x", ValueType::Number),
types::Typedef("y", ValueType::Number),
types::Typedef("new_value", ValueType::Any) } } } },
{ *list, x, y });

long idx_x = static_cast<long>(x.number());
idx_x = idx_x < 0 ? static_cast<long>(list->list().size()) + idx_x : idx_x;
if (std::cmp_greater_equal(idx_x, list->list().size()))
throwVMError(
ErrorKind::Index,
fmt::format("@@= index (x: {}) out of range (list size: {})", idx_x, list->list().size()));

if (!list->list()[static_cast<std::size_t>(idx_x)].isIndexable() ||
(list->list()[static_cast<std::size_t>(idx_x)].valueType() == ValueType::String && new_value.valueType() != ValueType::String))
types::generateError(
"@@=",
{ { types::Contract {
{ types::Typedef("list", ValueType::List),
types::Typedef("x", ValueType::Number),
types::Typedef("y", ValueType::Number),
types::Typedef("new_value", ValueType::Any) } } },
{ types::Contract {
{ types::Typedef("string", ValueType::String),
types::Typedef("x", ValueType::Number),
types::Typedef("y", ValueType::Number),
types::Typedef("char", ValueType::String) } } } },
{ *list, x, y });

const bool is_list = list->list()[static_cast<std::size_t>(idx_x)].valueType() == ValueType::List;
const std::size_t size =
is_list
? list->list()[static_cast<std::size_t>(idx_x)].list().size()
: list->list()[static_cast<std::size_t>(idx_x)].stringRef().size();

long idx_y = static_cast<long>(y.number());
idx_y = idx_y < 0 ? static_cast<long>(size) + idx_y : idx_y;
if (std::cmp_greater_equal(idx_y, size))
throwVMError(
ErrorKind::Index,
fmt::format("@@= index (y: {}) out of range (inner indexable size: {})", idx_x, size));

if (is_list)
list->list()[static_cast<std::size_t>(idx_x)].list()[static_cast<std::size_t>(idx_y)] = new_value;
else
list->list()[static_cast<std::size_t>(idx_x)].stringRef()[static_cast<std::size_t>(idx_y)] = new_value.string()[0];
}
DISPATCH();
}

TARGET(POP)
{
pop(context);
Expand Down
13 changes: 13 additions & 0 deletions tests/unittests/resources/LangSuite/builtins-tests.ark
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@
(test:eq (@ (list:setAt short_list 5 "a") 5) "a")
(del short_list)

(mut numbers [0 1 2 3 4])
(@= numbers 2 5)
(@= numbers -1 9)
(@= numbers -2 8)
(test:eq numbers [0 1 5 8 9] "@=")

(set numbers [[0 1 2 3] [4 5 6 7] [8 9 0 1]])
(@@= numbers 0 0 9)
(@@= numbers 1 1 "a")
(@@= numbers -1 -1 -1)
(@@= numbers -2 -2 -2)
(test:eq numbers [[9 1 2 3] [4 "a" -2 7] [8 9 0 -1]])

(test:expect (not (io:fileExists? "test.txt")))
(io:writeFile "test.txt" "hello, world!")
(test:expect (io:fileExists? "test.txt"))
Expand Down

0 comments on commit 9cfde62

Please sign in to comment.