22 changed files with 1313 additions and 1114 deletions
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Riyyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include <cstddef> // size_t |
||||
#include <memory> // 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<Constant>(begin->get()) |
||||
&& std::static_pointer_cast<Constant>(*begin)->state() == Constant::Nil) { |
||||
return makePtr<Constant>(); |
||||
} |
||||
|
||||
VALUE_CAST(collection, Collection, (*begin)); |
||||
|
||||
return (collection->empty()) ? makePtr<Constant>() : 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<size_t>(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<Constant>(begin->get()) |
||||
&& std::static_pointer_cast<Constant>(*begin)->state() == Constant::Nil) { |
||||
return makePtr<List>(); |
||||
} |
||||
|
||||
VALUE_CAST(collection, Collection, (*begin)); |
||||
|
||||
return makePtr<List>(collection->rest()); |
||||
}); |
||||
|
||||
// -----------------------------------------
|
||||
|
||||
// (get {:kw "value"} :kw) -> "value"
|
||||
ADD_FUNCTION( |
||||
"get", |
||||
{ |
||||
CHECK_ARG_COUNT_AT_LEAST("get", SIZE(), 1); |
||||
|
||||
if (is<Constant>(begin->get()) |
||||
&& std::static_pointer_cast<Constant>(*begin)->state() == Constant::Nil) { |
||||
return makePtr<Constant>(); |
||||
} |
||||
|
||||
VALUE_CAST(hash_map, HashMap, (*begin)); |
||||
begin++; |
||||
|
||||
if (SIZE() == 0) { |
||||
return makePtr<Constant>(); |
||||
} |
||||
|
||||
auto result = hash_map->get(*begin); |
||||
return (result) ? result : makePtr<Constant>(); |
||||
}); |
||||
|
||||
// (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<Keyword>(pair.first.substr(1)); |
||||
} |
||||
else { |
||||
nodes.at(i) = makePtr<String>(pair.first); |
||||
} |
||||
i++; |
||||
} |
||||
|
||||
return makePtr<List>(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<List>(nodes); |
||||
}); |
||||
|
||||
} // namespace blaze
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Riyyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include <cstddef> // size_t |
||||
#include <memory> // 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<List>(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<size_t>(number->number() < 0 ? 0 : number->number()); |
||||
auto value = *std::next(begin); |
||||
|
||||
auto nodes = ValueVector(count); |
||||
if (is<Atom>(value.get())) { |
||||
auto atom = std::static_pointer_cast<Atom>(value); |
||||
for (size_t i = 0; i < count; ++i) { |
||||
nodes[i] = makePtr<Atom>(atom); |
||||
} |
||||
} |
||||
// else if (is<Collection>(value.get())) {
|
||||
// for (size_t i = 0; i < count; ++i) {
|
||||
// auto nodes = std::static_pointer_cast<Collection>(value)->nodesCopy();
|
||||
// if (is<Vector>(value.get())) {
|
||||
// makePtr<Vector>(nodes);
|
||||
// continue;
|
||||
// }
|
||||
// nodes[i] = makePtr<List>(nodes);
|
||||
// }
|
||||
// }
|
||||
// else if (is<Constant>(value.get())) {
|
||||
// for (size_t i = 0; i < count; ++i) {
|
||||
// auto constant = std::static_pointer_cast<Constant>(value);
|
||||
// nodes[i] = makePtr<Constant>(constant);
|
||||
// }
|
||||
// }
|
||||
|
||||
// TODO:
|
||||
// Atom
|
||||
// Collection
|
||||
// Constant
|
||||
// Function
|
||||
// HashMap
|
||||
// Keyword
|
||||
// Lambda
|
||||
// List
|
||||
// Macro
|
||||
// Number
|
||||
// String
|
||||
// Symbol
|
||||
// Vector
|
||||
|
||||
return makePtr<List>(std::move(nodes)); |
||||
}); |
||||
|
||||
// -----------------------------------------
|
||||
|
||||
// (vec (list 1 2 3))
|
||||
ADD_FUNCTION( |
||||
"vec", |
||||
{ |
||||
CHECK_ARG_COUNT_IS("vec", SIZE(), 1); |
||||
|
||||
if (is<Vector>(begin->get())) { |
||||
return *begin; |
||||
} |
||||
|
||||
VALUE_CAST(collection, Collection, (*begin)); |
||||
|
||||
return makePtr<Vector>(collection->nodesCopy()); |
||||
}); |
||||
|
||||
// (vector 1 2 3) -> [1 2 3]
|
||||
ADD_FUNCTION( |
||||
"vector", |
||||
{ |
||||
auto result = makePtr<Vector>(); |
||||
|
||||
return makePtr<Vector>(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<HashMap>(elements); |
||||
}); |
||||
|
||||
} // namespace blaze
|
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Riyyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include <algorithm> // std::copy, std::reverse_copy |
||||
#include <cstddef> // size_t |
||||
#include <memory> // 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<Function>(callable.get())) { |
||||
auto function = std::static_pointer_cast<Function>(callable)->function(); |
||||
value = function(arguments.begin(), arguments.end()); |
||||
} |
||||
else { |
||||
auto lambda = std::static_pointer_cast<Lambda>(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<List>(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<Collection>(*it)->nodesRead(); |
||||
std::copy(collection_nodes.begin(), collection_nodes.end(), result_nodes.begin() + offset); |
||||
offset += collection_nodes.size(); |
||||
} |
||||
|
||||
return makePtr<List>(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<List>(collection.get())) { |
||||
std::reverse_copy(begin, end, nodes.begin()); |
||||
std::copy(collection_nodes.begin(), collection_nodes.end(), nodes.begin() + argument_count); |
||||
|
||||
return makePtr<List>(nodes); |
||||
} |
||||
|
||||
std::copy(collection_nodes.begin(), collection_nodes.end(), nodes.begin()); |
||||
std::copy(begin, end, nodes.begin() + collection_count); |
||||
|
||||
return makePtr<Vector>(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<Function>(callable.get())) { |
||||
auto function = std::static_pointer_cast<Function>(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<Lambda>(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<List>(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<size_t>(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<Constant>()); |
||||
} |
||||
collection_nodes[index] = value; |
||||
|
||||
if (is<Vector>(begin->get())) { |
||||
return makePtr<Vector>(collection_nodes); |
||||
} |
||||
|
||||
return makePtr<List>(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<Constant>(front_raw_ptr) && std::static_pointer_cast<Constant>(front)->state() == Constant::Nil) { |
||||
return makePtr<Constant>(); |
||||
} |
||||
if (is<Collection>(front_raw_ptr)) { |
||||
auto collection = std::static_pointer_cast<Collection>(front); |
||||
|
||||
if (collection->empty()) { |
||||
return makePtr<Constant>(); |
||||
} |
||||
|
||||
if (is<List>(front_raw_ptr)) { |
||||
return front; |
||||
} |
||||
|
||||
return makePtr<List>(collection->nodesCopy()); |
||||
} |
||||
if (is<String>(front_raw_ptr)) { |
||||
auto string = std::static_pointer_cast<String>(front); |
||||
|
||||
if (string->empty()) { |
||||
return makePtr<Constant>(); |
||||
} |
||||
|
||||
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<String>(data[i]); |
||||
} |
||||
|
||||
return makePtr<List>(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<HashMap>(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<HashMap>(elements); |
||||
}); |
||||
|
||||
} // namespace blaze
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Riyyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include <cstdint> // int64_t |
||||
#include <functional> // std::function |
||||
#include <memory> // 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<Constant>((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<bool(ValuePtr, ValuePtr)> equal = |
||||
[&equal](ValuePtr lhs, ValuePtr rhs) -> bool { |
||||
if ((is<List>(lhs.get()) || is<Vector>(lhs.get())) |
||||
&& (is<List>(rhs.get()) || is<Vector>(rhs.get()))) { |
||||
auto lhs_collection = std::static_pointer_cast<Collection>(lhs); |
||||
auto rhs_collection = std::static_pointer_cast<Collection>(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<HashMap>(lhs.get()) && is<HashMap>(rhs.get())) { |
||||
const auto& lhs_nodes = std::static_pointer_cast<HashMap>(lhs)->elements(); |
||||
const auto& rhs_nodes = std::static_pointer_cast<HashMap>(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<String>(lhs.get()) && is<String>(rhs.get()) |
||||
&& std::static_pointer_cast<String>(lhs)->data() == std::static_pointer_cast<String>(rhs)->data()) { |
||||
return true; |
||||
} |
||||
if (is<Keyword>(lhs.get()) && is<Keyword>(rhs.get()) |
||||
&& std::static_pointer_cast<Keyword>(lhs)->keyword() == std::static_pointer_cast<Keyword>(rhs)->keyword()) { |
||||
return true; |
||||
} |
||||
if (is<Number>(lhs.get()) && is<Number>(rhs.get()) |
||||
&& std::static_pointer_cast<Number>(lhs)->number() == std::static_pointer_cast<Number>(rhs)->number()) { |
||||
return true; |
||||
} |
||||
if (is<Constant>(lhs.get()) && is<Constant>(rhs.get()) |
||||
&& std::static_pointer_cast<Constant>(lhs)->state() == std::static_pointer_cast<Constant>(rhs)->state()) { |
||||
return true; |
||||
} |
||||
if (is<Symbol>(lhs.get()) && is<Symbol>(rhs.get()) |
||||
&& std::static_pointer_cast<Symbol>(lhs)->symbol() == std::static_pointer_cast<Symbol>(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<Constant>((result) ? Constant::True : Constant::False); |
||||
}); |
||||
|
||||
} // namespace blaze
|
@ -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<Symbol>(begin->get())) { |
||||
return *begin; |
||||
} |
||||
|
||||
VALUE_CAST(stringValue, String, (*begin)); |
||||
|
||||
return makePtr<Symbol>(stringValue->data()); |
||||
}); |
||||
|
||||
// (keyword "foo") -> :foo
|
||||
// (keyword 123) -> :123
|
||||
ADD_FUNCTION( |
||||
"keyword", |
||||
{ |
||||
CHECK_ARG_COUNT_IS("symbol", SIZE(), 1); |
||||
|
||||
if (is<Keyword>(begin->get())) { |
||||
return *begin; |
||||
} |
||||
else if (is<Number>(begin->get())) { |
||||
VALUE_CAST(numberValue, Number, (*begin)); |
||||
|
||||
return makePtr<Keyword>(numberValue->number()); |
||||
} |
||||
|
||||
VALUE_CAST(stringValue, String, (*begin)); |
||||
|
||||
return makePtr<Keyword>(stringValue->data()); |
||||
}); |
||||
|
||||
} // namespace blaze
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Riyyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include <iterator> // std::next |
||||
#include <string> |
||||
|
||||
#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<String>(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<Constant>(); \
|
||||
} |
||||
|
||||
ADD_FUNCTION("prn", PRINTER_PRINT(true)); |
||||
ADD_FUNCTION("println", PRINTER_PRINT(false)); |
||||
|
||||
} // namespace blaze
|
@ -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<Collection>(front_raw_ptr) && // List / Vector
|
||||
!is<HashMap>(front_raw_ptr) && // HashMap
|
||||
!is<Callable>(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<Collection>(front_raw_ptr) && // List / Vector
|
||||
!is<HashMap>(front_raw_ptr) && // HashMap
|
||||
!is<Callable>(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
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Riyyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include <algorithm> // std::copy |
||||
#include <memory> // 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<Atom>(*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<Function>(callable.get())) { |
||||
auto function = std::static_pointer_cast<Function>(callable)->function(); |
||||
value = function(arguments.begin(), arguments.end()); |
||||
} |
||||
else { |
||||
auto lambda = std::static_pointer_cast<Lambda>(callable); |
||||
value = eval(lambda->body(), Environment::create(lambda, std::move(arguments))); |
||||
} |
||||
|
||||
return atom->reset(value); |
||||
}); |
||||
|
||||
} // namespace blaze
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Riyyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include <cstdint> // 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<Number>(result); |
||||
}); |
||||
|
||||
ADD_FUNCTION( |
||||
"-", |
||||
{ |
||||
if (SIZE() == 0) { |
||||
return makePtr<Number>(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<Number>(result); |
||||
}); |
||||
|
||||
ADD_FUNCTION( |
||||
"*", |
||||
{ |
||||
int64_t result = 1; |
||||
|
||||
for (auto it = begin; it != end; ++it) { |
||||
VALUE_CAST(number, Number, (*it)); |
||||
result *= number->number(); |
||||
} |
||||
|
||||
return makePtr<Number>(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<Number>((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<Number>(divide->number() % by->number()); |
||||
}); |
||||
|
||||
} // namespace blaze
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Riyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include <chrono> // std::chrono::sytem_clock |
||||
#include <cstddef> // size_t |
||||
#include <cstdint> // int64_t |
||||
#include <memory> // 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<Constant>(begin->get()) && std::static_pointer_cast<Constant>(*begin)->state() == Constant::Nil) { |
||||
// result = 0
|
||||
} |
||||
else if (is<Collection>(begin->get())) { |
||||
result = std::static_pointer_cast<Collection>(*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<Number>((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::milliseconds>( |
||||
std::chrono::system_clock::now().time_since_epoch()) |
||||
.count(); |
||||
|
||||
return makePtr<Number>(elapsed); |
||||
}); |
||||
|
||||
} // namespace blaze
|
@ -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<Constant>( \
|
||||
is<Constant>(begin->get()) \
|
||||
&& std::static_pointer_cast<Constant>(*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<type>(it->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("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<Callable>(it->get()) || is<Macro>(it->get())) { |
||||
result = false; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
return makePtr<Constant>(result); |
||||
}); |
||||
|
||||
ADD_FUNCTION( |
||||
"macro?", |
||||
{ |
||||
bool result = true; |
||||
|
||||
if (SIZE() == 0) { |
||||
result = false; |
||||
} |
||||
|
||||
for (auto it = begin; it != end; ++it) { |
||||
if (!is<Macro>(it->get())) { |
||||
result = false; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
return makePtr<Constant>(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<Constant>(false); |
||||
} |
||||
|
||||
return makePtr<Constant>(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<Constant>((result) ? Constant::True : Constant::False); |
||||
}); |
||||
|
||||
} // namespace blaze
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Riyyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include <string> |
||||
|
||||
#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<String>(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
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Riyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include <unordered_map> |
||||
|
||||
#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) |
Loading…
Reference in new issue