#include "ansi_lib.h" #include #include #include #include #include #include #include #include #include "hydroforth/hydroforth.h" const char *argp_program_version = "hydroforth " HF__VERSION; const char *argp_program_bug_address = ""; static char doc[] = "Hydroforth is a minimal Forth interpreter written in C"; static char args_doc[] = "[SRC]"; static struct argp_option options[] = { {"debug", 'd', 0, 0, "Prints debug information"}, {"shell", 's', 0, 0, "Starts Forth interpreter shell"}, {"output", 'o', "FILE", 0, "Output to FILE instead of standard output"}, {0}, }; struct arguments { char *args[1]; bool debug; bool shell; char *output_file; }; static error_t parse_opt(int key, char *arg, struct argp_state *state) { struct arguments *arguments = state->input; switch (key) { case 'd': arguments->debug = true; break; case 's': arguments->shell = true; break; case 'o': arguments->output_file = arg; break; case ARGP_KEY_ARG: if (state->arg_num >= 1) /* Too many arguments. */ argp_usage(state); arguments->args[state->arg_num] = arg; break; case ARGP_KEY_END: // if (state->arg_num < 1) // /* Not enough arguments. */ // argp_usage(state); break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = {options, parse_opt, args_doc, doc}; bool read_src(FILE *fp, char **src, size_t *const len) { if (fseek(fp, 0L, SEEK_END) != 0) { fputs("Error seeking to file end!\n", stderr); return false; } const size_t bufsize = ftell(fp); if (bufsize == -1) { fputs("Error getting file size!\n", stderr); return false; } *len = bufsize != 0 ? bufsize - 1 : 0; *src = malloc(sizeof(char) * bufsize); if (fseek(fp, 0L, SEEK_SET) != 0) { fputs("Error rewinding file to start!\n", stderr); return false; } fread(*src, sizeof(char), bufsize, fp); if (ferror(fp) != 0) { fputs("Error reading file!\n", stderr); return false; } return true; } struct hf__result shell(const struct arguments *const arguments) { SET_8_VALUE_COLOUR(TXT_GREEN); printf("hydroforth@%s shell\n\n", HF__VERSION); SET_8_VALUE_COLOUR(TXT_DEFAULT); struct hf__parser parser = { .keyword_map = NULL, .keyword_map_is_init = false, }; struct hf__interpreter interpreter = { .call_stack = malloc(sizeof(struct hf__node) * 10), .call_stack_len = 0, .call_stack_cap = 10, .call_stack_cap_max = HF__INTERPRETER__CALL_STACK_CAP_MAX, .words = { .arr = calloc(HF__INTERPRETER__WORDS_CAP, sizeof(struct hf__hashmap__node *)), .cap = HF__INTERPRETER__WORDS_CAP, }, .stack = malloc(sizeof(int) * 10), .stack_len = 0, .stack_size = 10, .is_running = true, }; using_history(); char *input; while ((input = readline("hydroforth> ")) != NULL) { if (*input) { add_history(input); } size_t tokens_len = 0; size_t tokens_size = 0; struct hf__token *tokens = malloc(sizeof(struct hf__token) * tokens_size); hf__lex(input, strlen(input), &tokens, &tokens_len, &tokens_size); if (tokens_len == 0) { continue; } size_t nodes_len = 0; size_t nodes_size = 0; struct hf__node *nodes = NULL; struct hf__result parse_res = hf__parse(&parser, input, tokens, tokens_len, &nodes, &nodes_len, &nodes_size); free(tokens); free(input); if (!parse_res.ok) { hf__handle_error_light(&parse_res.error); putchar('\n'); continue; } for (size_t i = nodes_len - 1; i != 0 - 1; i--) { hf__parser__node_array_push(&interpreter.call_stack, &interpreter.call_stack_len, &interpreter.call_stack_cap, nodes[i]); } free(nodes); SET_8_VALUE_COLOUR(TXT_GREEN); printf("=> "); SET_8_VALUE_COLOUR(TXT_DEFAULT); while (interpreter.call_stack_len != 0 && interpreter.is_running) { struct hf__result res = hf__interpreter__run(&interpreter); if (!res.ok) { hf__handle_error_light(&res.error); putchar('\n'); } } putchar('\n'); if (interpreter.stack_len != 0) { printf("stack:"); SET_8_VALUE_COLOUR(TXT_YELLOW); for (size_t i = 0; i < interpreter.stack_len; i++) { printf(" %i", interpreter.stack[i]); } SET_8_VALUE_COLOUR(TXT_DEFAULT); putchar('\n'); } if (!interpreter.is_running) { SET_GRAPHIC_MODE(DIM_MODE); printf("\nexiting...\n"); RESET_GRAPHICS_MODES; break; } } // free(interpreter.call_stack); // free(interpreter.stack); hf__interpreter__free(&interpreter); return HF__OK; } int main(int argc, char *argv[]) { struct arguments arguments; arguments.args[0] = NULL; arguments.debug = false; arguments.shell = false; arguments.output_file = NULL; argp_parse(&argp, argc, argv, 0, 0, &arguments); int fd; if (arguments.output_file) { fd = open(arguments.output_file, O_WRONLY | O_CREAT, 0644); printf("fd = %i\n", fd); if (fd == -1) { perror("open failed"); return 1; } if (dup2(fd, 1) == -1) { perror("dup2 failed"); return 1; } } if (arguments.shell) { struct hf__result res = shell(&arguments); if (arguments.output_file) { close(fd); } if (!res.ok) { hf__handle_error_light(&res.error); putchar('\n'); return 1; } } else { FILE *fp = fopen(arguments.args[0], "r"); if (fp == NULL) { fputs("Error opening file!\n", stderr); return 1; } char *src; size_t src_len; const bool read_res = read_src(fp, &src, &src_len); fclose(fp); if (!read_res) { fputs("Error reading file!\n", stderr); return 1; } struct hf__parser parser = { .keyword_map = NULL, .keyword_map_is_init = false, }; struct hf__interpreter interpreter = { .call_stack = malloc(sizeof(struct hf__node) * 10), .call_stack_len = 0, .call_stack_cap = 10, .call_stack_cap_max = HF__INTERPRETER__CALL_STACK_CAP_MAX, .words = { .arr = calloc(HF__INTERPRETER__WORDS_CAP, sizeof(struct hf__hashmap__node *)), .cap = HF__INTERPRETER__WORDS_CAP, }, .stack = malloc(sizeof(int) * 10), .stack_len = 0, .stack_size = 10, .is_running = true, }; size_t tokens_len = 0; size_t tokens_size = 0; struct hf__token *tokens = malloc(sizeof(struct hf__token) * tokens_size); hf__lex(src, src_len, &tokens, &tokens_len, &tokens_size); size_t nodes_len = 0; size_t nodes_size = 0; struct hf__node *nodes = NULL; struct hf__result parse_res = hf__parse(&parser, src, tokens, tokens_len, &nodes, &nodes_len, &nodes_size); if (!parse_res.ok) { hf__handle_error_light(&parse_res.error); putchar('\n'); return 1; } free(tokens); free(src); for (size_t i = nodes_len - 1; i != 0 - 1; i--) { hf__parser__node_array_push(&interpreter.call_stack, &interpreter.call_stack_len, &interpreter.call_stack_cap, nodes[i]); } free(nodes); bool err = false; while (interpreter.call_stack_len != 0 && interpreter.is_running) { struct hf__result res = hf__interpreter__run(&interpreter); if (!res.ok) { hf__handle_error_light(&res.error); putchar('\n'); if (arguments.debug) { SET_8_VALUE_COLOUR(TXT_RED); puts("\n=== DEBUG ==="); SET_8_VALUE_COLOUR(TXT_DEFAULT); printf("stack:"); SET_8_VALUE_COLOUR(TXT_YELLOW); for (size_t i = 0; i < interpreter.stack_len; i++) { printf(" %i", interpreter.stack[i]); } SET_8_VALUE_COLOUR(TXT_DEFAULT); putchar('\n'); } err = true; break; } } if (parser.keyword_map_is_init) { hf__hashmap__free(&parser.keyword_map, NULL); } // free(interpreter.call_stack); // free(interpreter.stack); hf__interpreter__free(&interpreter); if (arguments.output_file) { close(fd); } if (err) { return 1; } else if (!interpreter.is_running) { return interpreter.exit_code; } } return 0; }