Compare commits
21
Commits
3aa99d0045
...
11f0553b5a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11f0553b5a | ||
|
|
705e80ad6b | ||
|
|
4cc2acc8a0 | ||
|
|
f5dc1168eb | ||
|
|
536e55e75a | ||
|
|
25871cd5d5 | ||
|
|
929fb5d645 | ||
|
|
9db041946e | ||
|
|
b74f3448b2 | ||
|
|
b727f7147e | ||
|
|
c6c6d69e73 | ||
|
|
bb6f3e7496 | ||
|
|
1915621427 | ||
|
|
9895195410 | ||
|
|
e8206d762c | ||
|
|
b65482eb68 | ||
|
|
67b982fd4c | ||
|
|
d3a50abfbc | ||
|
|
80b25f8c21 | ||
|
|
0d43512ea9 | ||
|
|
fa4bd63dca |
+13
-21
@@ -2,7 +2,7 @@
|
|||||||
# User config between these lines
|
# User config between these lines
|
||||||
|
|
||||||
# Set project name
|
# Set project name
|
||||||
set(PROJECT "stepA_mal")
|
set(PROJECT "blaze")
|
||||||
|
|
||||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||||
set(BLAZE_STANDALONE TRUE)
|
set(BLAZE_STANDALONE TRUE)
|
||||||
@@ -64,26 +64,18 @@ add_subdirectory("vendor/ruc")
|
|||||||
|
|
||||||
# Define source files
|
# Define source files
|
||||||
file(GLOB_RECURSE PROJECT_SOURCES "src/*.cpp")
|
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(${PROJECT} ${PROJECT_SOURCES})
|
||||||
add_executable(${TARGET_NAME} ${PROJECT_SOURCES} ${MAIN_PATH})
|
target_include_directories(${PROJECT} PRIVATE "src")
|
||||||
target_include_directories(${TARGET_NAME} PRIVATE "src")
|
target_link_libraries(${PROJECT} readline ruc)
|
||||||
target_link_libraries(${TARGET_NAME} readline ruc)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
add_step(step0_repl "src/step0_repl.cpp")
|
# ------------------------------------------
|
||||||
add_step(step1_read_print "src/step1_read_print.cpp")
|
# Std target
|
||||||
add_step(step2_eval "src/step2_eval.cpp")
|
|
||||||
add_step(step3_env "src/step3_env.cpp")
|
add_custom_target(${PROJECT}-lisp
|
||||||
add_step(step4_if_fn_do "src/step4_if_fn_do.cpp")
|
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/copy-lisp.cmake
|
||||||
add_step(step5_tco "src/step5_tco.cpp")
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
add_step(step6_file "src/step6_file.cpp")
|
add_dependencies(${PROJECT} ${PROJECT}-lisp)
|
||||||
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")
|
|
||||||
|
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
# Execute target
|
# Execute target
|
||||||
@@ -97,8 +89,8 @@ add_custom_target(run
|
|||||||
|
|
||||||
function(make_test_target target_name step_name)
|
function(make_test_target target_name step_name)
|
||||||
add_custom_target(${target_name}
|
add_custom_target(${target_name}
|
||||||
COMMAND ../vendor/mal/runtest.py --deferrable --optional ../tests/${step_name}.mal -- ./${step_name})
|
COMMAND ../vendor/mal/runtest.py --deferrable --optional ../tests/${step_name}.mal -- ./${PROJECT})
|
||||||
add_dependencies(${target_name} ${step_name})
|
add_dependencies(${target_name} ${PROJECT})
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
make_test_target("test0" "step0_repl")
|
make_test_target("test0" "step0_repl")
|
||||||
|
|||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
#+TITLE: mal - Make a Lisp
|
#+TITLE: blaze lisp
|
||||||
#+AUTHOR: Riyyi
|
#+AUTHOR: Riyyi
|
||||||
#+LANGUAGE: en
|
#+LANGUAGE: en
|
||||||
#+OPTIONS: toc:nil
|
#+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
|
** Usage
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
file(COPY ${CMAKE_CURRENT_LIST_DIR}/../lisp DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
(defmacro! cond (fn* [& xs]
|
||||||
|
(if (> (count xs) 0)
|
||||||
|
(list 'if (first xs)
|
||||||
|
(if (> (count xs) 1)
|
||||||
|
(nth xs 1)
|
||||||
|
(throw "odd number of forms to cond"))
|
||||||
|
(cons 'cond (rest (rest xs)))))))
|
||||||
|
|
||||||
|
;; Local Variables:
|
||||||
|
;; eval: (emacs-lisp-mode)
|
||||||
|
;; End:
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
(defmacro! defmacro
|
||||||
|
(fn* [name args & body]
|
||||||
|
`(defmacro! ~name (fn* ~args ~@body))))
|
||||||
|
|
||||||
|
(defmacro defn [name args & body]
|
||||||
|
`(def! ~name (fn* ~args ~@body)))
|
||||||
|
|
||||||
|
(defmacro def [name & body]
|
||||||
|
`(def! ~name ~@body))
|
||||||
|
|
||||||
|
;; Local Variables:
|
||||||
|
;; eval: (emacs-lisp-mode)
|
||||||
|
;; End:
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
(defn load-file [file]
|
||||||
|
"Load the Lisp file named FILE."
|
||||||
|
(eval (read-string (str "(do " (slurp file) "\nnil)"))))
|
||||||
|
|
||||||
|
(defn load [file]
|
||||||
|
"Load the Lisp file named FILE."
|
||||||
|
(eval (read-string (str "(let* [] (do " (slurp file) "))"))))
|
||||||
|
|
||||||
|
;; Local Variables:
|
||||||
|
;; eval: (emacs-lisp-mode)
|
||||||
|
;; End:
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
(def *host-language* "C++")
|
||||||
|
|
||||||
|
;; Local Variables:
|
||||||
|
;; eval: (emacs-lisp-mode)
|
||||||
|
;; End:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
(defn not [object]
|
||||||
|
"Return true if OBJECT is nil or false, and return false otherwise."
|
||||||
|
(if object false true))
|
||||||
|
|
||||||
|
;; Local Variables:
|
||||||
|
;; eval: (emacs-lisp-mode)
|
||||||
|
;; End:
|
||||||
+39
-6
@@ -7,10 +7,11 @@
|
|||||||
#include <cstdint> // int64_t
|
#include <cstdint> // int64_t
|
||||||
#include <memory> // std::static_pointer_cast
|
#include <memory> // std::static_pointer_cast
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility> // std::move
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
#include "environment.h"
|
#include "env/environment.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "forward.h"
|
#include "forward.h"
|
||||||
#include "printer.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)
|
Collection::Collection(ValueVectorIt begin, ValueVectorIt end)
|
||||||
: m_nodes(ValueVector(begin, 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)
|
List::List(ValueVectorIt begin, ValueVectorIt end)
|
||||||
: Collection(begin, 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)
|
Vector::Vector(ValueVectorIt begin, ValueVectorIt end)
|
||||||
: Collection(begin, 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)
|
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)
|
Function::Function(std::string_view name, std::string_view bindings, std::string_view documentation, FunctionType function)
|
||||||
: m_name(name)
|
: Callable()
|
||||||
|
, m_name(name)
|
||||||
|
, m_bindings(bindings)
|
||||||
|
, m_documentation(documentation)
|
||||||
, m_function(function)
|
, 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)
|
Lambda::Lambda(const std::vector<std::string>& bindings, ValuePtr body, EnvironmentPtr env)
|
||||||
: m_bindings(bindings)
|
: Callable()
|
||||||
|
, m_bindings(bindings)
|
||||||
, m_body(body)
|
, m_body(body)
|
||||||
, m_env(env)
|
, m_env(env)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Lambda::Lambda(const Lambda& that)
|
Lambda::Lambda(const Lambda& that)
|
||||||
: m_bindings(that.m_bindings)
|
: Callable()
|
||||||
|
, m_bindings(that.m_bindings)
|
||||||
, m_body(that.m_body)
|
, m_body(that.m_body)
|
||||||
, m_env(that.m_env)
|
, m_env(that.m_env)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -52,7 +52,9 @@ public:
|
|||||||
virtual bool isHashMap() const { return false; }
|
virtual bool isHashMap() const { return false; }
|
||||||
virtual bool isString() const { return false; }
|
virtual bool isString() const { return false; }
|
||||||
virtual bool isKeyword() const { return false; }
|
virtual bool isKeyword() const { return false; }
|
||||||
|
virtual bool isNumeric() const { return false; }
|
||||||
virtual bool isNumber() const { return false; }
|
virtual bool isNumber() const { return false; }
|
||||||
|
virtual bool isDecimal() const { return false; }
|
||||||
virtual bool isConstant() const { return false; }
|
virtual bool isConstant() const { return false; }
|
||||||
virtual bool isSymbol() const { return false; }
|
virtual bool isSymbol() const { return false; }
|
||||||
virtual bool isCallable() const { return false; }
|
virtual bool isCallable() const { return false; }
|
||||||
@@ -101,11 +103,18 @@ public:
|
|||||||
ValuePtr front() const { return m_nodes.front(); }
|
ValuePtr front() const { return m_nodes.front(); }
|
||||||
ValueVector rest() const;
|
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:
|
protected:
|
||||||
Collection() = default;
|
Collection() = default;
|
||||||
Collection(const ValueVector& nodes);
|
Collection(const ValueVector& nodes);
|
||||||
|
Collection(ValueVector&& nodes) noexcept;
|
||||||
Collection(ValueVectorIt begin, ValueVectorIt end);
|
Collection(ValueVectorIt begin, ValueVectorIt end);
|
||||||
Collection(ValueVectorConstIt begin, ValueVectorConstIt end);
|
Collection(ValueVectorConstIt begin, ValueVectorConstIt end);
|
||||||
Collection(const Collection& that, ValuePtr meta);
|
Collection(const Collection& that, ValuePtr meta);
|
||||||
@@ -129,6 +138,7 @@ class List final : public Collection {
|
|||||||
public:
|
public:
|
||||||
List() = default;
|
List() = default;
|
||||||
List(const ValueVector& nodes);
|
List(const ValueVector& nodes);
|
||||||
|
List(ValueVector&& nodes) noexcept;
|
||||||
List(ValueVectorIt begin, ValueVectorIt end);
|
List(ValueVectorIt begin, ValueVectorIt end);
|
||||||
List(ValueVectorConstIt begin, ValueVectorConstIt end);
|
List(ValueVectorConstIt begin, ValueVectorConstIt end);
|
||||||
List(const List& that, ValuePtr meta);
|
List(const List& that, ValuePtr meta);
|
||||||
@@ -154,6 +164,7 @@ class Vector final : public Collection {
|
|||||||
public:
|
public:
|
||||||
Vector() = default;
|
Vector() = default;
|
||||||
Vector(const ValueVector& nodes);
|
Vector(const ValueVector& nodes);
|
||||||
|
Vector(ValueVector&& nodes) noexcept;
|
||||||
Vector(ValueVectorIt begin, ValueVectorIt end);
|
Vector(ValueVectorIt begin, ValueVectorIt end);
|
||||||
Vector(ValueVectorConstIt begin, ValueVectorConstIt end);
|
Vector(ValueVectorConstIt begin, ValueVectorConstIt end);
|
||||||
Vector(const Vector& that, ValuePtr meta);
|
Vector(const Vector& that, ValuePtr meta);
|
||||||
@@ -229,6 +240,7 @@ private:
|
|||||||
class Keyword final : public Value {
|
class Keyword final : public Value {
|
||||||
public:
|
public:
|
||||||
Keyword(const std::string& data);
|
Keyword(const std::string& data);
|
||||||
|
Keyword(int64_t number);
|
||||||
virtual ~Keyword() = default;
|
virtual ~Keyword() = default;
|
||||||
|
|
||||||
virtual bool isKeyword() const override { return true; }
|
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
|
// 123
|
||||||
class Number final : public Value {
|
class Number final : public Numeric {
|
||||||
public:
|
public:
|
||||||
Number(int64_t number);
|
Number(int64_t number);
|
||||||
virtual ~Number() = default;
|
virtual ~Number() = default;
|
||||||
@@ -258,6 +281,22 @@ private:
|
|||||||
const int64_t m_number { 0 };
|
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
|
// true, false, nil
|
||||||
@@ -322,11 +361,13 @@ using FunctionType = std::function<ValuePtr(ValueVectorConstIt, ValueVectorConst
|
|||||||
|
|
||||||
class Function final : public Callable {
|
class Function final : public Callable {
|
||||||
public:
|
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);
|
Function(const Function& that, ValuePtr meta);
|
||||||
virtual ~Function() = default;
|
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; }
|
FunctionType function() const { return m_function; }
|
||||||
|
|
||||||
WITH_META(Function);
|
WITH_META(Function);
|
||||||
@@ -334,7 +375,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
virtual bool isFunction() const override { return true; }
|
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;
|
const FunctionType m_function;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -414,9 +457,15 @@ inline bool Value::fastIs<String>() const { return isString(); }
|
|||||||
template<>
|
template<>
|
||||||
inline bool Value::fastIs<Keyword>() const { return isKeyword(); }
|
inline bool Value::fastIs<Keyword>() const { return isKeyword(); }
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline bool Value::fastIs<Numeric>() const { return isNumeric(); }
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bool Value::fastIs<Number>() const { return isNumber(); }
|
inline bool Value::fastIs<Number>() const { return isNumber(); }
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline bool Value::fastIs<Decimal>() const { return isDecimal(); }
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bool Value::fastIs<Constant>() const { return isConstant(); }
|
inline bool Value::fastIs<Constant>() const { return isConstant(); }
|
||||||
|
|
||||||
|
|||||||
Vendored
+174
@@ -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
|
||||||
Vendored
+68
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
Vendored
+166
@@ -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
|
||||||
Vendored
+149
@@ -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
|
||||||
Vendored
+75
@@ -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
|
||||||
Vendored
+60
@@ -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
|
||||||
Vendored
+93
@@ -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
|
||||||
Vendored
+166
@@ -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
|
||||||
Vendored
+51
@@ -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
|
||||||
Vendored
+143
@@ -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
|
||||||
Vendored
+75
@@ -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
|
||||||
Vendored
+18
@@ -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)
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
+376
-46
@@ -4,16 +4,26 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* 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 <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "ruc/format/color.h"
|
||||||
|
#include "ruc/format/format.h"
|
||||||
|
#include "ruc/format/print.h"
|
||||||
|
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
#include "environment.h"
|
#include "env/environment.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "eval.h"
|
#include "eval.h"
|
||||||
#include "forward.h"
|
#include "forward.h"
|
||||||
|
#include "macro.h"
|
||||||
|
#include "printer.h"
|
||||||
|
#include "settings.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
@@ -21,7 +31,7 @@ namespace blaze {
|
|||||||
|
|
||||||
static ValuePtr evalQuasiQuoteImpl(ValuePtr ast);
|
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)
|
ValuePtr Eval::evalDef(const ValueVector& nodes, EnvironmentPtr env)
|
||||||
{
|
{
|
||||||
CHECK_ARG_COUNT_IS("def!", nodes.size(), 2);
|
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);
|
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)
|
ValuePtr Eval::evalDefMacro(const ValueVector& nodes, EnvironmentPtr env)
|
||||||
{
|
{
|
||||||
CHECK_ARG_COUNT_IS("defmacro!", nodes.size(), 2);
|
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));
|
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)
|
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
|
// First element needs to be a List or Vector
|
||||||
VALUE_CAST(collection, Collection, nodes.front());
|
VALUE_CAST(collection, Collection, nodes.front());
|
||||||
const auto& collection_nodes = collection->nodes();
|
const auto& collection_nodes = collection->nodesRead();
|
||||||
|
|
||||||
std::vector<std::string> bindings;
|
std::vector<std::string> bindings;
|
||||||
bindings.reserve(collection_nodes.size());
|
bindings.reserve(collection_nodes.size());
|
||||||
@@ -83,19 +231,25 @@ ValuePtr Eval::evalFn(const ValueVector& nodes, EnvironmentPtr env)
|
|||||||
bindings.push_back(symbol->symbol());
|
bindings.push_back(symbol->symbol());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove limitation of 3 arguments
|
// If more than one s-exp in lambda body, wrap in list
|
||||||
// Wrap all other nodes in list and add that as lambda body
|
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);
|
||||||
|
|
||||||
|
return makePtr<Lambda>(bindings, body, env);
|
||||||
|
}
|
||||||
|
|
||||||
return makePtr<Lambda>(bindings, *std::next(nodes.begin()), env);
|
return makePtr<Lambda>(bindings, *std::next(nodes.begin()), env);
|
||||||
}
|
}
|
||||||
|
|
||||||
ValuePtr Eval::evalMacroExpand(const ValueVector& nodes, EnvironmentPtr env)
|
// -----------------------------------------
|
||||||
{
|
|
||||||
CHECK_ARG_COUNT_IS("macroexpand", nodes.size(), 1);
|
|
||||||
|
|
||||||
return macroExpand(nodes.front(), env);
|
|
||||||
}
|
|
||||||
|
|
||||||
// (quasiquoteexpand x)
|
// (quasiquoteexpand x)
|
||||||
|
EVAL_FUNCTION("quasiquoteexpand", "arg", ""); // TODO
|
||||||
ValuePtr Eval::evalQuasiQuoteExpand(const ValueVector& nodes)
|
ValuePtr Eval::evalQuasiQuoteExpand(const ValueVector& nodes)
|
||||||
{
|
{
|
||||||
CHECK_ARG_COUNT_IS("quasiquoteexpand", nodes.size(), 1);
|
CHECK_ARG_COUNT_IS("quasiquoteexpand", nodes.size(), 1);
|
||||||
@@ -103,7 +257,7 @@ ValuePtr Eval::evalQuasiQuoteExpand(const ValueVector& nodes)
|
|||||||
return evalQuasiQuoteImpl(nodes.front());
|
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)
|
ValuePtr Eval::evalQuote(const ValueVector& nodes)
|
||||||
{
|
{
|
||||||
CHECK_ARG_COUNT_IS("quote", nodes.size(), 1);
|
CHECK_ARG_COUNT_IS("quote", nodes.size(), 1);
|
||||||
@@ -111,48 +265,69 @@ ValuePtr Eval::evalQuote(const ValueVector& nodes)
|
|||||||
return nodes.front();
|
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)
|
ValuePtr Eval::evalTry(const ValueVector& nodes, EnvironmentPtr env)
|
||||||
{
|
{
|
||||||
CHECK_ARG_COUNT_AT_LEAST("try*", nodes.size(), 1);
|
CHECK_ARG_COUNT_AT_LEAST("try*", nodes.size(), 1);
|
||||||
|
|
||||||
// Try 'x'
|
// Is last node a catch* block
|
||||||
m_ast = nodes.front();
|
bool is_last_node_catch = false;
|
||||||
m_env = env;
|
std::span<const ValuePtr> catch_nodes;
|
||||||
auto result = evalImpl();
|
if (nodes.size() >= 2 && is<List>(nodes.back().get())) {
|
||||||
|
VALUE_CAST(list, List, nodes.back());
|
||||||
if (!Error::the().hasAnyError()) {
|
catch_nodes = list->nodesRead();
|
||||||
return result;
|
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;
|
||||||
}
|
}
|
||||||
if (nodes.size() == 1) {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dont have to eval on malformed (catch*)
|
||||||
|
if (Error::the().hasAnyError()) {
|
||||||
return nullptr;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
// Catch
|
// Catch
|
||||||
|
|
||||||
|
if (!is_last_node_catch) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the error message
|
// Get the error message
|
||||||
auto error = (Error::the().hasOtherError())
|
auto error = (Error::the().hasOtherError())
|
||||||
? makePtr<String>(Error::the().otherError())
|
? makePtr<String>(Error::the().otherError())
|
||||||
: Error::the().exception();
|
: Error::the().exception();
|
||||||
Error::the().clearErrors();
|
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())));
|
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);
|
auto catch_env = Environment::create(env);
|
||||||
catch_env->set(catch_binding->symbol(), error);
|
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_ast = catch_nodes.back();
|
||||||
m_env = catch_env;
|
m_env = catch_env;
|
||||||
return evalImpl();
|
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)
|
void Eval::evalDo(const ValueVector& nodes, EnvironmentPtr env)
|
||||||
{
|
{
|
||||||
CHECK_ARG_COUNT_AT_LEAST("do", nodes.size(), 1, void());
|
CHECK_ARG_COUNT_AT_LEAST("do", nodes.size(), 1, void());
|
||||||
@@ -178,7 +380,11 @@ void Eval::evalDo(const ValueVector& nodes, EnvironmentPtr env)
|
|||||||
return; // TCO
|
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)
|
void Eval::evalIf(const ValueVector& nodes, EnvironmentPtr env)
|
||||||
{
|
{
|
||||||
CHECK_ARG_COUNT_BETWEEN("if", nodes.size(), 2, 3, void());
|
CHECK_ARG_COUNT_BETWEEN("if", nodes.size(), 2, 3, void());
|
||||||
@@ -202,14 +408,19 @@ void Eval::evalIf(const ValueVector& nodes, EnvironmentPtr env)
|
|||||||
return; // TCO
|
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)
|
void Eval::evalLet(const ValueVector& nodes, EnvironmentPtr env)
|
||||||
{
|
{
|
||||||
CHECK_ARG_COUNT_IS("let*", nodes.size(), 2, void());
|
CHECK_ARG_COUNT_IS("let*", nodes.size(), 2, void());
|
||||||
|
|
||||||
// First argument needs to be a List or Vector
|
// First argument needs to be a List or Vector
|
||||||
VALUE_CAST(bindings, Collection, nodes.front(), void());
|
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
|
// List or Vector needs to have an even number of elements
|
||||||
CHECK_ARG_COUNT_EVEN("bindings", binding_nodes.size(), void());
|
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)
|
static bool isSymbol(ValuePtr value, const std::string& symbol)
|
||||||
{
|
{
|
||||||
if (!is<Symbol>(value.get())) {
|
if (!is<Symbol>(value.get())) {
|
||||||
return false;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,7 +550,7 @@ static ValuePtr startsWith(ValuePtr ast, const std::string& symbol)
|
|||||||
return nullptr;
|
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)) {
|
if (nodes.empty() || !isSymbol(nodes.front(), symbol)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -294,10 +586,10 @@ static ValuePtr evalQuasiQuoteImpl(ValuePtr ast)
|
|||||||
|
|
||||||
ValuePtr result = makePtr<List>();
|
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)
|
// `() 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& elt = *it;
|
||||||
|
|
||||||
const auto splice_unquote = startsWith(elt, "splice-unquote"); // (list 2 2 2)
|
const auto splice_unquote = startsWith(elt, "splice-unquote"); // (list 2 2 2)
|
||||||
@@ -320,6 +612,7 @@ static ValuePtr evalQuasiQuoteImpl(ValuePtr ast)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// (quasiquote x)
|
// (quasiquote x)
|
||||||
|
EVAL_FUNCTION("quasiquote", "arg", R"()"); // TODO
|
||||||
void Eval::evalQuasiQuote(const ValueVector& nodes, EnvironmentPtr env)
|
void Eval::evalQuasiQuote(const ValueVector& nodes, EnvironmentPtr env)
|
||||||
{
|
{
|
||||||
CHECK_ARG_COUNT_IS("quasiquote", nodes.size(), 1, void());
|
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
|
} // namespace blaze
|
||||||
|
|||||||
+75
-93
@@ -4,14 +4,14 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <iterator> // sd::advance, std::next, std::prev
|
#include <iterator> // std::advance, std::next, std::prev
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory> // std::static_pointer_cast
|
#include <memory> // std::static_pointer_cast
|
||||||
#include <span> // std::span
|
#include <span> // std::span
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
#include "environment.h"
|
#include "env/environment.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "eval.h"
|
#include "eval.h"
|
||||||
#include "forward.h"
|
#include "forward.h"
|
||||||
@@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
namespace blaze {
|
namespace blaze {
|
||||||
|
|
||||||
|
std::vector<SpecialFormParts> Eval::s_special_form_parts;
|
||||||
|
|
||||||
Eval::Eval(ValuePtr ast, EnvironmentPtr env)
|
Eval::Eval(ValuePtr ast, EnvironmentPtr env)
|
||||||
: m_ast(ast)
|
: m_ast(ast)
|
||||||
, m_env(env)
|
, 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()
|
void Eval::eval()
|
||||||
{
|
{
|
||||||
m_ast = evalImpl();
|
m_ast = evalImpl();
|
||||||
@@ -39,6 +46,10 @@ ValuePtr Eval::evalImpl()
|
|||||||
ValuePtr ast = nullptr;
|
ValuePtr ast = nullptr;
|
||||||
EnvironmentPtr env = nullptr;
|
EnvironmentPtr env = nullptr;
|
||||||
|
|
||||||
|
if (env == nullptr) {
|
||||||
|
env = m_outer_env;
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (Error::the().hasAnyError()) {
|
if (Error::the().hasAnyError()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -47,18 +58,17 @@ ValuePtr Eval::evalImpl()
|
|||||||
ast = m_ast;
|
ast = m_ast;
|
||||||
env = m_env;
|
env = m_env;
|
||||||
|
|
||||||
if (env == nullptr) {
|
if (is<Symbol>(ast.get())) {
|
||||||
env = m_outer_env;
|
return evalSymbol(ast, env);
|
||||||
}
|
}
|
||||||
|
if (is<Vector>(ast.get())) {
|
||||||
if (!is<List>(ast.get())) {
|
return evalVector(ast, env);
|
||||||
return evalAst(ast, env);
|
}
|
||||||
|
if (is<HashMap>(ast.get())) {
|
||||||
|
return evalHashMap(ast, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
ast = macroExpand(ast, env);
|
|
||||||
|
|
||||||
if (!is<List>(ast.get())) {
|
if (!is<List>(ast.get())) {
|
||||||
return evalAst(ast, env);
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto list = std::static_pointer_cast<List>(ast);
|
auto list = std::static_pointer_cast<List>(ast);
|
||||||
@@ -77,12 +87,12 @@ ValuePtr Eval::evalImpl()
|
|||||||
if (symbol == "defmacro!") {
|
if (symbol == "defmacro!") {
|
||||||
return evalDefMacro(nodes, env);
|
return evalDefMacro(nodes, env);
|
||||||
}
|
}
|
||||||
|
if (symbol == "describe") {
|
||||||
|
return evalDescribe(nodes, env);
|
||||||
|
}
|
||||||
if (symbol == "fn*") {
|
if (symbol == "fn*") {
|
||||||
return evalFn(nodes, env);
|
return evalFn(nodes, env);
|
||||||
}
|
}
|
||||||
if (symbol == "macroexpand") {
|
|
||||||
return evalMacroExpand(nodes, env);
|
|
||||||
}
|
|
||||||
if (symbol == "quasiquoteexpand") {
|
if (symbol == "quasiquoteexpand") {
|
||||||
return evalQuasiQuoteExpand(nodes);
|
return evalQuasiQuoteExpand(nodes);
|
||||||
}
|
}
|
||||||
@@ -93,6 +103,10 @@ ValuePtr Eval::evalImpl()
|
|||||||
return evalTry(nodes, env);
|
return evalTry(nodes, env);
|
||||||
}
|
}
|
||||||
// Tail call optimized functions
|
// Tail call optimized functions
|
||||||
|
if (symbol == "and") {
|
||||||
|
evalAnd(nodes, env);
|
||||||
|
continue; // TCO
|
||||||
|
}
|
||||||
if (symbol == "do") {
|
if (symbol == "do") {
|
||||||
evalDo(nodes, env);
|
evalDo(nodes, env);
|
||||||
continue; // TCO
|
continue; // TCO
|
||||||
@@ -105,40 +119,61 @@ ValuePtr Eval::evalImpl()
|
|||||||
evalLet(nodes, env);
|
evalLet(nodes, env);
|
||||||
continue; // TCO
|
continue; // TCO
|
||||||
}
|
}
|
||||||
|
if (symbol == "macroexpand-1") {
|
||||||
|
evalMacroExpand1(nodes, env);
|
||||||
|
continue; // TCO
|
||||||
|
}
|
||||||
|
if (symbol == "or") {
|
||||||
|
evalOr(nodes, env);
|
||||||
|
continue; // TCO
|
||||||
|
}
|
||||||
if (symbol == "quasiquote") {
|
if (symbol == "quasiquote") {
|
||||||
evalQuasiQuote(nodes, env);
|
evalQuasiQuote(nodes, env);
|
||||||
continue; // TCO
|
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) {
|
if (is<Macro>(evaluated_front.get())) { // FIXME
|
||||||
return nullptr;
|
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
|
// Regular list
|
||||||
if (is<Lambda>(evaluated_list->front().get())) {
|
if (is<Lambda>(evaluated_front.get())) {
|
||||||
auto lambda = std::static_pointer_cast<Lambda>(evaluated_list->front());
|
auto lambda = std::static_pointer_cast<Lambda>(evaluated_front);
|
||||||
|
|
||||||
m_ast = lambda->body();
|
m_ast = lambda->body();
|
||||||
m_env = Environment::create(lambda, evaluated_list->rest());
|
m_env = Environment::create(lambda, std::move(evaluated_nodes));
|
||||||
continue; // TCO
|
continue; // TCO
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function call
|
// 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) {
|
|
||||||
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());
|
auto result = env->get(std::static_pointer_cast<Symbol>(ast)->symbol());
|
||||||
if (!result) {
|
if (!result) {
|
||||||
Error::the().add(::format("'{}' not found", ast));
|
Error::the().add(::format("'{}' not found", ast));
|
||||||
@@ -147,8 +182,10 @@ ValuePtr Eval::evalAst(ValuePtr ast, EnvironmentPtr env)
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else if (is<Collection>(ast_raw_ptr)) {
|
|
||||||
const auto& nodes = std::static_pointer_cast<Collection>(ast)->nodes();
|
ValuePtr Eval::evalVector(ValuePtr ast, EnvironmentPtr env)
|
||||||
|
{
|
||||||
|
const auto& nodes = std::static_pointer_cast<Collection>(ast)->nodesRead();
|
||||||
size_t count = nodes.size();
|
size_t count = nodes.size();
|
||||||
auto evaluated_nodes = ValueVector(count);
|
auto evaluated_nodes = ValueVector(count);
|
||||||
|
|
||||||
@@ -162,13 +199,11 @@ ValuePtr Eval::evalAst(ValuePtr ast, EnvironmentPtr env)
|
|||||||
evaluated_nodes.at(i) = eval_node;
|
evaluated_nodes.at(i) = eval_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is<List>(ast_raw_ptr)) {
|
|
||||||
return makePtr<List>(evaluated_nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return makePtr<Vector>(evaluated_nodes);
|
return makePtr<Vector>(evaluated_nodes);
|
||||||
}
|
}
|
||||||
else if (is<HashMap>(ast_raw_ptr)) {
|
|
||||||
|
ValuePtr Eval::evalHashMap(ValuePtr ast, EnvironmentPtr env)
|
||||||
|
{
|
||||||
const auto& elements = std::static_pointer_cast<HashMap>(ast)->elements();
|
const auto& elements = std::static_pointer_cast<HashMap>(ast)->elements();
|
||||||
Elements evaluated_elements;
|
Elements evaluated_elements;
|
||||||
for (const auto& element : elements) {
|
for (const auto& element : elements) {
|
||||||
@@ -184,71 +219,18 @@ ValuePtr Eval::evalAst(ValuePtr ast, EnvironmentPtr env)
|
|||||||
return makePtr<HashMap>(evaluated_elements);
|
return makePtr<HashMap>(evaluated_elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ast;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
// (x y z)
|
ValuePtr Eval::apply(ValuePtr function, const ValueVector& nodes)
|
||||||
bool Eval::isMacroCall(ValuePtr ast, EnvironmentPtr env)
|
|
||||||
{
|
{
|
||||||
auto list = dynamic_cast<List*>(ast.get());
|
if (!is<Function>(function.get())) {
|
||||||
|
Error::the().add(::format("invalid function: {}", function));
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// (x y z)
|
|
||||||
ValuePtr Eval::macroExpand(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();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ast;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
|
|
||||||
ValuePtr Eval::apply(std::shared_ptr<List> evaluated_list)
|
|
||||||
{
|
|
||||||
if (evaluated_list == nullptr) {
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nodes = evaluated_list->nodes();
|
auto func = std::static_pointer_cast<Function>(function)->function();
|
||||||
|
|
||||||
if (!is<Function>(nodes.front().get())) {
|
return func(nodes.begin(), nodes.end());
|
||||||
Error::the().add(::format("invalid function: {}", nodes.front()));
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto function = std::static_pointer_cast<Function>(nodes.front())->function();
|
|
||||||
|
|
||||||
return function(nodes.begin() + 1, nodes.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace blaze
|
} // namespace blaze
|
||||||
|
|||||||
+21
-11
@@ -6,50 +6,60 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <list>
|
#include <vector>
|
||||||
#include <stack>
|
|
||||||
|
|
||||||
#include "environment.h"
|
#include "forward.h" // EnvironmentPtr
|
||||||
#include "forward.h"
|
|
||||||
|
|
||||||
namespace blaze {
|
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 {
|
class Eval {
|
||||||
public:
|
public:
|
||||||
Eval(ValuePtr ast, EnvironmentPtr env);
|
Eval(ValuePtr ast, EnvironmentPtr env);
|
||||||
virtual ~Eval() = default;
|
virtual ~Eval() = default;
|
||||||
|
|
||||||
|
static void registerSpecialForm(SpecialFormParts special_form_parts);
|
||||||
|
|
||||||
void eval();
|
void eval();
|
||||||
|
|
||||||
ValuePtr ast() const { return m_ast; }
|
ValuePtr ast() const { return m_ast; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ValuePtr evalImpl();
|
ValuePtr evalImpl();
|
||||||
ValuePtr evalAst(ValuePtr ast, EnvironmentPtr env);
|
ValuePtr evalSymbol(ValuePtr ast, EnvironmentPtr env);
|
||||||
|
ValuePtr evalVector(ValuePtr ast, EnvironmentPtr env);
|
||||||
bool isMacroCall(ValuePtr ast, EnvironmentPtr env);
|
ValuePtr evalHashMap(ValuePtr ast, EnvironmentPtr env);
|
||||||
ValuePtr macroExpand(ValuePtr ast, EnvironmentPtr env);
|
|
||||||
|
|
||||||
ValuePtr evalDef(const ValueVector& nodes, EnvironmentPtr env);
|
ValuePtr evalDef(const ValueVector& nodes, EnvironmentPtr env);
|
||||||
ValuePtr evalDefMacro(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 evalFn(const ValueVector& nodes, EnvironmentPtr env);
|
||||||
ValuePtr evalMacroExpand(const ValueVector& nodes, EnvironmentPtr env);
|
|
||||||
ValuePtr evalQuasiQuoteExpand(const ValueVector& nodes);
|
ValuePtr evalQuasiQuoteExpand(const ValueVector& nodes);
|
||||||
ValuePtr evalQuote(const ValueVector& nodes);
|
ValuePtr evalQuote(const ValueVector& nodes);
|
||||||
ValuePtr evalTry(const ValueVector& nodes, EnvironmentPtr env);
|
ValuePtr evalTry(const ValueVector& nodes, EnvironmentPtr env);
|
||||||
|
|
||||||
|
void evalAnd(const ValueVector& nodes, EnvironmentPtr env);
|
||||||
void evalDo(const ValueVector& nodes, EnvironmentPtr env);
|
void evalDo(const ValueVector& nodes, EnvironmentPtr env);
|
||||||
void evalIf(const ValueVector& nodes, EnvironmentPtr env);
|
void evalIf(const ValueVector& nodes, EnvironmentPtr env);
|
||||||
void evalLet(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 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;
|
ValuePtr m_ast;
|
||||||
EnvironmentPtr m_env;
|
EnvironmentPtr m_env;
|
||||||
EnvironmentPtr m_outer_env;
|
EnvironmentPtr m_outer_env;
|
||||||
|
|
||||||
|
static std::vector<SpecialFormParts> s_special_form_parts;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace blaze
|
} // namespace blaze
|
||||||
|
|||||||
+8
-4
@@ -18,18 +18,22 @@ class Value;
|
|||||||
typedef std::shared_ptr<Value> ValuePtr;
|
typedef std::shared_ptr<Value> ValuePtr;
|
||||||
typedef std::vector<ValuePtr> ValueVector;
|
typedef std::vector<ValuePtr> ValueVector;
|
||||||
typedef ValueVector::iterator ValueVectorIt;
|
typedef ValueVector::iterator ValueVectorIt;
|
||||||
|
typedef ValueVector::reverse_iterator ValueVectorReverseIt;
|
||||||
typedef ValueVector::const_iterator ValueVectorConstIt;
|
typedef ValueVector::const_iterator ValueVectorConstIt;
|
||||||
|
typedef ValueVector::const_reverse_iterator ValueVectorConstReverseIt;
|
||||||
|
|
||||||
class Environment;
|
class Environment;
|
||||||
typedef std::shared_ptr<Environment> EnvironmentPtr;
|
typedef std::shared_ptr<Environment> EnvironmentPtr;
|
||||||
|
|
||||||
|
class Readline;
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
// Functions
|
// Functions
|
||||||
|
|
||||||
extern ValuePtr readline(const std::string& prompt);
|
// -----------------------------------------
|
||||||
extern ValuePtr read(std::string_view input);
|
// Variables
|
||||||
extern ValuePtr eval(ValuePtr ast, EnvironmentPtr env);
|
|
||||||
|
|
||||||
extern void installFunctions(EnvironmentPtr env);
|
extern Readline g_readline;
|
||||||
|
extern EnvironmentPtr g_outer_env;
|
||||||
|
|
||||||
} // namespace blaze
|
} // namespace blaze
|
||||||
|
|||||||
@@ -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
@@ -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__))
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
+50
-37
@@ -8,12 +8,14 @@
|
|||||||
#include <memory> // std::static_pointer_cast
|
#include <memory> // std::static_pointer_cast
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "ruc/format/color.h"
|
||||||
#include "ruc/format/format.h"
|
#include "ruc/format/format.h"
|
||||||
|
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
#include "printer.h"
|
#include "printer.h"
|
||||||
|
#include "settings.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "util.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()) {
|
if (Error::the().hasAnyError()) {
|
||||||
init();
|
init();
|
||||||
@@ -37,18 +39,18 @@ std::string Printer::print(ValuePtr node, bool print_readably)
|
|||||||
return m_print;
|
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();
|
init();
|
||||||
|
|
||||||
if (node == nullptr) {
|
if (value == nullptr) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
printImpl(node, print_readably);
|
printImpl(value, print_readably);
|
||||||
|
|
||||||
return m_print;
|
return m_print;
|
||||||
}
|
}
|
||||||
@@ -62,33 +64,35 @@ void Printer::init()
|
|||||||
m_print = "";
|
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 {
|
auto printSpacing = [this]() -> void {
|
||||||
if (!m_first_node && !m_previous_node_is_list) {
|
if (!m_first_node && !m_previous_node_is_list) {
|
||||||
m_print += ' ';
|
m_print += ' ';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Value* node_raw_ptr = node.get();
|
Value* value_raw_ptr = value.get();
|
||||||
if (is<Collection>(node_raw_ptr)) {
|
if (is<Collection>(value_raw_ptr)) {
|
||||||
printSpacing();
|
printSpacing();
|
||||||
m_print += (is<List>(node_raw_ptr)) ? '(' : '[';
|
m_print += (is<List>(value_raw_ptr)) ? '(' : '[';
|
||||||
m_first_node = false;
|
m_first_node = false;
|
||||||
m_previous_node_is_list = true;
|
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) {
|
for (auto node : nodes) {
|
||||||
printImpl(node, print_readably);
|
printImpl(node, print_readably);
|
||||||
m_previous_node_is_list = false;
|
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();
|
printSpacing();
|
||||||
m_print += "{";
|
m_print += "{";
|
||||||
m_first_node = false;
|
m_first_node = false;
|
||||||
m_previous_node_is_list = true;
|
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) {
|
for (auto it = elements.begin(); it != elements.end(); ++it) {
|
||||||
m_print += ::format("{} ", (it->first.front() == 0x7f) ? ":" + it->first.substr(1) : '"' + it->first + '"'); // 127
|
m_print += ::format("{} ", (it->first.front() == 0x7f) ? ":" + it->first.substr(1) : '"' + it->first + '"'); // 127
|
||||||
printImpl(it->second, print_readably);
|
printImpl(it->second, print_readably);
|
||||||
@@ -100,8 +104,8 @@ void Printer::printImpl(ValuePtr node, bool print_readably)
|
|||||||
m_previous_node_is_list = false;
|
m_previous_node_is_list = false;
|
||||||
m_print += '}';
|
m_print += '}';
|
||||||
}
|
}
|
||||||
else if (is<String>(node_raw_ptr)) {
|
else if (is<String>(value_raw_ptr)) {
|
||||||
std::string text = std::static_pointer_cast<String>(node)->data();
|
std::string text = std::static_pointer_cast<String>(value)->data();
|
||||||
if (print_readably) {
|
if (print_readably) {
|
||||||
text = replaceAll(text, "\\", "\\\\");
|
text = replaceAll(text, "\\", "\\\\");
|
||||||
text = replaceAll(text, "\"", "\\\"");
|
text = replaceAll(text, "\"", "\\\"");
|
||||||
@@ -109,46 +113,55 @@ void Printer::printImpl(ValuePtr node, bool print_readably)
|
|||||||
text = "\"" + text + "\"";
|
text = "\"" + text + "\"";
|
||||||
}
|
}
|
||||||
printSpacing();
|
printSpacing();
|
||||||
|
if (pretty_print) {
|
||||||
|
m_print += ::format(fg(ruc::format::TerminalColor::BrightGreen), "{}", text);
|
||||||
|
}
|
||||||
|
else {
|
||||||
m_print += ::format("{}", text);
|
m_print += ::format("{}", text);
|
||||||
}
|
}
|
||||||
else if (is<Keyword>(node_raw_ptr)) {
|
}
|
||||||
|
else if (is<Keyword>(value_raw_ptr)) {
|
||||||
printSpacing();
|
printSpacing();
|
||||||
m_print += ::format(":{}", std::static_pointer_cast<Keyword>(node)->keyword().substr(1));
|
m_print += ::format(":{}", std::static_pointer_cast<Keyword>(value)->keyword().substr(1));
|
||||||
}
|
}
|
||||||
else if (is<Number>(node_raw_ptr)) {
|
else if (is<Number>(value_raw_ptr)) {
|
||||||
printSpacing();
|
printSpacing();
|
||||||
m_print += ::format("{}", std::static_pointer_cast<Number>(node)->number());
|
m_print += ::format("{}", std::static_pointer_cast<Number>(value)->number());
|
||||||
}
|
}
|
||||||
else if (is<Constant>(node_raw_ptr)) {
|
else if (is<Decimal>(value_raw_ptr)) {
|
||||||
printSpacing();
|
printSpacing();
|
||||||
std::string value;
|
m_print += ::format("{:.15}", std::static_pointer_cast<Decimal>(value)->decimal());
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
m_print += ::format("{}", value);
|
else if (is<Constant>(value_raw_ptr)) {
|
||||||
}
|
|
||||||
else if (is<Symbol>(node_raw_ptr)) {
|
|
||||||
printSpacing();
|
printSpacing();
|
||||||
m_print += ::format("{}", std::static_pointer_cast<Symbol>(node)->symbol());
|
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;
|
||||||
}
|
}
|
||||||
else if (is<Function>(node_raw_ptr)) {
|
m_print += ::format("{}", constant);
|
||||||
|
}
|
||||||
|
else if (is<Symbol>(value_raw_ptr)) {
|
||||||
printSpacing();
|
printSpacing();
|
||||||
m_print += ::format("#<builtin-function>({})", std::static_pointer_cast<Function>(node)->name());
|
m_print += ::format("{}", std::static_pointer_cast<Symbol>(value)->symbol());
|
||||||
}
|
}
|
||||||
else if (is<Lambda>(node_raw_ptr)) {
|
else if (is<Function>(value_raw_ptr)) {
|
||||||
printSpacing();
|
printSpacing();
|
||||||
m_print += ::format("#<user-function>({:p})", node_raw_ptr);
|
m_print += ::format("#<builtin-function>({})", std::static_pointer_cast<Function>(value)->name());
|
||||||
}
|
}
|
||||||
else if (is<Macro>(node_raw_ptr)) {
|
else if (is<Lambda>(value_raw_ptr)) {
|
||||||
printSpacing();
|
printSpacing();
|
||||||
m_print += ::format("#<user-macro>({:p})", node_raw_ptr);
|
m_print += ::format("#<user-function>({:p})", value_raw_ptr);
|
||||||
}
|
}
|
||||||
else if (is<Atom>(node_raw_ptr)) {
|
else if (is<Macro>(value_raw_ptr)) {
|
||||||
|
printSpacing();
|
||||||
|
m_print += ::format("#<user-macro>({:p})", value_raw_ptr);
|
||||||
|
}
|
||||||
|
else if (is<Atom>(value_raw_ptr)) {
|
||||||
printSpacing();
|
printSpacing();
|
||||||
m_print += "(atom ";
|
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 += ")";
|
m_print += ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -17,12 +17,12 @@ public:
|
|||||||
Printer();
|
Printer();
|
||||||
virtual ~Printer();
|
virtual ~Printer();
|
||||||
|
|
||||||
std::string print(ValuePtr node, bool print_readably = true);
|
std::string print(ValuePtr value, bool print_readably = true);
|
||||||
std::string printNoErrorCheck(ValuePtr node, bool print_readably = true);
|
std::string printNoErrorCheck(ValuePtr value, bool print_readably = true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init();
|
void init();
|
||||||
void printImpl(ValuePtr node, bool print_readably = true);
|
void printImpl(ValuePtr value, bool print_readably = true);
|
||||||
void printError();
|
void printError();
|
||||||
|
|
||||||
bool m_first_node { true };
|
bool m_first_node { true };
|
||||||
|
|||||||
+121
-29
@@ -4,10 +4,12 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <charconv> // std::from_chars
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint64_t
|
#include <cstdint> // uint64_t
|
||||||
#include <cstdlib> // std::strtoll
|
#include <cstdlib> // std::strtoll
|
||||||
#include <memory> // std::static_pointer_cast
|
#include <memory> // std::static_pointer_cast
|
||||||
|
#include <system_error> // std::errc
|
||||||
#include <utility> // std::move
|
#include <utility> // std::move
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
@@ -16,10 +18,15 @@
|
|||||||
|
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
#include "reader.h"
|
#include "reader.h"
|
||||||
|
#include "settings.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
namespace blaze {
|
namespace blaze {
|
||||||
|
|
||||||
|
Reader::Reader()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Reader::Reader(std::vector<Token>&& tokens) noexcept
|
Reader::Reader(std::vector<Token>&& tokens) noexcept
|
||||||
: m_tokens(std::move(tokens))
|
: m_tokens(std::move(tokens))
|
||||||
{
|
{
|
||||||
@@ -142,7 +149,7 @@ ValuePtr Reader::readList()
|
|||||||
nodes.push_back(node);
|
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");
|
Error::the().add("expected ')', got EOF");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -163,7 +170,7 @@ ValuePtr Reader::readVector()
|
|||||||
nodes.push_back(node);
|
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");
|
Error::the().add("expected ']', got EOF");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +203,7 @@ ValuePtr Reader::readHashMap()
|
|||||||
elements.insert_or_assign(HashMap::getKeyString(key), value);
|
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");
|
Error::the().add("expected '}', got EOF");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -301,24 +308,33 @@ ValuePtr Reader::readKeyword()
|
|||||||
|
|
||||||
ValuePtr Reader::readValue()
|
ValuePtr Reader::readValue()
|
||||||
{
|
{
|
||||||
Token token = consume();
|
auto symbol = consume().symbol;
|
||||||
char* end_ptr = nullptr;
|
|
||||||
int64_t result = std::strtoll(token.symbol.c_str(), &end_ptr, 10);
|
int64_t number;
|
||||||
if (end_ptr == token.symbol.c_str() + token.symbol.size()) {
|
auto [_, error] = std::from_chars(symbol.data(), symbol.data() + symbol.size(), number);
|
||||||
return makePtr<Number>(result);
|
if (error == std::errc() && symbol.find('.') == std::string::npos) {
|
||||||
|
return makePtr<Number>(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.symbol == "nil") {
|
double decimal;
|
||||||
|
{
|
||||||
|
auto [_, error] = std::from_chars(symbol.data(), symbol.data() + symbol.size(), decimal);
|
||||||
|
if (error == std::errc()) {
|
||||||
|
return makePtr<Decimal>(decimal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbol == "nil") {
|
||||||
return makePtr<Constant>(Constant::Nil);
|
return makePtr<Constant>(Constant::Nil);
|
||||||
}
|
}
|
||||||
else if (token.symbol == "true") {
|
else if (symbol == "true") {
|
||||||
return makePtr<Constant>(Constant::True);
|
return makePtr<Constant>(Constant::True);
|
||||||
}
|
}
|
||||||
else if (token.symbol == "false") {
|
else if (symbol == "false") {
|
||||||
return makePtr<Constant>(Constant::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)
|
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();
|
Value* node_raw_ptr = node.get();
|
||||||
if (is<Collection>(node_raw_ptr)) {
|
if (is<Collection>(node_raw_ptr)) {
|
||||||
auto nodes = std::static_pointer_cast<List>(node)->nodes();
|
auto container = (is<List>(node_raw_ptr)) ? "List" : "Vector";
|
||||||
print("{}", indentation);
|
auto parens = (is<List>(node_raw_ptr)) ? "()" : "[]";
|
||||||
print(fg(ruc::format::TerminalColor::Blue), "{}Container", (is<List>(node_raw_ptr)) ? "List" : "Vector");
|
pretty_print ? print(blue, "{}", container) : print("{}", container);
|
||||||
print(" <");
|
print(" <");
|
||||||
print(fg(ruc::format::TerminalColor::Blue), "{}", (is<List>(node_raw_ptr)) ? "()" : "{}");
|
pretty_print ? print(blue, "{}", parens) : print("{}", parens);
|
||||||
print(">\n");
|
print(">\n");
|
||||||
m_indentation++;
|
m_indentation++;
|
||||||
|
auto nodes = std::static_pointer_cast<List>(node)->nodesRead();
|
||||||
for (auto node : nodes) {
|
for (auto node : nodes) {
|
||||||
dumpImpl(node);
|
dumpImpl(node);
|
||||||
}
|
}
|
||||||
m_indentation--;
|
m_indentation--;
|
||||||
return;
|
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)) {
|
else if (is<String>(node_raw_ptr)) {
|
||||||
print("{}", indentation);
|
pretty_print ? print(yellow, "StringNode") : print("StringNode");
|
||||||
print(fg(ruc::format::TerminalColor::Yellow), "StringNode");
|
|
||||||
print(" <{}>", node);
|
print(" <{}>", node);
|
||||||
}
|
}
|
||||||
else if (is<Keyword>(node_raw_ptr)) {
|
else if (is<Keyword>(node_raw_ptr)) {
|
||||||
print("{}", indentation);
|
pretty_print ? print(yellow, "KeywordNode") : print("KeywordNode");
|
||||||
print(fg(ruc::format::TerminalColor::Yellow), "KeywordNode");
|
|
||||||
print(" <{}>", node);
|
print(" <{}>", node);
|
||||||
}
|
}
|
||||||
else if (is<Number>(node_raw_ptr)) {
|
else if (is<Number>(node_raw_ptr)) {
|
||||||
print("{}", indentation);
|
pretty_print ? print(yellow, "NumberNode") : print("NumberNode");
|
||||||
print(fg(ruc::format::TerminalColor::Yellow), "NumberNode");
|
print(" <{}>", node);
|
||||||
|
}
|
||||||
|
else if (is<Decimal>(node_raw_ptr)) {
|
||||||
|
pretty_print ? print(yellow, "DecimalNode") : print("DecimalNode");
|
||||||
print(" <{}>", node);
|
print(" <{}>", node);
|
||||||
}
|
}
|
||||||
else if (is<Constant>(node_raw_ptr)) {
|
else if (is<Constant>(node_raw_ptr)) {
|
||||||
print("{}", indentation);
|
pretty_print ? print(yellow, "ValueNode") : print("ValueNode");
|
||||||
print(fg(ruc::format::TerminalColor::Yellow), "ValueNode");
|
|
||||||
print(" <{}>", node);
|
print(" <{}>", node);
|
||||||
}
|
}
|
||||||
else if (is<Symbol>(node_raw_ptr)) {
|
else if (is<Symbol>(node_raw_ptr)) {
|
||||||
print("{}", indentation);
|
pretty_print ? print(yellow, "SymbolNode") : print("SymbolNode");
|
||||||
print(fg(ruc::format::TerminalColor::Yellow), "SymbolNode");
|
|
||||||
print(" <{}>", node);
|
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");
|
print("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -13,17 +13,20 @@
|
|||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
|
|
||||||
|
#define INDENTATION_WIDTH 2
|
||||||
|
|
||||||
namespace blaze {
|
namespace blaze {
|
||||||
|
|
||||||
// Parsing -> creates AST
|
// Parsing -> creates AST
|
||||||
class Reader {
|
class Reader {
|
||||||
public:
|
public:
|
||||||
|
Reader();
|
||||||
Reader(std::vector<Token>&& tokens) noexcept;
|
Reader(std::vector<Token>&& tokens) noexcept;
|
||||||
virtual ~Reader();
|
virtual ~Reader();
|
||||||
|
|
||||||
void read();
|
void read();
|
||||||
|
|
||||||
void dump();
|
void dump(ValuePtr node = nullptr);
|
||||||
|
|
||||||
ValuePtr node() { return m_node; }
|
ValuePtr node() { return m_node; }
|
||||||
|
|
||||||
|
|||||||
+100
@@ -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
@@ -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
|
||||||
@@ -4,7 +4,14 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* 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 "settings.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
namespace blaze {
|
namespace blaze {
|
||||||
|
|
||||||
@@ -14,4 +21,10 @@ std::string_view Settings::get(std::string_view key) const
|
|||||||
return m_settings.at(key);
|
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
|
} // namespace blaze
|
||||||
|
|||||||
+4
-4
@@ -6,12 +6,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ruc/meta/assert.h"
|
|
||||||
#include "ruc/singleton.h"
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
|
||||||
|
#include "ruc/singleton.h"
|
||||||
|
|
||||||
namespace blaze {
|
namespace blaze {
|
||||||
|
|
||||||
@@ -23,6 +21,8 @@ public:
|
|||||||
std::string_view get(std::string_view key) const;
|
std::string_view get(std::string_view key) const;
|
||||||
void set(std::string_view key, std::string_view value) { m_settings[key] = value; };
|
void set(std::string_view key, std::string_view value) { m_settings[key] = value; };
|
||||||
|
|
||||||
|
bool getEnvBool(std::string_view key) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<std::string_view, std::string_view> m_settings;
|
std::unordered_map<std::string_view, std::string_view> m_settings;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
Vendored
+1
-1
Submodule vendor/ruc updated: 07c9f9959d...c8e4ae884e
Reference in New Issue
Block a user