This commit is contained in:
Robert Bendun 2022-05-17 16:10:56 +02:00
parent 8025e9bd0f
commit 881b90506b
5 changed files with 136 additions and 26 deletions

12
examples/factorial.mq Normal file
View File

@ -0,0 +1,12 @@
var for = [ start stop iteration |
if (start > stop)
[| nil ]
[| iteration start; for (start + 1) stop iteration ]
];
var factorial = [n | if (n <= 1)
[| 1]
[| n * (factorial (n-1)) ]
];
for 1 10 [i | say (factorial i)];

View File

@ -1,5 +1,7 @@
#include <musique.hh>
#include <iostream>
std::vector<Env> *Env::pool = nullptr;
Env& Env::force_define(std::string name, Value new_value)
@ -25,6 +27,7 @@ Value* Env::find(std::string const& name)
usize Env::operator++() const
{
std::cerr << "ENTER SCOPE" << std::endl;
auto const parent_id = this - pool->data();
auto const free = std::find_if(pool->begin(), pool->end(), [](Env const& env) { return env.parent_enviroment_id == Env::Unused; });
Env* next = free == pool->end()
@ -37,6 +40,7 @@ usize Env::operator++() const
usize Env::operator--()
{
std::cerr << "LEAVE SCOPE" << std::endl;
if (this == pool->data())
return 0;
variables.clear();

View File

@ -2,14 +2,19 @@
#include <iostream>
static auto numeric_binary_operator(auto binop)
static auto binary_operator(auto binop)
{
return [binop = std::move(binop)](std::vector<Value> args) -> Result<Value> {
return [binop = std::move(binop)](Interpreter&, std::vector<Value> args) -> Result<Value> {
auto result = std::move(args.front());
for (auto &v : std::span(args).subspan(1)) {
assert(result.type == Value::Type::Number, "LHS should be a number");
assert(v.type == Value::Type::Number, "RHS should be a number");
result.n = binop(std::move(result.n), v.n);
if constexpr (std::is_same_v<Number, std::invoke_result_t<decltype(binop), Number, Number>>) {
result.n = binop(std::move(result.n), std::move(v).n);
} else {
result.type = Value::Type::Bool;
result.b = binop(std::move(result.n), std::move(v).n);
}
}
return result;
};
@ -34,25 +39,43 @@ Interpreter::Interpreter(std::ostream& out)
auto &global = env_pool.emplace_back();
global.parent_enviroment_id = 0;
global.force_define("typeof", [](std::vector<Value> args) -> Value {
global.force_define("typeof", [](Interpreter&, std::vector<Value> args) -> Value {
assert(args.size() == 1, "typeof expects only one argument");
return Value::symbol(std::string(type_name(args.front().type)));
});
global.force_define("say", [&out](std::vector<Value> args) -> Value {
for (auto it = args.begin(); it != args.end(); ++it) {
out << *it;
if (std::next(it) != args.end())
out << ' ';
global.force_define("if", [](Interpreter &i, std::vector<Value> args) -> Result<Value> {
assert(args.size() == 2 || args.size() == 3, "argument count does not add up - expected: if <condition> <then> [<else>]");
if (args.front().truthy()) {
return args[1](i, {});
} else if (args.size() == 3) {
return args[2](i, {});
} else {
return Value{};
}
out << '\n';
});
global.force_define("say", [](Interpreter &i, std::vector<Value> args) -> Value {
for (auto it = args.begin(); it != args.end(); ++it) {
i.out << *it;
if (std::next(it) != args.end())
i.out << ' ';
}
i.out << '\n';
return {};
});
operators["+"] = numeric_binary_operator(std::plus<>{});
operators["-"] = numeric_binary_operator(std::minus<>{});
operators["*"] = numeric_binary_operator(std::multiplies<>{});
operators["/"] = numeric_binary_operator(std::divides<>{});
operators["+"] = binary_operator(std::plus<>{});
operators["-"] = binary_operator(std::minus<>{});
operators["*"] = binary_operator(std::multiplies<>{});
operators["/"] = binary_operator(std::divides<>{});
operators["<"] = binary_operator(std::less<>{});
operators[">"] = binary_operator(std::greater<>{});
operators["<="] = binary_operator(std::less_equal<>{});
operators[">="] = binary_operator(std::greater_equal<>{});
operators["=="] = binary_operator(std::equal_to<>{});
operators["!="] = binary_operator(std::not_equal_to<>{});
}
Env& Interpreter::env()
@ -73,7 +96,7 @@ Result<Value> Interpreter::eval(Ast &&ast)
case Token::Type::Symbol:
if (ast.token.source != "nil") {
auto const value = env().find(std::string(ast.token.source));
assert(value, "Missing variable error is not implemented yet");
assert(value, "Missing variable error is not implemented yet: variable: "s + std::string(ast.token.source));
return *value;
}
return Value{};
@ -97,7 +120,7 @@ Result<Value> Interpreter::eval(Ast &&ast)
values.push_back(Try(eval(std::move(a))));
}
return op->second(std::move(values));
return op->second(*this, std::move(values));
}
break;
@ -118,7 +141,7 @@ Result<Value> Interpreter::eval(Ast &&ast)
for (auto& a : std::span(ast.arguments).subspan(1)) {
values.push_back(Try(eval(std::move(a))));
}
return std::move(func)(std::move(values));
return std::move(func)(*this, std::move(values));
}
case Ast::Type::Variable_Declaration:
@ -130,6 +153,19 @@ Result<Value> Interpreter::eval(Ast &&ast)
return Value{};
}
case Ast::Type::Lambda:
{
Lambda lambda;
auto parameters = std::span(ast.arguments.begin(), std::prev(ast.arguments.end()));
lambda.parameters.reserve(parameters.size());
for (auto &param : parameters) {
assert(param.type == Ast::Type::Literal && param.token.type == Token::Type::Symbol, "Not a name in parameter section of Ast::lambda");
lambda.parameters.push_back(std::string(std::move(param).token.source));
}
lambda.body = std::move(ast.arguments.back());
return Value::lambda(std::move(lambda));
}
default:
unimplemented();
}

View File

@ -19,6 +19,9 @@
#undef assert
#endif
using namespace std::string_literals;
using namespace std::string_view_literals;
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
@ -422,7 +425,7 @@ std::ostream& operator<<(std::ostream& os, Number const& num);
struct Value;
struct Interpreter;
using Function = std::function<Result<Value>(std::vector<Value>)>;
using Function = std::function<Result<Value>(Interpreter &i, std::vector<Value>)>;
struct Lambda
{
@ -440,6 +443,7 @@ constexpr auto is_one_of = (std::is_same_v<T, XS> || ...);
struct Value
{
static Result<Value> from(Token t);
static Value boolean(bool b);
static Value number(Number n);
static Value symbol(std::string s);
static Value lambda(Function f);
@ -447,6 +451,7 @@ struct Value
enum class Type
{
Nil,
Bool,
Number,
Symbol,
Lambda,
@ -460,21 +465,22 @@ struct Value
template<typename Callable>
requires (!std::is_same_v<std::remove_cvref_t<Callable>, Value>)
&& std::invocable<Callable, std::vector<Value>>
&& is_one_of<std::invoke_result_t<Callable, std::vector<Value>>, Value, Result<Value>>
&& std::invocable<Callable, Interpreter&, std::vector<Value>>
&& is_one_of<std::invoke_result_t<Callable, Interpreter&, std::vector<Value>>, Value, Result<Value>>
inline Value(Callable &&callable)
: type{Type::Lambda}
{
if constexpr (std::is_same_v<Result<Value>, std::invoke_result_t<Callable, std::vector<Value>>>) {
if constexpr (std::is_same_v<Result<Value>, std::invoke_result_t<Callable, Interpreter&, std::vector<Value>>>) {
f = std::move(callable);
} else {
f = [fun = std::move(callable)](std::vector<Value> args) -> Result<Value> {
return fun(std::move(args));
f = [fun = std::move(callable)](Interpreter &i, std::vector<Value> args) -> Result<Value> {
return fun(i, std::move(args));
};
}
}
Type type = Type::Nil;
bool b{};
Number n{};
Function f{};
@ -484,7 +490,9 @@ struct Value
// std::string_view - not-owning string type
std::string s{};
Result<Value> operator()(std::vector<Value> args);
bool truthy() const;
bool falsy() const;
Result<Value> operator()(Interpreter &i, std::vector<Value> args);
bool operator==(Value const& other) const;
};

View File

@ -7,6 +7,10 @@ Result<Value> Value::from(Token t)
return Number::from(std::move(t)).map(Value::number);
case Token::Type::Symbol:
if (t.source == "true")
return Value::boolean(true);
if (t.source == "false")
return Value::boolean(false);
if (t.source == "nil")
return Value{};
return Value::symbol(std::string(t.source));
@ -16,6 +20,14 @@ Result<Value> Value::from(Token t)
}
}
Value Value::boolean(bool b)
{
Value v;
v.type = Value::Type::Bool;
v.b = b;
return v;
}
Value Value::number(Number n)
{
Value v;
@ -40,15 +52,32 @@ Value Value::lambda(Function f)
return v;
}
Result<Value> Value::operator()(std::vector<Value> args)
Result<Value> Value::operator()(Interpreter &i, std::vector<Value> args)
{
if (type == Type::Lambda) {
return f(std::move(args));
return f(i, std::move(args));
}
// TODO Fill location
return errors::not_callable(std::nullopt, type);
}
bool Value::truthy() const
{
switch (type) {
case Type::Bool: return b;
case Type::Nil: return false;
case Type::Number: return n != Number(0);
case Type::Lambda:
case Type::Symbol: return true;
}
unreachable();
}
bool Value::falsy() const
{
return not truthy();
}
bool Value::operator==(Value const& other) const
{
if (type != other.type)
@ -59,6 +88,7 @@ bool Value::operator==(Value const& other) const
case Type::Number: return n == other.n;
case Type::Symbol: return s == other.s;
case Type::Lambda: return false; // TODO Reconsider if functions are comparable
case Type::Bool: return b == other.b;
}
unreachable();
@ -76,6 +106,9 @@ std::ostream& operator<<(std::ostream& os, Value const& v)
case Value::Type::Symbol:
return os << v.s;
case Value::Type::Bool:
return os << (v.b ? "true" : "false");
case Value::Type::Lambda:
return os << "<lambda>";
}
@ -85,6 +118,7 @@ std::ostream& operator<<(std::ostream& os, Value const& v)
std::string_view type_name(Value::Type t)
{
switch (t) {
case Value::Type::Bool: return "bool";
case Value::Type::Lambda: return "lambda";
case Value::Type::Nil: return "nil";
case Value::Type::Number: return "number";
@ -92,3 +126,19 @@ std::string_view type_name(Value::Type t)
}
unreachable();
}
Result<Value> Lambda::operator()(Interpreter &i, std::vector<Value> arguments)
{
i.current_env = ++i.env();
assert(parameters.size() == arguments.size(), "wrong number of arguments");
auto &env = i.env();
for (usize j = 0; j < parameters.size(); ++j) {
env.force_define(parameters[j], std::move(arguments[j]));
}
auto result = i.eval((Ast)body);
i.current_env = --i.env();
return result;
}