From 6c631dc075df4fa1996522c247a1a457e5ae151f Mon Sep 17 00:00:00 2001 From: Robert Bendun Date: Thu, 20 Oct 2022 16:42:05 +0200 Subject: [PATCH] Added file as function commandline switch --- CHANGELOG.md | 1 + musique/main.cc | 90 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 84 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe4953f..18252eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `:quit` REPL command that mirrors `:exit` command * Virtual MIDI output port creation as default action (--output connects to existing one) * Added build instructions +* `-f` commandline argument that will turn file into deffered function ### Changed diff --git a/musique/main.cc b/musique/main.cc index f061192..45b40b3 100644 --- a/musique/main.cc +++ b/musique/main.cc @@ -16,6 +16,7 @@ #include #include #include +#include #ifdef _WIN32 extern "C" { @@ -76,6 +77,9 @@ static T pop(std::span &span) " -I,--interactive,--repl\n" " enables interactive mode even when another code was passed\n" "\n" + " -f,--as-function FILENAME\n" + " deffer file execution and turn it into a file\n" + "\n" " --ast\n" " prints ast for given code\n" "\n" @@ -119,6 +123,37 @@ static void trim(std::string_view &s) } } +static std::string filename_to_function_name(std::string_view filename) +{ + if (filename == "-") { + return "stdin"; + } + + auto stem = fs::path(filename).stem().string(); + auto stem_view = std::string_view(stem); + + std::string name; + + auto is_first = unicode::First_Character::Yes; + while (!stem_view.empty()) { + auto [rune, next_stem] = utf8::decode(stem_view); + if (!unicode::is_identifier(rune, is_first)) { + if (rune != ' ' && rune != '-') { + // TODO Nicer error + std::cerr << "[ERROR] Failed to construct function name from filename " << stem + << "\n due to presence of invalid character: " << utf8::Print{rune} << "(U+" << std::hex << rune << ")\n" + << std::flush; + std::exit(1); + } + name += '_'; + } else { + name += stem_view.substr(0, stem_view.size() - next_stem.size()); + } + stem_view = next_stem; + } + return name; +} + /// Runs interpreter on given source code struct Runner { @@ -160,6 +195,29 @@ struct Runner Runner& operator=(Runner const&) = delete; Runner& operator=(Runner &&) = delete; + /// Consider given input file as new definition of parametless function + /// + /// Useful for deffering execution of files to the point when all configuration of midi devices + /// is beeing known as working. + std::optional deffered_file(std::string_view source, std::string_view filename) + { + auto ast = Try(Parser::parse(source, filename, repl_line_number)); + if (ast_only_mode) { + dump(ast); + return {}; + } + + auto name = filename_to_function_name(filename); + + Block block; + block.location = ast.location; + block.body = std::move(ast); + block.context = Env::global; + std::cout << "Defined function " << name << " as file " << filename << std::endl; + Env::global->force_define(std::move(name), Value(std::move(block))); + return {}; + } + /// Run given source std::optional run(std::string_view source, std::string_view filename, bool output = false) { @@ -286,7 +344,12 @@ static std::optional Main(std::span args) /// Describes all arguments that will be run struct Run { - bool is_file = true; + enum Type + { + File, + Argument, + Deffered_File + } type; std::string_view argument; }; @@ -299,7 +362,7 @@ static std::optional Main(std::span args) std::string_view arg = pop(args); if (arg == "-" || !arg.starts_with('-')) { - runnables.push_back({ .is_file = true, .argument = std::move(arg) }); + runnables.push_back({ .type = Run::File, .argument = std::move(arg) }); continue; } @@ -313,7 +376,16 @@ static std::optional Main(std::span args) std::cerr << "musique: error: option " << arg << " requires an argument" << std::endl; std::exit(1); } - runnables.push_back({ .is_file = false, .argument = pop(args) }); + runnables.push_back({ .type = Run::Argument, .argument = pop(args) }); + continue; + } + + if (arg == "-f" || arg == "--as-function") { + if (args.empty()) { + std::cerr << "musique: error: option " << arg << " requires an argument" << std::endl; + std::exit(1); + } + runnables.push_back({ .type = Run::Deffered_File, .argument = pop(args) }); continue; } @@ -346,8 +418,8 @@ static std::optional Main(std::span args) Runner runner{output_port}; - for (auto const& [is_file, argument] : runnables) { - if (!is_file) { + for (auto const& [type, argument] : runnables) { + if (type == Run::Argument) { Lines::the.add_line("", argument, repl_line_number); Try(runner.run(argument, "")); repl_line_number++; @@ -355,7 +427,6 @@ static std::optional Main(std::span args) } auto path = argument; if (path == "-") { - path = ""; eternal_sources.emplace_back(std::istreambuf_iterator(std::cin), std::istreambuf_iterator()); } else { if (not fs::exists(path)) { @@ -367,7 +438,11 @@ static std::optional Main(std::span args) } Lines::the.add_file(std::string(path), eternal_sources.back()); - Try(runner.run(eternal_sources.back(), path)); + if (type == Run::File) { + Try(runner.run(eternal_sources.back(), path)); + } else { + Try(runner.deffered_file(eternal_sources.back(), argument)); + } } if (runnables.empty() || enable_repl) { @@ -442,4 +517,5 @@ int main(int argc, char const** argv) std::cerr << result.value() << std::flush; return 1; } + return 0; }