Compare commits

...

4 Commits

7 changed files with 478 additions and 158 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ if (NOT DEFINED bshell_enable_floating_point)
set(bshell_enable_floating_point 1) set(bshell_enable_floating_point 1)
endif () endif ()
set(required_fx_assemblies fx.runtime fx.collections fx.io fx.diagnostics) set(required_fx_assemblies fx.runtime fx.collections fx.io fx.diagnostics fx.cmdline)
if (bshell_interactive) if (bshell_interactive)
set(required_fx_assemblies ${required_fx_assemblies} fx.term) set(required_fx_assemblies ${required_fx_assemblies} fx.term)
-3
View File
@@ -91,7 +91,6 @@ static enum bshell_status begin_processing(
{ {
struct bshell_native_command_p *cmd_p struct bshell_native_command_p *cmd_p
= fx_object_get_private(cmd, BSHELL_TYPE_NATIVE_COMMAND); = fx_object_get_private(cmd, BSHELL_TYPE_NATIVE_COMMAND);
printf("begin processing '%s'\n", fx_string_get_cstr(cmd_p->cmd_path));
int flags = 0; int flags = 0;
switch (position) { switch (position) {
case BSHELL_COMMAND_POSITION_BEGINNING: case BSHELL_COMMAND_POSITION_BEGINNING:
@@ -117,7 +116,6 @@ static enum bshell_status process_record(
{ {
struct bshell_native_command_p *cmd_p struct bshell_native_command_p *cmd_p
= fx_object_get_private(cmd, BSHELL_TYPE_NATIVE_COMMAND); = fx_object_get_private(cmd, BSHELL_TYPE_NATIVE_COMMAND);
printf("process record '%s'\n", fx_string_get_cstr(cmd_p->cmd_path));
if (cmd_p->cmd_flags & F_REDIRECT_STDIN) { if (cmd_p->cmd_flags & F_REDIRECT_STDIN) {
fx_value in = bshell_pipeline_read_value(pipeline); fx_value in = bshell_pipeline_read_value(pipeline);
@@ -161,7 +159,6 @@ static enum bshell_status end_processing(bshell_command *cmd)
{ {
struct bshell_native_command_p *cmd_p struct bshell_native_command_p *cmd_p
= fx_object_get_private(cmd, BSHELL_TYPE_NATIVE_COMMAND); = fx_object_get_private(cmd, BSHELL_TYPE_NATIVE_COMMAND);
printf("end processing '%s'\n", fx_string_get_cstr(cmd_p->cmd_path));
if (cmd_p->cmd_writer_init) { if (cmd_p->cmd_writer_init) {
bshell_value_writer_cleanup(&cmd_p->cmd_writer); bshell_value_writer_cleanup(&cmd_p->cmd_writer);
cmd_p->cmd_writer_init = false; cmd_p->cmd_writer_init = false;
+19
View File
@@ -1,9 +1,23 @@
#include "scope.h" #include "scope.h"
#include <bshell/runtime/runtime.h> #include <bshell/runtime/runtime.h>
#include <bshell/verb.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
static fx_once global_once = FX_ONCE_INIT;
static void cleanup_globals(void)
{
bshell_cleanup_all_verbs();
}
static void init_globals(void)
{
bshell_init_all_verbs();
bshell_table_init();
}
struct bshell_runtime *bshell_runtime_create(void) struct bshell_runtime *bshell_runtime_create(void)
{ {
struct bshell_runtime *out = malloc(sizeof *out); struct bshell_runtime *out = malloc(sizeof *out);
@@ -16,6 +30,11 @@ struct bshell_runtime *bshell_runtime_create(void)
out->rt_global = runtime_push_scope(out, RUNTIME_SCOPE_GLOBAL, NULL); out->rt_global = runtime_push_scope(out, RUNTIME_SCOPE_GLOBAL, NULL);
out->rt_aliases = fx_hashtable_create(); out->rt_aliases = fx_hashtable_create();
if (fx_init_once(&global_once)) {
init_globals();
atexit(cleanup_globals);
}
return out; return out;
} }
+1 -1
View File
@@ -24,7 +24,7 @@ add_executable(bshell ${bshell_sources})
target_link_libraries(bshell target_link_libraries(bshell
bshell.core bshell.runtime bshell.core bshell.runtime
FX::Runtime FX::Collections) FX::Runtime FX::Collections FX::Cmdline)
if (bshell_interactive) if (bshell_interactive)
target_link_libraries(bshell FX::Term) target_link_libraries(bshell FX::Term)
+250
View File
@@ -0,0 +1,250 @@
#include "frontend.h"
#include "debug.h"
#include <bshell/ast.h>
#include <bshell/compile.h>
#include <bshell/file.h>
#include <bshell/runtime/runtime.h>
#include <fx/io/path.h>
#include <fx/reflection/assembly.h>
#if BSHELL_INTERACTIVE == 1
#include "line-ed/line-ed.h"
#endif
#define FRONTEND_FLAG_SET(f, flag) \
(((f)->ctx_flags & (FRONTEND_F_##flag)) == (FRONTEND_F_##flag))
enum eval_flags {
EVAL_PRINT_RESULT = 0x01u,
};
extern const fx_assembly *bshell_assembly_get(void);
extern const fx_assembly *bshell_runtime_assembly_get(void);
extern const fx_assembly *bshell_core_assembly_get(void);
static void lex_token_scanned(
struct bshell_lex_ctx *ctx,
struct bshell_lex_token *tok)
{
print_lex_token(tok);
}
void frontend_init(struct frontend_ctx *ctx, enum frontend_flags flags)
{
memset(ctx, 0x0, sizeof *ctx);
bshell_assembly_get();
bshell_runtime_assembly_get();
bshell_core_assembly_get();
ctx->ctx_flags = flags;
ctx->ctx_block = bshell_scriptblock_create();
ctx->ctx_rt = bshell_runtime_create();
}
static enum bshell_status lex_only(
struct frontend_ctx *ctx,
struct bshell_line_source *linesrc,
enum eval_flags flags)
{
while (1) {
struct bshell_lex_token *tok
= bshell_lex_ctx_peek(&ctx->ctx_lex);
if (!tok) {
break;
}
bshell_lex_ctx_discard(&ctx->ctx_lex);
}
return BSHELL_SUCCESS;
}
static enum bshell_status parse_only(
struct frontend_ctx *ctx,
struct bshell_line_source *linesrc,
enum eval_flags flags)
{
while (1) {
struct bshell_ast_node *node
= bshell_parse_ctx_read_node(&ctx->ctx_parse);
if (!node) {
break;
}
if (FRONTEND_FLAG_SET(ctx, PRINT_AST)) {
print_ast_node_recursive(node);
}
bshell_ast_node_destroy(node);
}
return BSHELL_SUCCESS;
}
static enum bshell_status eval(
struct frontend_ctx *ctx,
struct bshell_line_source *linesrc,
enum eval_flags flags)
{
enum bshell_status status = BSHELL_SUCCESS;
bshell_lex_ctx_init(&ctx->ctx_lex, 0, linesrc);
if (FRONTEND_FLAG_SET(ctx, PRINT_LEX)) {
ctx->ctx_lex.lex_token_scanned = lex_token_scanned;
}
if (!FRONTEND_FLAG_SET(ctx, ENABLE_PARSE)) {
status = lex_only(ctx, linesrc, flags);
bshell_lex_ctx_cleanup(&ctx->ctx_lex);
return status;
}
bshell_parse_ctx_init(&ctx->ctx_parse, &ctx->ctx_lex);
if (!FRONTEND_FLAG_SET(ctx, ENABLE_EVAL)) {
status = parse_only(ctx, linesrc, flags);
bshell_parse_ctx_cleanup(&ctx->ctx_parse);
bshell_lex_ctx_cleanup(&ctx->ctx_lex);
return status;
}
while (1) {
struct bshell_ast_node *node
= bshell_parse_ctx_read_node(&ctx->ctx_parse);
if (!node) {
break;
}
if (FRONTEND_FLAG_SET(ctx, PRINT_AST)) {
print_ast_node_recursive(node);
printf("----\n");
}
bshell_scriptblock_clear_text(ctx->ctx_block);
switch (node->n_type) {
case BSHELL_AST_FUNC: {
bshell_function *func = NULL;
bshell_compile_function(node, &func);
if (FRONTEND_FLAG_SET(ctx, PRINT_BYTECODE)) {
print_function(func);
}
bshell_runtime_define_function(ctx->ctx_rt, func);
bshell_function_unref(func);
break;
}
default: {
bshell_compile(node, ctx->ctx_block);
if (FRONTEND_FLAG_SET(ctx, PRINT_BYTECODE)) {
print_scriptblock(ctx->ctx_block, 0);
}
fx_value result = bshell_runtime_eval_global(
ctx->ctx_rt,
ctx->ctx_block);
bool print_result = flags & EVAL_PRINT_RESULT
&& result.v_type != NULL;
if (print_result) {
bshell_runtime_begin_output(ctx->ctx_rt);
bshell_runtime_output_value(
ctx->ctx_rt,
&result);
bshell_runtime_end_output(ctx->ctx_rt);
}
fx_value_unset(&result);
break;
}
}
bshell_ast_node_destroy(node);
}
bshell_parse_ctx_cleanup(&ctx->ctx_parse);
bshell_lex_ctx_cleanup(&ctx->ctx_lex);
return BSHELL_SUCCESS;
}
static fx_path *create_path(const char *dir_path, const char *script_path)
{
if (!script_path) {
return NULL;
}
fx_path *paths[2] = {0};
if (dir_path) {
paths[0] = fx_path_create_from_cstr(dir_path);
}
if (script_path) {
paths[1] = fx_path_create_from_cstr(script_path);
}
if (!dir_path) {
return paths[1];
}
fx_path *result = fx_path_join(
(const fx_path **)paths,
sizeof paths / sizeof paths[0]);
fx_path_unref(paths[0]);
fx_path_unref(paths[1]);
return result;
}
enum bshell_status frontend_eval_file(
struct frontend_ctx *ctx,
const char *dir_path,
const char *script_path)
{
fx_path *path = create_path(dir_path, script_path);
if (!path) {
return BSHELL_ERR_NO_MEMORY;
}
struct bshell_file *file = NULL;
ctx->ctx_status = bshell_file_open(fx_path_ptr(path), &file);
fx_path_unref(path);
if (!FX_OK(ctx->ctx_status)) {
return ctx->ctx_status;
}
struct bshell_line_source *linesrc = &file->f_base;
ctx->ctx_status = eval(ctx, linesrc, 0);
bshell_file_close(file);
return ctx->ctx_status;
}
enum bshell_status frontend_repl(struct frontend_ctx *ctx)
{
#if BSHELL_INTERACTIVE
struct line_ed *ed = line_ed_create();
struct bshell_line_source *linesrc = line_ed_to_line_source(ed);
ctx->ctx_status = eval(ctx, linesrc, EVAL_PRINT_RESULT);
line_ed_destroy(ed);
return ctx->ctx_status;
#else
return BSHELL_ERR_NOT_SUPPORTED;
#endif
}
void frontend_cleanup(struct frontend_ctx *ctx)
{
bshell_runtime_destroy(ctx->ctx_rt);
bshell_scriptblock_unref(ctx->ctx_block);
}
+48
View File
@@ -0,0 +1,48 @@
#ifndef FRONTEND_H_
#define FRONTEND_H_
#include <bshell/parse/lex.h>
#include <bshell/parse/parse.h>
#include <bshell/runtime/script-block.h>
#include <bshell/status.h>
struct line_ed;
struct bshell_file;
struct bshell_runtime;
#define FRONTEND_DEFAULT_FLAGS (FRONTEND_F_ENABLE_EVAL)
enum frontend_flags {
FRONTEND_F_ENABLE_LEX = 0x01u,
FRONTEND_F_ENABLE_PARSE = 0x03u,
FRONTEND_F_ENABLE_EVAL = 0x07u,
FRONTEND_F_PRINT_LEX = 0x10u,
FRONTEND_F_PRINT_AST = 0x20u,
FRONTEND_F_PRINT_BYTECODE = 0x40u,
FRONTEND_F_INTERACTIVE = 0x80u,
};
struct frontend_ctx {
enum bshell_status ctx_status;
enum frontend_flags ctx_flags;
struct bshell_lex_ctx ctx_lex;
struct bshell_parse_ctx ctx_parse;
struct bshell_runtime *ctx_rt;
bshell_scriptblock *ctx_block;
};
extern void frontend_init(struct frontend_ctx *ctx, enum frontend_flags flags);
extern enum bshell_status frontend_eval_file(
struct frontend_ctx *ctx,
const char *dir_path,
const char *script_path);
extern enum bshell_status frontend_repl(struct frontend_ctx *ctx);
extern void frontend_cleanup(struct frontend_ctx *ctx);
#endif
+159 -153
View File
@@ -1,4 +1,4 @@
#include "debug.h" #include "frontend.h"
#include <bshell/ast.h> #include <bshell/ast.h>
#include <bshell/compile.h> #include <bshell/compile.h>
@@ -7,177 +7,183 @@
#include <bshell/parse/lex.h> #include <bshell/parse/lex.h>
#include <bshell/parse/parse.h> #include <bshell/parse/parse.h>
#include <bshell/parse/token.h> #include <bshell/parse/token.h>
#include <bshell/runtime/runtime.h>
#include <bshell/verb.h> #include <bshell/verb.h>
#include <fx/cmdline/cmd.h>
#include <stdio.h> #include <stdio.h>
#if BSHELL_INTERACTIVE == 1 enum {
#include "line-ed/line-ed.h" CMD_ROOT,
#endif OPT_LEX,
OPT_PARSE,
OPT_EVAL,
OPT_PRINT_LEX,
OPT_PRINT_AST,
OPT_PRINT_BYTECODE,
ARG_SCRIPT_FILE,
};
extern const fx_assembly *bshell_assembly_get(void); #define FEATURE_FLAGS \
extern const fx_assembly *bshell_runtime_assembly_get(void); (FRONTEND_F_ENABLE_LEX | FRONTEND_F_ENABLE_PARSE \
extern const fx_assembly *bshell_core_assembly_get(void); | FRONTEND_F_ENABLE_EVAL)
#define PRINT_FLAGS \
(FRONTEND_F_PRINT_LEX | FRONTEND_F_PRINT_AST \
| FRONTEND_F_PRINT_BYTECODE)
static void print_token_queue(fx_queue *q) const char *exec_name = NULL;
static enum frontend_flags collect_frontend_flags(const fx_arglist *opt)
{ {
fx_queue_entry *cur = fx_queue_first(q); enum frontend_flags result = 0;
while (cur) { #define OPTION_IS_SET(id) \
struct bshell_lex_token *tok fx_arglist_get_count(opt, id, FX_COMMAND_INVALID_ID) > 0
= fx_unbox(struct bshell_lex_token, cur, tok_entry);
print_lex_token(tok); if (OPTION_IS_SET(OPT_LEX)) {
cur = fx_queue_next(cur); result |= FRONTEND_F_ENABLE_LEX;
} }
if (OPTION_IS_SET(OPT_PARSE)) {
result |= FRONTEND_F_ENABLE_PARSE;
}
if (OPTION_IS_SET(OPT_EVAL)) {
result |= FRONTEND_F_ENABLE_EVAL;
}
if (OPTION_IS_SET(OPT_PRINT_LEX)) {
result |= FRONTEND_F_PRINT_LEX;
}
if (OPTION_IS_SET(OPT_PRINT_AST)) {
result |= FRONTEND_F_PRINT_AST;
}
if (OPTION_IS_SET(OPT_PRINT_BYTECODE)) {
result |= FRONTEND_F_PRINT_BYTECODE;
}
if ((result & PRINT_FLAGS) && !(result & FEATURE_FLAGS)) {
result |= FRONTEND_F_ENABLE_EVAL;
}
#undef OPTION_IS_SET
return result;
} }
static void lex_only(struct bshell_lex_ctx *lex) static int bshell(
const fx_command *self,
const fx_arglist *opt,
const fx_array *args)
{ {
while (1) { enum frontend_flags flags = collect_frontend_flags(opt);
struct bshell_lex_token *tok = bshell_lex_ctx_peek(lex); if (flags == 0) {
if (!tok) { flags = FRONTEND_DEFAULT_FLAGS;
break; }
struct frontend_ctx ctx;
frontend_init(&ctx, flags);
enum bshell_status status = BSHELL_SUCCESS;
size_t nr_input_files = fx_arglist_get_count(
opt,
FX_COMMAND_INVALID_ID,
ARG_SCRIPT_FILE);
if (nr_input_files == 0) {
printf("B Shell " BSHELL_VERSION "\n");
frontend_repl(&ctx);
frontend_cleanup(&ctx);
return 0;
}
fx_arglist_iterator it;
fx_arglist_iterator_begin(
opt,
FX_COMMAND_INVALID_ID,
ARG_SCRIPT_FILE,
&it);
while (fx_arglist_iterator_is_valid(&it)) {
const char *script_path = it.value->val_str;
status = frontend_eval_file(&ctx, NULL, script_path);
if (status != BSHELL_SUCCESS) {
fprintf(stderr,
"%s: cannot open '%s': %s\n",
exec_name,
script_path,
bshell_status_get_description(status));
} }
bshell_lex_ctx_discard(lex); fx_arglist_iterator_next(&it);
} }
frontend_cleanup(&ctx);
return 0;
} }
static void lex_and_parse(struct bshell_parse_ctx *parse) FX_COMMAND(CMD_ROOT, FX_COMMAND_INVALID_ID)
{ {
} FX_COMMAND_NAME("bshell");
FX_COMMAND_DESC("B Shell script interpreter.");
FX_COMMAND_FUNCTION(bshell);
#if BSHELL_VERBOSE == 1 FX_COMMAND_OPTION(OPT_LEX)
static void lex_token_scanned( {
struct bshell_lex_ctx *ctx, FX_OPTION_LONG_NAME("lex");
struct bshell_lex_token *tok) FX_OPTION_SHORT_NAME('l');
{ FX_OPTION_DESC("Enable lexing of shell input.");
print_lex_token(tok); }
}
#endif
extern int compare_token_types(unsigned int a, unsigned int b); FX_COMMAND_OPTION(OPT_PARSE)
{
FX_OPTION_LONG_NAME("parse");
FX_OPTION_SHORT_NAME('a');
FX_OPTION_DESC(
"Enable parsing and ast generation of shell input.");
}
FX_COMMAND_OPTION(OPT_EVAL)
{
FX_OPTION_LONG_NAME("eval");
FX_OPTION_SHORT_NAME('b');
FX_OPTION_DESC(
"Enable evaluation and bytecode generation of shell "
"input. This option is the default if no 'enable' "
"flags are specified.");
}
FX_COMMAND_OPTION(OPT_PRINT_LEX)
{
FX_OPTION_LONG_NAME("print-lex");
FX_OPTION_SHORT_NAME('L');
FX_OPTION_DESC("Print lex tokens generated from shell input.");
}
FX_COMMAND_OPTION(OPT_PRINT_AST)
{
FX_OPTION_LONG_NAME("print-ast");
FX_OPTION_SHORT_NAME('A');
FX_OPTION_DESC("Print ast nodes generated from shell input.");
}
FX_COMMAND_OPTION(OPT_PRINT_BYTECODE)
{
FX_OPTION_LONG_NAME("print-bytecode");
FX_OPTION_SHORT_NAME('B');
FX_OPTION_DESC("Print bytecode generated from shell input.");
}
FX_COMMAND_ARG(ARG_SCRIPT_FILE)
{
FX_ARG_DESC("Script file to evaluate.");
FX_ARG_NR_VALUES(FX_ARG_0_OR_MORE_VALUES);
}
FX_COMMAND_HELP_OPTION();
}
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
bshell_assembly_get(); exec_name = argv[0];
bshell_runtime_assembly_get(); return fx_command_dispatch(CMD_ROOT, argc, argv);
bshell_core_assembly_get();
bshell_init_all_verbs();
bshell_table_init();
struct bshell_file *file = NULL;
struct line_ed *ed = NULL;
struct bshell_line_source *linesrc = NULL;
enum bshell_status status = BSHELL_SUCCESS;
bool interactive = true;
if (argc > 1) {
status = bshell_file_open(argv[1], &file);
linesrc = &file->f_base;
interactive = false;
if (!FX_OK(status)) {
fprintf(stderr,
"%s: cannot open '%s': %s\n",
argv[0],
argv[1],
bshell_status_get_description(status));
return -1;
}
} else {
#if BSHELL_INTERACTIVE == 1
ed = line_ed_create();
linesrc = line_ed_to_line_source(ed);
#else
fprintf(stderr, "Interactive mode is not supported.\n");
return -1;
#endif
}
struct bshell_lex_ctx lex = {0};
bshell_lex_ctx_init(&lex, 0, linesrc);
#if BSHELL_VERBOSE == 1
lex.lex_token_scanned = lex_token_scanned;
#endif
struct bshell_parse_ctx parse = {0};
bshell_parse_ctx_init(&parse, &lex);
#if 0
lex_only(&lex);
return 0;
#endif
if (interactive) {
printf("B Shell " BSHELL_VERSION "\n");
}
bshell_scriptblock *block = bshell_scriptblock_create();
struct bshell_runtime *rt = bshell_runtime_create();
while (1) {
struct bshell_ast_node *node
= bshell_parse_ctx_read_node(&parse);
if (!node) {
break;
}
#if BSHELL_VERBOSE == 1
print_ast_node_recursive(node);
printf("----\n");
#endif
bshell_scriptblock_clear_text(block);
switch (node->n_type) {
case BSHELL_AST_FUNC: {
bshell_function *func = NULL;
bshell_compile_function(node, &func);
#if BSHELL_VERBOSE == 1
print_function(func);
#endif
bshell_runtime_define_function(rt, func);
bshell_function_unref(func);
break;
}
default: {
bshell_compile(node, block);
#if BSHELL_VERBOSE == 1
print_scriptblock(block, 0);
#endif
fx_value result = bshell_runtime_eval_global(rt, block);
if (interactive && result.v_type != NULL) {
format_value_default(&result, fx_stdout);
}
fx_value_unset(&result);
break;
}
}
bshell_ast_node_destroy(node);
}
bshell_runtime_destroy(rt);
bshell_scriptblock_unref(block);
bshell_parse_ctx_cleanup(&parse);
bshell_lex_ctx_cleanup(&lex);
#if BSHELL_INTERACTIVE == 1
if (ed) {
line_ed_destroy(ed);
}
#endif
if (file) {
bshell_file_close(file);
}
bshell_cleanup_all_verbs();
return 0;
} }