diff --git a/examples/factorial.mq b/examples/factorial.mq new file mode 100644 index 0000000..8f8c6c0 --- /dev/null +++ b/examples/factorial.mq @@ -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)]; diff --git a/src/environment.cc b/src/environment.cc index 6077cff..a91454e 100644 --- a/src/environment.cc +++ b/src/environment.cc @@ -1,5 +1,7 @@ #include +#include + std::vector *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(); diff --git a/src/interpreter.cc b/src/interpreter.cc index c822f55..0e1e10d 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -2,14 +2,19 @@ #include -static auto numeric_binary_operator(auto binop) +static auto binary_operator(auto binop) { - return [binop = std::move(binop)](std::vector args) -> Result { + return [binop = std::move(binop)](Interpreter&, std::vector args) -> Result { 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>) { + 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 args) -> Value { + global.force_define("typeof", [](Interpreter&, std::vector 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 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 args) -> Result { + assert(args.size() == 2 || args.size() == 3, "argument count does not add up - expected: if []"); + 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 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 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 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 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 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(); } diff --git a/src/musique.hh b/src/musique.hh index 670d7a5..d96270f 100644 --- a/src/musique.hh +++ b/src/musique.hh @@ -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(std::vector)>; +using Function = std::function(Interpreter &i, std::vector)>; struct Lambda { @@ -440,6 +443,7 @@ constexpr auto is_one_of = (std::is_same_v || ...); struct Value { static Result 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 requires (!std::is_same_v, Value>) - && std::invocable> - && is_one_of>, Value, Result> + && std::invocable> + && is_one_of>, Value, Result> inline Value(Callable &&callable) : type{Type::Lambda} { - if constexpr (std::is_same_v, std::invoke_result_t>>) { + if constexpr (std::is_same_v, std::invoke_result_t>>) { f = std::move(callable); } else { - f = [fun = std::move(callable)](std::vector args) -> Result { - return fun(std::move(args)); + f = [fun = std::move(callable)](Interpreter &i, std::vector args) -> Result { + 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 operator()(std::vector args); + bool truthy() const; + bool falsy() const; + Result operator()(Interpreter &i, std::vector args); bool operator==(Value const& other) const; }; diff --git a/src/value.cc b/src/value.cc index bd492cb..75eea17 100644 --- a/src/value.cc +++ b/src/value.cc @@ -7,6 +7,10 @@ Result 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::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::operator()(std::vector args) +Result Value::operator()(Interpreter &i, std::vector 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 << ""; } @@ -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 Lambda::operator()(Interpreter &i, std::vector 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; +}