Compare commits
No commits in common. '11f0553b5ac0864322dcea9fd9a46cd8143309de' and '3aa99d004579d9fd09aedfa7398066df3ce04bae' have entirely different histories.
11f0553b5a
...
3aa99d0045
54 changed files with 2948 additions and 2941 deletions
@ -1 +0,0 @@ |
|||||||
file(COPY ${CMAKE_CURRENT_LIST_DIR}/../lisp DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) |
|
@ -1,12 +0,0 @@ |
|||||||
|
|
||||||
(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: |
|
@ -1,14 +0,0 @@ |
|||||||
|
|
||||||
(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: |
|
@ -1,12 +0,0 @@ |
|||||||
|
|
||||||
(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: |
|
@ -1,6 +0,0 @@ |
|||||||
|
|
||||||
(def *host-language* "C++") |
|
||||||
|
|
||||||
;; Local Variables: |
|
||||||
;; eval: (emacs-lisp-mode) |
|
||||||
;; End: |
|
@ -1,8 +0,0 @@ |
|||||||
|
|
||||||
(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: |
|
@ -1,174 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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
|
|
@ -1,68 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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
|
|
@ -1,185 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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
|
|
@ -1,132 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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
|
|
@ -1,287 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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
|
|
@ -1,166 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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(); \
|
|
||||||
current_numeric_is_number = false; \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
/* Skip the first node */ \
|
|
||||||
for (auto it = begin + 1; it != end; ++it) { \
|
|
||||||
IS_VALUE(Numeric, (*it)); \
|
|
||||||
if (is<Number>(*it->get())) { \
|
|
||||||
int64_t it_number = std::static_pointer_cast<Number>(*it)->number(); \
|
|
||||||
if (!((current_numeric_is_number ? number : decimal) operator it_number)) { \
|
|
||||||
result = false; \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
number = it_number; \
|
|
||||||
current_numeric_is_number = true; \
|
|
||||||
} \
|
|
||||||
else { \
|
|
||||||
double it_decimal = std::static_pointer_cast<Decimal>(*it)->decimal(); \
|
|
||||||
if (!((current_numeric_is_number ? number : decimal) operator it_decimal)) { \
|
|
||||||
result = false; \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
decimal = it_decimal; \
|
|
||||||
current_numeric_is_number = false; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
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<Collection>(lhs.get()) && is<Collection>(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; |
|
||||||
} |
|
||||||
// clang-format off
|
|
||||||
if (is<Numeric>(lhs.get()) && is<Numeric>(rhs.get()) |
|
||||||
&& (is<Number>(lhs.get()) |
|
||||||
? std::static_pointer_cast<Number>(lhs)->number() |
|
||||||
: std::static_pointer_cast<Decimal>(lhs)->decimal()) |
|
||||||
== (is<Number>(rhs.get()) |
|
||||||
? std::static_pointer_cast<Number>(rhs)->number() |
|
||||||
: std::static_pointer_cast<Decimal>(rhs)->decimal())) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
// clang-format on
|
|
||||||
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
|
|
@ -1,149 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright (C) 2023 Riyyi |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: MIT |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <charconv> // std::from_chars, std::to_chars |
|
||||||
#include <memory> |
|
||||||
#include <system_error> // std::errc |
|
||||||
|
|
||||||
#include "ast.h" |
|
||||||
#include "env/macro.h" |
|
||||||
#include "util.h" |
|
||||||
|
|
||||||
namespace blaze { |
|
||||||
|
|
||||||
void Environment::loadConvert() |
|
||||||
{ |
|
||||||
// (number-to-string 123) -> "123"
|
|
||||||
ADD_FUNCTION( |
|
||||||
"number-to-string", |
|
||||||
"", |
|
||||||
"", |
|
||||||
{ |
|
||||||
CHECK_ARG_COUNT_IS("number-to-string", SIZE(), 1); |
|
||||||
|
|
||||||
IS_VALUE(Numeric, (*begin)); |
|
||||||
|
|
||||||
char result[32]; |
|
||||||
auto conversion = std::to_chars(result, |
|
||||||
result + sizeof(result), |
|
||||||
is<Number>(begin->get()) |
|
||||||
? std::static_pointer_cast<Number>(*begin)->number() |
|
||||||
: std::static_pointer_cast<Decimal>(*begin)->decimal()); |
|
||||||
if (conversion.ec != std::errc()) { |
|
||||||
return makePtr<Constant>(Constant::Nil); |
|
||||||
} |
|
||||||
|
|
||||||
return makePtr<String>(std::string(result, conversion.ptr - result)); |
|
||||||
}); |
|
||||||
|
|
||||||
// (string-to-char "123") -> 49
|
|
||||||
ADD_FUNCTION( |
|
||||||
"string-to-char", |
|
||||||
"", |
|
||||||
"", |
|
||||||
{ |
|
||||||
CHECK_ARG_COUNT_IS("string-to-char", SIZE(), 1); |
|
||||||
|
|
||||||
VALUE_CAST(string_value, String, (*begin)); |
|
||||||
std::string data = string_value->data(); |
|
||||||
|
|
||||||
return makePtr<Number>(data.c_str()[0]); |
|
||||||
}); |
|
||||||
|
|
||||||
// (string-to-number "123") -> 123
|
|
||||||
ADD_FUNCTION( |
|
||||||
"string-to-number", |
|
||||||
"", |
|
||||||
"", |
|
||||||
{ |
|
||||||
CHECK_ARG_COUNT_IS("string-to-number", SIZE(), 1); |
|
||||||
|
|
||||||
VALUE_CAST(string_value, String, (*begin)); |
|
||||||
std::string data = string_value->data(); |
|
||||||
|
|
||||||
if (data.find('.') == std::string::npos) { |
|
||||||
int64_t number; |
|
||||||
auto conversion_number = std::from_chars(data.c_str(), data.c_str() + data.size(), number); |
|
||||||
if (conversion_number.ec != std::errc()) { |
|
||||||
return makePtr<Constant>(Constant::Nil); |
|
||||||
} |
|
||||||
|
|
||||||
return makePtr<Number>(number); |
|
||||||
} |
|
||||||
|
|
||||||
double decimal; |
|
||||||
auto conversion_decimal = std::from_chars(data.c_str(), data.c_str() + data.size(), decimal); |
|
||||||
if (conversion_decimal.ec != std::errc()) { |
|
||||||
return makePtr<Constant>(Constant::Nil); |
|
||||||
} |
|
||||||
|
|
||||||
return makePtr<Decimal>(decimal); |
|
||||||
}); |
|
||||||
|
|
||||||
#define STRING_TO_COLLECTION(name, type) \ |
|
||||||
{ \
|
|
||||||
CHECK_ARG_COUNT_IS(name, SIZE(), 1); \
|
|
||||||
\
|
|
||||||
VALUE_CAST(string_value, String, (*begin)); \
|
|
||||||
std::string data = string_value->data(); \
|
|
||||||
\
|
|
||||||
ValueVector nodes(data.size()); \
|
|
||||||
for (size_t i = 0; i < data.size(); ++i) { \
|
|
||||||
nodes.at(i) = makePtr<Number>(data.c_str()[i]); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
return makePtr<type>(nodes); \
|
|
||||||
} |
|
||||||
|
|
||||||
// (string-to-list "foo") -> (102 111 111)
|
|
||||||
// (string-to-vector "foo") -> [102 111 111]
|
|
||||||
ADD_FUNCTION("string-to-list", "", "", STRING_TO_COLLECTION("string-to-list", List)); |
|
||||||
ADD_FUNCTION("string-to-vector", "", "", STRING_TO_COLLECTION("string-to-vector", Vector)); |
|
||||||
|
|
||||||
// -------------------------------------
|
|
||||||
|
|
||||||
// (symbol "foo") -> foo
|
|
||||||
ADD_FUNCTION( |
|
||||||
"symbol", |
|
||||||
"", |
|
||||||
"", |
|
||||||
{ |
|
||||||
CHECK_ARG_COUNT_IS("symbol", SIZE(), 1); |
|
||||||
|
|
||||||
if (is<Symbol>(begin->get())) { |
|
||||||
return *begin; |
|
||||||
} |
|
||||||
|
|
||||||
VALUE_CAST(string_value, String, (*begin)); |
|
||||||
|
|
||||||
return makePtr<Symbol>(string_value->data()); |
|
||||||
}); |
|
||||||
|
|
||||||
// (keyword "foo") -> :foo
|
|
||||||
// (keyword 123) -> :123
|
|
||||||
ADD_FUNCTION( |
|
||||||
"keyword", |
|
||||||
"", |
|
||||||
"", |
|
||||||
{ |
|
||||||
CHECK_ARG_COUNT_IS("keyword", SIZE(), 1); |
|
||||||
|
|
||||||
if (is<Keyword>(begin->get())) { |
|
||||||
return *begin; |
|
||||||
} |
|
||||||
else if (is<Number>(begin->get())) { |
|
||||||
VALUE_CAST(number_value, Number, (*begin)); |
|
||||||
|
|
||||||
return makePtr<Keyword>(number_value->number()); |
|
||||||
} |
|
||||||
|
|
||||||
VALUE_CAST(string_value, String, (*begin)); |
|
||||||
|
|
||||||
return makePtr<Keyword>(string_value->data()); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace blaze
|
|
@ -1,75 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright (C) 2023 Riyyi |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: MIT |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <iterator> // std::next |
|
||||||
#include <string> |
|
||||||
|
|
||||||
#include "reader.h" |
|
||||||
#include "ruc/format/print.h" |
|
||||||
|
|
||||||
#include "ast.h" |
|
||||||
#include "env/macro.h" |
|
||||||
#include "printer.h" |
|
||||||
#include "util.h" |
|
||||||
|
|
||||||
namespace blaze { |
|
||||||
|
|
||||||
void Environment::loadFormat() |
|
||||||
{ |
|
||||||
#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)); |
|
||||||
|
|
||||||
// -------------------------------------
|
|
||||||
|
|
||||||
ADD_FUNCTION( |
|
||||||
"dump", |
|
||||||
"arg", |
|
||||||
"Print AST of the value ARG.", |
|
||||||
{ |
|
||||||
CHECK_ARG_COUNT_IS("dump", SIZE(), 1); |
|
||||||
|
|
||||||
Reader reader; |
|
||||||
reader.dump(*begin); |
|
||||||
|
|
||||||
return nullptr; |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace blaze
|
|
@ -1,60 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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 { |
|
||||||
|
|
||||||
void Environment::loadMeta() |
|
||||||
{ |
|
||||||
// (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
|
|
@ -1,93 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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 "repl.h" |
|
||||||
#include "util.h" |
|
||||||
|
|
||||||
namespace blaze { |
|
||||||
|
|
||||||
void Environment::loadMutable() |
|
||||||
{ |
|
||||||
// (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 = Repl::eval(lambda->body(), Environment::create(lambda, std::move(arguments))); |
|
||||||
} |
|
||||||
|
|
||||||
return atom->reset(value); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace blaze
|
|
@ -1,166 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright (C) 2023 Riyyi |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: MIT |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <cstdint> // int64_t |
|
||||||
#include <memory> // std::static_pointer_cast |
|
||||||
|
|
||||||
#include "ast.h" |
|
||||||
#include "env/macro.h" |
|
||||||
#include "util.h" |
|
||||||
|
|
||||||
namespace blaze { |
|
||||||
|
|
||||||
void Environment::loadOperators() |
|
||||||
{ |
|
||||||
#define APPLY_NUMBER_OR_DECIMAL(it, apply) \ |
|
||||||
IS_VALUE(Numeric, (*it)); \
|
|
||||||
if (is<Number>(it->get())) { \
|
|
||||||
auto it_numeric = std::static_pointer_cast<Number>(*it)->number(); \
|
|
||||||
do { \
|
|
||||||
apply \
|
|
||||||
} while (0); \
|
|
||||||
} \
|
|
||||||
else { \
|
|
||||||
return_decimal = true; \
|
|
||||||
auto it_numeric = std::static_pointer_cast<Decimal>(*it)->decimal(); \
|
|
||||||
do { \
|
|
||||||
apply \
|
|
||||||
} while (0); \
|
|
||||||
} |
|
||||||
|
|
||||||
#define RETURN_NUMBER_OR_DECIMAL() \ |
|
||||||
if (!return_decimal) { \
|
|
||||||
return makePtr<Number>(number); \
|
|
||||||
} \
|
|
||||||
return makePtr<Decimal>(decimal); |
|
||||||
|
|
||||||
ADD_FUNCTION( |
|
||||||
"+", |
|
||||||
"number...", |
|
||||||
"Return the sum of any amount of arguments, where NUMBER is of type number.", |
|
||||||
{ |
|
||||||
bool return_decimal = false; |
|
||||||
|
|
||||||
int64_t number = 0; |
|
||||||
double decimal = 0; |
|
||||||
|
|
||||||
for (auto it = begin; it != end; ++it) { |
|
||||||
APPLY_NUMBER_OR_DECIMAL(it, { |
|
||||||
number += it_numeric; |
|
||||||
decimal += it_numeric; |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
RETURN_NUMBER_OR_DECIMAL(); |
|
||||||
}); |
|
||||||
|
|
||||||
ADD_FUNCTION( |
|
||||||
"-", |
|
||||||
"[number] subtract...", |
|
||||||
R"(Negate NUMBER or SUBTRACT numbers and return the result. |
|
||||||
|
|
||||||
With one arg, negates it. With more than one arg, |
|
||||||
subtracts all but the first from the first.)", |
|
||||||
{ |
|
||||||
size_t length = SIZE(); |
|
||||||
if (length == 0) { |
|
||||||
return makePtr<Number>(0); |
|
||||||
} |
|
||||||
|
|
||||||
bool return_decimal = false; |
|
||||||
|
|
||||||
int64_t number = 0; |
|
||||||
double decimal = 0; |
|
||||||
|
|
||||||
// Start with the first number
|
|
||||||
APPLY_NUMBER_OR_DECIMAL(begin, { |
|
||||||
number = it_numeric; |
|
||||||
decimal = it_numeric; |
|
||||||
}); |
|
||||||
|
|
||||||
// Return negative on single argument
|
|
||||||
if (length == 1) { |
|
||||||
number = -number; |
|
||||||
decimal = -decimal; |
|
||||||
RETURN_NUMBER_OR_DECIMAL(); |
|
||||||
} |
|
||||||
|
|
||||||
// Skip the first node
|
|
||||||
for (auto it = begin + 1; it != end; ++it) { |
|
||||||
APPLY_NUMBER_OR_DECIMAL(it, { |
|
||||||
number -= it_numeric; |
|
||||||
decimal -= it_numeric; |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
RETURN_NUMBER_OR_DECIMAL(); |
|
||||||
}); |
|
||||||
|
|
||||||
ADD_FUNCTION( |
|
||||||
"*", |
|
||||||
"", |
|
||||||
"", |
|
||||||
{ |
|
||||||
bool return_decimal = false; |
|
||||||
|
|
||||||
int64_t number = 1; |
|
||||||
double decimal = 1; |
|
||||||
|
|
||||||
for (auto it = begin; it != end; ++it) { |
|
||||||
APPLY_NUMBER_OR_DECIMAL(it, { |
|
||||||
number *= it_numeric; |
|
||||||
decimal *= it_numeric; |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
RETURN_NUMBER_OR_DECIMAL(); |
|
||||||
}); |
|
||||||
|
|
||||||
ADD_FUNCTION( |
|
||||||
"/", |
|
||||||
"", |
|
||||||
"", |
|
||||||
{ |
|
||||||
CHECK_ARG_COUNT_AT_LEAST("/", SIZE(), 1); |
|
||||||
|
|
||||||
bool return_decimal = false; |
|
||||||
|
|
||||||
int64_t number = 0; |
|
||||||
double decimal = 0; |
|
||||||
|
|
||||||
// Start with the first number
|
|
||||||
APPLY_NUMBER_OR_DECIMAL(begin, { |
|
||||||
number = it_numeric; |
|
||||||
decimal = it_numeric; |
|
||||||
}); |
|
||||||
|
|
||||||
// Skip the first node
|
|
||||||
for (auto it = begin + 1; it != end; ++it) { |
|
||||||
APPLY_NUMBER_OR_DECIMAL(it, { |
|
||||||
number /= it_numeric; |
|
||||||
decimal /= it_numeric; |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
RETURN_NUMBER_OR_DECIMAL(); |
|
||||||
}); |
|
||||||
|
|
||||||
// (% 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
|
|
@ -1,51 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright (C) 2023 Riyi |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: MIT |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <chrono> // std::chrono::sytem_clock |
|
||||||
#include <cstdint> // int64_t |
|
||||||
|
|
||||||
#include "ast.h" |
|
||||||
#include "env/macro.h" |
|
||||||
#include "error.h" |
|
||||||
#include "forward.h" |
|
||||||
#include "util.h" |
|
||||||
|
|
||||||
namespace blaze { |
|
||||||
|
|
||||||
void Environment::loadOther() |
|
||||||
{ |
|
||||||
// (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
|
|
@ -1,143 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright (C) 2023 Riyyi |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: MIT |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "ast.h" |
|
||||||
#include "env/macro.h" |
|
||||||
#include "util.h" |
|
||||||
|
|
||||||
namespace blaze { |
|
||||||
|
|
||||||
void Environment::loadPredicate() |
|
||||||
{ |
|
||||||
#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
|
|
@ -1,75 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright (C) 2023 Riyyi |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: MIT |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <string> |
|
||||||
|
|
||||||
#include "ruc/file.h" |
|
||||||
|
|
||||||
#include "ast.h" |
|
||||||
#include "env/macro.h" |
|
||||||
#include "repl.h" |
|
||||||
#include "util.h" |
|
||||||
|
|
||||||
namespace blaze { |
|
||||||
|
|
||||||
void Environment::loadRepl() |
|
||||||
{ |
|
||||||
// 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 Repl::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 Repl::readline(prompt->data()); |
|
||||||
}); |
|
||||||
|
|
||||||
// REPL eval
|
|
||||||
ADD_FUNCTION( |
|
||||||
"eval", |
|
||||||
"", |
|
||||||
"", |
|
||||||
{ |
|
||||||
CHECK_ARG_COUNT_IS("eval", SIZE(), 1); |
|
||||||
|
|
||||||
return Repl::eval(*begin, nullptr); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace blaze
|
|
@ -1,18 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright (C) 2023 Riyi |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: MIT |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <unordered_map> |
|
||||||
|
|
||||||
#include "env/environment.h" |
|
||||||
|
|
||||||
#define ADD_FUNCTION(name, signature, documentation, lambda) \ |
|
||||||
Environment::registerFunction( \
|
|
||||||
{ name, \
|
|
||||||
signature, \
|
|
||||||
documentation, \
|
|
||||||
[](ValueVectorConstIt begin, ValueVectorConstIt end) -> blaze::ValuePtr lambda }); |
|
||||||
|
|
||||||
#define SIZE() std::distance(begin, end) |
|
@ -0,0 +1,102 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <memory> // std::static_pointer_cast |
||||||
|
|
||||||
|
#include "ruc/format/format.h" |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
#include "environment.h" |
||||||
|
#include "error.h" |
||||||
|
#include "forward.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
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, const 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; |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
bool Environment::exists(const std::string& symbol) |
||||||
|
{ |
||||||
|
return m_values.find(symbol) != m_values.end(); |
||||||
|
} |
||||||
|
|
||||||
|
ValuePtr Environment::set(const std::string& symbol, ValuePtr value) |
||||||
|
{ |
||||||
|
if (exists(symbol)) { |
||||||
|
m_values.erase(symbol); |
||||||
|
} |
||||||
|
|
||||||
|
m_values.emplace(symbol, value); |
||||||
|
|
||||||
|
return value; |
||||||
|
} |
||||||
|
|
||||||
|
ValuePtr Environment::get(const std::string& symbol) |
||||||
|
{ |
||||||
|
if (exists(symbol)) { |
||||||
|
return m_values[symbol]; |
||||||
|
} |
||||||
|
|
||||||
|
if (m_outer) { |
||||||
|
return m_outer->get(symbol); |
||||||
|
} |
||||||
|
|
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
@ -0,0 +1,37 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <list> |
||||||
|
#include <string> |
||||||
|
#include <unordered_map> |
||||||
|
|
||||||
|
#include "forward.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
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, const ValueVector& arguments); |
||||||
|
|
||||||
|
bool exists(const std::string& symbol); |
||||||
|
ValuePtr set(const std::string& symbol, ValuePtr value); |
||||||
|
ValuePtr get(const std::string& symbol); |
||||||
|
|
||||||
|
protected: |
||||||
|
Environment() {} |
||||||
|
|
||||||
|
EnvironmentPtr m_outer { nullptr }; |
||||||
|
std::unordered_map<std::string, ValuePtr> m_values; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace blaze
|
@ -0,0 +1,992 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <algorithm> // std::copy, std::reverse_copy |
||||||
|
#include <chrono> // std::chrono::sytem_clock |
||||||
|
#include <cstdint> // int64_t |
||||||
|
#include <iterator> // std::advance, std::distance, std::next, std::prev |
||||||
|
#include <memory> // std::static_pointer_cast |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#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 line number in the file 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(__LINE__, symbol, lambda); |
||||||
|
|
||||||
|
#define SIZE() std::distance(begin, end) |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
static std::unordered_map<std::string, FunctionType> 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<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); |
||||||
|
}); |
||||||
|
|
||||||
|
// // -----------------------------------------
|
||||||
|
|
||||||
|
#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(>=)); |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
ADD_FUNCTION( |
||||||
|
"list", |
||||||
|
{ |
||||||
|
return makePtr<List>(begin, end); |
||||||
|
}); |
||||||
|
|
||||||
|
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); |
||||||
|
}); |
||||||
|
|
||||||
|
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); |
||||||
|
}); |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
#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)); |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
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()))) { |
||||||
|
const auto& lhs_nodes = std::static_pointer_cast<Collection>(lhs)->nodes(); |
||||||
|
const auto& rhs_nodes = std::static_pointer_cast<Collection>(rhs)->nodes(); |
||||||
|
|
||||||
|
if (lhs_nodes.size() != rhs_nodes.size()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
auto lhs_it = lhs_nodes.cbegin(); |
||||||
|
auto rhs_it = rhs_nodes.cbegin(); |
||||||
|
for (; lhs_it != lhs_nodes.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); |
||||||
|
}); |
||||||
|
|
||||||
|
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<String>(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<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(end - begin + 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, 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->nodes(); |
||||||
|
|
||||||
|
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)->nodes(); |
||||||
|
std::copy(collection_nodes.begin(), collection_nodes.end(), result_nodes.begin() + offset); |
||||||
|
offset += collection_nodes.size(); |
||||||
|
} |
||||||
|
|
||||||
|
return makePtr<List>(result_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->nodes()); |
||||||
|
}); |
||||||
|
|
||||||
|
// (nth (list 1 2 3) 0)
|
||||||
|
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->nodes(); |
||||||
|
auto index = (size_t)number_node->number(); |
||||||
|
|
||||||
|
if (number_node->number() < 0 || index >= collection_nodes.size()) { |
||||||
|
Error::the().add("index is out of range"); |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
auto result = collection_nodes.begin(); |
||||||
|
for (size_t i = 0; i < index; ++i) { |
||||||
|
result++; |
||||||
|
} |
||||||
|
|
||||||
|
return *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(); |
||||||
|
}); |
||||||
|
|
||||||
|
// (rest (list 1 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()); |
||||||
|
}); |
||||||
|
|
||||||
|
// (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->nodes(); |
||||||
|
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, 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))); |
||||||
|
const auto& collection_nodes = collection->nodes(); |
||||||
|
|
||||||
|
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_nodes.begin() + i, collection_nodes.begin() + i + 1); |
||||||
|
} |
||||||
|
} |
||||||
|
else { |
||||||
|
auto lambda = std::static_pointer_cast<Lambda>(callable); |
||||||
|
for (size_t i = 0; i < count; ++i) { |
||||||
|
nodes.at(i) = (eval(lambda->body(), Environment::create(lambda, { collection_nodes[i] }))); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return makePtr<List>(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<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); |
||||||
|
}); |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
#define STRING_TO_TYPE(name, type) \ |
||||||
|
{ \
|
||||||
|
CHECK_ARG_COUNT_IS(name, SIZE(), 1); \
|
||||||
|
\
|
||||||
|
if (is<type>(begin->get())) { \
|
||||||
|
return *begin; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
VALUE_CAST(stringValue, String, (*begin)); \
|
||||||
|
\
|
||||||
|
return makePtr<type>(stringValue->data()); \
|
||||||
|
} |
||||||
|
|
||||||
|
// (symbol "foo")
|
||||||
|
ADD_FUNCTION("symbol", STRING_TO_TYPE("symbol", Symbol)); |
||||||
|
ADD_FUNCTION("keyword", STRING_TO_TYPE("keyword", Keyword)); |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
// (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); |
||||||
|
}); |
||||||
|
|
||||||
|
// (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); |
||||||
|
}); |
||||||
|
|
||||||
|
// (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>(); |
||||||
|
}); |
||||||
|
|
||||||
|
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))); |
||||||
|
}); |
||||||
|
|
||||||
|
// (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); |
||||||
|
}); |
||||||
|
|
||||||
|
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::milliseconds>( |
||||||
|
std::chrono::system_clock::now().time_since_epoch()) |
||||||
|
.count(); |
||||||
|
|
||||||
|
return makePtr<Number>(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<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)); |
||||||
|
}); |
||||||
|
|
||||||
|
// (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->nodes(); |
||||||
|
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); |
||||||
|
}); |
||||||
|
|
||||||
|
// (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->nodes()); |
||||||
|
} |
||||||
|
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; |
||||||
|
}); |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
void installFunctions(EnvironmentPtr env) |
||||||
|
{ |
||||||
|
for (const auto& [name, function] : s_functions) { |
||||||
|
env->set(name, makePtr<Function>(name, function)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
@ -1,22 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright (C) 2023 Riyi |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: MIT |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "eval.h" |
|
||||||
|
|
||||||
#define CONCAT(a, b) CONCAT_IMPL(a, b) |
|
||||||
#define CONCAT_IMPL(a, b) a##b |
|
||||||
|
|
||||||
#define EVAL_FUNCTION_IMPL(name, signature, documentation, struct_name) \ |
|
||||||
struct struct_name { \
|
|
||||||
struct_name() \
|
|
||||||
{ \
|
|
||||||
Eval::registerSpecialForm({ name, signature, documentation }); \
|
|
||||||
} \
|
|
||||||
}; \
|
|
||||||
static struct struct_name struct_name; // NOLINT(clang-diagnostic-unused-function)
|
|
||||||
|
|
||||||
#define EVAL_FUNCTION(name, signature, documentation) \ |
|
||||||
EVAL_FUNCTION_IMPL(name, signature, documentation, CONCAT(__EVAL_STRUCT_, __COUNTER__)) |
|
@ -1,84 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright (C) 2023 Riyyi |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: MIT |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <csignal> // std::signal |
|
||||||
#include <string> |
|
||||||
#include <string_view> |
|
||||||
#include <vector> |
|
||||||
|
|
||||||
#include "ruc/argparser.h" |
|
||||||
#include "ruc/format/color.h" |
|
||||||
#include "ruc/format/print.h" |
|
||||||
|
|
||||||
#include "ast.h" |
|
||||||
#include "env/environment.h" |
|
||||||
#include "forward.h" |
|
||||||
#include "repl.h" |
|
||||||
#include "settings.h" |
|
||||||
|
|
||||||
namespace blaze { |
|
||||||
|
|
||||||
auto main(int argc, char* argv[]) -> int |
|
||||||
{ |
|
||||||
bool dump_lexer = false; |
|
||||||
bool dump_reader = false; |
|
||||||
bool pretty_print = false; |
|
||||||
std::string_view history_path = "~/.blaze-history"; |
|
||||||
std::vector<std::string> arguments; |
|
||||||
|
|
||||||
// CLI arguments
|
|
||||||
ruc::ArgParser arg_parser; |
|
||||||
arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr); |
|
||||||
arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); |
|
||||||
arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); |
|
||||||
arg_parser.addOption(history_path, 'h', "history-path", nullptr, nullptr, nullptr, ruc::ArgParser::Required::Yes); |
|
||||||
// TODO: Add overload for addArgument(std::vector<std::string_view>)
|
|
||||||
arg_parser.addArgument(arguments, "arguments", nullptr, nullptr, ruc::ArgParser::Required::No); |
|
||||||
arg_parser.parse(argc, argv); |
|
||||||
|
|
||||||
// Set settings
|
|
||||||
g_outer_env->set("*DUMP-LEXER*", makePtr<Constant>(dump_lexer)); |
|
||||||
g_outer_env->set("*DUMP-READER*", makePtr<Constant>(dump_reader)); |
|
||||||
g_outer_env->set("*PRETTY-PRINT*", makePtr<Constant>(pretty_print)); |
|
||||||
|
|
||||||
// Signal callbacks
|
|
||||||
std::signal(SIGINT, Repl::cleanup); |
|
||||||
std::signal(SIGTERM, Repl::cleanup); |
|
||||||
|
|
||||||
Environment::loadFunctions(); |
|
||||||
Environment::installFunctions(g_outer_env); |
|
||||||
Repl::makeArgv(g_outer_env, arguments); |
|
||||||
|
|
||||||
if (arguments.size() > 0) { |
|
||||||
Repl::rep(format("(load-file \"{}\")", arguments.front()), g_outer_env); |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
Repl::rep("(println (str \"Blaze [\" *host-language* \"]\"))", g_outer_env); |
|
||||||
|
|
||||||
g_readline = Readline(pretty_print, history_path); |
|
||||||
|
|
||||||
std::string input; |
|
||||||
while (g_readline.get(input)) { |
|
||||||
std::string output = Repl::rep(input, g_outer_env); |
|
||||||
if (output.length() > 0) { |
|
||||||
print("{}\n", output); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (pretty_print) { |
|
||||||
print("\033[0m"); |
|
||||||
} |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace blaze
|
|
||||||
|
|
||||||
auto main(int argc, char* argv[]) -> int |
|
||||||
{ |
|
||||||
return blaze::main(argc, argv); |
|
||||||
} |
|
@ -1,100 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright (C) 2023 Riyyi |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: MIT |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <cstdlib> // std::exit |
|
||||||
#include <string> |
|
||||||
#include <string_view> |
|
||||||
#include <vector> |
|
||||||
|
|
||||||
#include "ruc/format/print.h" |
|
||||||
|
|
||||||
#include "env/environment.h" |
|
||||||
#include "error.h" |
|
||||||
#include "eval.h" |
|
||||||
#include "forward.h" |
|
||||||
#include "lexer.h" |
|
||||||
#include "printer.h" |
|
||||||
#include "reader.h" |
|
||||||
#include "readline.h" |
|
||||||
#include "repl.h" |
|
||||||
#include "settings.h" |
|
||||||
|
|
||||||
namespace blaze { |
|
||||||
|
|
||||||
Readline g_readline; |
|
||||||
EnvironmentPtr g_outer_env = Environment::create(); |
|
||||||
|
|
||||||
auto Repl::cleanup(int signal) -> void |
|
||||||
{ |
|
||||||
::print("\033[0m\n"); |
|
||||||
std::exit(signal); |
|
||||||
} |
|
||||||
|
|
||||||
auto Repl::readline(const std::string& prompt) -> ValuePtr |
|
||||||
{ |
|
||||||
std::string input; |
|
||||||
if (g_readline.get(input, g_readline.createPrompt(prompt))) { |
|
||||||
return makePtr<String>(input); |
|
||||||
} |
|
||||||
|
|
||||||
return makePtr<Constant>(); |
|
||||||
} |
|
||||||
|
|
||||||
auto Repl::read(std::string_view input) -> ValuePtr |
|
||||||
{ |
|
||||||
Lexer lexer(input); |
|
||||||
lexer.tokenize(); |
|
||||||
if (Settings::the().getEnvBool("*DUMP-LEXER*")) { |
|
||||||
lexer.dump(); |
|
||||||
} |
|
||||||
|
|
||||||
Reader reader(std::move(lexer.tokens())); |
|
||||||
reader.read(); |
|
||||||
if (Settings::the().getEnvBool("*DUMP-READER*")) { |
|
||||||
reader.dump(); |
|
||||||
} |
|
||||||
|
|
||||||
return reader.node(); |
|
||||||
} |
|
||||||
|
|
||||||
auto Repl::eval(ValuePtr ast, EnvironmentPtr env) -> ValuePtr |
|
||||||
{ |
|
||||||
if (env == nullptr) { |
|
||||||
env = g_outer_env; |
|
||||||
} |
|
||||||
|
|
||||||
Eval eval(ast, env); |
|
||||||
eval.eval(); |
|
||||||
|
|
||||||
return eval.ast(); |
|
||||||
} |
|
||||||
|
|
||||||
auto Repl::print(ValuePtr value) -> std::string |
|
||||||
{ |
|
||||||
Printer printer; |
|
||||||
|
|
||||||
return printer.print(value, true); |
|
||||||
} |
|
||||||
|
|
||||||
auto Repl::rep(std::string_view input, EnvironmentPtr env) -> std::string |
|
||||||
{ |
|
||||||
Error::the().clearErrors(); |
|
||||||
Error::the().setInput(input); |
|
||||||
|
|
||||||
return print(eval(read(input), env)); |
|
||||||
} |
|
||||||
|
|
||||||
auto Repl::makeArgv(EnvironmentPtr env, std::vector<std::string> arguments) -> void |
|
||||||
{ |
|
||||||
size_t count = arguments.size(); |
|
||||||
auto nodes = ValueVector(); |
|
||||||
for (size_t i = 1; i < count; ++i) { |
|
||||||
nodes.push_back(makePtr<String>(arguments[i])); |
|
||||||
} |
|
||||||
env->set("*ARGV*", makePtr<List>(nodes)); |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace blaze
|
|
@ -1,29 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright (C) 2023 Riyyi |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: MIT |
|
||||||
*/ |
|
||||||
|
|
||||||
#pragma once |
|
||||||
|
|
||||||
#include <string> |
|
||||||
#include <string_view> |
|
||||||
#include <vector> |
|
||||||
|
|
||||||
#include "forward.h" |
|
||||||
#include "readline.h" |
|
||||||
|
|
||||||
namespace blaze { |
|
||||||
|
|
||||||
class Repl { |
|
||||||
public: |
|
||||||
static auto cleanup(int signal) -> void; |
|
||||||
static auto eval(ValuePtr ast, EnvironmentPtr env) -> ValuePtr; |
|
||||||
static auto makeArgv(EnvironmentPtr env, std::vector<std::string> arguments) -> void; |
|
||||||
static auto print(ValuePtr value) -> std::string; |
|
||||||
static auto read(std::string_view input) -> ValuePtr; |
|
||||||
static auto readline(const std::string& prompt) -> ValuePtr; |
|
||||||
static auto rep(std::string_view input, EnvironmentPtr env) -> std::string; |
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace blaze
|
|
@ -0,0 +1,62 @@ |
|||||||
|
#include <cstdio> |
||||||
|
#include <iostream> // std::cin |
||||||
|
#include <string> // std::getline |
||||||
|
#include <string_view> |
||||||
|
|
||||||
|
#include "forward.h" |
||||||
|
|
||||||
|
auto read(std::string_view data) -> std::string_view |
||||||
|
{ |
||||||
|
return data; |
||||||
|
} |
||||||
|
|
||||||
|
auto eval(std::string_view data) -> std::string_view |
||||||
|
{ |
||||||
|
return data; |
||||||
|
} |
||||||
|
|
||||||
|
auto print(std::string_view data) -> void |
||||||
|
{ |
||||||
|
printf("%s\n", data.data()); |
||||||
|
} |
||||||
|
|
||||||
|
auto rep(std::string_view data) -> void |
||||||
|
{ |
||||||
|
print(eval(read(data))); |
||||||
|
} |
||||||
|
|
||||||
|
auto main() -> int |
||||||
|
{ |
||||||
|
while (true) { |
||||||
|
printf("user> "); |
||||||
|
std::string line; |
||||||
|
std::getline(std::cin, line); |
||||||
|
|
||||||
|
// Exit with Ctrl-D
|
||||||
|
if (std::cin.eof() || std::cin.fail()) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
rep(line); |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Below is needed for compilation
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
auto read(std::string_view) -> ValuePtr |
||||||
|
{ |
||||||
|
return {}; |
||||||
|
} |
||||||
|
|
||||||
|
auto eval(ValuePtr, EnvironmentPtr) -> ValuePtr |
||||||
|
{ |
||||||
|
return {}; |
||||||
|
} |
||||||
|
|
||||||
|
// Added to keep the linker happy at step A
|
||||||
|
ValuePtr readline(const std::string&) { return nullptr; } |
||||||
|
|
||||||
|
} // namespace blaze
|
@ -0,0 +1,114 @@ |
|||||||
|
#include <csignal> // std::signal |
||||||
|
#include <cstdlib> // std::exit |
||||||
|
#include <iostream> // std::cin |
||||||
|
#include <string> // std::getline |
||||||
|
#include <string_view> |
||||||
|
|
||||||
|
#include "ruc/argparser.h" |
||||||
|
#include "ruc/format/color.h" |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
#include "error.h" |
||||||
|
#include "forward.h" |
||||||
|
#include "lexer.h" |
||||||
|
#include "printer.h" |
||||||
|
#include "reader.h" |
||||||
|
#include "settings.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
auto read(std::string_view input) -> ValuePtr |
||||||
|
{ |
||||||
|
Lexer lexer(input); |
||||||
|
lexer.tokenize(); |
||||||
|
if (Settings::the().get("dump-lexer") == "1") { |
||||||
|
lexer.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
Reader reader(std::move(lexer.tokens())); |
||||||
|
reader.read(); |
||||||
|
if (Settings::the().get("dump-reader") == "1") { |
||||||
|
reader.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
return reader.node(); |
||||||
|
} |
||||||
|
|
||||||
|
auto eval(ValuePtr ast, EnvironmentPtr) -> ValuePtr |
||||||
|
{ |
||||||
|
return ast; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
||||||
|
|
||||||
|
auto print(blaze::ValuePtr exp) -> std::string |
||||||
|
{ |
||||||
|
blaze::Printer printer; |
||||||
|
return printer.print(exp); |
||||||
|
} |
||||||
|
|
||||||
|
auto rep(std::string_view input) -> void |
||||||
|
{ |
||||||
|
blaze::Error::the().clearErrors(); |
||||||
|
blaze::Error::the().setInput(input); |
||||||
|
|
||||||
|
print("{}\n", print(blaze::eval(blaze::read(input), nullptr)).c_str()); |
||||||
|
} |
||||||
|
|
||||||
|
static auto cleanup(int signal) -> void |
||||||
|
{ |
||||||
|
print("\033[0m"); |
||||||
|
std::exit(signal); |
||||||
|
} |
||||||
|
|
||||||
|
auto main(int argc, char* argv[]) -> int |
||||||
|
{ |
||||||
|
bool dump_lexer = false; |
||||||
|
bool dump_reader = false; |
||||||
|
bool pretty_print = false; |
||||||
|
|
||||||
|
// CLI arguments
|
||||||
|
ruc::ArgParser arg_parser; |
||||||
|
arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr); |
||||||
|
arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); |
||||||
|
arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); |
||||||
|
arg_parser.parse(argc, argv); |
||||||
|
|
||||||
|
// Set settings
|
||||||
|
blaze::Settings::the().set("dump-lexer", dump_lexer ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("dump-reader", dump_reader ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("pretty-print", pretty_print ? "1" : "0"); |
||||||
|
|
||||||
|
// Signal callbacks
|
||||||
|
std::signal(SIGINT, cleanup); |
||||||
|
std::signal(SIGTERM, cleanup); |
||||||
|
|
||||||
|
while (true) { |
||||||
|
if (!pretty_print) { |
||||||
|
print("user> "); |
||||||
|
} |
||||||
|
else { |
||||||
|
print(fg(ruc::format::TerminalColor::Blue), "user>"); |
||||||
|
print(" \033[1m"); |
||||||
|
} |
||||||
|
std::string line; |
||||||
|
std::getline(std::cin, line); |
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
|
||||||
|
// Exit with Ctrl-D
|
||||||
|
if (std::cin.eof() || std::cin.fail()) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
rep(line); |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Added to keep the linker happy at step A
|
||||||
|
namespace blaze { |
||||||
|
ValuePtr readline(const std::string&) { return nullptr; } |
||||||
|
} // namespace blaze
|
@ -0,0 +1,118 @@ |
|||||||
|
#include <csignal> // std::signal |
||||||
|
#include <cstdlib> // std::exit |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
|
||||||
|
#include "ruc/argparser.h" |
||||||
|
#include "ruc/format/color.h" |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
#include "environment.h" |
||||||
|
#include "error.h" |
||||||
|
#include "eval.h" |
||||||
|
#include "forward.h" |
||||||
|
#include "lexer.h" |
||||||
|
#include "printer.h" |
||||||
|
#include "reader.h" |
||||||
|
#include "readline.h" |
||||||
|
#include "settings.h" |
||||||
|
|
||||||
|
static blaze::EnvironmentPtr s_outer_env = blaze::Environment::create(); |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
auto read(std::string_view input) -> ValuePtr |
||||||
|
{ |
||||||
|
Lexer lexer(input); |
||||||
|
lexer.tokenize(); |
||||||
|
if (Settings::the().get("dump-lexer") == "1") { |
||||||
|
lexer.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
Reader reader(std::move(lexer.tokens())); |
||||||
|
reader.read(); |
||||||
|
if (Settings::the().get("dump-reader") == "1") { |
||||||
|
reader.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
return reader.node(); |
||||||
|
} |
||||||
|
|
||||||
|
auto eval(ValuePtr ast, EnvironmentPtr env) -> ValuePtr |
||||||
|
{ |
||||||
|
Eval eval(ast, env); |
||||||
|
eval.eval(); |
||||||
|
|
||||||
|
return eval.ast(); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
||||||
|
|
||||||
|
auto print(blaze::ValuePtr exp) -> std::string |
||||||
|
{ |
||||||
|
blaze::Printer printer; |
||||||
|
|
||||||
|
return printer.print(exp); |
||||||
|
} |
||||||
|
|
||||||
|
auto rep(std::string_view input) -> std::string |
||||||
|
{ |
||||||
|
blaze::Error::the().clearErrors(); |
||||||
|
blaze::Error::the().setInput(input); |
||||||
|
|
||||||
|
return print(blaze::eval(blaze::read(input), s_outer_env)); |
||||||
|
} |
||||||
|
|
||||||
|
static auto cleanup(int signal) -> void |
||||||
|
{ |
||||||
|
print("\033[0m\n"); |
||||||
|
std::exit(signal); |
||||||
|
} |
||||||
|
|
||||||
|
auto main(int argc, char* argv[]) -> int |
||||||
|
{ |
||||||
|
bool dump_lexer = false; |
||||||
|
bool dump_reader = false; |
||||||
|
bool pretty_print = false; |
||||||
|
std::string_view history_path = "~/.mal-history"; |
||||||
|
|
||||||
|
// CLI arguments
|
||||||
|
ruc::ArgParser arg_parser; |
||||||
|
arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr); |
||||||
|
arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); |
||||||
|
arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); |
||||||
|
arg_parser.addOption(history_path, 'h', "history", nullptr, nullptr, nullptr, ruc::ArgParser::Required::Yes); |
||||||
|
arg_parser.parse(argc, argv); |
||||||
|
|
||||||
|
// Set settings
|
||||||
|
blaze::Settings::the().set("dump-lexer", dump_lexer ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("dump-reader", dump_reader ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("pretty-print", pretty_print ? "1" : "0"); |
||||||
|
|
||||||
|
// Signal callbacks
|
||||||
|
std::signal(SIGINT, cleanup); |
||||||
|
std::signal(SIGTERM, cleanup); |
||||||
|
|
||||||
|
installFunctions(s_outer_env); |
||||||
|
|
||||||
|
blaze::Readline readline(pretty_print, history_path); |
||||||
|
|
||||||
|
std::string input; |
||||||
|
while (readline.get(input)) { |
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
print("{}\n", rep(input)); |
||||||
|
} |
||||||
|
|
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Added to keep the linker happy at step A
|
||||||
|
namespace blaze { |
||||||
|
ValuePtr readline(const std::string&) { return nullptr; } |
||||||
|
} // namespace blaze
|
@ -0,0 +1,118 @@ |
|||||||
|
#include <csignal> // std::signal |
||||||
|
#include <cstdlib> // std::exit |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
|
||||||
|
#include "ruc/argparser.h" |
||||||
|
#include "ruc/format/color.h" |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
#include "environment.h" |
||||||
|
#include "error.h" |
||||||
|
#include "eval.h" |
||||||
|
#include "forward.h" |
||||||
|
#include "lexer.h" |
||||||
|
#include "printer.h" |
||||||
|
#include "reader.h" |
||||||
|
#include "readline.h" |
||||||
|
#include "settings.h" |
||||||
|
|
||||||
|
static blaze::EnvironmentPtr s_outer_env = blaze::Environment::create(); |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
auto read(std::string_view input) -> ValuePtr |
||||||
|
{ |
||||||
|
Lexer lexer(input); |
||||||
|
lexer.tokenize(); |
||||||
|
if (Settings::the().get("dump-lexer") == "1") { |
||||||
|
lexer.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
Reader reader(std::move(lexer.tokens())); |
||||||
|
reader.read(); |
||||||
|
if (Settings::the().get("dump-reader") == "1") { |
||||||
|
reader.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
return reader.node(); |
||||||
|
} |
||||||
|
|
||||||
|
auto eval(ValuePtr ast, EnvironmentPtr env) -> ValuePtr |
||||||
|
{ |
||||||
|
Eval eval(ast, env); |
||||||
|
eval.eval(); |
||||||
|
|
||||||
|
return eval.ast(); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
||||||
|
|
||||||
|
auto print(blaze::ValuePtr exp) -> std::string |
||||||
|
{ |
||||||
|
blaze::Printer printer; |
||||||
|
|
||||||
|
return printer.print(exp); |
||||||
|
} |
||||||
|
|
||||||
|
auto rep(std::string_view input) -> std::string |
||||||
|
{ |
||||||
|
blaze::Error::the().clearErrors(); |
||||||
|
blaze::Error::the().setInput(input); |
||||||
|
|
||||||
|
return print(blaze::eval(blaze::read(input), s_outer_env)); |
||||||
|
} |
||||||
|
|
||||||
|
static auto cleanup(int signal) -> void |
||||||
|
{ |
||||||
|
print("\033[0m\n"); |
||||||
|
std::exit(signal); |
||||||
|
} |
||||||
|
|
||||||
|
auto main(int argc, char* argv[]) -> int |
||||||
|
{ |
||||||
|
bool dump_lexer = false; |
||||||
|
bool dump_reader = false; |
||||||
|
bool pretty_print = false; |
||||||
|
std::string_view history_path = "~/.mal-history"; |
||||||
|
|
||||||
|
// CLI arguments
|
||||||
|
ruc::ArgParser arg_parser; |
||||||
|
arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr); |
||||||
|
arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); |
||||||
|
arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); |
||||||
|
arg_parser.addOption(history_path, 'h', "history", nullptr, nullptr, nullptr, ruc::ArgParser::Required::Yes); |
||||||
|
arg_parser.parse(argc, argv); |
||||||
|
|
||||||
|
// Set settings
|
||||||
|
blaze::Settings::the().set("dump-lexer", dump_lexer ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("dump-reader", dump_reader ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("pretty-print", pretty_print ? "1" : "0"); |
||||||
|
|
||||||
|
// Signal callbacks
|
||||||
|
std::signal(SIGINT, cleanup); |
||||||
|
std::signal(SIGTERM, cleanup); |
||||||
|
|
||||||
|
installFunctions(s_outer_env); |
||||||
|
|
||||||
|
blaze::Readline readline(pretty_print, history_path); |
||||||
|
|
||||||
|
std::string input; |
||||||
|
while (readline.get(input)) { |
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
print("{}\n", rep(input)); |
||||||
|
} |
||||||
|
|
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Added to keep the linker happy at step A
|
||||||
|
namespace blaze { |
||||||
|
ValuePtr readline(const std::string&) { return nullptr; } |
||||||
|
} // namespace blaze
|
@ -0,0 +1,135 @@ |
|||||||
|
#include <csignal> // std::signal |
||||||
|
#include <cstdlib> // std::exit |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
|
||||||
|
#include "ruc/argparser.h" |
||||||
|
#include "ruc/format/color.h" |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
#include "environment.h" |
||||||
|
#include "error.h" |
||||||
|
#include "eval.h" |
||||||
|
#include "forward.h" |
||||||
|
#include "lexer.h" |
||||||
|
#include "printer.h" |
||||||
|
#include "reader.h" |
||||||
|
#include "readline.h" |
||||||
|
#include "settings.h" |
||||||
|
|
||||||
|
static blaze::EnvironmentPtr s_outer_env = blaze::Environment::create(); |
||||||
|
|
||||||
|
static auto cleanup(int signal) -> void; |
||||||
|
static auto installLambdas(blaze::EnvironmentPtr env) -> void; |
||||||
|
static auto rep(std::string_view input, blaze::EnvironmentPtr env) -> std::string; |
||||||
|
static auto print(blaze::ValuePtr exp) -> std::string; |
||||||
|
|
||||||
|
auto main(int argc, char* argv[]) -> int |
||||||
|
{ |
||||||
|
bool dump_lexer = false; |
||||||
|
bool dump_reader = false; |
||||||
|
bool pretty_print = false; |
||||||
|
std::string_view history_path = "~/.mal-history"; |
||||||
|
|
||||||
|
// CLI arguments
|
||||||
|
ruc::ArgParser arg_parser; |
||||||
|
arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr); |
||||||
|
arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); |
||||||
|
arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); |
||||||
|
arg_parser.addOption(history_path, 'h', "history", nullptr, nullptr, nullptr, ruc::ArgParser::Required::Yes); |
||||||
|
arg_parser.parse(argc, argv); |
||||||
|
|
||||||
|
// Set settings
|
||||||
|
blaze::Settings::the().set("dump-lexer", dump_lexer ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("dump-reader", dump_reader ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("pretty-print", pretty_print ? "1" : "0"); |
||||||
|
|
||||||
|
// Signal callbacks
|
||||||
|
std::signal(SIGINT, cleanup); |
||||||
|
std::signal(SIGTERM, cleanup); |
||||||
|
|
||||||
|
installFunctions(s_outer_env); |
||||||
|
installLambdas(s_outer_env); |
||||||
|
|
||||||
|
blaze::Readline readline(pretty_print, history_path); |
||||||
|
|
||||||
|
std::string input; |
||||||
|
while (readline.get(input)) { |
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
print("{}\n", rep(input, s_outer_env)); |
||||||
|
} |
||||||
|
|
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static auto cleanup(int signal) -> void |
||||||
|
{ |
||||||
|
print("\033[0m\n"); |
||||||
|
std::exit(signal); |
||||||
|
} |
||||||
|
|
||||||
|
static std::string_view lambdaTable[] = { |
||||||
|
"(def! not (fn* (cond) (if cond false true)))", |
||||||
|
}; |
||||||
|
|
||||||
|
static auto installLambdas(blaze::EnvironmentPtr env) -> void |
||||||
|
{ |
||||||
|
for (auto function : lambdaTable) { |
||||||
|
rep(function, env); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static auto rep(std::string_view input, blaze::EnvironmentPtr env) -> std::string |
||||||
|
{ |
||||||
|
blaze::Error::the().clearErrors(); |
||||||
|
blaze::Error::the().setInput(input); |
||||||
|
|
||||||
|
return print(blaze::eval(blaze::read(input), env)); |
||||||
|
} |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
auto read(std::string_view input) -> ValuePtr |
||||||
|
{ |
||||||
|
Lexer lexer(input); |
||||||
|
lexer.tokenize(); |
||||||
|
if (Settings::the().get("dump-lexer") == "1") { |
||||||
|
lexer.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
Reader reader(std::move(lexer.tokens())); |
||||||
|
reader.read(); |
||||||
|
if (Settings::the().get("dump-reader") == "1") { |
||||||
|
reader.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
return reader.node(); |
||||||
|
} |
||||||
|
|
||||||
|
auto eval(ValuePtr ast, EnvironmentPtr env) -> ValuePtr |
||||||
|
{ |
||||||
|
Eval eval(ast, env); |
||||||
|
eval.eval(); |
||||||
|
|
||||||
|
return eval.ast(); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
||||||
|
|
||||||
|
static auto print(blaze::ValuePtr exp) -> std::string |
||||||
|
{ |
||||||
|
blaze::Printer printer; |
||||||
|
|
||||||
|
return printer.print(exp, true); |
||||||
|
} |
||||||
|
|
||||||
|
// Added to keep the linker happy at step A
|
||||||
|
namespace blaze { |
||||||
|
ValuePtr readline(const std::string&) { return nullptr; } |
||||||
|
} // namespace blaze
|
@ -0,0 +1,141 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <csignal> // std::signal |
||||||
|
#include <cstdlib> // std::exit |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
|
||||||
|
#include "ruc/argparser.h" |
||||||
|
#include "ruc/format/color.h" |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
#include "environment.h" |
||||||
|
#include "error.h" |
||||||
|
#include "eval.h" |
||||||
|
#include "forward.h" |
||||||
|
#include "lexer.h" |
||||||
|
#include "printer.h" |
||||||
|
#include "reader.h" |
||||||
|
#include "readline.h" |
||||||
|
#include "settings.h" |
||||||
|
|
||||||
|
static blaze::EnvironmentPtr s_outer_env = blaze::Environment::create(); |
||||||
|
|
||||||
|
static auto cleanup(int signal) -> void; |
||||||
|
static auto installLambdas(blaze::EnvironmentPtr env) -> void; |
||||||
|
static auto rep(std::string_view input, blaze::EnvironmentPtr env) -> std::string; |
||||||
|
static auto print(blaze::ValuePtr exp) -> std::string; |
||||||
|
|
||||||
|
auto main(int argc, char* argv[]) -> int |
||||||
|
{ |
||||||
|
bool dump_lexer = false; |
||||||
|
bool dump_reader = false; |
||||||
|
bool pretty_print = false; |
||||||
|
std::string_view history_path = "~/.mal-history"; |
||||||
|
|
||||||
|
// CLI arguments
|
||||||
|
ruc::ArgParser arg_parser; |
||||||
|
arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr); |
||||||
|
arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); |
||||||
|
arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); |
||||||
|
arg_parser.addOption(history_path, 'h', "history", nullptr, nullptr, nullptr, ruc::ArgParser::Required::Yes); |
||||||
|
arg_parser.parse(argc, argv); |
||||||
|
|
||||||
|
// Set settings
|
||||||
|
blaze::Settings::the().set("dump-lexer", dump_lexer ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("dump-reader", dump_reader ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("pretty-print", pretty_print ? "1" : "0"); |
||||||
|
|
||||||
|
// Signal callbacks
|
||||||
|
std::signal(SIGINT, cleanup); |
||||||
|
std::signal(SIGTERM, cleanup); |
||||||
|
|
||||||
|
installFunctions(s_outer_env); |
||||||
|
installLambdas(s_outer_env); |
||||||
|
|
||||||
|
blaze::Readline readline(pretty_print, history_path); |
||||||
|
|
||||||
|
std::string input; |
||||||
|
while (readline.get(input)) { |
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
print("{}\n", rep(input, s_outer_env)); |
||||||
|
} |
||||||
|
|
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static auto cleanup(int signal) -> void |
||||||
|
{ |
||||||
|
print("\033[0m\n"); |
||||||
|
std::exit(signal); |
||||||
|
} |
||||||
|
|
||||||
|
static std::string_view lambdaTable[] = { |
||||||
|
"(def! not (fn* (cond) (if cond false true)))", |
||||||
|
}; |
||||||
|
|
||||||
|
static auto installLambdas(blaze::EnvironmentPtr env) -> void |
||||||
|
{ |
||||||
|
for (auto function : lambdaTable) { |
||||||
|
rep(function, env); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static auto rep(std::string_view input, blaze::EnvironmentPtr env) -> std::string |
||||||
|
{ |
||||||
|
blaze::Error::the().clearErrors(); |
||||||
|
blaze::Error::the().setInput(input); |
||||||
|
|
||||||
|
return print(blaze::eval(blaze::read(input), env)); |
||||||
|
} |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
auto read(std::string_view input) -> ValuePtr |
||||||
|
{ |
||||||
|
Lexer lexer(input); |
||||||
|
lexer.tokenize(); |
||||||
|
if (Settings::the().get("dump-lexer") == "1") { |
||||||
|
lexer.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
Reader reader(std::move(lexer.tokens())); |
||||||
|
reader.read(); |
||||||
|
if (Settings::the().get("dump-reader") == "1") { |
||||||
|
reader.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
return reader.node(); |
||||||
|
} |
||||||
|
|
||||||
|
auto eval(ValuePtr ast, EnvironmentPtr env) -> ValuePtr |
||||||
|
{ |
||||||
|
Eval eval(ast, env); |
||||||
|
eval.eval(); |
||||||
|
|
||||||
|
return eval.ast(); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
||||||
|
|
||||||
|
static auto print(blaze::ValuePtr exp) -> std::string |
||||||
|
{ |
||||||
|
blaze::Printer printer; |
||||||
|
|
||||||
|
return printer.print(exp, true); |
||||||
|
} |
||||||
|
|
||||||
|
// Added to keep the linker happy at step A
|
||||||
|
namespace blaze { |
||||||
|
ValuePtr readline(const std::string&) { return nullptr; } |
||||||
|
} // namespace blaze
|
@ -0,0 +1,161 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <csignal> // std::signal |
||||||
|
#include <cstdlib> // std::exit |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "ruc/argparser.h" |
||||||
|
#include "ruc/format/color.h" |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
#include "environment.h" |
||||||
|
#include "error.h" |
||||||
|
#include "eval.h" |
||||||
|
#include "forward.h" |
||||||
|
#include "lexer.h" |
||||||
|
#include "printer.h" |
||||||
|
#include "reader.h" |
||||||
|
#include "readline.h" |
||||||
|
#include "settings.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
static EnvironmentPtr s_outer_env = Environment::create(); |
||||||
|
|
||||||
|
static auto cleanup(int signal) -> void |
||||||
|
{ |
||||||
|
print("\033[0m\n"); |
||||||
|
std::exit(signal); |
||||||
|
} |
||||||
|
|
||||||
|
auto read(std::string_view input) -> ValuePtr |
||||||
|
{ |
||||||
|
Lexer lexer(input); |
||||||
|
lexer.tokenize(); |
||||||
|
if (Settings::the().get("dump-lexer") == "1") { |
||||||
|
lexer.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
Reader reader(std::move(lexer.tokens())); |
||||||
|
reader.read(); |
||||||
|
if (Settings::the().get("dump-reader") == "1") { |
||||||
|
reader.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
return reader.node(); |
||||||
|
} |
||||||
|
|
||||||
|
auto eval(ValuePtr ast, EnvironmentPtr env) -> ValuePtr |
||||||
|
{ |
||||||
|
if (env == nullptr) { |
||||||
|
env = s_outer_env; |
||||||
|
} |
||||||
|
|
||||||
|
Eval eval(ast, env); |
||||||
|
eval.eval(); |
||||||
|
|
||||||
|
return eval.ast(); |
||||||
|
} |
||||||
|
|
||||||
|
static auto print(ValuePtr exp) -> std::string |
||||||
|
{ |
||||||
|
Printer printer; |
||||||
|
|
||||||
|
return printer.print(exp, true); |
||||||
|
} |
||||||
|
|
||||||
|
static auto rep(std::string_view input, EnvironmentPtr env) -> std::string |
||||||
|
{ |
||||||
|
Error::the().clearErrors(); |
||||||
|
Error::the().setInput(input); |
||||||
|
|
||||||
|
return print(eval(read(input), env)); |
||||||
|
} |
||||||
|
|
||||||
|
static std::string_view lambdaTable[] = { |
||||||
|
"(def! not (fn* (cond) (if cond false true)))", |
||||||
|
"(def! load-file (fn* (filename) \
|
||||||
|
(eval (read-string (str \"(do \" (slurp filename) \"\nnil)\")))))", |
||||||
|
}; |
||||||
|
|
||||||
|
static auto installLambdas(EnvironmentPtr env) -> void |
||||||
|
{ |
||||||
|
for (auto function : lambdaTable) { |
||||||
|
rep(function, env); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static auto makeArgv(EnvironmentPtr env, std::vector<std::string> arguments) -> void |
||||||
|
{ |
||||||
|
size_t count = arguments.size(); |
||||||
|
auto nodes = ValueVector(); |
||||||
|
for (size_t i = 1; i < count; ++i) { |
||||||
|
nodes.push_back(makePtr<String>(arguments[i])); |
||||||
|
} |
||||||
|
env->set("*ARGV*", makePtr<List>(nodes)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
||||||
|
|
||||||
|
auto main(int argc, char* argv[]) -> int |
||||||
|
{ |
||||||
|
bool dump_lexer = false; |
||||||
|
bool dump_reader = false; |
||||||
|
bool pretty_print = false; |
||||||
|
std::string_view history_path = "~/.blaze-history"; |
||||||
|
std::vector<std::string> arguments; |
||||||
|
|
||||||
|
// CLI arguments
|
||||||
|
ruc::ArgParser arg_parser; |
||||||
|
arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr); |
||||||
|
arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); |
||||||
|
arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); |
||||||
|
arg_parser.addOption(history_path, 'h', "history-path", nullptr, nullptr, nullptr, ruc::ArgParser::Required::Yes); |
||||||
|
arg_parser.addArgument(arguments, "arguments", nullptr, nullptr, ruc::ArgParser::Required::No); |
||||||
|
arg_parser.parse(argc, argv); |
||||||
|
|
||||||
|
// Set settings
|
||||||
|
blaze::Settings::the().set("dump-lexer", dump_lexer ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("dump-reader", dump_reader ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("pretty-print", pretty_print ? "1" : "0"); |
||||||
|
|
||||||
|
// Signal callbacks
|
||||||
|
std::signal(SIGINT, blaze::cleanup); |
||||||
|
std::signal(SIGTERM, blaze::cleanup); |
||||||
|
|
||||||
|
installFunctions(blaze::s_outer_env); |
||||||
|
installLambdas(blaze::s_outer_env); |
||||||
|
makeArgv(blaze::s_outer_env, arguments); |
||||||
|
|
||||||
|
if (arguments.size() > 0) { |
||||||
|
rep(format("(load-file \"{}\")", arguments.front()), blaze::s_outer_env); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
blaze::Readline readline(pretty_print, history_path); |
||||||
|
|
||||||
|
std::string input; |
||||||
|
while (readline.get(input)) { |
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
print("{}\n", rep(input, blaze::s_outer_env)); |
||||||
|
} |
||||||
|
|
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Added to keep the linker happy at step A
|
||||||
|
namespace blaze { |
||||||
|
ValuePtr readline(const std::string&) { return nullptr; } |
||||||
|
} // namespace blaze
|
@ -0,0 +1,161 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <csignal> // std::signal |
||||||
|
#include <cstdlib> // std::exit |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "ruc/argparser.h" |
||||||
|
#include "ruc/format/color.h" |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
#include "environment.h" |
||||||
|
#include "error.h" |
||||||
|
#include "eval.h" |
||||||
|
#include "forward.h" |
||||||
|
#include "lexer.h" |
||||||
|
#include "printer.h" |
||||||
|
#include "reader.h" |
||||||
|
#include "readline.h" |
||||||
|
#include "settings.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
static EnvironmentPtr s_outer_env = Environment::create(); |
||||||
|
|
||||||
|
static auto cleanup(int signal) -> void |
||||||
|
{ |
||||||
|
print("\033[0m\n"); |
||||||
|
std::exit(signal); |
||||||
|
} |
||||||
|
|
||||||
|
auto read(std::string_view input) -> ValuePtr |
||||||
|
{ |
||||||
|
Lexer lexer(input); |
||||||
|
lexer.tokenize(); |
||||||
|
if (Settings::the().get("dump-lexer") == "1") { |
||||||
|
lexer.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
Reader reader(std::move(lexer.tokens())); |
||||||
|
reader.read(); |
||||||
|
if (Settings::the().get("dump-reader") == "1") { |
||||||
|
reader.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
return reader.node(); |
||||||
|
} |
||||||
|
|
||||||
|
auto eval(ValuePtr ast, EnvironmentPtr env) -> ValuePtr |
||||||
|
{ |
||||||
|
if (env == nullptr) { |
||||||
|
env = s_outer_env; |
||||||
|
} |
||||||
|
|
||||||
|
Eval eval(ast, env); |
||||||
|
eval.eval(); |
||||||
|
|
||||||
|
return eval.ast(); |
||||||
|
} |
||||||
|
|
||||||
|
static auto print(ValuePtr exp) -> std::string |
||||||
|
{ |
||||||
|
Printer printer; |
||||||
|
|
||||||
|
return printer.print(exp, true); |
||||||
|
} |
||||||
|
|
||||||
|
static auto rep(std::string_view input, EnvironmentPtr env) -> std::string |
||||||
|
{ |
||||||
|
Error::the().clearErrors(); |
||||||
|
Error::the().setInput(input); |
||||||
|
|
||||||
|
return print(eval(read(input), env)); |
||||||
|
} |
||||||
|
|
||||||
|
static std::string_view lambdaTable[] = { |
||||||
|
"(def! not (fn* (cond) (if cond false true)))", |
||||||
|
"(def! load-file (fn* (filename) \
|
||||||
|
(eval (read-string (str \"(do \" (slurp filename) \"\nnil)\")))))", |
||||||
|
}; |
||||||
|
|
||||||
|
static auto installLambdas(EnvironmentPtr env) -> void |
||||||
|
{ |
||||||
|
for (auto function : lambdaTable) { |
||||||
|
rep(function, env); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static auto makeArgv(EnvironmentPtr env, std::vector<std::string> arguments) -> void |
||||||
|
{ |
||||||
|
size_t count = arguments.size(); |
||||||
|
auto nodes = ValueVector(); |
||||||
|
for (size_t i = 1; i < count; ++i) { |
||||||
|
nodes.push_back(makePtr<String>(arguments[i])); |
||||||
|
} |
||||||
|
env->set("*ARGV*", makePtr<List>(nodes)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
||||||
|
|
||||||
|
auto main(int argc, char* argv[]) -> int |
||||||
|
{ |
||||||
|
bool dump_lexer = false; |
||||||
|
bool dump_reader = false; |
||||||
|
bool pretty_print = false; |
||||||
|
std::string_view history_path = "~/.blaze-history"; |
||||||
|
std::vector<std::string> arguments; |
||||||
|
|
||||||
|
// CLI arguments
|
||||||
|
ruc::ArgParser arg_parser; |
||||||
|
arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr); |
||||||
|
arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); |
||||||
|
arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); |
||||||
|
arg_parser.addOption(history_path, 'h', "history-path", nullptr, nullptr, nullptr, ruc::ArgParser::Required::Yes); |
||||||
|
arg_parser.addArgument(arguments, "arguments", nullptr, nullptr, ruc::ArgParser::Required::No); |
||||||
|
arg_parser.parse(argc, argv); |
||||||
|
|
||||||
|
// Set settings
|
||||||
|
blaze::Settings::the().set("dump-lexer", dump_lexer ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("dump-reader", dump_reader ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("pretty-print", pretty_print ? "1" : "0"); |
||||||
|
|
||||||
|
// Signal callbacks
|
||||||
|
std::signal(SIGINT, blaze::cleanup); |
||||||
|
std::signal(SIGTERM, blaze::cleanup); |
||||||
|
|
||||||
|
installFunctions(blaze::s_outer_env); |
||||||
|
installLambdas(blaze::s_outer_env); |
||||||
|
makeArgv(blaze::s_outer_env, arguments); |
||||||
|
|
||||||
|
if (arguments.size() > 0) { |
||||||
|
rep(format("(load-file \"{}\")", arguments.front()), blaze::s_outer_env); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
blaze::Readline readline(pretty_print, history_path); |
||||||
|
|
||||||
|
std::string input; |
||||||
|
while (readline.get(input)) { |
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
print("{}\n", rep(input, blaze::s_outer_env)); |
||||||
|
} |
||||||
|
|
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Added to keep the linker happy at step A
|
||||||
|
namespace blaze { |
||||||
|
ValuePtr readline(const std::string&) { return nullptr; } |
||||||
|
} // namespace blaze
|
@ -0,0 +1,168 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <csignal> // std::signal |
||||||
|
#include <cstdlib> // std::exit |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "ruc/argparser.h" |
||||||
|
#include "ruc/format/color.h" |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
#include "environment.h" |
||||||
|
#include "error.h" |
||||||
|
#include "eval.h" |
||||||
|
#include "forward.h" |
||||||
|
#include "lexer.h" |
||||||
|
#include "printer.h" |
||||||
|
#include "reader.h" |
||||||
|
#include "readline.h" |
||||||
|
#include "settings.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
static EnvironmentPtr s_outer_env = Environment::create(); |
||||||
|
|
||||||
|
static auto cleanup(int signal) -> void |
||||||
|
{ |
||||||
|
print("\033[0m\n"); |
||||||
|
std::exit(signal); |
||||||
|
} |
||||||
|
|
||||||
|
auto read(std::string_view input) -> ValuePtr |
||||||
|
{ |
||||||
|
Lexer lexer(input); |
||||||
|
lexer.tokenize(); |
||||||
|
if (Settings::the().get("dump-lexer") == "1") { |
||||||
|
lexer.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
Reader reader(std::move(lexer.tokens())); |
||||||
|
reader.read(); |
||||||
|
if (Settings::the().get("dump-reader") == "1") { |
||||||
|
reader.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
return reader.node(); |
||||||
|
} |
||||||
|
|
||||||
|
auto eval(ValuePtr ast, EnvironmentPtr env) -> ValuePtr |
||||||
|
{ |
||||||
|
if (env == nullptr) { |
||||||
|
env = s_outer_env; |
||||||
|
} |
||||||
|
|
||||||
|
Eval eval(ast, env); |
||||||
|
eval.eval(); |
||||||
|
|
||||||
|
return eval.ast(); |
||||||
|
} |
||||||
|
|
||||||
|
static auto print(ValuePtr exp) -> std::string |
||||||
|
{ |
||||||
|
Printer printer; |
||||||
|
|
||||||
|
return printer.print(exp, true); |
||||||
|
} |
||||||
|
|
||||||
|
static auto rep(std::string_view input, EnvironmentPtr env) -> std::string |
||||||
|
{ |
||||||
|
Error::the().clearErrors(); |
||||||
|
Error::the().setInput(input); |
||||||
|
|
||||||
|
return print(eval(read(input), env)); |
||||||
|
} |
||||||
|
|
||||||
|
static std::string_view lambdaTable[] = { |
||||||
|
"(def! not (fn* (cond) (if cond false true)))", |
||||||
|
"(def! load-file (fn* (filename) \
|
||||||
|
(eval (read-string (str \"(do \" (slurp filename) \"\nnil)\")))))", |
||||||
|
"(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)))))))", |
||||||
|
}; |
||||||
|
|
||||||
|
static auto installLambdas(EnvironmentPtr env) -> void |
||||||
|
{ |
||||||
|
for (auto function : lambdaTable) { |
||||||
|
rep(function, env); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static auto makeArgv(EnvironmentPtr env, std::vector<std::string> arguments) -> void |
||||||
|
{ |
||||||
|
size_t count = arguments.size(); |
||||||
|
auto nodes = ValueVector(); |
||||||
|
for (size_t i = 1; i < count; ++i) { |
||||||
|
nodes.push_back(makePtr<String>(arguments[i])); |
||||||
|
} |
||||||
|
env->set("*ARGV*", makePtr<List>(nodes)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
||||||
|
|
||||||
|
auto main(int argc, char* argv[]) -> int |
||||||
|
{ |
||||||
|
bool dump_lexer = false; |
||||||
|
bool dump_reader = false; |
||||||
|
bool pretty_print = false; |
||||||
|
std::string_view history_path = "~/.blaze-history"; |
||||||
|
std::vector<std::string> arguments; |
||||||
|
|
||||||
|
// CLI arguments
|
||||||
|
ruc::ArgParser arg_parser; |
||||||
|
arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr); |
||||||
|
arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); |
||||||
|
arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); |
||||||
|
arg_parser.addOption(history_path, 'h', "history-path", nullptr, nullptr, nullptr, ruc::ArgParser::Required::Yes); |
||||||
|
arg_parser.addArgument(arguments, "arguments", nullptr, nullptr, ruc::ArgParser::Required::No); |
||||||
|
arg_parser.parse(argc, argv); |
||||||
|
|
||||||
|
// Set settings
|
||||||
|
blaze::Settings::the().set("dump-lexer", dump_lexer ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("dump-reader", dump_reader ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("pretty-print", pretty_print ? "1" : "0"); |
||||||
|
|
||||||
|
// Signal callbacks
|
||||||
|
std::signal(SIGINT, blaze::cleanup); |
||||||
|
std::signal(SIGTERM, blaze::cleanup); |
||||||
|
|
||||||
|
installFunctions(blaze::s_outer_env); |
||||||
|
installLambdas(blaze::s_outer_env); |
||||||
|
makeArgv(blaze::s_outer_env, arguments); |
||||||
|
|
||||||
|
if (arguments.size() > 0) { |
||||||
|
rep(format("(load-file \"{}\")", arguments.front()), blaze::s_outer_env); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
blaze::Readline readline(pretty_print, history_path); |
||||||
|
|
||||||
|
std::string input; |
||||||
|
while (readline.get(input)) { |
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
print("{}\n", rep(input, blaze::s_outer_env)); |
||||||
|
} |
||||||
|
|
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Added to keep the linker happy at step A
|
||||||
|
namespace blaze { |
||||||
|
ValuePtr readline(const std::string&) { return nullptr; } |
||||||
|
} // namespace blaze
|
@ -0,0 +1,171 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <csignal> // std::signal |
||||||
|
#include <cstdlib> // std::exit |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "ruc/argparser.h" |
||||||
|
#include "ruc/format/color.h" |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
#include "environment.h" |
||||||
|
#include "error.h" |
||||||
|
#include "eval.h" |
||||||
|
#include "forward.h" |
||||||
|
#include "lexer.h" |
||||||
|
#include "printer.h" |
||||||
|
#include "reader.h" |
||||||
|
#include "readline.h" |
||||||
|
#include "settings.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
static EnvironmentPtr s_outer_env = Environment::create(); |
||||||
|
|
||||||
|
static auto cleanup(int signal) -> void |
||||||
|
{ |
||||||
|
print("\033[0m\n"); |
||||||
|
std::exit(signal); |
||||||
|
} |
||||||
|
|
||||||
|
auto read(std::string_view input) -> ValuePtr |
||||||
|
{ |
||||||
|
Lexer lexer(input); |
||||||
|
lexer.tokenize(); |
||||||
|
if (Settings::the().get("dump-lexer") == "1") { |
||||||
|
lexer.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
Reader reader(std::move(lexer.tokens())); |
||||||
|
reader.read(); |
||||||
|
if (Settings::the().get("dump-reader") == "1") { |
||||||
|
reader.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
return reader.node(); |
||||||
|
} |
||||||
|
|
||||||
|
auto eval(ValuePtr ast, EnvironmentPtr env) -> ValuePtr |
||||||
|
{ |
||||||
|
if (env == nullptr) { |
||||||
|
env = s_outer_env; |
||||||
|
} |
||||||
|
|
||||||
|
Eval eval(ast, env); |
||||||
|
eval.eval(); |
||||||
|
|
||||||
|
return eval.ast(); |
||||||
|
} |
||||||
|
|
||||||
|
static auto print(ValuePtr exp) -> std::string |
||||||
|
{ |
||||||
|
Printer printer; |
||||||
|
|
||||||
|
return printer.print(exp, true); |
||||||
|
} |
||||||
|
|
||||||
|
static auto rep(std::string_view input, EnvironmentPtr env) -> std::string |
||||||
|
{ |
||||||
|
Error::the().clearErrors(); |
||||||
|
Error::the().setInput(input); |
||||||
|
|
||||||
|
return print(eval(read(input), env)); |
||||||
|
} |
||||||
|
|
||||||
|
static std::string_view lambdaTable[] = { |
||||||
|
"(def! not (fn* (cond) (if cond false true)))", |
||||||
|
"(def! load-file (fn* (filename) \
|
||||||
|
(eval (read-string (str \"(do \" (slurp filename) \"\nnil)\")))))", |
||||||
|
"(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)))))))", |
||||||
|
}; |
||||||
|
|
||||||
|
static auto installLambdas(EnvironmentPtr env) -> void |
||||||
|
{ |
||||||
|
for (auto function : lambdaTable) { |
||||||
|
rep(function, env); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static auto makeArgv(EnvironmentPtr env, std::vector<std::string> arguments) -> void |
||||||
|
{ |
||||||
|
size_t count = arguments.size(); |
||||||
|
auto nodes = ValueVector(); |
||||||
|
for (size_t i = 1; i < count; ++i) { |
||||||
|
nodes.push_back(makePtr<String>(arguments[i])); |
||||||
|
} |
||||||
|
env->set("*ARGV*", makePtr<List>(nodes)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
||||||
|
|
||||||
|
auto main(int argc, char* argv[]) -> int |
||||||
|
{ |
||||||
|
bool dump_lexer = false; |
||||||
|
bool dump_reader = false; |
||||||
|
bool pretty_print = false; |
||||||
|
std::string_view history_path = "~/.blaze-history"; |
||||||
|
std::vector<std::string> arguments; |
||||||
|
|
||||||
|
// CLI arguments
|
||||||
|
ruc::ArgParser arg_parser; |
||||||
|
arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr); |
||||||
|
arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); |
||||||
|
arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); |
||||||
|
arg_parser.addOption(history_path, 'h', "history-path", nullptr, nullptr, nullptr, ruc::ArgParser::Required::Yes); |
||||||
|
arg_parser.addArgument(arguments, "arguments", nullptr, nullptr, ruc::ArgParser::Required::No); |
||||||
|
arg_parser.parse(argc, argv); |
||||||
|
|
||||||
|
// Set settings
|
||||||
|
blaze::Settings::the().set("dump-lexer", dump_lexer ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("dump-reader", dump_reader ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("pretty-print", pretty_print ? "1" : "0"); |
||||||
|
|
||||||
|
// Signal callbacks
|
||||||
|
std::signal(SIGINT, blaze::cleanup); |
||||||
|
std::signal(SIGTERM, blaze::cleanup); |
||||||
|
|
||||||
|
installFunctions(blaze::s_outer_env); |
||||||
|
installLambdas(blaze::s_outer_env); |
||||||
|
makeArgv(blaze::s_outer_env, arguments); |
||||||
|
|
||||||
|
if (arguments.size() > 0) { |
||||||
|
rep(format("(load-file \"{}\")", arguments.front()), blaze::s_outer_env); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
blaze::Readline readline(pretty_print, history_path); |
||||||
|
|
||||||
|
std::string input; |
||||||
|
while (readline.get(input)) { |
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
std::string output = rep(input, blaze::s_outer_env); |
||||||
|
if (output.length() > 0) { |
||||||
|
print("{}\n", output); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Added to keep the linker happy at step A
|
||||||
|
namespace blaze { |
||||||
|
ValuePtr readline(const std::string&) { return nullptr; } |
||||||
|
} // namespace blaze
|
@ -0,0 +1,178 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <csignal> // std::signal |
||||||
|
#include <cstdlib> // std::exit |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "ruc/argparser.h" |
||||||
|
#include "ruc/format/color.h" |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
#include "environment.h" |
||||||
|
#include "error.h" |
||||||
|
#include "eval.h" |
||||||
|
#include "forward.h" |
||||||
|
#include "lexer.h" |
||||||
|
#include "printer.h" |
||||||
|
#include "reader.h" |
||||||
|
#include "readline.h" |
||||||
|
#include "settings.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
static blaze::Readline s_readline; |
||||||
|
static EnvironmentPtr s_outer_env = Environment::create(); |
||||||
|
|
||||||
|
static auto cleanup(int signal) -> void |
||||||
|
{ |
||||||
|
print("\033[0m\n"); |
||||||
|
std::exit(signal); |
||||||
|
} |
||||||
|
|
||||||
|
auto readline(const std::string& prompt) -> ValuePtr |
||||||
|
{ |
||||||
|
std::string input; |
||||||
|
if (s_readline.get(input, s_readline.createPrompt(prompt))) { |
||||||
|
return makePtr<String>(input); |
||||||
|
} |
||||||
|
|
||||||
|
return makePtr<Constant>(); |
||||||
|
} |
||||||
|
|
||||||
|
auto read(std::string_view input) -> ValuePtr |
||||||
|
{ |
||||||
|
Lexer lexer(input); |
||||||
|
lexer.tokenize(); |
||||||
|
if (Settings::the().get("dump-lexer") == "1") { |
||||||
|
lexer.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
Reader reader(std::move(lexer.tokens())); |
||||||
|
reader.read(); |
||||||
|
if (Settings::the().get("dump-reader") == "1") { |
||||||
|
reader.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
return reader.node(); |
||||||
|
} |
||||||
|
|
||||||
|
auto eval(ValuePtr ast, EnvironmentPtr env) -> ValuePtr |
||||||
|
{ |
||||||
|
if (env == nullptr) { |
||||||
|
env = s_outer_env; |
||||||
|
} |
||||||
|
|
||||||
|
Eval eval(ast, env); |
||||||
|
eval.eval(); |
||||||
|
|
||||||
|
return eval.ast(); |
||||||
|
} |
||||||
|
|
||||||
|
static auto print(ValuePtr exp) -> std::string |
||||||
|
{ |
||||||
|
Printer printer; |
||||||
|
|
||||||
|
return printer.print(exp, true); |
||||||
|
} |
||||||
|
|
||||||
|
static auto rep(std::string_view input, EnvironmentPtr env) -> std::string |
||||||
|
{ |
||||||
|
Error::the().clearErrors(); |
||||||
|
Error::the().setInput(input); |
||||||
|
|
||||||
|
return print(eval(read(input), env)); |
||||||
|
} |
||||||
|
|
||||||
|
static std::string_view lambdaTable[] = { |
||||||
|
"(def! not (fn* (cond) (if cond false true)))", |
||||||
|
"(def! load-file (fn* (filename) \
|
||||||
|
(eval (read-string (str \"(do \" (slurp filename) \"\nnil)\")))))", |
||||||
|
"(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)))))))", |
||||||
|
"(def! *host-language* \"C++\")", |
||||||
|
}; |
||||||
|
|
||||||
|
static auto installLambdas(EnvironmentPtr env) -> void |
||||||
|
{ |
||||||
|
for (auto function : lambdaTable) { |
||||||
|
rep(function, env); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static auto makeArgv(EnvironmentPtr env, std::vector<std::string> arguments) -> void |
||||||
|
{ |
||||||
|
size_t count = arguments.size(); |
||||||
|
auto nodes = ValueVector(); |
||||||
|
for (size_t i = 1; i < count; ++i) { |
||||||
|
nodes.push_back(makePtr<String>(arguments[i])); |
||||||
|
} |
||||||
|
env->set("*ARGV*", makePtr<List>(nodes)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
||||||
|
|
||||||
|
auto main(int argc, char* argv[]) -> int |
||||||
|
{ |
||||||
|
bool dump_lexer = false; |
||||||
|
bool dump_reader = false; |
||||||
|
bool pretty_print = false; |
||||||
|
std::string_view history_path = "~/.blaze-history"; |
||||||
|
std::vector<std::string> arguments; |
||||||
|
|
||||||
|
// CLI arguments
|
||||||
|
ruc::ArgParser arg_parser; |
||||||
|
arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr); |
||||||
|
arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); |
||||||
|
arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); |
||||||
|
arg_parser.addOption(history_path, 'h', "history-path", nullptr, nullptr, nullptr, ruc::ArgParser::Required::Yes); |
||||||
|
// TODO: Add overload for addArgument(std::vector<std::string_view>)
|
||||||
|
arg_parser.addArgument(arguments, "arguments", nullptr, nullptr, ruc::ArgParser::Required::No); |
||||||
|
arg_parser.parse(argc, argv); |
||||||
|
|
||||||
|
// Set settings
|
||||||
|
blaze::Settings::the().set("dump-lexer", dump_lexer ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("dump-reader", dump_reader ? "1" : "0"); |
||||||
|
blaze::Settings::the().set("pretty-print", pretty_print ? "1" : "0"); |
||||||
|
|
||||||
|
// Signal callbacks
|
||||||
|
std::signal(SIGINT, blaze::cleanup); |
||||||
|
std::signal(SIGTERM, blaze::cleanup); |
||||||
|
|
||||||
|
installFunctions(blaze::s_outer_env); |
||||||
|
installLambdas(blaze::s_outer_env); |
||||||
|
makeArgv(blaze::s_outer_env, arguments); |
||||||
|
|
||||||
|
if (arguments.size() > 0) { |
||||||
|
rep(format("(load-file \"{}\")", arguments.front()), blaze::s_outer_env); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
rep("(println (str \"Mal [\" *host-language* \"]\"))", blaze::s_outer_env); |
||||||
|
|
||||||
|
blaze::s_readline = blaze::Readline(pretty_print, history_path); |
||||||
|
|
||||||
|
std::string input; |
||||||
|
while (blaze::s_readline.get(input)) { |
||||||
|
std::string output = rep(input, blaze::s_outer_env); |
||||||
|
if (output.length() > 0) { |
||||||
|
print("{}\n", output); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (pretty_print) { |
||||||
|
print("\033[0m"); |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
Loading…
Reference in new issue