diff --git a/IMPLS.yml b/IMPLS.yml index 1421b0bba2..f6f84a26d3 100644 --- a/IMPLS.yml +++ b/IMPLS.yml @@ -9,6 +9,7 @@ IMPL: - {IMPL: c} - {IMPL: c.2} - {IMPL: cpp} + - {IMPL: cpp.2} - {IMPL: coffee} - {IMPL: cs} - {IMPL: chuck, NO_SELF_HOST_PERF: 1} # perf OOM diff --git a/Makefile.impls b/Makefile.impls index 1a3cbabca8..78f6ce3a9d 100644 --- a/Makefile.impls +++ b/Makefile.impls @@ -34,7 +34,7 @@ wasm_MODE = wasmtime # Implementation specific settings # -IMPLS = ada ada.2 awk bash basic bbc-basic c c.2 chuck clojure coffee common-lisp cpp crystal cs d dart \ +IMPLS = ada ada.2 awk bash basic bbc-basic c c.2 chuck clojure coffee common-lisp cpp cpp.2 crystal cs d dart \ elisp elixir elm erlang es6 factor fantom fennel forth fsharp go groovy gnu-smalltalk \ guile haskell haxe hy io janet java java-truffle js jq julia kotlin latex3 livescript logo lua make mal \ matlab miniMAL nasm nim objc objpascal ocaml perl perl6 php picolisp pike plpgsql \ @@ -117,6 +117,7 @@ clojure_STEP_TO_PROG = $(clojure_STEP_TO_PROG_$(clojure_MODE)) coffee_STEP_TO_PROG = impls/coffee/$($(1)).coffee common-lisp_STEP_TO_PROG = impls/common-lisp/$($(1)) cpp_STEP_TO_PROG = impls/cpp/$($(1)) +cpp.2_STEP_TO_PROG = impls/cpp.2/$($(1)) crystal_STEP_TO_PROG = impls/crystal/$($(1)) cs_STEP_TO_PROG = impls/cs/$($(1)).exe d_STEP_TO_PROG = impls/d/$($(1)) diff --git a/impls/.gitignore b/impls/.gitignore index 292498c104..2b452f59ea 100644 --- a/impls/.gitignore +++ b/impls/.gitignore @@ -36,6 +36,8 @@ old ada/obj/ awk/mal.awk bash/mal.sh +cc/*.a +cc/.deps clojure/mal.jar clojure/target clojure/.lein-repl-history diff --git a/impls/cpp.2/Dockerfile b/impls/cpp.2/Dockerfile new file mode 100644 index 0000000000..db1cfc7122 --- /dev/null +++ b/impls/cpp.2/Dockerfile @@ -0,0 +1,27 @@ +FROM ubuntu:24.04 +MAINTAINER Le Yang +LABEL org.opencontainers.image.source=https://github.com/kanaka/mal +LABEL org.opencontainers.image.description="mal test container: cpp.2" +########################################################## +# General requirements for testing or common across many +# implementations +########################################################## + +RUN apt-get -y update + +# Required for running tests +RUN apt-get -y install make python3 +RUN ln -sf /usr/bin/python3 /usr/bin/python + +# Some typical implementation and test requirements +RUN apt-get -y install curl libreadline-dev libedit-dev + +RUN mkdir -p /mal +WORKDIR /mal + +########################################################## +# Specific implementation requirements +########################################################## + +# Install g++ for any C/C++ based implementations +RUN apt-get -y install g++ diff --git a/impls/cpp.2/Makefile b/impls/cpp.2/Makefile new file mode 100644 index 0000000000..baa0e647e7 --- /dev/null +++ b/impls/cpp.2/Makefile @@ -0,0 +1,38 @@ +LD=$(CXX) +DEBUG=-g +CXXFLAGS=-O2 -Wall $(DEBUG) $(INCPATHS) -std=c++17 #-fsanitize=address -fsanitize=leak +LDFLAGS=-O2 $(DEBUG) $(LIBPATHS) -L. -lreadline -lhistory #-fsanitize=address -fsanitize=leak + +LIBSOURCES=reader.cc printer.cc core.cc +LIBOBJS=$(LIBSOURCES:%.cc=%.o) + +MAINS=$(wildcard step*.cc) +TARGETS=$(MAINS:%.cc=%) + +.PHONY: all clean + +.SUFFIXES: .cc .o + +all: $(TARGETS) + +dist: mal + +mal: stepA_mal + cp $< $@ + +.deps: *.cc *.hh + $(CXX) $(CXXFLAGS) -MM *.cc > .deps + +$(TARGETS): %: %.o libmal.a + $(LD) $^ -o $@ $(LDFLAGS) + +libmal.a: $(LIBOBJS) + $(AR) rcs $@ $^ + +.cc.o: + $(CXX) $(CXXFLAGS) -c $< -o $@ + +clean: + rm -rf *.o $(TARGETS) libmal.a .deps mal + +-include .deps diff --git a/impls/cpp.2/core.cc b/impls/cpp.2/core.cc new file mode 100644 index 0000000000..235dcfe7a1 --- /dev/null +++ b/impls/cpp.2/core.cc @@ -0,0 +1,690 @@ +#include "core.hh" +#include "printer.hh" +#include "reader.hh" +#include +#include +#include +#include +#include + +MalSymbol true_("true"); +MalSymbol false_("false"); +MalSymbol nil_("nil"); + +std::shared_ptr add(std::vector> args) +{ + int result = std::accumulate(args.begin(), args.end(), 0, [](int acc, std::shared_ptr i) + { return acc + static_cast(*i); }); + return std::make_shared(result); +} + +std::shared_ptr sub(std::vector> args) +{ + int result = static_cast(*args[0]) - static_cast(*args[1]); + return std::make_shared(result); +} + +std::shared_ptr mul(std::vector> args) +{ + int result = std::accumulate(args.begin(), args.end(), 1, [](int acc, std::shared_ptr i) + { return acc * static_cast(*i); }); + return std::make_shared(result); +} + +std::shared_ptr divide(std::vector> args) +{ + int result = static_cast(*args[0]) / static_cast(*args[1]); + return std::make_shared(result); +} + +std::shared_ptr prstr(std::vector> args) +{ + std::ostringstream oss; + + oss << '\"'; + if (!args.empty()) + { + for (auto arg : args) + oss << pr_str(arg, true) << ' '; + oss.seekp(-1, oss.end); + } + oss << '\"'; + + return std::make_shared(oss.str()); +} + +std::shared_ptr str(std::vector> args) +{ + std::ostringstream oss; + + oss << '\"'; + for (auto arg : args) + oss << pr_str(arg, false); + oss << '\"'; + + return std::make_shared(oss.str()); +} + +std::shared_ptr prn(std::vector> args) +{ + std::ostringstream oss; + + for (auto arg : args) + oss << pr_str(arg, true) << ' '; + + std::cout << (args.empty() ? "" : oss.str().substr(0, oss.str().length() - 1)) << std::endl; + + return std::make_shared(nil_); +} + +std::shared_ptr println(std::vector> args) +{ + std::ostringstream oss; + + for (auto arg : args) + oss << pr_str(arg, false) << ' '; + + std::cout << (args.empty() ? "" : oss.str().substr(0, oss.str().length() - 1)) << std::endl; + + return std::make_shared(nil_); +} + +std::shared_ptr list(std::vector> args) +{ + auto list = std::make_shared('(', ')'); + for (auto arg : args) + list->push_back(arg); + + return list; +} + +std::shared_ptr is_list(std::vector> args) +{ + bool result = false; + + if (args[0]->type() == MalType::Type::List) + { + auto list = static_cast(*args[0]); + result = list.is_list(); + } + + return result ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr empty(std::vector> args) +{ + MalList &list = static_cast(*args[0]); + return list.empty() ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr count(std::vector> args) +{ + if (args[0]->type() != MalType::Type::List) + return std::make_shared(0); + + MalList &list = static_cast(*args[0]); + return std::make_shared(list.size()); +} + +std::shared_ptr eq(std::vector> args) +{ + if (args[0]->type() != args[1]->type()) + return std::make_shared(false_); + + return *args[0] == *args[1] ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr gt(std::vector> args) +{ + bool result = static_cast(*args[0]) > static_cast(*args[1]); + return result ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr ge(std::vector> args) +{ + bool result = static_cast(*args[0]) >= static_cast(*args[1]); + return result ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr lt(std::vector> args) +{ + bool result = static_cast(*args[0]) < static_cast(*args[1]); + return result ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr le(std::vector> args) +{ + bool result = static_cast(*args[0]) <= static_cast(*args[1]); + return result ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr read_string(std::vector> args) +{ + auto &input = static_cast(*args[0]); + return read_str(input); +} + +std::shared_ptr slurp(std::vector> args) +{ + std::string input = static_cast(*args[0]); + std::ifstream ifs(input); + std::string content((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + return std::make_shared('"' + content + '"'); +} + +std::shared_ptr atom(std::vector> args) +{ + return std::make_shared(args[0]); +} + +std::shared_ptr is_atom(std::vector> args) +{ + return args[0]->type() == MalType::Type::Atom ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr deref(std::vector> args) +{ + auto &atom = static_cast(*args[0]); + return atom.deref(); +} + +std::shared_ptr reset(std::vector> args) +{ + auto &atom = static_cast(*args[0]); + return atom.reset(args[1]); +} + +std::shared_ptr swap(std::vector> args) +{ + auto &atom = static_cast(*args[0]); + auto &func = static_cast(*args[1]); + + std::vector> args_{atom.deref()}; + for (unsigned i = 2; i < args.size(); ++i) + args_.push_back(args[i]); + + return atom.reset(func(args_)); +} + +std::shared_ptr cons(std::vector> args) +{ + auto list = std::make_shared('(', ')'); + list->push_back(args[0]); + + for (auto arg : static_cast(*args[1])) + list->push_back(arg); + + return list; +} + +std::shared_ptr concat(std::vector> args) +{ + auto list = std::make_shared('(', ')'); + + for (auto arg : args) + for (auto item : static_cast(*arg)) + list->push_back(item); + + return list; +} + +std::shared_ptr vec(std::vector> args) +{ + auto &list = static_cast(*args[0]); + if (list.is_vector()) + return args[0]; + + return list.to_vector(); +} + +std::shared_ptr nth(std::vector> args) +{ + if (args[0]->type() != MalType::Type::List) + return std::make_shared(nil_); + + auto &list = static_cast(*args[0]); + auto &index = static_cast(*args[1]); + + return list[index]; +} + +std::shared_ptr first(std::vector> args) +{ + if (args[0]->type() != MalType::Type::List) + return std::make_shared(nil_); + + auto &list = static_cast(*args[0]); + if (list.empty()) + return std::make_shared(nil_); + + return list[0]; +} + +std::shared_ptr rest(std::vector> args) +{ + auto new_list = std::make_shared('(', ')'); + + if (args[0]->type() != MalType::Type::List) + return new_list; + + auto &list = static_cast(*args[0]); + if (list.empty()) + return new_list; + + for (unsigned i = 1; i < list.size(); ++i) + new_list->push_back(list[i]); + + return new_list; +} + +[[noreturn]] std::shared_ptr mal_throw(std::vector> args) +{ + throw args[0]; +} + +std::shared_ptr apply(std::vector> args) +{ + std::vector> list; + + for (unsigned i = 1; i < args.size() - 1; ++i) + list.push_back(args[i]); + + for (auto item : static_cast(*args[args.size() - 1])) + list.push_back(item); + + auto &func = static_cast(*args[0]); + return func(list); +} + +std::shared_ptr map(std::vector> args) +{ + auto result = std::make_shared('(', ')'); + auto &func = static_cast(*args[0]); + std::vector> args_(1); + for (auto item : static_cast(*args[1])) + { + args_[0] = item; + result->push_back(func(args_)); + } + return result; +} + +std::shared_ptr is_nil(std::vector> args) +{ + if (args[0]->type() != MalType::Type::Symbol) + return std::make_shared(false_); + + return *args[0] == nil_ ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr is_true(std::vector> args) +{ + if (args[0]->type() != MalType::Type::Symbol) + return std::make_shared(false_); + + return *args[0] == true_ ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr is_false(std::vector> args) +{ + if (args[0]->type() != MalType::Type::Symbol) + return std::make_shared(false_); + + return *args[0] == false_ ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr is_symbol(std::vector> args) +{ + if (args[0]->type() != MalType::Type::Symbol) + return std::make_shared(false_); + + auto &symbol = static_cast(*args[0]); + + return !symbol.is_keyword() && !symbol.is_string() && !symbol.is_reserved() ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr symbol(std::vector> args) +{ + std::string symbol = static_cast(*args[0]); + return std::make_shared(symbol); +} + +std::shared_ptr keyword(std::vector> args) +{ + auto &symbol = static_cast(*args[0]); + return symbol.is_keyword() ? args[0] : std::make_shared(':' + static_cast(symbol)); +} + +std::shared_ptr is_keyword(std::vector> args) +{ + if (args[0]->type() != MalType::Type::Symbol) + return std::make_shared(false_); + + auto &symbol = static_cast(*args[0]); + + return symbol.is_keyword() ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr vector(std::vector> args) +{ + auto result = std::make_shared('[', ']'); + + for (auto arg : args) + result->push_back(arg); + + return result; +} + +std::shared_ptr is_vector(std::vector> args) +{ + bool result = false; + + if (args[0]->type() == MalType::Type::List) + { + auto list = static_cast(*args[0]); + result = list.is_vector(); + } + + return result ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr sequential(std::vector> args) +{ + return args[0]->type() == MalType::Type::List ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr hash_map(std::vector> args) +{ + auto result = std::make_shared(); + + for (unsigned i = 0; i < args.size(); i += 2) + { + auto key = static_cast(*args[i]); + (*result)[key] = args[i + 1]; + } + + return result; +} + +std::shared_ptr is_map(std::vector> args) +{ + return args[0]->type() == MalType::Type::Map ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr assoc(std::vector> args) +{ + auto result = std::make_shared(); + + for (auto [key, value] : static_cast(*args[0])) + (*result)[key] = value; + + for (unsigned i = 1; i < args.size(); i += 2) + { + auto key = static_cast(*args[i]); + (*result)[key] = args[i + 1]; + } + + return result; +} + +std::shared_ptr dissoc(std::vector> args) +{ + auto &map = static_cast(*args[0]); + auto result = std::make_shared(map); + + for (unsigned i = 1; i < args.size(); ++i) + { + auto key = static_cast(*args[i]); + result->erase(key); + } + + return result; +} + +std::shared_ptr get(std::vector> args) +{ + auto &map = static_cast(*args[0]); + auto key = static_cast(*args[1]); + + if (args[0]->type() != MalType::Type::Map || map.find(key) == map.end()) + return std::make_shared(nil_); + + return map[key]; +} + +std::shared_ptr contains(std::vector> args) +{ + auto &map = static_cast(*args[0]); + auto key = static_cast(*args[1]); + return map.find(key) != map.end() ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr keys(std::vector> args) +{ + auto result = std::make_shared('(', ')'); + + for (auto [key, _] : static_cast(*args[0])) + result->push_back(std::make_shared(key)); + + return result; +} + +std::shared_ptr vals(std::vector> args) +{ + auto result = std::make_shared('(', ')'); + + for (auto [_, val] : static_cast(*args[0])) + result->push_back(val); + + return result; +} + +std::shared_ptr readline(std::vector> args) +{ + std::string prompt = static_cast(*args[0]); + std::cout << prompt; + + if (std::cin.eof()) + return std::make_shared(nil_); + + std::string input; + std::getline(std::cin, input); + + return std::make_shared('"' + input + '"'); +} + +std::shared_ptr time_ms(std::vector> args) +{ + auto now = std::chrono::steady_clock::now().time_since_epoch(); + auto ms = std::chrono::duration_cast(now); + return std::make_shared(ms.count()); +} + +std::shared_ptr meta(std::vector> args) +{ + if (args[0]->type() == MalType::Type::List) + return static_cast(*args[0]).get_meta(); + if (args[0]->type() == MalType::Type::Map) + return static_cast(*args[0]).get_meta(); + if (args[0]->type() == MalType::Type::Func) + return static_cast(*args[0]).get_meta(); + throw std::invalid_argument("meta called with invalid argument"); +} + +std::shared_ptr with_meta(std::vector> args) +{ + if (args[0]->type() == MalType::Type::List) + { + auto list = static_cast(*args[0]); + list.set_meta(args[1]); + return std::make_shared(list); + } + if (args[0]->type() == MalType::Type::Map) + { + auto map = static_cast(*args[0]); + map.set_meta(args[1]); + return std::make_shared(map); + } + if (args[0]->type() == MalType::Type::Func) + { + auto func = static_cast(*args[0]); + func.set_meta(args[1]); + return std::make_shared(func); + } + throw std::invalid_argument("with-meta called with invalid argument"); +} + +std::shared_ptr is_fn(std::vector> args) +{ + if (args[0]->type() != MalType::Type::Func) + return std::make_shared(false_); + + auto &func = static_cast(*args[0]); + + return !func.is_macro ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr is_macro(std::vector> args) +{ + if (args[0]->type() != MalType::Type::Func) + return std::make_shared(false_); + + auto &func = static_cast(*args[0]); + + return func.is_macro ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr is_string(std::vector> args) +{ + if (args[0]->type() != MalType::Type::Symbol) + return std::make_shared(false_); + + auto &symbol = static_cast(*args[0]); + + return symbol.is_string() ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr is_number(std::vector> args) +{ + return args[0]->type() == MalType::Type::Int ? std::make_shared(true_) : std::make_shared(false_); +} + +std::shared_ptr seq(std::vector> args) +{ + if (args[0]->type() == MalType::Type::List) + { + auto &list = static_cast(*args[0]); + + if (list.empty()) + return std::make_shared(nil_); + + return list.is_list() ? args[0] : list.to_list(); + } + + if (args[0]->type() == MalType::Type::Symbol) + { + auto &symbol = static_cast(*args[0]); + if (symbol == "\"\"" || symbol == "nil") + return std::make_shared(nil_); + + if (symbol.is_string()) + { + auto list = std::make_shared('(', ')'); + for (auto c : static_cast(symbol)) + list->push_back(std::make_shared('"' + std::string(1, c) + '"')); + return list; + } + } + + return std::make_shared(nil_); +} + +std::shared_ptr conj(std::vector> args) +{ + auto &list = static_cast(*args[0]); + + if (list.is_vector()) + { + auto vector = list; + for (unsigned i = 1; i < args.size(); ++i) + vector.push_back(args[i]); + return std::make_shared(vector); + } + + auto new_list = std::make_shared('(', ')'); + for (unsigned i = args.size() - 1; i >= 1; --i) + new_list->push_back(args[i]); + for (auto arg : list) + new_list->push_back(arg); + return new_list; +} + +std::map> ns() +{ + return { + {"true", std::make_shared(true_)}, + {"false", std::make_shared(false_)}, + {"nil", std::make_shared(nil_)}, + {"+", std::make_shared(add)}, + {"-", std::make_shared(sub)}, + {"*", std::make_shared(mul)}, + {"/", std::make_shared(divide)}, + {"pr-str", std::make_shared(prstr)}, + {"str", std::make_shared(str)}, + {"prn", std::make_shared(prn)}, + {"println", std::make_shared(println)}, + {"list", std::make_shared(list)}, + {"list?", std::make_shared(is_list)}, + {"empty?", std::make_shared(empty)}, + {"count", std::make_shared(count)}, + {"=", std::make_shared(eq)}, + {"<", std::make_shared(lt)}, + {"<=", std::make_shared(le)}, + {">", std::make_shared(gt)}, + {">=", std::make_shared(ge)}, + {"read-string", std::make_shared(read_string)}, + {"slurp", std::make_shared(slurp)}, + {"atom", std::make_shared(atom)}, + {"atom?", std::make_shared(is_atom)}, + {"deref", std::make_shared(deref)}, + {"reset!", std::make_shared(reset)}, + {"swap!", std::make_shared(swap)}, + {"cons", std::make_shared(cons)}, + {"concat", std::make_shared(concat)}, + {"vec", std::make_shared(vec)}, + {"nth", std::make_shared(nth)}, + {"first", std::make_shared(first)}, + {"rest", std::make_shared(rest)}, + {"throw", std::make_shared(mal_throw)}, + {"apply", std::make_shared(apply)}, + {"map", std::make_shared(map)}, + {"nil?", std::make_shared(is_nil)}, + {"true?", std::make_shared(is_true)}, + {"false?", std::make_shared(is_false)}, + {"symbol?", std::make_shared(is_symbol)}, + {"symbol", std::make_shared(symbol)}, + {"keyword", std::make_shared(keyword)}, + {"keyword?", std::make_shared(is_keyword)}, + {"vector", std::make_shared(vector)}, + {"vector?", std::make_shared(is_vector)}, + {"sequential?", std::make_shared(sequential)}, + {"hash-map", std::make_shared(hash_map)}, + {"map?", std::make_shared(is_map)}, + {"assoc", std::make_shared(assoc)}, + {"dissoc", std::make_shared(dissoc)}, + {"get", std::make_shared(get)}, + {"contains?", std::make_shared(contains)}, + {"keys", std::make_shared(keys)}, + {"vals", std::make_shared(vals)}, + {"readline", std::make_shared(readline)}, + {"time-ms", std::make_shared(time_ms)}, + {"meta", std::make_shared(meta)}, + {"with-meta", std::make_shared(with_meta)}, + {"fn?", std::make_shared(is_fn)}, + {"macro?", std::make_shared(is_macro)}, + {"string?", std::make_shared(is_string)}, + {"number?", std::make_shared(is_number)}, + {"seq", std::make_shared(seq)}, + {"conj", std::make_shared(conj)}, + }; +} diff --git a/impls/cpp.2/core.hh b/impls/cpp.2/core.hh new file mode 100644 index 0000000000..abcb4196d2 --- /dev/null +++ b/impls/cpp.2/core.hh @@ -0,0 +1,6 @@ +#pragma once + +#include "types.hh" +#include + +std::map> ns(); diff --git a/impls/cpp.2/env.hh b/impls/cpp.2/env.hh new file mode 100644 index 0000000000..dcbafcaf81 --- /dev/null +++ b/impls/cpp.2/env.hh @@ -0,0 +1,55 @@ +#pragma once + +#include "types.hh" +#include +#include +#include +#include + +class Env +{ +public: + explicit Env(std::shared_ptr outer = nullptr) + : outer_(outer) {} + + Env(const MalList &binds, std::vector> exprs, std::shared_ptr outer) + : outer_(outer) + { + for (unsigned i = 0; i < binds.size(); ++i) + { + auto symbol = static_cast(*binds[i]); + if (symbol == "&") + { + auto rest = std::make_shared('(', ')'); + for (unsigned j = i; j < exprs.size(); ++j) + rest->push_back(exprs[j]); + set(static_cast(*binds[i + 1]), rest); + break; + } + set(symbol, exprs[i]); + } + } + + void set(const std::string &key, std::shared_ptr value) { data_[key] = value; } + + const Env *find(const std::string &key) const + { + if (data_.find(key) != data_.end()) + return this; + return outer_ ? outer_->find(key) : nullptr; + } + + std::shared_ptr get(const std::string &key) const + { + auto env = find(key); + if (!env) + throw std::runtime_error('\'' + key + "' not found"); + return env->data_.at(key); + } + + bool is_root() const { return !outer_; } + +private: + std::shared_ptr outer_; + std::map> data_; +}; diff --git a/impls/cpp.2/printer.cc b/impls/cpp.2/printer.cc new file mode 100644 index 0000000000..1dd29c2880 --- /dev/null +++ b/impls/cpp.2/printer.cc @@ -0,0 +1,86 @@ +#include "printer.hh" +#include + +std::string escape(const std::string &token) +{ + std::ostringstream oss; + + oss << '"'; + for (unsigned int i = 0; i < token.size(); ++i) + { + switch (token[i]) + { + case '"': + oss << "\\\""; + break; + case '\n': + oss << "\\n"; + break; + case '\\': + oss << "\\\\"; + break; + default: + oss << token[i]; + break; + } + } + oss << '"'; + + return oss.str(); +} + +std::string pr_str(std::shared_ptr input, bool print_readably) +{ + if (!input) + { + return ""; + } + + switch (input->type()) + { + case MalType::Type::Symbol: + { + auto symbol = static_cast(*input); + return (print_readably && symbol.is_string()) ? escape(symbol) : static_cast(symbol); + } + case MalType::Type::Int: + return std::to_string(static_cast(*input)); + case MalType::Type::List: + { + std::ostringstream oss; + auto &list = static_cast(*input); + oss << list.lparen(); + if (!list.empty()) + { + for (auto &l : list) + oss << pr_str(l, print_readably) << ' '; + oss.seekp(-1, oss.end); + } + oss << list.rparen(); + return oss.str(); + } + case MalType::Type::Map: + { + std::ostringstream oss; + auto &map = static_cast(*input); + oss << '{'; + if (!map.empty()) + { + for (auto &[key, value] : map) + oss << ((print_readably && key.is_string()) ? escape(key) : static_cast(key)) << ' ' << pr_str(value, print_readably) << ' '; + oss.seekp(-1, oss.end); + } + oss << '}'; + return oss.str(); + } + case MalType::Type::Func: + return "#"; + case MalType::Type::Atom: + { + auto &atom = static_cast(*input); + return "(atom " + pr_str(atom.deref(), print_readably) + ")"; + } + default: + return ""; + } +} diff --git a/impls/cpp.2/printer.hh b/impls/cpp.2/printer.hh new file mode 100644 index 0000000000..bf65542196 --- /dev/null +++ b/impls/cpp.2/printer.hh @@ -0,0 +1,5 @@ +#pragma once + +#include "types.hh" + +std::string pr_str(std::shared_ptr input, bool print_readably = true); diff --git a/impls/cpp.2/reader.cc b/impls/cpp.2/reader.cc new file mode 100644 index 0000000000..169f0d2272 --- /dev/null +++ b/impls/cpp.2/reader.cc @@ -0,0 +1,187 @@ +#include "reader.hh" +#include +#include +#include + +std::shared_ptr read_form(Reader &reader); + +Reader tokenize(const std::string &input) +{ + std::regex regex(R"([\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]*))"); + return Reader(input, regex); +} + +std::string unescape(const std::string &token) +{ + std::ostringstream oss; + + unsigned int i = 0; + for (; i < token.size() - 1; ++i) + { + if (token[i] == '\\') + { + switch (token[++i]) + { + case '"': + oss << '"'; + break; + case 'n': + oss << '\n'; + break; + case '\\': + oss << '\\'; + break; + default: + oss << '\\' << token[i]; + break; + } + } + else + { + oss << token[i]; + } + } + + oss << token[i]; + return oss.str(); +} + +std::shared_ptr read_atom(Reader &reader) +{ + if (reader.empty()) + { + return nullptr; + } + + auto token = reader.next(); + if (token[0] == '"') + { + auto unescaped_token = unescape(token); + if (*unescaped_token.rbegin() != '"' || unescaped_token.size() < 2) + throw std::runtime_error("unbalanced"); + + return std::make_shared(unescaped_token); + } + + int result; + auto [ptr, ec] = std::from_chars(token.c_str(), token.c_str() + token.size(), result); + if (ec == std::errc() && ptr == token.c_str() + token.size()) + { + return std::make_shared(result); + } + + return std::make_shared(token); +} + +std::shared_ptr read_list(Reader &reader, char lparen, char rparen) +{ + if (reader.empty()) + throw std::runtime_error("unbalanced"); + + auto list = std::make_shared(lparen, rparen); + auto token = read_form(reader); + + while (token && (token->type() != MalType::Type::Symbol || + static_cast(*token) != std::string(1, rparen))) + { + list->push_back(token); + token = read_form(reader); + } + + if (!token) + throw std::runtime_error("unbalanced"); + + return list; +} + +std::shared_ptr read_map(Reader &reader) +{ + if (reader.empty()) + throw std::runtime_error("unbalanced"); + + auto map = std::make_shared(); + auto token = read_form(reader); + + while (token && token->type() == MalType::Type::Symbol) + { + auto &key = static_cast(*token); + if (key == std::string(1, '}')) + break; + + (*map)[key] = read_form(reader); + token = read_form(reader); + } + + if (!token) + throw std::runtime_error("unbalanced"); + + return map; +} + +std::shared_ptr read_macro(Reader &reader, const std::string &name) +{ + auto macro = read_form(reader); + auto result = std::make_shared('(', ')'); + result->push_back(std::make_shared(name)); + result->push_back(macro); + return result; +} + +std::shared_ptr read_meta(Reader &reader) +{ + auto meta = read_form(reader); + auto item = read_form(reader); + auto result = std::make_shared('(', ')'); + result->push_back(std::make_shared("with-meta")); + result->push_back(item); + result->push_back(meta); + return result; +} + +std::shared_ptr read_form(Reader &reader) +{ + if (reader.empty()) + { + return nullptr; + } + + auto token = reader.peak(); + switch (token[0]) + { + case '(': + reader.next(); + return read_list(reader, '(', ')'); + case '[': + reader.next(); + return read_list(reader, '[', ']'); + case '{': + reader.next(); + return read_map(reader); + case '\'': + reader.next(); + return read_macro(reader, "quote"); + case '`': + reader.next(); + return read_macro(reader, "quasiquote"); + case '@': + reader.next(); + return read_macro(reader, "deref"); + case '~': + reader.next(); + return read_macro(reader, token.size() == 1 ? "unquote" : "splice-unquote"); + case '^': + reader.next(); + return read_meta(reader); + case ';': + reader.next(); + return read_form(reader); + default: + return read_atom(reader); + } +} + +std::shared_ptr read_str(const std::string &input) +{ + auto reader = tokenize(input); + return read_form(reader); +} diff --git a/impls/cpp.2/reader.hh b/impls/cpp.2/reader.hh new file mode 100644 index 0000000000..1aea819388 --- /dev/null +++ b/impls/cpp.2/reader.hh @@ -0,0 +1,30 @@ +#pragma once + +#include "types.hh" +#include + +class Reader +{ +public: + Reader(const std::string &input, std::regex regex) + : regex_(regex) + { + iter_ = std::sregex_iterator(input.begin(), input.end(), regex_); + } + + std::string next() { return iter_++->str(1); } + std::string peak() const { return iter_->str(1); } + bool empty() const + { + return iter_ == std::sregex_iterator() || + iter_->empty() || + !iter_->operator[](1).matched || + iter_->operator[](1).length() == 0; + } + +private: + std::regex regex_; + std::sregex_iterator iter_; +}; + +std::shared_ptr read_str(const std::string &input); diff --git a/impls/cpp.2/run b/impls/cpp.2/run new file mode 100755 index 0000000000..8ba68a5484 --- /dev/null +++ b/impls/cpp.2/run @@ -0,0 +1,2 @@ +#!/bin/bash +exec $(dirname $0)/${STEP:-stepA_mal} "${@}" diff --git a/impls/cpp.2/step0_repl.cc b/impls/cpp.2/step0_repl.cc new file mode 100644 index 0000000000..d39c4d9918 --- /dev/null +++ b/impls/cpp.2/step0_repl.cc @@ -0,0 +1,47 @@ +#include +#include + +std::string read(std::string input) +{ + return input; +} + +std::string eval(std::string input) +{ + return input; +} + +std::string print(std::string input) +{ + return input; +} + +std::string rep(std::string input) +{ + auto read_result = read(input); + auto eval_result = eval(read_result); + auto print_result = print(eval_result); + return print_result; +} + +int main(int argc, char *argv[]) +{ + std::string input; + + while (!std::cin.eof()) + { + std::cout << "user> "; + std::getline(std::cin, input); + try + { + auto rep_result = rep(input); + std::cout << rep_result << std::endl; + } + catch (std::exception &e) + { + std::cerr << e.what() << std::endl; + } + } + + return 0; +} diff --git a/impls/cpp.2/step1_read_print.cc b/impls/cpp.2/step1_read_print.cc new file mode 100644 index 0000000000..ee62374a91 --- /dev/null +++ b/impls/cpp.2/step1_read_print.cc @@ -0,0 +1,49 @@ +#include "printer.hh" +#include "reader.hh" +#include +#include + +std::shared_ptr read(const std::string &input) +{ + return read_str(input); +} + +std::shared_ptr eval(std::shared_ptr input) +{ + return input; +} + +std::string print(std::shared_ptr input) +{ + return pr_str(std::move(input)); +} + +std::string rep(const std::string &input) +{ + auto read_result = read(input); + auto eval_result = eval(std::move(read_result)); + auto print_result = print(std::move(eval_result)); + return print_result; +} + +int main(int argc, char *argv[]) +{ + std::string input; + + while (!std::cin.eof()) + { + std::cout << "user> "; + std::getline(std::cin, input); + try + { + auto rep_result = rep(input); + std::cout << rep_result << std::endl; + } + catch (std::exception &e) + { + std::cerr << e.what() << std::endl; + } + } + + return 0; +} diff --git a/impls/cpp.2/step2_eval.cc b/impls/cpp.2/step2_eval.cc new file mode 100644 index 0000000000..b5429e771e --- /dev/null +++ b/impls/cpp.2/step2_eval.cc @@ -0,0 +1,139 @@ +#include "printer.hh" +#include "reader.hh" +#include +#include +#include +#include + +std::shared_ptr eval(std::shared_ptr input, const std::map(std::vector>)>> &env); + +std::map(std::vector>)>> repl_env = { + {"+", [](std::vector> args) + { return std::make_shared(std::accumulate(args.begin(), args.end(), 0, [](int acc, std::shared_ptr i) + { return acc + static_cast(*i); })); }}, + {"-", [](std::vector> args) + { return std::make_shared(static_cast(*args[0]) - static_cast(*args[1])); }}, + {"*", [](std::vector> args) + { return std::make_shared(std::accumulate(args.begin(), args.end(), 1, [](int acc, std::shared_ptr i) + { return acc * static_cast(*i); })); }}, + {"/", [](std::vector> args) + { return std::make_shared(static_cast(*args[0]) / static_cast(*args[1])); }}, +}; + +std::shared_ptr read(const std::string &input) +{ + return read_str(input); +} + +std::shared_ptr eval_ast(std::shared_ptr ast, const std::map(std::vector>)>> &env) +{ + switch (ast->type()) + { + case MalType::Type::Symbol: + { + auto symbol = static_cast(*ast); + if (symbol.is_string() || symbol.is_keyword()) + return ast; + + auto iter = env.find(symbol); + if (iter == env.end()) + { + std::cerr << "Invallid symbol"; + return nullptr; + } + return std::make_shared(iter->second); + } + case MalType::Type::List: + { + auto &list = static_cast(*ast); + auto new_list = std::make_shared(list.lparen(), list.rparen()); + for (auto &a : list) + { + auto l = eval(std::move(a), env); + if (!l) + return nullptr; + new_list->push_back(std::move(l)); + } + return new_list; + } + case MalType::Type::Map: + { + auto &map = static_cast(*ast); + auto new_map = std::make_shared(); + for (auto &[key, value] : map) + { + auto v = eval(value, env); + if (!v) + return nullptr; + (*new_map)[key] = v; + } + return new_map; + } + default: + return ast; + } +} + +std::shared_ptr eval(std::shared_ptr input, const std::map(std::vector>)>> &env) +{ + if (!input) + return nullptr; + + if (input->type() != MalType::Type::List) + return eval_ast(std::move(input), env); + + auto &list = static_cast(*input); + if (list.empty()) + return input; + + if (!list.is_list()) + return eval_ast(std::move(input), env); + + auto plist = eval_ast(std::move(input), env); + if (!plist) + return nullptr; + + auto &new_list = static_cast(*plist); + auto &func = static_cast(*new_list[0]); + + std::vector> args; + for (unsigned i = 1; i < new_list.size(); ++i) + args.push_back(new_list[i]); + + return func(args); +} + +std::string print(std::shared_ptr input) +{ + return pr_str(std::move(input)); +} + +std::string rep(const std::string &input) +{ + auto read_result = read(input); + auto eval_result = eval(std::move(read_result), repl_env); + auto print_result = print(std::move(eval_result)); + return print_result; +} + +int main(int argc, char *argv[]) +{ + std::string input; + + while (!std::cin.eof()) + { + std::cout << "user> "; + std::getline(std::cin, input); + try + { + auto rep_result = rep(input); + std::cout << rep_result << std::endl; + } + catch (std::exception &e) + { + std::cerr << e.what() << std::endl; + } + } + + return 0; +} diff --git a/impls/cpp.2/step3_env.cc b/impls/cpp.2/step3_env.cc new file mode 100644 index 0000000000..e4510458aa --- /dev/null +++ b/impls/cpp.2/step3_env.cc @@ -0,0 +1,163 @@ +#include "env.hh" +#include "printer.hh" +#include "reader.hh" +#include +#include +#include +#include + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env); + +std::shared_ptr read(const std::string &input) +{ + return read_str(input); +} + +std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr env) +{ + switch (ast->type()) + { + case MalType::Type::Symbol: + { + auto symbol = static_cast(*ast); + if (symbol.is_string() || symbol.is_keyword()) + return ast; + + return env->get(symbol); + } + case MalType::Type::List: + { + auto &list = static_cast(*ast); + auto new_list = std::make_shared(list.lparen(), list.rparen()); + for (auto &a : list) + { + auto l = eval(std::move(a), env); + if (!l) + return nullptr; + new_list->push_back(std::move(l)); + } + return new_list; + } + case MalType::Type::Map: + { + auto &map = static_cast(*ast); + auto new_map = std::make_shared(); + for (auto &[key, value] : map) + { + auto v = eval(value, env); + if (!v) + return nullptr; + (*new_map)[key] = v; + } + return new_map; + } + default: + return ast; + } +} + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env) +{ + if (!input) + return nullptr; + + if (input->type() != MalType::Type::List) + return eval_ast(std::move(input), env); + + auto &list = static_cast(*input); + if (list.empty()) + return input; + + if (!list.is_list()) + return eval_ast(std::move(input), env); + + std::string symbol = static_cast(*list[0]); + if (symbol == "def!") + { + std::string key = static_cast(*list[1]); + auto val = eval(list[2], env); + if (!val) + return nullptr; + env->set(key, val); + return val; + } + + if (symbol == "let*") + { + auto new_env = std::make_shared(env); + auto &bindings = static_cast(*list[1]); + for (unsigned i = 0; i < bindings.size(); i += 2) + { + std::string key = static_cast(*bindings[i]); + auto val = eval(bindings[i + 1], new_env); + if (!val) + return nullptr; + new_env->set(key, val); + } + return eval(list[2], new_env); + } + + auto plist = eval_ast(std::move(input), env); + if (!plist) + return nullptr; + + auto &new_list = static_cast(*plist); + auto &func = static_cast(*new_list[0]); + + std::vector> args; + for (unsigned i = 1; i < new_list.size(); ++i) + args.push_back(new_list[i]); + + return func(args); +} + +std::string print(std::shared_ptr input) +{ + return pr_str(std::move(input)); +} + +std::string rep(const std::string &input, std::shared_ptr env) +{ + auto read_result = read(input); + auto eval_result = eval(std::move(read_result), env); + auto print_result = print(std::move(eval_result)); + return print_result; +} + +int main(int argc, char *argv[]) +{ + auto add = std::make_shared([](std::vector> args) + { return std::make_shared(std::accumulate(args.begin(), args.end(), 0, [](int acc, std::shared_ptr i) + { return acc + static_cast(*i); })); }); + auto sub = std::make_shared([](std::vector> args) + { return std::make_shared(static_cast(*args[0]) - static_cast(*args[1])); }); + auto mul = std::make_shared([](std::vector> args) + { return std::make_shared(std::accumulate(args.begin(), args.end(), 1, [](int acc, std::shared_ptr i) + { return acc * static_cast(*i); })); }); + auto div = std::make_shared([](std::vector> args) + { return std::make_shared(static_cast(*args[0]) / static_cast(*args[1])); }); + + auto repl_env = std::make_shared(); + repl_env->set("+", add); + repl_env->set("-", sub); + repl_env->set("*", mul); + repl_env->set("/", div); + + while (!std::cin.eof()) + { + std::string input; + std::cout << "user> "; + std::getline(std::cin, input); + try + { + auto rep_result = rep(input, repl_env); + std::cout << rep_result << std::endl; + } + catch (std::exception &e) + { + std::cerr << e.what() << std::endl; + } + } + + return 0; +} diff --git a/impls/cpp.2/step4_if_fn_do.cc b/impls/cpp.2/step4_if_fn_do.cc new file mode 100644 index 0000000000..ada7715e45 --- /dev/null +++ b/impls/cpp.2/step4_if_fn_do.cc @@ -0,0 +1,196 @@ +#include "core.hh" +#include "env.hh" +#include "printer.hh" +#include "reader.hh" +#include +#include +#include + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env); + +std::shared_ptr read(const std::string &input) +{ + return read_str(input); +} + +std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr env) +{ + switch (ast->type()) + { + case MalType::Type::Symbol: + { + auto symbol = static_cast(*ast); + if (symbol.is_string() || symbol.is_keyword()) + return ast; + + return env->get(symbol); + } + case MalType::Type::List: + { + auto &list = static_cast(*ast); + auto new_list = std::make_shared(list.lparen(), list.rparen()); + for (auto &a : list) + { + auto l = eval(a, env); + if (!l) + return nullptr; + new_list->push_back(l); + } + return new_list; + } + case MalType::Type::Map: + { + auto &map = static_cast(*ast); + auto new_map = std::make_shared(); + for (auto &[key, value] : map) + { + auto v = eval(value, env); + if (!v) + return nullptr; + (*new_map)[key] = v; + } + return new_map; + } + default: + return ast; + } +} + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env) +{ + if (!input) + return nullptr; + + if (input->type() != MalType::Type::List) + return eval_ast(input, env); + + auto &list = static_cast(*input); + if (list.empty()) + return input; + + if (!list.is_list()) + return eval_ast(input, env); + + if (list[0]->type() == MalType::Type::Symbol) + { + std::string symbol = static_cast(*list[0]); + if (symbol == "def!") + { + std::string key = static_cast(*list[1]); + auto val = eval(list[2], env); + if (!val) + return nullptr; + env->set(key, val); + return val; + } + + if (symbol == "let*") + { + auto new_env = std::make_shared(env); + auto &bindings = static_cast(*list[1]); + for (unsigned i = 0; i < bindings.size(); i += 2) + { + std::string key = static_cast(*bindings[i]); + auto val = eval(bindings[i + 1], new_env); + if (!val) + return nullptr; + new_env->set(key, val); + } + return eval(list[2], new_env); + } + + if (symbol == "do") + { + for (unsigned i = 1; i < list.size() - 1; ++i) + eval(list[i], env); + return eval(list[list.size() - 1], env); + } + + if (symbol == "if") + { + auto cond = eval(list[1], env); + if (cond && cond->type() == MalType::Type::Symbol) + { + auto condition = static_cast(*cond); + if (!condition) + return list.size() > 3 ? eval(list[3], env) : env->get("nil"); + } + return eval(list[2], env); + } + + if (symbol == "fn*") + { + if (env->is_root()) + { + std::weak_ptr weak_env = env; + auto closure = [list, weak_env](std::vector> params) + { + auto new_env = std::make_shared(static_cast(*list[1]), params, weak_env.lock()); + return eval(list[2], new_env); + }; + return std::make_shared(closure, list[2], list[1], env); + } + + auto closure = [list, env](std::vector> params) + { + auto new_env = std::make_shared(static_cast(*list[1]), params, env); + return eval(list[2], new_env); + }; + return std::make_shared(closure); + } + } + + auto plist = eval_ast(input, env); + if (!plist) + return nullptr; + + auto &new_list = static_cast(*plist); + auto &func = static_cast(*new_list[0]); + + std::vector> args; + for (unsigned i = 1; i < new_list.size(); ++i) + args.push_back(new_list[i]); + + return func(args); +} + +std::string print(std::shared_ptr input) +{ + return pr_str(input); +} + +std::string rep(const std::string &input, std::shared_ptr env) +{ + auto read_result = read(input); + auto eval_result = eval(read_result, env); + auto print_result = print(eval_result); + return print_result; +} + +int main(int argc, char *argv[]) +{ + auto repl_env = std::make_shared(); + + for (auto &[key, value] : ns()) + repl_env->set(key, value); + + rep("(def! not (fn* (a) (if a false true)))", repl_env); + + while (!std::cin.eof()) + { + std::string input; + std::cout << "user> "; + std::getline(std::cin, input); + try + { + auto rep_result = rep(input, repl_env); + std::cout << rep_result << std::endl; + } + catch (std::exception &e) + { + std::cerr << e.what() << std::endl; + } + } + + return 0; +} diff --git a/impls/cpp.2/step5_tco.cc b/impls/cpp.2/step5_tco.cc new file mode 100644 index 0000000000..a5a729c021 --- /dev/null +++ b/impls/cpp.2/step5_tco.cc @@ -0,0 +1,218 @@ +#include "core.hh" +#include "env.hh" +#include "printer.hh" +#include "reader.hh" +#include +#include +#include + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env); + +std::shared_ptr read(const std::string &input) +{ + return read_str(input); +} + +std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr env) +{ + switch (ast->type()) + { + case MalType::Type::Symbol: + { + auto symbol = static_cast(*ast); + if (symbol.is_string() || symbol.is_keyword()) + return ast; + + return env->get(symbol); + } + case MalType::Type::List: + { + auto &list = static_cast(*ast); + auto new_list = std::make_shared(list.lparen(), list.rparen()); + for (auto &a : list) + { + auto l = eval(a, env); + if (!l) + return nullptr; + new_list->push_back(l); + } + return new_list; + } + case MalType::Type::Map: + { + auto &map = static_cast(*ast); + auto new_map = std::make_shared(); + for (auto &[key, value] : map) + { + auto v = eval(value, env); + if (!v) + return nullptr; + (*new_map)[key] = v; + } + return new_map; + } + default: + return ast; + } +} + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env) +{ + while (true) + { + if (!input) + return nullptr; + + if (input->type() != MalType::Type::List) + return eval_ast(input, env); + + auto &list = static_cast(*input); + if (list.empty()) + return input; + + if (!list.is_list()) + return eval_ast(input, env); + + if (list[0]->type() == MalType::Type::Symbol) + { + std::string symbol = static_cast(*list[0]); + if (symbol == "def!") + { + std::string key = static_cast(*list[1]); + auto val = eval(list[2], env); + if (!val) + return nullptr; + env->set(key, val); + return val; + } + + if (symbol == "let*") + { + auto new_env = std::make_shared(env); + auto &bindings = static_cast(*list[1]); + for (unsigned i = 0; i < bindings.size(); i += 2) + { + std::string key = static_cast(*bindings[i]); + auto val = eval(bindings[i + 1], new_env); + if (!val) + return nullptr; + new_env->set(key, val); + } + input = list[2]; + env = new_env; + continue; + } + + if (symbol == "do") + { + for (unsigned i = 1; i < list.size() - 1; ++i) + eval(list[i], env); + input = list[list.size() - 1]; + continue; + } + + if (symbol == "if") + { + auto cond = eval(list[1], env); + if (cond && cond->type() == MalType::Type::Symbol) + { + auto condition = static_cast(*cond); + if (!condition) + { + if (list.size() > 3) + { + input = list[3]; + continue; + } + else + return env->get("nil"); + } + } + input = list[2]; + continue; + } + + if (symbol == "fn*") + { + if (env->is_root()) + { + std::weak_ptr weak_env = env; + auto closure = [list, weak_env](std::vector> params) + { + auto new_env = std::make_shared(static_cast(*list[1]), params, weak_env.lock()); + return eval(list[2], new_env); + }; + return std::make_shared(closure, list[2], list[1], env); + } + + auto closure = [list, env](std::vector> params) + { + auto new_env = std::make_shared(static_cast(*list[1]), params, env); + return eval(list[2], new_env); + }; + return std::make_shared(closure, list[2], list[1], env); + } + } + + auto plist = eval_ast(input, env); + if (!plist) + return nullptr; + + auto &new_list = static_cast(*plist); + auto &func = static_cast(*new_list[0]); + + std::vector> args; + for (unsigned i = 1; i < new_list.size(); ++i) + args.push_back(new_list[i]); + + if (func.is_fn()) + { + input = func.ast(); + env = std::make_shared(static_cast(*func.params()), args, func.env()); + continue; + } + + return func(args); + } +} + +std::string print(std::shared_ptr input) +{ + return pr_str(input); +} + +std::string rep(const std::string &input, std::shared_ptr env) +{ + auto read_result = read(input); + auto eval_result = eval(read_result, env); + auto print_result = print(eval_result); + return print_result; +} + +int main(int argc, char *argv[]) +{ + auto repl_env = std::make_shared(); + + for (auto &[key, value] : ns()) + repl_env->set(key, value); + + rep("(def! not (fn* (a) (if a false true)))", repl_env); + + while (!std::cin.eof()) + { + std::string input; + std::cout << "user> "; + std::getline(std::cin, input); + try + { + auto rep_result = rep(input, repl_env); + std::cout << rep_result << std::endl; + } + catch (std::exception &e) + { + std::cerr << e.what() << std::endl; + } + } + + return 0; +} diff --git a/impls/cpp.2/step6_file.cc b/impls/cpp.2/step6_file.cc new file mode 100644 index 0000000000..3fdb906166 --- /dev/null +++ b/impls/cpp.2/step6_file.cc @@ -0,0 +1,237 @@ +#include "core.hh" +#include "env.hh" +#include "printer.hh" +#include "reader.hh" +#include +#include +#include + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env); + +std::shared_ptr read(const std::string &input) +{ + return read_str(input); +} + +std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr env) +{ + switch (ast->type()) + { + case MalType::Type::Symbol: + { + auto symbol = static_cast(*ast); + if (symbol.is_string() || symbol.is_keyword()) + return ast; + + return env->get(symbol); + } + case MalType::Type::List: + { + auto &list = static_cast(*ast); + auto new_list = std::make_shared(list.lparen(), list.rparen()); + for (auto &a : list) + { + auto l = eval(a, env); + if (!l) + return nullptr; + new_list->push_back(l); + } + return new_list; + } + case MalType::Type::Map: + { + auto &map = static_cast(*ast); + auto new_map = std::make_shared(); + for (auto &[key, value] : map) + { + auto v = eval(value, env); + if (!v) + return nullptr; + (*new_map)[key] = v; + } + return new_map; + } + default: + return ast; + } +} + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env) +{ + while (true) + { + if (!input) + return nullptr; + + if (input->type() != MalType::Type::List) + return eval_ast(input, env); + + auto &list = static_cast(*input); + if (list.empty()) + return input; + + if (!list.is_list()) + return eval_ast(input, env); + + if (list[0]->type() == MalType::Type::Symbol) + { + std::string symbol = static_cast(*list[0]); + if (symbol == "def!") + { + std::string key = static_cast(*list[1]); + auto val = eval(list[2], env); + if (!val) + return nullptr; + env->set(key, val); + return val; + } + + if (symbol == "let*") + { + auto new_env = std::make_shared(env); + auto &bindings = static_cast(*list[1]); + for (unsigned i = 0; i < bindings.size(); i += 2) + { + std::string key = static_cast(*bindings[i]); + auto val = eval(bindings[i + 1], new_env); + if (!val) + return nullptr; + new_env->set(key, val); + } + input = list[2]; + env = new_env; + continue; + } + + if (symbol == "do") + { + for (unsigned i = 1; i < list.size() - 1; ++i) + eval(list[i], env); + input = list[list.size() - 1]; + continue; + } + + if (symbol == "if") + { + auto cond = eval(list[1], env); + if (cond && cond->type() == MalType::Type::Symbol) + { + auto condition = static_cast(*cond); + if (!condition) + { + if (list.size() > 3) + { + input = list[3]; + continue; + } + else + return env->get("nil"); + } + } + input = list[2]; + continue; + } + + if (symbol == "fn*") + { + if (env->is_root()) + { + std::weak_ptr weak_env = env; + auto closure = [list, weak_env](std::vector> params) + { + auto new_env = std::make_shared(static_cast(*list[1]), params, weak_env.lock()); + return eval(list[2], new_env); + }; + return std::make_shared(closure, list[2], list[1], env); + } + + auto closure = [list, env](std::vector> params) + { + auto new_env = std::make_shared(static_cast(*list[1]), params, env); + return eval(list[2], new_env); + }; + return std::make_shared(closure, list[2], list[1], env); + } + } + + auto plist = eval_ast(input, env); + if (!plist) + return nullptr; + + auto &new_list = static_cast(*plist); + auto &func = static_cast(*new_list[0]); + + std::vector> args; + for (unsigned i = 1; i < new_list.size(); ++i) + args.push_back(new_list[i]); + + if (func.is_fn()) + { + input = func.ast(); + env = std::make_shared(static_cast(*func.params()), args, func.env()); + continue; + } + + return func(args); + } +} + +std::string print(std::shared_ptr input) +{ + return pr_str(input); +} + +std::string rep(const std::string &input, std::shared_ptr env) +{ + auto read_result = read(input); + auto eval_result = eval(read_result, env); + auto print_result = print(eval_result); + return print_result; +} + +int main(int argc, char *argv[]) +{ + auto repl_env = std::make_shared(); + + for (auto &[key, value] : ns()) + repl_env->set(key, value); + + std::weak_ptr weak_env = repl_env; + auto closure = [weak_env](std::vector> ast) + { + return eval(ast[0], weak_env.lock()); + }; + repl_env->set("eval", std::make_shared(closure)); + + rep("(def! not (fn* (a) (if a false true)))", repl_env); + rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env); + + auto args = std::make_shared('(', ')'); + for (int i = 2; i < argc; ++i) + args->push_back(read('"' + std::string(argv[i]) + '"')); + repl_env->set("*ARGV*", args); + + if (argc > 1) + { + rep("(load-file \"" + std::string(argv[1]) + "\")", repl_env); + return 0; + } + + while (!std::cin.eof()) + { + std::string input; + std::cout << "user> "; + std::getline(std::cin, input); + try + { + auto rep_result = rep(input, repl_env); + std::cout << rep_result << std::endl; + } + catch (std::exception &e) + { + std::cerr << e.what() << std::endl; + } + } + + return 0; +} diff --git a/impls/cpp.2/step7_quote.cc b/impls/cpp.2/step7_quote.cc new file mode 100644 index 0000000000..0f8710344b --- /dev/null +++ b/impls/cpp.2/step7_quote.cc @@ -0,0 +1,311 @@ +#include "core.hh" +#include "env.hh" +#include "printer.hh" +#include "reader.hh" +#include +#include +#include + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env); + +std::shared_ptr read(const std::string &input) +{ + return read_str(input); +} + +std::shared_ptr quasiquote(std::shared_ptr ast, bool handle_vec = true) +{ + auto new_list = std::make_shared('(', ')'); + + switch (ast->type()) + { + case MalType::Type::List: + { + auto &list = static_cast(*ast); + if (!handle_vec || list.is_list()) + { + if (list.empty()) + return ast; + + if (handle_vec && list.size() > 1 && list[0]->type() == MalType::Type::Symbol && static_cast(*list[0]) == "unquote") + return list[1]; + + auto rest = std::make_shared(list.lparen(), list.rparen()); + for (unsigned i = 1; i < list.size(); ++i) + rest->push_back(list[i]); + + if (list[0]->type() == MalType::Type::List) + { + auto &sublist = static_cast(*list[0]); + if (!sublist.empty() && sublist[0]->type() == MalType::Type::Symbol && static_cast(*sublist[0]) == "splice-unquote") + { + new_list->push_back(std::make_shared("concat")); + new_list->push_back(sublist[1]); + new_list->push_back(quasiquote(rest)); + return new_list; + } + } + + new_list->push_back(std::make_shared("cons")); + new_list->push_back(quasiquote(list[0])); + new_list->push_back(quasiquote(rest)); + return new_list; + } + if (list.is_vector()) + { + new_list->push_back(std::make_shared("vec")); + new_list->push_back(quasiquote(list.to_list(), false)); + return new_list; + } + [[fallthrough]]; + } + case MalType::Type::Symbol: + { + auto &symbol = static_cast(*ast); + if ((symbol == "nil") || (symbol == "true") || (symbol == "false")) + return ast; + [[fallthrough]]; + } + case MalType::Type::Map: + new_list->push_back(std::make_shared("quote")); + new_list->push_back(ast); + return new_list; + default: + return ast; + } +} + +std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr env) +{ + switch (ast->type()) + { + case MalType::Type::Symbol: + { + auto &symbol = static_cast(*ast); + if (symbol.is_string() || symbol.is_keyword()) + return ast; + + return env->get(symbol); + } + case MalType::Type::List: + { + auto &list = static_cast(*ast); + auto new_list = std::make_shared(list.lparen(), list.rparen()); + for (auto &a : list) + { + auto l = eval(a, env); + if (!l) + return nullptr; + new_list->push_back(l); + } + return new_list; + } + case MalType::Type::Map: + { + auto &map = static_cast(*ast); + auto new_map = std::make_shared(); + for (auto &[key, value] : map) + { + auto v = eval(value, env); + if (!v) + return nullptr; + (*new_map)[key] = v; + } + return new_map; + } + default: + return ast; + } +} + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env) +{ + while (true) + { + if (!input) + return nullptr; + + if (input->type() != MalType::Type::List) + return eval_ast(input, env); + + auto &list = static_cast(*input); + if (list.empty()) + return input; + + if (!list.is_list()) + return eval_ast(input, env); + + if (list[0]->type() == MalType::Type::Symbol) + { + std::string symbol = static_cast(*list[0]); + if (symbol == "def!") + { + std::string key = static_cast(*list[1]); + auto val = eval(list[2], env); + if (!val) + return nullptr; + env->set(key, val); + return val; + } + + if (symbol == "let*") + { + auto new_env = std::make_shared(env); + auto &bindings = static_cast(*list[1]); + for (unsigned i = 0; i < bindings.size(); i += 2) + { + std::string key = static_cast(*bindings[i]); + auto val = eval(bindings[i + 1], new_env); + if (!val) + return nullptr; + new_env->set(key, val); + } + input = list[2]; + env = new_env; + continue; + } + + if (symbol == "do") + { + for (unsigned i = 1; i < list.size() - 1; ++i) + eval(list[i], env); + input = list[list.size() - 1]; + continue; + } + + if (symbol == "if") + { + auto cond = eval(list[1], env); + if (cond && cond->type() == MalType::Type::Symbol) + { + auto condition = static_cast(*cond); + if (!condition) + { + if (list.size() > 3) + { + input = list[3]; + continue; + } + else + return env->get("nil"); + } + } + input = list[2]; + continue; + } + + if (symbol == "fn*") + { + if (env->is_root()) + { + std::weak_ptr weak_env = env; + auto closure = [list, weak_env](std::vector> params) + { + auto new_env = std::make_shared(static_cast(*list[1]), params, weak_env.lock()); + return eval(list[2], new_env); + }; + return std::make_shared(closure, list[2], list[1], env); + } + + auto closure = [list, env](std::vector> params) + { + auto new_env = std::make_shared(static_cast(*list[1]), params, env); + return eval(list[2], new_env); + }; + return std::make_shared(closure, list[2], list[1], env); + } + + if (symbol == "quote") + return list[1]; + + if (symbol == "quasiquoteexpand") + return quasiquote(list[1]); + + if (symbol == "quasiquote") + { + input = quasiquote(list[1]); + continue; + } + } + + auto plist = eval_ast(input, env); + if (!plist) + return nullptr; + + auto &new_list = static_cast(*plist); + auto &func = static_cast(*new_list[0]); + + std::vector> args; + for (unsigned i = 1; i < new_list.size(); ++i) + args.push_back(new_list[i]); + + if (func.is_fn()) + { + input = func.ast(); + env = std::make_shared(static_cast(*func.params()), args, func.env()); + continue; + } + + return func(args); + } +} + +std::string print(std::shared_ptr input) +{ + return pr_str(input); +} + +std::string rep(const std::string &input, std::shared_ptr env) +{ + auto read_result = read(input); + auto eval_result = eval(read_result, env); + auto print_result = print(eval_result); + return print_result; +} + +int main(int argc, char *argv[]) +{ + auto repl_env = std::make_shared(); + + for (auto &[key, value] : ns()) + repl_env->set(key, value); + + std::weak_ptr weak_env = repl_env; + auto closure = [weak_env](std::vector> ast) + { + return eval(ast[0], weak_env.lock()); + }; + repl_env->set("eval", std::make_shared(closure)); + + rep("(def! not (fn* (a) (if a false true)))", repl_env); + rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env); + + auto args = std::make_shared('(', ')'); + for (int i = 2; i < argc; ++i) + args->push_back(read('"' + std::string(argv[i]) + '"')); + repl_env->set("*ARGV*", args); + + if (argc > 1) + { + rep("(load-file \"" + std::string(argv[1]) + "\")", repl_env); + return 0; + } + + while (!std::cin.eof()) + { + std::string input; + std::cout << "user> "; + std::getline(std::cin, input); + try + { + auto rep_result = rep(input, repl_env); + std::cout << rep_result << std::endl; + } + catch (std::exception &e) + { + std::cerr << e.what() << std::endl; + } + } + + return 0; +} diff --git a/impls/cpp.2/step8_macros.cc b/impls/cpp.2/step8_macros.cc new file mode 100644 index 0000000000..b9796e0dae --- /dev/null +++ b/impls/cpp.2/step8_macros.cc @@ -0,0 +1,372 @@ +#include "core.hh" +#include "env.hh" +#include "printer.hh" +#include "reader.hh" +#include +#include +#include + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env); + +std::shared_ptr read(const std::string &input) +{ + return read_str(input); +} + +std::shared_ptr quasiquote(std::shared_ptr ast, bool handle_vec = true) +{ + auto new_list = std::make_shared('(', ')'); + + switch (ast->type()) + { + case MalType::Type::List: + { + auto &list = static_cast(*ast); + if (!handle_vec || list.is_list()) + { + if (list.empty()) + return ast; + + if (handle_vec && list.size() > 1 && list[0]->type() == MalType::Type::Symbol && static_cast(*list[0]) == "unquote") + return list[1]; + + auto rest = std::make_shared(list.lparen(), list.rparen()); + for (unsigned i = 1; i < list.size(); ++i) + rest->push_back(list[i]); + + if (list[0]->type() == MalType::Type::List) + { + auto &sublist = static_cast(*list[0]); + if (!sublist.empty() && sublist[0]->type() == MalType::Type::Symbol && static_cast(*sublist[0]) == "splice-unquote") + { + new_list->push_back(std::make_shared("concat")); + new_list->push_back(sublist[1]); + new_list->push_back(quasiquote(rest)); + return new_list; + } + } + + new_list->push_back(std::make_shared("cons")); + new_list->push_back(quasiquote(list[0])); + new_list->push_back(quasiquote(rest)); + return new_list; + } + if (list.is_vector()) + { + new_list->push_back(std::make_shared("vec")); + new_list->push_back(quasiquote(list.to_list(), false)); + return new_list; + } + [[fallthrough]]; + } + case MalType::Type::Symbol: + { + auto &symbol = static_cast(*ast); + if ((symbol == "nil") || (symbol == "true") || (symbol == "false")) + return ast; + [[fallthrough]]; + } + case MalType::Type::Map: + new_list->push_back(std::make_shared("quote")); + new_list->push_back(ast); + return new_list; + default: + return ast; + } +} + +bool is_macro_call(std::shared_ptr ast, std::shared_ptr env) +{ + if (!ast || ast->type() != MalType::Type::List) + return false; + + auto &list = static_cast(*ast); + if (!list.is_list() || list.empty() || list[0]->type() != MalType::Type::Symbol) + return false; + + auto &symbol_ = static_cast(*list[0]); + if (!env->find(symbol_)) + return false; + + auto symbol = env->get(symbol_); + if (symbol->type() != MalType::Type::Func) + return false; + + auto &func = static_cast(*symbol); + return func.is_macro; +} + +std::shared_ptr macroexpand(std::shared_ptr ast, std::shared_ptr env) +{ + auto ast_ = ast; + + while (is_macro_call(ast_, env)) + { + auto &list = static_cast(*ast); + auto symbol = env->get(static_cast(*list[0])); + auto &func = static_cast(*symbol); + + std::vector> rest; + for (unsigned i = 1; i < list.size(); ++i) + rest.push_back(list[i]); + + ast_ = func(rest); + } + + return ast_; +} + +std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr env) +{ + switch (ast->type()) + { + case MalType::Type::Symbol: + { + auto &symbol = static_cast(*ast); + if (symbol.is_string() || symbol.is_keyword()) + return ast; + + return env->get(symbol); + } + case MalType::Type::List: + { + auto &list = static_cast(*ast); + auto new_list = std::make_shared(list.lparen(), list.rparen()); + for (auto &a : list) + { + auto l = eval(a, env); + if (!l) + return nullptr; + new_list->push_back(l); + } + return new_list; + } + case MalType::Type::Map: + { + auto &map = static_cast(*ast); + auto new_map = std::make_shared(); + for (auto &[key, value] : map) + { + auto v = eval(value, env); + if (!v) + return nullptr; + (*new_map)[key] = v; + } + return new_map; + } + default: + return ast; + } +} + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env) +{ + while (true) + { + input = macroexpand(input, env); + + if (!input) + return nullptr; + + if (input->type() != MalType::Type::List) + return eval_ast(input, env); + + auto &list = static_cast(*input); + if (list.empty()) + return input; + + if (!list.is_list()) + return eval_ast(input, env); + + if (list[0]->type() == MalType::Type::Symbol) + { + std::string symbol = static_cast(*list[0]); + if (symbol == "def!") + { + std::string key = static_cast(*list[1]); + auto val = eval(list[2], env); + if (!val) + return nullptr; + env->set(key, val); + return val; + } + + if (symbol == "defmacro!") + { + std::string key = static_cast(*list[1]); + auto val = eval(list[2], env); + if (!val) + return nullptr; + + auto &func = static_cast(*val); + func.is_macro = true; + + env->set(key, val); + return val; + } + + if (symbol == "let*") + { + auto new_env = std::make_shared(env); + auto &bindings = static_cast(*list[1]); + for (unsigned i = 0; i < bindings.size(); i += 2) + { + std::string key = static_cast(*bindings[i]); + auto val = eval(bindings[i + 1], new_env); + if (!val) + return nullptr; + new_env->set(key, val); + } + input = list[2]; + env = new_env; + continue; + } + + if (symbol == "do") + { + for (unsigned i = 1; i < list.size() - 1; ++i) + eval(list[i], env); + input = list[list.size() - 1]; + continue; + } + + if (symbol == "if") + { + auto cond = eval(list[1], env); + if (cond && cond->type() == MalType::Type::Symbol) + { + auto condition = static_cast(*cond); + if (!condition) + { + if (list.size() > 3) + { + input = list[3]; + continue; + } + else + return env->get("nil"); + } + } + input = list[2]; + continue; + } + + if (symbol == "fn*") + { + if (env->is_root()) + { + std::weak_ptr weak_env = env; + auto closure = [list, weak_env](std::vector> params) + { + auto new_env = std::make_shared(static_cast(*list[1]), params, weak_env.lock()); + return eval(list[2], new_env); + }; + return std::make_shared(closure, list[2], list[1], env); + } + + auto closure = [list, env](std::vector> params) + { + auto new_env = std::make_shared(static_cast(*list[1]), params, env); + return eval(list[2], new_env); + }; + return std::make_shared(closure, list[2], list[1], env); + } + + if (symbol == "quote") + return list[1]; + + if (symbol == "quasiquoteexpand") + return quasiquote(list[1]); + + if (symbol == "quasiquote") + { + input = quasiquote(list[1]); + continue; + } + + if (symbol == "macroexpand") + return macroexpand(list[1], env); + } + + auto plist = eval_ast(input, env); + if (!plist) + return nullptr; + + auto &new_list = static_cast(*plist); + auto &func = static_cast(*new_list[0]); + + std::vector> args; + for (unsigned i = 1; i < new_list.size(); ++i) + args.push_back(new_list[i]); + + if (func.is_fn()) + { + input = func.ast(); + env = std::make_shared(static_cast(*func.params()), args, func.env()); + continue; + } + + return func(args); + } +} + +std::string print(std::shared_ptr input) +{ + return pr_str(input); +} + +std::string rep(const std::string &input, std::shared_ptr env) +{ + auto read_result = read(input); + auto eval_result = eval(read_result, env); + auto print_result = print(eval_result); + return print_result; +} + +int main(int argc, char *argv[]) +{ + auto repl_env = std::make_shared(); + + for (auto &[key, value] : ns()) + repl_env->set(key, value); + + std::weak_ptr weak_env = repl_env; + auto closure = [weak_env](std::vector> ast) + { + return eval(ast[0], weak_env.lock()); + }; + repl_env->set("eval", std::make_shared(closure)); + + rep("(def! not (fn* (a) (if a false true)))", repl_env); + rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env); + rep("(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)))))))", repl_env); + + auto args = std::make_shared('(', ')'); + for (int i = 2; i < argc; ++i) + args->push_back(read('"' + std::string(argv[i]) + '"')); + repl_env->set("*ARGV*", args); + + if (argc > 1) + { + rep("(load-file \"" + std::string(argv[1]) + "\")", repl_env); + return 0; + } + + while (!std::cin.eof()) + { + std::string input; + std::cout << "user> "; + std::getline(std::cin, input); + try + { + auto rep_result = rep(input, repl_env); + std::cout << rep_result << std::endl; + } + catch (std::exception &e) + { + std::cerr << e.what() << std::endl; + } + } + + return 0; +} diff --git a/impls/cpp.2/step9_try.cc b/impls/cpp.2/step9_try.cc new file mode 100644 index 0000000000..4480f6a3b5 --- /dev/null +++ b/impls/cpp.2/step9_try.cc @@ -0,0 +1,406 @@ +#include "core.hh" +#include "env.hh" +#include "printer.hh" +#include "reader.hh" +#include +#include +#include + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env); + +std::shared_ptr read(const std::string &input) +{ + return read_str(input); +} + +std::shared_ptr quasiquote(std::shared_ptr ast, bool handle_vec = true) +{ + auto new_list = std::make_shared('(', ')'); + + switch (ast->type()) + { + case MalType::Type::List: + { + auto &list = static_cast(*ast); + if (!handle_vec || list.is_list()) + { + if (list.empty()) + return ast; + + if (handle_vec && list.size() > 1 && list[0]->type() == MalType::Type::Symbol && static_cast(*list[0]) == "unquote") + return list[1]; + + auto rest = std::make_shared(list.lparen(), list.rparen()); + for (unsigned i = 1; i < list.size(); ++i) + rest->push_back(list[i]); + + if (list[0]->type() == MalType::Type::List) + { + auto &sublist = static_cast(*list[0]); + if (!sublist.empty() && sublist[0]->type() == MalType::Type::Symbol && static_cast(*sublist[0]) == "splice-unquote") + { + new_list->push_back(std::make_shared("concat")); + new_list->push_back(sublist[1]); + new_list->push_back(quasiquote(rest)); + return new_list; + } + } + + new_list->push_back(std::make_shared("cons")); + new_list->push_back(quasiquote(list[0])); + new_list->push_back(quasiquote(rest)); + return new_list; + } + if (list.is_vector()) + { + new_list->push_back(std::make_shared("vec")); + new_list->push_back(quasiquote(list.to_list(), false)); + return new_list; + } + [[fallthrough]]; + } + case MalType::Type::Symbol: + { + auto &symbol = static_cast(*ast); + if ((symbol == "nil") || (symbol == "true") || (symbol == "false")) + return ast; + [[fallthrough]]; + } + case MalType::Type::Map: + new_list->push_back(std::make_shared("quote")); + new_list->push_back(ast); + return new_list; + default: + return ast; + } +} + +bool is_macro_call(std::shared_ptr ast, std::shared_ptr env) +{ + if (!ast || ast->type() != MalType::Type::List) + return false; + + auto &list = static_cast(*ast); + if (!list.is_list() || list.empty() || list[0]->type() != MalType::Type::Symbol) + return false; + + auto &symbol_ = static_cast(*list[0]); + if (!env->find(symbol_)) + return false; + + auto symbol = env->get(symbol_); + if (symbol->type() != MalType::Type::Func) + return false; + + auto &func = static_cast(*symbol); + return func.is_macro; +} + +std::shared_ptr macroexpand(std::shared_ptr ast, std::shared_ptr env) +{ + auto ast_ = ast; + + while (is_macro_call(ast_, env)) + { + auto &list = static_cast(*ast); + auto symbol = env->get(static_cast(*list[0])); + auto &func = static_cast(*symbol); + + std::vector> rest; + for (unsigned i = 1; i < list.size(); ++i) + rest.push_back(list[i]); + + ast_ = func(rest); + } + + return ast_; +} + +std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr env) +{ + switch (ast->type()) + { + case MalType::Type::Symbol: + { + auto &symbol = static_cast(*ast); + if (symbol.is_string() || symbol.is_keyword()) + return ast; + + return env->get(symbol); + } + case MalType::Type::List: + { + auto &list = static_cast(*ast); + auto new_list = std::make_shared(list.lparen(), list.rparen()); + for (auto &a : list) + { + auto l = eval(a, env); + if (!l) + return nullptr; + new_list->push_back(l); + } + return new_list; + } + case MalType::Type::Map: + { + auto &map = static_cast(*ast); + auto new_map = std::make_shared(); + for (auto &[key, value] : map) + { + auto v = eval(value, env); + if (!v) + return nullptr; + (*new_map)[key] = v; + } + return new_map; + } + default: + return ast; + } +} + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env) +{ + while (true) + { + input = macroexpand(input, env); + + if (!input) + return nullptr; + + if (input->type() != MalType::Type::List) + return eval_ast(input, env); + + auto &list = static_cast(*input); + if (list.empty()) + return input; + + if (!list.is_list()) + return eval_ast(input, env); + + if (list[0]->type() == MalType::Type::Symbol) + { + std::string symbol = static_cast(*list[0]); + if (symbol == "def!") + { + std::string key = static_cast(*list[1]); + auto val = eval(list[2], env); + if (!val) + return nullptr; + env->set(key, val); + return val; + } + + if (symbol == "defmacro!") + { + std::string key = static_cast(*list[1]); + auto val = eval(list[2], env); + if (!val) + return nullptr; + + auto &func = static_cast(*val); + func.is_macro = true; + + env->set(key, val); + return val; + } + + if (symbol == "let*") + { + auto new_env = std::make_shared(env); + auto &bindings = static_cast(*list[1]); + for (unsigned i = 0; i < bindings.size(); i += 2) + { + std::string key = static_cast(*bindings[i]); + auto val = eval(bindings[i + 1], new_env); + if (!val) + return nullptr; + new_env->set(key, val); + } + input = list[2]; + env = new_env; + continue; + } + + if (symbol == "do") + { + for (unsigned i = 1; i < list.size() - 1; ++i) + eval(list[i], env); + input = list[list.size() - 1]; + continue; + } + + if (symbol == "if") + { + auto cond = eval(list[1], env); + if (cond && cond->type() == MalType::Type::Symbol) + { + auto condition = static_cast(*cond); + if (!condition) + { + if (list.size() > 3) + { + input = list[3]; + continue; + } + else + return env->get("nil"); + } + } + input = list[2]; + continue; + } + + if (symbol == "fn*") + { + if (env->is_root()) + { + std::weak_ptr weak_env = env; + auto closure = [list, weak_env](std::vector> params) + { + auto new_env = std::make_shared(static_cast(*list[1]), params, weak_env.lock()); + return eval(list[2], new_env); + }; + return std::make_shared(closure, list[2], list[1], env); + } + + auto closure = [list, env](std::vector> params) + { + auto new_env = std::make_shared(static_cast(*list[1]), params, env); + return eval(list[2], new_env); + }; + return std::make_shared(closure, list[2], list[1], env); + } + + if (symbol == "quote") + return list[1]; + + if (symbol == "quasiquoteexpand") + return quasiquote(list[1]); + + if (symbol == "quasiquote") + { + input = quasiquote(list[1]); + continue; + } + + if (symbol == "macroexpand") + return macroexpand(list[1], env); + + if (symbol == "try*") + { + try + { + return eval(list[1], env); + } + catch (std::shared_ptr &e) + { + if (list.size() < 3) + throw; + + auto new_env = std::make_shared(env); + auto &catch_clause = static_cast(*list[2]); + std::string key = static_cast(*catch_clause[1]); + new_env->set(key, e); + return eval(catch_clause[2], new_env); + } + catch (std::exception &e) + { + if (list.size() < 3) + throw; + + auto new_env = std::make_shared(env); + auto &catch_clause = static_cast(*list[2]); + std::string key = static_cast(*catch_clause[1]); + new_env->set(key, std::make_shared('"' + std::string(e.what()) + '"')); + return eval(catch_clause[2], new_env); + } + } + } + + auto plist = eval_ast(input, env); + if (!plist) + return nullptr; + + auto &new_list = static_cast(*plist); + auto &func = static_cast(*new_list[0]); + + std::vector> args; + for (unsigned i = 1; i < new_list.size(); ++i) + args.push_back(new_list[i]); + + if (func.is_fn()) + { + input = func.ast(); + env = std::make_shared(static_cast(*func.params()), args, func.env()); + continue; + } + + return func(args); + } +} + +std::string print(std::shared_ptr input) +{ + return pr_str(input); +} + +std::string rep(const std::string &input, std::shared_ptr env) +{ + auto read_result = read(input); + auto eval_result = eval(read_result, env); + auto print_result = print(eval_result); + return print_result; +} + +int main(int argc, char *argv[]) +{ + auto repl_env = std::make_shared(); + + for (auto &[key, value] : ns()) + repl_env->set(key, value); + + std::weak_ptr weak_env = repl_env; + auto closure = [weak_env](std::vector> ast) + { + return eval(ast[0], weak_env.lock()); + }; + repl_env->set("eval", std::make_shared(closure)); + + rep("(def! not (fn* (a) (if a false true)))", repl_env); + rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env); + rep("(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)))))))", repl_env); + + auto args = std::make_shared('(', ')'); + for (int i = 2; i < argc; ++i) + args->push_back(read('"' + std::string(argv[i]) + '"')); + repl_env->set("*ARGV*", args); + + if (argc > 1) + { + rep("(load-file \"" + std::string(argv[1]) + "\")", repl_env); + return 0; + } + + while (!std::cin.eof()) + { + std::string input; + std::cout << "user> "; + std::getline(std::cin, input); + try + { + auto rep_result = rep(input, repl_env); + std::cout << rep_result << std::endl; + } + catch (std::exception &e) + { + std::cerr << e.what() << std::endl; + } + catch (std::shared_ptr &e) + { + std::cerr << "exception " << pr_str(e) << std::endl; + } + } + + return 0; +} diff --git a/impls/cpp.2/stepA_mal.cc b/impls/cpp.2/stepA_mal.cc new file mode 100644 index 0000000000..d64cc79463 --- /dev/null +++ b/impls/cpp.2/stepA_mal.cc @@ -0,0 +1,410 @@ +#include "core.hh" +#include "env.hh" +#include "printer.hh" +#include "reader.hh" +#include +#include +#include + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env); + +std::shared_ptr read(const std::string &input) +{ + return read_str(input); +} + +std::shared_ptr quasiquote(std::shared_ptr ast, bool handle_vec = true) +{ + auto new_list = std::make_shared('(', ')'); + + switch (ast->type()) + { + case MalType::Type::List: + { + auto &list = static_cast(*ast); + if (!handle_vec || list.is_list()) + { + if (list.empty()) + return ast; + + if (handle_vec && list.size() > 1 && list[0]->type() == MalType::Type::Symbol && static_cast(*list[0]) == "unquote") + return list[1]; + + auto rest = std::make_shared(list.lparen(), list.rparen()); + for (unsigned i = 1; i < list.size(); ++i) + rest->push_back(list[i]); + + if (list[0]->type() == MalType::Type::List) + { + auto &sublist = static_cast(*list[0]); + if (!sublist.empty() && sublist[0]->type() == MalType::Type::Symbol && static_cast(*sublist[0]) == "splice-unquote") + { + new_list->push_back(std::make_shared("concat")); + new_list->push_back(sublist[1]); + new_list->push_back(quasiquote(rest)); + return new_list; + } + } + + new_list->push_back(std::make_shared("cons")); + new_list->push_back(quasiquote(list[0])); + new_list->push_back(quasiquote(rest)); + return new_list; + } + if (list.is_vector()) + { + new_list->push_back(std::make_shared("vec")); + new_list->push_back(quasiquote(list.to_list(), false)); + return new_list; + } + [[fallthrough]]; + } + case MalType::Type::Symbol: + { + auto &symbol = static_cast(*ast); + if ((symbol == "nil") || (symbol == "true") || (symbol == "false")) + return ast; + [[fallthrough]]; + } + case MalType::Type::Map: + new_list->push_back(std::make_shared("quote")); + new_list->push_back(ast); + return new_list; + default: + return ast; + } +} + +bool is_macro_call(std::shared_ptr ast, std::shared_ptr env) +{ + if (!ast || ast->type() != MalType::Type::List) + return false; + + auto &list = static_cast(*ast); + if (!list.is_list() || list.empty() || list[0]->type() != MalType::Type::Symbol) + return false; + + auto &symbol_ = static_cast(*list[0]); + if (!env->find(symbol_)) + return false; + + auto symbol = env->get(symbol_); + if (symbol->type() != MalType::Type::Func) + return false; + + auto &func = static_cast(*symbol); + return func.is_macro; +} + +std::shared_ptr macroexpand(std::shared_ptr ast, std::shared_ptr env) +{ + auto ast_ = ast; + + while (is_macro_call(ast_, env)) + { + auto &list = static_cast(*ast_); + auto symbol = env->get(static_cast(*list[0])); + auto &func = static_cast(*symbol); + + std::vector> rest; + for (unsigned i = 1; i < list.size(); ++i) + rest.push_back(list[i]); + + ast_ = func(rest); + } + + return ast_; +} + +std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr env) +{ + switch (ast->type()) + { + case MalType::Type::Symbol: + { + auto &symbol = static_cast(*ast); + if (symbol.is_string() || symbol.is_keyword()) + return ast; + + return env->get(symbol); + } + case MalType::Type::List: + { + auto &list = static_cast(*ast); + auto new_list = std::make_shared(list.lparen(), list.rparen()); + for (auto &a : list) + { + auto l = eval(a, env); + if (!l) + return nullptr; + new_list->push_back(l); + } + return new_list; + } + case MalType::Type::Map: + { + auto &map = static_cast(*ast); + auto new_map = std::make_shared(); + for (auto &[key, value] : map) + { + auto v = eval(value, env); + if (!v) + return nullptr; + (*new_map)[key] = v; + } + return new_map; + } + default: + return ast; + } +} + +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env) +{ + while (true) + { + input = macroexpand(input, env); + + if (!input) + return nullptr; + + if (input->type() != MalType::Type::List) + return eval_ast(input, env); + + auto &list = static_cast(*input); + if (list.empty()) + return input; + + if (!list.is_list()) + return eval_ast(input, env); + + if (list[0]->type() == MalType::Type::Symbol) + { + std::string symbol = static_cast(*list[0]); + if (symbol == "def!") + { + std::string key = static_cast(*list[1]); + auto val = eval(list[2], env); + if (!val) + return nullptr; + env->set(key, val); + return val; + } + + if (symbol == "defmacro!") + { + std::string key = static_cast(*list[1]); + auto val = eval(list[2], env); + if (!val) + return nullptr; + + auto func = static_cast(*val); + func.is_macro = true; + + auto new_val = std::make_shared(func); + env->set(key, new_val); + return new_val; + } + + if (symbol == "let*") + { + auto new_env = std::make_shared(env); + auto &bindings = static_cast(*list[1]); + for (unsigned i = 0; i < bindings.size(); i += 2) + { + std::string key = static_cast(*bindings[i]); + auto val = eval(bindings[i + 1], new_env); + if (!val) + return nullptr; + new_env->set(key, val); + } + input = list[2]; + env = new_env; + continue; + } + + if (symbol == "do") + { + for (unsigned i = 1; i < list.size() - 1; ++i) + eval(list[i], env); + input = list[list.size() - 1]; + continue; + } + + if (symbol == "if") + { + auto cond = eval(list[1], env); + if (cond && cond->type() == MalType::Type::Symbol) + { + auto condition = static_cast(*cond); + if (!condition) + { + if (list.size() > 3) + { + input = list[3]; + continue; + } + else + return env->get("nil"); + } + } + input = list[2]; + continue; + } + + if (symbol == "fn*") + { + if (env->is_root()) + { + std::weak_ptr weak_env = env; + auto closure = [list, weak_env](std::vector> params) + { + auto new_env = std::make_shared(static_cast(*list[1]), params, weak_env.lock()); + return eval(list[2], new_env); + }; + return std::make_shared(closure, list[2], list[1], env); + } + + auto closure = [list, env](std::vector> params) + { + auto new_env = std::make_shared(static_cast(*list[1]), params, env); + return eval(list[2], new_env); + }; + return std::make_shared(closure, list[2], list[1], env); + } + + if (symbol == "quote") + return list[1]; + + if (symbol == "quasiquoteexpand") + return quasiquote(list[1]); + + if (symbol == "quasiquote") + { + input = quasiquote(list[1]); + continue; + } + + if (symbol == "macroexpand") + return macroexpand(list[1], env); + + if (symbol == "try*") + { + try + { + return eval(list[1], env); + } + catch (std::shared_ptr &e) + { + if (list.size() < 3) + throw; + + auto new_env = std::make_shared(env); + auto &catch_clause = static_cast(*list[2]); + std::string key = static_cast(*catch_clause[1]); + new_env->set(key, e); + return eval(catch_clause[2], new_env); + } + catch (std::exception &e) + { + if (list.size() < 3) + throw; + + auto new_env = std::make_shared(env); + auto &catch_clause = static_cast(*list[2]); + std::string key = static_cast(*catch_clause[1]); + new_env->set(key, std::make_shared('"' + std::string(e.what()) + '"')); + return eval(catch_clause[2], new_env); + } + } + } + + auto plist = eval_ast(input, env); + if (!plist) + return nullptr; + + auto &new_list = static_cast(*plist); + auto &func = static_cast(*new_list[0]); + + std::vector> args; + for (unsigned i = 1; i < new_list.size(); ++i) + args.push_back(new_list[i]); + + if (func.is_fn()) + { + input = func.ast(); + env = std::make_shared(static_cast(*func.params()), args, func.env()); + continue; + } + + return func(args); + } +} + +std::string print(std::shared_ptr input) +{ + return pr_str(input); +} + +std::string rep(const std::string &input, std::shared_ptr env) +{ + auto read_result = read(input); + auto eval_result = eval(read_result, env); + auto print_result = print(eval_result); + return print_result; +} + +int main(int argc, char *argv[]) +{ + auto repl_env = std::make_shared(); + + for (auto &[key, value] : ns()) + repl_env->set(key, value); + + std::weak_ptr weak_env = repl_env; + auto closure = [weak_env](std::vector> ast) + { + return eval(ast[0], weak_env.lock()); + }; + repl_env->set("eval", std::make_shared(closure)); + + rep("(def! not (fn* (a) (if a false true)))", repl_env); + rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env); + rep("(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)))))))", repl_env); + rep("(def! *host-language* \"cc\")", repl_env); + + auto args = std::make_shared('(', ')'); + for (int i = 2; i < argc; ++i) + args->push_back(read('"' + std::string(argv[i]) + '"')); + repl_env->set("*ARGV*", args); + + if (argc > 1) + { + rep("(load-file \"" + std::string(argv[1]) + "\")", repl_env); + return 0; + } + + rep("(println (str \"Mal [\" *host-language* \"]\"))", repl_env); + + while (!std::cin.eof()) + { + std::string input; + std::cout << "user> "; + std::getline(std::cin, input); + try + { + auto rep_result = rep(input, repl_env); + std::cout << rep_result << std::endl; + } + catch (std::exception &e) + { + std::cerr << e.what() << std::endl; + } + catch (std::shared_ptr &e) + { + std::cerr << "exception " << pr_str(e) << std::endl; + } + } + + return 0; +} diff --git a/impls/cpp.2/types.hh b/impls/cpp.2/types.hh new file mode 100644 index 0000000000..ca87e7914c --- /dev/null +++ b/impls/cpp.2/types.hh @@ -0,0 +1,258 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class Env; + +class MalType +{ +public: + enum class Type + { + Int, + Symbol, + List, + Map, + Func, + Atom + }; + + MalType(Type t) : type_(t) {} + virtual ~MalType() = default; + + virtual bool operator==(const MalType &rhs) const noexcept = 0; + + Type type() const { return type_; } + +private: + Type type_; +}; + +class MalInt : public MalType +{ +public: + MalInt(int val = 0) + : MalType(Type::Int), val_(val) {} + + operator int() const { return val_; } + + virtual bool operator==(const MalType &rhs) const noexcept override { return val_ == static_cast(rhs).val_; }; + +private: + int val_; +}; + +class MalSymbol : public MalType +{ +public: + MalSymbol(const std::string &symbol) + : MalType(Type::Symbol), symbol_(symbol) {} + + MalSymbol &operator=(const std::string &symbol) + { + symbol_ = symbol; + return *this; + } + + operator std::string() const { return symbol_[0] == '"' ? symbol_.substr(1, symbol_.length() - 2) : symbol_; } + + bool operator<(const MalSymbol &str) const noexcept { return symbol_ < str.symbol_; } + + bool operator==(const std::string &str) const noexcept { return symbol_ == str; } + bool operator==(const char *str) const noexcept { return symbol_ == str; } + bool operator!=(const std::string &str) const noexcept { return symbol_ != str; } + bool operator!=(const char *str) const noexcept { return symbol_ != str; } + + virtual bool operator==(const MalType &rhs) const noexcept override { return symbol_ == static_cast(rhs).symbol_; }; + + bool is_string() const { return symbol_[0] == '"'; } + bool is_keyword() const { return symbol_[0] == ':'; } + bool is_reserved() const { return symbol_ == "true" || symbol_ == "false" || symbol_ == "nil"; } + + operator bool() const { return symbol_ != "nil" && symbol_ != "false"; } + +private: + std::string symbol_; +}; + +namespace std +{ + template <> + struct hash + { + typedef MalSymbol argument_type; + typedef std::size_t result_type; + result_type operator()(argument_type const &s) const + { + return std::hash()(s); + } + }; +} + +class MalList : public MalType +{ +public: + MalList(char lparen, char rparen) + : MalType(Type::List), lparen_(lparen), rparen_(rparen) {} + + const std::shared_ptr &operator[](std::size_t pos) const { return list_.at(pos); } + [[nodiscard]] bool empty() const noexcept { return list_.empty(); } + std::size_t size() const noexcept { return list_.size(); } + void push_back(std::shared_ptr value) { return list_.push_back(value); } + + std::vector>::iterator begin() noexcept { return list_.begin(); } + std::vector>::const_iterator begin() const noexcept { return list_.begin(); } + std::vector>::iterator end() noexcept { return list_.end(); } + std::vector>::const_iterator end() const noexcept { return list_.end(); } + + char lparen() const { return lparen_; } + char rparen() const { return rparen_; } + + bool is_list() const { return lparen_ == '(' && rparen_ == ')'; } + bool is_vector() const { return lparen_ == '[' && rparen_ == ']'; } + + void set_meta(std::shared_ptr meta) { meta_ = meta; } + std::shared_ptr get_meta() const { return meta_; } + + std::shared_ptr to_list() const + { + auto ret = std::make_shared(*this); + ret->lparen_ = '('; + ret->rparen_ = ')'; + return ret; + } + + std::shared_ptr to_vector() const + { + auto ret = std::make_shared(*this); + ret->lparen_ = '['; + ret->rparen_ = ']'; + return ret; + } + + virtual bool operator==(const MalType &rhs) const noexcept override + { + auto &rhs_list = static_cast(rhs); + + if (rhs_list.size() != list_.size()) + return false; + + for (unsigned i = 0; i < list_.size(); ++i) + { + if (rhs_list[i]->type() != list_[i]->type()) + return false; + if (!(*(rhs_list[i]) == *(list_[i]))) + return false; + } + + return true; + } + +private: + char lparen_; + char rparen_; + std::vector> list_; + std::shared_ptr meta_ = std::make_shared("nil"); +}; + +class MalMap : public MalType +{ +public: + // using map_t = std::unordered_map>; + using map_t = std::map>; + + MalMap() + : MalType(Type::Map) {} + + std::shared_ptr &operator[](const MalSymbol &key) { return map_[key]; } + [[nodiscard]] bool empty() const noexcept { return map_.empty(); } + std::size_t size() const noexcept { return map_.size(); } + void erase(const MalSymbol &key) { map_.erase(key); } + + map_t::iterator begin() noexcept { return map_.begin(); } + map_t::const_iterator begin() const noexcept { return map_.begin(); } + map_t::iterator end() noexcept { return map_.end(); } + map_t::const_iterator end() const noexcept { return map_.end(); } + + map_t::const_iterator find(const MalSymbol &key) const { return map_.find(key); } + + void set_meta(std::shared_ptr meta) { meta_ = meta; } + std::shared_ptr get_meta() const { return meta_; } + + virtual bool operator==(const MalType &rhs) const noexcept override + { + auto &rhs_map = static_cast(rhs); + + if (rhs_map.size() != map_.size()) + return false; + + for (auto [key, val] : map_) + { + auto result = rhs_map.find(key); + if (result == rhs_map.end()) + return false; + if (result->second->type() != val->type()) + return false; + if (!(*result->second == *val)) + return false; + } + + return true; + } + +private: + map_t map_; + std::shared_ptr meta_ = std::make_shared("nil"); +}; + +class MalFunc : public MalType +{ +public: + MalFunc(const std::function(std::vector>)> &func) + : MalType(Type::Func), func_(func) {} + + MalFunc(const std::function(std::vector>)> &func, std::shared_ptr ast, std::shared_ptr params, std::shared_ptr env) + : MalType(Type::Func), func_(func), ast_(ast), params_(params), env_(env), is_fn_(true) {} + + std::shared_ptr operator()(std::vector> args) const { return func_(args); } + + virtual bool operator==(const MalType &rhs) const noexcept override { return false; } + + bool is_fn() const { return is_fn_; } + std::shared_ptr ast() const { return ast_; } + std::shared_ptr params() const { return params_; } + std::shared_ptr env() const { return env_.lock(); } + + void set_meta(std::shared_ptr meta) { meta_ = meta; } + std::shared_ptr get_meta() const { return meta_; } + + bool is_macro = false; + +private: + std::function(std::vector>)> func_; + std::shared_ptr meta_ = std::make_shared("nil"); + std::shared_ptr ast_; + std::shared_ptr params_; + std::weak_ptr env_; + bool is_fn_ = false; +}; + +class MalAtom : public MalType +{ +public: + MalAtom(std::shared_ptr a) + : MalType(Type::Atom), a_(a) {} + + virtual bool operator==(const MalType &rhs) const noexcept override { return *a_ == rhs; } + + std::shared_ptr deref() const { return a_; } + std::shared_ptr reset(std::shared_ptr a) { return a_ = a; } + +private: + std::shared_ptr a_; +};