349 lines
8.6 KiB
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;
|
|
}
|