AST+Env: Add more core functions
This commit is contained in:
+74
-4
@@ -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<ValuePtr>& 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<String>(key.get()) && !is<Keyword>(key.get())) {
|
||||
Error::the().add(format("wrong argument type: string or keyword, {}", key));
|
||||
return {};
|
||||
}
|
||||
|
||||
return is<String>(key.get())
|
||||
? std::static_pointer_cast<String>(key)->data()
|
||||
: std::static_pointer_cast<Keyword>(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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
#include <cstdint> // int64_t, uint8_t
|
||||
#include <functional> // std::function
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory> // std::shared_ptr
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <typeinfo> // typeid
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "ruc/format/formatter.h"
|
||||
@@ -128,19 +128,31 @@ private:
|
||||
// {}
|
||||
class HashMap final : public Value {
|
||||
public:
|
||||
using Elements = std::map<std::string, ValuePtr>;
|
||||
|
||||
HashMap() = default;
|
||||
HashMap(const Elements& elements);
|
||||
virtual ~HashMap() = default;
|
||||
|
||||
void add(const std::string& key, ValuePtr value);
|
||||
void add(ValuePtr key, ValuePtr value);
|
||||
void remove(const std::string& key);
|
||||
void remove(ValuePtr key);
|
||||
|
||||
const std::unordered_map<std::string, ValuePtr>& elements() const { return m_elements; }
|
||||
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<std::string, ValuePtr> m_elements;
|
||||
std::string getKeyString(ValuePtr key);
|
||||
|
||||
Elements m_elements;
|
||||
};
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
+266
-46
@@ -4,7 +4,8 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <memory> // std::static_pointer_cast
|
||||
#include <iterator> // std::advance
|
||||
#include <memory> // std::static_pointer_cast
|
||||
#include <string>
|
||||
|
||||
#include "ruc/file.h"
|
||||
@@ -146,25 +147,6 @@ ADD_FUNCTION(
|
||||
return makePtr<List>(nodes);
|
||||
});
|
||||
|
||||
ADD_FUNCTION(
|
||||
"list?",
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (nodes.size() == 0) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
for (auto node : nodes) {
|
||||
if (!is<List>(node.get())) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return makePtr<Constant>((result) ? Constant::True : Constant::False);
|
||||
});
|
||||
|
||||
ADD_FUNCTION(
|
||||
"empty?",
|
||||
{
|
||||
@@ -369,26 +351,6 @@ ADD_FUNCTION(
|
||||
return makePtr<Atom>(nodes.front());
|
||||
});
|
||||
|
||||
// (atom? myatom 2 "foo")
|
||||
ADD_FUNCTION(
|
||||
"atom?",
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (nodes.size() == 0) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
for (auto node : nodes) {
|
||||
if (!is<Atom>(node.get())) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return makePtr<Constant>((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<Function>(second_argument.get())) {
|
||||
auto function = std::static_pointer_cast<Function>(second_argument)->function();
|
||||
if (is<Function>(callable.get())) {
|
||||
auto function = std::static_pointer_cast<Function>(callable)->function();
|
||||
value = function(nodes);
|
||||
}
|
||||
else {
|
||||
auto lambda = std::static_pointer_cast<Lambda>(second_argument);
|
||||
auto lambda = std::static_pointer_cast<Lambda>(callable);
|
||||
value = eval(lambda->body(), Environment::create(lambda, nodes));
|
||||
}
|
||||
|
||||
@@ -548,6 +510,264 @@ ADD_FUNCTION(
|
||||
return makePtr<List>(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<Function>(callable.get())) {
|
||||
auto function = std::static_pointer_cast<Function>(callable)->function();
|
||||
value = function(nodes);
|
||||
}
|
||||
else {
|
||||
auto lambda = std::static_pointer_cast<Lambda>(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<List>();
|
||||
|
||||
if (is<Function>(callable.get())) {
|
||||
auto function = std::static_pointer_cast<Function>(callable)->function();
|
||||
for (auto node : collection_nodes) {
|
||||
result->add(function({ node }));
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto lambda = std::static_pointer_cast<Lambda>(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<Constant>( \
|
||||
is<Constant>(nodes.front().get()) \
|
||||
&& std::static_pointer_cast<Constant>(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<type>(node.get())) { \
|
||||
result = false; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
return makePtr<Constant>(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<type>(nodes.front().get())) { \
|
||||
return nodes.front(); \
|
||||
} \
|
||||
\
|
||||
VALUE_CAST(stringValue, String, nodes.front()); \
|
||||
\
|
||||
return makePtr<type>(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<Vector>();
|
||||
|
||||
for (auto node : nodes) {
|
||||
result->add(node);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
ADD_FUNCTION(
|
||||
"hash-map",
|
||||
{
|
||||
CHECK_ARG_COUNT_EVEN("hash-map", nodes.size());
|
||||
|
||||
auto result = makePtr<HashMap>();
|
||||
|
||||
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<HashMap>(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<HashMap>(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<Constant>();
|
||||
}
|
||||
|
||||
auto result = hash_map->get(nodes.front());
|
||||
return (result) ? result : makePtr<Constant>();
|
||||
});
|
||||
|
||||
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<Constant>(false);
|
||||
}
|
||||
|
||||
return makePtr<Constant>(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<List>();
|
||||
|
||||
auto elements = hash_map->elements();
|
||||
for (auto pair : elements) {
|
||||
if (pair.first.front() == 0x7f) { // 127
|
||||
result->add(makePtr<Keyword>(pair.first.substr(1)));
|
||||
}
|
||||
else {
|
||||
result->add(makePtr<String>(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<List>();
|
||||
|
||||
auto elements = hash_map->elements();
|
||||
for (auto pair : elements) {
|
||||
result->add(pair.second);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
void installFunctions(EnvironmentPtr env)
|
||||
|
||||
+1
-3
@@ -196,9 +196,7 @@ ValuePtr Reader::readHashMap()
|
||||
}
|
||||
|
||||
auto value = readImpl();
|
||||
|
||||
std::string keyString = is<String>(key.get()) ? std::static_pointer_cast<String>(key)->data() : std::static_pointer_cast<Keyword>(key)->keyword();
|
||||
hash_map->add(keyString, value);
|
||||
hash_map->add(key, value);
|
||||
}
|
||||
|
||||
if (!consumeSpecific(Token { .type = Token::Type::BraceClose })) { // }
|
||||
|
||||
Reference in New Issue
Block a user