From 213501fe6622f7a484360177568e76603aa1e1a1 Mon Sep 17 00:00:00 2001 From: cozis Date: Sat, 5 Mar 2022 15:12:50 +0100 Subject: [PATCH] added evalf and reorganized main --- build.sh | 1 + samples/generated_000.noja | 25 ++++ samples/test.noja | 1 + src/eval.c | 18 +++ src/eval.h | 3 +- src/main.c | 25 +--- src/noja.c | 260 +++++++++++++++++++++++++++++++++++++ src/noja.h | 2 + 8 files changed, 316 insertions(+), 19 deletions(-) create mode 100644 samples/generated_000.noja create mode 100644 samples/test.noja create mode 100644 src/noja.c create mode 100644 src/noja.h diff --git a/build.sh b/build.sh index 1f26cea..d267ea6 100755 --- a/build.sh +++ b/build.sh @@ -72,6 +72,7 @@ gcc tests/src/test-parse.c -o build/test-parse $FLAGS -Lbuild/ -lnoja-compil gcc tests/src/test-objects.c -o build/test-objects $FLAGS -Lbuild/ -lnoja-objects gcc src/main.c \ + src/noja.c \ src/debug.c \ src/eval.c \ src/buildvalue.c \ diff --git a/samples/generated_000.noja b/samples/generated_000.noja new file mode 100644 index 0000000..76433f6 --- /dev/null +++ b/samples/generated_000.noja @@ -0,0 +1,25 @@ +{ + fun Apple(Apple, Apple, Apple) + while 13: + return 26.7; + + return "Hello, world!"; +} +do + while 46.9: + do + fun Apple() + {} + while {67.97: "Hello, world!", 50.36: "Hello, world!", 54.91: 9}; + +while 26; +[{90: "Hello, world!"}, {14: 56.96, 16.24: 23, []: [20.31, 41.3, 33.49]}, 44.62]; +fun Apple(Apple, Apple, Apple) while {[]: 88.42}: do return 57.21; while []; +do if []: if {"Hello, world!": "Hello, world!", 0.65: 13.22, "Hello, world!": 61.3}: fun Apple(Apple, Apple, Apple) {} while 3.15; +return 41; +["Hello, world!", 34.8]; +return {34.81: {}}; +do return "Hello, world!"; while [[[12.36, "Hello, world!"], 98, [48.22, 29.87, "Hello, world!"]], [{10.78: 32.44}], "Hello, world!"]; +do {return 99.31;fun Apple(Apple, Apple) if "Hello, world!": {}} while "Hello, world!"; +return 38.57; +return {67: {"Hello, world!": 93, 5: 84.14, 9: [83.73, 65.86, 47.73]}, "Hello, world!": 40}; diff --git a/samples/test.noja b/samples/test.noja new file mode 100644 index 0000000..4b3ec3a --- /dev/null +++ b/samples/test.noja @@ -0,0 +1 @@ +'hello' + ' world!'; diff --git a/src/eval.c b/src/eval.c index d83d2d5..f32adf2 100644 --- a/src/eval.c +++ b/src/eval.c @@ -5,6 +5,24 @@ #include "o_builtins.h" #include "eval.h" +Object *evalf(Object *closure, Heap *heap, Error *error, const char *fmt, ...) +{ + char buffer[1024]; + + va_list va; + va_start(va, fmt); + int n = vsnprintf(buffer, sizeof(buffer), fmt, va); + va_end(va); + + if(n >= (int) sizeof(buffer)) + { + Error_Report(error, 1, "Static buffer is too small"); + return NULL; + } + + return eval(buffer, n, closure, heap, error); +} + Object *eval(const char *str, int len, Object *closure, Heap *heap, Error *error) { if(len < 0) diff --git a/src/eval.h b/src/eval.h index 6fef22f..a21fe37 100644 --- a/src/eval.h +++ b/src/eval.h @@ -1,3 +1,4 @@ #include "utils/error.h" #include "objects/objects.h" -Object *eval(const char *str, int len, Object *closure, Heap *heap, Error *error); \ No newline at end of file +Object *eval(const char *str, int len, Object *closure, Heap *heap, Error *error); +Object *evalf(Object *closure, Heap *heap, Error *error, const char *fmt, ...); diff --git a/src/main.c b/src/main.c index c291510..ef36da1 100644 --- a/src/main.c +++ b/src/main.c @@ -1,20 +1,5 @@ -#include -#include -#include -#include -#include "debug.h" -#include "o_builtins.h" -#include "compiler/parse.h" -#include "compiler/serialize.h" -#include "compiler/compile.h" -#include "runtime/runtime.h" -#include "runtime/runtime_error.h" - -/* $ noja -f - * $ noja -c - * $ noja -f -o - */ - +#include "noja.h" +/* typedef enum { RUN, HELP, PARSE, VERSION, DISASSEMBLY } Action; typedef struct { @@ -102,9 +87,12 @@ static int parse_args(Options *opts, int argc, char **argv, Error *error) return i; } +*/ int main(int argc, char **argv) { + return noja(argc, argv) ? 0 : -1; + /* Error error; Error_Init(&error); @@ -217,7 +205,7 @@ int main(int argc, char **argv) RuntimeError error; RuntimeError_Init(&error, runtime); - + Object *builtins = Object_NewBuiltinsMap(runtime, Runtime_GetHeap(runtime), (Error*) &error); if(builtins == NULL) @@ -407,5 +395,6 @@ int main(int argc, char **argv) } return 0; + */ } diff --git a/src/noja.c b/src/noja.c new file mode 100644 index 0000000..43d9832 --- /dev/null +++ b/src/noja.c @@ -0,0 +1,260 @@ +#include +#include +#include +#include +#include "debug.h" +#include "o_builtins.h" +#include "compiler/parse.h" +#include "compiler/serialize.h" +#include "compiler/compile.h" +#include "runtime/runtime.h" +#include "runtime/runtime_error.h" + +static const char usage[] = + "Usage:\n" + " $ noja [ | -f | -c | -h ]\n" + "\n" + "For example:\n" + " $ noja file.noja\n" + " $ noja -f file.noja\n" + " $ noja -c \"print('some noja code')\"\n" + "\n" + "NOTE: When a line starts with $ it means that it's a terminal command.\n"; + +static _Bool interpret_file(const char *file); +static _Bool interpret_code(const char *code); + +_Bool noja(int argc, char **argv) +{ + assert(argc > 0); + + if(argc == 1) + { + // $ noja + fprintf(stderr, "Error: Incorrect usage.\n\n"); + fprintf(stderr, usage); + return 0; + } + + assert(argc > 1); + + if(!strcmp(argv[1], "-f")) + { + if(argc < 3) + { + fprintf(stderr, "Error: Missing file after -f option.\n"); + return 0; + } + + + if(argc > 3) + fprintf(stderr, "Warning: Ignoring %d options\n", argc - 3); + + const char *file = argv[2]; + return interpret_file(file); + } + + if(!strcmp(argv[1], "-c")) + { + if(argc < 3) + { + fprintf(stderr, "Error: Missing code after -c option.\n"); + return 0; + } + + if(argc > 3) + fprintf(stderr, "Warning: Ignoring %d options\n", argc - 3); + + const char *code = argv[2]; + return interpret_code(code); + } + + if(!strcmp(argv[1], "-h")) + { + if(argc > 2) + fprintf(stderr, "Warning: Ignoring %d options\n", argc - 2); + + fprintf(stdout, usage); + return 1; + } + + if(argc > 2) + fprintf(stderr, "Warning: Ignoring %d options\n", argc - 2); + + const char *file = argv[1]; + return interpret_file(file); +} + +static void print_error(const char *type, Error *error) +{ + if(type == NULL) + fprintf(stderr, "Error"); + else if(error->internal) + fprintf(stderr, "Internal Error"); + else + fprintf(stderr, "%s Error", type); + + fprintf(stderr, ": %s. ", error->message); + + if(error->file != NULL) + { + if(error->line > 0 && error->func != NULL) + fprintf(stderr, "(Reported in %s:%d in %s)", error->file, error->line, error->func); + else if(error->line > 0 && error->func == NULL) + fprintf(stderr, "(Reported in %s:%d)", error->file, error->line); + else if(error->line < 1 && error->func != NULL) + fprintf(stderr, "(Reported in %s in %s)", error->file, error->func); + } + + fprintf(stderr, "\n"); +} + +static _Bool interpret(Source *src) +{ + // Compile the code. This section transforms + // a [Source] into an [Executable]. + Executable *exe; + { + // Create a bump-pointer allocator to hold the AST. + BPAlloc *alloc = BPAlloc_Init(-1); + + if(alloc == NULL) + { + fprintf(stderr, "Internal Error: Couldn't allocate bump-pointer allocator to hold the AST.\n"); + return 0; + } + + Error error; + Error_Init(&error); + + // NOTE: The AST is stored in the BPAlloc. It's + // lifetime is the same as the pool. + AST *ast = parse(src, alloc, &error); + + if(ast == NULL) + { + assert(error.occurred); + print_error("Parsing", &error); + Error_Free(&error); + BPAlloc_Free(alloc); + return 0; + } + + exe = compile(ast, alloc, &error); + + // We're done with the AST, independently from + // the compilation result. + BPAlloc_Free(alloc); + + if(exe == NULL) + { + assert(error.occurred); + print_error("Compilation", &error); + Error_Free(&error); + return 0; + } + } + + // Now execute it. + { + Runtime *runt = Runtime_New(-1, -1, NULL, NULL); + + if(runt == NULL) + { + Error error; + Error_Init(&error); + Error_Report(&error, 1, "Couldn't initialize runtime"); + print_error(NULL, &error); + Error_Free(&error); + Executable_Free(exe); + return 0; + } + + // We use a [RuntimeError] instead of a simple [Error] + // because the [RuntimeError] makes a snapshot of the + // runtime state when an error is reported. Other than + // this fact they are interchangable. Any function that + // expects a pointer to [Error] can receive a [RuntimeError] + // upcasted to [Error]. + RuntimeError error; + RuntimeError_Init(&error, runt); // Here we specify the runtime to snapshot in case of failure. + + Object *bins = Object_NewBuiltinsMap(runt, Runtime_GetHeap(runt), (Error*) &error); + + if(bins == NULL) + { + assert(error.base.occurred == 1); + print_error(NULL, (Error*) &error); + RuntimeError_Free(&error); + Executable_Free(exe); + Runtime_Free(runt); + return 0; + } + + Runtime_SetBuiltins(runt, bins); + + Object *o = run(runt, (Error*) &error, exe, 0, NULL, NULL, 0); + + // NOTE: The pointer to the builtins object is invalidated + // now because it may be moved by the garbage collector. + + if(o == NULL) + { + print_error("Runtime", (Error*) &error); + + if(error.snapshot == NULL) + fprintf(stderr, "No snapshot available.\n"); + else + Snapshot_Print(error.snapshot, stderr); + + RuntimeError_Free(&error); + } + + Runtime_Free(runt); + Executable_Free(exe); + + return o != NULL; + } +} + +static _Bool interpret_file(const char *file) +{ + Error error; + Error_Init(&error); + + Source *src = Source_FromFile(file, &error); + + if(src == NULL) + { + assert(error.occurred == 1); + print_error(NULL, &error); + Error_Free(&error); + return 0; + } + + _Bool r = interpret(src); + + Source_Free(src); + return r; +} + +static _Bool interpret_code(const char *code) +{ + Error error; + Error_Init(&error); + + Source *src = Source_FromString(NULL, code, -1, &error); + + if(src == NULL) + { + assert(error.occurred); + print_error(NULL, &error); + Error_Free(&error); + return 0; + } + + _Bool r = interpret(src); + + Source_Free(src); + return r; +} \ No newline at end of file diff --git a/src/noja.h b/src/noja.h new file mode 100644 index 0000000..e241194 --- /dev/null +++ b/src/noja.h @@ -0,0 +1,2 @@ + +_Bool noja(int argc, char **argv); \ No newline at end of file