Compare commits

...

21 Commits

Author SHA1 Message Date
Riyyi 11f0553b5a AST+Env+Printer+Reader: Implement floating point numbers 5 months ago
Riyyi 705e80ad6b Everywhere: Split REPL from main(), put settings in environment 5 months ago
Riyyi 4cc2acc8a0 Printer: Rename node -> value 5 months ago
Riyyi f5dc1168eb Reader+Env: Add dump function 5 months ago
Riyyi 536e55e75a Everywhere: Add docstring support 5 months ago
Riyyi 25871cd5d5 Print: Make strings brightgreen color 6 months ago
Riyyi 929fb5d645 Env+Eval: Tweak (and) and (-) 6 months ago
Riyyi 9db041946e Env: Add string/number conversion functions 6 months ago
Riyyi b74f3448b2 Eval: Allow multiple s-expr in fn* 6 months ago
Riyyi b727f7147e Eval: Add and/or special forms 6 months ago
Riyyi c6c6d69e73 Eval: Allow multiple s-expr in try* 6 months ago
Riyyi bb6f3e7496 Meta: Change README.org title and description 6 months ago
Riyyi 1915621427 Eval: Change (macroexpand) into (macroexpand-1) 6 months ago
Riyyi 9895195410 Env: Add support to (count) for HashMap type 8 months ago
Riyyi e8206d762c Env: Load lisp code at runtime from files 8 months ago
Riyyi b65482eb68 Env: Allow load order control for native functions 8 months ago
Riyyi 67b982fd4c Eval: Merge eval, eval-ast and macroexpand 8 months ago
Riyyi d3a50abfbc Env: Organize functions better by splitting into multiple files 8 months ago
Riyyi 80b25f8c21 Eval: Add special form while 8 months ago
Riyyi 0d43512ea9 Everywhere: Do less Collection nodes copying 8 months ago
Riyyi fa4bd63dca Main: Remove step mains 8 months ago
  1. 34
      CMakeLists.txt
  2. 4
      README.org
  3. 1
      cmake/copy-lisp.cmake
  4. 12
      lisp/compare.bl
  5. 14
      lisp/init.bl
  6. 12
      lisp/load.bl
  7. 6
      lisp/other.bl
  8. 8
      lisp/predicate.bl
  9. 45
      src/ast.cpp
  10. 59
      src/ast.h
  11. 174
      src/env/environment.cpp
  12. 68
      src/env/environment.h
  13. 185
      src/env/functions/collection-access.cpp
  14. 132
      src/env/functions/collection-constructor.cpp
  15. 287
      src/env/functions/collection-modify.cpp
  16. 166
      src/env/functions/compare.cpp
  17. 149
      src/env/functions/convert.cpp
  18. 75
      src/env/functions/format.cpp
  19. 60
      src/env/functions/meta.cpp
  20. 93
      src/env/functions/mutable.cpp
  21. 166
      src/env/functions/operators.cpp
  22. 51
      src/env/functions/other.cpp
  23. 143
      src/env/functions/predicate.cpp
  24. 75
      src/env/functions/repl.cpp
  25. 18
      src/env/macro.h
  26. 102
      src/environment.cpp
  27. 37
      src/environment.h
  28. 420
      src/eval-special-form.cpp
  29. 214
      src/eval.cpp
  30. 32
      src/eval.h
  31. 12
      src/forward.h
  32. 992
      src/functions.cpp
  33. 22
      src/macro.h
  34. 84
      src/main.cpp
  35. 87
      src/printer.cpp
  36. 6
      src/printer.h
  37. 160
      src/reader.cpp
  38. 5
      src/reader.h
  39. 100
      src/repl.cpp
  40. 29
      src/repl.h
  41. 13
      src/settings.cpp
  42. 8
      src/settings.h
  43. 62
      src/step0_repl.cpp
  44. 114
      src/step1_read_print.cpp
  45. 118
      src/step2_eval.cpp
  46. 118
      src/step3_env.cpp
  47. 135
      src/step4_if_fn_do.cpp
  48. 141
      src/step5_tco.cpp
  49. 161
      src/step6_file.cpp
  50. 161
      src/step7_quote.cpp
  51. 168
      src/step8_macros.cpp
  52. 171
      src/step9_try.cpp
  53. 178
      src/stepA_mal.cpp
  54. 2
      vendor/ruc

34
CMakeLists.txt

@ -2,7 +2,7 @@
# User config between these lines
# Set project name
set(PROJECT "stepA_mal")
set(PROJECT "blaze")
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(BLAZE_STANDALONE TRUE)
@ -64,26 +64,18 @@ add_subdirectory("vendor/ruc")
# Define source files
file(GLOB_RECURSE PROJECT_SOURCES "src/*.cpp")
file(GLOB_RECURSE EXCLUDED_SOURCES "src/step*.cpp")
list(REMOVE_ITEM PROJECT_SOURCES ${EXCLUDED_SOURCES})
function(add_step TARGET_NAME MAIN_PATH)
add_executable(${TARGET_NAME} ${PROJECT_SOURCES} ${MAIN_PATH})
target_include_directories(${TARGET_NAME} PRIVATE "src")
target_link_libraries(${TARGET_NAME} readline ruc)
endfunction()
add_executable(${PROJECT} ${PROJECT_SOURCES})
target_include_directories(${PROJECT} PRIVATE "src")
target_link_libraries(${PROJECT} readline ruc)
# ------------------------------------------
# Std target
add_step(step0_repl "src/step0_repl.cpp")
add_step(step1_read_print "src/step1_read_print.cpp")
add_step(step2_eval "src/step2_eval.cpp")
add_step(step3_env "src/step3_env.cpp")
add_step(step4_if_fn_do "src/step4_if_fn_do.cpp")
add_step(step5_tco "src/step5_tco.cpp")
add_step(step6_file "src/step6_file.cpp")
add_step(step7_quote "src/step7_quote.cpp")
add_step(step8_macros "src/step8_macros.cpp")
add_step(step9_try "src/step9_try.cpp")
add_step(stepA_mal "src/stepA_mal.cpp")
add_custom_target(${PROJECT}-lisp
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/copy-lisp.cmake
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_dependencies(${PROJECT} ${PROJECT}-lisp)
# ------------------------------------------
# Execute target
@ -97,8 +89,8 @@ add_custom_target(run
function(make_test_target target_name step_name)
add_custom_target(${target_name}
COMMAND ../vendor/mal/runtest.py --deferrable --optional ../tests/${step_name}.mal -- ./${step_name})
add_dependencies(${target_name} ${step_name})
COMMAND ../vendor/mal/runtest.py --deferrable --optional ../tests/${step_name}.mal -- ./${PROJECT})
add_dependencies(${target_name} ${PROJECT})
endfunction()
make_test_target("test0" "step0_repl")

4
README.org

@ -1,9 +1,9 @@
#+TITLE: mal - Make a Lisp
#+TITLE: blaze lisp
#+AUTHOR: Riyyi
#+LANGUAGE: en
#+OPTIONS: toc:nil
This is an implementation of the [[https://github.com/kanaka/mal][Make A Lisp]] project, done in C++20.
blaze lisp, written in C++20.
** Usage

1
cmake/copy-lisp.cmake

@ -0,0 +1 @@
file(COPY ${CMAKE_CURRENT_LIST_DIR}/../lisp DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

12
lisp/compare.bl

@ -0,0 +1,12 @@
(defmacro! cond (fn* [& xs]
(if (> (count xs) 0)
(list 'if (first xs)
(if (> (count xs) 1)
(nth xs 1)
(throw "odd number of forms to cond"))
(cons 'cond (rest (rest xs)))))))
;; Local Variables:
;; eval: (emacs-lisp-mode)
;; End:

14
lisp/init.bl

@ -0,0 +1,14 @@
(defmacro! defmacro
(fn* [name args & body]
`(defmacro! ~name (fn* ~args ~@body))))
(defmacro defn [name args & body]
`(def! ~name (fn* ~args ~@body)))
(defmacro def [name & body]
`(def! ~name ~@body))
;; Local Variables:
;; eval: (emacs-lisp-mode)
;; End:

12
lisp/load.bl

@ -0,0 +1,12 @@
(defn load-file [file]
"Load the Lisp file named FILE."
(eval (read-string (str "(do " (slurp file) "\nnil)"))))
(defn load [file]
"Load the Lisp file named FILE."
(eval (read-string (str "(let* [] (do " (slurp file) "))"))))
;; Local Variables:
;; eval: (emacs-lisp-mode)
;; End:

6
lisp/other.bl

@ -0,0 +1,6 @@
(def *host-language* "C++")
;; Local Variables:
;; eval: (emacs-lisp-mode)
;; End:

8
lisp/predicate.bl

@ -0,0 +1,8 @@
(defn not [object]
"Return true if OBJECT is nil or false, and return false otherwise."
(if object false true))
;; Local Variables:
;; eval: (emacs-lisp-mode)
;; End:

45
src/ast.cpp

@ -7,10 +7,11 @@
#include <cstdint> // int64_t
#include <memory> // std::static_pointer_cast
#include <string>
#include <utility> // std::move
#include <vector>
#include "ast.h"
#include "environment.h"
#include "env/environment.h"
#include "error.h"
#include "forward.h"
#include "printer.h"
@ -35,6 +36,11 @@ Collection::Collection(const ValueVector& nodes)
{
}
Collection::Collection(ValueVector&& nodes) noexcept
: m_nodes(std::move(nodes))
{
}
Collection::Collection(ValueVectorIt begin, ValueVectorIt end)
: m_nodes(ValueVector(begin, end))
{
@ -64,6 +70,11 @@ List::List(const ValueVector& nodes)
{
}
List::List(ValueVector&& nodes) noexcept
: Collection(std::move(nodes))
{
}
List::List(ValueVectorIt begin, ValueVectorIt end)
: Collection(begin, end)
{
@ -86,6 +97,11 @@ Vector::Vector(const ValueVector& nodes)
{
}
Vector::Vector(ValueVector&& nodes) noexcept
: Collection(std::move(nodes))
{
}
Vector::Vector(ValueVectorIt begin, ValueVectorIt end)
: Collection(begin, end)
{
@ -169,10 +185,22 @@ Keyword::Keyword(const std::string& data)
{
}
Keyword::Keyword(int64_t number)
: m_data(std::string(1, 0x7f) + std::to_string(number)) // 127
{
}
// -----------------------------------------
Number::Number(int64_t number)
: m_number(number)
: Numeric()
, m_number(number)
{
}
Decimal::Decimal(double decimal)
: Numeric()
, m_decimal(decimal)
{
}
@ -204,8 +232,11 @@ Callable::Callable(ValuePtr meta)
// -----------------------------------------
Function::Function(const std::string& name, FunctionType function)
: m_name(name)
Function::Function(std::string_view name, std::string_view bindings, std::string_view documentation, FunctionType function)
: Callable()
, m_name(name)
, m_bindings(bindings)
, m_documentation(documentation)
, m_function(function)
{
}
@ -220,14 +251,16 @@ Function::Function(const Function& that, ValuePtr meta)
// -----------------------------------------
Lambda::Lambda(const std::vector<std::string>& bindings, ValuePtr body, EnvironmentPtr env)
: m_bindings(bindings)
: Callable()
, m_bindings(bindings)
, m_body(body)
, m_env(env)
{
}
Lambda::Lambda(const Lambda& that)
: m_bindings(that.m_bindings)
: Callable()
, m_bindings(that.m_bindings)
, m_body(that.m_body)
, m_env(that.m_env)
{

59
src/ast.h

@ -52,7 +52,9 @@ public:
virtual bool isHashMap() const { return false; }
virtual bool isString() const { return false; }
virtual bool isKeyword() const { return false; }
virtual bool isNumeric() const { return false; }
virtual bool isNumber() const { return false; }
virtual bool isDecimal() const { return false; }
virtual bool isConstant() const { return false; }
virtual bool isSymbol() const { return false; }
virtual bool isCallable() const { return false; }
@ -101,11 +103,18 @@ public:
ValuePtr front() const { return m_nodes.front(); }
ValueVector rest() const;
const ValueVector& nodes() const { return m_nodes; }
ValueVectorConstIt begin() const { return m_nodes.cbegin(); }
ValueVectorConstIt end() const { return m_nodes.cend(); }
ValueVectorConstReverseIt beginReverse() const { return m_nodes.crbegin(); }
ValueVectorConstReverseIt endReverse() const { return m_nodes.crend(); }
const ValueVector& nodesCopy() const { return m_nodes; }
std::span<const ValuePtr> nodesRead() const { return m_nodes; }
protected:
Collection() = default;
Collection(const ValueVector& nodes);
Collection(ValueVector&& nodes) noexcept;
Collection(ValueVectorIt begin, ValueVectorIt end);
Collection(ValueVectorConstIt begin, ValueVectorConstIt end);
Collection(const Collection& that, ValuePtr meta);
@ -129,6 +138,7 @@ class List final : public Collection {
public:
List() = default;
List(const ValueVector& nodes);
List(ValueVector&& nodes) noexcept;
List(ValueVectorIt begin, ValueVectorIt end);
List(ValueVectorConstIt begin, ValueVectorConstIt end);
List(const List& that, ValuePtr meta);
@ -154,6 +164,7 @@ class Vector final : public Collection {
public:
Vector() = default;
Vector(const ValueVector& nodes);
Vector(ValueVector&& nodes) noexcept;
Vector(ValueVectorIt begin, ValueVectorIt end);
Vector(ValueVectorConstIt begin, ValueVectorConstIt end);
Vector(const Vector& that, ValuePtr meta);
@ -229,6 +240,7 @@ private:
class Keyword final : public Value {
public:
Keyword(const std::string& data);
Keyword(int64_t number);
virtual ~Keyword() = default;
virtual bool isKeyword() const override { return true; }
@ -242,8 +254,19 @@ private:
};
// -----------------------------------------
class Numeric : public Value {
public:
virtual ~Numeric() = default;
protected:
Numeric() = default;
virtual bool isNumeric() const override { return true; }
};
// 123
class Number final : public Value {
class Number final : public Numeric {
public:
Number(int64_t number);
virtual ~Number() = default;
@ -258,6 +281,22 @@ private:
const int64_t m_number { 0 };
};
// 123.456
class Decimal final : public Numeric {
public:
Decimal(double decimal);
virtual ~Decimal() = default;
double decimal() const { return m_decimal; }
WITH_NO_META();
private:
virtual bool isDecimal() const override { return true; }
const double m_decimal { 0 };
};
// -----------------------------------------
// true, false, nil
@ -322,11 +361,13 @@ using FunctionType = std::function<ValuePtr(ValueVectorConstIt, ValueVectorConst
class Function final : public Callable {
public:
Function(const std::string& name, FunctionType function);
Function(std::string_view name, std::string_view bindings, std::string_view documentation, FunctionType function);
Function(const Function& that, ValuePtr meta);
virtual ~Function() = default;
const std::string& name() const { return m_name; }
std::string_view name() const { return m_name; }
std::string_view bindings() const { return m_bindings; }
std::string_view documentation() const { return m_documentation; }
FunctionType function() const { return m_function; }
WITH_META(Function);
@ -334,7 +375,9 @@ public:
private:
virtual bool isFunction() const override { return true; }
const std::string m_name;
std::string_view m_name;
std::string_view m_bindings;
std::string_view m_documentation;
const FunctionType m_function;
};
@ -414,9 +457,15 @@ inline bool Value::fastIs<String>() const { return isString(); }
template<>
inline bool Value::fastIs<Keyword>() const { return isKeyword(); }
template<>
inline bool Value::fastIs<Numeric>() const { return isNumeric(); }
template<>
inline bool Value::fastIs<Number>() const { return isNumber(); }
template<>
inline bool Value::fastIs<Decimal>() const { return isDecimal(); }
template<>
inline bool Value::fastIs<Constant>() const { return isConstant(); }

174
src/env/environment.cpp vendored

@ -0,0 +1,174 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <filesystem>
#include <iterator> // std::distance
#include <memory> // std::static_pointer_cast
#include "ruc/file.h"
#include "ruc/format/format.h"
#include "ast.h"
#include "env/environment.h"
#include "error.h"
#include "forward.h"
#include "repl.h"
namespace blaze {
std::vector<FunctionParts> Environment::s_function_parts;
std::vector<std::string> Environment::s_lambdas;
EnvironmentPtr Environment::create()
{
return std::shared_ptr<Environment>(new Environment);
}
EnvironmentPtr Environment::create(EnvironmentPtr outer)
{
auto env = create();
env->m_outer = outer;
return env;
}
EnvironmentPtr Environment::create(const ValuePtr lambda, ValueVector&& arguments)
{
auto lambda_casted = std::static_pointer_cast<Lambda>(lambda);
auto env = create(lambda_casted->env());
auto bindings = lambda_casted->bindings();
auto it = arguments.begin();
for (size_t i = 0; i < bindings.size(); ++i, ++it) {
if (bindings[i] == "&") {
if (i + 2 != bindings.size()) {
Error::the().add(::format("invalid function: {}", lambda));
return nullptr;
}
auto nodes = ValueVector();
for (; it != arguments.end(); ++it) {
nodes.push_back(*it);
}
env->set(bindings[i + 1], makePtr<List>(nodes));
return env;
}
if (it == arguments.end()) {
Error::the().add(::format("wrong number of arguments: {}, {}", lambda, arguments.size()));
return nullptr;
}
env->set(bindings[i], *it);
}
if (it != arguments.end()) {
Error::the().add(::format("wrong number of arguments: {}, {}", lambda, arguments.size()));
return nullptr;
}
return env;
}
// -----------------------------------------
void Environment::loadFunctions()
{
loadCollectionAccess();
loadCollectionConstructor();
loadCollectionModify();
loadCompare();
loadConvert();
loadFormat();
loadMeta();
loadMutable();
loadOperators();
loadOther();
loadPredicate();
loadRepl();
// Load std files
std::filesystem::path std = "./lisp";
if (!std::filesystem::exists(std) || !std::filesystem::is_directory(std)) {
return;
}
s_lambdas.reserve(std::distance(std::filesystem::directory_iterator(std), {}));
for (const auto& entry : std::filesystem::directory_iterator(std)) {
if (!std::filesystem::is_regular_file(entry.path())
|| entry.path().extension().string() != ".bl") {
continue;
}
std::filesystem::path filename = entry.path().filename();
ruc::File file((std / filename).string());
// The init will be added to the front and executed first
if (filename.string() == "init.bl") {
s_lambdas.emplace(s_lambdas.begin(), file.data());
}
else {
s_lambdas.push_back(file.data());
}
}
}
void Environment::registerFunction(FunctionParts function_parts)
{
s_function_parts.push_back(function_parts);
}
void Environment::installFunctions(EnvironmentPtr env)
{
for (const auto& function_parts : s_function_parts) {
env->set(std::string(function_parts.name),
makePtr<Function>(
function_parts.name,
function_parts.signature,
function_parts.documentation,
function_parts.function));
}
for (const auto& lambda : s_lambdas) {
// Ensure all s-exprs are run with (do)
Repl::eval(Repl::read("(do " + lambda + ")"), env);
}
}
// -----------------------------------------
bool Environment::exists(std::string_view symbol)
{
return m_values.find(std::string(symbol)) != m_values.end();
}
ValuePtr Environment::set(std::string_view symbol, ValuePtr value)
{
if (exists(symbol)) {
m_values.erase(std::string(symbol));
}
m_values.emplace(symbol, value);
return value;
}
ValuePtr Environment::get(std::string_view symbol)
{
if (exists(symbol)) {
return m_values[std::string(symbol)];
}
if (m_outer) {
return m_outer->get(symbol);
}
return nullptr;
}
} // namespace blaze

68
src/env/environment.h vendored

@ -0,0 +1,68 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <list>
#include <string>
#include <unordered_map>
#include <vector>
#include "ast.h"
#include "forward.h"
namespace blaze {
// All of these combined become a Function in the Environment
struct FunctionParts {
std::string_view name;
std::string_view signature;
std::string_view documentation;
FunctionType function;
};
class Environment {
public:
virtual ~Environment() = default;
// Factory functions instead of constructors because it can fail in the bindings/arguments case
static EnvironmentPtr create();
static EnvironmentPtr create(EnvironmentPtr outer);
static EnvironmentPtr create(const ValuePtr lambda, ValueVector&& arguments);
static void loadFunctions();
static void registerFunction(FunctionParts function_parts);
static void installFunctions(EnvironmentPtr env);
bool exists(std::string_view symbol);
ValuePtr set(std::string_view symbol, ValuePtr value);
ValuePtr get(std::string_view symbol);
private:
Environment() {}
// Outer environment native functions, "Core"
static void loadCollectionAccess();
static void loadCollectionConstructor();
static void loadCollectionModify();
static void loadCompare();
static void loadConvert();
static void loadFormat();
static void loadMeta();
static void loadMutable();
static void loadOperators();
static void loadOther();
static void loadPredicate();
static void loadRepl();
EnvironmentPtr m_outer { nullptr };
std::unordered_map<std::string, ValuePtr> m_values;
static std::vector<FunctionParts> s_function_parts;
static std::vector<std::string> s_lambdas;
};
} // namespace blaze

185
src/env/functions/collection-access.cpp vendored

@ -0,0 +1,185 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <memory> // std:static_pointer_cast
#include "ast.h"
#include "env/macro.h"
#include "forward.h"
#include "util.h"
namespace blaze {
void Environment::loadCollectionAccess()
{
// (count '(1 2 3)) -> 3
// (count [1 2 3]) -> 3
// (count {:foo 2 :bar 3}) -> 2
ADD_FUNCTION(
"count",
"",
"",
{
CHECK_ARG_COUNT_IS("count", SIZE(), 1);
size_t result = 0;
if (is<Constant>(begin->get()) && std::static_pointer_cast<Constant>(*begin)->state() == Constant::Nil) {
// result = 0
}
else if (is<Collection>(begin->get())) {
result = std::static_pointer_cast<Collection>(*begin)->size();
}
else if (is<HashMap>(begin->get())) {
result = std::static_pointer_cast<HashMap>(*begin)->size();
}
else {
Error::the().add(::format("wrong argument type: Collection, '{}'", *begin));
return nullptr;
}
// FIXME: Add numeric_limits check for implicit cast: size_t > int64_t
return makePtr<Number>((int64_t)result);
});
// -----------------------------------------
// (first (list 1 2 3)) -> 1
ADD_FUNCTION(
"first",
"",
"",
{
CHECK_ARG_COUNT_IS("first", SIZE(), 1);
if (is<Constant>(begin->get())
&& std::static_pointer_cast<Constant>(*begin)->state() == Constant::Nil) {
return makePtr<Constant>();
}
VALUE_CAST(collection, Collection, (*begin));
return (collection->empty()) ? makePtr<Constant>() : collection->front();
});
// (nth (list 1 2 3) 0) -> 1
ADD_FUNCTION(
"nth",
"",
"",
{
CHECK_ARG_COUNT_IS("nth", SIZE(), 2);
VALUE_CAST(collection, Collection, (*begin));
VALUE_CAST(number_node, Number, (*(begin + 1)));
auto collection_nodes = collection->nodesRead();
auto index = static_cast<size_t>(number_node->number());
if (number_node->number() < 0 || index >= collection_nodes.size()) {
Error::the().add("index is out of range");
return nullptr;
}
return collection_nodes[index];
});
// (rest (list 1 2 3)) -> (2 3)
ADD_FUNCTION(
"rest",
"",
"",
{
CHECK_ARG_COUNT_IS("rest", SIZE(), 1);
if (is<Constant>(begin->get())
&& std::static_pointer_cast<Constant>(*begin)->state() == Constant::Nil) {
return makePtr<List>();
}
VALUE_CAST(collection, Collection, (*begin));
return makePtr<List>(collection->rest());
});
// -----------------------------------------
// (get {:kw "value"} :kw) -> "value"
ADD_FUNCTION(
"get",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("get", SIZE(), 1);
if (is<Constant>(begin->get())
&& std::static_pointer_cast<Constant>(*begin)->state() == Constant::Nil) {
return makePtr<Constant>();
}
VALUE_CAST(hash_map, HashMap, (*begin));
begin++;
if (SIZE() == 0) {
return makePtr<Constant>();
}
auto result = hash_map->get(*begin);
return (result) ? result : makePtr<Constant>();
});
// (keys {"foo" 3 :bar 5}) -> ("foo" :bar)
ADD_FUNCTION(
"keys",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("keys", SIZE(), 1);
VALUE_CAST(hash_map, HashMap, (*begin));
size_t count = hash_map->size();
auto nodes = ValueVector(count);
size_t i = 0;
auto elements = hash_map->elements();
for (auto pair : elements) {
if (pair.first.front() == 0x7f) { // 127
nodes.at(i) = makePtr<Keyword>(pair.first.substr(1));
}
else {
nodes.at(i) = makePtr<String>(pair.first);
}
i++;
}
return makePtr<List>(nodes);
});
// (vals {"foo" 3 :bar 5}) -> (3 5)
ADD_FUNCTION(
"vals",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("vals", SIZE(), 1);
VALUE_CAST(hash_map, HashMap, (*begin));
size_t count = hash_map->size();
auto nodes = ValueVector(count);
size_t i = 0;
auto elements = hash_map->elements();
for (auto pair : elements) {
nodes.at(i) = pair.second;
i++;
}
return makePtr<List>(nodes);
});
}
} // namespace blaze

132
src/env/functions/collection-constructor.cpp vendored

@ -0,0 +1,132 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <memory> // std:static_pointer_cast
#include "ast.h"
#include "env/macro.h"
#include "forward.h"
#include "util.h"
namespace blaze {
void Environment::loadCollectionConstructor()
{
// (list 1 2) -> (1 2)
ADD_FUNCTION(
"list",
"",
"",
{
return makePtr<List>(begin, end);
});
// (make-list 4 nil) -> (nil nil nil nil)
ADD_FUNCTION(
"make-list",
"",
"",
{
CHECK_ARG_COUNT_IS("make-list", SIZE(), 2);
VALUE_CAST(number, Number, (*begin));
auto count = static_cast<size_t>(number->number() < 0 ? 0 : number->number());
auto value = *std::next(begin);
auto nodes = ValueVector(count);
if (is<Atom>(value.get())) {
auto atom = std::static_pointer_cast<Atom>(value);
for (size_t i = 0; i < count; ++i) {
nodes[i] = makePtr<Atom>(atom);
}
}
// else if (is<Collection>(value.get())) {
// for (size_t i = 0; i < count; ++i) {
// auto nodes = std::static_pointer_cast<Collection>(value)->nodesCopy();
// if (is<Vector>(value.get())) {
// makePtr<Vector>(nodes);
// continue;
// }
// nodes[i] = makePtr<List>(nodes);
// }
// }
// else if (is<Constant>(value.get())) {
// for (size_t i = 0; i < count; ++i) {
// auto constant = std::static_pointer_cast<Constant>(value);
// nodes[i] = makePtr<Constant>(constant);
// }
// }
// TODO:
// Atom
// Collection
// Constant
// Function
// HashMap
// Keyword
// Lambda
// List
// Macro
// Number
// String
// Symbol
// Vector
return makePtr<List>(std::move(nodes));
});
// -----------------------------------------
// (vec (list 1 2 3))
ADD_FUNCTION(
"vec",
"",
"",
{
CHECK_ARG_COUNT_IS("vec", SIZE(), 1);
if (is<Vector>(begin->get())) {
return *begin;
}
VALUE_CAST(collection, Collection, (*begin));
return makePtr<Vector>(collection->nodesCopy());
});
// (vector 1 2 3) -> [1 2 3]
ADD_FUNCTION(
"vector",
"",
"",
{
auto result = makePtr<Vector>();
return makePtr<Vector>(begin, end);
});
// -----------------------------------------
// (hash-map "foo" 5 :bar 10) -> {"foo" 5 :bar 10}
ADD_FUNCTION(
"hash-map",
"",
"",
{
CHECK_ARG_COUNT_EVEN("hash-map", SIZE());
Elements elements;
for (auto it = begin; it != end; std::advance(it, 2)) {
const ValuePtr& value = *(std::next(it)); // temporary instance to get around const
elements.insert_or_assign(HashMap::getKeyString(*it), value);
}
return makePtr<HashMap>(elements);
});
}
} // namespace blaze

287
src/env/functions/collection-modify.cpp vendored

@ -0,0 +1,287 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <algorithm> // std::copy, std::reverse_copy
#include <cstddef> // size_t
#include <memory> // std::static_pointer_cast
#include "ast.h"
#include "env/environment.h"
#include "env/macro.h"
#include "forward.h"
#include "repl.h"
#include "util.h"
namespace blaze {
void Environment::loadCollectionModify()
{
// (apply + 1 2 (list 3 4)) -> (+ 1 2 3 4) -> 10
ADD_FUNCTION(
"apply",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("apply", SIZE(), 2);
auto callable = *begin;
IS_VALUE(Callable, callable);
VALUE_CAST(collection, Collection, (*std::prev(end)));
auto arguments = ValueVector(begin + 1, end - 1);
arguments.reserve(arguments.size() + collection->size());
// Append list nodes to the argument leftovers
auto nodes = collection->nodesRead();
for (const auto& node : nodes) {
arguments.push_back(node);
}
ValuePtr value = nullptr;
if (is<Function>(callable.get())) {
auto function = std::static_pointer_cast<Function>(callable)->function();
value = function(arguments.begin(), arguments.end());
}
else {
auto lambda = std::static_pointer_cast<Lambda>(callable);
value = Repl::eval(lambda->body(), Environment::create(lambda, std::move(arguments)));
}
return value;
});
// (cons 1 (list 2 3))
ADD_FUNCTION(
"cons",
"",
"",
{
CHECK_ARG_COUNT_IS("cons", SIZE(), 2);
ValuePtr first = *begin;
begin++;
VALUE_CAST(collection, Collection, (*begin));
const auto& collection_nodes = collection->nodesRead();
auto result_nodes = ValueVector(collection_nodes.size() + 1);
result_nodes.at(0) = first;
std::copy(collection_nodes.begin(), collection_nodes.end(), result_nodes.begin() + 1);
return makePtr<List>(result_nodes);
});
// (concat (list 1) (list 2 3)) -> (1 2 3)
ADD_FUNCTION(
"concat",
"",
"",
{
size_t count = 0;
for (auto it = begin; it != end; ++it) {
VALUE_CAST(collection, Collection, (*it));
count += collection->size();
}
auto result_nodes = ValueVector(count);
size_t offset = 0;
for (auto it = begin; it != end; ++it) {
const auto& collection_nodes = std::static_pointer_cast<Collection>(*it)->nodesRead();
std::copy(collection_nodes.begin(), collection_nodes.end(), result_nodes.begin() + offset);
offset += collection_nodes.size();
}
return makePtr<List>(result_nodes);
});
// (conj '(1 2 3) 4 5 6) -> (6 5 4 1 2 3)
// (conj [1 2 3] 4 5 6) -> [1 2 3 4 5 6]
ADD_FUNCTION(
"conj",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("conj", SIZE(), 1);
VALUE_CAST(collection, Collection, (*begin));
begin++;
const auto& collection_nodes = collection->nodesRead();
size_t collection_count = collection_nodes.size();
size_t argument_count = SIZE();
auto nodes = ValueVector(argument_count + collection_count);
if (is<List>(collection.get())) {
std::reverse_copy(begin, end, nodes.begin());
std::copy(collection_nodes.begin(), collection_nodes.end(), nodes.begin() + argument_count);
return makePtr<List>(nodes);
}
std::copy(collection_nodes.begin(), collection_nodes.end(), nodes.begin());
std::copy(begin, end, nodes.begin() + collection_count);
return makePtr<Vector>(nodes);
});
// (map (fn* (x) (* x 2)) (list 1 2 3)) -> (2 4 6)
ADD_FUNCTION(
"map",
"",
"",
{
CHECK_ARG_COUNT_IS("map", SIZE(), 2);
VALUE_CAST(callable, Callable, (*begin));
VALUE_CAST(collection, Collection, (*(begin + 1)));
size_t count = collection->size();
auto nodes = ValueVector(count);
if (is<Function>(callable.get())) {
auto function = std::static_pointer_cast<Function>(callable)->function();
for (size_t i = 0; i < count; ++i) {
nodes.at(i) = function(collection->begin() + i, collection->begin() + i + 1);
}
}
else {
auto lambda = std::static_pointer_cast<Lambda>(callable);
auto collection_nodes = collection->nodesRead();
for (size_t i = 0; i < count; ++i) {
nodes.at(i) = (Repl::eval(lambda->body(), Environment::create(lambda, { collection_nodes[i] })));
}
}
return makePtr<List>(nodes);
});
// (set-nth (list 1 2 3) 1 "foo") -> (1 "foo" 3)
ADD_FUNCTION(
"set-nth",
"",
"",
{
CHECK_ARG_COUNT_IS("set-nth-element", SIZE(), 3);
VALUE_CAST(collection, Collection, (*begin));
VALUE_CAST(number_node, Number, (*(begin + 1)));
auto index = static_cast<size_t>(number_node->number() < 0 ? 0 : number_node->number());
auto value = *(begin + 2);
auto collection_nodes = collection->nodesCopy();
if (index >= collection->size()) { // Enlarge list if index out of bounds
collection_nodes.resize(index + 1, makePtr<Constant>());
}
collection_nodes[index] = value;
if (is<Vector>(begin->get())) {
return makePtr<Vector>(collection_nodes);
}
return makePtr<List>(collection_nodes);
});
// (seq '(1 2 3)) -> (1 2 3)
// (seq [1 2 3]) -> (1 2 3)
// (seq "foo") -> ("f" "o" "o")
ADD_FUNCTION(
"seq",
"",
"",
{
CHECK_ARG_COUNT_IS("seq", SIZE(), 1);
auto front = *begin;
Value* front_raw_ptr = front.get();
if (is<Constant>(front_raw_ptr) && std::static_pointer_cast<Constant>(front)->state() == Constant::Nil) {
return makePtr<Constant>();
}
if (is<Collection>(front_raw_ptr)) {
auto collection = std::static_pointer_cast<Collection>(front);
if (collection->empty()) {
return makePtr<Constant>();
}
if (is<List>(front_raw_ptr)) {
return front;
}
return makePtr<List>(collection->nodesCopy());
}
if (is<String>(front_raw_ptr)) {
auto string = std::static_pointer_cast<String>(front);
if (string->empty()) {
return makePtr<Constant>();
}
size_t count = string->size();
auto nodes = ValueVector(count);
const auto& data = string->data();
for (size_t i = 0; i < count; ++i) {
nodes.at(i) = makePtr<String>(data[i]);
}
return makePtr<List>(nodes);
}
Error::the().add(::format("wrong argument type: Collection or String, {}", front));
return nullptr;
});
// -----------------------------------------
// (assoc {:a 1 :b 2} :a 3 :c 1) -> {:a 3 :b 2 :c 1}
ADD_FUNCTION(
"assoc",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("assoc", SIZE(), 1);
VALUE_CAST(hash_map, HashMap, (*begin));
begin++;
CHECK_ARG_COUNT_EVEN("assoc", SIZE());
Elements elements(hash_map->elements());
for (auto it = begin; it != end; std::advance(it, 2)) {
const ValuePtr& value = *(std::next(it)); // temporary instance to get around const
elements.insert_or_assign(HashMap::getKeyString(*it), value);
}
return makePtr<HashMap>(elements);
});
// (dissoc {:a 1 :b 2 :c 3} :a :c :d) -> {:b 2}
ADD_FUNCTION(
"dissoc",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("dissoc", SIZE(), 1);
VALUE_CAST(hash_map, HashMap, (*begin));
begin++;
Elements elements(hash_map->elements());
for (auto it = begin; it != end; ++it) {
elements.erase(HashMap::getKeyString(*it));
}
return makePtr<HashMap>(elements);
});
}
} // namespace blaze

166
src/env/functions/compare.cpp vendored

@ -0,0 +1,166 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstdint> // int64_t
#include <functional> // std::function
#include <memory> // std::static_pointer_cast
#include "ast.h"
#include "env/macro.h"
#include "util.h"
namespace blaze {
void Environment::loadCompare()
{
#define NUMBER_COMPARE(operator) \
{ \
CHECK_ARG_COUNT_AT_LEAST(#operator, SIZE(), 2); \
\
bool result = true; \
\
int64_t number = 0; \
double decimal = 0; \
bool current_numeric_is_number = false; \
\
/* Start with the first number */ \
IS_VALUE(Numeric, (*begin)); \
if (is<Number>(begin->get())) { \
number = std::static_pointer_cast<Number>(*begin)->number(); \
current_numeric_is_number = true; \
} \
else { \
decimal = std::static_pointer_cast<Decimal>(*begin)->decimal(); \
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

149
src/env/functions/convert.cpp vendored

@ -0,0 +1,149 @@
/*
* 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

75
src/env/functions/format.cpp vendored

@ -0,0 +1,75 @@
/*
* 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

60
src/env/functions/meta.cpp vendored

@ -0,0 +1,60 @@
/*
* 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

93
src/env/functions/mutable.cpp vendored

@ -0,0 +1,93 @@
/*
* 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

166
src/env/functions/operators.cpp vendored

@ -0,0 +1,166 @@
/*
* 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

51
src/env/functions/other.cpp vendored

@ -0,0 +1,51 @@
/*
* 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

143
src/env/functions/predicate.cpp vendored

@ -0,0 +1,143 @@
/*
* 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

75
src/env/functions/repl.cpp vendored

@ -0,0 +1,75 @@
/*
* 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

18
src/env/macro.h vendored

@ -0,0 +1,18 @@
/*
* 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)

102
src/environment.cpp

@ -1,102 +0,0 @@
/*
* 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

37
src/environment.h

@ -1,37 +0,0 @@
/*
* 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

420
src/eval-special-form.cpp

@ -4,16 +4,26 @@
* SPDX-License-Identifier: MIT
*/
#include <iterator> // std::next, std::prev
#include <algorithm> // std::find_if, std::transform
#include <cctype> // std::toupper
#include <iterator> // std::distance, std::next, std::prev
#include <list>
#include <memory>
#include <span>
#include <string>
#include "ruc/format/color.h"
#include "ruc/format/format.h"
#include "ruc/format/print.h"
#include "ast.h"
#include "environment.h"
#include "env/environment.h"
#include "error.h"
#include "eval.h"
#include "forward.h"
#include "macro.h"
#include "printer.h"
#include "settings.h"
#include "types.h"
#include "util.h"
@ -21,7 +31,7 @@ namespace blaze {
static ValuePtr evalQuasiQuoteImpl(ValuePtr ast);
// (def! x 2)
EVAL_FUNCTION("def!", "symbol value", "Set SYMBOL to the value VALUE.");
ValuePtr Eval::evalDef(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_IS("def!", nodes.size(), 2);
@ -43,7 +53,13 @@ ValuePtr Eval::evalDef(const ValueVector& nodes, EnvironmentPtr env)
return env->set(symbol->symbol(), value);
}
// (defmacro! x (fn* (x) x))
EVAL_FUNCTION("defmacro!", "symbol function",
R"(Define SYMBOL as a macro.
When the macro is called, as in (NAME ARGS...),
the FUNCTION (fn* ARGLIST BODY...) is applied to
the list ARGS... as it appears in the expression,
and the result should be a form to be evaluated instead of the original.)");
ValuePtr Eval::evalDefMacro(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_IS("defmacro!", nodes.size(), 2);
@ -66,14 +82,146 @@ ValuePtr Eval::evalDefMacro(const ValueVector& nodes, EnvironmentPtr env)
return env->set(symbol->symbol(), makePtr<Macro>(*lambda));
}
// (fn* (x) x)
EVAL_FUNCTION("describe", "symbol", "Display the full documentation of SYMBOL.");
ValuePtr Eval::evalDescribe(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_IS("describe", nodes.size(), 1);
// First argument needs to be a Symbol
VALUE_CAST(symbol, Symbol, nodes.front());
auto symbol_string = symbol->symbol();
std::string type;
std::string signature;
std::string documentation;
std::string value_string;
bool pretty_print = Settings::the().getEnvBool("*PRETTY-PRINT*");
auto bold = fg(ruc::format::TerminalColor::None) | ruc::format::Emphasis::Bold;
auto describe = [&]() {
print("{} is a {}.\n\n", symbol_string, type);
if (!signature.empty()) {
pretty_print ? print(bold, "Signature\n") : print("Signature\n");
print("({})\n", signature);
}
if (!documentation.empty()) {
pretty_print ? print(bold, "\nDocumentation\n") : print("\nDocumentation\n");
print("{}\n", documentation);
}
if (!value_string.empty()) {
pretty_print ? print(bold, "Value\n") : print("Value\n");
print("{}\n", value_string);
}
};
// Verify if symbol is a special form
auto special_form = std::find_if(
s_special_form_parts.begin(),
s_special_form_parts.end(),
[&symbol_string](const SpecialFormParts& special_form_parts) {
return special_form_parts.name == symbol_string;
});
// If symbol is not special form, lookup in the environment
ValuePtr value;
if (special_form == s_special_form_parts.end()) {
value = env->get(symbol_string);
if (!value) {
Error::the().add(format("'{}' not found", symbol_string));
return nullptr;
}
}
// Variable
if (special_form == s_special_form_parts.end() && !is<Callable>(value.get())) {
type = "variable";
Printer printer;
value_string = format("{}", printer.printNoErrorCheck(value, true));
describe();
return nullptr;
}
signature = pretty_print ? format(fg(ruc::format::TerminalColor::BrightBlue), "{}", symbol_string)
: symbol_string;
// Special form
if (special_form != s_special_form_parts.end()) {
type = "special form";
std::string signature_lower = std::string(special_form->signature);
std::transform(signature_lower.begin(), signature_lower.end(), signature_lower.begin(), ::toupper);
signature += !signature_lower.empty() ? " " : "";
signature += signature_lower;
documentation = special_form->documentation;
describe();
return nullptr;
}
// Function / lambda / macro
if (is<Function>(value.get())) {
type = "function";
auto function = std::static_pointer_cast<Function>(value);
signature += !function->bindings().empty() ? " " : "";
signature += function->bindings();
documentation = function->documentation();
}
else if (is<Lambda>(value.get()) || is<Macro>(value.get())) {
type = is<Lambda>(value.get()) ? "function" : "macro";
auto lambda = std::static_pointer_cast<Lambda>(value);
auto bindings = lambda->bindings();
std::string binding;
for (size_t i = 0; i < bindings.size(); ++i) {
binding = bindings[i];
std::transform(binding.begin(), binding.end(), binding.begin(), ::toupper);
signature += " " + binding;
}
auto body = lambda->body();
if (is<String>(body.get())) {
documentation = std::static_pointer_cast<String>(body)->data();
}
else if (is<List>(body.get())) {
VALUE_CAST(list, List, body);
if (list->size() > 1) {
auto second = list->nodesRead()[1];
if (is<String>(second.get())) {
documentation = std::static_pointer_cast<String>(second)->data();
}
}
}
}
describe();
return nullptr;
}
EVAL_FUNCTION("fn*", "args [docstring] body...", R"(Return an anonymous function.
ARGS should take the form of an argument list or vector.
DOCSTRING is an optional documentation string.
If present, it should describe how to call the function.
BODY should be a list of Lisp expressions.)");
ValuePtr Eval::evalFn(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_IS("fn*", nodes.size(), 2);
CHECK_ARG_COUNT_AT_LEAST("fn*", nodes.size(), 2);
// First element needs to be a List or Vector
VALUE_CAST(collection, Collection, nodes.front());
const auto& collection_nodes = collection->nodes();
const auto& collection_nodes = collection->nodesRead();
std::vector<std::string> bindings;
bindings.reserve(collection_nodes.size());
@ -83,19 +231,25 @@ ValuePtr Eval::evalFn(const ValueVector& nodes, EnvironmentPtr env)
bindings.push_back(symbol->symbol());
}
// TODO: Remove limitation of 3 arguments
// Wrap all other nodes in list and add that as lambda body
return makePtr<Lambda>(bindings, *std::next(nodes.begin()), env);
}
// If more than one s-exp in lambda body, wrap in list
if (nodes.size() > 2) {
auto first = std::next(nodes.begin());
auto last = std::prev(nodes.end());
auto body_nodes = ValueVector(std::distance(first, last) + 2);
body_nodes.at(0) = makePtr<Symbol>("do");
std::copy(first, nodes.end(), body_nodes.begin() + 1);
auto body = makePtr<List>(body_nodes);
ValuePtr Eval::evalMacroExpand(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_IS("macroexpand", nodes.size(), 1);
return makePtr<Lambda>(bindings, body, env);
}
return macroExpand(nodes.front(), env);
return makePtr<Lambda>(bindings, *std::next(nodes.begin()), env);
}
// -----------------------------------------
// (quasiquoteexpand x)
EVAL_FUNCTION("quasiquoteexpand", "arg", ""); // TODO
ValuePtr Eval::evalQuasiQuoteExpand(const ValueVector& nodes)
{
CHECK_ARG_COUNT_IS("quasiquoteexpand", nodes.size(), 1);
@ -103,7 +257,7 @@ ValuePtr Eval::evalQuasiQuoteExpand(const ValueVector& nodes)
return evalQuasiQuoteImpl(nodes.front());
}
// (quote x)
EVAL_FUNCTION("quote", "arg", "Return the ARG, without evaluating it. (quote x) yields x.");
ValuePtr Eval::evalQuote(const ValueVector& nodes)
{
CHECK_ARG_COUNT_IS("quote", nodes.size(), 1);
@ -111,48 +265,69 @@ ValuePtr Eval::evalQuote(const ValueVector& nodes)
return nodes.front();
}
// (try* x (catch* y z))
EVAL_FUNCTION("try*", "body... [catch]", R"(Eval BODY allowing exceptions to get caught.
CATCH should take the form of (catch* binding handler).
The BODY is evaluated, if it throws an exception, then form CATCH is
handled by creating a new environment that binds the symbol BINDING
to the value of the exception that was thrown. Finally, HANDLER is evaluated.)");
ValuePtr Eval::evalTry(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_AT_LEAST("try*", nodes.size(), 1);
// Try 'x'
m_ast = nodes.front();
m_env = env;
auto result = evalImpl();
// Is last node a catch* block
bool is_last_node_catch = false;
std::span<const ValuePtr> catch_nodes;
if (nodes.size() >= 2 && is<List>(nodes.back().get())) {
VALUE_CAST(list, List, nodes.back());
catch_nodes = list->nodesRead();
if (!list->empty() && is<Symbol>(catch_nodes.front().get())) {
VALUE_CAST(catch_symbol, Symbol, catch_nodes.front());
if (catch_symbol->symbol() == "catch*") {
CHECK_ARG_COUNT_IS("catch*", catch_nodes.size() - 1, 2);
is_last_node_catch = true;
}
}
}
// Dont have to eval on malformed (catch*)
if (Error::the().hasAnyError()) {
return nullptr;
}
// Try
ValuePtr result;
auto end = (!is_last_node_catch) ? nodes.end() : std::prev(nodes.end(), 1);
for (auto it = nodes.begin(); it != end; ++it) {
m_ast = *it;
m_env = env;
result = evalImpl();
}
if (!Error::the().hasAnyError()) {
return result;
}
if (nodes.size() == 1) {
return nullptr;
}
// Catch
if (!is_last_node_catch) {
return nullptr;
}
// Get the error message
auto error = (Error::the().hasOtherError())
? makePtr<String>(Error::the().otherError())
: Error::the().exception();
Error::the().clearErrors();
VALUE_CAST(catch_list, List, nodes.back());
const auto& catch_nodes = catch_list->nodes();
CHECK_ARG_COUNT_IS("catch*", catch_nodes.size() - 1, 2);
VALUE_CAST(catch_symbol, Symbol, catch_nodes.front());
if (catch_symbol->symbol() != "catch*") {
Error::the().add("catch block must begin with catch*");
return nullptr;
}
VALUE_CAST(catch_binding, Symbol, (*std::next(catch_nodes.begin())));
// Create new Environment that binds 'y' to the value of the exception
// Create new Environment that binds 'binding' to the value of the exception
auto catch_env = Environment::create(env);
catch_env->set(catch_binding->symbol(), error);
// Evaluate 'z' using the new Environment
// Evaluate 'handler' using the new Environment
m_ast = catch_nodes.back();
m_env = catch_env;
return evalImpl();
@ -160,7 +335,34 @@ ValuePtr Eval::evalTry(const ValueVector& nodes, EnvironmentPtr env)
// -----------------------------------------
// (do 1 2 3)
EVAL_FUNCTION("and", "args...", R"(Eval ARGS until one of them yields nil, then return nil.
The remaining args are not evalled at all.
If no arg yields nil, return the last arg's value.)");
void Eval::evalAnd(const ValueVector& nodes, EnvironmentPtr env)
{
ValuePtr result = makePtr<Constant>(Constant::True);
for (auto node : nodes) {
m_ast = node;
m_env = env;
result = evalImpl();
if (is<Constant>(result.get())) {
VALUE_CAST(constant, Constant, result, void());
if (constant->state() == Constant::Nil || constant->state() == Constant::False) {
m_ast = makePtr<Constant>(Constant::Nil);
m_env = env;
return; // TCO
}
}
}
m_ast = result;
m_env = env;
return; // TCO
}
EVAL_FUNCTION("do", "body...", "Eval BODY forms sequentially and return value of the last one.");
void Eval::evalDo(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_AT_LEAST("do", nodes.size(), 1, void());
@ -178,7 +380,11 @@ void Eval::evalDo(const ValueVector& nodes, EnvironmentPtr env)
return; // TCO
}
// (if x true false)
EVAL_FUNCTION("if", "COND THEN [ELSE]", R"(If COND yields non-nil, do THEN, else do ELSE.
Returns the value of THEN or the value of ELSE.
Both THEN and ELSE must be one expression.
If COND yields nil, and there is no ELSE, the value is nil.)");
void Eval::evalIf(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_BETWEEN("if", nodes.size(), 2, 3, void());
@ -202,14 +408,19 @@ void Eval::evalIf(const ValueVector& nodes, EnvironmentPtr env)
return; // TCO
}
// (let* (x 1) x)
EVAL_FUNCTION("let*", "varlist body", R"(Bind variables accoring to VARLIST then eval BODY.
The value of the BODY form is returned.
VARLIST is a list or vector with an even amount of elements,
where each odd number is a symbol gets bind the even element.
All even elements are evalled before any symbols are bound.)");
void Eval::evalLet(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_IS("let*", nodes.size(), 2, void());
// First argument needs to be a List or Vector
VALUE_CAST(bindings, Collection, nodes.front(), void());
const auto& binding_nodes = bindings->nodes();
const auto& binding_nodes = bindings->nodesRead();
// List or Vector needs to have an even number of elements
CHECK_ARG_COUNT_EVEN("bindings", binding_nodes.size(), void());
@ -237,15 +448,96 @@ void Eval::evalLet(const ValueVector& nodes, EnvironmentPtr env)
// -----------------------------------------
// (x y z)
static bool isMacroCall(ValuePtr ast, EnvironmentPtr env)
{
auto list = dynamic_cast<List*>(ast.get());
if (list == nullptr || list->empty()) {
return false;
}
auto front = list->front().get();
if (!is<Symbol>(front)) {
return false;
}
auto symbol = dynamic_cast<Symbol*>(front)->symbol();
auto value = env->get(symbol).get();
if (!is<Macro>(value)) {
return false;
}
return true;
}
EVAL_FUNCTION("macroexpand-1", "expression", "Macroexpand EXPRESSION and pretty-print its value.");
void Eval::evalMacroExpand1(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_IS("macroexpand-1", nodes.size(), 1, void());
if (!isMacroCall(nodes.front(), env)) {
m_ast = nodes.front();
m_env = env;
return;
}
auto list = std::static_pointer_cast<List>(nodes.front());
auto value = env->get(std::static_pointer_cast<Symbol>(list->front())->symbol());
auto lambda = std::static_pointer_cast<Lambda>(value);
m_ast = lambda->body();
m_env = Environment::create(lambda, list->rest());
return; // TCO
}
// -----------------------------------------
EVAL_FUNCTION("or", "args...", R"(Eval ARGS until one of them yields non-nil, then return that value.
The remaining args are not evalled at all.
If all args return nil, return nil.)");
void Eval::evalOr(const ValueVector& nodes, EnvironmentPtr env)
{
ValuePtr result;
for (auto node : nodes) {
m_ast = node;
m_env = env;
result = evalImpl();
if (!is<Constant>(result.get())) {
m_ast = result;
m_env = env;
return; // TCO
}
VALUE_CAST(constant, Constant, result, void());
if (constant->state() == Constant::True) {
m_ast = result;
m_env = env;
return; // TCO
}
}
m_ast = makePtr<Constant>(Constant::Nil);
m_env = env;
return; // TCO
}
// -----------------------------------------
static bool isSymbol(ValuePtr value, const std::string& symbol)
{
if (!is<Symbol>(value.get())) {
return false;
}
auto valueSymbol = std::static_pointer_cast<Symbol>(value)->symbol();
auto value_symbol = std::static_pointer_cast<Symbol>(value)->symbol();
if (valueSymbol != symbol) {
if (value_symbol != symbol) {
return false;
}
@ -258,7 +550,7 @@ static ValuePtr startsWith(ValuePtr ast, const std::string& symbol)
return nullptr;
}
const auto& nodes = std::static_pointer_cast<List>(ast)->nodes();
const auto& nodes = std::static_pointer_cast<List>(ast)->nodesRead();
if (nodes.empty() || !isSymbol(nodes.front(), symbol)) {
return nullptr;
@ -294,10 +586,10 @@ static ValuePtr evalQuasiQuoteImpl(ValuePtr ast)
ValuePtr result = makePtr<List>();
const auto& nodes = std::static_pointer_cast<Collection>(ast)->nodes();
auto collection = std::static_pointer_cast<Collection>(ast);
// `() or `(1 ~2 3) or `(1 ~@(list 2 2 2) 3)
for (auto it = nodes.crbegin(); it != nodes.crend(); ++it) {
for (auto it = collection->beginReverse(); it != collection->endReverse(); ++it) {
const auto& elt = *it;
const auto splice_unquote = startsWith(elt, "splice-unquote"); // (list 2 2 2)
@ -320,6 +612,7 @@ static ValuePtr evalQuasiQuoteImpl(ValuePtr ast)
}
// (quasiquote x)
EVAL_FUNCTION("quasiquote", "arg", R"()"); // TODO
void Eval::evalQuasiQuote(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_IS("quasiquote", nodes.size(), 1, void());
@ -333,4 +626,41 @@ void Eval::evalQuasiQuote(const ValueVector& nodes, EnvironmentPtr env)
// -----------------------------------------
// (while true body...)
EVAL_FUNCTION("while", "test body...", R"(If TEST yields non-nil, eval BODY... and repeat
The order of execution is thus TEST, BODY, TEST, BODY and so on
until TEST returns nil.
The value of a while form is always nil.)");
void Eval::evalWhile(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_AT_LEAST("while", nodes.size(), 2, void());
// Condition
ValuePtr predicate = *nodes.begin();
m_ast = predicate;
m_env = env;
ValuePtr condition = evalImpl();
while (!is<Constant>(condition.get())
|| std::static_pointer_cast<Constant>(condition)->state() == Constant::True) {
for (auto it = nodes.begin() + 1; it != nodes.end(); ++it) {
m_ast = *it;
m_env = env;
evalImpl();
}
m_ast = predicate;
m_env = env;
condition = evalImpl();
}
m_ast = makePtr<Constant>();
m_env = env;
return; // TCO
}
// -----------------------------------------
} // namespace blaze

214
src/eval.cpp

@ -4,14 +4,14 @@
* SPDX-License-Identifier: MIT
*/
#include <iterator> // sd::advance, std::next, std::prev
#include <iterator> // std::advance, std::next, std::prev
#include <list>
#include <memory> // std::static_pointer_cast
#include <span> // std::span
#include <string>
#include "ast.h"
#include "environment.h"
#include "env/environment.h"
#include "error.h"
#include "eval.h"
#include "forward.h"
@ -20,6 +20,8 @@
namespace blaze {
std::vector<SpecialFormParts> Eval::s_special_form_parts;
Eval::Eval(ValuePtr ast, EnvironmentPtr env)
: m_ast(ast)
, m_env(env)
@ -29,6 +31,11 @@ Eval::Eval(ValuePtr ast, EnvironmentPtr env)
// -----------------------------------------
void Eval::registerSpecialForm(SpecialFormParts special_form_parts)
{
s_special_form_parts.push_back(special_form_parts);
}
void Eval::eval()
{
m_ast = evalImpl();
@ -39,6 +46,10 @@ ValuePtr Eval::evalImpl()
ValuePtr ast = nullptr;
EnvironmentPtr env = nullptr;
if (env == nullptr) {
env = m_outer_env;
}
while (true) {
if (Error::the().hasAnyError()) {
return nullptr;
@ -47,18 +58,17 @@ ValuePtr Eval::evalImpl()
ast = m_ast;
env = m_env;
if (env == nullptr) {
env = m_outer_env;
if (is<Symbol>(ast.get())) {
return evalSymbol(ast, env);
}
if (!is<List>(ast.get())) {
return evalAst(ast, env);
if (is<Vector>(ast.get())) {
return evalVector(ast, env);
}
if (is<HashMap>(ast.get())) {
return evalHashMap(ast, env);
}
ast = macroExpand(ast, env);
if (!is<List>(ast.get())) {
return evalAst(ast, env);
return ast;
}
auto list = std::static_pointer_cast<List>(ast);
@ -77,12 +87,12 @@ ValuePtr Eval::evalImpl()
if (symbol == "defmacro!") {
return evalDefMacro(nodes, env);
}
if (symbol == "describe") {
return evalDescribe(nodes, env);
}
if (symbol == "fn*") {
return evalFn(nodes, env);
}
if (symbol == "macroexpand") {
return evalMacroExpand(nodes, env);
}
if (symbol == "quasiquoteexpand") {
return evalQuasiQuoteExpand(nodes);
}
@ -93,6 +103,10 @@ ValuePtr Eval::evalImpl()
return evalTry(nodes, env);
}
// Tail call optimized functions
if (symbol == "and") {
evalAnd(nodes, env);
continue; // TCO
}
if (symbol == "do") {
evalDo(nodes, env);
continue; // TCO
@ -105,150 +119,118 @@ ValuePtr Eval::evalImpl()
evalLet(nodes, env);
continue; // TCO
}
if (symbol == "macroexpand-1") {
evalMacroExpand1(nodes, env);
continue; // TCO
}
if (symbol == "or") {
evalOr(nodes, env);
continue; // TCO
}
if (symbol == "quasiquote") {
evalQuasiQuote(nodes, env);
continue; // TCO
}
if (symbol == "while") {
evalWhile(nodes, env);
continue; // TCO
}
}
auto evaluated_list = std::static_pointer_cast<List>(evalAst(ast, env));
m_ast = list->front();
m_env = env;
auto evaluated_front = evalImpl();
auto unevaluated_nodes = list->rest();
if (evaluated_list == nullptr) {
return nullptr;
if (is<Macro>(evaluated_front.get())) { // FIXME
auto lambda = std::static_pointer_cast<Lambda>(evaluated_front);
m_ast = lambda->body();
m_env = Environment::create(lambda, std::move(unevaluated_nodes));
m_ast = evalImpl();
m_env = env;
continue; // TCO
}
ValueVector evaluated_nodes(unevaluated_nodes.size());
for (size_t i = 0; i < unevaluated_nodes.size(); ++i) {
m_ast = unevaluated_nodes[i];
m_env = env;
evaluated_nodes[i] = evalImpl();
}
// Regular list
if (is<Lambda>(evaluated_list->front().get())) {
auto lambda = std::static_pointer_cast<Lambda>(evaluated_list->front());
if (is<Lambda>(evaluated_front.get())) {
auto lambda = std::static_pointer_cast<Lambda>(evaluated_front);
m_ast = lambda->body();
m_env = Environment::create(lambda, evaluated_list->rest());
m_env = Environment::create(lambda, std::move(evaluated_nodes));
continue; // TCO
}
// Function call
return apply(evaluated_list);
return apply(evaluated_front, evaluated_nodes);
}
}
ValuePtr Eval::evalAst(ValuePtr ast, EnvironmentPtr env)
ValuePtr Eval::evalSymbol(ValuePtr ast, EnvironmentPtr env)
{
if (ast == nullptr || env == nullptr) {
auto result = env->get(std::static_pointer_cast<Symbol>(ast)->symbol());
if (!result) {
Error::the().add(::format("'{}' not found", ast));
return nullptr;
}
Value* ast_raw_ptr = ast.get();
if (is<Symbol>(ast_raw_ptr)) {
auto result = env->get(std::static_pointer_cast<Symbol>(ast)->symbol());
if (!result) {
Error::the().add(::format("'{}' not found", ast));
return nullptr;
}
return result;
}
else if (is<Collection>(ast_raw_ptr)) {
const auto& nodes = std::static_pointer_cast<Collection>(ast)->nodes();
size_t count = nodes.size();
auto evaluated_nodes = ValueVector(count);
for (size_t i = 0; i < count; ++i) {
m_ast = nodes[i];
m_env = env;
ValuePtr eval_node = evalImpl();
if (eval_node == nullptr) {
return nullptr;
}
evaluated_nodes.at(i) = eval_node;
}
if (is<List>(ast_raw_ptr)) {
return makePtr<List>(evaluated_nodes);
}
return makePtr<Vector>(evaluated_nodes);
}
else if (is<HashMap>(ast_raw_ptr)) {
const auto& elements = std::static_pointer_cast<HashMap>(ast)->elements();
Elements evaluated_elements;
for (const auto& element : elements) {
m_ast = element.second;
m_env = env;
ValuePtr element_node = evalImpl();
if (element_node == nullptr) {
return nullptr;
}
evaluated_elements.insert_or_assign(element.first, element_node);
}
return makePtr<HashMap>(evaluated_elements);
}
return ast;
return result;
}
// -----------------------------------------
// (x y z)
bool Eval::isMacroCall(ValuePtr ast, EnvironmentPtr env)
ValuePtr Eval::evalVector(ValuePtr ast, EnvironmentPtr env)
{
auto list = dynamic_cast<List*>(ast.get());
if (list == nullptr || list->empty()) {
return false;
}
auto front = list->front().get();
if (!is<Symbol>(front)) {
return false;
}
auto symbol = dynamic_cast<Symbol*>(front)->symbol();
auto value = env->get(symbol).get();
if (!is<Macro>(value)) {
return false;
const auto& nodes = std::static_pointer_cast<Collection>(ast)->nodesRead();
size_t count = nodes.size();
auto evaluated_nodes = ValueVector(count);
for (size_t i = 0; i < count; ++i) {
m_ast = nodes[i];
m_env = env;
ValuePtr eval_node = evalImpl();
if (eval_node == nullptr) {
return nullptr;
}
evaluated_nodes.at(i) = eval_node;
}
return true;
return makePtr<Vector>(evaluated_nodes);
}
// (x y z)
ValuePtr Eval::macroExpand(ValuePtr ast, EnvironmentPtr env)
ValuePtr Eval::evalHashMap(ValuePtr ast, EnvironmentPtr env)
{
while (isMacroCall(ast, env)) {
auto list = std::static_pointer_cast<List>(ast);
auto value = env->get(std::static_pointer_cast<Symbol>(list->front())->symbol());
auto lambda = std::static_pointer_cast<Lambda>(value);
m_ast = lambda->body();
m_env = Environment::create(lambda, list->rest());
ast = evalImpl();
const auto& elements = std::static_pointer_cast<HashMap>(ast)->elements();
Elements evaluated_elements;
for (const auto& element : elements) {
m_ast = element.second;
m_env = env;
ValuePtr element_node = evalImpl();
if (element_node == nullptr) {
return nullptr;
}
evaluated_elements.insert_or_assign(element.first, element_node);
}
return ast;
return makePtr<HashMap>(evaluated_elements);
}
// -----------------------------------------
ValuePtr Eval::apply(std::shared_ptr<List> evaluated_list)
ValuePtr Eval::apply(ValuePtr function, const ValueVector& nodes)
{
if (evaluated_list == nullptr) {
return nullptr;
}
auto nodes = evaluated_list->nodes();
if (!is<Function>(nodes.front().get())) {
Error::the().add(::format("invalid function: {}", nodes.front()));
if (!is<Function>(function.get())) {
Error::the().add(::format("invalid function: {}", function));
return nullptr;
}
auto function = std::static_pointer_cast<Function>(nodes.front())->function();
auto func = std::static_pointer_cast<Function>(function)->function();
return function(nodes.begin() + 1, nodes.end());
return func(nodes.begin(), nodes.end());
}
} // namespace blaze

32
src/eval.h

@ -6,50 +6,60 @@
#pragma once
#include <list>
#include <stack>
#include <vector>
#include "environment.h"
#include "forward.h"
#include "forward.h" // EnvironmentPtr
namespace blaze {
class List;
// All of these combined become a Function in the Environment
struct SpecialFormParts {
std::string_view name;
std::string_view signature;
std::string_view documentation;
};
class Eval {
public:
Eval(ValuePtr ast, EnvironmentPtr env);
virtual ~Eval() = default;
static void registerSpecialForm(SpecialFormParts special_form_parts);
void eval();
ValuePtr ast() const { return m_ast; }
private:
ValuePtr evalImpl();
ValuePtr evalAst(ValuePtr ast, EnvironmentPtr env);
bool isMacroCall(ValuePtr ast, EnvironmentPtr env);
ValuePtr macroExpand(ValuePtr ast, EnvironmentPtr env);
ValuePtr evalSymbol(ValuePtr ast, EnvironmentPtr env);
ValuePtr evalVector(ValuePtr ast, EnvironmentPtr env);
ValuePtr evalHashMap(ValuePtr ast, EnvironmentPtr env);
ValuePtr evalDef(const ValueVector& nodes, EnvironmentPtr env);
ValuePtr evalDefMacro(const ValueVector& nodes, EnvironmentPtr env);
ValuePtr evalDescribe(const ValueVector& nodes, EnvironmentPtr env);
ValuePtr evalFn(const ValueVector& nodes, EnvironmentPtr env);
ValuePtr evalMacroExpand(const ValueVector& nodes, EnvironmentPtr env);
ValuePtr evalQuasiQuoteExpand(const ValueVector& nodes);
ValuePtr evalQuote(const ValueVector& nodes);
ValuePtr evalTry(const ValueVector& nodes, EnvironmentPtr env);
void evalAnd(const ValueVector& nodes, EnvironmentPtr env);
void evalDo(const ValueVector& nodes, EnvironmentPtr env);
void evalIf(const ValueVector& nodes, EnvironmentPtr env);
void evalLet(const ValueVector& nodes, EnvironmentPtr env);
void evalMacroExpand1(const ValueVector& nodes, EnvironmentPtr env);
void evalOr(const ValueVector& nodes, EnvironmentPtr env);
void evalQuasiQuote(const ValueVector& nodes, EnvironmentPtr env);
void evalWhile(const ValueVector& nodes, EnvironmentPtr env);
ValuePtr apply(std::shared_ptr<List> evaluated_list);
ValuePtr apply(ValuePtr function, const ValueVector& nodes);
ValuePtr m_ast;
EnvironmentPtr m_env;
EnvironmentPtr m_outer_env;
static std::vector<SpecialFormParts> s_special_form_parts;
};
} // namespace blaze

12
src/forward.h

@ -18,18 +18,22 @@ class Value;
typedef std::shared_ptr<Value> ValuePtr;
typedef std::vector<ValuePtr> ValueVector;
typedef ValueVector::iterator ValueVectorIt;
typedef ValueVector::reverse_iterator ValueVectorReverseIt;
typedef ValueVector::const_iterator ValueVectorConstIt;
typedef ValueVector::const_reverse_iterator ValueVectorConstReverseIt;
class Environment;
typedef std::shared_ptr<Environment> EnvironmentPtr;
class Readline;
// -----------------------------------------
// Functions
extern ValuePtr readline(const std::string& prompt);
extern ValuePtr read(std::string_view input);
extern ValuePtr eval(ValuePtr ast, EnvironmentPtr env);
// -----------------------------------------
// Variables
extern void installFunctions(EnvironmentPtr env);
extern Readline g_readline;
extern EnvironmentPtr g_outer_env;
} // namespace blaze

992
src/functions.cpp

@ -1,992 +0,0 @@
/*
* 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

22
src/macro.h

@ -0,0 +1,22 @@
/*
* 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__))

84
src/main.cpp

@ -0,0 +1,84 @@
/*
* 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);
}

87
src/printer.cpp

@ -8,12 +8,14 @@
#include <memory> // std::static_pointer_cast
#include <string>
#include "ruc/format/color.h"
#include "ruc/format/format.h"
#include "ast.h"
#include "error.h"
#include "lexer.h"
#include "printer.h"
#include "settings.h"
#include "types.h"
#include "util.h"
@ -29,7 +31,7 @@ Printer::~Printer()
// -----------------------------------------
std::string Printer::print(ValuePtr node, bool print_readably)
std::string Printer::print(ValuePtr value, bool print_readably)
{
if (Error::the().hasAnyError()) {
init();
@ -37,18 +39,18 @@ std::string Printer::print(ValuePtr node, bool print_readably)
return m_print;
}
return printNoErrorCheck(node, print_readably);
return printNoErrorCheck(value, print_readably);
}
std::string Printer::printNoErrorCheck(ValuePtr node, bool print_readably)
std::string Printer::printNoErrorCheck(ValuePtr value, bool print_readably)
{
init();
if (node == nullptr) {
if (value == nullptr) {
return {};
}
printImpl(node, print_readably);
printImpl(value, print_readably);
return m_print;
}
@ -62,33 +64,35 @@ void Printer::init()
m_print = "";
}
void Printer::printImpl(ValuePtr node, bool print_readably)
void Printer::printImpl(ValuePtr value, bool print_readably)
{
bool pretty_print = Settings::the().getEnvBool("*PRETTY-PRINT*");
auto printSpacing = [this]() -> void {
if (!m_first_node && !m_previous_node_is_list) {
m_print += ' ';
}
};
Value* node_raw_ptr = node.get();
if (is<Collection>(node_raw_ptr)) {
Value* value_raw_ptr = value.get();
if (is<Collection>(value_raw_ptr)) {
printSpacing();
m_print += (is<List>(node_raw_ptr)) ? '(' : '[';
m_print += (is<List>(value_raw_ptr)) ? '(' : '[';
m_first_node = false;
m_previous_node_is_list = true;
auto nodes = std::static_pointer_cast<Collection>(node)->nodes();
auto nodes = std::static_pointer_cast<Collection>(value)->nodesRead();
for (auto node : nodes) {
printImpl(node, print_readably);
m_previous_node_is_list = false;
}
m_print += (is<List>(node_raw_ptr)) ? ')' : ']';
m_print += (is<List>(value_raw_ptr)) ? ')' : ']';
}
else if (is<HashMap>(node_raw_ptr)) {
else if (is<HashMap>(value_raw_ptr)) {
printSpacing();
m_print += "{";
m_first_node = false;
m_previous_node_is_list = true;
auto elements = std::static_pointer_cast<HashMap>(node)->elements();
auto elements = std::static_pointer_cast<HashMap>(value)->elements();
for (auto it = elements.begin(); it != elements.end(); ++it) {
m_print += ::format("{} ", (it->first.front() == 0x7f) ? ":" + it->first.substr(1) : '"' + it->first + '"'); // 127
printImpl(it->second, print_readably);
@ -100,8 +104,8 @@ void Printer::printImpl(ValuePtr node, bool print_readably)
m_previous_node_is_list = false;
m_print += '}';
}
else if (is<String>(node_raw_ptr)) {
std::string text = std::static_pointer_cast<String>(node)->data();
else if (is<String>(value_raw_ptr)) {
std::string text = std::static_pointer_cast<String>(value)->data();
if (print_readably) {
text = replaceAll(text, "\\", "\\\\");
text = replaceAll(text, "\"", "\\\"");
@ -109,46 +113,55 @@ void Printer::printImpl(ValuePtr node, bool print_readably)
text = "\"" + text + "\"";
}
printSpacing();
m_print += ::format("{}", text);
if (pretty_print) {
m_print += ::format(fg(ruc::format::TerminalColor::BrightGreen), "{}", text);
}
else {
m_print += ::format("{}", text);
}
}
else if (is<Keyword>(value_raw_ptr)) {
printSpacing();
m_print += ::format(":{}", std::static_pointer_cast<Keyword>(value)->keyword().substr(1));
}
else if (is<Keyword>(node_raw_ptr)) {
else if (is<Number>(value_raw_ptr)) {
printSpacing();
m_print += ::format(":{}", std::static_pointer_cast<Keyword>(node)->keyword().substr(1));
m_print += ::format("{}", std::static_pointer_cast<Number>(value)->number());
}
else if (is<Number>(node_raw_ptr)) {
else if (is<Decimal>(value_raw_ptr)) {
printSpacing();
m_print += ::format("{}", std::static_pointer_cast<Number>(node)->number());
m_print += ::format("{:.15}", std::static_pointer_cast<Decimal>(value)->decimal());
}
else if (is<Constant>(node_raw_ptr)) {
else if (is<Constant>(value_raw_ptr)) {
printSpacing();
std::string value;
switch (std::static_pointer_cast<Constant>(node)->state()) {
case Constant::Nil: value = "nil"; break;
case Constant::True: value = "true"; break;
case Constant::False: value = "false"; break;
std::string constant;
switch (std::static_pointer_cast<Constant>(value)->state()) {
case Constant::Nil: constant = "nil"; break;
case Constant::True: constant = "true"; break;
case Constant::False: constant = "false"; break;
}
m_print += ::format("{}", value);
m_print += ::format("{}", constant);
}
else if (is<Symbol>(node_raw_ptr)) {
else if (is<Symbol>(value_raw_ptr)) {
printSpacing();
m_print += ::format("{}", std::static_pointer_cast<Symbol>(node)->symbol());
m_print += ::format("{}", std::static_pointer_cast<Symbol>(value)->symbol());
}
else if (is<Function>(node_raw_ptr)) {
else if (is<Function>(value_raw_ptr)) {
printSpacing();
m_print += ::format("#<builtin-function>({})", std::static_pointer_cast<Function>(node)->name());
m_print += ::format("#<builtin-function>({})", std::static_pointer_cast<Function>(value)->name());
}
else if (is<Lambda>(node_raw_ptr)) {
else if (is<Lambda>(value_raw_ptr)) {
printSpacing();
m_print += ::format("#<user-function>({:p})", node_raw_ptr);
m_print += ::format("#<user-function>({:p})", value_raw_ptr);
}
else if (is<Macro>(node_raw_ptr)) {
else if (is<Macro>(value_raw_ptr)) {
printSpacing();
m_print += ::format("#<user-macro>({:p})", node_raw_ptr);
m_print += ::format("#<user-macro>({:p})", value_raw_ptr);
}
else if (is<Atom>(node_raw_ptr)) {
else if (is<Atom>(value_raw_ptr)) {
printSpacing();
m_print += "(atom ";
printImpl(std::static_pointer_cast<Atom>(node)->deref(), print_readably);
printImpl(std::static_pointer_cast<Atom>(value)->deref(), print_readably);
m_print += ")";
}
}

6
src/printer.h

@ -17,12 +17,12 @@ public:
Printer();
virtual ~Printer();
std::string print(ValuePtr node, bool print_readably = true);
std::string printNoErrorCheck(ValuePtr node, bool print_readably = true);
std::string print(ValuePtr value, bool print_readably = true);
std::string printNoErrorCheck(ValuePtr value, bool print_readably = true);
private:
void init();
void printImpl(ValuePtr node, bool print_readably = true);
void printImpl(ValuePtr value, bool print_readably = true);
void printError();
bool m_first_node { true };

160
src/reader.cpp

@ -4,11 +4,13 @@
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <cstdint> // uint64_t
#include <cstdlib> // std::strtoll
#include <memory> // std::static_pointer_cast
#include <utility> // std::move
#include <charconv> // std::from_chars
#include <cstddef> // size_t
#include <cstdint> // uint64_t
#include <cstdlib> // std::strtoll
#include <memory> // std::static_pointer_cast
#include <system_error> // std::errc
#include <utility> // std::move
#include "error.h"
#include "ruc/format/color.h"
@ -16,10 +18,15 @@
#include "ast.h"
#include "reader.h"
#include "settings.h"
#include "types.h"
namespace blaze {
Reader::Reader()
{
}
Reader::Reader(std::vector<Token>&& tokens) noexcept
: m_tokens(std::move(tokens))
{
@ -142,7 +149,7 @@ ValuePtr Reader::readList()
nodes.push_back(node);
}
if (!consumeSpecific(Token { .type = Token::Type::ParenClose })) { // )
if (!consumeSpecific(Token { .type = Token::Type::ParenClose, .symbol = "" })) { // )
Error::the().add("expected ')', got EOF");
return nullptr;
}
@ -163,7 +170,7 @@ ValuePtr Reader::readVector()
nodes.push_back(node);
}
if (!consumeSpecific(Token { .type = Token::Type::BracketClose })) { // ]
if (!consumeSpecific(Token { .type = Token::Type::BracketClose, .symbol = "" })) { // ]
Error::the().add("expected ']', got EOF");
}
@ -196,7 +203,7 @@ ValuePtr Reader::readHashMap()
elements.insert_or_assign(HashMap::getKeyString(key), value);
}
if (!consumeSpecific(Token { .type = Token::Type::BraceClose })) { // }
if (!consumeSpecific(Token { .type = Token::Type::BraceClose, .symbol = "" })) { // }
Error::the().add("expected '}', got EOF");
return nullptr;
}
@ -301,24 +308,33 @@ ValuePtr Reader::readKeyword()
ValuePtr Reader::readValue()
{
Token token = consume();
char* end_ptr = nullptr;
int64_t result = std::strtoll(token.symbol.c_str(), &end_ptr, 10);
if (end_ptr == token.symbol.c_str() + token.symbol.size()) {
return makePtr<Number>(result);
auto symbol = consume().symbol;
int64_t number;
auto [_, error] = std::from_chars(symbol.data(), symbol.data() + symbol.size(), number);
if (error == std::errc() && symbol.find('.') == std::string::npos) {
return makePtr<Number>(number);
}
double decimal;
{
auto [_, error] = std::from_chars(symbol.data(), symbol.data() + symbol.size(), decimal);
if (error == std::errc()) {
return makePtr<Decimal>(decimal);
}
}
if (token.symbol == "nil") {
if (symbol == "nil") {
return makePtr<Constant>(Constant::Nil);
}
else if (token.symbol == "true") {
else if (symbol == "true") {
return makePtr<Constant>(Constant::True);
}
else if (token.symbol == "false") {
else if (symbol == "false") {
return makePtr<Constant>(Constant::False);
}
return makePtr<Symbol>(token.symbol);
return makePtr<Symbol>(symbol);
}
// -----------------------------------------
@ -362,55 +378,131 @@ void Reader::retreat()
// -----------------------------------------
void Reader::dump()
void Reader::dump(ValuePtr node)
{
dumpImpl(m_node);
m_indentation = 0;
dumpImpl((node != nullptr) ? node : m_node);
}
void Reader::dumpImpl(ValuePtr node)
{
std::string indentation = std::string(m_indentation * 2, ' ');
std::string indentation = std::string(m_indentation * INDENTATION_WIDTH, ' ');
print("{}", indentation);
bool pretty_print = Settings::the().getEnvBool("*PRETTY-PRINT*");
auto blue = fg(ruc::format::TerminalColor::BrightBlue);
auto yellow = fg(ruc::format::TerminalColor::Yellow);
Value* node_raw_ptr = node.get();
if (is<Collection>(node_raw_ptr)) {
auto nodes = std::static_pointer_cast<List>(node)->nodes();
print("{}", indentation);
print(fg(ruc::format::TerminalColor::Blue), "{}Container", (is<List>(node_raw_ptr)) ? "List" : "Vector");
auto container = (is<List>(node_raw_ptr)) ? "List" : "Vector";
auto parens = (is<List>(node_raw_ptr)) ? "()" : "[]";
pretty_print ? print(blue, "{}", container) : print("{}", container);
print(" <");
print(fg(ruc::format::TerminalColor::Blue), "{}", (is<List>(node_raw_ptr)) ? "()" : "{}");
pretty_print ? print(blue, "{}", parens) : print("{}", parens);
print(">\n");
m_indentation++;
auto nodes = std::static_pointer_cast<List>(node)->nodesRead();
for (auto node : nodes) {
dumpImpl(node);
}
m_indentation--;
return;
}
else if (is<HashMap>(node_raw_ptr)) {
auto hash_map = std::static_pointer_cast<HashMap>(node);
auto elements = hash_map->elements();
pretty_print ? print(blue, "HashMap") : print("HashMap");
print(" <");
pretty_print ? print(blue, "{{}}") : print("{{}}");
print(">\n");
m_indentation++;
ValuePtr key_node = nullptr;
for (auto element : elements) {
bool is_keyword = element.first.front() == 0x7f; // 127
is_keyword
? dumpImpl(makePtr<Keyword>(element.first.substr(1)))
: dumpImpl(makePtr<String>(element.first));
m_indentation++;
dumpImpl(element.second);
m_indentation--;
}
m_indentation--;
return;
}
else if (is<String>(node_raw_ptr)) {
print("{}", indentation);
print(fg(ruc::format::TerminalColor::Yellow), "StringNode");
pretty_print ? print(yellow, "StringNode") : print("StringNode");
print(" <{}>", node);
}
else if (is<Keyword>(node_raw_ptr)) {
print("{}", indentation);
print(fg(ruc::format::TerminalColor::Yellow), "KeywordNode");
pretty_print ? print(yellow, "KeywordNode") : print("KeywordNode");
print(" <{}>", node);
}
else if (is<Number>(node_raw_ptr)) {
print("{}", indentation);
print(fg(ruc::format::TerminalColor::Yellow), "NumberNode");
pretty_print ? print(yellow, "NumberNode") : print("NumberNode");
print(" <{}>", node);
}
else if (is<Decimal>(node_raw_ptr)) {
pretty_print ? print(yellow, "DecimalNode") : print("DecimalNode");
print(" <{}>", node);
}
else if (is<Constant>(node_raw_ptr)) {
print("{}", indentation);
print(fg(ruc::format::TerminalColor::Yellow), "ValueNode");
pretty_print ? print(yellow, "ValueNode") : print("ValueNode");
print(" <{}>", node);
}
else if (is<Symbol>(node_raw_ptr)) {
print("{}", indentation);
print(fg(ruc::format::TerminalColor::Yellow), "SymbolNode");
pretty_print ? print(yellow, "SymbolNode") : print("SymbolNode");
print(" <{}>", node);
}
else if (is<Function>(node_raw_ptr)) {
auto function = std::static_pointer_cast<Function>(node);
pretty_print ? print(blue, "Function") : print("Function");
print(" <");
pretty_print ? print(blue, "{}", function->name()) : print("{}", function->name());
print(">\n");
m_indentation++;
indentation = std::string(m_indentation * INDENTATION_WIDTH, ' ');
// bindings
print("{}", indentation);
pretty_print ? print(blue, "Bindings") : print("Bindings");
print(" <{}>\n", function->bindings());
m_indentation--;
return;
}
else if (is<Lambda>(node_raw_ptr) || is<Macro>(node_raw_ptr)) {
auto container = (is<Lambda>(node_raw_ptr)) ? "Lambda" : "Macro";
auto lambda = std::static_pointer_cast<Lambda>(node);
pretty_print ? print(blue, "{}", container) : print("{}", container);
print(" <");
pretty_print ? print(blue, "{:p}", node_raw_ptr) : print("{:p}", node_raw_ptr);
print(">\n");
m_indentation++;
indentation = std::string(m_indentation * INDENTATION_WIDTH, ' ');
// bindings
print("{}", indentation);
pretty_print ? print(blue, "Bindings") : print("Bindings");
print(" <");
const auto& bindings = lambda->bindings();
for (size_t i = 0; i < bindings.size(); ++i) {
print("{}{}", (i > 0) ? " " : "", bindings[i]);
}
print(">\n");
// body
dumpImpl(lambda->body());
m_indentation--;
return;
}
else if (is<Atom>(node_raw_ptr)) {
pretty_print ? print(yellow, "AtomNode") : print("AtomNode");
print(" <{}>", std::static_pointer_cast<Atom>(node)->deref());
}
print("\n");
}

5
src/reader.h

@ -13,17 +13,20 @@
#include "ast.h"
#include "lexer.h"
#define INDENTATION_WIDTH 2
namespace blaze {
// Parsing -> creates AST
class Reader {
public:
Reader();
Reader(std::vector<Token>&& tokens) noexcept;
virtual ~Reader();
void read();
void dump();
void dump(ValuePtr node = nullptr);
ValuePtr node() { return m_node; }

100
src/repl.cpp

@ -0,0 +1,100 @@
/*
* 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

29
src/repl.h

@ -0,0 +1,29 @@
/*
* 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

13
src/settings.cpp

@ -4,7 +4,14 @@
* SPDX-License-Identifier: MIT
*/
#include <memory> // std::static_pointer_cast
#include "ruc/meta/assert.h"
#include "env/environment.h"
#include "forward.h"
#include "settings.h"
#include "types.h"
namespace blaze {
@ -14,4 +21,10 @@ std::string_view Settings::get(std::string_view key) const
return m_settings.at(key);
};
bool Settings::getEnvBool(std::string_view key) const
{
auto env = g_outer_env->get(key);
return is<Constant>(env.get()) && std::static_pointer_cast<Constant>(env)->state() == Constant::State::True;
}
} // namespace blaze

8
src/settings.h

@ -6,12 +6,10 @@
#pragma once
#include "ruc/meta/assert.h"
#include "ruc/singleton.h"
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
#include "ruc/singleton.h"
namespace blaze {
@ -23,6 +21,8 @@ public:
std::string_view get(std::string_view key) const;
void set(std::string_view key, std::string_view value) { m_settings[key] = value; };
bool getEnvBool(std::string_view key) const;
private:
std::unordered_map<std::string_view, std::string_view> m_settings;
};

62
src/step0_repl.cpp

@ -1,62 +0,0 @@
#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

114
src/step1_read_print.cpp

@ -1,114 +0,0 @@
#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

118
src/step2_eval.cpp

@ -1,118 +0,0 @@
#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

118
src/step3_env.cpp

@ -1,118 +0,0 @@
#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

135
src/step4_if_fn_do.cpp

@ -1,135 +0,0 @@
#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

141
src/step5_tco.cpp

@ -1,141 +0,0 @@
/*
* 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

161
src/step6_file.cpp

@ -1,161 +0,0 @@
/*
* 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

161
src/step7_quote.cpp

@ -1,161 +0,0 @@
/*
* 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

168
src/step8_macros.cpp

@ -1,168 +0,0 @@
/*
* 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

171
src/step9_try.cpp

@ -1,171 +0,0 @@
/*
* 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

178
src/stepA_mal.cpp

@ -1,178 +0,0 @@
/*
* 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;
}

2
vendor/ruc vendored

@ -1 +1 @@
Subproject commit 07c9f9959d3ce46da8bc7b0777d803524f9d1ec0
Subproject commit c8e4ae884eacc963dc0fe6a5254679aad050cb74
Loading…
Cancel
Save