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.
188 lines
4.2 KiB
C
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;
|
|
}
|