changed shape of Value constructors; added Array type

This commit is contained in:
Robert Bendun 2022-05-24 17:39:07 +02:00
parent d549d23f0a
commit 64417bf187
6 changed files with 149 additions and 96 deletions

View File

@ -28,7 +28,7 @@ constexpr auto equality_operator()
{ {
return [](Interpreter&, std::vector<Value> args) -> Result<Value> { return [](Interpreter&, std::vector<Value> args) -> Result<Value> {
assert(args.size() == 2, "(in)Equality only allows for 2 operands"); // TODO(assert) assert(args.size() == 2, "(in)Equality only allows for 2 operands"); // TODO(assert)
return Value::boolean(Binary_Predicate{}(std::move(args.front()), std::move(args.back()))); return Value::from(Binary_Predicate{}(std::move(args.front()), std::move(args.back())));
}; };
} }
@ -41,10 +41,10 @@ constexpr auto comparison_operator()
switch (args.front().type) { switch (args.front().type) {
case Value::Type::Number: case Value::Type::Number:
return Value::boolean(Binary_Predicate{}(std::move(args.front()).n, std::move(args.back()).n)); return Value::from(Binary_Predicate{}(std::move(args.front()).n, std::move(args.back()).n));
case Value::Type::Bool: case Value::Type::Bool:
return Value::boolean(Binary_Predicate{}(std::move(args.front()).b, std::move(args.back()).b)); return Value::from(Binary_Predicate{}(std::move(args.front()).b, std::move(args.back()).b));
default: default:
assert(false, "Cannot compare value of given types"); // TODO(assert) assert(false, "Cannot compare value of given types"); // TODO(assert)
@ -57,21 +57,21 @@ constexpr auto comparison_operator()
static inline void register_note_length_constants() static inline void register_note_length_constants()
{ {
auto &global = *Env::global; auto &global = *Env::global;
global.force_define("fn", Value::number(Number(1, 1))); global.force_define("fn", Value::from(Number(1, 1)));
global.force_define("dfn", Value::number(Number(3, 2))); global.force_define("dfn", Value::from(Number(3, 2)));
global.force_define("hn", Value::number(Number(1, 2))); global.force_define("hn", Value::from(Number(1, 2)));
global.force_define("dhn", Value::number(Number(3, 4))); global.force_define("dhn", Value::from(Number(3, 4)));
global.force_define("ddhn", Value::number(Number(7, 8))); global.force_define("ddhn", Value::from(Number(7, 8)));
global.force_define("qn", Value::number(Number(1, 4))); global.force_define("qn", Value::from(Number(1, 4)));
global.force_define("dqn", Value::number(Number(3, 8))); global.force_define("dqn", Value::from(Number(3, 8)));
global.force_define("ddqn", Value::number(Number(7, 16))); global.force_define("ddqn", Value::from(Number(7, 16)));
global.force_define("en", Value::number(Number(1, 8))); global.force_define("en", Value::from(Number(1, 8)));
global.force_define("den", Value::number(Number(3, 16))); global.force_define("den", Value::from(Number(3, 16)));
global.force_define("dden", Value::number(Number(7, 32))); global.force_define("dden", Value::from(Number(7, 32)));
global.force_define("sn", Value::number(Number(1, 16))); global.force_define("sn", Value::from(Number(1, 16)));
global.force_define("dsn", Value::number(Number(3, 32))); global.force_define("dsn", Value::from(Number(3, 32)));
global.force_define("tn", Value::number(Number(1, 32))); global.force_define("tn", Value::from(Number(1, 32)));
global.force_define("dtn", Value::number(Number(3, 64))); global.force_define("dtn", Value::from(Number(3, 64)));
} }
Interpreter::Interpreter() Interpreter::Interpreter()
@ -90,7 +90,7 @@ Interpreter::Interpreter()
global.force_define("typeof", +[](Interpreter&, std::vector<Value> args) -> Result<Value> { global.force_define("typeof", +[](Interpreter&, std::vector<Value> args) -> Result<Value> {
assert(args.size() == 1, "typeof expects only one argument"); assert(args.size() == 1, "typeof expects only one argument");
return Value::symbol(std::string(type_name(args.front().type))); return Value::from(std::string(type_name(args.front().type)));
}); });
global.force_define("if", +[](Interpreter &i, std::vector<Value> args) -> Result<Value> { global.force_define("if", +[](Interpreter &i, std::vector<Value> args) -> Result<Value> {
@ -108,9 +108,9 @@ Interpreter::Interpreter()
assert(args.size() == 1, "len only accepts one argument"); assert(args.size() == 1, "len only accepts one argument");
assert(args.front().type == Value::Type::Block, "Only blocks can be measure"); assert(args.front().type == Value::Type::Block, "Only blocks can be measure");
if (args.front().blk.body.type != Ast::Type::Sequence) { if (args.front().blk.body.type != Ast::Type::Sequence) {
return Value::number(Number(1)); return Value::from(Number(1));
} else { } else {
return Value::number(Number(args.front().blk.body.arguments.size())); return Value::from(Number(args.front().blk.body.arguments.size()));
} }
}); });
@ -228,7 +228,7 @@ Result<Value> Interpreter::eval(Ast &&ast)
block.context = env; block.context = env;
block.body = std::move(ast.arguments.back()); block.body = std::move(ast.arguments.back());
return Value::block(std::move(block)); return Value::from(std::move(block));
} }
default: default:

View File

@ -201,6 +201,23 @@ struct [[nodiscard("This value may contain critical error, so it should NOT be i
std::move(try_value).value(); \ std::move(try_value).value(); \
}) })
/// Drop in replacement for bool when C++ implcit conversions stand in your way
struct Explicit_Bool
{
bool value;
constexpr Explicit_Bool(bool b) : value(b)
{
}
constexpr Explicit_Bool(auto &&) = delete;
constexpr operator bool() const
{
return value;
}
};
namespace unicode namespace unicode
{ {
inline namespace special_runes inline namespace special_runes
@ -439,6 +456,7 @@ struct Value;
using Intrinsic = Result<Value>(*)(Interpreter &i, std::vector<Value>); using Intrinsic = Result<Value>(*)(Interpreter &i, std::vector<Value>);
/// Lazy Array / Continuation / Closure type thingy
struct Block struct Block
{ {
Location location; Location location;
@ -475,18 +493,30 @@ struct Note
std::ostream& operator<<(std::ostream& os, Note const& note); std::ostream& operator<<(std::ostream& os, Note const& note);
template<typename T, typename ...XS> /// Eager Array
constexpr auto is_one_of = (std::is_same_v<T, XS> || ...); struct Array
{
/// Elements that are stored in array
std::vector<Value> elements;
Result<Value> index(Interpreter &i, unsigned position);
};
// TODO Add location // TODO Add location
struct Value struct Value
{ {
static Result<Value> from(Token t); static Result<Value> from(Token t);
static Value boolean(bool b); static Value from(Explicit_Bool b);
static Value number(Number n); static Value from(Number n);
static Value symbol(std::string s);
static Value block(Block &&l); // Symbol creating functions
static Value music(Note n); static Value from(std::string s);
static Value from(std::string_view s);
static Value from(char const* s);
static Value from(Block &&l);
static Value from(Array &&array);
static Value from(Note n);
enum class Type enum class Type
{ {
@ -496,6 +526,7 @@ struct Value
Symbol, Symbol,
Intrinsic, Intrinsic,
Block, Block,
Array,
Music Music
}; };

View File

@ -27,22 +27,22 @@ suite environment_test = [] {
should("nested scoping preserve outer scope") = [] { should("nested scoping preserve outer scope") = [] {
Interpreter i; Interpreter i;
i.env->force_define("x", Value::number(Number(10))); i.env->force_define("x", Value::from(Number(10)));
i.env->force_define("y", Value::number(Number(20))); i.env->force_define("y", Value::from(Number(20)));
equals(i.env->find("x"), Value::number(Number(10))); equals(i.env->find("x"), Value::from(Number(10)));
equals(i.env->find("y"), Value::number(Number(20))); equals(i.env->find("y"), Value::from(Number(20)));
i.enter_scope(); i.enter_scope();
{ {
i.env->force_define("x", Value::number(Number(30))); i.env->force_define("x", Value::from(Number(30)));
equals(i.env->find("x"), Value::number(Number(30))); equals(i.env->find("x"), Value::from(Number(30)));
equals(i.env->find("y"), Value::number(Number(20))); equals(i.env->find("y"), Value::from(Number(20)));
} }
i.leave_scope(); i.leave_scope();
equals(i.env->find("x"), Value::number(Number(10))); equals(i.env->find("x"), Value::from(Number(10)));
equals(i.env->find("y"), Value::number(Number(20))); equals(i.env->find("y"), Value::from(Number(20)));
}; };
should("nested variables missing from outer scope") = [] { should("nested variables missing from outer scope") = [] {
@ -50,8 +50,8 @@ suite environment_test = [] {
i.enter_scope(); i.enter_scope();
{ {
i.env->force_define("x", Value::number(Number(30))); i.env->force_define("x", Value::from(Number(30)));
equals(i.env->find("x"), Value::number(Number(30))); equals(i.env->find("x"), Value::from(Number(30)));
} }
i.leave_scope(); i.leave_scope();

View File

@ -27,26 +27,26 @@ void evaluates_to(Value value, std::string_view source_code, reflection::source_
suite intepreter_test = [] { suite intepreter_test = [] {
"Interpreter"_test = [] { "Interpreter"_test = [] {
should("evaluate literals") = [] { should("evaluate literals") = [] {
evaluates_to(Value::boolean(false), "false"); evaluates_to(Value::from(false), "false");
evaluates_to(Value::boolean(true), "true"); evaluates_to(Value::from(true), "true");
evaluates_to(Value::number(Number(10)), "10"); evaluates_to(Value::from(Number(10)), "10");
evaluates_to(Value{}, "nil"); evaluates_to(Value{}, "nil");
}; };
should("evaluate arithmetic") = [] { should("evaluate arithmetic") = [] {
evaluates_to(Value::number(Number(10)), "5 + 3 + 2"); evaluates_to(Value::from(Number(10)), "5 + 3 + 2");
evaluates_to(Value::number(Number(25)), "5 * (3 + 2)"); evaluates_to(Value::from(Number(25)), "5 * (3 + 2)");
evaluates_to(Value::number(Number(1, 2)), "1 / 2"); evaluates_to(Value::from(Number(1, 2)), "1 / 2");
evaluates_to(Value::number(Number(-10)), "10 - 20"); evaluates_to(Value::from(Number(-10)), "10 - 20");
}; };
should("call builtin functions") = [] { should("call builtin functions") = [] {
evaluates_to(Value::symbol("nil"), "typeof nil"); evaluates_to(Value::from("nil"), "typeof nil");
evaluates_to(Value::symbol("number"), "typeof 100"); evaluates_to(Value::from("number"), "typeof 100");
}; };
should("allows only for calling which is callable") = [] { should("allows only for calling which is callable") = [] {
evaluates_to(Value::number(Number(0)), "[i|i] 0"); evaluates_to(Value::from(Number(0)), "[i|i] 0");
{ {
Interpreter i; Interpreter i;
{ {
@ -55,7 +55,7 @@ suite intepreter_test = [] {
expect(eq(result.error().type, errors::Not_Callable)); expect(eq(result.error().type, errors::Not_Callable));
} }
{ {
i.env->force_define("call_me", Value::number(Number(10))); i.env->force_define("call_me", Value::from(Number(10)));
auto result = Parser::parse("call_me 20", "test").and_then([&](Ast &&ast) { return i.eval(std::move(ast)); }); auto result = Parser::parse("call_me 20", "test").and_then([&](Ast &&ast) { return i.eval(std::move(ast)); });
expect(!result.has_value()) << "Expected code to have failed"; expect(!result.has_value()) << "Expected code to have failed";
expect(eq(result.error().type, errors::Not_Callable)); expect(eq(result.error().type, errors::Not_Callable));
@ -64,36 +64,36 @@ suite intepreter_test = [] {
}; };
should("allow for value (in)equality comparisons") = [] { should("allow for value (in)equality comparisons") = [] {
evaluates_to(Value::boolean(true), "nil == nil"); evaluates_to(Value::from(true), "nil == nil");
evaluates_to(Value::boolean(false), "nil != nil"); evaluates_to(Value::from(false), "nil != nil");
evaluates_to(Value::boolean(true), "true == true"); evaluates_to(Value::from(true), "true == true");
evaluates_to(Value::boolean(false), "true != true"); evaluates_to(Value::from(false), "true != true");
evaluates_to(Value::boolean(false), "true == false"); evaluates_to(Value::from(false), "true == false");
evaluates_to(Value::boolean(true), "true != false"); evaluates_to(Value::from(true), "true != false");
evaluates_to(Value::boolean(true), "0 == 0"); evaluates_to(Value::from(true), "0 == 0");
evaluates_to(Value::boolean(false), "0 != 0"); evaluates_to(Value::from(false), "0 != 0");
evaluates_to(Value::boolean(true), "1 != 0"); evaluates_to(Value::from(true), "1 != 0");
evaluates_to(Value::boolean(false), "1 == 0"); evaluates_to(Value::from(false), "1 == 0");
}; };
should("allow for value ordering comparisons") = [] { should("allow for value ordering comparisons") = [] {
evaluates_to(Value::boolean(false), "true < true"); evaluates_to(Value::from(false), "true < true");
evaluates_to(Value::boolean(true), "true <= true"); evaluates_to(Value::from(true), "true <= true");
evaluates_to(Value::boolean(true), "false < true"); evaluates_to(Value::from(true), "false < true");
evaluates_to(Value::boolean(false), "false > true"); evaluates_to(Value::from(false), "false > true");
evaluates_to(Value::boolean(false), "0 < 0"); evaluates_to(Value::from(false), "0 < 0");
evaluates_to(Value::boolean(true), "0 <= 0"); evaluates_to(Value::from(true), "0 <= 0");
evaluates_to(Value::boolean(true), "1 < 2"); evaluates_to(Value::from(true), "1 < 2");
evaluates_to(Value::boolean(false), "1 > 2"); evaluates_to(Value::from(false), "1 > 2");
}; };
// Added to explicitly test against bug that was in old implementation of enviroments. // Added to explicitly test against bug that was in old implementation of enviroments.
// Previously this test would segfault // Previously this test would segfault
should("allow assigning result of function calls to a variable") = [] { should("allow assigning result of function calls to a variable") = [] {
evaluates_to(Value::number(Number(42)), "var x = [i|i] 42; x"); evaluates_to(Value::from(Number(42)), "var x = [i|i] 42; x");
}; };
}; };
}; };

View File

@ -54,40 +54,40 @@ static void test_note_resolution(
suite value_test = [] { suite value_test = [] {
"Value"_test = [] { "Value"_test = [] {
should("be properly created using Value::from") = [] { should("be properly created using Value::from") = [] {
expect_value(Value::from({ Token::Type::Numeric, "10", {} }), Value::number(Number(10))); expect_value(Value::from({ Token::Type::Numeric, "10", {} }), Value::from(Number(10)));
expect_value(Value::from({ Token::Type::Keyword, "nil", {} }), Value{}); expect_value(Value::from({ Token::Type::Keyword, "nil", {} }), Value{});
expect_value(Value::from({ Token::Type::Keyword, "true", {} }), Value::boolean(true)); expect_value(Value::from({ Token::Type::Keyword, "true", {} }), Value::from(true));
expect_value(Value::from({ Token::Type::Keyword, "false", {} }), Value::boolean(false)); expect_value(Value::from({ Token::Type::Keyword, "false", {} }), Value::from(false));
expect_value(Value::from({ Token::Type::Symbol, "foobar", {} }), Value::symbol("foobar")); expect_value(Value::from({ Token::Type::Symbol, "foobar", {} }), Value::from("foobar"));
}; };
should("have be considered truthy or falsy") = [] { should("have be considered truthy or falsy") = [] {
either_truthy_or_falsy(&Value::truthy, Value::boolean(true)); either_truthy_or_falsy(&Value::truthy, Value::from(true));
either_truthy_or_falsy(&Value::truthy, Value::number(Number(1))); either_truthy_or_falsy(&Value::truthy, Value::from(Number(1)));
either_truthy_or_falsy(&Value::truthy, Value::symbol("foo")); either_truthy_or_falsy(&Value::truthy, Value::from("foo"));
either_truthy_or_falsy(&Value::truthy, Value(Intrinsic(nullptr))); either_truthy_or_falsy(&Value::truthy, Value(Intrinsic(nullptr)));
either_truthy_or_falsy(&Value::falsy, Value{}); either_truthy_or_falsy(&Value::falsy, Value{});
either_truthy_or_falsy(&Value::falsy, Value::boolean(false)); either_truthy_or_falsy(&Value::falsy, Value::from(false));
either_truthy_or_falsy(&Value::falsy, Value::number(Number(0))); either_truthy_or_falsy(&Value::falsy, Value::from(Number(0)));
}; };
}; };
"Value comparisons"_test = [] { "Value comparisons"_test = [] {
should("are always not equal when types differ") = [] { should("are always not equal when types differ") = [] {
expect(neq(Value::symbol("0"), Value::number(Number(0)))); expect(neq(Value::from("0"), Value::from(Number(0))));
}; };
}; };
"Value printing"_test = [] { "Value printing"_test = [] {
expect(eq("nil"sv, str(Value{}))); expect(eq("nil"sv, str(Value{})));
expect(eq("true"sv, str(Value::boolean(true)))); expect(eq("true"sv, str(Value::from(true))));
expect(eq("false"sv, str(Value::boolean(false)))); expect(eq("false"sv, str(Value::from(false))));
expect(eq("10"sv, str(Value::number(Number(10))))); expect(eq("10"sv, str(Value::from(Number(10)))));
expect(eq("1/2"sv, str(Value::number(Number(2, 4))))); expect(eq("1/2"sv, str(Value::from(Number(2, 4)))));
expect(eq("foo"sv, str(Value::symbol("foo")))); expect(eq("foo"sv, str(Value::from("foo"))));
expect(eq("<intrinsic>"sv, str(Value(Intrinsic(nullptr))))); expect(eq("<intrinsic>"sv, str(Value(Intrinsic(nullptr)))));
}; };

View File

@ -65,22 +65,22 @@ Result<Value> Value::from(Token t)
{ {
switch (t.type) { switch (t.type) {
case Token::Type::Numeric: case Token::Type::Numeric:
return Value::number(Try(Number::from(std::move(t)))); return Value::from(Try(Number::from(std::move(t))));
case Token::Type::Symbol: case Token::Type::Symbol:
return Value::symbol(std::string(t.source)); return Value::from(std::string(t.source));
case Token::Type::Keyword: case Token::Type::Keyword:
if (t.source == "false") return Value::boolean(false); if (t.source == "false") return Value::from(false);
if (t.source == "nil") return Value{}; if (t.source == "nil") return Value{};
if (t.source == "true") return Value::boolean(true); if (t.source == "true") return Value::from(true);
unreachable(); unreachable();
case Token::Type::Chord: case Token::Type::Chord:
if (t.source.size() == 1 || (t.source.size() == 2 && t.source.back() == '#')) { if (t.source.size() == 1 || (t.source.size() == 2 && t.source.back() == '#')) {
auto maybe_note = Note::from(t.source); auto maybe_note = Note::from(t.source);
assert(maybe_note.has_value(), "Somehow parser passed invalid note literal"); assert(maybe_note.has_value(), "Somehow parser passed invalid note literal");
return Value::music(*maybe_note); return Value::from(*maybe_note);
} }
unimplemented("only simple note values (like c or e#) are supported now"); unimplemented("only simple note values (like c or e#) are supported now");
@ -90,7 +90,7 @@ Result<Value> Value::from(Token t)
} }
} }
Value Value::boolean(bool b) Value Value::from(Explicit_Bool b)
{ {
Value v; Value v;
v.type = Value::Type::Bool; v.type = Value::Type::Bool;
@ -98,7 +98,7 @@ Value Value::boolean(bool b)
return v; return v;
} }
Value Value::number(Number n) Value Value::from(Number n)
{ {
Value v; Value v;
v.type = Type::Number; v.type = Type::Number;
@ -106,7 +106,7 @@ Value Value::number(Number n)
return v; return v;
} }
Value Value::symbol(std::string s) Value Value::from(std::string s)
{ {
Value v; Value v;
v.type = Type::Symbol; v.type = Type::Symbol;
@ -114,7 +114,23 @@ Value Value::symbol(std::string s)
return v; return v;
} }
Value Value::block(Block &&block) Value Value::from(std::string_view s)
{
Value v;
v.type = Type::Symbol;
v.s = std::move(s);
return v;
}
Value Value::from(char const* s)
{
Value v;
v.type = Type::Symbol;
v.s = std::move(s);
return v;
}
Value Value::from(Block &&block)
{ {
Value v; Value v;
v.type = Type::Block; v.type = Type::Block;
@ -122,7 +138,7 @@ Value Value::block(Block &&block)
return v; return v;
} }
Value Value::music(Note n) Value Value::from(Note n)
{ {
Value v; Value v;
v.type = Type::Music; v.type = Type::Music;
@ -174,7 +190,8 @@ bool Value::truthy() const
case Type::Bool: return b; case Type::Bool: return b;
case Type::Nil: return false; case Type::Nil: return false;
case Type::Number: return n != Number(0); case Type::Number: return n != Number(0);
case Type::Block: case Type::Array: // for array and block maybe test emptyness?
case Type::Block: //
case Type::Intrinsic: case Type::Intrinsic:
case Type::Music: case Type::Music:
case Type::Symbol: return true; case Type::Symbol: return true;
@ -200,6 +217,7 @@ bool Value::operator==(Value const& other) const
case Type::Block: return false; // TODO Reconsider if functions are comparable case Type::Block: return false; // TODO Reconsider if functions are comparable
case Type::Bool: return b == other.b; case Type::Bool: return b == other.b;
case Type::Music: return note == other.note; case Type::Music: return note == other.note;
case Type::Array: unimplemented();
} }
unreachable(); unreachable();
@ -226,6 +244,9 @@ std::ostream& operator<<(std::ostream& os, Value const& v)
case Value::Type::Block: case Value::Type::Block:
return os << "<block>"; return os << "<block>";
case Value::Type::Array:
unimplemented();
case Value::Type::Music: case Value::Type::Music:
return os << v.note; return os << v.note;
} }
@ -235,6 +256,7 @@ std::ostream& operator<<(std::ostream& os, Value const& v)
std::string_view type_name(Value::Type t) std::string_view type_name(Value::Type t)
{ {
switch (t) { switch (t) {
case Value::Type::Array: return "array";
case Value::Type::Block: return "block"; case Value::Type::Block: return "block";
case Value::Type::Bool: return "bool"; case Value::Type::Bool: return "bool";
case Value::Type::Intrinsic: return "intrinsic"; case Value::Type::Intrinsic: return "intrinsic";