Lambdas!
This commit is contained in:
parent
8025e9bd0f
commit
881b90506b
12
examples/factorial.mq
Normal file
12
examples/factorial.mq
Normal 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)];
|
@ -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();
|
||||
|
@ -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 ¶m : 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();
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
54
src/value.cc
54
src/value.cc
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user