Complete rewrite
This commit is contained in:
parent
115dc530e1
commit
cd77044bf5
24 changed files with 4650 additions and 836 deletions
405
src/main.c
405
src/main.c
|
@ -1,92 +1,345 @@
|
|||
#include <stdio.h>
|
||||
#include "ansi_lib.h"
|
||||
#include <argp.h>
|
||||
#include <fcntl.h>
|
||||
#include <readline/history.h>
|
||||
#include <readline/readline.h>
|
||||
#include <stdbool.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "hydroforth/hydroforth.h"
|
||||
|
||||
struct ReadSrcResult
|
||||
{
|
||||
bool success;
|
||||
char *const src;
|
||||
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 ReadSrcResult read_src(FILE *fp)
|
||||
{
|
||||
if (fseek(fp, 0L, SEEK_END) != 0)
|
||||
{
|
||||
fputs("Error seeking to file end!\n", stderr);
|
||||
return (struct ReadSrcResult){
|
||||
.success = false,
|
||||
};
|
||||
}
|
||||
const long bufsize = ftell(fp);
|
||||
if (bufsize == -1)
|
||||
{
|
||||
fputs("Error getting file size!\n", stderr);
|
||||
return (struct ReadSrcResult){
|
||||
.success = false,
|
||||
};
|
||||
}
|
||||
char *const src = malloc(sizeof(char) * bufsize);
|
||||
if (fseek(fp, 0L, SEEK_SET) != 0)
|
||||
{
|
||||
fputs("Error rewinding file to start!\n", stderr);
|
||||
return (struct ReadSrcResult){
|
||||
.success = false,
|
||||
};
|
||||
}
|
||||
fread(src, sizeof(char), bufsize, fp);
|
||||
if (ferror(fp) != 0)
|
||||
{
|
||||
fputs("Error reading file!\n", stderr);
|
||||
return (struct ReadSrcResult){
|
||||
.success = false,
|
||||
};
|
||||
}
|
||||
struct arguments {
|
||||
char *args[1];
|
||||
bool debug;
|
||||
bool shell;
|
||||
char *output_file;
|
||||
};
|
||||
|
||||
return (struct ReadSrcResult){
|
||||
.success = true,
|
||||
.src = src,
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
fputs("No source file specified!\n", stderr);
|
||||
return 1;
|
||||
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;
|
||||
}
|
||||
FILE *fp = fopen(argv[1], "r");
|
||||
if (fp == NULL)
|
||||
{
|
||||
fputs("Error opening file!\n", stderr);
|
||||
return 1;
|
||||
const size_t bufsize = ftell(fp);
|
||||
if (bufsize == -1) {
|
||||
fputs("Error getting file size!\n", stderr);
|
||||
return false;
|
||||
}
|
||||
const struct ReadSrcResult res = read_src(fp);
|
||||
fclose(fp);
|
||||
if (res.success)
|
||||
{
|
||||
HYDROFORTH__INTERPRETER interpreter = {
|
||||
.src = res.src,
|
||||
.pos = 0,
|
||||
.single_char_word_keys_len = 0,
|
||||
.word_keys_len = 0,
|
||||
.word_definitions_len = 0,
|
||||
};
|
||||
HYDROFORTH__RESULT result = {.error = OK, .backtrace_len = 0};
|
||||
hydroforth.run(&result, &interpreter);
|
||||
free(interpreter.src);
|
||||
if (result.error)
|
||||
{
|
||||
hydroforth__add_func_backtrace(&result);
|
||||
return hydroforth.result.unwrap(&result, 1);
|
||||
*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_size = 10,
|
||||
|
||||
.words =
|
||||
{
|
||||
.arr = calloc(HF__INTERPRETER__WORDS_CAP,
|
||||
sizeof(struct hf__hashmap__node *)),
|
||||
.cap = HF__INTERPRETER__WORDS_CAP,
|
||||
},
|
||||
|
||||
.stack = malloc(sizeof(long) * 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__print_error(&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_size, 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__print_error(&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(" %li", 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;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
free(res.src);
|
||||
return 1;
|
||||
|
||||
// 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__print_error(&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_size = 10,
|
||||
|
||||
.words =
|
||||
{
|
||||
.arr = calloc(HF__INTERPRETER__WORDS_CAP,
|
||||
sizeof(struct hf__hashmap__node *)),
|
||||
.cap = HF__INTERPRETER__WORDS_CAP,
|
||||
},
|
||||
|
||||
.stack = malloc(sizeof(long) * 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__print_error(&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_size, 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__print_error(&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(" %li", 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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue