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 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 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 Value at(Value& container, Value& index, VM& vm)
75 {
76 if (index.valueType() != ValueType::Number)
78 "@",
81 { container, index });
82
83 const auto num = static_cast<long>(index.number());
84
85 if (container.valueType() == ValueType::List)
86 {
87 const auto i = static_cast<std::size_t>(num < 0 ? static_cast<long>(container.list().size()) + num : num);
88 if (i < container.list().size())
89 return container.list()[i];
90 else
92 ErrorKind::Index,
93 fmt::format("{} out of range {} (length {})", num, container.toString(vm), container.list().size()));
94 }
95 else if (container.valueType() == ValueType::String)
96 {
97 const auto i = static_cast<std::size_t>(num < 0 ? static_cast<long>(container.string().size()) + num : num);
98 if (i < container.string().size())
99 return Value(std::string(1, container.string()[i]));
100 else
102 ErrorKind::Index,
103 fmt::format("{} out of range \"{}\" (length {})", num, container.string(), container.string().size()));
104 }
105 else
107 "@",
110 { container, index });
111 }
112
113 inline Value atAt(const Value* x, const Value* y, Value& list)
114 {
116 list.valueType() != ValueType::List)
118 "@@",
119 { { types::Contract {
122 types::Typedef("x", ValueType::Number) } } } },
123 { list, *y, *x });
124
125 long idx_y = static_cast<long>(y->number());
126 idx_y = idx_y < 0 ? static_cast<long>(list.list().size()) + idx_y : idx_y;
127 if (std::cmp_greater_equal(idx_y, list.list().size()) || idx_y < 0)
129 ErrorKind::Index,
130 fmt::format("@@ index ({}) out of range (list size: {})", idx_y, list.list().size()));
131
132 const bool is_list = list.list()[static_cast<std::size_t>(idx_y)].valueType() == ValueType::List;
133 const std::size_t size =
134 is_list
135 ? list.list()[static_cast<std::size_t>(idx_y)].list().size()
136 : list.list()[static_cast<std::size_t>(idx_y)].stringRef().size();
137
138 long idx_x = static_cast<long>(x->number());
139 idx_x = idx_x < 0 ? static_cast<long>(size) + idx_x : idx_x;
140 if (std::cmp_greater_equal(idx_x, size) || idx_x < 0)
142 ErrorKind::Index,
143 fmt::format("@@ index (x: {}) out of range (inner indexable size: {})", idx_x, size));
144
145 if (is_list)
146 return list.list()[static_cast<std::size_t>(idx_y)].list()[static_cast<std::size_t>(idx_x)];
147 else
148 return Value(std::string(1, list.list()[static_cast<std::size_t>(idx_y)].stringRef()[static_cast<std::size_t>(idx_x)]));
149 }
150
151 inline double doMath(double a, double b, const Instruction op)
152 {
153 if (op == ADD)
154 a += b;
155 else if (op == SUB)
156 a -= b;
157 else if (op == MUL)
158 a *= b;
159 else if (op == DIV)
160 {
161 if (b == 0)
162 Ark::VM::throwVMError(ErrorKind::DivisionByZero, fmt::format("Can not compute expression (/ {} {})", a, b));
163 a /= b;
164 }
165
166 return a;
167 }
168
169 inline std::string mathInstToStr(const Instruction op)
170 {
171 if (op == ADD)
172 return "+";
173 if (op == SUB)
174 return "-";
175 if (op == MUL)
176 return "*";
177 if (op == DIV)
178 return "/";
179 return "???";
180 }
181}
182
183#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:48
static void throwVMError(internal::ErrorKind kind, const std::string &message)
Throw a VM error message.
Definition VM.cpp:395
const String_t & string() const
Definition Value.hpp:166
const List_t & constList() const
Definition Value.hpp:170
String_t & stringRef()
Definition Value.hpp:167
List_t & list()
Definition Value.hpp:171
ValueType valueType() const noexcept
Definition Value.hpp:153
Number_t number() const
Definition Value.hpp:164
std::string toString(VM &vm, bool show_as_code=false) const noexcept
Definition Value.cpp:77
double doMath(double a, double b, const Instruction op)
Definition Helpers.hpp:151
Value head(Value *a)
Definition Helpers.hpp:52
Value at(Value &container, Value &index, VM &vm)
Definition Helpers.hpp:74
std::string mathInstToStr(const Instruction op)
Definition Helpers.hpp:169
Value atAt(const Value *x, const Value *y, Value &list)
Definition Helpers.hpp:113
Value tail(Value *a)
Definition Helpers.hpp:23
Instruction
The different bytecodes are stored here.
A contract is a list of typed arguments that a function can follow.
A type definition within a contract.