Compare commits
21 Commits
3aa99d0045
...
11f0553b5a
Author | SHA1 | Date |
---|---|---|
Riyyi | 11f0553b5a | 10 months ago |
Riyyi | 705e80ad6b | 10 months ago |
Riyyi | 4cc2acc8a0 | 10 months ago |
Riyyi | f5dc1168eb | 10 months ago |
Riyyi | 536e55e75a | 10 months ago |
Riyyi | 25871cd5d5 | 10 months ago |
Riyyi | 929fb5d645 | 10 months ago |
Riyyi | 9db041946e | 10 months ago |
Riyyi | b74f3448b2 | 10 months ago |
Riyyi | b727f7147e | 10 months ago |
Riyyi | c6c6d69e73 | 11 months ago |
Riyyi | bb6f3e7496 | 11 months ago |
Riyyi | 1915621427 | 11 months ago |
Riyyi | 9895195410 | 1 year ago |
Riyyi | e8206d762c | 1 year ago |
Riyyi | b65482eb68 | 1 year ago |
Riyyi | 67b982fd4c | 1 year ago |
Riyyi | d3a50abfbc | 1 year ago |
Riyyi | 80b25f8c21 | 1 year ago |
Riyyi | 0d43512ea9 | 1 year ago |
Riyyi | fa4bd63dca | 1 year ago |
54 changed files with 2941 additions and 2948 deletions
@ -0,0 +1 @@
|
||||
file(COPY ${CMAKE_CURRENT_LIST_DIR}/../lisp DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) |
@ -0,0 +1,12 @@
|
||||
|
||||
(defmacro! cond (fn* [& xs] |
||||
(if (> (count xs) 0) |
||||
(list 'if (first xs) |
||||
(if (> (count xs) 1) |
||||
(nth xs 1) |
||||
(throw "odd number of forms to cond")) |
||||
(cons 'cond (rest (rest xs))))))) |
||||
|
||||
;; Local Variables: |
||||
;; eval: (emacs-lisp-mode) |
||||
;; End: |
@ -0,0 +1,14 @@
|
||||
|
||||
(defmacro! defmacro |
||||
(fn* [name args & body] |
||||
`(defmacro! ~name (fn* ~args ~@body)))) |
||||
|
||||
(defmacro defn [name args & body] |
||||
`(def! ~name (fn* ~args ~@body))) |
||||
|
||||
(defmacro def [name & body] |
||||
`(def! ~name ~@body)) |
||||
|
||||
;; Local Variables: |
||||
;; eval: (emacs-lisp-mode) |
||||
;; End: |
@ -0,0 +1,12 @@
|
||||
|
||||
(defn load-file [file] |
||||
"Load the Lisp file named FILE." |
||||
(eval (read-string (str "(do " (slurp file) "\nnil)")))) |
||||
|
||||
(defn load [file] |
||||
"Load the Lisp file named FILE." |
||||
(eval (read-string (str "(let* [] (do " (slurp file) "))")))) |
||||
|
||||
;; Local Variables: |
||||
;; eval: (emacs-lisp-mode) |
||||
;; End: |
@ -0,0 +1,6 @@
|
||||
|
||||
(def *host-language* "C++") |
||||
|
||||
;; Local Variables: |
||||
;; eval: (emacs-lisp-mode) |
||||
;; End: |
@ -0,0 +1,8 @@
|
||||
|
||||
(defn not [object] |
||||
"Return true if OBJECT is nil or false, and return false otherwise." |
||||
(if object false true)) |
||||
|
||||
;; Local Variables: |
||||
;; eval: (emacs-lisp-mode) |
||||
;; End: |
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Riyyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include <filesystem> |
||||
#include <iterator> // std::distance |
||||
#include <memory> // std::static_pointer_cast |
||||
|
||||
#include "ruc/file.h" |
||||
#include "ruc/format/format.h" |
||||
|
||||
#include "ast.h" |
||||
#include "env/environment.h" |
||||
#include "error.h" |
||||
#include "forward.h" |
||||
#include "repl.h" |
||||
|
||||
namespace blaze { |
||||
|
||||
std::vector<FunctionParts> Environment::s_function_parts; |
||||
std::vector<std::string> Environment::s_lambdas; |
||||
|
||||
EnvironmentPtr Environment::create() |
||||
{ |
||||
return std::shared_ptr<Environment>(new Environment); |
||||
} |
||||
|
||||
EnvironmentPtr Environment::create(EnvironmentPtr outer) |
||||
{ |
||||
auto env = create(); |
||||
|
||||
env->m_outer = outer; |
||||
|
||||
return env; |
||||
} |
||||
|
||||
EnvironmentPtr Environment::create(const ValuePtr lambda, ValueVector&& arguments) |
||||
{ |
||||
auto lambda_casted = std::static_pointer_cast<Lambda>(lambda); |
||||
auto env = create(lambda_casted->env()); |
||||
auto bindings = lambda_casted->bindings(); |
||||
|
||||
auto it = arguments.begin(); |
||||
for (size_t i = 0; i < bindings.size(); ++i, ++it) { |
||||
if (bindings[i] == "&") { |
||||
if (i + 2 != bindings.size()) { |
||||
Error::the().add(::format("invalid function: {}", lambda)); |
||||
return nullptr; |
||||
} |
||||
|
||||
auto nodes = ValueVector(); |
||||
for (; it != arguments.end(); ++it) { |
||||
nodes.push_back(*it); |
||||
} |
||||
env->set(bindings[i + 1], makePtr<List>(nodes)); |
||||
|
||||
return env; |
||||
} |
||||
|
||||
if (it == arguments.end()) { |
||||
Error::the().add(::format("wrong number of arguments: {}, {}", lambda, arguments.size())); |
||||
return nullptr; |
||||
} |
||||
|
||||
env->set(bindings[i], *it); |
||||
} |
||||
|
||||
if (it != arguments.end()) { |
||||
Error::the().add(::format("wrong number of arguments: {}, {}", lambda, arguments.size())); |
||||
return nullptr; |
||||
} |
||||
|
||||
return env; |
||||
} |
||||
|
||||
// -----------------------------------------
|
||||
|
||||
void Environment::loadFunctions() |
||||
{ |
||||
loadCollectionAccess(); |
||||
loadCollectionConstructor(); |
||||
loadCollectionModify(); |
||||
loadCompare(); |
||||
loadConvert(); |
||||
loadFormat(); |
||||
loadMeta(); |
||||
loadMutable(); |
||||
loadOperators(); |
||||
loadOther(); |
||||
loadPredicate(); |
||||
loadRepl(); |
||||
|
||||
// Load std files
|
||||
|
||||
std::filesystem::path std = "./lisp"; |
||||
if (!std::filesystem::exists(std) || !std::filesystem::is_directory(std)) { |
||||
return; |
||||
} |
||||
|
||||
s_lambdas.reserve(std::distance(std::filesystem::directory_iterator(std), {})); |
||||
for (const auto& entry : std::filesystem::directory_iterator(std)) { |
||||
if (!std::filesystem::is_regular_file(entry.path()) |
||||
|| entry.path().extension().string() != ".bl") { |
||||
continue; |
||||
} |
||||
|
||||
std::filesystem::path filename = entry.path().filename(); |
||||
ruc::File file((std / filename).string()); |
||||
|
||||
// The init will be added to the front and executed first
|
||||
if (filename.string() == "init.bl") { |
||||
s_lambdas.emplace(s_lambdas.begin(), file.data()); |
||||
} |
||||
else { |
||||
s_lambdas.push_back(file.data()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void Environment::registerFunction(FunctionParts function_parts) |
||||
{ |
||||
s_function_parts.push_back(function_parts); |
||||
} |
||||
|
||||
void Environment::installFunctions(EnvironmentPtr env) |
||||
{ |
||||
for (const auto& function_parts : s_function_parts) { |
||||
env->set(std::string(function_parts.name), |
||||
makePtr<Function>( |
||||
function_parts.name, |
||||
function_parts.signature, |
||||
function_parts.documentation, |
||||
function_parts.function)); |
||||
} |
||||
for (const auto& lambda : s_lambdas) { |
||||
// Ensure all s-exprs are run with (do)
|
||||
Repl::eval(Repl::read("(do " + lambda + ")"), env); |
||||
} |
||||
} |
||||
|
||||
// -----------------------------------------
|
||||
|
||||
bool Environment::exists(std::string_view symbol) |
||||
{ |
||||
return m_values.find(std::string(symbol)) != m_values.end(); |
||||
} |
||||
|
||||
ValuePtr Environment::set(std::string_view symbol, ValuePtr value) |
||||
{ |
||||
if (exists(symbol)) { |
||||
m_values.erase(std::string(symbol)); |
||||
} |
||||
|
||||
m_values.emplace(symbol, value); |
||||
|
||||
return value; |
||||
} |
||||
|
||||
ValuePtr Environment::get(std::string_view symbol) |
||||
{ |
||||
if (exists(symbol)) { |
||||
return m_values[std::string(symbol)]; |
||||
} |
||||
|
||||
if (m_outer) { |
||||
return m_outer->get(symbol); |
||||
} |
||||
|
||||
return nullptr; |
||||
} |
||||
|
||||
} // namespace blaze
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Riyyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <list> |
||||
#include <string> |
||||
#include <unordered_map> |
||||
#include <vector> |
||||
|
||||
#include "ast.h" |
||||
#include "forward.h" |
||||
|
||||
namespace blaze { |
||||
|
||||
// All of these combined become a Function in the Environment
|
||||
struct FunctionParts { |
||||
std::string_view name; |
||||
std::string_view signature; |
||||
std::string_view documentation; |
||||
FunctionType function; |
||||
}; |
||||
|
||||
class Environment { |
||||
public: |
||||
virtual ~Environment() = default; |
||||
|
||||
// Factory functions instead of constructors because it can fail in the bindings/arguments case
|
||||
static EnvironmentPtr create(); |
||||
static EnvironmentPtr create(EnvironmentPtr outer); |
||||
static EnvironmentPtr create(const ValuePtr lambda, ValueVector&& arguments); |
||||
|
||||
static void loadFunctions(); |
||||
static void registerFunction(FunctionParts function_parts); |
||||
static void installFunctions(EnvironmentPtr env); |
||||
|
||||
bool exists(std::string_view symbol); |
||||
ValuePtr set(std::string_view symbol, ValuePtr value); |
||||
ValuePtr get(std::string_view symbol); |
||||
|
||||
private: |
||||
Environment() {} |
||||
|
||||
// Outer environment native functions, "Core"
|
||||
static void loadCollectionAccess(); |
||||
static void loadCollectionConstructor(); |
||||
static void loadCollectionModify(); |
||||
static void loadCompare(); |
||||
static void loadConvert(); |
||||
static void loadFormat(); |
||||
static void loadMeta(); |
||||
static void loadMutable(); |
||||
static void loadOperators(); |
||||
static void loadOther(); |
||||
static void loadPredicate(); |
||||
static void loadRepl(); |
||||
|
||||
EnvironmentPtr m_outer { nullptr }; |
||||
std::unordered_map<std::string, ValuePtr> m_values; |
||||
|
||||
static std::vector<FunctionParts> s_function_parts; |
||||
static std::vector<std::string> s_lambdas; |
||||
}; |
||||
|
||||
} // namespace blaze
|
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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 { |
||||
|
||||
void Environment::loadCollectionAccess() |
||||
{ |
||||
// (count '(1 2 3)) -> 3
|
||||
// (count [1 2 3]) -> 3
|
||||
// (count {:foo 2 :bar 3}) -> 2
|
||||
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 if (is<HashMap>(begin->get())) { |
||||
result = std::static_pointer_cast<HashMap>(*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); |
||||
}); |
||||
|
||||
// -----------------------------------------
|
||||
|
||||
// (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,132 @@
|
||||
/*
|
||||
* 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 { |
||||
|
||||
void Environment::loadCollectionConstructor() |
||||
{ |
||||
// (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,287 @@
|
||||
/*
|
||||
* 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 "repl.h" |
||||
#include "util.h" |
||||
|
||||
namespace blaze { |
||||
|
||||
void Environment::loadCollectionModify() |
||||
{ |
||||
// (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 = Repl::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) = (Repl::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,166 @@
|
||||
/*
|
||||
* 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 { |
||||
|
||||
void Environment::loadCompare() |
||||
{ |
||||
#define NUMBER_COMPARE(operator) \ |
||||
{ \
|
||||
CHECK_ARG_COUNT_AT_LEAST(#operator, SIZE(), 2); \
|
||||
\
|
||||
bool result = true; \
|
||||
\
|
||||
int64_t number = 0; \
|
||||
double decimal = 0; \
|
||||
bool current_numeric_is_number = false; \
|
||||
\
|
||||
/* Start with the first number */ \
|
||||
IS_VALUE(Numeric, (*begin)); \
|
||||
if (is<Number>(begin->get())) { \
|
||||
number = std::static_pointer_cast<Number>(*begin)->number(); \
|
||||
current_numeric_is_number = true; \
|
||||
} \
|
||||
else { \
|
||||
decimal = std::static_pointer_cast<Decimal>(*begin)->decimal(); \
|
||||