hydroforth/src/main.c

349 lines
8.6 KiB
C

#include "ansi_lib.h"
#include <argp.h>
#include <fcntl.h>
#include <readline/history.h>
#include <readline/readline.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "hydroforth/hydroforth.h"
const char *argp_program_version = "hydroforth " HF__VERSION;
const char *argp_program_bug_address = "<dominic@dergrimm.net>";
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;
}