From d3a50abfbcd42c70ced406a9a910a04340238a64 Mon Sep 17 00:00:00 2001 From: Riyyi Date: Sat, 26 Aug 2023 11:57:21 +0200 Subject: [PATCH] Env: Organize functions better by splitting into multiple files --- src/ast.cpp | 2 +- src/{ => env}/environment.cpp | 18 +- src/{ => env}/environment.h | 8 +- src/env/functions/collection-access.cpp | 139 +++ src/env/functions/collection-constructor.cpp | 119 ++ src/env/functions/collection-modify.cpp | 265 +++++ src/env/functions/compare.cpp | 132 +++ src/env/functions/convert.cpp | 49 + src/env/functions/format.cpp | 56 + src/env/functions/meta.cpp | 53 + src/env/functions/mutable.cpp | 81 ++ src/env/functions/operators.cpp | 91 ++ src/env/functions/other.cpp | 71 ++ src/env/functions/predicate.cpp | 132 +++ src/env/functions/repl.cpp | 63 + src/env/macro.h | 32 + src/eval-special-form.cpp | 2 +- src/eval.cpp | 2 +- src/eval.h | 2 +- src/forward.h | 2 - src/functions.cpp | 1104 ------------------ src/repl.cpp | 4 +- 22 files changed, 1313 insertions(+), 1114 deletions(-) rename src/{ => env}/environment.cpp (81%) rename src/{ => env}/environment.h (77%) create mode 100644 src/env/functions/collection-access.cpp create mode 100644 src/env/functions/collection-constructor.cpp create mode 100644 src/env/functions/collection-modify.cpp create mode 100644 src/env/functions/compare.cpp create mode 100644 src/env/functions/convert.cpp create mode 100644 src/env/functions/format.cpp create mode 100644 src/env/functions/meta.cpp create mode 100644 src/env/functions/mutable.cpp create mode 100644 src/env/functions/operators.cpp create mode 100644 src/env/functions/other.cpp create mode 100644 src/env/functions/predicate.cpp create mode 100644 src/env/functions/repl.cpp create mode 100644 src/env/macro.h delete mode 100644 src/functions.cpp diff --git a/src/ast.cpp b/src/ast.cpp index 00b810c..7936420 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -11,7 +11,7 @@ #include #include "ast.h" -#include "environment.h" +#include "env/environment.h" #include "error.h" #include "forward.h" #include "printer.h" diff --git a/src/environment.cpp b/src/env/environment.cpp similarity index 81% rename from src/environment.cpp rename to src/env/environment.cpp index ce83ac2..fe02120 100644 --- a/src/environment.cpp +++ b/src/env/environment.cpp @@ -9,12 +9,14 @@ #include "ruc/format/format.h" #include "ast.h" -#include "environment.h" +#include "env/environment.h" #include "error.h" #include "forward.h" namespace blaze { +std::unordered_map Environment::s_functions; + EnvironmentPtr Environment::create() { return std::shared_ptr(new Environment); @@ -70,6 +72,20 @@ EnvironmentPtr Environment::create(const ValuePtr lambda, ValueVector&& argument // ----------------------------------------- +void Environment::registerFunction(const std::string& name, FunctionType function) +{ + s_functions.insert_or_assign(name, function); +} + +void Environment::installFunctions(EnvironmentPtr env) +{ + for (const auto& [name, function] : s_functions) { + env->set(name, makePtr(name, function)); + } +} + +// ----------------------------------------- + bool Environment::exists(const std::string& symbol) { return m_values.find(symbol) != m_values.end(); diff --git a/src/environment.h b/src/env/environment.h similarity index 77% rename from src/environment.h rename to src/env/environment.h index f116e12..583ead7 100644 --- a/src/environment.h +++ b/src/env/environment.h @@ -10,6 +10,7 @@ #include #include +#include "ast.h" #include "forward.h" namespace blaze { @@ -23,15 +24,20 @@ public: static EnvironmentPtr create(EnvironmentPtr outer); static EnvironmentPtr create(const ValuePtr lambda, ValueVector&& arguments); + static void registerFunction(const std::string& name, FunctionType function); + static void installFunctions(EnvironmentPtr env); + bool exists(const std::string& symbol); ValuePtr set(const std::string& symbol, ValuePtr value); ValuePtr get(const std::string& symbol); -protected: +private: Environment() {} EnvironmentPtr m_outer { nullptr }; std::unordered_map m_values; + + static std::unordered_map s_functions; }; } // namespace blaze diff --git a/src/env/functions/collection-access.cpp b/src/env/functions/collection-access.cpp new file mode 100644 index 0000000..9ce2aa3 --- /dev/null +++ b/src/env/functions/collection-access.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2023 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include // size_t +#include // std:static_pointer_cast + +#include "ast.h" +#include "env/macro.h" +#include "forward.h" +#include "util.h" + +namespace blaze { + +// (first (list 1 2 3)) -> 1 +ADD_FUNCTION( + "first", + { + CHECK_ARG_COUNT_IS("first", SIZE(), 1); + + if (is(begin->get()) + && std::static_pointer_cast(*begin)->state() == Constant::Nil) { + return makePtr(); + } + + VALUE_CAST(collection, Collection, (*begin)); + + return (collection->empty()) ? makePtr() : collection->front(); + }); + +// (nth (list 1 2 3) 0) -> 1 +ADD_FUNCTION( + "nth", + { + CHECK_ARG_COUNT_IS("nth", SIZE(), 2); + + VALUE_CAST(collection, Collection, (*begin)); + VALUE_CAST(number_node, Number, (*(begin + 1))); + auto collection_nodes = collection->nodesRead(); + auto index = static_cast(number_node->number()); + + if (number_node->number() < 0 || index >= collection_nodes.size()) { + Error::the().add("index is out of range"); + return nullptr; + } + + return collection_nodes[index]; + }); + +// (rest (list 1 2 3)) -> (2 3) +ADD_FUNCTION( + "rest", + { + CHECK_ARG_COUNT_IS("rest", SIZE(), 1); + + if (is(begin->get()) + && std::static_pointer_cast(*begin)->state() == Constant::Nil) { + return makePtr(); + } + + VALUE_CAST(collection, Collection, (*begin)); + + return makePtr(collection->rest()); + }); + +// ----------------------------------------- + +// (get {:kw "value"} :kw) -> "value" +ADD_FUNCTION( + "get", + { + CHECK_ARG_COUNT_AT_LEAST("get", SIZE(), 1); + + if (is(begin->get()) + && std::static_pointer_cast(*begin)->state() == Constant::Nil) { + return makePtr(); + } + + VALUE_CAST(hash_map, HashMap, (*begin)); + begin++; + + if (SIZE() == 0) { + return makePtr(); + } + + auto result = hash_map->get(*begin); + return (result) ? result : makePtr(); + }); + +// (keys {"foo" 3 :bar 5}) -> ("foo" :bar) +ADD_FUNCTION( + "keys", + { + CHECK_ARG_COUNT_AT_LEAST("keys", SIZE(), 1); + + VALUE_CAST(hash_map, HashMap, (*begin)); + + size_t count = hash_map->size(); + auto nodes = ValueVector(count); + + size_t i = 0; + auto elements = hash_map->elements(); + for (auto pair : elements) { + if (pair.first.front() == 0x7f) { // 127 + nodes.at(i) = makePtr(pair.first.substr(1)); + } + else { + nodes.at(i) = makePtr(pair.first); + } + i++; + } + + return makePtr(nodes); + }); + +// (vals {"foo" 3 :bar 5}) -> (3 5) +ADD_FUNCTION( + "vals", + { + CHECK_ARG_COUNT_AT_LEAST("vals", SIZE(), 1); + + VALUE_CAST(hash_map, HashMap, (*begin)); + + size_t count = hash_map->size(); + auto nodes = ValueVector(count); + + size_t i = 0; + auto elements = hash_map->elements(); + for (auto pair : elements) { + nodes.at(i) = pair.second; + i++; + } + + return makePtr(nodes); + }); + +} // namespace blaze diff --git a/src/env/functions/collection-constructor.cpp b/src/env/functions/collection-constructor.cpp new file mode 100644 index 0000000..60bba49 --- /dev/null +++ b/src/env/functions/collection-constructor.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2023 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include // size_t +#include // std:static_pointer_cast + +#include "ast.h" +#include "env/macro.h" +#include "forward.h" +#include "util.h" + +namespace blaze { + +// (list 1 2) -> (1 2) +ADD_FUNCTION( + "list", + { + return makePtr(begin, end); + }); + +// (make-list 4 nil) -> (nil nil nil nil) +ADD_FUNCTION( + "make-list", + { + CHECK_ARG_COUNT_IS("make-list", SIZE(), 2); + + VALUE_CAST(number, Number, (*begin)); + auto count = static_cast(number->number() < 0 ? 0 : number->number()); + auto value = *std::next(begin); + + auto nodes = ValueVector(count); + if (is(value.get())) { + auto atom = std::static_pointer_cast(value); + for (size_t i = 0; i < count; ++i) { + nodes[i] = makePtr(atom); + } + } + // else if (is(value.get())) { + // for (size_t i = 0; i < count; ++i) { + // auto nodes = std::static_pointer_cast(value)->nodesCopy(); + // if (is(value.get())) { + // makePtr(nodes); + // continue; + // } + // nodes[i] = makePtr(nodes); + // } + // } + // else if (is(value.get())) { + // for (size_t i = 0; i < count; ++i) { + // auto constant = std::static_pointer_cast(value); + // nodes[i] = makePtr(constant); + // } + // } + + // TODO: + // Atom + // Collection + // Constant + // Function + // HashMap + // Keyword + // Lambda + // List + // Macro + // Number + // String + // Symbol + // Vector + + return makePtr(std::move(nodes)); + }); + +// ----------------------------------------- + +// (vec (list 1 2 3)) +ADD_FUNCTION( + "vec", + { + CHECK_ARG_COUNT_IS("vec", SIZE(), 1); + + if (is(begin->get())) { + return *begin; + } + + VALUE_CAST(collection, Collection, (*begin)); + + return makePtr(collection->nodesCopy()); + }); + +// (vector 1 2 3) -> [1 2 3] +ADD_FUNCTION( + "vector", + { + auto result = makePtr(); + + return makePtr(begin, end); + }); + +// ----------------------------------------- + +// (hash-map "foo" 5 :bar 10) -> {"foo" 5 :bar 10} +ADD_FUNCTION( + "hash-map", + { + CHECK_ARG_COUNT_EVEN("hash-map", SIZE()); + + Elements elements; + for (auto it = begin; it != end; std::advance(it, 2)) { + const ValuePtr& value = *(std::next(it)); // temporary instance to get around const + elements.insert_or_assign(HashMap::getKeyString(*it), value); + } + + return makePtr(elements); + }); + +} // namespace blaze diff --git a/src/env/functions/collection-modify.cpp b/src/env/functions/collection-modify.cpp new file mode 100644 index 0000000..af48e8b --- /dev/null +++ b/src/env/functions/collection-modify.cpp @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2023 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include // std::copy, std::reverse_copy +#include // size_t +#include // std::static_pointer_cast + +#include "ast.h" +#include "env/environment.h" +#include "env/macro.h" +#include "forward.h" +#include "util.h" + +namespace blaze { + +// (apply + 1 2 (list 3 4)) -> (+ 1 2 3 4) -> 10 +ADD_FUNCTION( + "apply", + { + CHECK_ARG_COUNT_AT_LEAST("apply", SIZE(), 2); + + auto callable = *begin; + IS_VALUE(Callable, callable); + + VALUE_CAST(collection, Collection, (*std::prev(end))); + + auto arguments = ValueVector(begin + 1, end - 1); + arguments.reserve(arguments.size() + collection->size()); + + // Append list nodes to the argument leftovers + auto nodes = collection->nodesRead(); + for (const auto& node : nodes) { + arguments.push_back(node); + } + + ValuePtr value = nullptr; + if (is(callable.get())) { + auto function = std::static_pointer_cast(callable)->function(); + value = function(arguments.begin(), arguments.end()); + } + else { + auto lambda = std::static_pointer_cast(callable); + value = eval(lambda->body(), Environment::create(lambda, std::move(arguments))); + } + + return value; + }); + +// (cons 1 (list 2 3)) +ADD_FUNCTION( + "cons", + { + CHECK_ARG_COUNT_IS("cons", SIZE(), 2); + + ValuePtr first = *begin; + begin++; + + VALUE_CAST(collection, Collection, (*begin)); + const auto& collection_nodes = collection->nodesRead(); + + auto result_nodes = ValueVector(collection_nodes.size() + 1); + result_nodes.at(0) = first; + std::copy(collection_nodes.begin(), collection_nodes.end(), result_nodes.begin() + 1); + + return makePtr(result_nodes); + }); + +// (concat (list 1) (list 2 3)) -> (1 2 3) +ADD_FUNCTION( + "concat", + { + size_t count = 0; + for (auto it = begin; it != end; ++it) { + VALUE_CAST(collection, Collection, (*it)); + count += collection->size(); + } + + auto result_nodes = ValueVector(count); + size_t offset = 0; + for (auto it = begin; it != end; ++it) { + const auto& collection_nodes = std::static_pointer_cast(*it)->nodesRead(); + std::copy(collection_nodes.begin(), collection_nodes.end(), result_nodes.begin() + offset); + offset += collection_nodes.size(); + } + + return makePtr(result_nodes); + }); + +// (conj '(1 2 3) 4 5 6) -> (6 5 4 1 2 3) +// (conj [1 2 3] 4 5 6) -> [1 2 3 4 5 6] +ADD_FUNCTION( + "conj", + { + CHECK_ARG_COUNT_AT_LEAST("conj", SIZE(), 1); + + VALUE_CAST(collection, Collection, (*begin)); + begin++; + + const auto& collection_nodes = collection->nodesRead(); + size_t collection_count = collection_nodes.size(); + size_t argument_count = SIZE(); + + auto nodes = ValueVector(argument_count + collection_count); + + if (is(collection.get())) { + std::reverse_copy(begin, end, nodes.begin()); + std::copy(collection_nodes.begin(), collection_nodes.end(), nodes.begin() + argument_count); + + return makePtr(nodes); + } + + std::copy(collection_nodes.begin(), collection_nodes.end(), nodes.begin()); + std::copy(begin, end, nodes.begin() + collection_count); + + return makePtr(nodes); + }); + +// (map (fn* (x) (* x 2)) (list 1 2 3)) -> (2 4 6) +ADD_FUNCTION( + "map", + { + CHECK_ARG_COUNT_IS("map", SIZE(), 2); + + VALUE_CAST(callable, Callable, (*begin)); + VALUE_CAST(collection, Collection, (*(begin + 1))); + + size_t count = collection->size(); + auto nodes = ValueVector(count); + + if (is(callable.get())) { + auto function = std::static_pointer_cast(callable)->function(); + for (size_t i = 0; i < count; ++i) { + nodes.at(i) = function(collection->begin() + i, collection->begin() + i + 1); + } + } + else { + auto lambda = std::static_pointer_cast(callable); + auto collection_nodes = collection->nodesRead(); + for (size_t i = 0; i < count; ++i) { + nodes.at(i) = (eval(lambda->body(), Environment::create(lambda, { collection_nodes[i] }))); + } + } + + return makePtr(nodes); + }); + +// (set-nth (list 1 2 3) 1 "foo") -> (1 "foo" 3) +ADD_FUNCTION( + "set-nth", + { + CHECK_ARG_COUNT_IS("set-nth-element", SIZE(), 3); + + VALUE_CAST(collection, Collection, (*begin)); + + VALUE_CAST(number_node, Number, (*(begin + 1))); + auto index = static_cast(number_node->number() < 0 ? 0 : number_node->number()); + + auto value = *(begin + 2); + + auto collection_nodes = collection->nodesCopy(); + if (index >= collection->size()) { // Enlarge list if index out of bounds + collection_nodes.resize(index + 1, makePtr()); + } + collection_nodes[index] = value; + + if (is(begin->get())) { + return makePtr(collection_nodes); + } + + return makePtr(collection_nodes); + }); + +// (seq '(1 2 3)) -> (1 2 3) +// (seq [1 2 3]) -> (1 2 3) +// (seq "foo") -> ("f" "o" "o") +ADD_FUNCTION( + "seq", + { + CHECK_ARG_COUNT_IS("seq", SIZE(), 1); + + auto front = *begin; + Value* front_raw_ptr = front.get(); + + if (is(front_raw_ptr) && std::static_pointer_cast(front)->state() == Constant::Nil) { + return makePtr(); + } + if (is(front_raw_ptr)) { + auto collection = std::static_pointer_cast(front); + + if (collection->empty()) { + return makePtr(); + } + + if (is(front_raw_ptr)) { + return front; + } + + return makePtr(collection->nodesCopy()); + } + if (is(front_raw_ptr)) { + auto string = std::static_pointer_cast(front); + + if (string->empty()) { + return makePtr(); + } + + size_t count = string->size(); + auto nodes = ValueVector(count); + + const auto& data = string->data(); + for (size_t i = 0; i < count; ++i) { + nodes.at(i) = makePtr(data[i]); + } + + return makePtr(nodes); + } + + Error::the().add(::format("wrong argument type: Collection or String, {}", front)); + + return nullptr; + }); + +// ----------------------------------------- + +// (assoc {:a 1 :b 2} :a 3 :c 1) -> {:a 3 :b 2 :c 1} +ADD_FUNCTION( + "assoc", + { + CHECK_ARG_COUNT_AT_LEAST("assoc", SIZE(), 1); + + VALUE_CAST(hash_map, HashMap, (*begin)); + begin++; + + CHECK_ARG_COUNT_EVEN("assoc", SIZE()); + + Elements elements(hash_map->elements()); + for (auto it = begin; it != end; std::advance(it, 2)) { + const ValuePtr& value = *(std::next(it)); // temporary instance to get around const + elements.insert_or_assign(HashMap::getKeyString(*it), value); + } + + return makePtr(elements); + }); + +// (dissoc {:a 1 :b 2 :c 3} :a :c :d) -> {:b 2} +ADD_FUNCTION( + "dissoc", + { + CHECK_ARG_COUNT_AT_LEAST("dissoc", SIZE(), 1); + + VALUE_CAST(hash_map, HashMap, (*begin)); + begin++; + + Elements elements(hash_map->elements()); + for (auto it = begin; it != end; ++it) { + elements.erase(HashMap::getKeyString(*it)); + } + + return makePtr(elements); + }); + +} // namespace blaze diff --git a/src/env/functions/compare.cpp b/src/env/functions/compare.cpp new file mode 100644 index 0000000..abaa7e9 --- /dev/null +++ b/src/env/functions/compare.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2023 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include // int64_t +#include // std::function +#include // std::static_pointer_cast + +#include "ast.h" +#include "env/macro.h" +#include "util.h" + +namespace blaze { + +#define NUMBER_COMPARE(operator) \ + { \ + bool result = true; \ + \ + CHECK_ARG_COUNT_AT_LEAST(#operator, SIZE(), 2); \ + \ + /* Start with the first number */ \ + VALUE_CAST(number_node, Number, (*begin)); \ + int64_t number = number_node->number(); \ + \ + /* Skip the first node */ \ + for (auto it = begin + 1; it != end; ++it) { \ + VALUE_CAST(current_number_node, Number, (*it)); \ + int64_t current_number = current_number_node->number(); \ + if (!(number operator current_number)) { \ + result = false; \ + break; \ + } \ + number = current_number; \ + } \ + \ + return makePtr((result) ? Constant::True : Constant::False); \ + } + +ADD_FUNCTION("<", NUMBER_COMPARE(<)); +ADD_FUNCTION("<=", NUMBER_COMPARE(<=)); +ADD_FUNCTION(">", NUMBER_COMPARE(>)); +ADD_FUNCTION(">=", NUMBER_COMPARE(>=)); + +// ----------------------------------------- + +// (= 1 1) -> true +// (= "foo" "foo") -> true +ADD_FUNCTION( + "=", + { + CHECK_ARG_COUNT_AT_LEAST("=", SIZE(), 2); + + std::function equal = + [&equal](ValuePtr lhs, ValuePtr rhs) -> bool { + if ((is(lhs.get()) || is(lhs.get())) + && (is(rhs.get()) || is(rhs.get()))) { + auto lhs_collection = std::static_pointer_cast(lhs); + auto rhs_collection = std::static_pointer_cast(rhs); + + if (lhs_collection->size() != rhs_collection->size()) { + return false; + } + + auto lhs_it = lhs_collection->begin(); + auto rhs_it = rhs_collection->begin(); + for (; lhs_it != lhs_collection->end(); ++lhs_it, ++rhs_it) { + if (!equal(*lhs_it, *rhs_it)) { + return false; + } + } + + return true; + } + + if (is(lhs.get()) && is(rhs.get())) { + const auto& lhs_nodes = std::static_pointer_cast(lhs)->elements(); + const auto& rhs_nodes = std::static_pointer_cast(rhs)->elements(); + + if (lhs_nodes.size() != rhs_nodes.size()) { + return false; + } + + for (const auto& [key, value] : lhs_nodes) { + auto it = rhs_nodes.find(key); + if (it == rhs_nodes.cend() || !equal(value, it->second)) { + return false; + } + } + + return true; + } + + if (is(lhs.get()) && is(rhs.get()) + && std::static_pointer_cast(lhs)->data() == std::static_pointer_cast(rhs)->data()) { + return true; + } + if (is(lhs.get()) && is(rhs.get()) + && std::static_pointer_cast(lhs)->keyword() == std::static_pointer_cast(rhs)->keyword()) { + return true; + } + if (is(lhs.get()) && is(rhs.get()) + && std::static_pointer_cast(lhs)->number() == std::static_pointer_cast(rhs)->number()) { + return true; + } + if (is(lhs.get()) && is(rhs.get()) + && std::static_pointer_cast(lhs)->state() == std::static_pointer_cast(rhs)->state()) { + return true; + } + if (is(lhs.get()) && is(rhs.get()) + && std::static_pointer_cast(lhs)->symbol() == std::static_pointer_cast(rhs)->symbol()) { + return true; + } + + return false; + }; + + bool result = true; + auto it = begin; + auto it_next = begin + 1; + for (; it_next != end; ++it, ++it_next) { + if (!equal(*it, *it_next)) { + result = false; + break; + } + } + + return makePtr((result) ? Constant::True : Constant::False); + }); + +} // namespace blaze diff --git a/src/env/functions/convert.cpp b/src/env/functions/convert.cpp new file mode 100644 index 0000000..9025c04 --- /dev/null +++ b/src/env/functions/convert.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include "ast.h" +#include "env/macro.h" +#include "util.h" + +namespace blaze { + +// (symbol "foo") -> foo +ADD_FUNCTION( + "symbol", + { + CHECK_ARG_COUNT_IS("symbol", SIZE(), 1); + + if (is(begin->get())) { + return *begin; + } + + VALUE_CAST(stringValue, String, (*begin)); + + return makePtr(stringValue->data()); + }); + +// (keyword "foo") -> :foo +// (keyword 123) -> :123 +ADD_FUNCTION( + "keyword", + { + CHECK_ARG_COUNT_IS("symbol", SIZE(), 1); + + if (is(begin->get())) { + return *begin; + } + else if (is(begin->get())) { + VALUE_CAST(numberValue, Number, (*begin)); + + return makePtr(numberValue->number()); + } + + VALUE_CAST(stringValue, String, (*begin)); + + return makePtr(stringValue->data()); + }); + +} // namespace blaze diff --git a/src/env/functions/format.cpp b/src/env/functions/format.cpp new file mode 100644 index 0000000..a481522 --- /dev/null +++ b/src/env/functions/format.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include // std::next +#include + +#include "ruc/format/print.h" + +#include "ast.h" +#include "env/macro.h" +#include "printer.h" +#include "util.h" + +namespace blaze { + +#define PRINTER_STRING(print_readably, concatenation) \ + { \ + std::string result; \ + \ + Printer printer; \ + for (auto it = begin; it != end; ++it) { \ + result += ::format("{}", printer.printNoErrorCheck(*it, print_readably)); \ + \ + if (it != end && std::next(it) != end) { \ + result += concatenation; \ + } \ + } \ + \ + return makePtr(result); \ + } + +ADD_FUNCTION("str", PRINTER_STRING(false, "")); +ADD_FUNCTION("pr-str", PRINTER_STRING(true, " ")); + +#define PRINTER_PRINT(print_readably) \ + { \ + Printer printer; \ + for (auto it = begin; it != end; ++it) { \ + print("{}", printer.printNoErrorCheck(*it, print_readably)); \ + \ + if (it != end && std::next(it) != end) { \ + print(" "); \ + } \ + } \ + print("\n"); \ + \ + return makePtr(); \ + } + +ADD_FUNCTION("prn", PRINTER_PRINT(true)); +ADD_FUNCTION("println", PRINTER_PRINT(false)); + +} // namespace blaze diff --git a/src/env/functions/meta.cpp b/src/env/functions/meta.cpp new file mode 100644 index 0000000..a0cc891 --- /dev/null +++ b/src/env/functions/meta.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include "ast.h" +#include "env/macro.h" +#include "error.h" +#include "forward.h" +#include "util.h" + +namespace blaze { + +// (meta [1 2 3]) +ADD_FUNCTION( + "meta", + { + CHECK_ARG_COUNT_IS("meta", SIZE(), 1); + + auto front = *begin; + Value* front_raw_ptr = begin->get(); + + if (!is(front_raw_ptr) && // List / Vector + !is(front_raw_ptr) && // HashMap + !is(front_raw_ptr)) { // Function / Lambda + Error::the().add(::format("wrong argument type: Collection, HashMap or Callable, {}", front)); + return nullptr; + } + + return front->meta(); + }); + +// (with-meta [1 2 3] "some metadata") +ADD_FUNCTION( + "with-meta", + { + CHECK_ARG_COUNT_IS("with-meta", SIZE(), 2); + + auto front = *begin; + Value* front_raw_ptr = begin->get(); + + if (!is(front_raw_ptr) && // List / Vector + !is(front_raw_ptr) && // HashMap + !is(front_raw_ptr)) { // Function / Lambda + Error::the().add(::format("wrong argument type: Collection, HashMap or Callable, {}", front)); + return nullptr; + } + + return front->withMeta(*(begin + 1)); + }); + +} // namespace blaze diff --git a/src/env/functions/mutable.cpp b/src/env/functions/mutable.cpp new file mode 100644 index 0000000..a2b9887 --- /dev/null +++ b/src/env/functions/mutable.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include // std::copy +#include // std::static_pointer_cast + +#include "ast.h" +#include "env/environment.h" +#include "env/macro.h" +#include "forward.h" +#include "util.h" + +namespace blaze { + +// (atom 1) +ADD_FUNCTION( + "atom", + { + CHECK_ARG_COUNT_IS("atom", SIZE(), 1); + + return makePtr(*begin); + }); + +// (deref myatom) +ADD_FUNCTION( + "deref", + { + CHECK_ARG_COUNT_IS("deref", SIZE(), 1); + + VALUE_CAST(atom, Atom, (*begin)); + + return atom->deref(); + }); + +// (reset! myatom 2) +ADD_FUNCTION( + "reset!", + { + CHECK_ARG_COUNT_IS("reset!", SIZE(), 2); + + VALUE_CAST(atom, Atom, (*begin)); + auto value = *(begin + 1); + + atom->reset(value); + + return value; + }); + +// (swap! myatom (fn* [x y] (+ 1 x y)) 2) -> (deref (def! myatom (atom ((fn* [x y] (+ 1 x y)) (deref myatom) 2)))) +ADD_FUNCTION( + "swap!", + { + CHECK_ARG_COUNT_AT_LEAST("swap!", SIZE(), 2); + + VALUE_CAST(atom, Atom, (*begin)); + + VALUE_CAST(callable, Callable, (*(begin + 1))); + + // Remove atom and function from the argument list, add atom value + begin += 2; + auto arguments = ValueVector(SIZE() + 1); + arguments[0] = atom->deref(); + std::copy(begin, end, arguments.begin() + 1); + + ValuePtr value = nullptr; + if (is(callable.get())) { + auto function = std::static_pointer_cast(callable)->function(); + value = function(arguments.begin(), arguments.end()); + } + else { + auto lambda = std::static_pointer_cast(callable); + value = eval(lambda->body(), Environment::create(lambda, std::move(arguments))); + } + + return atom->reset(value); + }); + +} // namespace blaze diff --git a/src/env/functions/operators.cpp b/src/env/functions/operators.cpp new file mode 100644 index 0000000..f832e1f --- /dev/null +++ b/src/env/functions/operators.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include // int64_t + +#include "ast.h" +#include "env/macro.h" +#include "util.h" + +namespace blaze { + +ADD_FUNCTION( + "+", + { + int64_t result = 0; + + for (auto it = begin; it != end; ++it) { + VALUE_CAST(number, Number, (*it)); + result += number->number(); + } + + return makePtr(result); + }); + +ADD_FUNCTION( + "-", + { + if (SIZE() == 0) { + return makePtr(0); + } + + // Start with the first number + VALUE_CAST(number, Number, (*begin)); + int64_t result = number->number(); + + // Skip the first node + for (auto it = begin + 1; it != end; ++it) { + VALUE_CAST(number, Number, (*it)); + result -= number->number(); + } + + return makePtr(result); + }); + +ADD_FUNCTION( + "*", + { + int64_t result = 1; + + for (auto it = begin; it != end; ++it) { + VALUE_CAST(number, Number, (*it)); + result *= number->number(); + } + + return makePtr(result); + }); + +ADD_FUNCTION( + "/", + { + CHECK_ARG_COUNT_AT_LEAST("/", SIZE(), 1); + + // Start with the first number + VALUE_CAST(number, Number, (*begin)); + double result = number->number(); + + // Skip the first node + for (auto it = begin + 1; it != end; ++it) { + VALUE_CAST(number, Number, (*it)); + result /= number->number(); + } + + return makePtr((int64_t)result); + }); + +// (% 5 2) -> 1 +ADD_FUNCTION( + "%", + { + CHECK_ARG_COUNT_IS("/", SIZE(), 2); + + VALUE_CAST(divide, Number, (*begin)); + VALUE_CAST(by, Number, (*(begin + 1))); + + return makePtr(divide->number() % by->number()); + }); + +} // namespace blaze diff --git a/src/env/functions/other.cpp b/src/env/functions/other.cpp new file mode 100644 index 0000000..63b90fb --- /dev/null +++ b/src/env/functions/other.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2023 Riyi + * + * SPDX-License-Identifier: MIT + */ + +#include // std::chrono::sytem_clock +#include // size_t +#include // int64_t +#include // std::static_pointer_cast + +#include "ast.h" +#include "env/macro.h" +#include "error.h" +#include "forward.h" +#include "util.h" + +namespace blaze { + +// (count '(1 2 3)) -> 3 +// (count [1 2 3]) -> 3 +ADD_FUNCTION( + "count", + { + CHECK_ARG_COUNT_IS("count", SIZE(), 1); + + size_t result = 0; + if (is(begin->get()) && std::static_pointer_cast(*begin)->state() == Constant::Nil) { + // result = 0 + } + else if (is(begin->get())) { + result = std::static_pointer_cast(*begin)->size(); + } + else { + Error::the().add(::format("wrong argument type: Collection, '{}'", *begin)); + return nullptr; + } + + // FIXME: Add numeric_limits check for implicit cast: size_t > int64_t + return makePtr((int64_t)result); + }); + +// ----------------------------------------- + +// (throw x) +ADD_FUNCTION( + "throw", + { + CHECK_ARG_COUNT_IS("throw", SIZE(), 1); + + Error::the().add(*begin); + + return nullptr; + }) + +// ----------------------------------------- + +// (time-ms) +ADD_FUNCTION( + "time-ms", + { + CHECK_ARG_COUNT_IS("time-ms", SIZE(), 0); + + int64_t elapsed = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + return makePtr(elapsed); + }); + +} // namespace blaze diff --git a/src/env/functions/predicate.cpp b/src/env/functions/predicate.cpp new file mode 100644 index 0000000..2848fb8 --- /dev/null +++ b/src/env/functions/predicate.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2023 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include "ast.h" +#include "env/macro.h" +#include "util.h" + +namespace blaze { + +#define IS_CONSTANT(name, constant) \ + { \ + CHECK_ARG_COUNT_IS(name, SIZE(), 1); \ + \ + return makePtr( \ + is(begin->get()) \ + && std::static_pointer_cast(*begin)->state() == constant); \ + } + +// (nil? nil) +ADD_FUNCTION("nil?", IS_CONSTANT("nil?", Constant::Nil)); +ADD_FUNCTION("true?", IS_CONSTANT("true?", Constant::True)); +ADD_FUNCTION("false?", IS_CONSTANT("false?", Constant::False)); + +// ----------------------------------------- + +#define IS_TYPE(type) \ + { \ + bool result = true; \ + \ + if (SIZE() == 0) { \ + result = false; \ + } \ + \ + for (auto it = begin; it != end; ++it) { \ + if (!is(it->get())) { \ + result = false; \ + break; \ + } \ + } \ + \ + return makePtr(result); \ + } + +// (symbol? 'foo) +ADD_FUNCTION("atom?", IS_TYPE(Atom)); +ADD_FUNCTION("keyword?", IS_TYPE(Keyword)); +ADD_FUNCTION("list?", IS_TYPE(List)); +ADD_FUNCTION("map?", IS_TYPE(HashMap)); +ADD_FUNCTION("number?", IS_TYPE(Number)); +ADD_FUNCTION("sequential?", IS_TYPE(Collection)); +ADD_FUNCTION("string?", IS_TYPE(String)); +ADD_FUNCTION("symbol?", IS_TYPE(Symbol)); +ADD_FUNCTION("vector?", IS_TYPE(Vector)); + +ADD_FUNCTION( + "fn?", + { + bool result = true; + + if (SIZE() == 0) { + result = false; + } + + for (auto it = begin; it != end; ++it) { + if (!is(it->get()) || is(it->get())) { + result = false; + break; + } + } + + return makePtr(result); + }); + +ADD_FUNCTION( + "macro?", + { + bool result = true; + + if (SIZE() == 0) { + result = false; + } + + for (auto it = begin; it != end; ++it) { + if (!is(it->get())) { + result = false; + break; + } + } + + return makePtr(result); + }); + +// ----------------------------------------- + +// (contains? {:foo 5} :foo) -> true +// (contains? {"bar" 5} "foo") -> false +ADD_FUNCTION( + "contains?", + { + CHECK_ARG_COUNT_IS("contains?", SIZE(), 2); + + VALUE_CAST(hash_map, HashMap, (*begin)); + + if (SIZE() == 0) { + return makePtr(false); + } + + return makePtr(hash_map->exists(*(begin + 1))); + }); + +// (empty? '() '()) -> true +// (empty? [] [1 2 3] []) -> false +ADD_FUNCTION( + "empty?", + { + bool result = true; + + for (auto it = begin; it != end; ++it) { + VALUE_CAST(collection, Collection, (*it)); + if (!collection->empty()) { + result = false; + break; + } + } + + return makePtr((result) ? Constant::True : Constant::False); + }); + +} // namespace blaze diff --git a/src/env/functions/repl.cpp b/src/env/functions/repl.cpp new file mode 100644 index 0000000..3f934a3 --- /dev/null +++ b/src/env/functions/repl.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2023 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include + +#include "ruc/file.h" + +#include "ast.h" +#include "env/macro.h" +#include "util.h" + +namespace blaze { + +// REPL reader +ADD_FUNCTION( + "read-string", + { + CHECK_ARG_COUNT_IS("read-string", SIZE(), 1); + + VALUE_CAST(node, String, (*begin)); + std::string input = node->data(); + + return read(input); + }); + +// Read file contents +ADD_FUNCTION( + "slurp", + { + CHECK_ARG_COUNT_IS("slurp", SIZE(), 1); + + VALUE_CAST(node, String, (*begin)); + std::string path = node->data(); + + auto file = ruc::File(path); + + return makePtr(file.data()); + }); + +// Prompt readline +ADD_FUNCTION( + "readline", + { + CHECK_ARG_COUNT_IS("readline", SIZE(), 1); + + VALUE_CAST(prompt, String, (*begin)); + + return readline(prompt->data()); + }); + +// REPL eval +ADD_FUNCTION( + "eval", + { + CHECK_ARG_COUNT_IS("eval", SIZE(), 1); + + return eval(*begin, nullptr); + }); + +} // namespace blaze diff --git a/src/env/macro.h b/src/env/macro.h new file mode 100644 index 0000000..7630ca1 --- /dev/null +++ b/src/env/macro.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 Riyi + * + * SPDX-License-Identifier: MIT + */ + +#include + +#include "env/environment.h" + +// At the top-level you cant invoke any function, but you can create variables. +// Using a struct's constructor you can work around this limitation. +// Also, the counter macro is used to make the struct names unique. + +#define FUNCTION_STRUCT_NAME(unique) __functionStruct##unique + +#define ADD_FUNCTION_IMPL(unique, symbol, lambda) \ + struct FUNCTION_STRUCT_NAME(unique) { \ + FUNCTION_STRUCT_NAME(unique) \ + (const std::string& __symbol, FunctionType __lambda) \ + { \ + Environment::registerFunction(__symbol, __lambda); \ + } \ + }; \ + static struct FUNCTION_STRUCT_NAME(unique) \ + FUNCTION_STRUCT_NAME(unique)( \ + symbol, \ + [](ValueVectorConstIt begin, ValueVectorConstIt end) -> ValuePtr lambda); + +#define ADD_FUNCTION(symbol, lambda) ADD_FUNCTION_IMPL(__COUNTER__, symbol, lambda); + +#define SIZE() std::distance(begin, end) diff --git a/src/eval-special-form.cpp b/src/eval-special-form.cpp index 2b34681..0fcd7f6 100644 --- a/src/eval-special-form.cpp +++ b/src/eval-special-form.cpp @@ -10,7 +10,7 @@ #include #include "ast.h" -#include "environment.h" +#include "env/environment.h" #include "error.h" #include "eval.h" #include "forward.h" diff --git a/src/eval.cpp b/src/eval.cpp index ffbbc2a..6ebf210 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -11,7 +11,7 @@ #include #include "ast.h" -#include "environment.h" +#include "env/environment.h" #include "error.h" #include "eval.h" #include "forward.h" diff --git a/src/eval.h b/src/eval.h index f49672b..205d630 100644 --- a/src/eval.h +++ b/src/eval.h @@ -9,7 +9,7 @@ #include #include -#include "environment.h" +#include "env/environment.h" #include "forward.h" namespace blaze { diff --git a/src/forward.h b/src/forward.h index 5bebba1..b3f744b 100644 --- a/src/forward.h +++ b/src/forward.h @@ -32,6 +32,4 @@ extern ValuePtr readline(const std::string& prompt); extern ValuePtr read(std::string_view input); extern ValuePtr eval(ValuePtr ast, EnvironmentPtr env); -extern void installFunctions(EnvironmentPtr env); - } // namespace blaze diff --git a/src/functions.cpp b/src/functions.cpp deleted file mode 100644 index 96f3e70..0000000 --- a/src/functions.cpp +++ /dev/null @@ -1,1104 +0,0 @@ -/* - * Copyright (C) 2023 Riyi - * - * SPDX-License-Identifier: MIT - */ - -#include // std::copy, std::reverse_copy -#include // std::chrono::sytem_clock -#include // int64_t -#include // std::advance, std::distance, std::next, std::prev -#include // std::static_pointer_cast -#include -#include - -#include "ruc/file.h" -#include "ruc/format/format.h" - -#include "ast.h" -#include "environment.h" -#include "error.h" -#include "forward.h" -#include "printer.h" -#include "types.h" -#include "util.h" - -// At the top-level you cant invoke any function, but you can create variables. -// Using a struct's constructor you can work around this limitation. -// Also, the counter macro is used to make the struct names unique. - -#define FUNCTION_STRUCT_NAME(unique) __functionStruct##unique - -#define ADD_FUNCTION_IMPL(unique, symbol, lambda) \ - struct FUNCTION_STRUCT_NAME(unique) { \ - FUNCTION_STRUCT_NAME(unique) \ - (const std::string& __symbol, FunctionType __lambda) \ - { \ - s_functions.emplace(__symbol, __lambda); \ - } \ - }; \ - static struct FUNCTION_STRUCT_NAME(unique) \ - FUNCTION_STRUCT_NAME(unique)( \ - symbol, \ - [](ValueVectorConstIt begin, ValueVectorConstIt end) -> ValuePtr lambda); - -#define ADD_FUNCTION(symbol, lambda) ADD_FUNCTION_IMPL(__COUNTER__, symbol, lambda); - -#define SIZE() std::distance(begin, end) - -namespace blaze { - -static std::unordered_map s_functions; - -ADD_FUNCTION( - "+", - { - int64_t result = 0; - - for (auto it = begin; it != end; ++it) { - VALUE_CAST(number, Number, (*it)); - result += number->number(); - } - - return makePtr(result); - }); - -ADD_FUNCTION( - "-", - { - if (SIZE() == 0) { - return makePtr(0); - } - - // Start with the first number - VALUE_CAST(number, Number, (*begin)); - int64_t result = number->number(); - - // Skip the first node - for (auto it = begin + 1; it != end; ++it) { - VALUE_CAST(number, Number, (*it)); - result -= number->number(); - } - - return makePtr(result); - }); - -ADD_FUNCTION( - "*", - { - int64_t result = 1; - - for (auto it = begin; it != end; ++it) { - VALUE_CAST(number, Number, (*it)); - result *= number->number(); - } - - return makePtr(result); - }); - -ADD_FUNCTION( - "/", - { - CHECK_ARG_COUNT_AT_LEAST("/", SIZE(), 1); - - // Start with the first number - VALUE_CAST(number, Number, (*begin)); - double result = number->number(); - - // Skip the first node - for (auto it = begin + 1; it != end; ++it) { - VALUE_CAST(number, Number, (*it)); - result /= number->number(); - } - - return makePtr((int64_t)result); - }); - -// (% 5 2) -> 1 -ADD_FUNCTION( - "%", - { - CHECK_ARG_COUNT_IS("/", SIZE(), 2); - - VALUE_CAST(divide, Number, (*begin)); - VALUE_CAST(by, Number, (*(begin + 1))); - - return makePtr(divide->number() % by->number()); - }); - -// // ----------------------------------------- - -#define NUMBER_COMPARE(operator) \ - { \ - bool result = true; \ - \ - CHECK_ARG_COUNT_AT_LEAST(#operator, SIZE(), 2); \ - \ - /* Start with the first number */ \ - VALUE_CAST(number_node, Number, (*begin)); \ - int64_t number = number_node->number(); \ - \ - /* Skip the first node */ \ - for (auto it = begin + 1; it != end; ++it) { \ - VALUE_CAST(current_number_node, Number, (*it)); \ - int64_t current_number = current_number_node->number(); \ - if (!(number operator current_number)) { \ - result = false; \ - break; \ - } \ - number = current_number; \ - } \ - \ - return makePtr((result) ? Constant::True : Constant::False); \ - } - -ADD_FUNCTION("<", NUMBER_COMPARE(<)); -ADD_FUNCTION("<=", NUMBER_COMPARE(<=)); -ADD_FUNCTION(">", NUMBER_COMPARE(>)); -ADD_FUNCTION(">=", NUMBER_COMPARE(>=)); - -// ----------------------------------------- - -// (list 1 2) -> (1 2) -ADD_FUNCTION( - "list", - { - return makePtr(begin, end); - }); - -// (empty?) -ADD_FUNCTION( - "empty?", - { - bool result = true; - - for (auto it = begin; it != end; ++it) { - VALUE_CAST(collection, Collection, (*it)); - if (!collection->empty()) { - result = false; - break; - } - } - - return makePtr((result) ? Constant::True : Constant::False); - }); - -ADD_FUNCTION( - "count", - { - CHECK_ARG_COUNT_IS("count", SIZE(), 1); - - size_t result = 0; - if (is(begin->get()) && std::static_pointer_cast(*begin)->state() == Constant::Nil) { - // result = 0 - } - else if (is(begin->get())) { - result = std::static_pointer_cast(*begin)->size(); - } - else { - Error::the().add(::format("wrong argument type: Collection, '{}'", *begin)); - return nullptr; - } - - // FIXME: Add numeric_limits check for implicit cast: size_t > int64_t - return makePtr((int64_t)result); - }); - -// ----------------------------------------- - -#define PRINTER_STRING(print_readably, concatenation) \ - { \ - std::string result; \ - \ - Printer printer; \ - for (auto it = begin; it != end; ++it) { \ - result += ::format("{}", printer.printNoErrorCheck(*it, print_readably)); \ - \ - if (it != end && std::next(it) != end) { \ - result += concatenation; \ - } \ - } \ - \ - return makePtr(result); \ - } - -ADD_FUNCTION("str", PRINTER_STRING(false, "")); -ADD_FUNCTION("pr-str", PRINTER_STRING(true, " ")); - -#define PRINTER_PRINT(print_readably) \ - { \ - Printer printer; \ - for (auto it = begin; it != end; ++it) { \ - print("{}", printer.printNoErrorCheck(*it, print_readably)); \ - \ - if (it != end && std::next(it) != end) { \ - print(" "); \ - } \ - } \ - print("\n"); \ - \ - return makePtr(); \ - } - -ADD_FUNCTION("prn", PRINTER_PRINT(true)); -ADD_FUNCTION("println", PRINTER_PRINT(false)); - -// ----------------------------------------- - -// (= 1 1) -> true -// (= "foo" "foo") -> true -ADD_FUNCTION( - "=", - { - CHECK_ARG_COUNT_AT_LEAST("=", SIZE(), 2); - - std::function equal = - [&equal](ValuePtr lhs, ValuePtr rhs) -> bool { - if ((is(lhs.get()) || is(lhs.get())) - && (is(rhs.get()) || is(rhs.get()))) { - auto lhs_collection = std::static_pointer_cast(lhs); - auto rhs_collection = std::static_pointer_cast(rhs); - - if (lhs_collection->size() != rhs_collection->size()) { - return false; - } - - auto lhs_it = lhs_collection->begin(); - auto rhs_it = rhs_collection->begin(); - for (; lhs_it != lhs_collection->end(); ++lhs_it, ++rhs_it) { - if (!equal(*lhs_it, *rhs_it)) { - return false; - } - } - - return true; - } - - if (is(lhs.get()) && is(rhs.get())) { - const auto& lhs_nodes = std::static_pointer_cast(lhs)->elements(); - const auto& rhs_nodes = std::static_pointer_cast(rhs)->elements(); - - if (lhs_nodes.size() != rhs_nodes.size()) { - return false; - } - - for (const auto& [key, value] : lhs_nodes) { - auto it = rhs_nodes.find(key); - if (it == rhs_nodes.cend() || !equal(value, it->second)) { - return false; - } - } - - return true; - } - - if (is(lhs.get()) && is(rhs.get()) - && std::static_pointer_cast(lhs)->data() == std::static_pointer_cast(rhs)->data()) { - return true; - } - if (is(lhs.get()) && is(rhs.get()) - && std::static_pointer_cast(lhs)->keyword() == std::static_pointer_cast(rhs)->keyword()) { - return true; - } - if (is(lhs.get()) && is(rhs.get()) - && std::static_pointer_cast(lhs)->number() == std::static_pointer_cast(rhs)->number()) { - return true; - } - if (is(lhs.get()) && is(rhs.get()) - && std::static_pointer_cast(lhs)->state() == std::static_pointer_cast(rhs)->state()) { - return true; - } - if (is(lhs.get()) && is(rhs.get()) - && std::static_pointer_cast(lhs)->symbol() == std::static_pointer_cast(rhs)->symbol()) { - return true; - } - - return false; - }; - - bool result = true; - auto it = begin; - auto it_next = begin + 1; - for (; it_next != end; ++it, ++it_next) { - if (!equal(*it, *it_next)) { - result = false; - break; - } - } - - return makePtr((result) ? Constant::True : Constant::False); - }); - -ADD_FUNCTION( - "read-string", - { - CHECK_ARG_COUNT_IS("read-string", SIZE(), 1); - - VALUE_CAST(node, String, (*begin)); - std::string input = node->data(); - - return read(input); - }); - -ADD_FUNCTION( - "slurp", - { - CHECK_ARG_COUNT_IS("slurp", SIZE(), 1); - - VALUE_CAST(node, String, (*begin)); - std::string path = node->data(); - - auto file = ruc::File(path); - - return makePtr(file.data()); - }); - -ADD_FUNCTION( - "eval", - { - CHECK_ARG_COUNT_IS("eval", SIZE(), 1); - - return eval(*begin, nullptr); - }); - -// (atom 1) -ADD_FUNCTION( - "atom", - { - CHECK_ARG_COUNT_IS("atom", SIZE(), 1); - - return makePtr(*begin); - }); - -// (deref myatom) -ADD_FUNCTION( - "deref", - { - CHECK_ARG_COUNT_IS("deref", SIZE(), 1); - - VALUE_CAST(atom, Atom, (*begin)); - - return atom->deref(); - }); - -// (reset! myatom 2) -ADD_FUNCTION( - "reset!", - { - CHECK_ARG_COUNT_IS("reset!", SIZE(), 2); - - VALUE_CAST(atom, Atom, (*begin)); - auto value = *(begin + 1); - - atom->reset(value); - - return value; - }); - -// (swap! myatom (fn* [x y] (+ 1 x y)) 2) -> (deref (def! myatom (atom ((fn* [x y] (+ 1 x y)) (deref myatom) 2)))) -ADD_FUNCTION( - "swap!", - { - CHECK_ARG_COUNT_AT_LEAST("swap!", SIZE(), 2); - - VALUE_CAST(atom, Atom, (*begin)); - - VALUE_CAST(callable, Callable, (*(begin + 1))); - - // Remove atom and function from the argument list, add atom value - begin += 2; - auto arguments = ValueVector(SIZE() + 1); - arguments[0] = atom->deref(); - std::copy(begin, end, arguments.begin() + 1); - - ValuePtr value = nullptr; - if (is(callable.get())) { - auto function = std::static_pointer_cast(callable)->function(); - value = function(arguments.begin(), arguments.end()); - } - else { - auto lambda = std::static_pointer_cast(callable); - value = eval(lambda->body(), Environment::create(lambda, std::move(arguments))); - } - - return atom->reset(value); - }); - -// (cons 1 (list 2 3)) -ADD_FUNCTION( - "cons", - { - CHECK_ARG_COUNT_IS("cons", SIZE(), 2); - - ValuePtr first = *begin; - begin++; - - VALUE_CAST(collection, Collection, (*begin)); - const auto& collection_nodes = collection->nodesRead(); - - auto result_nodes = ValueVector(collection_nodes.size() + 1); - result_nodes.at(0) = first; - std::copy(collection_nodes.begin(), collection_nodes.end(), result_nodes.begin() + 1); - - return makePtr(result_nodes); - }); - -// (concat (list 1) (list 2 3)) -> (1 2 3) -ADD_FUNCTION( - "concat", - { - size_t count = 0; - for (auto it = begin; it != end; ++it) { - VALUE_CAST(collection, Collection, (*it)); - count += collection->size(); - } - - auto result_nodes = ValueVector(count); - size_t offset = 0; - for (auto it = begin; it != end; ++it) { - const auto& collection_nodes = std::static_pointer_cast(*it)->nodesRead(); - std::copy(collection_nodes.begin(), collection_nodes.end(), result_nodes.begin() + offset); - offset += collection_nodes.size(); - } - - return makePtr(result_nodes); - }); - -// (set-nth-element (list 1 2 3) 1 "foo") -> (1 "foo" 3) -ADD_FUNCTION( - "set-nth-element", - { - CHECK_ARG_COUNT_IS("set-nth-element", SIZE(), 3); - - VALUE_CAST(collection, Collection, (*begin)); - - VALUE_CAST(number_node, Number, (*(begin + 1))); - auto index = static_cast(number_node->number() < 0 ? 0 : number_node->number()); - - auto value = *(begin + 2); - - auto collection_nodes = collection->nodesCopy(); - if (index >= collection->size()) { // Enlarge list if index out of bounds - collection_nodes.resize(index + 1, makePtr()); - } - collection_nodes[index] = value; - - if (is(begin->get())) { - return makePtr(collection_nodes); - } - - return makePtr(collection_nodes); - }); - -// (vec (list 1 2 3)) -ADD_FUNCTION( - "vec", - { - CHECK_ARG_COUNT_IS("vec", SIZE(), 1); - - if (is(begin->get())) { - return *begin; - } - - VALUE_CAST(collection, Collection, (*begin)); - - return makePtr(collection->nodesCopy()); - }); - -// (nth (list 1 2 3) 0) -> 1 -ADD_FUNCTION( - "nth", - { - CHECK_ARG_COUNT_IS("nth", SIZE(), 2); - - VALUE_CAST(collection, Collection, (*begin)); - VALUE_CAST(number_node, Number, (*(begin + 1))); - auto collection_nodes = collection->nodesRead(); - auto index = static_cast(number_node->number()); - - if (number_node->number() < 0 || index >= collection_nodes.size()) { - Error::the().add("index is out of range"); - return nullptr; - } - - return collection_nodes[index]; - }); - -// (first (list 1 2 3)) -> 1 -ADD_FUNCTION( - "first", - { - CHECK_ARG_COUNT_IS("first", SIZE(), 1); - - if (is(begin->get()) - && std::static_pointer_cast(*begin)->state() == Constant::Nil) { - return makePtr(); - } - - VALUE_CAST(collection, Collection, (*begin)); - - return (collection->empty()) ? makePtr() : collection->front(); - }); - -// (rest (list 1 2 3)) -> (2 3) -ADD_FUNCTION( - "rest", - { - CHECK_ARG_COUNT_IS("rest", SIZE(), 1); - - if (is(begin->get()) - && std::static_pointer_cast(*begin)->state() == Constant::Nil) { - return makePtr(); - } - - VALUE_CAST(collection, Collection, (*begin)); - - return makePtr(collection->rest()); - }); - -// (apply + 1 2 (list 3 4)) -> (+ 1 2 3 4) -> 10 -ADD_FUNCTION( - "apply", - { - CHECK_ARG_COUNT_AT_LEAST("apply", SIZE(), 2); - - auto callable = *begin; - IS_VALUE(Callable, callable); - - VALUE_CAST(collection, Collection, (*std::prev(end))); - - auto arguments = ValueVector(begin + 1, end - 1); - arguments.reserve(arguments.size() + collection->size()); - - // Append list nodes to the argument leftovers - auto nodes = collection->nodesRead(); - for (const auto& node : nodes) { - arguments.push_back(node); - } - - ValuePtr value = nullptr; - if (is(callable.get())) { - auto function = std::static_pointer_cast(callable)->function(); - value = function(arguments.begin(), arguments.end()); - } - else { - auto lambda = std::static_pointer_cast(callable); - value = eval(lambda->body(), Environment::create(lambda, std::move(arguments))); - } - - return value; - }); - -// (map (fn* (x) (* x 2)) (list 1 2 3)) -> (2 4 6) -ADD_FUNCTION( - "map", - { - CHECK_ARG_COUNT_IS("map", SIZE(), 2); - - VALUE_CAST(callable, Callable, (*begin)); - VALUE_CAST(collection, Collection, (*(begin + 1))); - - size_t count = collection->size(); - auto nodes = ValueVector(count); - - if (is(callable.get())) { - auto function = std::static_pointer_cast(callable)->function(); - for (size_t i = 0; i < count; ++i) { - nodes.at(i) = function(collection->begin() + i, collection->begin() + i + 1); - } - } - else { - auto lambda = std::static_pointer_cast(callable); - auto collection_nodes = collection->nodesRead(); - for (size_t i = 0; i < count; ++i) { - nodes.at(i) = (eval(lambda->body(), Environment::create(lambda, { collection_nodes[i] }))); - } - } - - return makePtr(nodes); - }); - -// (throw x) -ADD_FUNCTION( - "throw", - { - CHECK_ARG_COUNT_IS("throw", SIZE(), 1); - - Error::the().add(*begin); - - return nullptr; - }) - -// ----------------------------------------- - -#define IS_CONSTANT(name, constant) \ - { \ - CHECK_ARG_COUNT_IS(name, SIZE(), 1); \ - \ - return makePtr( \ - is(begin->get()) \ - && std::static_pointer_cast(*begin)->state() == constant); \ - } - -// (nil? nil) -ADD_FUNCTION("nil?", IS_CONSTANT("nil?", Constant::Nil)); -ADD_FUNCTION("true?", IS_CONSTANT("true?", Constant::True)); -ADD_FUNCTION("false?", IS_CONSTANT("false?", Constant::False)); - -// ----------------------------------------- - -#define IS_TYPE(type) \ - { \ - bool result = true; \ - \ - if (SIZE() == 0) { \ - result = false; \ - } \ - \ - for (auto it = begin; it != end; ++it) { \ - if (!is(it->get())) { \ - result = false; \ - break; \ - } \ - } \ - \ - return makePtr(result); \ - } - -// (symbol? 'foo) -ADD_FUNCTION("atom?", IS_TYPE(Atom)); -ADD_FUNCTION("keyword?", IS_TYPE(Keyword)); -ADD_FUNCTION("list?", IS_TYPE(List)); -ADD_FUNCTION("map?", IS_TYPE(HashMap)); -ADD_FUNCTION("number?", IS_TYPE(Number)); -ADD_FUNCTION("sequential?", IS_TYPE(Collection)); -ADD_FUNCTION("string?", IS_TYPE(String)); -ADD_FUNCTION("symbol?", IS_TYPE(Symbol)); -ADD_FUNCTION("vector?", IS_TYPE(Vector)); - -ADD_FUNCTION( - "fn?", - { - bool result = true; - - if (SIZE() == 0) { - result = false; - } - - for (auto it = begin; it != end; ++it) { - if (!is(it->get()) || is(it->get())) { - result = false; - break; - } - } - - return makePtr(result); - }); - -ADD_FUNCTION( - "macro?", - { - bool result = true; - - if (SIZE() == 0) { - result = false; - } - - for (auto it = begin; it != end; ++it) { - if (!is(it->get())) { - result = false; - break; - } - } - - return makePtr(result); - }); - -// ----------------------------------------- - -// (symbol "foo") -> foo -ADD_FUNCTION( - "symbol", - { - CHECK_ARG_COUNT_IS("symbol", SIZE(), 1); - - if (is(begin->get())) { - return *begin; - } - - VALUE_CAST(stringValue, String, (*begin)); - - return makePtr(stringValue->data()); - }); - -// (keyword "foo") -> :foo -// (keyword 123) -> :123 -ADD_FUNCTION( - "keyword", - { - CHECK_ARG_COUNT_IS("symbol", SIZE(), 1); - - if (is(begin->get())) { - return *begin; - } - else if (is(begin->get())) { - VALUE_CAST(numberValue, Number, (*begin)); - - return makePtr(numberValue->number()); - } - - VALUE_CAST(stringValue, String, (*begin)); - - return makePtr(stringValue->data()); - }); - -// ----------------------------------------- - -// (vector 1 2 3) -> [1 2 3] -ADD_FUNCTION( - "vector", - { - auto result = makePtr(); - - return makePtr(begin, end); - }); - -// (hash-map "foo" 5 :bar 10) -> {"foo" 5 :bar 10} -ADD_FUNCTION( - "hash-map", - { - CHECK_ARG_COUNT_EVEN("hash-map", SIZE()); - - Elements elements; - for (auto it = begin; it != end; std::advance(it, 2)) { - const ValuePtr& value = *(std::next(it)); // temporary instance to get around const - elements.insert_or_assign(HashMap::getKeyString(*it), value); - } - - return makePtr(elements); - }); - -// (assoc {:a 1 :b 2} :a 3 :c 1) -> {:a 3 :b 2 :c 1} -ADD_FUNCTION( - "assoc", - { - CHECK_ARG_COUNT_AT_LEAST("assoc", SIZE(), 1); - - VALUE_CAST(hash_map, HashMap, (*begin)); - begin++; - - CHECK_ARG_COUNT_EVEN("assoc", SIZE()); - - Elements elements(hash_map->elements()); - for (auto it = begin; it != end; std::advance(it, 2)) { - const ValuePtr& value = *(std::next(it)); // temporary instance to get around const - elements.insert_or_assign(HashMap::getKeyString(*it), value); - } - - return makePtr(elements); - }); - -// (dissoc {:a 1 :b 2 :c 3} :a :c :d) -> {:b 2} -ADD_FUNCTION( - "dissoc", - { - CHECK_ARG_COUNT_AT_LEAST("dissoc", SIZE(), 1); - - VALUE_CAST(hash_map, HashMap, (*begin)); - begin++; - - Elements elements(hash_map->elements()); - for (auto it = begin; it != end; ++it) { - elements.erase(HashMap::getKeyString(*it)); - } - - return makePtr(elements); - }); - -// (get {:kw "value"} :kw) -> "value" -ADD_FUNCTION( - "get", - { - CHECK_ARG_COUNT_AT_LEAST("get", SIZE(), 1); - - if (is(begin->get()) - && std::static_pointer_cast(*begin)->state() == Constant::Nil) { - return makePtr(); - } - - VALUE_CAST(hash_map, HashMap, (*begin)); - begin++; - - if (SIZE() == 0) { - return makePtr(); - } - - auto result = hash_map->get(*begin); - return (result) ? result : makePtr(); - }); - -ADD_FUNCTION( - "contains?", - { - CHECK_ARG_COUNT_IS("contains?", SIZE(), 2); - - VALUE_CAST(hash_map, HashMap, (*begin)); - - if (SIZE() == 0) { - return makePtr(false); - } - - return makePtr(hash_map->exists(*(begin + 1))); - }); - -// (keys {"foo" 3 :bar 5}) -> ("foo" :bar) -ADD_FUNCTION( - "keys", - { - CHECK_ARG_COUNT_AT_LEAST("keys", SIZE(), 1); - - VALUE_CAST(hash_map, HashMap, (*begin)); - - size_t count = hash_map->size(); - auto nodes = ValueVector(count); - - size_t i = 0; - auto elements = hash_map->elements(); - for (auto pair : elements) { - if (pair.first.front() == 0x7f) { // 127 - nodes.at(i) = makePtr(pair.first.substr(1)); - } - else { - nodes.at(i) = makePtr(pair.first); - } - i++; - } - - return makePtr(nodes); - }); - -// (vals {"foo" 3 :bar 5}) -> (3 5) -ADD_FUNCTION( - "vals", - { - CHECK_ARG_COUNT_AT_LEAST("vals", SIZE(), 1); - - VALUE_CAST(hash_map, HashMap, (*begin)); - - size_t count = hash_map->size(); - auto nodes = ValueVector(count); - - size_t i = 0; - auto elements = hash_map->elements(); - for (auto pair : elements) { - nodes.at(i) = pair.second; - i++; - } - - return makePtr(nodes); - }); - -ADD_FUNCTION( - "readline", - { - CHECK_ARG_COUNT_IS("readline", SIZE(), 1); - - VALUE_CAST(prompt, String, (*begin)); - - return readline(prompt->data()); - }); - -ADD_FUNCTION( - "time-ms", - { - CHECK_ARG_COUNT_IS("time-ms", SIZE(), 0); - - int64_t elapsed = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - - return makePtr(elapsed); - }); - -// (meta [1 2 3]) -ADD_FUNCTION( - "meta", - { - CHECK_ARG_COUNT_IS("meta", SIZE(), 1); - - auto front = *begin; - Value* front_raw_ptr = begin->get(); - - if (!is(front_raw_ptr) && // List / Vector - !is(front_raw_ptr) && // HashMap - !is(front_raw_ptr)) { // Function / Lambda - Error::the().add(::format("wrong argument type: Collection, HashMap or Callable, {}", front)); - return nullptr; - } - - return front->meta(); - }); - -// (with-meta [1 2 3] "some metadata") -ADD_FUNCTION( - "with-meta", - { - CHECK_ARG_COUNT_IS("with-meta", SIZE(), 2); - - auto front = *begin; - Value* front_raw_ptr = begin->get(); - - if (!is(front_raw_ptr) && // List / Vector - !is(front_raw_ptr) && // HashMap - !is(front_raw_ptr)) { // Function / Lambda - Error::the().add(::format("wrong argument type: Collection, HashMap or Callable, {}", front)); - return nullptr; - } - - return front->withMeta(*(begin + 1)); - }); - -// (conj '(1 2 3) 4 5 6) -> (6 5 4 1 2 3) -// (conj [1 2 3] 4 5 6) -> [1 2 3 4 5 6] -ADD_FUNCTION( - "conj", - { - CHECK_ARG_COUNT_AT_LEAST("conj", SIZE(), 1); - - VALUE_CAST(collection, Collection, (*begin)); - begin++; - - const auto& collection_nodes = collection->nodesRead(); - size_t collection_count = collection_nodes.size(); - size_t argument_count = SIZE(); - - auto nodes = ValueVector(argument_count + collection_count); - - if (is(collection.get())) { - std::reverse_copy(begin, end, nodes.begin()); - std::copy(collection_nodes.begin(), collection_nodes.end(), nodes.begin() + argument_count); - - return makePtr(nodes); - } - - std::copy(collection_nodes.begin(), collection_nodes.end(), nodes.begin()); - std::copy(begin, end, nodes.begin() + collection_count); - - return makePtr(nodes); - }); - -// (seq '(1 2 3)) -> (1 2 3) -// (seq [1 2 3]) -> (1 2 3) -// (seq "foo") -> ("f" "o" "o") -ADD_FUNCTION( - "seq", - { - CHECK_ARG_COUNT_IS("seq", SIZE(), 1); - - auto front = *begin; - Value* front_raw_ptr = front.get(); - - if (is(front_raw_ptr) && std::static_pointer_cast(front)->state() == Constant::Nil) { - return makePtr(); - } - if (is(front_raw_ptr)) { - return front; - } - if (is(front_raw_ptr)) { - auto collection = std::static_pointer_cast(front); - - if (collection->empty()) { - return makePtr(); - } - - if (is(front_raw_ptr)) { - return front; - } - - return makePtr(collection->nodesCopy()); - } - if (is(front_raw_ptr)) { - auto string = std::static_pointer_cast(front); - - if (string->empty()) { - return makePtr(); - } - - size_t count = string->size(); - auto nodes = ValueVector(count); - - const auto& data = string->data(); - for (size_t i = 0; i < count; ++i) { - nodes.at(i) = makePtr(data[i]); - } - - return makePtr(nodes); - } - - Error::the().add(::format("wrong argument type: Collection or String, {}", front)); - - return nullptr; - }); - -// (make-list 4 nil) -> (nil nil nil nil) -ADD_FUNCTION( - "make-list", - { - CHECK_ARG_COUNT_IS("make-list", SIZE(), 2); - - VALUE_CAST(number, Number, (*begin)); - auto count = static_cast(number->number() < 0 ? 0 : number->number()); - auto value = *std::next(begin); - - auto nodes = ValueVector(count); - if (is(value.get())) { - auto atom = std::static_pointer_cast(value); - for (size_t i = 0; i < count; ++i) { - nodes[i] = makePtr(atom); - } - } - // else if (is(value.get())) { - // for (size_t i = 0; i < count; ++i) { - // auto nodes = std::static_pointer_cast(value)->nodesCopy(); - // if (is(value.get())) { - // makePtr(nodes); - // continue; - // } - // nodes[i] = makePtr(nodes); - // } - // } - // else if (is(value.get())) { - // for (size_t i = 0; i < count; ++i) { - // auto constant = std::static_pointer_cast(value); - // nodes[i] = makePtr(constant); - // } - // } - - // TODO: - // Atom - // Collection - // Constant - // Function - // HashMap - // Keyword - // Lambda - // List - // Macro - // Number - // String - // Symbol - // Vector - - return makePtr(std::move(nodes)); - }); - -// ----------------------------------------- - -void installFunctions(EnvironmentPtr env) -{ - for (const auto& [name, function] : s_functions) { - env->set(name, makePtr(name, function)); - } -} - -} // namespace blaze diff --git a/src/repl.cpp b/src/repl.cpp index cdd1bfa..ef84eed 100644 --- a/src/repl.cpp +++ b/src/repl.cpp @@ -14,7 +14,7 @@ #include "ruc/format/color.h" #include "ast.h" -#include "environment.h" +#include "env/environment.h" #include "error.h" #include "eval.h" #include "forward.h" @@ -149,7 +149,7 @@ auto main(int argc, char* argv[]) -> int std::signal(SIGINT, blaze::cleanup); std::signal(SIGTERM, blaze::cleanup); - installFunctions(blaze::s_outer_env); + blaze::Environment::installFunctions(blaze::s_outer_env); installLambdas(blaze::s_outer_env); makeArgv(blaze::s_outer_env, arguments);