diff --git a/CMakeLists.txt b/CMakeLists.txt index fb62bc4..ca92e99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,3 +119,7 @@ add_dependencies(test8 ${PROJECT}) add_custom_target(test9 COMMAND env STEP=step_env MAL_IMPL=js ../vendor/mal/runtest.py --deferrable --optional ../vendor/mal/tests/step9_try.mal -- ./${PROJECT}) add_dependencies(test9 ${PROJECT}) + +add_custom_target(testA + COMMAND env STEP=step_env MAL_IMPL=js ../vendor/mal/runtest.py --deferrable --optional ../vendor/mal/tests/stepA_mal.mal -- ./${PROJECT}) +add_dependencies(testA ${PROJECT}) diff --git a/lib b/lib new file mode 120000 index 0000000..bfa6c1d --- /dev/null +++ b/lib @@ -0,0 +1 @@ +vendor/mal/impls/lib \ No newline at end of file diff --git a/mal b/mal new file mode 120000 index 0000000..501d4ef --- /dev/null +++ b/mal @@ -0,0 +1 @@ +vendor/mal/impls/mal \ No newline at end of file diff --git a/src/ast.cpp b/src/ast.cpp index 8464be3..a26b4d9 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -17,6 +17,18 @@ namespace blaze { +ValuePtr Value::withMeta(ValuePtr meta) const +{ + return withMetaImpl(meta); +} + +ValuePtr Value::meta() const +{ + return (m_meta == nullptr) ? makePtr() : m_meta; +} + +// ----------------------------------------- + Collection::Collection(const std::list& nodes) : m_nodes(nodes) { @@ -27,6 +39,12 @@ Collection::Collection(ValueListConstIt begin, ValueListConstIt end) { } +Collection::Collection(const Collection& that, ValuePtr meta) + : Value(meta) + , m_nodes(that.m_nodes) +{ +} + void Collection::add(ValuePtr node) { if (node == nullptr) { @@ -53,6 +71,11 @@ ValueList Collection::rest() const // ----------------------------------------- +List::List(const List& that, ValuePtr meta) + : Collection(that, meta) +{ +} + List::List(const std::list& nodes) : Collection(nodes) { @@ -75,6 +98,11 @@ Vector::Vector(ValueListConstIt begin, ValueListConstIt end) { } +Vector::Vector(const Vector& that, ValuePtr meta) + : Collection(that, meta) +{ +} + // ----------------------------------------- HashMap::HashMap(const Elements& elements) @@ -82,6 +110,12 @@ HashMap::HashMap(const Elements& elements) { } +HashMap::HashMap(const HashMap& that, ValuePtr meta) + : Value(meta) + , m_elements(that.m_elements) +{ +} + void HashMap::add(const std::string& key, ValuePtr value) { if (value == nullptr) { @@ -157,6 +191,11 @@ String::String(const std::string& data) { } +String::String(char character) + : m_data(std::string(1, character)) +{ +} + // ----------------------------------------- Keyword::Keyword(const std::string& data) @@ -192,12 +231,26 @@ Symbol::Symbol(const std::string& symbol) // ----------------------------------------- +Callable::Callable(ValuePtr meta) + : Value(meta) +{ +} + +// ----------------------------------------- + Function::Function(const std::string& name, FunctionType function) : m_name(name) , m_function(function) { } +Function::Function(const Function& that, ValuePtr meta) + : Callable(meta) + , m_name(that.m_name) + , m_function(that.m_function) +{ +} + // ----------------------------------------- Lambda::Lambda(const std::vector& bindings, ValuePtr body, EnvironmentPtr env) @@ -215,6 +268,15 @@ Lambda::Lambda(std::shared_ptr that, bool is_macro) { } +Lambda::Lambda(const Lambda& that, ValuePtr meta) + : Callable(meta) + , m_bindings(that.m_bindings) + , m_body(that.m_body) + , m_env(that.m_env) + , m_is_macro(that.m_is_macro) +{ +} + // ----------------------------------------- Atom::Atom(ValuePtr pointer) diff --git a/src/ast.h b/src/ast.h index c51ea52..f064c3f 100644 --- a/src/ast.h +++ b/src/ast.h @@ -24,10 +24,23 @@ namespace blaze { +template +std::shared_ptr makePtr(Args&&... args) +{ + return std::make_shared(std::forward(args)...); +} + +// ----------------------------------------- + class Value { public: virtual ~Value() = default; + virtual ValuePtr withMetaImpl(ValuePtr meta) const = 0; + + ValuePtr withMeta(ValuePtr meta) const; + ValuePtr meta() const; + std::string className() const { return typeid(*this).name(); } template @@ -49,8 +62,28 @@ public: protected: Value() {} + + Value(ValuePtr meta) + : m_meta(meta) + { + } + + ValuePtr m_meta; }; +#define WITH_META(Type) \ + virtual ValuePtr withMetaImpl(ValuePtr meta) const override \ + { \ + return makePtr(*this, meta); \ + } + +#define WITH_NO_META() \ + virtual ValuePtr withMetaImpl(ValuePtr meta) const override \ + { \ + (void)meta; \ + return nullptr; \ + } + // ----------------------------------------- template @@ -63,6 +96,7 @@ public: void add(ValuePtr node); void addFront(ValuePtr node); + // TODO: rename size -> count size_t size() const { return m_nodes.size(); } bool empty() const { return m_nodes.size() == 0; } @@ -75,6 +109,7 @@ protected: Collection() = default; Collection(const ValueList& nodes); Collection(ValueListConstIt begin, ValueListConstIt end); + Collection(const Collection& that, ValuePtr meta); template Collection(std::shared_ptr... nodes) @@ -96,6 +131,7 @@ public: List() = default; List(const std::list& nodes); List(ValueListConstIt begin, ValueListConstIt end); + List(const List& that, ValuePtr meta); template List(std::shared_ptr... nodes) @@ -105,6 +141,8 @@ public: virtual ~List() = default; + WITH_META(List); + private: virtual bool isList() const override { return true; } }; @@ -117,6 +155,7 @@ public: Vector() = default; Vector(const std::list& nodes); Vector(ValueListConstIt begin, ValueListConstIt end); + Vector(const Vector& that, ValuePtr meta); template Vector(std::shared_ptr... nodes) @@ -126,6 +165,8 @@ public: virtual ~Vector() = default; + WITH_META(Vector); + private: virtual bool isVector() const override { return true; } }; @@ -139,6 +180,7 @@ public: HashMap() = default; HashMap(const Elements& elements); + HashMap(const HashMap& that, ValuePtr meta); virtual ~HashMap() = default; void add(const std::string& key, ValuePtr value); @@ -154,6 +196,8 @@ public: size_t size() const { return m_elements.size(); } bool empty() const { return m_elements.size() == 0; } + WITH_META(HashMap); + private: virtual bool isHashMap() const override { return true; } @@ -168,9 +212,13 @@ private: class String final : public Value { public: String(const std::string& data); + String(char character); virtual ~String() = default; const std::string& data() const { return m_data; } + bool empty() const { return m_data.empty(); } + + WITH_NO_META(); private: virtual bool isString() const override { return true; } @@ -190,6 +238,8 @@ public: const std::string& keyword() const { return m_data; } + WITH_NO_META(); + private: const std::string m_data; }; @@ -203,6 +253,8 @@ public: int64_t number() const { return m_number; } + WITH_NO_META(); + private: virtual bool isNumber() const override { return true; } @@ -227,6 +279,8 @@ public: State state() const { return m_state; } + WITH_NO_META(); + private: virtual bool isConstant() const override { return true; } @@ -243,6 +297,8 @@ public: const std::string& symbol() const { return m_symbol; } + WITH_NO_META(); + private: virtual bool isSymbol() const override { return true; } @@ -257,6 +313,7 @@ public: protected: Callable() = default; + Callable(ValuePtr meta); private: virtual bool isCallable() const override { return true; } @@ -269,11 +326,14 @@ using FunctionType = std::function)>; class Function final : public Callable { public: Function(const std::string& name, FunctionType function); + Function(const Function& that, ValuePtr meta); virtual ~Function() = default; const std::string& name() const { return m_name; } FunctionType function() const { return m_function; } + WITH_META(Function); + private: virtual bool isFunction() const override { return true; } @@ -287,6 +347,7 @@ class Lambda final : public Callable { public: Lambda(const std::vector& bindings, ValuePtr body, EnvironmentPtr env); Lambda(std::shared_ptr that, bool is_macro); + Lambda(const Lambda& that, ValuePtr meta); virtual ~Lambda() = default; const std::vector& bindings() const { return m_bindings; } @@ -294,6 +355,8 @@ public: EnvironmentPtr env() const { return m_env; } bool isMacro() const { return m_is_macro; } + WITH_META(Lambda); + private: virtual bool isLambda() const override { return true; } @@ -314,6 +377,8 @@ public: ValuePtr reset(ValuePtr value) { return m_value = value; } ValuePtr deref() const { return m_value; } + WITH_NO_META(); + private: virtual bool isAtom() const override { return true; } @@ -322,14 +387,6 @@ private: // ----------------------------------------- -template -std::shared_ptr makePtr(Args&&... args) -{ - return std::make_shared(std::forward(args)...); -} - -// ----------------------------------------- - // clang-format off template<> inline bool Value::fastIs() const { return isCollection(); } diff --git a/src/functions.cpp b/src/functions.cpp index deb1254..b342bba 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -4,6 +4,8 @@ * SPDX-License-Identifier: MIT */ +#include // std::chrono::sytem_clock +#include // int64_t #include // std::advance #include // std::static_pointer_cast #include @@ -616,10 +618,62 @@ 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 (nodes.size() == 0) { + result = false; + } + + for (auto node : nodes) { + if (!is(node.get())) { + result = false; + break; + } + if (is(node.get())) { + auto lambda = std::static_pointer_cast(node); + if (lambda->isMacro()) { + result = false; + break; + } + } + } + + return makePtr(result); + }); + +ADD_FUNCTION( + "macro?", + { + bool result = true; + + if (nodes.size() == 0) { + result = false; + } + + for (auto node : nodes) { + if (!is(node.get())) { + result = false; + break; + } + auto lambda = std::static_pointer_cast(node); + if (!lambda->isMacro()) { + result = false; + break; + } + } + + return makePtr(result); + }); + // ----------------------------------------- #define STRING_TO_TYPE(name, type) \ @@ -790,6 +844,129 @@ ADD_FUNCTION( return readline(prompt->data()); }); +ADD_FUNCTION("time-ms", { + CHECK_ARG_COUNT_IS("time-ms", nodes.size(), 0); + + int64_t elapsed = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + return makePtr(elapsed); +}); + +// (meta [1 2 3]) +ADD_FUNCTION( + "meta", + { + CHECK_ARG_COUNT_IS("meta", nodes.size(), 1); + + auto front = nodes.front(); + Value* front_raw_ptr = nodes.front().get(); + + if (!is(front_raw_ptr) && // List / Vector + !is(front_raw_ptr) && // HashMap + !is(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", nodes.size(), 2); + + auto front = nodes.front(); + Value* front_raw_ptr = nodes.front().get(); + + if (!is(front_raw_ptr) && // List / Vector + !is(front_raw_ptr) && // HashMap + !is(front_raw_ptr)) { // Function / Lambda + Error::the().add(format("wrong argument type: Collection, HashMap or Callable, {}", front)); + return nullptr; + } + + return front->withMeta(nodes.back()); + }); + +// (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", nodes.size(), 1); + + VALUE_CAST(collection, Collection, nodes.front()); + nodes.pop_front(); + + auto collection_nodes = collection->nodes(); + + if (is(collection.get())) { + nodes.reverse(); + nodes.splice(nodes.end(), collection_nodes); + auto result = makePtr(nodes); + + return result; + } + + nodes.splice(nodes.begin(), collection_nodes); + auto result = makePtr(nodes); + + return result; + }); + +// (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", nodes.size(), 1); + + auto front = nodes.front(); + Value* front_raw_ptr = front.get(); + + if (is(front_raw_ptr) && std::static_pointer_cast(front)->state() == Constant::Nil) { + return makePtr(); + } + if (is(front_raw_ptr)) { + auto collection = std::static_pointer_cast(front); + + if (collection->empty()) { + return makePtr(); + } + + if (is(front_raw_ptr)) { + return front; + } + + return makePtr(collection->nodes()); + } + if (is(front_raw_ptr)) { + auto string = std::static_pointer_cast(front); + + if (string->empty()) { + return makePtr(); + } + + auto result = makePtr(); + + const auto& data = string->data(); + for (const auto& character : data) { + result->add(makePtr(character)); + } + + return result; + } + + Error::the().add(format("wrong argument type: Collection or String, {}", front)); + + return nullptr; + }); + // ----------------------------------------- void installFunctions(EnvironmentPtr env)