New intrinsic implementation primitives

This commit is contained in:
Robert Bendun 2022-05-30 15:10:03 +02:00
parent ada8e17d1b
commit 43d7aeac1d
3 changed files with 151 additions and 83 deletions

View File

@ -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;
} }

View File

@ -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 &note : 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 &note : 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 &note : 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 &note : 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)
{ {

View File

@ -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);