ArkScript
A small, lisp-inspired, functional scripting language
Helpers.hpp
Go to the documentation of this file.
1/**
2 * @file Helpers.hpp
3 * @author Lexy Plateau (lexplt.dev@gmail.com)
4 * @brief Helpers for the VM
5 * @date 2026-02-11
6 *
7 * @copyright Copyright (c) 2026-02-11
8 *
9 */
10
11#ifndef ARK_VM_HELPERS_HPP
12#define ARK_VM_HELPERS_HPP
13
15#include <Ark/TypeChecker.hpp>
17#include <Ark/VM/VM.hpp>
18
19namespace Ark::helper
20{
21 using namespace internal;
22
23 inline ARK_ALWAYS_INLINE Value tail(Value* a)
24 {
25 if (a->valueType() == ValueType::List)
26 {
27 if (a->constList().size() < 2)
28 return Value(ValueType::List);
29
30 std::vector<Value> tmp(a->constList().size() - 1);
31 for (std::size_t i = 1, end = a->constList().size(); i < end; ++i)
32 tmp[i - 1] = a->constList()[i];
33 return Value(std::move(tmp));
34 }
35 if (a->valueType() == ValueType::String)
36 {
37 if (a->string().size() < 2)
39
40 Value b { *a };
41 b.stringRef().erase(b.stringRef().begin());
42 return b;
43 }
44
46 "tail",
49 { *a });
50 }
51
52 inline ARK_ALWAYS_INLINE Value head(Value* a)
53 {
54 if (a->valueType() == ValueType::List)
55 {
56 if (a->constList().empty())
57 return Builtins::nil;
58 return a->constList()[0];
59 }
60 if (a->valueType() == ValueType::String)
61 {
62 if (a->string().empty())
64 return Value(std::string(1, a->stringRef()[0]));
65 }
66
68 "head",
71 { *a });
72 }
73
74 inline ARK_ALWAYS_INLINE Value at(Value& container, Value& index, VM& vm)
75 {
76 if (container.valueType() != ValueType::Dict)
77 {
78 if (index.valueType() != ValueType::Number)
80 "@",
84 { container, index });
85
86 const auto num = static_cast<long>(index.number());
87
88 if (container.valueType() == ValueType::List)
89 {
90 const auto i = static_cast<std::size_t>(num < 0 ? static_cast<long>(container.list().size()) + num : num);
91 if (i < container.list().size())
92 return container.list()[i];
93 else
95 ErrorKind::Index,
96 fmt::format("{} out of range {} (length {})", num, container.toString(vm), container.list().size()));
97 }
98 else if (container.valueType() == ValueType::String)
99 {
100 const auto i = static_cast<std::size_t>(num < 0 ? static_cast<long>(container.string().size()) + num : num);
101 if (i < container.string().size())
102 return Value(std::string(1, container.string()[i]));
103 else
105 ErrorKind::Index,
106 fmt::format("{} out of range \"{}\" (length {})", num, container.string(), container.string().size()));
107 }
108 else
110 "@",
114 { container, index });
115 }
116 else
117 return container.dictRef().get(index);
118 }
119
120 inline ARK_ALWAYS_INLINE Value atAt(const Value* x, const Value* y, Value& list)
121 {
123 list.valueType() != ValueType::List)
125 "@@",
126 { { types::Contract {
129 types::Typedef("x", ValueType::Number) } } } },
130 { list, *y, *x });
131
132 long idx_y = static_cast<long>(y->number());
133 idx_y = idx_y < 0 ? static_cast<long>(list.list().size()) + idx_y : idx_y;
134 if (std::cmp_greater_equal(idx_y, list.list().size()) || idx_y < 0)
136 ErrorKind::Index,
137 fmt::format("@@ index ({}) out of range (list size: {})", idx_y, list.list().size()));
138
139 const bool is_list = list.list()[static_cast<std::size_t>(idx_y)].valueType() == ValueType::List;
140 const std::size_t size =
141 is_list
142 ? list.list()[static_cast<std::size_t>(idx_y)].list().size()
143 : list.list()[static_cast<std::size_t>(idx_y)].stringRef().size();
144
145 long idx_x = static_cast<long>(x->number());
146 idx_x = idx_x < 0 ? static_cast<long>(size) + idx_x : idx_x;
147 if (std::cmp_greater_equal(idx_x, size) || idx_x < 0)
149 ErrorKind::Index,
150 fmt::format("@@ index (x: {}) out of range (inner indexable size: {})", idx_x, size));
151
152 if (is_list)
153 return list.list()[static_cast<std::size_t>(idx_y)].list()[static_cast<std::size_t>(idx_x)];
154 else
155 return Value(std::string(1, list.list()[static_cast<std::size_t>(idx_y)].stringRef()[static_cast<std::size_t>(idx_x)]));
156 }
157
158 inline ARK_ALWAYS_INLINE double doMath(double a, double b, const Instruction op)
159 {
160 if (op == ADD)
161 a += b;
162 else if (op == SUB)
163 a -= b;
164 else if (op == MUL)
165 a *= b;
166 else if (op == DIV)
167 {
168 if (b == 0)
169 Ark::VM::throwVMError(ErrorKind::DivisionByZero, fmt::format("Can not compute expression (/ {} {})", a, b));
170 a /= b;
171 }
172
173 return a;
174 }
175
176 inline ARK_ALWAYS_INLINE std::string mathInstToStr(const Instruction op)
177 {
178 if (op == ADD)
179 return "+";
180 if (op == SUB)
181 return "-";
182 if (op == MUL)
183 return "*";
184 if (op == DIV)
185 return "/";
186 return "???";
187 }
188}
189
190#endif // ARK_VM_HELPERS_HPP
Host the declaration of all the ArkScript builtins.
The ArkScript virtual machine.
Default value type handled by the virtual machine.
The ArkScript virtual machine, executing ArkScript bytecode.
Definition VM.hpp:40
static void throwVMError(internal::ErrorKind kind, const std::string &message)
Throw a VM error message.
Definition VM.cpp:389
const String_t & string() const
Definition Value.hpp:167
const List_t & constList() const
Definition Value.hpp:171
String_t & stringRef()
Definition Value.hpp:168
List_t & list()
Definition Value.hpp:172
ValueType valueType() const noexcept
Definition Value.hpp:154
Number_t number() const
Definition Value.hpp:165
std::string toString(VM &vm, bool show_as_code=false) const noexcept
Definition Value.cpp:81
Dict_t & dictRef()
Definition Value.hpp:178
const Value & get(const Value &key)
Try to get a value from a given key. If no value is found, return Nil.
Definition Dict.cpp:14
ARK_ALWAYS_INLINE Value head(Value *a)
Definition Helpers.hpp:52
ARK_ALWAYS_INLINE Value at(Value &container, Value &index, VM &vm)
Definition Helpers.hpp:74
ARK_ALWAYS_INLINE double doMath(double a, double b, const Instruction op)
Definition Helpers.hpp:158
ARK_ALWAYS_INLINE Value atAt(const Value *x, const Value *y, Value &list)
Definition Helpers.hpp:120
ARK_ALWAYS_INLINE Value tail(Value *a)
Definition Helpers.hpp:23
ARK_ALWAYS_INLINE std::string mathInstToStr(const Instruction op)
Definition Helpers.hpp:176
Instruction
The different bytecodes are stored here.
@ Any
Used only for typechecking.
A contract is a list of typed arguments that a function can follow.
A type definition within a contract.