From 2a16f5ddf5cb22dbebd2b220becdc0c97ab5fd89 Mon Sep 17 00:00:00 2001 From: Riyyi Date: Sat, 15 Apr 2023 20:19:24 +0200 Subject: [PATCH] AST+Env: Add more core functions --- src/ast.cpp | 78 +++++++++++- src/ast.h | 20 ++- src/functions.cpp | 312 +++++++++++++++++++++++++++++++++++++++------- src/reader.cpp | 4 +- 4 files changed, 357 insertions(+), 57 deletions(-) diff --git a/src/ast.cpp b/src/ast.cpp index 5c4bd93..c951bae 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -10,6 +10,7 @@ #include "ast.h" #include "environment.h" +#include "error.h" #include "forward.h" #include "printer.h" #include "types.h" @@ -46,6 +47,11 @@ Vector::Vector(const std::list& nodes) // ----------------------------------------- +HashMap::HashMap(const Elements& elements) + : m_elements(elements) +{ +} + void HashMap::add(const std::string& key, ValuePtr value) { if (value == nullptr) { @@ -55,6 +61,65 @@ void HashMap::add(const std::string& key, ValuePtr value) m_elements.emplace(key, value); } +void HashMap::add(ValuePtr key, ValuePtr value) +{ + if (key == nullptr || value == nullptr) { + return; + } + + m_elements.emplace(getKeyString(key), value); +} + +void HashMap::remove(const std::string& key) +{ + m_elements.erase(key); +} + +void HashMap::remove(ValuePtr key) +{ + if (key == nullptr) { + return; + } + + m_elements.erase(getKeyString(key)); +} + +bool HashMap::exists(const std::string& key) +{ + return m_elements.find(key) != m_elements.end(); +} + +bool HashMap::exists(ValuePtr key) +{ + return exists(getKeyString(key)); +} + +ValuePtr HashMap::get(const std::string& key) +{ + if (!exists(key)) { + return nullptr; + } + + return m_elements[key]; +} + +ValuePtr HashMap::get(ValuePtr key) +{ + return get(getKeyString(key)); +} + +std::string HashMap::getKeyString(ValuePtr key) +{ + if (!is(key.get()) && !is(key.get())) { + Error::the().add(format("wrong argument type: string or keyword, {}", key)); + return {}; + } + + return is(key.get()) + ? std::static_pointer_cast(key)->data() + : std::static_pointer_cast(key)->keyword(); +} + // ----------------------------------------- String::String(const std::string& data) @@ -78,15 +143,20 @@ Number::Number(int64_t number) // ----------------------------------------- -Symbol::Symbol(const std::string& symbol) - : m_symbol(symbol) +Constant::Constant(State state) + : m_state(state) +{ +} + +Constant::Constant(bool state) + : m_state(state ? Constant::True : Constant::False) { } // ----------------------------------------- -Constant::Constant(State state) - : m_state(state) +Symbol::Symbol(const std::string& symbol) + : m_symbol(symbol) { } diff --git a/src/ast.h b/src/ast.h index 7f3edfc..720cc42 100644 --- a/src/ast.h +++ b/src/ast.h @@ -10,12 +10,12 @@ #include // int64_t, uint8_t #include // std::function #include +#include #include // std::shared_ptr #include #include #include #include // typeid -#include #include #include "ruc/format/formatter.h" @@ -128,19 +128,31 @@ private: // {} class HashMap final : public Value { public: + using Elements = std::map; + HashMap() = default; + HashMap(const Elements& elements); virtual ~HashMap() = default; void add(const std::string& key, ValuePtr value); - - const std::unordered_map& elements() const { return m_elements; } + void add(ValuePtr key, ValuePtr value); + void remove(const std::string& key); + void remove(ValuePtr key); + + bool exists(const std::string& key); + bool exists(ValuePtr key); + ValuePtr get(const std::string& key); + ValuePtr get(ValuePtr key); + const Elements& elements() const { return m_elements; } size_t size() const { return m_elements.size(); } bool empty() const { return m_elements.size() == 0; } private: virtual bool isHashMap() const override { return true; } - std::unordered_map m_elements; + std::string getKeyString(ValuePtr key); + + Elements m_elements; }; // ----------------------------------------- diff --git a/src/functions.cpp b/src/functions.cpp index bb06d72..ee7fe5e 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -4,7 +4,8 @@ * SPDX-License-Identifier: MIT */ -#include // std::static_pointer_cast +#include // std::advance +#include // std::static_pointer_cast #include #include "ruc/file.h" @@ -146,25 +147,6 @@ ADD_FUNCTION( return makePtr(nodes); }); -ADD_FUNCTION( - "list?", - { - bool result = true; - - if (nodes.size() == 0) { - result = false; - } - - for (auto node : nodes) { - if (!is(node.get())) { - result = false; - break; - } - } - - return makePtr((result) ? Constant::True : Constant::False); - }); - ADD_FUNCTION( "empty?", { @@ -369,26 +351,6 @@ ADD_FUNCTION( return makePtr(nodes.front()); }); -// (atom? myatom 2 "foo") -ADD_FUNCTION( - "atom?", - { - bool result = true; - - if (nodes.size() == 0) { - result = false; - } - - for (auto node : nodes) { - if (!is(node.get())) { - result = false; - break; - } - } - - return makePtr((result) ? Constant::True : Constant::False); - }); - // (deref myatom) ADD_FUNCTION( "deref", @@ -414,7 +376,7 @@ ADD_FUNCTION( return value; }); -// (swap! myatom (fn* [x] (+ 1 x))) +// (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!", { @@ -422,8 +384,8 @@ ADD_FUNCTION( VALUE_CAST(atom, Atom, nodes.front()); - auto second_argument = *std::next(nodes.begin()); - IS_VALUE(Callable, second_argument); + auto callable = *std::next(nodes.begin()); + IS_VALUE(Callable, callable); // Remove atom and function from the argument list, add atom value nodes.pop_front(); @@ -431,12 +393,12 @@ ADD_FUNCTION( nodes.push_front(atom->deref()); ValuePtr value = nullptr; - if (is(second_argument.get())) { - auto function = std::static_pointer_cast(second_argument)->function(); + if (is(callable.get())) { + auto function = std::static_pointer_cast(callable)->function(); value = function(nodes); } else { - auto lambda = std::static_pointer_cast(second_argument); + auto lambda = std::static_pointer_cast(callable); value = eval(lambda->body(), Environment::create(lambda, nodes)); } @@ -548,6 +510,264 @@ ADD_FUNCTION( return makePtr(collection_nodes); }); +// (apply + 1 2 (list 3 4)) -> (+ 1 2 3 4) +ADD_FUNCTION( + "apply", + { + CHECK_ARG_COUNT_AT_LEAST("apply", nodes.size(), 2); + + auto callable = nodes.front(); + IS_VALUE(Callable, callable); + + VALUE_CAST(collection, Collection, nodes.back()); + + // Remove function and list from the arguments + nodes.pop_front(); + nodes.pop_back(); + + // Append list nodes to the argument leftovers + auto collection_nodes = collection->nodes(); + nodes.splice(nodes.end(), collection_nodes); + + ValuePtr value = nullptr; + if (is(callable.get())) { + auto function = std::static_pointer_cast(callable)->function(); + value = function(nodes); + } + else { + auto lambda = std::static_pointer_cast(callable); + value = eval(lambda->body(), Environment::create(lambda, nodes)); + } + + return value; + }); + +// (map (fn* (x) (* x 2)) (list 1 2 3)) +ADD_FUNCTION( + "map", + { + CHECK_ARG_COUNT_IS("map", nodes.size(), 2); + + VALUE_CAST(callable, Callable, nodes.front()); + VALUE_CAST(collection, Collection, nodes.back()); + auto collection_nodes = collection->nodes(); + + auto result = makePtr(); + + if (is(callable.get())) { + auto function = std::static_pointer_cast(callable)->function(); + for (auto node : collection_nodes) { + result->add(function({ node })); + } + } + else { + auto lambda = std::static_pointer_cast(callable); + for (auto node : collection_nodes) { + result->add(eval(lambda->body(), Environment::create(lambda, { node }))); + } + } + + return result; + }); + +// ----------------------------------------- + +#define IS_CONSTANT(name, constant) \ + { \ + CHECK_ARG_COUNT_IS(name, nodes.size(), 1); \ + \ + return makePtr( \ + is(nodes.front().get()) \ + && std::static_pointer_cast(nodes.front())->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 (nodes.size() == 0) { \ + result = false; \ + } \ + \ + for (auto node : nodes) { \ + if (!is(node.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("sequential?", IS_TYPE(Collection)); +ADD_FUNCTION("symbol?", IS_TYPE(Symbol)); +ADD_FUNCTION("vector?", IS_TYPE(Vector)); + +// ----------------------------------------- + +#define STRING_TO_TYPE(name, type) \ + { \ + CHECK_ARG_COUNT_IS(name, nodes.size(), 1); \ + \ + if (is(nodes.front().get())) { \ + return nodes.front(); \ + } \ + \ + VALUE_CAST(stringValue, String, nodes.front()); \ + \ + return makePtr(stringValue->data()); \ + } + +// (symbol "foo") +ADD_FUNCTION("symbol", STRING_TO_TYPE("symbol", Symbol)); +ADD_FUNCTION("keyword", STRING_TO_TYPE("keyword", Keyword)); + +// ----------------------------------------- + +ADD_FUNCTION( + "vector", + { + auto result = makePtr(); + + for (auto node : nodes) { + result->add(node); + } + + return result; + }); + +ADD_FUNCTION( + "hash-map", + { + CHECK_ARG_COUNT_EVEN("hash-map", nodes.size()); + + auto result = makePtr(); + + for (auto it = nodes.begin(); it != nodes.end(); std::advance(it, 2)) { + result->add(*it, *(std::next(it))); + } + + return result; + }); + +ADD_FUNCTION( + "assoc", + { + CHECK_ARG_COUNT_AT_LEAST("assoc", nodes.size(), 1); + + VALUE_CAST(hash_map, HashMap, nodes.front()); + nodes.pop_front(); + + CHECK_ARG_COUNT_EVEN("assoc", nodes.size()); + + auto result = makePtr(hash_map->elements()); + + for (auto it = nodes.begin(); it != nodes.end(); std::advance(it, 2)) { + result->add(*it, *(std::next(it))); + } + + return result; + }); + +ADD_FUNCTION( + "dissoc", + { + CHECK_ARG_COUNT_AT_LEAST("dissoc", nodes.size(), 1); + + VALUE_CAST(hash_map, HashMap, nodes.front()); + nodes.pop_front(); + + auto result = makePtr(hash_map->elements()); + + for (auto node : nodes) { + result->remove(node); + } + + return result; + }); + +ADD_FUNCTION( + "get", + { + CHECK_ARG_COUNT_AT_LEAST("get", nodes.size(), 1); + + VALUE_CAST(hash_map, HashMap, nodes.front()); + nodes.pop_front(); + + if (nodes.size() == 0) { + return makePtr(); + } + + auto result = hash_map->get(nodes.front()); + return (result) ? result : makePtr(); + }); + +ADD_FUNCTION( + "contains?", + { + CHECK_ARG_COUNT_AT_LEAST("contains?", nodes.size(), 1); + + VALUE_CAST(hash_map, HashMap, nodes.front()); + nodes.pop_front(); + + if (nodes.size() == 0) { + return makePtr(false); + } + + return makePtr(hash_map->exists(nodes.front())); + }); + +ADD_FUNCTION( + "keys", + { + CHECK_ARG_COUNT_AT_LEAST("keys", nodes.size(), 1); + + VALUE_CAST(hash_map, HashMap, nodes.front()); + + auto result = makePtr(); + + auto elements = hash_map->elements(); + for (auto pair : elements) { + if (pair.first.front() == 0x7f) { // 127 + result->add(makePtr(pair.first.substr(1))); + } + else { + result->add(makePtr(pair.first)); + } + } + + return result; + }); + +ADD_FUNCTION( + "vals", + { + CHECK_ARG_COUNT_AT_LEAST("vals", nodes.size(), 1); + + VALUE_CAST(hash_map, HashMap, nodes.front()); + + auto result = makePtr(); + + auto elements = hash_map->elements(); + for (auto pair : elements) { + result->add(pair.second); + } + + return result; + }); + // ----------------------------------------- void installFunctions(EnvironmentPtr env) diff --git a/src/reader.cpp b/src/reader.cpp index 4242b52..037bb09 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -196,9 +196,7 @@ ValuePtr Reader::readHashMap() } auto value = readImpl(); - - std::string keyString = is(key.get()) ? std::static_pointer_cast(key)->data() : std::static_pointer_cast(key)->keyword(); - hash_map->add(keyString, value); + hash_map->add(key, value); } if (!consumeSpecific(Token { .type = Token::Type::BraceClose })) { // }