From 6573ac0b22fd933e622ee22433e6c34d0afed612 Mon Sep 17 00:00:00 2001 From: Riyyi Date: Sun, 26 Mar 2023 23:20:53 +0200 Subject: [PATCH] Env: Add more native functions --- src/environment.cpp | 10 +++ src/environment.h | 21 ++++-- src/functions.cpp | 148 +++++++++++++++++++++++++++++++++++++++-- src/step3_env.cpp | 2 +- src/step4_if_fn_do.cpp | 108 ++++++++++++++++++++++++++++++ 5 files changed, 277 insertions(+), 12 deletions(-) create mode 100644 src/step4_if_fn_do.cpp diff --git a/src/environment.cpp b/src/environment.cpp index fcc4131..b28e2ed 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -57,6 +57,16 @@ GlobalEnvironment::GlobalEnvironment() sub(); mul(); div(); + + lt(); + lte(); + gt(); + gte(); + + list(); + is_list(); + is_empty(); + count(); } } // namespace blaze diff --git a/src/environment.h b/src/environment.h index f79ef45..7d06552 100644 --- a/src/environment.h +++ b/src/environment.h @@ -38,11 +38,22 @@ public: virtual ~GlobalEnvironment() = default; private: - // TODO: Add more native functions - void add(); - void sub(); - void mul(); - void div(); + void add(); // + + void sub(); // - + void mul(); // * + void div(); // / + + void lt(); // < + void lte(); // <= + void gt(); // > + void gte(); // >= + + void list(); // list + void is_list(); // list? + void is_empty(); // empty? + void count(); // count + + // void equal(); // = }; } // namespace blaze diff --git a/src/functions.cpp b/src/functions.cpp index 03bf231..ec46e6e 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -38,8 +38,6 @@ void GlobalEnvironment::add() void GlobalEnvironment::sub() { auto sub = [](std::span nodes) -> ASTNodePtr { - int64_t result = 0; - if (nodes.size() == 0) { return makePtr(0); } @@ -52,7 +50,7 @@ void GlobalEnvironment::sub() } // Start with the first number - result += std::static_pointer_cast(nodes[0])->number(); + int64_t result = std::static_pointer_cast(nodes[0])->number(); // Skip the first node for (auto it = std::next(nodes.begin()); it != nodes.end(); ++it) { @@ -88,8 +86,6 @@ void GlobalEnvironment::mul() void GlobalEnvironment::div() { auto div = [this](std::span nodes) -> ASTNodePtr { - double result = 0; - if (nodes.size() == 0) { Error::the().addError(format("wrong number of arguments: {}, 0", m_current_key)); return nullptr; @@ -103,7 +99,7 @@ void GlobalEnvironment::div() } // Start with the first number - result += std::static_pointer_cast(nodes[0])->number(); + double result = std::static_pointer_cast(nodes[0])->number(); // Skip the first node for (auto it = std::next(nodes.begin()); it != nodes.end(); ++it) { @@ -116,4 +112,144 @@ void GlobalEnvironment::div() m_values.emplace("/", makePtr(div)); } +// ----------------------------------------- + +#define NUMBER_COMPARE(symbol, comparison_symbol) \ + auto lambda = [this](std::span nodes) -> ASTNodePtr { \ + bool result = true; \ + \ + if (nodes.size() < 2) { \ + Error::the().addError(format("wrong number of arguments: {}, {}", m_current_key, nodes.size() - 1)); \ + return nullptr; \ + } \ + \ + for (auto node : nodes) { \ + if (!is(node.get())) { \ + Error::the().addError(format("wrong argument type: number, '{}'", node)); \ + return nullptr; \ + } \ + } \ + \ + /* Start with the first number */ \ + int64_t number = std::static_pointer_cast(nodes[0])->number(); \ + \ + /* Skip the first node */ \ + for (auto it = std::next(nodes.begin()); it != nodes.end(); ++it) { \ + int64_t current_number = std::static_pointer_cast(*it)->number(); \ + if (number comparison_symbol current_number) { \ + result = false; \ + break; \ + } \ + number = current_number; \ + } \ + \ + return makePtr((result) ? Value::True : Value::False); \ + }; \ + \ + m_values.emplace(symbol, makePtr(lambda)); + +void GlobalEnvironment::lt() +{ + NUMBER_COMPARE("<", >=); +} + +void GlobalEnvironment::lte() +{ + NUMBER_COMPARE("<=", >); +} + +void GlobalEnvironment::gt() +{ + NUMBER_COMPARE(">", <=); +} + +void GlobalEnvironment::gte() +{ + NUMBER_COMPARE(">=", <); +} + +// ----------------------------------------- + +void GlobalEnvironment::list() +{ + auto list = [](std::span nodes) -> ASTNodePtr { + auto list = makePtr(); + + for (auto node : nodes) { + list->addNode(node); + } + + return list; + }; + + m_values.emplace("list", makePtr(list)); +} + +void GlobalEnvironment::is_list() +{ + auto is_list = [](std::span nodes) -> ASTNodePtr { + bool result = true; + + for (auto node : nodes) { + if (!is(node.get())) { + result = false; + break; + } + } + + return makePtr((result) ? Value::True : Value::False); + }; + + m_values.emplace("list?", makePtr(is_list)); +} + +void GlobalEnvironment::is_empty() +{ + auto is_empty = [](std::span nodes) -> ASTNodePtr { + bool result = true; + + for (auto node : nodes) { + if (!is(node.get())) { + Error::the().addError(format("wrong argument type: list, '{}'", node)); + return nullptr; + } + + if (!std::static_pointer_cast(node)->empty()) { + result = false; + break; + } + } + + return makePtr((result) ? Value::True : Value::False); + }; + + m_values.emplace("empty?", makePtr(is_empty)); +} + +void GlobalEnvironment::count() +{ + auto count = [this](std::span nodes) -> ASTNodePtr { + if (nodes.size() > 1) { + Error::the().addError(format("wrong number of arguments: {}, {}", m_current_key, nodes.size() - 1)); + return nullptr; + } + + size_t result = 0; + + for (auto node : nodes) { + if (!is(node.get())) { + Error::the().addError(format("wrong argument type: list, '{}'", node)); + return nullptr; + } + + result = std::static_pointer_cast(node)->size(); + } + + // FIXME: Add numeric_limits check for implicit cast: size_t > int64_t + return makePtr((int64_t)result); + }; + + m_values.emplace("count", makePtr(count)); +} + } // namespace blaze diff --git a/src/step3_env.cpp b/src/step3_env.cpp index 2e0bf9e..0ee7056 100644 --- a/src/step3_env.cpp +++ b/src/step3_env.cpp @@ -16,7 +16,7 @@ #include "readline.h" #include "settings.h" -#if 1 +#if 0 static blaze::EnvironmentPtr env = blaze::makePtr(); auto read(std::string_view input) -> blaze::ASTNodePtr diff --git a/src/step4_if_fn_do.cpp b/src/step4_if_fn_do.cpp new file mode 100644 index 0000000..2e0bf9e --- /dev/null +++ b/src/step4_if_fn_do.cpp @@ -0,0 +1,108 @@ +#include // std::signal +#include // std::exit +#include +#include + +#include "ruc/argparser.h" +#include "ruc/format/color.h" + +#include "ast.h" +#include "environment.h" +#include "error.h" +#include "eval.h" +#include "lexer.h" +#include "printer.h" +#include "reader.h" +#include "readline.h" +#include "settings.h" + +#if 1 +static blaze::EnvironmentPtr env = blaze::makePtr(); + +auto read(std::string_view input) -> blaze::ASTNodePtr +{ + blaze::Lexer lexer(input); + lexer.tokenize(); + if (blaze::Settings::the().get("dump-lexer") == "1") { + lexer.dump(); + } + + blaze::Reader reader(std::move(lexer.tokens())); + reader.read(); + if (blaze::Settings::the().get("dump-reader") == "1") { + reader.dump(); + } + + return reader.node(); +} + +auto eval(blaze::ASTNodePtr ast) -> blaze::ASTNodePtr +{ + blaze::Eval eval(ast, env); + eval.eval(); + + return eval.ast(); +} + +auto print(blaze::ASTNodePtr exp) -> std::string +{ + blaze::Printer printer; + + return printer.print(exp); +} + +auto rep(std::string_view input) -> std::string +{ + blaze::Error::the().clearErrors(); + blaze::Error::the().setInput(input); + + return print(eval(read(input))); +} + +static auto cleanup(int signal) -> void +{ + print("\033[0m\n"); + std::exit(signal); +} + +auto main(int argc, char* argv[]) -> int +{ + bool dump_lexer = false; + bool dump_reader = false; + bool pretty_print = false; + std::string_view history_path = "~/.mal-history"; + + // CLI arguments + ruc::ArgParser arg_parser; + arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr); + arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); + arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); + arg_parser.addOption(history_path, 'h', "history", nullptr, nullptr, nullptr, ruc::ArgParser::Required::Yes); + arg_parser.parse(argc, argv); + + // Set settings + blaze::Settings::the().set("dump-lexer", dump_lexer ? "1" : "0"); + blaze::Settings::the().set("dump-reader", dump_reader ? "1" : "0"); + blaze::Settings::the().set("pretty-print", pretty_print ? "1" : "0"); + + // Signal callbacks + std::signal(SIGINT, cleanup); + std::signal(SIGTERM, cleanup); + + blaze::Readline readline(pretty_print, history_path); + + std::string input; + while (readline.get(input)) { + if (pretty_print) { + print("\033[0m"); + } + print("{}\n", rep(input)); + } + + if (pretty_print) { + print("\033[0m"); + } + + return 0; +} +#endif