diff --git a/bshell/runtime/cmdcall.c b/bshell/runtime/cmdcall.c new file mode 100644 index 0000000..96050e4 --- /dev/null +++ b/bshell/runtime/cmdcall.c @@ -0,0 +1,328 @@ +#include "cmdcall.h" + +#include "../command/alias.h" +#include "../command/command.h" +#include "../runtime/pipeline.h" +#include "../status.h" +#include "runtime.h" + +#include +#include +#include +#include +#include +#include +#include + +enum cmdcall_type { + CMDCALL_NONE = 0, + CMDCALL_CMDLET, + CMDCALL_NATIVE, +}; + +struct bshell_cmdcall_p { + enum cmdcall_type cmd_type; + bshell_command *cmd_target; + char *cmd_native_path; + bool cmd_initialised; + FX_VECTOR_DECLARE(fx_value, cmd_args); + fx_array *cmd_args_processed; +}; + +extern const fx_assembly *bshell_assembly_get(void); + +static void cmdcall_fini(fx_object *obj, void *priv) +{ + struct bshell_cmdcall_p *cmdcall = priv; + + if (cmdcall->cmd_native_path) { + free(cmdcall->cmd_native_path); + } + + if (cmdcall->cmd_args_processed) { + fx_array_unref(cmdcall->cmd_args_processed); + } + + for (size_t i = 0; i < cmdcall->cmd_args.count; i++) { + fx_value_unset(&cmdcall->cmd_args.items[i]); + } + + fx_vector_destroy(cmdcall->cmd_args, NULL); + + if (cmdcall->cmd_target) { + bshell_command_end_processing(cmdcall->cmd_target); + bshell_command_unref(cmdcall->cmd_target); + } +} + +static enum bshell_status cmdcall_push_arg( + struct bshell_cmdcall_p *cmdcall, + fx_value arg) +{ + fx_value *slot = fx_vector_emplace_back(cmdcall->cmd_args, NULL); + fx_value_copy(slot, &arg); + + return BSHELL_SUCCESS; +} + +static const fx_value *get_arg(struct bshell_cmdcall_p *cmdcall, size_t index) +{ + if (index >= cmdcall->cmd_args.count) { + return NULL; + } + + return &cmdcall->cmd_args.items[cmdcall->cmd_args.count - index - 1]; +} + +static bshell_command *find_call_target(const char *name) +{ +#if 0 +#define VERB_MAX 32 + const char *hyphen = strchr(name, '-'); + if (!hyphen) { + return NULL; + } + + const char *noun = hyphen + 1; + + size_t verb_length = hyphen - name; + if (verb_length >= VERB_MAX) { + return NULL; + } + + char verb[VERB_MAX] = {}; + memcpy(verb, name, verb_length); + for (size_t i = 0; i < verb_length; i++) { + verb[i] = tolower(verb[i]); + } + + bshell_verb *verb_info = bshell_verb_get_by_name(verb); + if (!verb_info) { + return NULL; + } + + enum bshell_verb_id verb_id = bshell_verb_get_id(verb_info); + fx_string *target_name = fx_string_create(); + fx_string *call_name = fx_string_create_from_cstr(name); + fx_string_transform_lowercase(call_name); + + const fx_assembly *self = bshell_assembly_get(); + fx_iterator *it = fx_assembly_get_types(self); + fx_foreach(v, it) + { + fx_type *ty = NULL; + fx_value_get_object(&v, &ty); + const char *ty_name = fx_type_get_name(ty); + bshell_command_class *cmd = fx_type_get_interface( + ty, + BSHELL_TYPE_COMMAND); + if (!cmd || !cmd->c_get_callable_name_static) { + continue; + } + + cmd->c_get_callable_name( + + if (fx_strcmp_nocase(cmdlet->c_noun, noun) != 0) { + continue; + } + + return fx_object_create(fx_type_get_id(ty)); + } + fx_iterator_unref(it); + +#undef VERB_MAX +#endif + return NULL; +} + +static bshell_command *__resolve_call_target( + const char *callable_name, + struct bshell_runtime *rt) +{ + return bshell_runtime_find_command(rt, callable_name); +} + +static enum bshell_status resolve_call_target( + struct bshell_cmdcall_p *cmdcall, + struct bshell_runtime *rt) +{ + if (cmdcall->cmd_type != CMDCALL_NONE) { + return BSHELL_SUCCESS; + } + + fx_value *cmd_name_v = fx_array_get_ref(cmdcall->cmd_args_processed, 0); + if (!cmd_name_v) { + return BSHELL_ERR_BAD_STATE; + } + + fx_string *cmd_name_s = NULL; + fx_value_get_object(cmd_name_v, &cmd_name_s); + + while (1) { + const char *cmd_name_cstr = fx_string_get_cstr(cmd_name_s); + bshell_command *cmd = bshell_runtime_find_command( + rt, + cmd_name_cstr); + if (!cmd) { + break; + } + + bshell_alias_class *alias = fx_object_get_interface( + cmd, + BSHELL_TYPE_ALIAS); + if (alias) { + fx_string_clear(cmd_name_s); + fx_string_append_cstr(cmd_name_s, alias->a_target_name); + bshell_command_unref(cmd); + continue; + } + + cmdcall->cmd_target = cmd; + cmdcall->cmd_type = CMDCALL_CMDLET; + return BSHELL_SUCCESS; + } + + const char *cmd_name_cstr = fx_string_get_cstr(cmd_name_s); + fx_printf( + "[bold,bright_red]%s: The term '%s' is not recognised as a " + "name of a cmdlet, " + "function, script file, or executable program.[reset]\n", + cmd_name_cstr, + cmd_name_cstr); + return BSHELL_ERR_NO_ENTRY; +} + +static enum bshell_status process_args(struct bshell_cmdcall_p *cmdcall) +{ + if (cmdcall->cmd_args_processed) { + return BSHELL_SUCCESS; + } + + fx_stringstream *str = fx_stringstream_create(); + if (!str) { + return BSHELL_ERR_NO_MEMORY; + } + + cmdcall->cmd_args_processed = fx_array_create(); + if (!cmdcall->cmd_args_processed) { + fx_stringstream_unref(str); + return BSHELL_ERR_NO_MEMORY; + } + + for (i32 i = cmdcall->cmd_args.count - 1; i >= 0; i--) { + fx_stringstream_reset(str); + fx_value_to_string(&cmdcall->cmd_args.items[i], str, NULL); + fx_string *arg_str = fx_string_create_from_cstr( + fx_stringstream_ptr(str)); + fx_array_push_back( + cmdcall->cmd_args_processed, + FX_VALUE_OBJECT(arg_str)); + fx_string_unref(arg_str); + } + + fx_stringstream_unref(str); + return BSHELL_SUCCESS; +} + +static enum bshell_status cmdcall_resolve( + struct bshell_cmdcall_p *cmdcall, + struct bshell_runtime *rt) +{ + enum bshell_status status = process_args(cmdcall); + if (status != BSHELL_SUCCESS) { + return status; + } + + status = resolve_call_target(cmdcall, rt); + if (status != BSHELL_SUCCESS) { + return status; + } + + bshell_command_set_args_reverse( + cmdcall->cmd_target, + cmdcall->cmd_args.items, + cmdcall->cmd_args.count); + + return BSHELL_SUCCESS; +} + +static enum bshell_status cmdcall_execute(struct bshell_cmdcall_p *cmdcall) +{ + return BSHELL_SUCCESS; +} + +static enum bshell_status cmdcall_process_record( + struct bshell_cmdcall_p *cmdcall, + bshell_pipeline *pipeline, + struct bshell_runtime *rt) +{ + if (!cmdcall->cmd_target) { + return BSHELL_ERR_NOT_SUPPORTED; + } + + if (!cmdcall->cmd_initialised) { + bshell_command_begin_processing(cmdcall->cmd_target); + cmdcall->cmd_initialised = true; + } + + return bshell_command_process_record(cmdcall->cmd_target, pipeline, rt); +} + +enum bshell_status bshell_cmdcall_push_arg( + bshell_cmdcall *cmdcall, + fx_value arg) +{ + FX_CLASS_DISPATCH_STATIC( + BSHELL_TYPE_CMDCALL, + cmdcall_push_arg, + cmdcall, + arg); +} + +enum bshell_status bshell_cmdcall_resolve( + bshell_cmdcall *cmdcall, + struct bshell_runtime *rt) +{ + FX_CLASS_DISPATCH_STATIC( + BSHELL_TYPE_CMDCALL, + cmdcall_resolve, + cmdcall, + rt); +} + +enum bshell_status bshell_cmdcall_execute(bshell_cmdcall *cmdcall) +{ + FX_CLASS_DISPATCH_STATIC_0( + BSHELL_TYPE_CMDCALL, + cmdcall_execute, + cmdcall); +} + +enum bshell_status bshell_cmdcall_process_record( + bshell_cmdcall *cmdcall, + bshell_pipeline *pipeline, + struct bshell_runtime *rt) +{ + FX_CLASS_DISPATCH_STATIC( + BSHELL_TYPE_CMDCALL, + cmdcall_process_record, + cmdcall, + pipeline, + rt); +} + +FX_TYPE_CLASS_BEGIN(bshell_cmdcall) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + + FX_TYPE_CONSTRUCTOR("create", bshell_cmdcall_create, 0, FX_TYPE_VOID); +FX_TYPE_CLASS_END(bshell_cmdcall) + +FX_TYPE_DEFINITION_BEGIN(bshell_cmdcall) + FX_TYPE_ID(0x08aeb275, 0xc09b, 0x4c3a, 0xa8df, 0xd9fba0bb4571); + FX_TYPE_NAME("bshell.cmdcall"); + FX_TYPE_CLASS(bshell_cmdcall_class); + FX_TYPE_INSTANCE_PRIVATE(struct bshell_cmdcall_p); + FX_TYPE_INSTANCE_FINI(cmdcall_fini); +FX_TYPE_DEFINITION_END(bshell_cmdcall) diff --git a/bshell/runtime/cmdcall.h b/bshell/runtime/cmdcall.h new file mode 100644 index 0000000..b3713a7 --- /dev/null +++ b/bshell/runtime/cmdcall.h @@ -0,0 +1,36 @@ +#ifndef RUNTIME_CMDCALL_H_ +#define RUNTIME_CMDCALL_H_ + +#include +#include + +FX_DECLS_BEGIN; + +struct bshell_runtime; + +#define BSHELL_TYPE_CMDCALL (bshell_cmdcall_get_type()) + +FX_DECLARE_TYPE(bshell_cmdcall); + +FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_cmdcall) +FX_TYPE_CLASS_DECLARATION_END(bshell_cmdcall) + +extern fx_type_id bshell_cmdcall_get_type(void); + +FX_TYPE_DEFAULT_CONSTRUCTOR(bshell_cmdcall, BSHELL_TYPE_CMDCALL); + +extern enum bshell_status bshell_cmdcall_push_arg( + bshell_cmdcall *cmdcall, + fx_value arg); + +extern enum bshell_status bshell_cmdcall_resolve( + bshell_cmdcall *cmdcall, + struct bshell_runtime *rt); +extern enum bshell_status bshell_cmdcall_process_record( + bshell_cmdcall *cmdcall, + FX_TYPE_FWDREF(bshell_pipeline) * pipeline, + struct bshell_runtime *rt); + +FX_DECLS_END; + +#endif diff --git a/bshell/runtime/pipeline.c b/bshell/runtime/pipeline.c new file mode 100644 index 0000000..324bded --- /dev/null +++ b/bshell/runtime/pipeline.c @@ -0,0 +1,277 @@ +#include "pipeline.h" + +#include "../status.h" +#include "cmdcall.h" + +#include +#include +#include +#include + +struct bshell_pipeline_p { + bshell_pipeline *p_self; + fx_array *p_in, *p_out; + FX_VECTOR_DECLARE(bshell_cmdcall *, p_cmds); +}; + +static void pipeline_init(fx_object *obj, void *priv) +{ + struct bshell_pipeline_p *pipeline = priv; + pipeline->p_self = obj; + pipeline->p_in = fx_array_create(); + pipeline->p_out = fx_array_create(); +} + +static void pipeline_fini(fx_object *obj, void *priv) +{ + struct bshell_pipeline_p *pipeline = priv; + for (size_t i = 0; i < pipeline->p_cmds.count; i++) { + bshell_cmdcall_unref(pipeline->p_cmds.items[i]); + } + + if (pipeline->p_in) { + fx_array_unref(pipeline->p_in); + pipeline->p_in = NULL; + } + + if (pipeline->p_out) { + fx_array_unref(pipeline->p_out); + pipeline->p_out = NULL; + } +} + +static enum bshell_status write_value( + struct bshell_pipeline_p *pipeline, + fx_array *dest, + fx_value value, + bool enumerate) +{ + if (!enumerate) { + fx_array_push_back(dest, value); + return BSHELL_SUCCESS; + } + + fx_object *container = NULL; + fx_value_get_object(&value, &container); + if (!container) { + fx_array_push_back(dest, value); + return BSHELL_SUCCESS; + } + + fx_iterator *it = fx_iterator_begin(container); + fx_foreach(v, it) + { + fx_array_push_back(dest, fx_value_copy_return(v)); + } + fx_iterator_unref(it); + + return BSHELL_SUCCESS; +} + +static enum bshell_status pipeline_add_input_value( + struct bshell_pipeline_p *pipeline, + fx_value value, + bool enumerate) +{ + return write_value(pipeline, pipeline->p_in, value, enumerate); +} + +static enum bshell_status pipeline_add_cmdcall( + struct bshell_pipeline_p *pipeline, + bshell_cmdcall *cmdcall) +{ + bshell_cmdcall **slot = fx_vector_emplace_back(pipeline->p_cmds, NULL); + *slot = bshell_cmdcall_ref(cmdcall); + return BSHELL_SUCCESS; +} + +static enum bshell_status pipeline_execute_single( + struct bshell_pipeline_p *pipeline) +{ + return BSHELL_ERR_NOT_SUPPORTED; +} + +static fx_value pipeline_read_value(struct bshell_pipeline_p *pipeline) +{ + return fx_array_pop_front(pipeline->p_in); +} + +static enum bshell_status pipeline_write_value( + struct bshell_pipeline_p *pipeline, + fx_value value, + bool enumerate) +{ + return write_value(pipeline, pipeline->p_out, value, enumerate); +} + +static enum bshell_status pipeline_pump_record( + struct bshell_pipeline_p *pipeline, + struct bshell_runtime *rt, + fx_value *result, + int *out_end_of_data) +{ + enum bshell_status status = BSHELL_SUCCESS; + if (fx_array_get_size(pipeline->p_out)) { + fx_value out = fx_array_pop_front(pipeline->p_out); + *result = out; + return BSHELL_SUCCESS; + } + + size_t i = 0; + for (i = 0; i < pipeline->p_cmds.count;) { + status = bshell_cmdcall_process_record( + pipeline->p_cmds.items[i], + pipeline->p_self, + rt); + + if (status != BSHELL_SUCCESS) { + break; + } + + bool input_available = fx_array_get_size(pipeline->p_in) != 0; + bool output_available = fx_array_get_size(pipeline->p_out) != 0; + + if (input_available) { + /* repeat this pipeline stage until all available input + * records are consumed */ + continue; + } + + if (!output_available) { + /* no output produced, and no more input available */ + break; + } + + if (i < pipeline->p_cmds.count - 1) { + fx_array *tmp = pipeline->p_in; + pipeline->p_in = pipeline->p_out; + pipeline->p_out = tmp; + } + i++; + } + + if (fx_array_get_size(pipeline->p_out)) { + fx_value out = fx_array_pop_front(pipeline->p_out); + *result = out; + return BSHELL_SUCCESS; + } else if (i == 0) { + *result = FX_VALUE_EMPTY; + *out_end_of_data = 1; + } + +#if 0 + size_t i = 0; + for (i = 0; i < pipeline->p_cmds.count; i++) { + status = bshell_command_process_record( + pipeline->p_cmds.items[i], + rt); + + if (status != BSHELL_SUCCESS) { + break; + } + + fx_array *tmp = pipeline->p_in; + pipeline->p_in = pipeline->p_out; + pipeline->p_out = tmp; + + if (fx_array_size(pipeline->p_in)) { + break; + } + } + + if (in->v_type) { + fx_value_copy(result, in); + } else if (i == 0) { + *out_end_of_data = 1; + } + + fx_value_unset(in); + fx_value_unset(out); +#endif + return status; +} + +enum bshell_status bshell_pipeline_add_input_value( + bshell_pipeline *pipeline, + fx_value value, + bool enumerate) +{ + FX_CLASS_DISPATCH_STATIC( + BSHELL_TYPE_PIPELINE, + pipeline_add_input_value, + pipeline, + value, + enumerate); +} + +enum bshell_status bshell_pipeline_add_cmdcall( + bshell_pipeline *pipeline, + bshell_cmdcall *cmd) +{ + FX_CLASS_DISPATCH_STATIC( + BSHELL_TYPE_PIPELINE, + pipeline_add_cmdcall, + pipeline, + cmd); +} + +enum bshell_status bshell_pipeline_execute_single(bshell_pipeline *pipeline) +{ + FX_CLASS_DISPATCH_STATIC_0( + BSHELL_TYPE_PIPELINE, + pipeline_execute_single, + pipeline); +} + +enum bshell_status bshell_pipeline_pump_record( + bshell_pipeline *pipeline, + struct bshell_runtime *rt, + fx_value *out, + int *out_end_of_data) +{ + FX_CLASS_DISPATCH_STATIC( + BSHELL_TYPE_PIPELINE, + pipeline_pump_record, + pipeline, + rt, + out, + out_end_of_data); +} + +fx_value bshell_pipeline_read_value(bshell_pipeline *pipeline) +{ + FX_CLASS_DISPATCH_STATIC_0( + BSHELL_TYPE_PIPELINE, + pipeline_read_value, + pipeline); +} + +enum bshell_status bshell_pipeline_write_value( + bshell_pipeline *pipeline, + fx_value val, + bool enumerate) +{ + FX_CLASS_DISPATCH_STATIC( + BSHELL_TYPE_PIPELINE, + pipeline_write_value, + pipeline, + val, + enumerate); +} + +FX_TYPE_CLASS_BEGIN(bshell_pipeline) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + + FX_TYPE_CONSTRUCTOR("create", bshell_pipeline_create, 0, FX_TYPE_VOID); +FX_TYPE_CLASS_END(bshell_pipeline) + +FX_TYPE_DEFINITION_BEGIN(bshell_pipeline) + FX_TYPE_ID(0x64159f21, 0xb1dd, 0x4838, 0xba62, 0x69fa65cc01d3); + FX_TYPE_NAME("bshell.pipeline"); + FX_TYPE_CLASS(bshell_pipeline_class); + FX_TYPE_INSTANCE_PRIVATE(struct bshell_pipeline_p); + FX_TYPE_INSTANCE_INIT(pipeline_init); + FX_TYPE_INSTANCE_FINI(pipeline_fini); +FX_TYPE_DEFINITION_END(bshell_pipeline) diff --git a/bshell/runtime/pipeline.h b/bshell/runtime/pipeline.h new file mode 100644 index 0000000..6c41244 --- /dev/null +++ b/bshell/runtime/pipeline.h @@ -0,0 +1,49 @@ +#ifndef RUNTIME_PIPELINE_H_ +#define RUNTIME_PIPELINE_H_ + +#include "../command/command.h" +#include "../runtime/cmdcall.h" +#include "../status.h" + +#include +#include + +FX_DECLS_BEGIN; + +struct bshell_runtime; + +#define BSHELL_TYPE_PIPELINE (bshell_pipeline_get_type()) + +FX_DECLARE_TYPE(bshell_pipeline); + +FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_pipeline) +FX_TYPE_CLASS_DECLARATION_END(bshell_pipeline) + +extern fx_type_id bshell_pipeline_get_type(void); + +FX_TYPE_DEFAULT_CONSTRUCTOR(bshell_pipeline, BSHELL_TYPE_PIPELINE); + +extern enum bshell_status bshell_pipeline_add_input_value( + bshell_pipeline *pipeline, + fx_value value, + bool enumerate); +extern enum bshell_status bshell_pipeline_add_cmdcall( + bshell_pipeline *pipeline, + bshell_cmdcall *cmd); +extern enum bshell_status bshell_pipeline_execute_single( + bshell_pipeline *pipeline); +extern enum bshell_status bshell_pipeline_pump_record( + bshell_pipeline *pipeline, + struct bshell_runtime *rt, + fx_value *out, + int *out_end_of_data); + +extern fx_value bshell_pipeline_read_value(bshell_pipeline *pipeline); +extern enum bshell_status bshell_pipeline_write_value( + bshell_pipeline *pipeline, + fx_value val, + bool enumerate); + +FX_DECLS_END; + +#endif