diff --git a/src/forward.h b/src/forward.h index 5acb910..3eb3175 100644 --- a/src/forward.h +++ b/src/forward.h @@ -22,8 +22,9 @@ typedef std::shared_ptr EnvironmentPtr; // ----------------------------------------- // Functions +extern ValuePtr readline(const std::string& prompt); extern ValuePtr read(std::string_view input); -ValuePtr eval(ValuePtr ast, EnvironmentPtr env); +extern ValuePtr eval(ValuePtr ast, EnvironmentPtr env); extern void installFunctions(EnvironmentPtr env); diff --git a/src/functions.cpp b/src/functions.cpp index bd0fbf1..69b0312 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -786,6 +786,16 @@ ADD_FUNCTION( return result; }); +ADD_FUNCTION( + "readline", + { + CHECK_ARG_COUNT_IS("readline", nodes.size(), 1); + + VALUE_CAST(prompt, String, nodes.front()); + + return readline(prompt->data()); + }); + // ----------------------------------------- void installFunctions(EnvironmentPtr env) diff --git a/src/readline.cpp b/src/readline.cpp index f3a1cef..86f4407 100644 --- a/src/readline.cpp +++ b/src/readline.cpp @@ -13,6 +13,7 @@ #include #include "ruc/format/color.h" +#include "ruc/format/print.h" #include "readline.h" @@ -22,13 +23,7 @@ Readline::Readline(bool pretty_print, std::string_view history_path) : m_pretty_print(pretty_print) , m_history_path(tilde_expand(history_path.data())) { - if (!pretty_print) { - m_prompt = "user> "; - } - else { - m_prompt = format(fg(ruc::format::TerminalColor::Blue), "user>"); - m_prompt += format(" \033[1m"); - } + m_prompt = createPrompt("user> "); read_history(m_history_path); } @@ -40,9 +35,24 @@ Readline::~Readline() // ----------------------------------------- -bool Readline::get(std::string& output) +std::string Readline::createPrompt(const std::string& prompt) +{ + if (!m_pretty_print) { + return prompt; + } + + return format(fg(ruc::format::TerminalColor::Blue), "{}", prompt) + + format("\033[1m"); +} + +bool Readline::get(std::string& output, const std::string& prompt) { - char* line = readline(m_prompt.c_str()); + char* line = readline(prompt.c_str()); + + if (m_pretty_print) { + print("\033[0m"); + } + if (line == nullptr) { return false; } @@ -57,4 +67,9 @@ bool Readline::get(std::string& output) return true; } +bool Readline::get(std::string& output) +{ + return get(output, m_prompt); +} + } // namespace blaze diff --git a/src/readline.h b/src/readline.h index 79effb0..4dc04c7 100644 --- a/src/readline.h +++ b/src/readline.h @@ -15,15 +15,19 @@ namespace blaze { class Readline { public: + Readline() = default; Readline(bool pretty_print, std::string_view history_path); virtual ~Readline(); + std::string createPrompt(const std::string& prompt); + + bool get(std::string& output, const std::string& prompt); bool get(std::string& output); private: bool m_pretty_print { false }; - std::string m_prompt; char* m_history_path; + std::string m_prompt; }; } // namespace blaze diff --git a/src/step9_try.cpp b/src/step9_try.cpp index 518f48d..059ab64 100644 --- a/src/step9_try.cpp +++ b/src/step9_try.cpp @@ -126,7 +126,6 @@ auto main(int argc, char* argv[]) -> int arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); arg_parser.addOption(history_path, 'h', "history-path", nullptr, nullptr, nullptr, ruc::ArgParser::Required::Yes); - // TODO: Add overload for addArgument(std::vector) arg_parser.addArgument(arguments, "arguments", nullptr, nullptr, ruc::ArgParser::Required::No); arg_parser.parse(argc, argv); diff --git a/src/stepA_mal.cpp b/src/stepA_mal.cpp new file mode 100644 index 0000000..4df9ff2 --- /dev/null +++ b/src/stepA_mal.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2023 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include // std::signal +#include // std::exit +#include +#include +#include + +#include "ruc/argparser.h" +#include "ruc/format/color.h" + +#include "ast.h" +#include "environment.h" +#include "error.h" +#include "eval.h" +#include "forward.h" +#include "lexer.h" +#include "printer.h" +#include "reader.h" +#include "readline.h" +#include "settings.h" + +#if 1 +namespace blaze { + +static blaze::Readline s_readline; +static EnvironmentPtr s_outer_env = Environment::create(); + +static auto cleanup(int signal) -> void +{ + print("\033[0m\n"); + std::exit(signal); +} + +auto readline(const std::string& prompt) -> ValuePtr +{ + std::string input; + if (s_readline.get(input, s_readline.createPrompt(prompt))) { + return makePtr(input); + } + + return makePtr(); +} + +auto read(std::string_view input) -> ValuePtr +{ + Lexer lexer(input); + lexer.tokenize(); + if (Settings::the().get("dump-lexer") == "1") { + lexer.dump(); + } + + Reader reader(std::move(lexer.tokens())); + reader.read(); + if (Settings::the().get("dump-reader") == "1") { + reader.dump(); + } + + return reader.node(); +} + +auto eval(ValuePtr ast, EnvironmentPtr env) -> ValuePtr +{ + if (env == nullptr) { + env = s_outer_env; + } + + Eval eval(ast, env); + eval.eval(); + + return eval.ast(); +} + +static auto print(ValuePtr exp) -> std::string +{ + Printer printer; + + return printer.print(exp, true); +} + +static auto rep(std::string_view input, EnvironmentPtr env) -> std::string +{ + Error::the().clearErrors(); + Error::the().setInput(input); + + return print(eval(read(input), env)); +} + +static std::string_view lambdaTable[] = { + "(def! not (fn* (cond) (if cond false true)))", + "(def! load-file (fn* (filename) \ + (eval (read-string (str \"(do \" (slurp filename) \"\nnil)\")))))", + "(defmacro! cond (fn* (& xs) \ + (if (> (count xs) 0) \ + (list 'if (first xs) \ + (if (> (count xs) 1) \ + (nth xs 1) \ + (throw \"odd number of forms to cond\")) \ + (cons 'cond (rest (rest xs)))))))", + "(def! *host-language* \"C++\")", +}; + +static auto installLambdas(EnvironmentPtr env) -> void +{ + for (auto function : lambdaTable) { + rep(function, env); + } +} + +static auto makeArgv(EnvironmentPtr env, std::vector arguments) -> void +{ + auto list = makePtr(); + if (arguments.size() > 1) { + for (auto it = arguments.begin() + 1; it != arguments.end(); ++it) { + list->add(makePtr(*it)); + } + } + env->set("*ARGV*", list); +} + +} // namespace blaze + +auto main(int argc, char* argv[]) -> int +{ + bool dump_lexer = false; + bool dump_reader = false; + bool pretty_print = false; + std::string_view history_path = "~/.blaze-history"; + std::vector arguments; + + // CLI arguments + ruc::ArgParser arg_parser; + arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr); + arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); + arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); + arg_parser.addOption(history_path, 'h', "history-path", nullptr, nullptr, nullptr, ruc::ArgParser::Required::Yes); + // TODO: Add overload for addArgument(std::vector) + arg_parser.addArgument(arguments, "arguments", nullptr, nullptr, ruc::ArgParser::Required::No); + arg_parser.parse(argc, argv); + + // Set settings + blaze::Settings::the().set("dump-lexer", dump_lexer ? "1" : "0"); + blaze::Settings::the().set("dump-reader", dump_reader ? "1" : "0"); + blaze::Settings::the().set("pretty-print", pretty_print ? "1" : "0"); + + // Signal callbacks + std::signal(SIGINT, blaze::cleanup); + std::signal(SIGTERM, blaze::cleanup); + + installFunctions(blaze::s_outer_env); + installLambdas(blaze::s_outer_env); + makeArgv(blaze::s_outer_env, arguments); + + if (arguments.size() > 0) { + rep(format("(load-file \"{}\")", arguments.front()), blaze::s_outer_env); + return 0; + } + + rep("(println (str \"Mal [\" *host-language* \"]\"))", blaze::s_outer_env); + + blaze::s_readline = blaze::Readline(pretty_print, history_path); + + std::string input; + while (blaze::s_readline.get(input)) { + std::string output = rep(input, blaze::s_outer_env); + if (output.length() > 0) { + print("{}\n", output); + } + } + + if (pretty_print) { + print("\033[0m"); + } + + return 0; +} +#endif