diff --git a/container/src/finance/completion.bash b/container/src/finance/completion.bash index ff622a4..93634ef 100644 --- a/container/src/finance/completion.bash +++ b/container/src/finance/completion.bash @@ -145,7 +145,7 @@ function docker-finance::completion() mapfile -t _root < <({ find "${DOCKER_FINANCE_CONTAINER_PLUGINS}"/root "${_args[@]}" -printf 'plugins/custom/%P\n' 2>/dev/null find "${DOCKER_FINANCE_CONTAINER_REPO}"/plugins/root "${_args[@]}" -printf 'plugins/repo/%P\n' 2>/dev/null - find "${DOCKER_FINANCE_CONTAINER_REPO}"/src/root/macro "${_args[@]}" -printf 'macro/%P\n' 2>/dev/null + find "${DOCKER_FINANCE_CONTAINER_REPO}"/src/root/macro "${_args[@]}" -printf 'macros/repo/%P\n' 2>/dev/null }) declare -r _root mapfile -t _reply < <(compgen -W "help ${_root[*]}" -- "$_cur") diff --git a/container/src/finance/lib/internal/lib_root.bash b/container/src/finance/lib/internal/lib_root.bash index efd166c..88bcdc2 100644 --- a/container/src/finance/lib/internal/lib_root.bash +++ b/container/src/finance/lib/internal/lib_root.bash @@ -59,9 +59,10 @@ function lib_root::__parse_root() [TAB COMPLETION]: load (and run) given macro/plugin when starting interpreter - macro = repository macros in repository location - plugins/custom = custom plugins in custom plugin location - plugins/repo = repository plugins in repository location + macros/repo = macros in repository location + macros/custom = WIP (not currently supported) + plugins/repo = plugins in repository location + plugins/custom = plugins in custom plugin location [args]: arguments to macro or plugin @@ -72,30 +73,49 @@ function lib_root::__parse_root() \e[37;2m# The output of tab completion\e[0m $ $global_usage \\\t\\\t - help macro/crypto/random.C macro/test/unit.C plugins/custom/example.cc - macro/crypto/hash.C macro/test/benchmark.C macro/web/server.C plugins/repo/example.cc + help macros/repo/test/benchmark.C plugins/custom/example.cc + macros/repo/crypto/hash.C macros/repo/test/unit.C plugins/repo/bitcoin/bitcoin.cc + macros/repo/crypto/random.C macros/repo/web/server.C plugins/repo/example/example.cc - \e[37;2m# Load and run the repo's Web server macro\e[0m - \e[37;2m# NOTE: this macro will maintain a running ROOT instance\e[0m - $ $global_usage macro/web/server.C + \e[37;2m# Load and run repo's Web server macro\e[0m + \e[37;2m# NOTE: only server macros will maintain a running ROOT instance (others will 'one-off')\e[0m + $ $global_usage macros/repo/web/server.C - \e[37;2m# Load and run the repo's Hash macro with an argument\e[0m - \e[37;2m# NOTE: this macro will 'one-off' the ROOT instance\e[0m - $ $global_usage macro/crypto/hash.C 'this is a test message' + \e[37;2m# Load and run repo's Hash macro with loader argument 'this is a test message'\e[0m + $ $global_usage macros/repo/crypto/hash.C 'this is a test message' - \e[37;2m# Load repo example plugin, run within instance\e[0m - $ $global_usage plugins/repo/example.cc + \e[37;2m# Load and run unit tests for only Pluggable types, using filter as loader argument\e[0m + $ $global_usage macros/repo/test/unit.C 'Pluggable*:Macro*:Plugin*' + + \e[37;2m# Load and use repo's example plugin, dispatching with trivial loader and unloader arguments\e[0m + $ $global_usage plugins/repo/example/example.cc 'int i{123}; i' 'std::cout << --i << std::endl;' root [0] - Interpreting: '.L /home/business/docker-finance/plugins/root/example.cc' - root [1] dfi::plugin::my_plugin_namespace::example\\\t - example1 - example2 - example3 - root [1] dfi::plugin::my_plugin_namespace::example2() + Processing macro/rootlogon.C... ... + Loading: '${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc' + Interpreting: 'dfi::plugin::example::example_cc::load(\"int i{123}; i\")' + ... + Interpreting: 'int i{123}; i' + (int) 123 + root [3] dfi::plugin::example::Example e; // Plugin's Example class is now available to use + root [4] e.example2() + ... + root [5] g_plugin_example_cc.unload() // Plugin is a global instance based on Pluggable entrypoint name + Interpreting: 'dfi::plugin::example::example_cc::unload(\"std::cout << --i << std::endl;\")' + Interpreting: 'std::cout << --i << std::endl;' + 122 + ... + root [6] e.example2() // Since plugin has been unloaded, this is expected to not compile + input_line_27:2:3: error: use of undeclared identifier 'e' + (e.example2()) + ^ + Error in : Error evaluating expression (e.example2()) + Execution of your code was aborted. + root [7] .quit + $ " - if [[ "$#" -gt 0 && ! "$_arg" =~ (^macro/|^plugins/) ]]; then + if [[ "$#" -gt 0 && ! "$_arg" =~ (^macros/|^plugins/) ]]; then lib_utils::die_usage "$_usage" fi @@ -106,19 +126,18 @@ function lib_root::__parse_root() # Macro # - if [[ "$_arg" =~ ^macro ]]; then - # TODO: utilize stub when 'repo/' and 'custom/' is implemented (like plugins) - local -r _macro="$_arg" + if [[ "$_arg" =~ ^macros ]]; then + local -r _macro="${_stub#*/}" # Repo macros - [ ! -f "${DOCKER_FINANCE_CONTAINER_REPO}/src/root/${_macro}" ] \ + [ ! -f "${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/${_macro}" ] \ && lib_utils::die_fatal "repo macro not found '${_macro}'" # TODO: currently, only repo macros are supported # (no custom macros, use custom plugins instead) # Per API (v1), requires path stub format - declare -gr global_arg_macro="$_macro" + declare -gr global_arg_macro="$_stub" fi # @@ -181,64 +200,82 @@ function lib_root::__root() fi # - # Macro: non-interactive opts + # Pluggable's arguments + # + + local _load_arg="${*:2:1}" + local _unload_arg="${*:3}" + + # + # Pluggable: macro # if [ ! -z "$global_arg_macro" ]; then - _exec+=("-e" "dfi::macro::load(\"${global_arg_macro}\")") - - # NOTE: - # - # - Currently, only repo macros are supported - # - # * Per API (v1): - # - # - filename is named after class - # * classes begin uppercase - # * filename begins lowercase - # - # - run() static function is the entry point for all macros - # - - # TODO: modify substring when 'repo/' and 'custom/' is implemented (like plugins) - - local _namespace="${global_arg_macro#*/}" - _namespace="${_namespace%/*}" - declare -r _namespace local _class="${global_arg_macro##*/}" _class="${_class^}" _class="${_class%.*}" declare -r _class - local -r _caller="dfi::macro::${_namespace}::${_class}" - - # Per-API signatures (v1) - if [[ ${_class} == "Hash" ]]; then - # If no message is given, default to unique profile/subprofile - [[ -z "$global_parent_profile" || -z "$global_arg_delim_1" || -z "$global_child_profile" ]] && lib_utils::die_fatal - [ -z "${*:2}" ] \ - && _run="run(\"${global_parent_profile}${global_arg_delim_1}${global_child_profile}\")" \ - || _run="run(\"${*:2}\")" - else - _run="run()" + # NOTE: for benchmarks, google benchmark does not have a gtest equivalent of + # `GTEST_FLAG(filter)`, but will process shell environment `BENCHMARK_FILTER` + if [[ "${_class%.*}" == "Benchmark" ]]; then + declare -grx BENCHMARK_FILTER="$_load_arg" + # Current impl will treat the load arg as code which, in this case, is not + unset _load_arg fi - _exec+=("-e" "${_caller}::${_run}") + _exec+=("-e" "dfi::macro::load(\"${global_arg_macro}\", \"${_load_arg}\")") - # NOTE: when called via shell, server(s) are implied to - # require an interactive ROOT instance (not a 'one-off'). + # NOTE: when calling via shell, server(s) are implied to + # require an interactive ROOT instance (not a 'one-off') if [[ "${_class%.*}" != "Server" ]]; then _exec+=('-q') fi + + # NOTE: currently no need to "dfi::macro::unload()" from here fi # - # Plugin: non-interactive opts + # Pluggable: plugin # if [ ! -z "$global_arg_plugin" ]; then - _exec+=("-e" "dfi::plugin::load(\"${global_arg_plugin}\")") + + # + # Instead of using one-off wrappers, the following leaves the plugin's state + # intact during interpreter runtime. + # + # WARNING: + # + # The dispatcher (or any declarations outside of the dispatcher) + # will appear in the global namespace of the interpreter! + # + + # Gives plugin a global instance based on Pluggable entrypoint name + local _name="${global_arg_plugin##*/}" + _name="g_plugin_${_name%.*}_${_name#*.}" + declare -r _name + + local _code + _code+="using t_path = dfi::plugin::common::PluginPath;" + _code+="t_path path{\"${global_arg_plugin}\"};" + + _code+="using t_space = dfi::plugin::common::PluginSpace;" + _code+="t_space space{path.parent(), path.child()};" + + _code+="using t_args = dfi::plugin::common::PluginArgs;" + _code+="t_args args{\"${_load_arg}\", \"${_unload_arg}\"};" + + _code+="dfi::common::type::Pluggable type{path, space, args};" + _code+="dfi::plugin::common::Plugin plugin{type};" + + local _dispatch="auto dispatch = []() -> auto { $_code; return plugin; };" + _dispatch+="auto ${_name} = dispatch();" + + # NOTE: unload() at will, inside the interpreter + _exec+=("-e" "$_dispatch" "-e" "${_name}.load();") + fi #