diff --git a/build.sh b/build.sh index 82b6038..1f26cea 100755 --- a/build.sh +++ b/build.sh @@ -73,6 +73,8 @@ gcc tests/src/test-objects.c -o build/test-objects $FLAGS -Lbuild/ -lnoja-object gcc src/main.c \ src/debug.c \ + src/eval.c \ + src/buildvalue.c \ src/heap_reconst.c \ temp/o_builtins.o \ temp/o_net_builtins.o \ diff --git a/src/buildvalue.c b/src/buildvalue.c new file mode 100644 index 0000000..dff09b7 --- /dev/null +++ b/src/buildvalue.c @@ -0,0 +1,294 @@ +#include +#include +#include +#include +#include "buildvalue.h" +#include "eval.h" + +static Object *walkValue(const char *fmt, int *i, va_list va, Heap *heap, Error *error); + +static Object *walkMapValue(const char *fmt, int *i, va_list va, Heap *heap, Error *error) +{ + assert(fmt[*i] == '{'); + + *i += 1; // Skip the '{'. + + while(isspace(fmt[*i])) + *i += 1; + + Object *map = Object_NewMap(-1, heap, error); + + if(map == NULL) + return NULL; + + if(fmt[*i] != '}') + while(1) + { + Object *key = walkValue(fmt, i, va, heap, error); + + if(key == NULL) + return NULL; + + while(isspace(fmt[*i])) + *i += 1; + + if(fmt[*i] == '\0') + { + Error_Report(error, 0, "Format ended inside a map expression"); + return NULL; + } + + if(fmt[*i] != ':') + { + Error_Report(error, 0, "Got unexpected '%c' in format where ':' was expected", fmt[*i]); + return NULL; + } + + *i += 1; // Skip the ':'. + + Object *val = walkValue(fmt, i, va, heap, error); + + if(val == NULL) + return NULL; + + if(!Object_Insert(map, key, val, heap, error)) + return NULL; + + while(isspace(fmt[*i])) + *i += 1; + + if(fmt[*i] == '\0') + { + Error_Report(error, 0, "Format ended inside a map expression"); + return NULL; + } + + if(fmt[*i] == '}') + break; + + if(fmt[*i] != ',') + { + Error_Report(error, 0, "Got unexpected '%c' in format where either ',' or '}' were expected", fmt[*i]); + return NULL; + } + + *i += 1; // Skip the ','. + } + + assert(fmt[*i] == '}'); + + *i += 1; // Skip the '}'. + + return map; +} +static Object *walkListValue(const char *fmt, int *i, va_list va, Heap *heap, Error *error) +{ + assert(fmt[*i] == '['); + + *i += 1; // Skip the '['. + + while(isspace(fmt[*i])) + *i += 1; + + Object *maybe[8], **ptrs = maybe; + int ptrs_cap = sizeof(maybe) / sizeof(maybe[0]), + ptrs_num = 0; + + if(fmt[*i] != ']') + while(1) + { + Object *item = walkValue(fmt, i, va, heap, error); + + if(item == NULL) + { + if(ptrs != maybe) free(ptrs); + return NULL; + } + + if(ptrs_cap == ptrs_num) + { + int new_capacity = ptrs_cap * 2; + + Object **temp; + + if(ptrs == maybe) + { + temp = malloc(sizeof(Object*) * new_capacity); + + if(temp != NULL) + memcpy(temp, ptrs, ptrs_num * sizeof(Object*)); + } + else + temp = realloc(ptrs, sizeof(Object*) * new_capacity); + + if(temp == NULL) + { + Error_Report(error, 1, "Insufficient memory"); + if(ptrs != maybe) free(ptrs); + return NULL; + } + + ptrs = temp; + ptrs_cap = new_capacity; + } + + ptrs[ptrs_num++] = item; + + while(isspace(fmt[*i])) + *i += 1; + + if(fmt[*i] == '\0') + { + Error_Report(error, 0, "Format ended inside a list expression"); + if(ptrs != maybe) free(ptrs); + return NULL; + } + + if(fmt[*i] == ']') + break; + + if(fmt[*i] != ',') + { + Error_Report(error, 0, "Got unexpected '%c' in format where either ',' or ']' were expected", fmt[*i]); + if(ptrs != maybe) free(ptrs); + return NULL; + } + + *i += 1; // Skip the ','. + } + + assert(fmt[*i] == ']'); + + *i += 1; // Skip the ']'. + + Object *list = Object_NewList2(ptrs_num, ptrs, heap, error); + + if(ptrs != maybe) + free(ptrs); + + return list; +} + +static Object *walkValue(const char *fmt, int *i, va_list va, Heap *heap, Error *error) +{ + int i_; + if(i == NULL) + i = &i_; + + while(isspace(fmt[*i])) + *i += 1; + + if(fmt[*i] == '\0') + { + Error_Report(error, 0, "Empty format"); + return NULL; + } + + if(fmt[*i] == '{') + return walkMapValue(fmt, i, va, heap, error); + + if(fmt[*i] == '[') + return walkListValue(fmt, i, va, heap, error); + + if(fmt[*i] != '$') + { + Error_Report(error, 0, "Unexpected character '%c' in format", fmt[*i]); + return NULL; + } + + assert(fmt[*i] == '$'); + + *i += 1; + + Object *o; + + switch(fmt[*i]) + { + case '\0': + Error_Report(error, 0, "Format ended after '$'"); + return NULL; + + case '{': + { + // Noja expression until the closing '}'. + + *i += 1; // Skip the '{'. + + int start = *i; + + while(fmt[*i] != '\0' && fmt[*i] != '}') // TODO: Escape '}'. + *i += 1; + + if(fmt[*i] == '\0') + { + // ERROR: Format ended inside of ${..}. + return NULL; + } + + int end = fmt[*i]; + + // NOTE: The last '}' is skipped after the switch. + + const char *src = fmt + start; + int len = end - start; + + o = eval(src, len, NULL, heap, error); + + if(o == NULL) + return NULL; + break; + } + + case 'i': + o = Object_FromInt(va_arg(va, long long int), heap, error); + if(o == NULL) return NULL; + break; + + case 'f': + o = Object_FromFloat(va_arg(va, double), heap, error); + if(o == NULL) return NULL; + break; + + case 'b': + // NOTE: Actually we want a _Bool, not an int. + // But gcc says: + // > warning: ‘_Bool’ is promoted to ‘int’ when passed through ‘...’ + // > [...] + // > (so you should pass ‘int’ not ‘_Bool’ to ‘va_arg’) + // + o = Object_FromBool(va_arg(va, int), heap, error); + if(o == NULL) return NULL; + break; + + case 's': + o = Object_FromString(va_arg(va, char*), -1, heap, error); + if(o == NULL) return NULL; + break; + + case 'o': + o = va_arg(va, Object*); + assert(o != NULL); + break; + + default: + Error_Report(error, 0, "Invalid format specifier '%c'", fmt[*i]); + return NULL; + } + + *i += 1; + return o; +} + +Object *buildValue2(Heap *heap, Error *error, const char *fmt, va_list va) +{ + return walkValue(fmt, NULL, va, heap, error); +} + +Object *buildValue(Heap *heap, Error *error, const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + Object *o = buildValue2(heap, error, fmt, va); + va_end(va); + return o; +} \ No newline at end of file diff --git a/src/buildvalue.h b/src/buildvalue.h new file mode 100644 index 0000000..e8b5461 --- /dev/null +++ b/src/buildvalue.h @@ -0,0 +1,6 @@ +#include +#include "utils/error.h" +#include "objects/objects.h" + +Object *buildValue2(Heap *heap, Error *error, const char *fmt, va_list va); +Object *buildValue(Heap *heap, Error *error, const char *fmt, ...); \ No newline at end of file diff --git a/src/eval.c b/src/eval.c new file mode 100644 index 0000000..213c11c --- /dev/null +++ b/src/eval.c @@ -0,0 +1,75 @@ +#include "compiler/parse.h" +#include "compiler/compile.h" +#include "runtime/runtime.h" +#include "o_builtins.h" +#include "eval.h" + +Object *eval(const char *str, int len, Object *closure, Heap *heap, Error *error) +{ + Source *s = Source_FromString(NULL, str, len, error); + + if(s == NULL) + return NULL; // Propagate. + + BPAlloc *alloc = BPAlloc_Init(-1); + + if(alloc == NULL) + { + Error_Report(error, 1, "Insufficient memory"); + return NULL; + } + + AST *ast = parse(s, alloc, error); + + if(ast == NULL) + { + BPAlloc_Free(alloc); + Source_Free(s); + return NULL; + } + + Executable *exe = compile(ast, alloc, error); + + if(exe == NULL) + { + BPAlloc_Free(alloc); + Source_Free(s); + return NULL; + } + + BPAlloc_Free(alloc); + Source_Free(s); + + Runtime *runt = Runtime_New2(-1, heap, 0, NULL, NULL); + + if(runt == NULL) + { + Error_Report(error, 1, "Insufficient memory"); + Executable_Free(exe); + return NULL; + } + + Object *bins = Object_NewBuiltinsMap(runt, heap, error); + + if(bins == NULL) + { + Runtime_Free(runt); + Executable_Free(exe); + return NULL; + } + + Runtime_SetBuiltins(runt, bins); + + Object *o = run(runt, error, exe, 0, closure, NULL, 0); + + if(o == NULL) + { + Runtime_Free(runt); + Executable_Free(exe); + return NULL; + } + + Runtime_Free(runt); + Executable_Free(exe); + return o; +} \ No newline at end of file diff --git a/src/eval.h b/src/eval.h new file mode 100644 index 0000000..6fef22f --- /dev/null +++ b/src/eval.h @@ -0,0 +1,3 @@ +#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 diff --git a/src/main.c b/src/main.c index 9f60976..5179436 100644 --- a/src/main.c +++ b/src/main.c @@ -3,6 +3,7 @@ #include #include #include "debug.h" +#include "o_builtins.h" #include "compiler/parse.h" #include "compiler/serialize.h" #include "compiler/compile.h" @@ -14,8 +15,6 @@ * $ noja -f -o */ -Object *Object_NewBuiltinsMap(Runtime *runtime, Heap *heap, Error *err); - typedef enum { RUN, HELP, PARSE, VERSION, DISASSEMBLY } Action; typedef struct { diff --git a/src/o_builtins.c b/src/o_builtins.c index caa403e..24f4310 100644 --- a/src/o_builtins.c +++ b/src/o_builtins.c @@ -4,8 +4,7 @@ #include #include "runtime/runtime.h" #include "utils/defs.h" - -Object *Object_NewNetworkBuiltinsMap(Runtime *runtime, Heap *heap, Error *err); +#include "o_builtins.h" static Object *select_(Object *self, Object *key, Heap *heap, Error *err); diff --git a/src/o_builtins.h b/src/o_builtins.h new file mode 100644 index 0000000..f5b02e6 --- /dev/null +++ b/src/o_builtins.h @@ -0,0 +1,3 @@ +#include "runtime/runtime.h" +Object *Object_NewBuiltinsMap(Runtime *runtime, Heap *heap, Error *err); +Object *Object_NewNetworkBuiltinsMap(Runtime *runtime, Heap *heap, Error *err); \ No newline at end of file diff --git a/src/objects/o_list.c b/src/objects/o_list.c index 52bffe9..5296d98 100644 --- a/src/objects/o_list.c +++ b/src/objects/o_list.c @@ -1,4 +1,5 @@ #include +#include #include "../utils/defs.h" #include "objects.h" @@ -57,6 +58,21 @@ Object *Object_NewList(int capacity, Heap *heap, Error *error) return (Object*) obj; } +Object *Object_NewList2(int num, Object **items, Heap *heap, Error *error) +{ + assert(num > -1); + + ListObject *list = (ListObject*) Object_NewList(num, heap, error); + + if(list == NULL) + return NULL; + + memcpy(list->vals, items, num * sizeof(Object*)); + list->count = num; + + return (Object*) list; +} + static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp) { ListObject *list = (ListObject*) self; diff --git a/src/objects/objects.h b/src/objects/objects.h index a79455a..72d9815 100644 --- a/src/objects/objects.h +++ b/src/objects/objects.h @@ -105,6 +105,7 @@ void Object_WalkExtensions(Object *parent, void (*callback)(void **referer, Object* Object_NewMap(int num, Heap *heap, Error *error); Object* Object_NewList(int capacity, Heap *heap, Error *error); +Object* Object_NewList2(int num, Object **items, Heap *heap, Error *error); Object* Object_NewNone(Heap *heap, Error *error); Object* Object_NewBuffer(int size, Heap *heap, Error *error); Object* Object_NewClosure(Object *parent, Object *new_map, Heap *heap, Error *error); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 76d4ac8..4ad2799 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -9,6 +9,7 @@ typedef struct xRuntime Runtime; typedef void CallStackScanner; Runtime* Runtime_New(int stack_size, int heap_size, void *callback_userp, _Bool (*callback_addr)(Runtime*, void*)); +Runtime* Runtime_New2(int stack_size, Heap *heap, _Bool free_heap, void *callback_userp, _Bool (*callback_addr)(Runtime*, void*)); void Runtime_Free(Runtime *runtime); _Bool Runtime_Pop(Runtime *runtime, Error *error, unsigned int n); _Bool Runtime_Push(Runtime *runtime, Error *error, Object *obj);