New intrinsic implementation primitives
This commit is contained in:
parent
ada8e17d1b
commit
43d7aeac1d
@ -71,7 +71,8 @@ static void encourage_contact(std::ostream &os)
|
|||||||
{
|
{
|
||||||
os << pretty::begin_comment << "\n"
|
os << pretty::begin_comment << "\n"
|
||||||
"Interpreter got in state that was not expected by it's developers.\n"
|
"Interpreter got in state that was not expected by it's developers.\n"
|
||||||
"Contact them providing code that coused it and error message above to resolve this trouble\n"
|
"Contact them and provide code that coused this error and\n"
|
||||||
|
"error message above to resolve this trouble\n"
|
||||||
<< pretty::end << std::flush;
|
<< pretty::end << std::flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,21 +4,124 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
/// Intrinsic implementation primitive providing a short way to check if arguments match required type signature
|
||||||
|
static inline bool typecheck(std::vector<Value> const& args, auto const& ...expected_types)
|
||||||
|
{
|
||||||
|
return (args.size() == sizeof...(expected_types)) &&
|
||||||
|
[&args, expected_types...]<std::size_t ...I>(std::index_sequence<I...>) {
|
||||||
|
return ((expected_types == args[I].type) && ...);
|
||||||
|
} (std::make_index_sequence<sizeof...(expected_types)>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Intrinsic implementation primitive providing a short way to move values based on matched type signature
|
||||||
|
template<auto ...Types>
|
||||||
|
static inline auto move_from(std::vector<Value>& args)
|
||||||
|
{
|
||||||
|
return [&args]<std::size_t ...I>(std::index_sequence<I...>) {
|
||||||
|
return std::tuple { (std::move(args[I]).*(Member_For_Value_Type<Types>::value)) ... };
|
||||||
|
} (std::make_index_sequence<sizeof...(Types)>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shape abstraction to define what types are required once
|
||||||
|
template<auto ...Types>
|
||||||
|
struct Shape
|
||||||
|
{
|
||||||
|
static inline auto move_from(std::vector<Value>& args) { return ::move_from<Types...>(args); }
|
||||||
|
static inline auto typecheck(std::vector<Value>& args) { return ::typecheck(args, Types...); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Returns if type can be indexed
|
||||||
|
static constexpr bool is_indexable(Value::Type type)
|
||||||
|
{
|
||||||
|
return type == Value::Type::Array || type == Value::Type::Block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binary operation may be vectorized when there are two argument which one is indexable and other is not
|
||||||
|
static bool may_be_vectorized(std::vector<Value> const& args)
|
||||||
|
{
|
||||||
|
return args.size() == 2 && (is_indexable(args[0].type) != is_indexable(args[1].type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Intrinsic implementation primitive to ease operation vectorization
|
||||||
|
/// @invariant args.size() == 2
|
||||||
|
Result<Value> vectorize(auto &&operation, Interpreter &interpreter, std::vector<Value> args)
|
||||||
|
{
|
||||||
|
assert(args.size() == 2, "Vectorization primitive only supports two arguments");
|
||||||
|
Array array;
|
||||||
|
|
||||||
|
auto lhs = std::move(args.front());
|
||||||
|
auto rhs = std::move(args.back());
|
||||||
|
|
||||||
|
if (is_indexable(lhs.type) && !is_indexable(rhs.type)) {
|
||||||
|
Array array;
|
||||||
|
for (auto i = 0u; i < lhs.size(); ++i) {
|
||||||
|
array.elements.push_back(
|
||||||
|
Try(operation(interpreter, { Try(lhs.index(interpreter, i)), rhs })));
|
||||||
|
}
|
||||||
|
return Value::from(std::move(array));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0u; i < rhs.size(); ++i) {
|
||||||
|
array.elements.push_back(
|
||||||
|
Try(operation(interpreter, { lhs, Try(rhs.index(interpreter, i)) })));
|
||||||
|
}
|
||||||
|
return Value::from(std::move(array));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates implementation of plus/minus operator that support following operations:
|
||||||
|
/// number, number -> number (standard math operations)
|
||||||
|
/// n: number, m: music -> music
|
||||||
|
/// m: music, n: number -> music moves m by n semitones (+ goes up, - goes down)
|
||||||
|
template<typename Binary_Operation>
|
||||||
|
static Result<Value> plus_minus_operator(Interpreter &interpreter, std::vector<Value> args)
|
||||||
|
{
|
||||||
|
using NN = Shape<Value::Type::Number, Value::Type::Number>;
|
||||||
|
using MN = Shape<Value::Type::Music, Value::Type::Number>;
|
||||||
|
using NM = Shape<Value::Type::Number, Value::Type::Music>;
|
||||||
|
|
||||||
|
if (NN::typecheck(args)) {
|
||||||
|
auto [a, b] = NN::move_from(args);
|
||||||
|
return Value::from(Binary_Operation{}(std::move(a), std::move(b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MN::typecheck(args)) {
|
||||||
|
auto [chord, offset] = MN::move_from(args);
|
||||||
|
for (auto ¬e : chord.notes) {
|
||||||
|
note.base = Binary_Operation{}(note.base, offset.as_int());
|
||||||
|
note.simplify_inplace();
|
||||||
|
}
|
||||||
|
return Value::from(std::move(chord));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NM::typecheck(args)) {
|
||||||
|
auto [offset, chord] = NM::move_from(args);
|
||||||
|
for (auto ¬e : chord.notes) {
|
||||||
|
note.base = Binary_Operation{}(offset.as_int(), note.base);
|
||||||
|
note.simplify_inplace();
|
||||||
|
}
|
||||||
|
return Value::from(std::move(chord));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (may_be_vectorized(args)) {
|
||||||
|
return vectorize(plus_minus_operator<Binary_Operation>, interpreter, std::move(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false, "Unsupported types for this operation"); // TODO(assert)
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename Binary_Operation>
|
template<typename Binary_Operation>
|
||||||
static Result<Value> binary_operator(Interpreter&, std::vector<Value> args)
|
static Result<Value> binary_operator(Interpreter&, std::vector<Value> args)
|
||||||
{
|
{
|
||||||
auto result = std::move(args.front());
|
using NN = Shape<Value::Type::Number, Value::Type::Number>;
|
||||||
for (auto &v : std::span(args).subspan(1)) {
|
|
||||||
assert(result.type == Value::Type::Number, "LHS should be a number"); // TODO(assert)
|
if (NN::typecheck(args)) {
|
||||||
assert(v.type == Value::Type::Number, "RHS should be a number"); // TODO(assert)
|
auto [lhs, rhs] = NN::move_from(args);
|
||||||
if constexpr (std::is_same_v<Number, std::invoke_result_t<Binary_Operation, Number, Number>>) {
|
return Value::from(Binary_Operation{}(lhs, rhs));
|
||||||
result.n = Binary_Operation{}(std::move(result.n), std::move(v).n);
|
|
||||||
} else {
|
|
||||||
result.type = Value::Type::Bool;
|
|
||||||
result.b = Binary_Operation{}(std::move(result.n), std::move(v).n);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return result;
|
unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Binary_Predicate>
|
template<typename Binary_Predicate>
|
||||||
@ -31,19 +134,19 @@ static Result<Value> equality_operator(Interpreter&, std::vector<Value> args)
|
|||||||
template<typename Binary_Predicate>
|
template<typename Binary_Predicate>
|
||||||
static Result<Value> comparison_operator(Interpreter&, std::vector<Value> args)
|
static Result<Value> comparison_operator(Interpreter&, std::vector<Value> args)
|
||||||
{
|
{
|
||||||
assert(args.size() == 2, "Ordering only allows for 2 operands"); // TODO(assert)
|
using NN = Shape<Value::Type::Number, Value::Type::Number>;
|
||||||
assert(args.front().type == args.back().type, "Only values of the same type can be ordered"); // TODO(assert)
|
using BB = Shape<Value::Type::Bool, Value::Type::Bool>;
|
||||||
|
|
||||||
switch (args.front().type) {
|
if (NN::typecheck(args)) {
|
||||||
case Value::Type::Number:
|
auto [a, b] = NN::move_from(args);
|
||||||
return Value::from(Binary_Predicate{}(std::move(args.front()).n, std::move(args.back()).n));
|
return Value::from(Binary_Predicate{}(std::move(a), std::move(b)));
|
||||||
|
|
||||||
case Value::Type::Bool:
|
|
||||||
return Value::from(Binary_Predicate{}(std::move(args.front()).b, std::move(args.back()).b));
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false, "Cannot compare value of given types"); // TODO(assert)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (BB::typecheck(args)) {
|
||||||
|
auto [a, b] = BB::move_from(args);
|
||||||
|
return Value::from(Binary_Predicate{}(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
unreachable();
|
unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,66 +244,6 @@ static inline Result<void> play_notes(Interpreter &interpreter, T args)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool is_indexable(Value::Type type)
|
|
||||||
{
|
|
||||||
return type == Value::Type::Array || type == Value::Type::Block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates implementation of plus/minus operator that support following operations:
|
|
||||||
/// number, number -> number (standard math operations)
|
|
||||||
/// n: number, m: music -> music
|
|
||||||
/// m: music, n: number -> music moves m by n semitones (+ goes up, - goes down)
|
|
||||||
template<typename Binary_Operation>
|
|
||||||
static Result<Value> plus_minus_operator(Interpreter &interpreter, std::vector<Value> args)
|
|
||||||
{
|
|
||||||
assert(args.size() == 2, "Binary operator only accepts 2 arguments");
|
|
||||||
auto lhs = std::move(args.front());
|
|
||||||
auto rhs = std::move(args.back());
|
|
||||||
|
|
||||||
if (lhs.type == rhs.type && lhs.type == Value::Type::Number) {
|
|
||||||
return Value::from(Binary_Operation{}(std::move(lhs).n, std::move(rhs).n));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lhs.type == Value::Type::Music && rhs.type == Value::Type::Number) {
|
|
||||||
for (auto ¬e : lhs.chord.notes) {
|
|
||||||
note.base = Binary_Operation{}(note.base, rhs.n.as_int());
|
|
||||||
note.simplify_inplace();
|
|
||||||
}
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lhs.type == Value::Type::Number && rhs.type == Value::Type::Music) {
|
|
||||||
for (auto ¬e : rhs.chord.notes) {
|
|
||||||
note.base = Binary_Operation{}(lhs.n.as_int(), note.base);
|
|
||||||
note.simplify_inplace();
|
|
||||||
}
|
|
||||||
return rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_indexable(lhs.type) && !is_indexable(rhs.type)) {
|
|
||||||
Array array;
|
|
||||||
for (auto i = 0u; i < lhs.size(); ++i) {
|
|
||||||
array.elements.push_back(Try(
|
|
||||||
plus_minus_operator<Binary_Operation>(
|
|
||||||
interpreter, { Try(lhs.index(interpreter, i)), rhs })));
|
|
||||||
}
|
|
||||||
return Value::from(std::move(array));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_indexable(lhs.type) && is_indexable(rhs.type)) {
|
|
||||||
Array array;
|
|
||||||
for (auto i = 0u; i < rhs.size(); ++i) {
|
|
||||||
array.elements.push_back(Try(
|
|
||||||
plus_minus_operator<Binary_Operation>(
|
|
||||||
interpreter, { lhs, Try(rhs.index(interpreter, i)) })));
|
|
||||||
}
|
|
||||||
return Value::from(std::move(array));
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(false, "Unsupported types for this operation"); // TODO(assert)
|
|
||||||
unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<auto Mem_Ptr>
|
template<auto Mem_Ptr>
|
||||||
Result<Value> ctx_read_write_property(Interpreter &interpreter, std::vector<Value> args)
|
Result<Value> ctx_read_write_property(Interpreter &interpreter, std::vector<Value> args)
|
||||||
{
|
{
|
||||||
|
@ -770,6 +770,30 @@ struct Value
|
|||||||
bool operator==(Value const& other) const;
|
bool operator==(Value const& other) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<Value::Type>
|
||||||
|
struct Member_For_Value_Type {};
|
||||||
|
|
||||||
|
template<> struct Member_For_Value_Type<Value::Type::Bool>
|
||||||
|
{ static constexpr auto value = &Value::b; };
|
||||||
|
|
||||||
|
template<> struct Member_For_Value_Type<Value::Type::Number>
|
||||||
|
{ static constexpr auto value = &Value::n; };
|
||||||
|
|
||||||
|
template<> struct Member_For_Value_Type<Value::Type::Symbol>
|
||||||
|
{ static constexpr auto value = &Value::s; };
|
||||||
|
|
||||||
|
template<> struct Member_For_Value_Type<Value::Type::Intrinsic>
|
||||||
|
{ static constexpr auto value = &Value::intr; };
|
||||||
|
|
||||||
|
template<> struct Member_For_Value_Type<Value::Type::Block>
|
||||||
|
{ static constexpr auto value = &Value::blk; };
|
||||||
|
|
||||||
|
template<> struct Member_For_Value_Type<Value::Type::Array>
|
||||||
|
{ static constexpr auto value = &Value::array; };
|
||||||
|
|
||||||
|
template<> struct Member_For_Value_Type<Value::Type::Music>
|
||||||
|
{ static constexpr auto value = &Value::chord; };
|
||||||
|
|
||||||
/// Returns type name of Value type
|
/// Returns type name of Value type
|
||||||
std::string_view type_name(Value::Type t);
|
std::string_view type_name(Value::Type t);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user