From 7bf30168a0c6f82c9c28636dc86a7f5bbb925157 Mon Sep 17 00:00:00 2001 From: Yang Le Date: Sat, 3 Jun 2023 11:20:04 +0800 Subject: [PATCH 01/21] there you go --- Makefile.impls | 3 ++- impls/cc/Makefile | 39 +++++++++++++++++++++++++++++++++++++++ impls/cc/run | 2 ++ impls/cc/step0_repl.cc | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 impls/cc/Makefile create mode 100755 impls/cc/run create mode 100644 impls/cc/step0_repl.cc diff --git a/Makefile.impls b/Makefile.impls index 1a3cbabca8..3267383715 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 cc chuck clojure coffee common-lisp cpp 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 \ @@ -112,6 +112,7 @@ basic_STEP_TO_PROG = $(basic_STEP_TO_PROG_$(basic_MODE)) bbc-basic_STEP_TO_PROG = impls/bbc-basic/$($(1)).bas c_STEP_TO_PROG = impls/c/$($(1)) c.2_STEP_TO_PROG = impls/c.2/$($(1)) +cc_STEP_TO_PROG = impls/cc/$($(1)) chuck_STEP_TO_PROG = impls/chuck/$($(1)).ck clojure_STEP_TO_PROG = $(clojure_STEP_TO_PROG_$(clojure_MODE)) coffee_STEP_TO_PROG = impls/coffee/$($(1)).coffee diff --git a/impls/cc/Makefile b/impls/cc/Makefile new file mode 100644 index 0000000000..d6ce0c5edd --- /dev/null +++ b/impls/cc/Makefile @@ -0,0 +1,39 @@ +LD=$(CXX) +DEBUG=-ggdb +CXXFLAGS=-O3 -Wall $(DEBUG) $(INCPATHS) -std=c++11 +LDFLAGS=-O3 $(DEBUG) $(LIBPATHS) -L. -lreadline -lhistory + +LIBSOURCES=Core.cpp Environment.cpp Reader.cpp ReadLine.cpp String.cpp \ + Types.cpp Validation.cpp +LIBOBJS=$(LIBSOURCES:%.cpp=%.o) + +MAINS=$(wildcard step*.cc) +TARGETS=$(MAINS:%.cc=%) + +.PHONY: all clean + +.SUFFIXES: .cc .o + +all: $(TARGETS) + +dist: mal + +mal: stepA_mal + cp $< $@ + +.deps: *.cc *.h + $(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/cc/run b/impls/cc/run new file mode 100755 index 0000000000..8ba68a5484 --- /dev/null +++ b/impls/cc/run @@ -0,0 +1,2 @@ +#!/bin/bash +exec $(dirname $0)/${STEP:-stepA_mal} "${@}" diff --git a/impls/cc/step0_repl.cc b/impls/cc/step0_repl.cc new file mode 100644 index 0000000000..b5bd24cfb1 --- /dev/null +++ b/impls/cc/step0_repl.cc @@ -0,0 +1,40 @@ +#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); + auto rep_result = rep(input); + std::cout << rep_result << std::endl; + } + + return 0; +} From dd10408a79e5b6540df406e234338c7d917d98ed Mon Sep 17 00:00:00 2001 From: Yang Le Date: Sun, 4 Jun 2023 00:06:28 +0800 Subject: [PATCH 02/21] step1 --- impls/.gitignore | 2 + impls/cc/Makefile | 15 ++-- impls/cc/printer.cc | 67 ++++++++++++++ impls/cc/printer.hh | 5 ++ impls/cc/reader.cc | 169 +++++++++++++++++++++++++++++++++++ impls/cc/reader.hh | 30 +++++++ impls/cc/step1_read_print.cc | 42 +++++++++ impls/cc/types.hh | 84 +++++++++++++++++ 8 files changed, 407 insertions(+), 7 deletions(-) create mode 100644 impls/cc/printer.cc create mode 100644 impls/cc/printer.hh create mode 100644 impls/cc/reader.cc create mode 100644 impls/cc/reader.hh create mode 100644 impls/cc/step1_read_print.cc create mode 100644 impls/cc/types.hh 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/cc/Makefile b/impls/cc/Makefile index d6ce0c5edd..3c12025760 100644 --- a/impls/cc/Makefile +++ b/impls/cc/Makefile @@ -1,11 +1,12 @@ LD=$(CXX) -DEBUG=-ggdb -CXXFLAGS=-O3 -Wall $(DEBUG) $(INCPATHS) -std=c++11 -LDFLAGS=-O3 $(DEBUG) $(LIBPATHS) -L. -lreadline -lhistory +DEBUG=-g +CXXFLAGS=-O0 -Wall $(DEBUG) $(INCPATHS) -std=c++17 +LDFLAGS=-O0 $(DEBUG) $(LIBPATHS) -L. -lreadline -lhistory -LIBSOURCES=Core.cpp Environment.cpp Reader.cpp ReadLine.cpp String.cpp \ +#LIBSOURCES=Core.cpp Environment.cpp Reader.cpp ReadLine.cpp String.cpp \ Types.cpp Validation.cpp -LIBOBJS=$(LIBSOURCES:%.cpp=%.o) +LIBSOURCES=reader.cc printer.cc +LIBOBJS=$(LIBSOURCES:%.cc=%.o) MAINS=$(wildcard step*.cc) TARGETS=$(MAINS:%.cc=%) @@ -21,10 +22,10 @@ dist: mal mal: stepA_mal cp $< $@ -.deps: *.cc *.h +.deps: *.cc *.hh $(CXX) $(CXXFLAGS) -MM *.cc > .deps -$(TARGETS): %: %.o #libmal.a +$(TARGETS): %: %.o libmal.a $(LD) $^ -o $@ $(LDFLAGS) libmal.a: $(LIBOBJS) diff --git a/impls/cc/printer.cc b/impls/cc/printer.cc new file mode 100644 index 0000000000..ba2b36dd6f --- /dev/null +++ b/impls/cc/printer.cc @@ -0,0 +1,67 @@ +#include "printer.hh" +#include + +std::string escape(const std::string &token) +{ + std::ostringstream oss; + + oss << '"'; + for (unsigned int i = 1; i < token.size() - 1; ++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::unique_ptr input, bool print_readably) +{ + if (!input) + { + return ""; + } + + switch (input->type()) + { + case MalType::Type::Symbol: + { + std::string symbol = static_cast(*input); + return (print_readably && symbol[0] == '"') ? escape(symbol) : 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(std::move(l)) << ' '; + } + oss.seekp(-1, oss.end); + } + oss << list.rparen(); + return oss.str(); + } + default: + return ""; + } +} diff --git a/impls/cc/printer.hh b/impls/cc/printer.hh new file mode 100644 index 0000000000..55d07bd76f --- /dev/null +++ b/impls/cc/printer.hh @@ -0,0 +1,5 @@ +#pragma once + +#include "types.hh" + +std::string pr_str(std::unique_ptr input, bool print_readably = true); diff --git a/impls/cc/reader.cc b/impls/cc/reader.cc new file mode 100644 index 0000000000..fc39eaf220 --- /dev/null +++ b/impls/cc/reader.cc @@ -0,0 +1,169 @@ +#include "reader.hh" +#include +#include +#include + +std::unique_ptr read_from(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::unique_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) + { + std::cerr << "unbalanced"; + return nullptr; + } + + return std::make_unique(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_unique(result); + } + + return std::make_unique(token); +} + +std::unique_ptr read_list(Reader &reader, char lparen, char rparen) +{ + if (reader.empty()) + { + std::cerr << "unbalanced"; + return nullptr; + } + + auto list = std::make_unique(lparen, rparen); + auto token = read_from(reader); + + while (token && (token->type() != MalType::Type::Symbol || + static_cast(*token) != std::string(1, rparen))) + { + list->push_back(std::move(token)); + token = read_from(reader); + } + + if (!token) + { + std::cerr << "unbalanced"; + return nullptr; + } + + return list; +} + +std::unique_ptr read_macro(Reader &reader, const std::string &name) +{ + auto macro = read_from(reader); + auto result = std::make_unique('(', ')'); + result->push_back(std::make_unique(name)); + result->push_back(std::move(macro)); + return result; +} + +std::unique_ptr read_meta(Reader &reader) +{ + auto map = read_from(reader); + auto vector = read_from(reader); + auto result = std::make_unique('(', ')'); + result->push_back(std::make_unique("with-meta")); + result->push_back(std::move(vector)); + result->push_back(std::move(map)); + return result; +} + +std::unique_ptr read_from(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_list(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); + default: + return read_atom(reader); + } +} + +std::unique_ptr read_str(const std::string &input) +{ + auto reader = tokenize(input); + return read_from(reader); +} diff --git a/impls/cc/reader.hh b/impls/cc/reader.hh new file mode 100644 index 0000000000..945817c138 --- /dev/null +++ b/impls/cc/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::unique_ptr read_str(const std::string &input); diff --git a/impls/cc/step1_read_print.cc b/impls/cc/step1_read_print.cc new file mode 100644 index 0000000000..b717b3e67c --- /dev/null +++ b/impls/cc/step1_read_print.cc @@ -0,0 +1,42 @@ +#include "printer.hh" +#include "reader.hh" +#include +#include + +std::unique_ptr read(const std::string &input) +{ + return read_str(input); +} + +std::unique_ptr eval(std::unique_ptr input) +{ + return input; +} + +std::string print(std::unique_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); + auto rep_result = rep(input); + std::cout << rep_result << std::endl; + } + + return 0; +} diff --git a/impls/cc/types.hh b/impls/cc/types.hh new file mode 100644 index 0000000000..3a6dfc854b --- /dev/null +++ b/impls/cc/types.hh @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include + +class MalType +{ +public: + enum class Type + { + Int, + Symbol, + List + }; + + MalType(Type t) : type_(t) {} + virtual ~MalType() = default; + + 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_; } + +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_; } + + 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; } + +private: + std::string symbol_; +}; + +class MalList : public MalType +{ +public: + MalList(char lparen, char rparen) + : MalType(Type::List), lparen_(lparen), rparen_(rparen) {} + + const MalType &operator[](std::size_t pos) const { return *list_[pos]; } + [[nodiscard]] bool empty() const noexcept { return list_.empty(); } + std::size_t size() const noexcept { return list_.size(); } + void push_back(std::unique_ptr value) { return list_.push_back(std::move(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_; } + +private: + char lparen_; + char rparen_; + std::vector> list_; +}; From 5f2627e0ad2cbeae78fe321f6af8663d7fb5da25 Mon Sep 17 00:00:00 2001 From: Yang Le Date: Sun, 4 Jun 2023 16:38:30 +0800 Subject: [PATCH 03/21] step2 --- impls/cc/Makefile | 2 +- impls/cc/step2_eval.cc | 117 +++++++++++++++++++++++++++++++++++++++++ impls/cc/types.hh | 16 +++++- 3 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 impls/cc/step2_eval.cc diff --git a/impls/cc/Makefile b/impls/cc/Makefile index 3c12025760..f8b2ec90f5 100644 --- a/impls/cc/Makefile +++ b/impls/cc/Makefile @@ -1,5 +1,5 @@ LD=$(CXX) -DEBUG=-g +DEBUG=-ggdb CXXFLAGS=-O0 -Wall $(DEBUG) $(INCPATHS) -std=c++17 LDFLAGS=-O0 $(DEBUG) $(LIBPATHS) -L. -lreadline -lhistory diff --git a/impls/cc/step2_eval.cc b/impls/cc/step2_eval.cc new file mode 100644 index 0000000000..30f1935845 --- /dev/null +++ b/impls/cc/step2_eval.cc @@ -0,0 +1,117 @@ +#include "printer.hh" +#include "reader.hh" +#include +#include +#include +#include + +std::unique_ptr eval(std::unique_ptr input, const std::map)>> &env); + +std::map)>> repl_env = { + {"+", [](std::vector args) + { return std::accumulate(args.begin(), args.end(), 0, std::plus()); }}, + {"-", [](std::vector args) + { return args[0] - args[1]; }}, + {"*", [](std::vector args) + { return std::accumulate(args.begin(), args.end(), 1, std::multiplies()); }}, + {"/", [](std::vector args) + { return args[0] / args[1]; }}, +}; + +std::unique_ptr read(const std::string &input) +{ + return read_str(input); +} + +std::unique_ptr eval_ast(std::unique_ptr ast, const std::map)>> &env) +{ + switch (ast->type()) + { + case MalType::Type::Symbol: + { + std::string symbol = static_cast(*ast); + if (symbol[0] == '"' || symbol[0] == ':') + return ast; + + auto iter = env.find(symbol); + if (iter == env.end()) + { + std::cerr << "Invallid symbol"; + return nullptr; + } + return std::make_unique(iter->second); + } + case MalType::Type::List: + { + auto &list = static_cast(*ast); + auto new_list = std::make_unique(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; + } + default: + return ast; + } +} + +std::unique_ptr eval(std::unique_ptr input, const std::map)>> &env) +{ + if (!input) + return nullptr; + + if (input->type() != MalType::Type::List) + return eval_ast(std::move(input), env); + + auto list = static_cast(input.get()); + if (list->empty()) + return input; + + if (list->lparen() != '(') + 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(static_cast(new_list[i])); + + return std::make_unique(func(args)); +} + +std::string print(std::unique_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); + auto rep_result = rep(input); + std::cout << rep_result << std::endl; + } + + return 0; +} diff --git a/impls/cc/types.hh b/impls/cc/types.hh index 3a6dfc854b..a663b3dce7 100644 --- a/impls/cc/types.hh +++ b/impls/cc/types.hh @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -11,7 +12,8 @@ public: { Int, Symbol, - List + List, + Func }; MalType(Type t) : type_(t) {} @@ -82,3 +84,15 @@ private: char rparen_; std::vector> list_; }; + +class MalFunc : public MalType +{ +public: + MalFunc(const std::function)> &func) + : MalType(Type::Func), func_(func) {} + + int operator()(std::vector args) const { return func_(args); } + +private: + const std::function)> &func_; +}; From 8d7fa53011cdfa1aff940d4eaad268a3464a9e65 Mon Sep 17 00:00:00 2001 From: Yang Le Date: Sun, 4 Jun 2023 17:21:40 +0800 Subject: [PATCH 04/21] minor improve --- impls/cc/Makefile | 4 ++-- impls/cc/reader.cc | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/impls/cc/Makefile b/impls/cc/Makefile index f8b2ec90f5..131bdee0b8 100644 --- a/impls/cc/Makefile +++ b/impls/cc/Makefile @@ -1,7 +1,7 @@ LD=$(CXX) DEBUG=-ggdb -CXXFLAGS=-O0 -Wall $(DEBUG) $(INCPATHS) -std=c++17 -LDFLAGS=-O0 $(DEBUG) $(LIBPATHS) -L. -lreadline -lhistory +CXXFLAGS=-Og -Wall $(DEBUG) $(INCPATHS) -std=c++17 +LDFLAGS=-Og $(DEBUG) $(LIBPATHS) -L. -lreadline -lhistory #LIBSOURCES=Core.cpp Environment.cpp Reader.cpp ReadLine.cpp String.cpp \ Types.cpp Validation.cpp diff --git a/impls/cc/reader.cc b/impls/cc/reader.cc index fc39eaf220..8d0fe229b4 100644 --- a/impls/cc/reader.cc +++ b/impls/cc/reader.cc @@ -3,7 +3,7 @@ #include #include -std::unique_ptr read_from(Reader &reader); +std::unique_ptr read_form(Reader &reader); Reader tokenize(const std::string &input) { @@ -85,13 +85,13 @@ std::unique_ptr read_list(Reader &reader, char lparen, char rparen) } auto list = std::make_unique(lparen, rparen); - auto token = read_from(reader); + auto token = read_form(reader); while (token && (token->type() != MalType::Type::Symbol || static_cast(*token) != std::string(1, rparen))) { list->push_back(std::move(token)); - token = read_from(reader); + token = read_form(reader); } if (!token) @@ -105,7 +105,7 @@ std::unique_ptr read_list(Reader &reader, char lparen, char rparen) std::unique_ptr read_macro(Reader &reader, const std::string &name) { - auto macro = read_from(reader); + auto macro = read_form(reader); auto result = std::make_unique('(', ')'); result->push_back(std::make_unique(name)); result->push_back(std::move(macro)); @@ -114,8 +114,8 @@ std::unique_ptr read_macro(Reader &reader, const std::string &name) std::unique_ptr read_meta(Reader &reader) { - auto map = read_from(reader); - auto vector = read_from(reader); + auto map = read_form(reader); + auto vector = read_form(reader); auto result = std::make_unique('(', ')'); result->push_back(std::make_unique("with-meta")); result->push_back(std::move(vector)); @@ -123,7 +123,7 @@ std::unique_ptr read_meta(Reader &reader) return result; } -std::unique_ptr read_from(Reader &reader) +std::unique_ptr read_form(Reader &reader) { if (reader.empty()) { @@ -165,5 +165,5 @@ std::unique_ptr read_from(Reader &reader) std::unique_ptr read_str(const std::string &input) { auto reader = tokenize(input); - return read_from(reader); + return read_form(reader); } From 586113a7a24e3817630f6373db1c59c94a505952 Mon Sep 17 00:00:00 2001 From: Yang Le Date: Sun, 4 Jun 2023 18:08:55 +0800 Subject: [PATCH 05/21] minor improve --- impls/cc/reader.hh | 4 ++-- impls/cc/step2_eval.cc | 10 +++++----- impls/cc/types.hh | 7 +++++++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/impls/cc/reader.hh b/impls/cc/reader.hh index 945817c138..3660df3487 100644 --- a/impls/cc/reader.hh +++ b/impls/cc/reader.hh @@ -12,8 +12,8 @@ public: iter_ = std::sregex_iterator(input.begin(), input.end(), regex_); } - std::string next() { return (*iter_++).str(1); } - std::string peak() const { return (*iter_).str(1); } + std::string next() { return iter_++->str(1); } + std::string peak() const { return iter_->str(1); } bool empty() const { return iter_ == std::sregex_iterator() || diff --git a/impls/cc/step2_eval.cc b/impls/cc/step2_eval.cc index 30f1935845..96374cdd64 100644 --- a/impls/cc/step2_eval.cc +++ b/impls/cc/step2_eval.cc @@ -29,8 +29,8 @@ std::unique_ptr eval_ast(std::unique_ptr ast, const std::map(*ast); - if (symbol[0] == '"' || symbol[0] == ':') + auto symbol = static_cast(*ast); + if (symbol.is_string() || symbol.is_keyword()) return ast; auto iter = env.find(symbol); @@ -67,11 +67,11 @@ std::unique_ptr eval(std::unique_ptr input, const std::maptype() != MalType::Type::List) return eval_ast(std::move(input), env); - auto list = static_cast(input.get()); - if (list->empty()) + auto &list = static_cast(*input); + if (list.empty()) return input; - if (list->lparen() != '(') + if (!list.is_list()) return eval_ast(std::move(input), env); auto plist = eval_ast(std::move(input), env); diff --git a/impls/cc/types.hh b/impls/cc/types.hh index a663b3dce7..84ef551e71 100644 --- a/impls/cc/types.hh +++ b/impls/cc/types.hh @@ -56,6 +56,9 @@ public: bool operator!=(const std::string &str) const noexcept { return symbol_ != str; } bool operator!=(const char *str) const noexcept { return symbol_ != str; } + bool is_string() const { return symbol_[0] == '"'; } + bool is_keyword() const { return symbol_[0] == ':'; } + private: std::string symbol_; }; @@ -79,6 +82,10 @@ public: char lparen() const { return lparen_; } char rparen() const { return rparen_; } + bool is_list() const { return lparen_ == '('; } + bool is_vector() const { return lparen_ == '['; } + bool is_map() const { return lparen_ == '{'; } + private: char lparen_; char rparen_; From 41fd05cfede907cf22d347f351e82d49137d5778 Mon Sep 17 00:00:00 2001 From: Yang Le Date: Sun, 4 Jun 2023 22:51:09 +0800 Subject: [PATCH 06/21] step3(WIP) --- impls/cc/Makefile | 4 +- impls/cc/env.hh | 38 ++++++++++ impls/cc/printer.cc | 2 +- impls/cc/printer.hh | 2 +- impls/cc/reader.cc | 30 ++++---- impls/cc/reader.hh | 2 +- impls/cc/step1_read_print.cc | 6 +- impls/cc/step2_eval.cc | 20 ++--- impls/cc/step3_env.cc | 138 +++++++++++++++++++++++++++++++++++ impls/cc/types.hh | 16 ++-- 10 files changed, 217 insertions(+), 41 deletions(-) create mode 100644 impls/cc/env.hh create mode 100644 impls/cc/step3_env.cc diff --git a/impls/cc/Makefile b/impls/cc/Makefile index 131bdee0b8..8e8a011311 100644 --- a/impls/cc/Makefile +++ b/impls/cc/Makefile @@ -1,7 +1,7 @@ LD=$(CXX) DEBUG=-ggdb -CXXFLAGS=-Og -Wall $(DEBUG) $(INCPATHS) -std=c++17 -LDFLAGS=-Og $(DEBUG) $(LIBPATHS) -L. -lreadline -lhistory +CXXFLAGS=-O0 -Wall $(DEBUG) $(INCPATHS) -std=c++17 -fsanitize=address -fsanitize=leak +LDFLAGS=-O0 $(DEBUG) $(LIBPATHS) -L. -lreadline -lhistory -fsanitize=address -fsanitize=leak #LIBSOURCES=Core.cpp Environment.cpp Reader.cpp ReadLine.cpp String.cpp \ Types.cpp Validation.cpp diff --git a/impls/cc/env.hh b/impls/cc/env.hh new file mode 100644 index 0000000000..10f44bfd7a --- /dev/null +++ b/impls/cc/env.hh @@ -0,0 +1,38 @@ +#pragma once + +#include "types.hh" +#include +#include +#include +#include + +class Env +{ +public: + explicit Env(const Env *outer) + : outer_(outer) {} + + void set(const std::string &key, std::shared_ptr value) { data_.emplace(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) + { + std::cerr << "key " << key << " not found"; + return nullptr; + } + return env->data_.at(key); + } + +private: + const Env *outer_; + std::map> data_; +}; diff --git a/impls/cc/printer.cc b/impls/cc/printer.cc index ba2b36dd6f..aba61d303d 100644 --- a/impls/cc/printer.cc +++ b/impls/cc/printer.cc @@ -29,7 +29,7 @@ std::string escape(const std::string &token) return oss.str(); } -std::string pr_str(std::unique_ptr input, bool print_readably) +std::string pr_str(std::shared_ptr input, bool print_readably) { if (!input) { diff --git a/impls/cc/printer.hh b/impls/cc/printer.hh index 55d07bd76f..bf65542196 100644 --- a/impls/cc/printer.hh +++ b/impls/cc/printer.hh @@ -2,4 +2,4 @@ #include "types.hh" -std::string pr_str(std::unique_ptr input, bool print_readably = true); +std::string pr_str(std::shared_ptr input, bool print_readably = true); diff --git a/impls/cc/reader.cc b/impls/cc/reader.cc index 8d0fe229b4..0357cdf4df 100644 --- a/impls/cc/reader.cc +++ b/impls/cc/reader.cc @@ -3,7 +3,7 @@ #include #include -std::unique_ptr read_form(Reader &reader); +std::shared_ptr read_form(Reader &reader); Reader tokenize(const std::string &input) { @@ -46,7 +46,7 @@ std::string unescape(const std::string &token) return oss.str(); } -std::unique_ptr read_atom(Reader &reader) +std::shared_ptr read_atom(Reader &reader) { if (reader.empty()) { @@ -63,20 +63,20 @@ std::unique_ptr read_atom(Reader &reader) return nullptr; } - return std::make_unique(unescaped_token); + 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_unique(result); + return std::make_shared(result); } - return std::make_unique(token); + return std::make_shared(token); } -std::unique_ptr read_list(Reader &reader, char lparen, char rparen) +std::shared_ptr read_list(Reader &reader, char lparen, char rparen) { if (reader.empty()) { @@ -84,7 +84,7 @@ std::unique_ptr read_list(Reader &reader, char lparen, char rparen) return nullptr; } - auto list = std::make_unique(lparen, rparen); + auto list = std::make_shared(lparen, rparen); auto token = read_form(reader); while (token && (token->type() != MalType::Type::Symbol || @@ -103,27 +103,27 @@ std::unique_ptr read_list(Reader &reader, char lparen, char rparen) return list; } -std::unique_ptr read_macro(Reader &reader, const std::string &name) +std::shared_ptr read_macro(Reader &reader, const std::string &name) { auto macro = read_form(reader); - auto result = std::make_unique('(', ')'); - result->push_back(std::make_unique(name)); + auto result = std::make_shared('(', ')'); + result->push_back(std::make_shared(name)); result->push_back(std::move(macro)); return result; } -std::unique_ptr read_meta(Reader &reader) +std::shared_ptr read_meta(Reader &reader) { auto map = read_form(reader); auto vector = read_form(reader); - auto result = std::make_unique('(', ')'); - result->push_back(std::make_unique("with-meta")); + auto result = std::make_shared('(', ')'); + result->push_back(std::make_shared("with-meta")); result->push_back(std::move(vector)); result->push_back(std::move(map)); return result; } -std::unique_ptr read_form(Reader &reader) +std::shared_ptr read_form(Reader &reader) { if (reader.empty()) { @@ -162,7 +162,7 @@ std::unique_ptr read_form(Reader &reader) } } -std::unique_ptr read_str(const std::string &input) +std::shared_ptr read_str(const std::string &input) { auto reader = tokenize(input); return read_form(reader); diff --git a/impls/cc/reader.hh b/impls/cc/reader.hh index 3660df3487..1aea819388 100644 --- a/impls/cc/reader.hh +++ b/impls/cc/reader.hh @@ -27,4 +27,4 @@ private: std::sregex_iterator iter_; }; -std::unique_ptr read_str(const std::string &input); +std::shared_ptr read_str(const std::string &input); diff --git a/impls/cc/step1_read_print.cc b/impls/cc/step1_read_print.cc index b717b3e67c..c25c9c0775 100644 --- a/impls/cc/step1_read_print.cc +++ b/impls/cc/step1_read_print.cc @@ -3,17 +3,17 @@ #include #include -std::unique_ptr read(const std::string &input) +std::shared_ptr read(const std::string &input) { return read_str(input); } -std::unique_ptr eval(std::unique_ptr input) +std::shared_ptr eval(std::shared_ptr input) { return input; } -std::string print(std::unique_ptr input) +std::string print(std::shared_ptr input) { return pr_str(std::move(input)); } diff --git a/impls/cc/step2_eval.cc b/impls/cc/step2_eval.cc index 96374cdd64..2c91df882f 100644 --- a/impls/cc/step2_eval.cc +++ b/impls/cc/step2_eval.cc @@ -5,7 +5,7 @@ #include #include -std::unique_ptr eval(std::unique_ptr input, const std::map)>> &env); +std::shared_ptr eval(std::shared_ptr input, const std::map)>> &env); std::map)>> repl_env = { {"+", [](std::vector args) @@ -18,12 +18,12 @@ std::map)>> repl_env = { { return args[0] / args[1]; }}, }; -std::unique_ptr read(const std::string &input) +std::shared_ptr read(const std::string &input) { return read_str(input); } -std::unique_ptr eval_ast(std::unique_ptr ast, const std::map)>> &env) +std::shared_ptr eval_ast(std::shared_ptr ast, const std::map)>> &env) { switch (ast->type()) { @@ -39,12 +39,12 @@ std::unique_ptr eval_ast(std::unique_ptr ast, const std::map(iter->second); + return std::make_shared(iter->second); } case MalType::Type::List: { auto &list = static_cast(*ast); - auto new_list = std::make_unique(list.lparen(), list.rparen()); + auto new_list = std::make_shared(list.lparen(), list.rparen()); for (auto &a : list) { auto l = eval(std::move(a), env); @@ -59,7 +59,7 @@ std::unique_ptr eval_ast(std::unique_ptr ast, const std::map eval(std::unique_ptr input, const std::map)>> &env) +std::shared_ptr eval(std::shared_ptr input, const std::map)>> &env) { if (!input) return nullptr; @@ -79,16 +79,16 @@ std::unique_ptr eval(std::unique_ptr input, const std::map(*plist); - auto &func = static_cast(new_list[0]); + auto &func = static_cast(*new_list[0]); std::vector args; for (unsigned i = 1; i < new_list.size(); ++i) - args.push_back(static_cast(new_list[i])); + args.push_back(static_cast(*new_list[i])); - return std::make_unique(func(args)); + return std::make_shared(func(args)); } -std::string print(std::unique_ptr input) +std::string print(std::shared_ptr input) { return pr_str(std::move(input)); } diff --git a/impls/cc/step3_env.cc b/impls/cc/step3_env.cc new file mode 100644 index 0000000000..b4fbde9c56 --- /dev/null +++ b/impls/cc/step3_env.cc @@ -0,0 +1,138 @@ +#include "env.hh" +#include "printer.hh" +#include "reader.hh" +#include +#include +#include +#include + +std::shared_ptr eval(std::shared_ptr input, Env *env); + +std::shared_ptr read(const std::string &input) +{ + return read_str(input); +} + +std::shared_ptr eval_ast(std::shared_ptr ast, Env *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; + } + default: + return ast; + } +} + +std::shared_ptr eval(std::shared_ptr input, Env *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); + env->set(key, val); + return val; + } + + if (symbol == "let*") + { + Env new_env(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); + 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(static_cast(*new_list[i])); + + auto result = func(args); + return std::make_shared(result); +} + +std::string print(std::shared_ptr input) +{ + return pr_str(std::move(input)); +} + +std::string rep(const std::string &input, Env *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::accumulate(args.begin(), args.end(), 0, std::plus()); }); + auto sub = std::make_shared([](std::vector args) + { return args[0] - args[1]; }); + auto mul = std::make_shared([](std::vector args) + { return std::accumulate(args.begin(), args.end(), 1, std::multiplies()); }); + auto div = std::make_shared([](std::vector args) + { return args[0] / args[1]; }); + + Env repl_env(nullptr); + 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); + auto rep_result = rep(input, &repl_env); + std::cout << rep_result << std::endl; + } + + return 0; +} diff --git a/impls/cc/types.hh b/impls/cc/types.hh index 84ef551e71..d23b228b20 100644 --- a/impls/cc/types.hh +++ b/impls/cc/types.hh @@ -69,15 +69,15 @@ public: MalList(char lparen, char rparen) : MalType(Type::List), lparen_(lparen), rparen_(rparen) {} - const MalType &operator[](std::size_t pos) const { return *list_[pos]; } + const std::shared_ptr &operator[](std::size_t pos) const { return list_[pos]; } [[nodiscard]] bool empty() const noexcept { return list_.empty(); } std::size_t size() const noexcept { return list_.size(); } - void push_back(std::unique_ptr value) { return list_.push_back(std::move(value)); } + void push_back(std::shared_ptr value) { return list_.push_back(std::move(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(); } + 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_; } @@ -89,7 +89,7 @@ public: private: char lparen_; char rparen_; - std::vector> list_; + std::vector> list_; }; class MalFunc : public MalType @@ -101,5 +101,5 @@ public: int operator()(std::vector args) const { return func_(args); } private: - const std::function)> &func_; + std::function)> func_; }; From 0fef0a39cc4dcd2ac410fe195faf686e26f7048b Mon Sep 17 00:00:00 2001 From: Yang Le Date: Sat, 24 Jun 2023 16:03:19 +0800 Subject: [PATCH 07/21] step3 --- impls/cc/env.hh | 2 +- impls/cc/step3_env.cc | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/impls/cc/env.hh b/impls/cc/env.hh index 10f44bfd7a..0ab20b2b85 100644 --- a/impls/cc/env.hh +++ b/impls/cc/env.hh @@ -12,7 +12,7 @@ public: explicit Env(const Env *outer) : outer_(outer) {} - void set(const std::string &key, std::shared_ptr value) { data_.emplace(key, value); } + void set(const std::string &key, std::shared_ptr value) { data_[key] = value; } const Env *find(const std::string &key) const { diff --git a/impls/cc/step3_env.cc b/impls/cc/step3_env.cc index b4fbde9c56..acfff6a7e8 100644 --- a/impls/cc/step3_env.cc +++ b/impls/cc/step3_env.cc @@ -63,6 +63,8 @@ std::shared_ptr eval(std::shared_ptr input, Env *env) { std::string key = static_cast(*list[1]); auto val = eval(list[2], env); + if (!val) + return nullptr; env->set(key, val); return val; } @@ -75,6 +77,8 @@ std::shared_ptr eval(std::shared_ptr input, Env *env) { 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); From 02ca56579c47d748cd45d6f7a92e1e4c37fc50ea Mon Sep 17 00:00:00 2001 From: Yang Le Date: Mon, 26 Jun 2023 20:45:03 +0800 Subject: [PATCH 08/21] step4 --- impls/cc/Makefile | 2 +- impls/cc/core.cc | 181 +++++++++++++++++++++++++++++++++++++ impls/cc/core.hh | 6 ++ impls/cc/env.hh | 18 ++++ impls/cc/printer.cc | 10 +- impls/cc/reader.cc | 8 +- impls/cc/step4_if_fn_do.cc | 166 ++++++++++++++++++++++++++++++++++ impls/cc/types.hh | 38 +++++++- 8 files changed, 415 insertions(+), 14 deletions(-) create mode 100644 impls/cc/core.cc create mode 100644 impls/cc/core.hh create mode 100644 impls/cc/step4_if_fn_do.cc diff --git a/impls/cc/Makefile b/impls/cc/Makefile index 8e8a011311..40dac096c4 100644 --- a/impls/cc/Makefile +++ b/impls/cc/Makefile @@ -5,7 +5,7 @@ LDFLAGS=-O0 $(DEBUG) $(LIBPATHS) -L. -lreadline -lhistory -fsanitize=address -fs #LIBSOURCES=Core.cpp Environment.cpp Reader.cpp ReadLine.cpp String.cpp \ Types.cpp Validation.cpp -LIBSOURCES=reader.cc printer.cc +LIBSOURCES=reader.cc printer.cc core.cc LIBOBJS=$(LIBSOURCES:%.cc=%.o) MAINS=$(wildcard step*.cc) diff --git a/impls/cc/core.cc b/impls/cc/core.cc new file mode 100644 index 0000000000..a28fb21a5f --- /dev/null +++ b/impls/cc/core.cc @@ -0,0 +1,181 @@ +#include "core.hh" +#include "printer.hh" +#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::map> ns_ = { + {"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)}, +}; + +std::map> &ns() { return ns_; } diff --git a/impls/cc/core.hh b/impls/cc/core.hh new file mode 100644 index 0000000000..4c9dd8956c --- /dev/null +++ b/impls/cc/core.hh @@ -0,0 +1,6 @@ +#pragma once + +#include "types.hh" +#include + +std::map> &ns(); diff --git a/impls/cc/env.hh b/impls/cc/env.hh index 0ab20b2b85..6c6c4f87c7 100644 --- a/impls/cc/env.hh +++ b/impls/cc/env.hh @@ -12,6 +12,24 @@ public: explicit Env(const Env *outer) : outer_(outer) {} + Env(const MalList &binds, std::vector> exprs, const Env *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 diff --git a/impls/cc/printer.cc b/impls/cc/printer.cc index aba61d303d..43e4d3dcfa 100644 --- a/impls/cc/printer.cc +++ b/impls/cc/printer.cc @@ -6,7 +6,7 @@ std::string escape(const std::string &token) std::ostringstream oss; oss << '"'; - for (unsigned int i = 1; i < token.size() - 1; ++i) + for (unsigned int i = 0; i < token.size(); ++i) { switch (token[i]) { @@ -40,8 +40,8 @@ std::string pr_str(std::shared_ptr input, bool print_readably) { case MalType::Type::Symbol: { - std::string symbol = static_cast(*input); - return (print_readably && symbol[0] == '"') ? escape(symbol) : 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)); @@ -54,13 +54,15 @@ std::string pr_str(std::shared_ptr input, bool print_readably) { for (auto &l : list) { - oss << pr_str(std::move(l)) << ' '; + oss << pr_str(l, print_readably) << ' '; } oss.seekp(-1, oss.end); } oss << list.rparen(); return oss.str(); } + case MalType::Type::Func: + return "#"; default: return ""; } diff --git a/impls/cc/reader.cc b/impls/cc/reader.cc index 0357cdf4df..54a42e0c0c 100644 --- a/impls/cc/reader.cc +++ b/impls/cc/reader.cc @@ -90,7 +90,7 @@ std::shared_ptr read_list(Reader &reader, char lparen, char rparen) while (token && (token->type() != MalType::Type::Symbol || static_cast(*token) != std::string(1, rparen))) { - list->push_back(std::move(token)); + list->push_back(token); token = read_form(reader); } @@ -108,7 +108,7 @@ 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(std::move(macro)); + result->push_back(macro); return result; } @@ -118,8 +118,8 @@ std::shared_ptr read_meta(Reader &reader) auto vector = read_form(reader); auto result = std::make_shared('(', ')'); result->push_back(std::make_shared("with-meta")); - result->push_back(std::move(vector)); - result->push_back(std::move(map)); + result->push_back(vector); + result->push_back(map); return result; } diff --git a/impls/cc/step4_if_fn_do.cc b/impls/cc/step4_if_fn_do.cc new file mode 100644 index 0000000000..91036b2dac --- /dev/null +++ b/impls/cc/step4_if_fn_do.cc @@ -0,0 +1,166 @@ +#include "core.hh" +#include "env.hh" +#include "printer.hh" +#include "reader.hh" +#include +#include +#include + +std::shared_ptr eval(std::shared_ptr input, Env *env); + +std::shared_ptr read(const std::string &input) +{ + return read_str(input); +} + +std::shared_ptr eval_ast(std::shared_ptr ast, Env *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; + } + default: + return ast; + } +} + +std::shared_ptr eval(std::shared_ptr input, Env *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] && 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*") + { + Env new_env(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*") + { + auto closure = [list, env](std::vector> params) + { + // TODO: fix memory leak + auto new_env = new Env(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, Env *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[]) +{ + Env repl_env(nullptr); + + 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); + auto rep_result = rep(input, &repl_env); + std::cout << rep_result << std::endl; + } + + return 0; +} diff --git a/impls/cc/types.hh b/impls/cc/types.hh index d23b228b20..7f8824b0cf 100644 --- a/impls/cc/types.hh +++ b/impls/cc/types.hh @@ -19,6 +19,8 @@ public: MalType(Type t) : type_(t) {} virtual ~MalType() = default; + virtual bool operator==(const MalType &rhs) const noexcept = 0; + Type type() const { return type_; } private: @@ -33,6 +35,8 @@ public: operator int() const { return val_; } + virtual bool operator==(const MalType &rhs) const noexcept override { return val_ == static_cast(rhs).val_; }; + private: int val_; }; @@ -49,16 +53,20 @@ public: return *this; } - operator std::string() const { return symbol_; } + operator std::string() const { return symbol_[0] == '"' ? symbol_.substr(1, symbol_.length() - 2) : 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] == ':'; } + operator bool() const { return symbol_ != "nil" && symbol_ != "false"; } + private: std::string symbol_; }; @@ -72,7 +80,7 @@ public: const std::shared_ptr &operator[](std::size_t pos) const { return list_[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(std::move(value)); } + 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(); } @@ -86,6 +94,24 @@ public: bool is_vector() const { return lparen_ == '['; } bool is_map() const { return lparen_ == '{'; } + 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_; @@ -95,11 +121,13 @@ private: class MalFunc : public MalType { public: - MalFunc(const std::function)> &func) + MalFunc(const std::function(std::vector>)> &func) : MalType(Type::Func), func_(func) {} - int operator()(std::vector args) const { return func_(args); } + std::shared_ptr operator()(std::vector> args) const { return func_(args); } + + virtual bool operator==(const MalType &rhs) const noexcept override { return false; } private: - std::function)> func_; + std::function(std::vector>)> func_; }; From 436cee20d69c1085f868084ff6691fb3013783c7 Mon Sep 17 00:00:00 2001 From: Yang Le Date: Tue, 27 Jun 2023 22:04:48 +0800 Subject: [PATCH 09/21] fix memory leak --- impls/cc/core.cc | 49 +++++++++++++++++++------------------- impls/cc/core.hh | 2 +- impls/cc/env.hh | 6 ++--- impls/cc/step4_if_fn_do.cc | 29 +++++++++++----------- 4 files changed, 43 insertions(+), 43 deletions(-) diff --git a/impls/cc/core.cc b/impls/cc/core.cc index a28fb21a5f..1fa59c9bf0 100644 --- a/impls/cc/core.cc +++ b/impls/cc/core.cc @@ -155,27 +155,28 @@ std::shared_ptr le(std::vector> args) return result ? std::make_shared(true_) : std::make_shared(false_); } -std::map> ns_ = { - {"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)}, -}; - -std::map> &ns() { return ns_; } +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)}, + }; +} diff --git a/impls/cc/core.hh b/impls/cc/core.hh index 4c9dd8956c..abcb4196d2 100644 --- a/impls/cc/core.hh +++ b/impls/cc/core.hh @@ -3,4 +3,4 @@ #include "types.hh" #include -std::map> &ns(); +std::map> ns(); diff --git a/impls/cc/env.hh b/impls/cc/env.hh index 6c6c4f87c7..a446d8a99e 100644 --- a/impls/cc/env.hh +++ b/impls/cc/env.hh @@ -9,10 +9,10 @@ class Env { public: - explicit Env(const Env *outer) + explicit Env(std::shared_ptr outer) : outer_(outer) {} - Env(const MalList &binds, std::vector> exprs, const Env *outer) + Env(const MalList &binds, std::vector> exprs, std::shared_ptr outer) : outer_(outer) { for (unsigned i = 0; i < binds.size(); ++i) @@ -51,6 +51,6 @@ public: } private: - const Env *outer_; + std::shared_ptr outer_; std::map> data_; }; diff --git a/impls/cc/step4_if_fn_do.cc b/impls/cc/step4_if_fn_do.cc index 91036b2dac..a02078e43f 100644 --- a/impls/cc/step4_if_fn_do.cc +++ b/impls/cc/step4_if_fn_do.cc @@ -6,14 +6,14 @@ #include #include -std::shared_ptr eval(std::shared_ptr input, Env *env); +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, Env *env) +std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr env) { switch (ast->type()) { @@ -43,7 +43,7 @@ std::shared_ptr eval_ast(std::shared_ptr ast, Env *env) } } -std::shared_ptr eval(std::shared_ptr input, Env *env) +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env) { if (!input) return nullptr; @@ -58,7 +58,7 @@ std::shared_ptr eval(std::shared_ptr input, Env *env) if (!list.is_list()) return eval_ast(input, env); - if (list[0] && list[0]->type() == MalType::Type::Symbol) + if (list[0]->type() == MalType::Type::Symbol) { std::string symbol = static_cast(*list[0]); if (symbol == "def!") @@ -73,17 +73,17 @@ std::shared_ptr eval(std::shared_ptr input, Env *env) if (symbol == "let*") { - Env new_env(env); + 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); + auto val = eval(bindings[i + 1], new_env); if (!val) return nullptr; - new_env.set(key, val); + new_env->set(key, val); } - return eval(list[2], &new_env); + return eval(list[2], new_env); } if (symbol == "do") @@ -109,8 +109,7 @@ std::shared_ptr eval(std::shared_ptr input, Env *env) { auto closure = [list, env](std::vector> params) { - // TODO: fix memory leak - auto new_env = new Env(static_cast(*list[1]), params, env); + auto new_env = std::make_shared(static_cast(*list[1]), params, env); return eval(list[2], new_env); }; return std::make_shared(closure); @@ -136,7 +135,7 @@ std::string print(std::shared_ptr input) return pr_str(input); } -std::string rep(const std::string &input, Env *env) +std::string rep(const std::string &input, std::shared_ptr env) { auto read_result = read(input); auto eval_result = eval(read_result, env); @@ -146,19 +145,19 @@ std::string rep(const std::string &input, Env *env) int main(int argc, char *argv[]) { - Env repl_env(nullptr); + auto repl_env = std::make_shared(nullptr); for (auto &[key, value] : ns()) - repl_env.set(key, value); + repl_env->set(key, value); - rep("(def! not (fn* (a) (if a false true)))", &repl_env); + 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); - auto rep_result = rep(input, &repl_env); + auto rep_result = rep(input, repl_env); std::cout << rep_result << std::endl; } From 6824e331dc9c50e9357c2fd2f9e8033a2ae89c33 Mon Sep 17 00:00:00 2001 From: Yang Le Date: Tue, 4 Jul 2023 13:27:38 +0800 Subject: [PATCH 10/21] step5 --- impls/cc/step5_tco.cc | 187 ++++++++++++++++++++++++++++++++++++++++++ impls/cc/types.hh | 14 +++- 2 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 impls/cc/step5_tco.cc diff --git a/impls/cc/step5_tco.cc b/impls/cc/step5_tco.cc new file mode 100644 index 0000000000..6869222b35 --- /dev/null +++ b/impls/cc/step5_tco.cc @@ -0,0 +1,187 @@ +#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; + } + 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*") + { + 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(nullptr); + + 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); + auto rep_result = rep(input, repl_env); + std::cout << rep_result << std::endl; + } + + return 0; +} diff --git a/impls/cc/types.hh b/impls/cc/types.hh index 7f8824b0cf..2fe53d3a8b 100644 --- a/impls/cc/types.hh +++ b/impls/cc/types.hh @@ -5,6 +5,8 @@ #include #include +class Env; + class MalType { public: @@ -121,13 +123,21 @@ private: 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 = nullptr, std::shared_ptr params = nullptr, std::shared_ptr env = nullptr) + : MalType(Type::Func), func_(func), ast_(ast), params_(params), env_(env) {} 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 ast_ && params_ && env_; } + std::shared_ptr ast() const { return ast_; } + std::shared_ptr params() const { return params_; } + std::shared_ptr env() const { return env_; } + private: std::function(std::vector>)> func_; + std::shared_ptr ast_; + std::shared_ptr params_; + std::shared_ptr env_; }; From 9d6ad9d76a7e5022851b623112fd9d6d9a13b199 Mon Sep 17 00:00:00 2001 From: Yang Le Date: Tue, 4 Jul 2023 20:47:16 +0800 Subject: [PATCH 11/21] step6 but memory leak --- impls/cc/core.cc | 57 ++++++++++++ impls/cc/printer.cc | 5 + impls/cc/reader.cc | 3 + impls/cc/step6_file.cc | 205 +++++++++++++++++++++++++++++++++++++++++ impls/cc/types.hh | 18 +++- 5 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 impls/cc/step6_file.cc diff --git a/impls/cc/core.cc b/impls/cc/core.cc index 1fa59c9bf0..7be5675f7d 100644 --- a/impls/cc/core.cc +++ b/impls/cc/core.cc @@ -1,5 +1,7 @@ #include "core.hh" #include "printer.hh" +#include "reader.hh" +#include #include #include #include @@ -155,6 +157,54 @@ std::shared_ptr le(std::vector> args) 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::map> ns() { return { @@ -178,5 +228,12 @@ std::map> ns() {"<=", 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)}, }; } diff --git a/impls/cc/printer.cc b/impls/cc/printer.cc index 43e4d3dcfa..0530938bde 100644 --- a/impls/cc/printer.cc +++ b/impls/cc/printer.cc @@ -63,6 +63,11 @@ std::string pr_str(std::shared_ptr input, bool print_readably) } 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/cc/reader.cc b/impls/cc/reader.cc index 54a42e0c0c..436ff488d6 100644 --- a/impls/cc/reader.cc +++ b/impls/cc/reader.cc @@ -157,6 +157,9 @@ std::shared_ptr read_form(Reader &reader) case '^': reader.next(); return read_meta(reader); + case ';': + reader.next(); + return read_form(reader); default: return read_atom(reader); } diff --git a/impls/cc/step6_file.cc b/impls/cc/step6_file.cc new file mode 100644 index 0000000000..d9dacde25e --- /dev/null +++ b/impls/cc/step6_file.cc @@ -0,0 +1,205 @@ +#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; + } + 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*") + { + 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(nullptr); + + for (auto &[key, value] : ns()) + repl_env->set(key, value); + + auto closure = [repl_env](std::vector> ast) + { + return eval(ast[0], repl_env); + }; + 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); + auto rep_result = rep(input, repl_env); + std::cout << rep_result << std::endl; + } + + return 0; +} diff --git a/impls/cc/types.hh b/impls/cc/types.hh index 2fe53d3a8b..6eb07d4516 100644 --- a/impls/cc/types.hh +++ b/impls/cc/types.hh @@ -15,7 +15,8 @@ public: Int, Symbol, List, - Func + Func, + Atom }; MalType(Type t) : type_(t) {} @@ -141,3 +142,18 @@ private: std::shared_ptr params_; std::shared_ptr env_; }; + +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_; +}; From 993b7a7a1151f37fcf27ba974e3de887cf58a7c4 Mon Sep 17 00:00:00 2001 From: Yang Le Date: Thu, 6 Jul 2023 20:15:12 +0800 Subject: [PATCH 12/21] step7 --- impls/cc/core.cc | 34 +++++ impls/cc/step7_quote.cc | 278 ++++++++++++++++++++++++++++++++++++++++ impls/cc/types.hh | 22 +++- 3 files changed, 331 insertions(+), 3 deletions(-) create mode 100644 impls/cc/step7_quote.cc diff --git a/impls/cc/core.cc b/impls/cc/core.cc index 7be5675f7d..2de3495145 100644 --- a/impls/cc/core.cc +++ b/impls/cc/core.cc @@ -205,6 +205,37 @@ std::shared_ptr swap(std::vector> args) 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::map> ns() { return { @@ -235,5 +266,8 @@ std::map> ns() {"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)}, }; } diff --git a/impls/cc/step7_quote.cc b/impls/cc/step7_quote.cc new file mode 100644 index 0000000000..3077e40a35 --- /dev/null +++ b/impls/cc/step7_quote.cc @@ -0,0 +1,278 @@ +#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; + + 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; + } + 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*") + { + 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(nullptr); + + for (auto &[key, value] : ns()) + repl_env->set(key, value); + + auto closure = [repl_env](std::vector> ast) + { + return eval(ast[0], repl_env); + }; + 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); + auto rep_result = rep(input, repl_env); + std::cout << rep_result << std::endl; + } + + return 0; +} diff --git a/impls/cc/types.hh b/impls/cc/types.hh index 6eb07d4516..9a04cf5ba6 100644 --- a/impls/cc/types.hh +++ b/impls/cc/types.hh @@ -93,9 +93,25 @@ public: char lparen() const { return lparen_; } char rparen() const { return rparen_; } - bool is_list() const { return lparen_ == '('; } - bool is_vector() const { return lparen_ == '['; } - bool is_map() const { return lparen_ == '{'; } + bool is_list() const { return lparen_ == '(' && rparen_ == ')'; } + bool is_vector() const { return lparen_ == '[' && rparen_ == ']'; } + bool is_map() const { return lparen_ == '{' && rparen_ == '}'; } + + std::shared_ptr to_list() const + { + auto ret = *this; + ret.lparen_ = '('; + ret.rparen_ = ')'; + return std::make_shared(ret); + } + + std::shared_ptr to_vector() const + { + auto ret = *this; + ret.lparen_ = '['; + ret.rparen_ = ']'; + return std::make_shared(ret); + } virtual bool operator==(const MalType &rhs) const noexcept override { From b2f53f467487273a95ab811720da6e05444ae176 Mon Sep 17 00:00:00 2001 From: Yang Le Date: Thu, 6 Jul 2023 22:51:04 +0800 Subject: [PATCH 13/21] step8 --- impls/cc/core.cc | 43 +++++ impls/cc/step8_macros.cc | 346 +++++++++++++++++++++++++++++++++++++++ impls/cc/types.hh | 20 ++- 3 files changed, 400 insertions(+), 9 deletions(-) create mode 100644 impls/cc/step8_macros.cc diff --git a/impls/cc/core.cc b/impls/cc/core.cc index 2de3495145..f4ff0d0e75 100644 --- a/impls/cc/core.cc +++ b/impls/cc/core.cc @@ -236,6 +236,46 @@ std::shared_ptr vec(std::vector> args) 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; +} + std::map> ns() { return { @@ -269,5 +309,8 @@ std::map> ns() {"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)}, }; } diff --git a/impls/cc/step8_macros.cc b/impls/cc/step8_macros.cc new file mode 100644 index 0000000000..ecd58ac264 --- /dev/null +++ b/impls/cc/step8_macros.cc @@ -0,0 +1,346 @@ +#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; + + 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; + } + 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*") + { + 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(nullptr); + + for (auto &[key, value] : ns()) + repl_env->set(key, value); + + auto closure = [repl_env](std::vector> ast) + { + return eval(ast[0], repl_env); + }; + 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/cc/types.hh b/impls/cc/types.hh index 9a04cf5ba6..934fab854a 100644 --- a/impls/cc/types.hh +++ b/impls/cc/types.hh @@ -80,7 +80,7 @@ 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_[pos]; } + 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); } @@ -99,18 +99,18 @@ public: std::shared_ptr to_list() const { - auto ret = *this; - ret.lparen_ = '('; - ret.rparen_ = ')'; - return std::make_shared(ret); + auto ret = std::make_shared(*this); + ret->lparen_ = '('; + ret->rparen_ = ')'; + return ret; } std::shared_ptr to_vector() const { - auto ret = *this; - ret.lparen_ = '['; - ret.rparen_ = ']'; - return std::make_shared(ret); + auto ret = std::make_shared(*this); + ret->lparen_ = '['; + ret->rparen_ = ']'; + return ret; } virtual bool operator==(const MalType &rhs) const noexcept override @@ -152,6 +152,8 @@ public: std::shared_ptr params() const { return params_; } std::shared_ptr env() const { return env_; } + bool is_macro = false; + private: std::function(std::vector>)> func_; std::shared_ptr ast_; From befa4d53f414b3d9bace0fbcaf52ec3dcb15dacf Mon Sep 17 00:00:00 2001 From: Yang Le Date: Sat, 8 Jul 2023 22:50:50 +0800 Subject: [PATCH 14/21] fix errors and memory leaks --- impls/cc/Makefile | 2 -- impls/cc/env.hh | 4 ++- impls/cc/step2_eval.cc | 34 ++++++++++++----------- impls/cc/step3_env.cc | 55 +++++++++++++++++++------------------- impls/cc/step4_if_fn_do.cc | 13 ++++++++- impls/cc/step5_tco.cc | 13 ++++++++- impls/cc/step6_file.cc | 18 ++++++++++--- impls/cc/step7_quote.cc | 18 ++++++++++--- impls/cc/step8_macros.cc | 18 ++++++++++--- impls/cc/types.hh | 14 ++++++---- 10 files changed, 127 insertions(+), 62 deletions(-) diff --git a/impls/cc/Makefile b/impls/cc/Makefile index 40dac096c4..94d3414d27 100644 --- a/impls/cc/Makefile +++ b/impls/cc/Makefile @@ -3,8 +3,6 @@ DEBUG=-ggdb CXXFLAGS=-O0 -Wall $(DEBUG) $(INCPATHS) -std=c++17 -fsanitize=address -fsanitize=leak LDFLAGS=-O0 $(DEBUG) $(LIBPATHS) -L. -lreadline -lhistory -fsanitize=address -fsanitize=leak -#LIBSOURCES=Core.cpp Environment.cpp Reader.cpp ReadLine.cpp String.cpp \ - Types.cpp Validation.cpp LIBSOURCES=reader.cc printer.cc core.cc LIBOBJS=$(LIBSOURCES:%.cc=%.o) diff --git a/impls/cc/env.hh b/impls/cc/env.hh index a446d8a99e..be51ae99ca 100644 --- a/impls/cc/env.hh +++ b/impls/cc/env.hh @@ -9,7 +9,7 @@ class Env { public: - explicit Env(std::shared_ptr outer) + explicit Env(std::shared_ptr outer = nullptr) : outer_(outer) {} Env(const MalList &binds, std::vector> exprs, std::shared_ptr outer) @@ -50,6 +50,8 @@ public: return env->data_.at(key); } + bool is_root() const { return !outer_; } + private: std::shared_ptr outer_; std::map> data_; diff --git a/impls/cc/step2_eval.cc b/impls/cc/step2_eval.cc index 2c91df882f..e98ffb93ca 100644 --- a/impls/cc/step2_eval.cc +++ b/impls/cc/step2_eval.cc @@ -5,17 +5,19 @@ #include #include -std::shared_ptr eval(std::shared_ptr input, const std::map)>> &env); - -std::map)>> repl_env = { - {"+", [](std::vector args) - { return std::accumulate(args.begin(), args.end(), 0, std::plus()); }}, - {"-", [](std::vector args) - { return args[0] - args[1]; }}, - {"*", [](std::vector args) - { return std::accumulate(args.begin(), args.end(), 1, std::multiplies()); }}, - {"/", [](std::vector args) - { return args[0] / args[1]; }}, +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) @@ -23,7 +25,7 @@ std::shared_ptr read(const std::string &input) return read_str(input); } -std::shared_ptr eval_ast(std::shared_ptr ast, const std::map)>> &env) +std::shared_ptr eval_ast(std::shared_ptr ast, const std::map(std::vector>)>> &env) { switch (ast->type()) { @@ -59,7 +61,7 @@ std::shared_ptr eval_ast(std::shared_ptr ast, const std::map eval(std::shared_ptr input, const std::map)>> &env) +std::shared_ptr eval(std::shared_ptr input, const std::map(std::vector>)>> &env) { if (!input) return nullptr; @@ -81,11 +83,11 @@ std::shared_ptr eval(std::shared_ptr input, const std::map(*plist); auto &func = static_cast(*new_list[0]); - std::vector args; + std::vector> args; for (unsigned i = 1; i < new_list.size(); ++i) - args.push_back(static_cast(*new_list[i])); + args.push_back(new_list[i]); - return std::make_shared(func(args)); + return func(args); } std::string print(std::shared_ptr input) diff --git a/impls/cc/step3_env.cc b/impls/cc/step3_env.cc index acfff6a7e8..17e064f518 100644 --- a/impls/cc/step3_env.cc +++ b/impls/cc/step3_env.cc @@ -6,14 +6,14 @@ #include #include -std::shared_ptr eval(std::shared_ptr input, Env *env); +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, Env *env) +std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr env) { switch (ast->type()) { @@ -43,7 +43,7 @@ std::shared_ptr eval_ast(std::shared_ptr ast, Env *env) } } -std::shared_ptr eval(std::shared_ptr input, Env *env) +std::shared_ptr eval(std::shared_ptr input, std::shared_ptr env) { if (!input) return nullptr; @@ -71,17 +71,17 @@ std::shared_ptr eval(std::shared_ptr input, Env *env) if (symbol == "let*") { - Env new_env(env); + 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); + auto val = eval(bindings[i + 1], new_env); if (!val) return nullptr; - new_env.set(key, val); + new_env->set(key, val); } - return eval(list[2], &new_env); + return eval(list[2], new_env); } auto plist = eval_ast(std::move(input), env); @@ -91,12 +91,11 @@ std::shared_ptr eval(std::shared_ptr input, Env *env) auto &new_list = static_cast(*plist); auto &func = static_cast(*new_list[0]); - std::vector args; + std::vector> args; for (unsigned i = 1; i < new_list.size(); ++i) - args.push_back(static_cast(*new_list[i])); + args.push_back(new_list[i]); - auto result = func(args); - return std::make_shared(result); + return func(args); } std::string print(std::shared_ptr input) @@ -104,7 +103,7 @@ std::string print(std::shared_ptr input) return pr_str(std::move(input)); } -std::string rep(const std::string &input, Env *env) +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); @@ -114,27 +113,29 @@ std::string rep(const std::string &input, Env *env) int main(int argc, char *argv[]) { - auto add = std::make_shared([](std::vector args) - { return std::accumulate(args.begin(), args.end(), 0, std::plus()); }); - auto sub = std::make_shared([](std::vector args) - { return args[0] - args[1]; }); - auto mul = std::make_shared([](std::vector args) - { return std::accumulate(args.begin(), args.end(), 1, std::multiplies()); }); - auto div = std::make_shared([](std::vector args) - { return args[0] / args[1]; }); - - Env repl_env(nullptr); - repl_env.set("+", add); - repl_env.set("-", sub); - repl_env.set("*", mul); - repl_env.set("/", div); + 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); - auto rep_result = rep(input, &repl_env); + auto rep_result = rep(input, repl_env); std::cout << rep_result << std::endl; } diff --git a/impls/cc/step4_if_fn_do.cc b/impls/cc/step4_if_fn_do.cc index a02078e43f..d10417af89 100644 --- a/impls/cc/step4_if_fn_do.cc +++ b/impls/cc/step4_if_fn_do.cc @@ -107,6 +107,17 @@ std::shared_ptr eval(std::shared_ptr input, std::shared_ptris_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); @@ -145,7 +156,7 @@ std::string rep(const std::string &input, std::shared_ptr env) int main(int argc, char *argv[]) { - auto repl_env = std::make_shared(nullptr); + auto repl_env = std::make_shared(); for (auto &[key, value] : ns()) repl_env->set(key, value); diff --git a/impls/cc/step5_tco.cc b/impls/cc/step5_tco.cc index 6869222b35..482921828c 100644 --- a/impls/cc/step5_tco.cc +++ b/impls/cc/step5_tco.cc @@ -121,6 +121,17 @@ std::shared_ptr eval(std::shared_ptr input, std::shared_ptris_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); @@ -167,7 +178,7 @@ std::string rep(const std::string &input, std::shared_ptr env) int main(int argc, char *argv[]) { - auto repl_env = std::make_shared(nullptr); + auto repl_env = std::make_shared(); for (auto &[key, value] : ns()) repl_env->set(key, value); diff --git a/impls/cc/step6_file.cc b/impls/cc/step6_file.cc index d9dacde25e..5301cb04f0 100644 --- a/impls/cc/step6_file.cc +++ b/impls/cc/step6_file.cc @@ -121,6 +121,17 @@ std::shared_ptr eval(std::shared_ptr input, std::shared_ptris_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); @@ -167,14 +178,15 @@ std::string rep(const std::string &input, std::shared_ptr env) int main(int argc, char *argv[]) { - auto repl_env = std::make_shared(nullptr); + auto repl_env = std::make_shared(); for (auto &[key, value] : ns()) repl_env->set(key, value); - auto closure = [repl_env](std::vector> ast) + std::weak_ptr weak_env = repl_env; + auto closure = [weak_env](std::vector> ast) { - return eval(ast[0], repl_env); + return eval(ast[0], weak_env.lock()); }; repl_env->set("eval", std::make_shared(closure)); diff --git a/impls/cc/step7_quote.cc b/impls/cc/step7_quote.cc index 3077e40a35..e3c3ff8b7e 100644 --- a/impls/cc/step7_quote.cc +++ b/impls/cc/step7_quote.cc @@ -182,6 +182,17 @@ std::shared_ptr eval(std::shared_ptr input, std::shared_ptris_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); @@ -240,14 +251,15 @@ std::string rep(const std::string &input, std::shared_ptr env) int main(int argc, char *argv[]) { - auto repl_env = std::make_shared(nullptr); + auto repl_env = std::make_shared(); for (auto &[key, value] : ns()) repl_env->set(key, value); - auto closure = [repl_env](std::vector> ast) + std::weak_ptr weak_env = repl_env; + auto closure = [weak_env](std::vector> ast) { - return eval(ast[0], repl_env); + return eval(ast[0], weak_env.lock()); }; repl_env->set("eval", std::make_shared(closure)); diff --git a/impls/cc/step8_macros.cc b/impls/cc/step8_macros.cc index ecd58ac264..18d9a93bb4 100644 --- a/impls/cc/step8_macros.cc +++ b/impls/cc/step8_macros.cc @@ -239,6 +239,17 @@ std::shared_ptr eval(std::shared_ptr input, std::shared_ptris_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); @@ -300,14 +311,15 @@ std::string rep(const std::string &input, std::shared_ptr env) int main(int argc, char *argv[]) { - auto repl_env = std::make_shared(nullptr); + auto repl_env = std::make_shared(); for (auto &[key, value] : ns()) repl_env->set(key, value); - auto closure = [repl_env](std::vector> ast) + std::weak_ptr weak_env = repl_env; + auto closure = [weak_env](std::vector> ast) { - return eval(ast[0], repl_env); + return eval(ast[0], weak_env.lock()); }; repl_env->set("eval", std::make_shared(closure)); diff --git a/impls/cc/types.hh b/impls/cc/types.hh index 934fab854a..c6b2e0ed6f 100644 --- a/impls/cc/types.hh +++ b/impls/cc/types.hh @@ -140,17 +140,20 @@ private: class MalFunc : public MalType { public: - MalFunc(const std::function(std::vector>)> &func, std::shared_ptr ast = nullptr, std::shared_ptr params = nullptr, std::shared_ptr env = nullptr) - : MalType(Type::Func), func_(func), ast_(ast), params_(params), env_(env) {} + 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 ast_ && params_ && env_; } + 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_; } + std::shared_ptr env() const { return env_.lock(); } bool is_macro = false; @@ -158,7 +161,8 @@ private: std::function(std::vector>)> func_; std::shared_ptr ast_; std::shared_ptr params_; - std::shared_ptr env_; + std::weak_ptr env_; + bool is_fn_ = false; }; class MalAtom : public MalType From 171834b65af023d37a663f007bc2d13896c857be Mon Sep 17 00:00:00 2001 From: Yang Le Date: Sun, 9 Jul 2023 20:24:00 +0800 Subject: [PATCH 15/21] step9 --- impls/cc/core.cc | 226 +++++++++++++++++++++++ impls/cc/env.hh | 5 +- impls/cc/printer.cc | 16 +- impls/cc/reader.cc | 32 +++- impls/cc/step9_try.cc | 406 ++++++++++++++++++++++++++++++++++++++++++ impls/cc/types.hh | 58 +++++- 6 files changed, 733 insertions(+), 10 deletions(-) create mode 100644 impls/cc/step9_try.cc diff --git a/impls/cc/core.cc b/impls/cc/core.cc index f4ff0d0e75..fe12480ccc 100644 --- a/impls/cc/core.cc +++ b/impls/cc/core.cc @@ -276,6 +276,211 @@ std::shared_ptr rest(std::vector> args) 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(); ++i) + { + if (args[i]->type() == MalType::Type::List) + for (auto item : static_cast(*args[i])) + list.push_back(item); + else + list.push_back(args[i]); + } + + 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() ? 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::map> ns() { return { @@ -312,5 +517,26 @@ std::map> ns() {"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)}, }; } diff --git a/impls/cc/env.hh b/impls/cc/env.hh index be51ae99ca..dcbafcaf81 100644 --- a/impls/cc/env.hh +++ b/impls/cc/env.hh @@ -43,10 +43,7 @@ public: { auto env = find(key); if (!env) - { - std::cerr << "key " << key << " not found"; - return nullptr; - } + throw std::runtime_error('\'' + key + "' not found"); return env->data_.at(key); } diff --git a/impls/cc/printer.cc b/impls/cc/printer.cc index 0530938bde..1dd29c2880 100644 --- a/impls/cc/printer.cc +++ b/impls/cc/printer.cc @@ -53,14 +53,26 @@ std::string pr_str(std::shared_ptr input, bool print_readably) 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: diff --git a/impls/cc/reader.cc b/impls/cc/reader.cc index 436ff488d6..9ed88d0af2 100644 --- a/impls/cc/reader.cc +++ b/impls/cc/reader.cc @@ -103,6 +103,36 @@ std::shared_ptr read_list(Reader &reader, char lparen, char rparen) return list; } +std::shared_ptr read_map(Reader &reader) +{ + if (reader.empty()) + { + std::cerr << "unbalanced"; + return nullptr; + } + + 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) + { + std::cerr << "unbalanced"; + return nullptr; + } + + return map; +} + std::shared_ptr read_macro(Reader &reader, const std::string &name) { auto macro = read_form(reader); @@ -141,7 +171,7 @@ std::shared_ptr read_form(Reader &reader) return read_list(reader, '[', ']'); case '{': reader.next(); - return read_list(reader, '{', '}'); + return read_map(reader); case '\'': reader.next(); return read_macro(reader, "quote"); diff --git a/impls/cc/step9_try.cc b/impls/cc/step9_try.cc new file mode 100644 index 0000000000..4480f6a3b5 --- /dev/null +++ b/impls/cc/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/cc/types.hh b/impls/cc/types.hh index c6b2e0ed6f..e1cf5761b8 100644 --- a/impls/cc/types.hh +++ b/impls/cc/types.hh @@ -3,6 +3,7 @@ #include #include #include +#include #include class Env; @@ -15,6 +16,7 @@ public: Int, Symbol, List, + Map, Func, Atom }; @@ -70,6 +72,14 @@ public: operator bool() const { return symbol_ != "nil" && symbol_ != "false"; } + struct Hash + { + std::size_t operator()(const MalSymbol &k) const + { + return std::hash()(k.symbol_); + } + }; + private: std::string symbol_; }; @@ -95,7 +105,6 @@ public: bool is_list() const { return lparen_ == '(' && rparen_ == ')'; } bool is_vector() const { return lparen_ == '[' && rparen_ == ']'; } - bool is_map() const { return lparen_ == '{' && rparen_ == '}'; } std::shared_ptr to_list() const { @@ -115,7 +124,7 @@ public: virtual bool operator==(const MalType &rhs) const noexcept override { - auto rhs_list = static_cast(rhs); + auto &rhs_list = static_cast(rhs); if (rhs_list.size() != list_.size()) return false; @@ -129,7 +138,7 @@ public: } return true; - }; + } private: char lparen_; @@ -137,6 +146,49 @@ private: std::vector> list_; }; +class MalMap : public MalType +{ +public: + 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); } + + std::unordered_map, MalSymbol::Hash>::iterator begin() noexcept { return map_.begin(); } + std::unordered_map, MalSymbol::Hash>::const_iterator begin() const noexcept { return map_.begin(); } + std::unordered_map, MalSymbol::Hash>::iterator end() noexcept { return map_.end(); } + std::unordered_map, MalSymbol::Hash>::const_iterator end() const noexcept { return map_.end(); } + + std::unordered_map, MalSymbol::Hash>::const_iterator find(const MalSymbol &key) const { return map_.find(key); } + + 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: + std::unordered_map, MalSymbol::Hash> map_; +}; + class MalFunc : public MalType { public: From ddd6645baa617427b16d1d8849521f07fe40df4f Mon Sep 17 00:00:00 2001 From: Yang Le Date: Sun, 9 Jul 2023 20:25:05 +0800 Subject: [PATCH 16/21] update for hash-map --- impls/cc/step2_eval.cc | 13 +++++++++++++ impls/cc/step3_env.cc | 24 ++++++++++++++++++++++-- impls/cc/step4_if_fn_do.cc | 24 ++++++++++++++++++++++-- impls/cc/step5_tco.cc | 13 +++++++++++++ impls/cc/step6_file.cc | 13 +++++++++++++ impls/cc/step7_quote.cc | 18 ++++++++++++++++-- impls/cc/step8_macros.cc | 18 ++++++++++++++++-- 7 files changed, 115 insertions(+), 8 deletions(-) diff --git a/impls/cc/step2_eval.cc b/impls/cc/step2_eval.cc index e98ffb93ca..69f06f65b3 100644 --- a/impls/cc/step2_eval.cc +++ b/impls/cc/step2_eval.cc @@ -56,6 +56,19 @@ std::shared_ptr eval_ast(std::shared_ptr ast, const std::map(*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; } diff --git a/impls/cc/step3_env.cc b/impls/cc/step3_env.cc index 17e064f518..e4510458aa 100644 --- a/impls/cc/step3_env.cc +++ b/impls/cc/step3_env.cc @@ -38,6 +38,19 @@ std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr< } 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; } @@ -135,8 +148,15 @@ int main(int argc, char *argv[]) std::string input; std::cout << "user> "; std::getline(std::cin, input); - auto rep_result = rep(input, repl_env); - std::cout << rep_result << std::endl; + 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/cc/step4_if_fn_do.cc b/impls/cc/step4_if_fn_do.cc index d10417af89..ada7715e45 100644 --- a/impls/cc/step4_if_fn_do.cc +++ b/impls/cc/step4_if_fn_do.cc @@ -38,6 +38,19 @@ std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr< } 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; } @@ -168,8 +181,15 @@ int main(int argc, char *argv[]) std::string input; std::cout << "user> "; std::getline(std::cin, input); - auto rep_result = rep(input, repl_env); - std::cout << rep_result << std::endl; + 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/cc/step5_tco.cc b/impls/cc/step5_tco.cc index 482921828c..5e9588329e 100644 --- a/impls/cc/step5_tco.cc +++ b/impls/cc/step5_tco.cc @@ -38,6 +38,19 @@ std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr< } 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; } diff --git a/impls/cc/step6_file.cc b/impls/cc/step6_file.cc index 5301cb04f0..075a3fa3ba 100644 --- a/impls/cc/step6_file.cc +++ b/impls/cc/step6_file.cc @@ -38,6 +38,19 @@ std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr< } 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; } diff --git a/impls/cc/step7_quote.cc b/impls/cc/step7_quote.cc index e3c3ff8b7e..7fd987ab33 100644 --- a/impls/cc/step7_quote.cc +++ b/impls/cc/step7_quote.cc @@ -64,11 +64,12 @@ std::shared_ptr quasiquote(std::shared_ptr ast, bool handle_ve 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; } @@ -99,6 +100,19 @@ std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr< } 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; } diff --git a/impls/cc/step8_macros.cc b/impls/cc/step8_macros.cc index 18d9a93bb4..b9796e0dae 100644 --- a/impls/cc/step8_macros.cc +++ b/impls/cc/step8_macros.cc @@ -64,11 +64,12 @@ std::shared_ptr quasiquote(std::shared_ptr ast, bool handle_ve 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; } @@ -140,6 +141,19 @@ std::shared_ptr eval_ast(std::shared_ptr ast, std::shared_ptr< } 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; } From 12453ee0638c46cf1d23db9447c27c62069ca7d3 Mon Sep 17 00:00:00 2001 From: Yang Le Date: Thu, 13 Jul 2023 17:54:08 +0800 Subject: [PATCH 17/21] stepA --- impls/cc/Makefile | 6 +- impls/cc/core.cc | 153 ++++++++++++- impls/cc/reader.cc | 33 +-- impls/cc/step0_repl.cc | 11 +- impls/cc/step1_read_print.cc | 11 +- impls/cc/step2_eval.cc | 11 +- impls/cc/step5_tco.cc | 11 +- impls/cc/step6_file.cc | 11 +- impls/cc/step7_quote.cc | 11 +- impls/cc/stepA_mal.cc | 410 +++++++++++++++++++++++++++++++++++ impls/cc/types.hh | 13 ++ 11 files changed, 641 insertions(+), 40 deletions(-) create mode 100644 impls/cc/stepA_mal.cc diff --git a/impls/cc/Makefile b/impls/cc/Makefile index 94d3414d27..baa0e647e7 100644 --- a/impls/cc/Makefile +++ b/impls/cc/Makefile @@ -1,7 +1,7 @@ LD=$(CXX) -DEBUG=-ggdb -CXXFLAGS=-O0 -Wall $(DEBUG) $(INCPATHS) -std=c++17 -fsanitize=address -fsanitize=leak -LDFLAGS=-O0 $(DEBUG) $(LIBPATHS) -L. -lreadline -lhistory -fsanitize=address -fsanitize=leak +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) diff --git a/impls/cc/core.cc b/impls/cc/core.cc index fe12480ccc..c5dd2ddea5 100644 --- a/impls/cc/core.cc +++ b/impls/cc/core.cc @@ -1,6 +1,7 @@ #include "core.hh" #include "printer.hh" #include "reader.hh" +#include #include #include #include @@ -342,7 +343,7 @@ std::shared_ptr is_symbol(std::vector> args) auto &symbol = static_cast(*args[0]); - return !symbol.is_keyword() && !symbol.is_string() ? std::make_shared(true_) : std::make_shared(false_); + 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) @@ -481,6 +482,146 @@ std::shared_ptr vals(std::vector> args) 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 { @@ -538,5 +679,15 @@ std::map> ns() {"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/cc/reader.cc b/impls/cc/reader.cc index 9ed88d0af2..169f0d2272 100644 --- a/impls/cc/reader.cc +++ b/impls/cc/reader.cc @@ -58,10 +58,7 @@ std::shared_ptr read_atom(Reader &reader) { auto unescaped_token = unescape(token); if (*unescaped_token.rbegin() != '"' || unescaped_token.size() < 2) - { - std::cerr << "unbalanced"; - return nullptr; - } + throw std::runtime_error("unbalanced"); return std::make_shared(unescaped_token); } @@ -79,10 +76,7 @@ std::shared_ptr read_atom(Reader &reader) std::shared_ptr read_list(Reader &reader, char lparen, char rparen) { if (reader.empty()) - { - std::cerr << "unbalanced"; - return nullptr; - } + throw std::runtime_error("unbalanced"); auto list = std::make_shared(lparen, rparen); auto token = read_form(reader); @@ -95,10 +89,7 @@ std::shared_ptr read_list(Reader &reader, char lparen, char rparen) } if (!token) - { - std::cerr << "unbalanced"; - return nullptr; - } + throw std::runtime_error("unbalanced"); return list; } @@ -106,10 +97,7 @@ std::shared_ptr read_list(Reader &reader, char lparen, char rparen) std::shared_ptr read_map(Reader &reader) { if (reader.empty()) - { - std::cerr << "unbalanced"; - return nullptr; - } + throw std::runtime_error("unbalanced"); auto map = std::make_shared(); auto token = read_form(reader); @@ -125,10 +113,7 @@ std::shared_ptr read_map(Reader &reader) } if (!token) - { - std::cerr << "unbalanced"; - return nullptr; - } + throw std::runtime_error("unbalanced"); return map; } @@ -144,12 +129,12 @@ std::shared_ptr read_macro(Reader &reader, const std::string &name) std::shared_ptr read_meta(Reader &reader) { - auto map = read_form(reader); - auto vector = read_form(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(vector); - result->push_back(map); + result->push_back(item); + result->push_back(meta); return result; } diff --git a/impls/cc/step0_repl.cc b/impls/cc/step0_repl.cc index b5bd24cfb1..d39c4d9918 100644 --- a/impls/cc/step0_repl.cc +++ b/impls/cc/step0_repl.cc @@ -32,8 +32,15 @@ int main(int argc, char *argv[]) { std::cout << "user> "; std::getline(std::cin, input); - auto rep_result = rep(input); - std::cout << rep_result << std::endl; + 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/cc/step1_read_print.cc b/impls/cc/step1_read_print.cc index c25c9c0775..ee62374a91 100644 --- a/impls/cc/step1_read_print.cc +++ b/impls/cc/step1_read_print.cc @@ -34,8 +34,15 @@ int main(int argc, char *argv[]) { std::cout << "user> "; std::getline(std::cin, input); - auto rep_result = rep(input); - std::cout << rep_result << std::endl; + 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/cc/step2_eval.cc b/impls/cc/step2_eval.cc index 69f06f65b3..b5429e771e 100644 --- a/impls/cc/step2_eval.cc +++ b/impls/cc/step2_eval.cc @@ -124,8 +124,15 @@ int main(int argc, char *argv[]) { std::cout << "user> "; std::getline(std::cin, input); - auto rep_result = rep(input); - std::cout << rep_result << std::endl; + 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/cc/step5_tco.cc b/impls/cc/step5_tco.cc index 5e9588329e..a5a729c021 100644 --- a/impls/cc/step5_tco.cc +++ b/impls/cc/step5_tco.cc @@ -203,8 +203,15 @@ int main(int argc, char *argv[]) std::string input; std::cout << "user> "; std::getline(std::cin, input); - auto rep_result = rep(input, repl_env); - std::cout << rep_result << std::endl; + 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/cc/step6_file.cc b/impls/cc/step6_file.cc index 075a3fa3ba..3fdb906166 100644 --- a/impls/cc/step6_file.cc +++ b/impls/cc/step6_file.cc @@ -222,8 +222,15 @@ int main(int argc, char *argv[]) std::string input; std::cout << "user> "; std::getline(std::cin, input); - auto rep_result = rep(input, repl_env); - std::cout << rep_result << std::endl; + 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/cc/step7_quote.cc b/impls/cc/step7_quote.cc index 7fd987ab33..0f8710344b 100644 --- a/impls/cc/step7_quote.cc +++ b/impls/cc/step7_quote.cc @@ -296,8 +296,15 @@ int main(int argc, char *argv[]) std::string input; std::cout << "user> "; std::getline(std::cin, input); - auto rep_result = rep(input, repl_env); - std::cout << rep_result << std::endl; + 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/cc/stepA_mal.cc b/impls/cc/stepA_mal.cc new file mode 100644 index 0000000000..2cc1c80708 --- /dev/null +++ b/impls/cc/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/cc/types.hh b/impls/cc/types.hh index e1cf5761b8..72f467c339 100644 --- a/impls/cc/types.hh +++ b/impls/cc/types.hh @@ -69,6 +69,7 @@ public: 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"; } @@ -106,6 +107,9 @@ public: 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); @@ -144,6 +148,7 @@ private: char lparen_; char rparen_; std::vector> list_; + std::shared_ptr meta_ = std::make_shared("nil"); }; class MalMap : public MalType @@ -164,6 +169,9 @@ public: std::unordered_map, MalSymbol::Hash>::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); @@ -187,6 +195,7 @@ public: private: std::unordered_map, MalSymbol::Hash> map_; + std::shared_ptr meta_ = std::make_shared("nil"); }; class MalFunc : public MalType @@ -207,10 +216,14 @@ public: 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_; From 8bc59ebdd1eb379412a8269df64344a6dacf3e4b Mon Sep 17 00:00:00 2001 From: Yang Le Date: Thu, 13 Jul 2023 19:37:21 +0800 Subject: [PATCH 18/21] add Dockerfile --- impls/cc/Dockerfile | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 impls/cc/Dockerfile diff --git a/impls/cc/Dockerfile b/impls/cc/Dockerfile new file mode 100644 index 0000000000..81b14becec --- /dev/null +++ b/impls/cc/Dockerfile @@ -0,0 +1,25 @@ +FROM ubuntu:focal +MAINTAINER Le Yang + +########################################################## +# General requirements for testing or common across many +# implementations +########################################################## + +RUN apt-get -y update + +# Required for running tests +RUN apt-get -y install make 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++ From 77a251c90e75f19841e2031836d36caf1801df56 Mon Sep 17 00:00:00 2001 From: Yang Le Date: Sat, 22 Jul 2023 16:30:26 +0800 Subject: [PATCH 19/21] update for tests/lib --- impls/cc/stepA_mal.cc | 2 +- impls/cc/types.hh | 38 +++++++++++++++++++++++++------------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/impls/cc/stepA_mal.cc b/impls/cc/stepA_mal.cc index 2cc1c80708..d64cc79463 100644 --- a/impls/cc/stepA_mal.cc +++ b/impls/cc/stepA_mal.cc @@ -102,7 +102,7 @@ std::shared_ptr macroexpand(std::shared_ptr ast, std::shared_p while (is_macro_call(ast_, env)) { - auto &list = static_cast(*ast); + auto &list = static_cast(*ast_); auto symbol = env->get(static_cast(*list[0])); auto &func = static_cast(*symbol); diff --git a/impls/cc/types.hh b/impls/cc/types.hh index 72f467c339..ca87e7914c 100644 --- a/impls/cc/types.hh +++ b/impls/cc/types.hh @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -60,6 +61,8 @@ public: 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; } @@ -73,17 +76,23 @@ public: operator bool() const { return symbol_ != "nil" && symbol_ != "false"; } - struct Hash +private: + std::string symbol_; +}; + +namespace std +{ + template <> + struct hash { - std::size_t operator()(const MalSymbol &k) const + typedef MalSymbol argument_type; + typedef std::size_t result_type; + result_type operator()(argument_type const &s) const { - return std::hash()(k.symbol_); + return std::hash()(s); } }; - -private: - std::string symbol_; -}; +} class MalList : public MalType { @@ -154,6 +163,9 @@ private: class MalMap : public MalType { public: + // using map_t = std::unordered_map>; + using map_t = std::map>; + MalMap() : MalType(Type::Map) {} @@ -162,12 +174,12 @@ public: std::size_t size() const noexcept { return map_.size(); } void erase(const MalSymbol &key) { map_.erase(key); } - std::unordered_map, MalSymbol::Hash>::iterator begin() noexcept { return map_.begin(); } - std::unordered_map, MalSymbol::Hash>::const_iterator begin() const noexcept { return map_.begin(); } - std::unordered_map, MalSymbol::Hash>::iterator end() noexcept { return map_.end(); } - std::unordered_map, MalSymbol::Hash>::const_iterator end() const noexcept { return map_.end(); } + 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(); } - std::unordered_map, MalSymbol::Hash>::const_iterator find(const MalSymbol &key) const { return map_.find(key); } + 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_; } @@ -194,7 +206,7 @@ public: } private: - std::unordered_map, MalSymbol::Hash> map_; + map_t map_; std::shared_ptr meta_ = std::make_shared("nil"); }; From ef05f2fe207cb57d3b6f6d12cfdb292366d3a286 Mon Sep 17 00:00:00 2001 From: Yang Le Date: Mon, 24 Jul 2023 20:06:32 +0800 Subject: [PATCH 20/21] all tests passed --- impls/cc/core.cc | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/impls/cc/core.cc b/impls/cc/core.cc index c5dd2ddea5..235dcfe7a1 100644 --- a/impls/cc/core.cc +++ b/impls/cc/core.cc @@ -286,14 +286,11 @@ std::shared_ptr apply(std::vector> args) { std::vector> list; - for (unsigned i = 1; i < args.size(); ++i) - { - if (args[i]->type() == MalType::Type::List) - for (auto item : static_cast(*args[i])) - list.push_back(item); - else - list.push_back(args[i]); - } + 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); From 3b93b11077954779a0747e0f883ab35f1bc2785d Mon Sep 17 00:00:00 2001 From: Yang Le Date: Fri, 9 Aug 2024 20:50:14 +0800 Subject: [PATCH 21/21] sync with upstream --- IMPLS.yml | 1 + Makefile.impls | 4 ++-- impls/{cc => cpp.2}/Dockerfile | 8 +++++--- impls/{cc => cpp.2}/Makefile | 0 impls/{cc => cpp.2}/core.cc | 0 impls/{cc => cpp.2}/core.hh | 0 impls/{cc => cpp.2}/env.hh | 0 impls/{cc => cpp.2}/printer.cc | 0 impls/{cc => cpp.2}/printer.hh | 0 impls/{cc => cpp.2}/reader.cc | 0 impls/{cc => cpp.2}/reader.hh | 0 impls/{cc => cpp.2}/run | 0 impls/{cc => cpp.2}/step0_repl.cc | 0 impls/{cc => cpp.2}/step1_read_print.cc | 0 impls/{cc => cpp.2}/step2_eval.cc | 0 impls/{cc => cpp.2}/step3_env.cc | 0 impls/{cc => cpp.2}/step4_if_fn_do.cc | 0 impls/{cc => cpp.2}/step5_tco.cc | 0 impls/{cc => cpp.2}/step6_file.cc | 0 impls/{cc => cpp.2}/step7_quote.cc | 0 impls/{cc => cpp.2}/step8_macros.cc | 0 impls/{cc => cpp.2}/step9_try.cc | 0 impls/{cc => cpp.2}/stepA_mal.cc | 0 impls/{cc => cpp.2}/types.hh | 0 24 files changed, 8 insertions(+), 5 deletions(-) rename impls/{cc => cpp.2}/Dockerfile (73%) rename impls/{cc => cpp.2}/Makefile (100%) rename impls/{cc => cpp.2}/core.cc (100%) rename impls/{cc => cpp.2}/core.hh (100%) rename impls/{cc => cpp.2}/env.hh (100%) rename impls/{cc => cpp.2}/printer.cc (100%) rename impls/{cc => cpp.2}/printer.hh (100%) rename impls/{cc => cpp.2}/reader.cc (100%) rename impls/{cc => cpp.2}/reader.hh (100%) rename impls/{cc => cpp.2}/run (100%) rename impls/{cc => cpp.2}/step0_repl.cc (100%) rename impls/{cc => cpp.2}/step1_read_print.cc (100%) rename impls/{cc => cpp.2}/step2_eval.cc (100%) rename impls/{cc => cpp.2}/step3_env.cc (100%) rename impls/{cc => cpp.2}/step4_if_fn_do.cc (100%) rename impls/{cc => cpp.2}/step5_tco.cc (100%) rename impls/{cc => cpp.2}/step6_file.cc (100%) rename impls/{cc => cpp.2}/step7_quote.cc (100%) rename impls/{cc => cpp.2}/step8_macros.cc (100%) rename impls/{cc => cpp.2}/step9_try.cc (100%) rename impls/{cc => cpp.2}/stepA_mal.cc (100%) rename impls/{cc => cpp.2}/types.hh (100%) 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 3267383715..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 cc 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 \ @@ -112,12 +112,12 @@ basic_STEP_TO_PROG = $(basic_STEP_TO_PROG_$(basic_MODE)) bbc-basic_STEP_TO_PROG = impls/bbc-basic/$($(1)).bas c_STEP_TO_PROG = impls/c/$($(1)) c.2_STEP_TO_PROG = impls/c.2/$($(1)) -cc_STEP_TO_PROG = impls/cc/$($(1)) chuck_STEP_TO_PROG = impls/chuck/$($(1)).ck 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/cc/Dockerfile b/impls/cpp.2/Dockerfile similarity index 73% rename from impls/cc/Dockerfile rename to impls/cpp.2/Dockerfile index 81b14becec..db1cfc7122 100644 --- a/impls/cc/Dockerfile +++ b/impls/cpp.2/Dockerfile @@ -1,6 +1,7 @@ -FROM ubuntu:focal +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 @@ -9,7 +10,8 @@ MAINTAINER Le Yang RUN apt-get -y update # Required for running tests -RUN apt-get -y install make python +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 diff --git a/impls/cc/Makefile b/impls/cpp.2/Makefile similarity index 100% rename from impls/cc/Makefile rename to impls/cpp.2/Makefile diff --git a/impls/cc/core.cc b/impls/cpp.2/core.cc similarity index 100% rename from impls/cc/core.cc rename to impls/cpp.2/core.cc diff --git a/impls/cc/core.hh b/impls/cpp.2/core.hh similarity index 100% rename from impls/cc/core.hh rename to impls/cpp.2/core.hh diff --git a/impls/cc/env.hh b/impls/cpp.2/env.hh similarity index 100% rename from impls/cc/env.hh rename to impls/cpp.2/env.hh diff --git a/impls/cc/printer.cc b/impls/cpp.2/printer.cc similarity index 100% rename from impls/cc/printer.cc rename to impls/cpp.2/printer.cc diff --git a/impls/cc/printer.hh b/impls/cpp.2/printer.hh similarity index 100% rename from impls/cc/printer.hh rename to impls/cpp.2/printer.hh diff --git a/impls/cc/reader.cc b/impls/cpp.2/reader.cc similarity index 100% rename from impls/cc/reader.cc rename to impls/cpp.2/reader.cc diff --git a/impls/cc/reader.hh b/impls/cpp.2/reader.hh similarity index 100% rename from impls/cc/reader.hh rename to impls/cpp.2/reader.hh diff --git a/impls/cc/run b/impls/cpp.2/run similarity index 100% rename from impls/cc/run rename to impls/cpp.2/run diff --git a/impls/cc/step0_repl.cc b/impls/cpp.2/step0_repl.cc similarity index 100% rename from impls/cc/step0_repl.cc rename to impls/cpp.2/step0_repl.cc diff --git a/impls/cc/step1_read_print.cc b/impls/cpp.2/step1_read_print.cc similarity index 100% rename from impls/cc/step1_read_print.cc rename to impls/cpp.2/step1_read_print.cc diff --git a/impls/cc/step2_eval.cc b/impls/cpp.2/step2_eval.cc similarity index 100% rename from impls/cc/step2_eval.cc rename to impls/cpp.2/step2_eval.cc diff --git a/impls/cc/step3_env.cc b/impls/cpp.2/step3_env.cc similarity index 100% rename from impls/cc/step3_env.cc rename to impls/cpp.2/step3_env.cc diff --git a/impls/cc/step4_if_fn_do.cc b/impls/cpp.2/step4_if_fn_do.cc similarity index 100% rename from impls/cc/step4_if_fn_do.cc rename to impls/cpp.2/step4_if_fn_do.cc diff --git a/impls/cc/step5_tco.cc b/impls/cpp.2/step5_tco.cc similarity index 100% rename from impls/cc/step5_tco.cc rename to impls/cpp.2/step5_tco.cc diff --git a/impls/cc/step6_file.cc b/impls/cpp.2/step6_file.cc similarity index 100% rename from impls/cc/step6_file.cc rename to impls/cpp.2/step6_file.cc diff --git a/impls/cc/step7_quote.cc b/impls/cpp.2/step7_quote.cc similarity index 100% rename from impls/cc/step7_quote.cc rename to impls/cpp.2/step7_quote.cc diff --git a/impls/cc/step8_macros.cc b/impls/cpp.2/step8_macros.cc similarity index 100% rename from impls/cc/step8_macros.cc rename to impls/cpp.2/step8_macros.cc diff --git a/impls/cc/step9_try.cc b/impls/cpp.2/step9_try.cc similarity index 100% rename from impls/cc/step9_try.cc rename to impls/cpp.2/step9_try.cc diff --git a/impls/cc/stepA_mal.cc b/impls/cpp.2/stepA_mal.cc similarity index 100% rename from impls/cc/stepA_mal.cc rename to impls/cpp.2/stepA_mal.cc diff --git a/impls/cc/types.hh b/impls/cpp.2/types.hh similarity index 100% rename from impls/cc/types.hh rename to impls/cpp.2/types.hh