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 <musique.hh>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
std::vector<Env> *Env::pool = nullptr;
|
std::vector<Env> *Env::pool = nullptr;
|
||||||
|
|
||||||
Env& Env::force_define(std::string name, Value new_value)
|
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
|
usize Env::operator++() const
|
||||||
{
|
{
|
||||||
|
std::cerr << "ENTER SCOPE" << std::endl;
|
||||||
auto const parent_id = this - pool->data();
|
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; });
|
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()
|
Env* next = free == pool->end()
|
||||||
@ -37,6 +40,7 @@ usize Env::operator++() const
|
|||||||
|
|
||||||
usize Env::operator--()
|
usize Env::operator--()
|
||||||
{
|
{
|
||||||
|
std::cerr << "LEAVE SCOPE" << std::endl;
|
||||||
if (this == pool->data())
|
if (this == pool->data())
|
||||||
return 0;
|
return 0;
|
||||||
variables.clear();
|
variables.clear();
|
||||||
|
@ -2,14 +2,19 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#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());
|
auto result = std::move(args.front());
|
||||||
for (auto &v : std::span(args).subspan(1)) {
|
for (auto &v : std::span(args).subspan(1)) {
|
||||||
assert(result.type == Value::Type::Number, "LHS should be a number");
|
assert(result.type == Value::Type::Number, "LHS should be a number");
|
||||||
assert(v.type == Value::Type::Number, "RHS 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;
|
return result;
|
||||||
};
|
};
|
||||||
@ -34,25 +39,43 @@ Interpreter::Interpreter(std::ostream& out)
|
|||||||
auto &global = env_pool.emplace_back();
|
auto &global = env_pool.emplace_back();
|
||||||
global.parent_enviroment_id = 0;
|
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");
|
assert(args.size() == 1, "typeof expects only one argument");
|
||||||
return Value::symbol(std::string(type_name(args.front().type)));
|
return Value::symbol(std::string(type_name(args.front().type)));
|
||||||
});
|
});
|
||||||
|
|
||||||
global.force_define("say", [&out](std::vector<Value> args) -> Value {
|
global.force_define("if", [](Interpreter &i, std::vector<Value> args) -> Result<Value> {
|
||||||
for (auto it = args.begin(); it != args.end(); ++it) {
|
assert(args.size() == 2 || args.size() == 3, "argument count does not add up - expected: if <condition> <then> [<else>]");
|
||||||
out << *it;
|
if (args.front().truthy()) {
|
||||||
if (std::next(it) != args.end())
|
return args[1](i, {});
|
||||||
out << ' ';
|
} 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 {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
operators["+"] = numeric_binary_operator(std::plus<>{});
|
operators["+"] = binary_operator(std::plus<>{});
|
||||||
operators["-"] = numeric_binary_operator(std::minus<>{});
|
operators["-"] = binary_operator(std::minus<>{});
|
||||||
operators["*"] = numeric_binary_operator(std::multiplies<>{});
|
operators["*"] = binary_operator(std::multiplies<>{});
|
||||||
operators["/"] = numeric_binary_operator(std::divides<>{});
|
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()
|
Env& Interpreter::env()
|
||||||
@ -73,7 +96,7 @@ Result<Value> Interpreter::eval(Ast &&ast)
|
|||||||
case Token::Type::Symbol:
|
case Token::Type::Symbol:
|
||||||
if (ast.token.source != "nil") {
|
if (ast.token.source != "nil") {
|
||||||
auto const value = env().find(std::string(ast.token.source));
|
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;
|
||||||
}
|
}
|
||||||
return Value{};
|
return Value{};
|
||||||
@ -97,7 +120,7 @@ Result<Value> Interpreter::eval(Ast &&ast)
|
|||||||
values.push_back(Try(eval(std::move(a))));
|
values.push_back(Try(eval(std::move(a))));
|
||||||
}
|
}
|
||||||
|
|
||||||
return op->second(std::move(values));
|
return op->second(*this, std::move(values));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -118,7 +141,7 @@ Result<Value> Interpreter::eval(Ast &&ast)
|
|||||||
for (auto& a : std::span(ast.arguments).subspan(1)) {
|
for (auto& a : std::span(ast.arguments).subspan(1)) {
|
||||||
values.push_back(Try(eval(std::move(a))));
|
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:
|
case Ast::Type::Variable_Declaration:
|
||||||
@ -130,6 +153,19 @@ Result<Value> Interpreter::eval(Ast &&ast)
|
|||||||
return Value{};
|
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:
|
default:
|
||||||
unimplemented();
|
unimplemented();
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
#undef assert
|
#undef assert
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
using namespace std::string_literals;
|
||||||
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
using u8 = std::uint8_t;
|
using u8 = std::uint8_t;
|
||||||
using u16 = std::uint16_t;
|
using u16 = std::uint16_t;
|
||||||
using u32 = std::uint32_t;
|
using u32 = std::uint32_t;
|
||||||
@ -422,7 +425,7 @@ std::ostream& operator<<(std::ostream& os, Number const& num);
|
|||||||
struct Value;
|
struct Value;
|
||||||
struct Interpreter;
|
struct Interpreter;
|
||||||
|
|
||||||
using Function = std::function<Result<Value>(std::vector<Value>)>;
|
using Function = std::function<Result<Value>(Interpreter &i, std::vector<Value>)>;
|
||||||
|
|
||||||
struct Lambda
|
struct Lambda
|
||||||
{
|
{
|
||||||
@ -440,6 +443,7 @@ constexpr auto is_one_of = (std::is_same_v<T, XS> || ...);
|
|||||||
struct Value
|
struct Value
|
||||||
{
|
{
|
||||||
static Result<Value> from(Token t);
|
static Result<Value> from(Token t);
|
||||||
|
static Value boolean(bool b);
|
||||||
static Value number(Number n);
|
static Value number(Number n);
|
||||||
static Value symbol(std::string s);
|
static Value symbol(std::string s);
|
||||||
static Value lambda(Function f);
|
static Value lambda(Function f);
|
||||||
@ -447,6 +451,7 @@ struct Value
|
|||||||
enum class Type
|
enum class Type
|
||||||
{
|
{
|
||||||
Nil,
|
Nil,
|
||||||
|
Bool,
|
||||||
Number,
|
Number,
|
||||||
Symbol,
|
Symbol,
|
||||||
Lambda,
|
Lambda,
|
||||||
@ -460,21 +465,22 @@ struct Value
|
|||||||
|
|
||||||
template<typename Callable>
|
template<typename Callable>
|
||||||
requires (!std::is_same_v<std::remove_cvref_t<Callable>, Value>)
|
requires (!std::is_same_v<std::remove_cvref_t<Callable>, Value>)
|
||||||
&& std::invocable<Callable, std::vector<Value>>
|
&& std::invocable<Callable, Interpreter&, std::vector<Value>>
|
||||||
&& is_one_of<std::invoke_result_t<Callable, std::vector<Value>>, Value, Result<Value>>
|
&& is_one_of<std::invoke_result_t<Callable, Interpreter&, std::vector<Value>>, Value, Result<Value>>
|
||||||
inline Value(Callable &&callable)
|
inline Value(Callable &&callable)
|
||||||
: type{Type::Lambda}
|
: 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);
|
f = std::move(callable);
|
||||||
} else {
|
} else {
|
||||||
f = [fun = std::move(callable)](std::vector<Value> args) -> Result<Value> {
|
f = [fun = std::move(callable)](Interpreter &i, std::vector<Value> args) -> Result<Value> {
|
||||||
return fun(std::move(args));
|
return fun(i, std::move(args));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Type type = Type::Nil;
|
Type type = Type::Nil;
|
||||||
|
bool b{};
|
||||||
Number n{};
|
Number n{};
|
||||||
Function f{};
|
Function f{};
|
||||||
|
|
||||||
@ -484,7 +490,9 @@ struct Value
|
|||||||
// std::string_view - not-owning string type
|
// std::string_view - not-owning string type
|
||||||
std::string s{};
|
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;
|
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);
|
return Number::from(std::move(t)).map(Value::number);
|
||||||
|
|
||||||
case Token::Type::Symbol:
|
case Token::Type::Symbol:
|
||||||
|
if (t.source == "true")
|
||||||
|
return Value::boolean(true);
|
||||||
|
if (t.source == "false")
|
||||||
|
return Value::boolean(false);
|
||||||
if (t.source == "nil")
|
if (t.source == "nil")
|
||||||
return Value{};
|
return Value{};
|
||||||
return Value::symbol(std::string(t.source));
|
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 Value::number(Number n)
|
||||||
{
|
{
|
||||||
Value v;
|
Value v;
|
||||||
@ -40,15 +52,32 @@ Value Value::lambda(Function f)
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Value> Value::operator()(std::vector<Value> args)
|
Result<Value> Value::operator()(Interpreter &i, std::vector<Value> args)
|
||||||
{
|
{
|
||||||
if (type == Type::Lambda) {
|
if (type == Type::Lambda) {
|
||||||
return f(std::move(args));
|
return f(i, std::move(args));
|
||||||
}
|
}
|
||||||
// TODO Fill location
|
// TODO Fill location
|
||||||
return errors::not_callable(std::nullopt, type);
|
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
|
bool Value::operator==(Value const& other) const
|
||||||
{
|
{
|
||||||
if (type != other.type)
|
if (type != other.type)
|
||||||
@ -59,6 +88,7 @@ bool Value::operator==(Value const& other) const
|
|||||||
case Type::Number: return n == other.n;
|
case Type::Number: return n == other.n;
|
||||||
case Type::Symbol: return s == other.s;
|
case Type::Symbol: return s == other.s;
|
||||||
case Type::Lambda: return false; // TODO Reconsider if functions are comparable
|
case Type::Lambda: return false; // TODO Reconsider if functions are comparable
|
||||||
|
case Type::Bool: return b == other.b;
|
||||||
}
|
}
|
||||||
|
|
||||||
unreachable();
|
unreachable();
|
||||||
@ -76,6 +106,9 @@ std::ostream& operator<<(std::ostream& os, Value const& v)
|
|||||||
case Value::Type::Symbol:
|
case Value::Type::Symbol:
|
||||||
return os << v.s;
|
return os << v.s;
|
||||||
|
|
||||||
|
case Value::Type::Bool:
|
||||||
|
return os << (v.b ? "true" : "false");
|
||||||
|
|
||||||
case Value::Type::Lambda:
|
case Value::Type::Lambda:
|
||||||
return os << "<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)
|
std::string_view type_name(Value::Type t)
|
||||||
{
|
{
|
||||||
switch (t) {
|
switch (t) {
|
||||||
|
case Value::Type::Bool: return "bool";
|
||||||
case Value::Type::Lambda: return "lambda";
|
case Value::Type::Lambda: return "lambda";
|
||||||
case Value::Type::Nil: return "nil";
|
case Value::Type::Nil: return "nil";
|
||||||
case Value::Type::Number: return "number";
|
case Value::Type::Number: return "number";
|
||||||
@ -92,3 +126,19 @@ std::string_view type_name(Value::Type t)
|
|||||||
}
|
}
|
||||||
unreachable();
|
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