apfl/src/main.c
Laria Carolin Chabowski 21efc85dba Convert to Lua-style stack API
Only for evaluating expressions for now and right now the only exposed
operation is to debug print a value on the stack, this obviously needs to
be expanded.

I've done this for two reasons:

1. A Lua-style stack API is much nicer to work with than to manually manage
   refcounts.
2. We'll soon need a more sophisticated garbage collector (if you even want
   to count the refcounting as garbage collection). For this, the GC will
   need root objects to start tracing for live objects, the stack will be
   one of these roots.
2022-01-20 22:45:09 +01:00

188 lines
4.2 KiB
C

#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "apfl.h"
enum repl_mode {
REPL_TOKENIZER,
REPL_PARSER,
REPL_EVAL,
};
static bool
repl_source_reader(void *context, char *buf, size_t *len, bool need)
{
(void)context;
printf(need ? "... " : "> ");
fflush(stdout);
size_t maxlen = *len;
if (fgets(buf, maxlen, stdin) == NULL) {
if (feof(stdin)) {
*len = 0;
return true;
} else {
return false;
}
}
*len = strlen(buf);
return true;
}
static int
repl_tokenizer(apfl_tokenizer_ptr tokenizer)
{
while (true) {
struct apfl_error err;
struct apfl_token token;
switch (apfl_tokenizer_next(tokenizer, false)) {
case APFL_PARSE_OK:
token = apfl_tokenizer_get_token(tokenizer);
apfl_token_print(token, stdout);
apfl_token_deinit(&token);
break;
case APFL_PARSE_EOF:
return 0;
case APFL_PARSE_ERROR:
err = apfl_tokenizer_get_error(tokenizer);
apfl_error_print(err, stderr);
if (APFL_ERROR_IS_FATAL(err)) {
return 1;
}
break;
}
}
}
static bool
repl_parser_generic(apfl_parser_ptr parser, struct apfl_expr *expr, int *rv)
{
while (true) {
struct apfl_error err;
switch (apfl_parser_next(parser)) {
case APFL_PARSE_OK:
*expr = apfl_parser_get_expr(parser);
return true;
case APFL_PARSE_EOF:
*rv = 0;
return false;
case APFL_PARSE_ERROR:
err = apfl_parser_get_error(parser);
apfl_error_print(err, stderr);
if (APFL_ERROR_IS_FATAL(err)) {
*rv = 1;
return false;
}
break;
}
}
}
static int
repl_parser(apfl_parser_ptr parser)
{
struct apfl_expr expr;
int rv;
while (repl_parser_generic(parser, &expr, &rv)) {
apfl_expr_print(expr, stdout);
apfl_expr_deinit(&expr);
}
return rv;
}
static int
repl_eval(apfl_parser_ptr parser, apfl_ctx ctx)
{
struct apfl_expr expr;
int rv;
while (repl_parser_generic(parser, &expr, &rv)) {
switch (apfl_eval(ctx, expr)) {
case APFL_RESULT_OK:
apfl_debug_print_val(ctx, -1, stdout);
break;
case APFL_RESULT_ERR:
fprintf(stderr, "Error occurred during evaluation.\n");
break;
case APFL_RESULT_ERR_FATAL:
fprintf(stderr, "Fatal error occurred during evaluation.\n");
return 1;
}
}
return rv;
}
int
main(int argc, const char **argv)
{
int rv = 0;
enum repl_mode mode = REPL_PARSER;
if (argc > 1) {
if (apfl_string_eq(argv[1], "tokenizer")) {
mode = REPL_TOKENIZER;
} else if (apfl_string_eq(argv[1], "parser")) {
mode = REPL_PARSER;
} else if (apfl_string_eq(argv[1], "eval")) {
mode = REPL_EVAL;
} else {
fprintf(stderr, "Unknown mode: %s\n", argv[1]);
return 1;
}
}
apfl_tokenizer_ptr tokenizer = NULL;
apfl_parser_ptr parser = NULL;
apfl_ctx ctx = NULL;
if ((tokenizer = apfl_tokenizer_new(repl_source_reader, NULL)) == NULL) {
fprintf(stderr, "Failed initializing tokenizer\n");
rv = 1;
goto exit;
}
if (mode >= REPL_PARSER) {
if ((parser = apfl_parser_new(apfl_tokenizer_as_token_source(tokenizer))) == NULL) {
fprintf(stderr, "Failed initializing parser\n");
rv = 1;
goto exit;
}
}
if (mode >= REPL_EVAL) {
if ((ctx = apfl_ctx_new()) == NULL) {
fprintf(stderr, "Failed initializing context\n");
rv = 1;
goto exit;
}
}
switch (mode) {
case REPL_TOKENIZER:
rv = repl_tokenizer(tokenizer);
break;
case REPL_PARSER:
rv = repl_parser(parser);
break;
case REPL_EVAL:
rv = repl_eval(parser, ctx);
break;
}
exit:
apfl_tokenizer_destroy(tokenizer);
apfl_parser_destroy(parser);
apfl_ctx_destroy(ctx);
return rv;
}