diff --git a/examples/json.noja b/examples/json.noja index 9793bf7..bc5007b 100644 --- a/examples/json.noja +++ b/examples/json.noja @@ -9,11 +9,8 @@ fun isDigit(c) and unicode(c) >= unicode('0'); fun skip(ctx, test) - while not ended(ctx): { - if not test(current(ctx)): - break; + while not ended(ctx) and test(current(ctx)): next(ctx); - } fun ended(ctx) return ctx.i == count(ctx.str); @@ -167,13 +164,8 @@ fun parseString(ctx) { buff = ''; - while not ended(ctx): { - - if current(ctx) == '"': - break; - + while not ended(ctx) and current(ctx) != '"': { buff = strcat(buff, current(ctx)); - next(ctx); } @@ -191,9 +183,7 @@ fun parseNumber(ctx) { # Number (int or float) parsed = 0; - while not ended(ctx): { - if not isDigit(current(ctx)): - break; + while not ended(ctx) and isDigit(current(ctx)): { parsed = parsed * 10 + unicode(current(ctx)) - unicode('0'); next(ctx); } @@ -212,9 +202,7 @@ fun parseNumber(ctx) { return none, "Got something other than a digit after dot"; fact = 1; - while not ended(ctx): { - if not isDigit(current(ctx)): - break; + while not ended(ctx) and isDigit(current(ctx)): { fact = fact / 10.0; parsed = parsed + fact * (unicode(current(ctx)) - unicode('0')); next(ctx); diff --git a/examples/json/json.noja b/examples/json/json.noja new file mode 100644 index 0000000..8f37a10 --- /dev/null +++ b/examples/json/json.noja @@ -0,0 +1,171 @@ + +fun isSpace(c) + return c == " " or c == "\t" or c == "\n"; + +fun isDigit(c) { + u = unicode(c); + d = u - unicode("0"); + return d >= 0 and d <= 9; +} + +fun skipSpaces(context) { + # Skip zero or more whitespace characters. + while context.cur < count(context.str) + and isSpace(context.str[context.cur]): + context.cur = context.cur + 1; +} + +fun parseNull(context) { + + assert(context.cur < count(context) + and context.str[context.cur] == 'n'); + + if context.cur+3 > count(context) + or context.str[context.cur+1] != 'u' + or context.str[context.cur+2] != 'l' + or context.str[context.cur+3] != 'l': + return none, "Unexpected token starting with \"n\""; + + context.cur = context.cur + count("null"); + return none; +} + +fun parseTrue(context) { + + assert(context.cur < count(context) + and context.str[context.cur] == 't'); + + if context.cur+3 > count(context) + or context.str[context.cur+1] != 'r' + or context.str[context.cur+2] != 'u' + or context.str[context.cur+3] != 'e': + return none, "Unexpected token starting with \"t\""; + + context.cur = context.cur + count("true"); + return true; +} + +fun parseFalse(context) { + + assert(context.cur < count(context) + and context.str[context.cur] == 'f'); + + if context.cur+3 > count(context) + or context.str[context.cur+1] != 'a' + or context.str[context.cur+2] != 'l' + or context.str[context.cur+3] != 's' + or context.str[context.cur+4] != 'e': + return none, "Unexpected token starting with \"f\""; + + context.cur = context.cur + count("false"); + return false; +} + +fun skip(context, test_callback) { + k = context.cur; + while k < count(context) and test_callback(context.str[k]): + k = k + 1; + return k; +} + +fun parseInteger(context) { + assert(context.cur < count(context) and isDigit(context.str[context.cur])); + + buffer = 0; + + do { + c = context.str[context.cur]; + d = unicode(c) - unicode('0'); + buffer = buffer * 10 + d; + context.cur = context.cur + 1; + } while context.cur < count(context) + and isDigit(context.str[context.cur]); + + return buffer; +} + +fun parseFloating(context) { + assert(context.cur < count(context) and isDigit(context.str[context.cur])); + + # It's ensured by the caller (parseIntegerOrFloating) + # that now the strings contains two sequences of + # digits with a dot in the middle. + + buffer = 0.0; + + # Scan the integer part. + do { + c = context.str[context.cur]; + d = unicode(c) - unicode('0'); + buffer = buffer * 10 + d; + context.cur = context.cur + 1; + } while context.str[context.cur] != "."; + + assert(isDigit(context.str[context.cur+1])); + + # Scan the decimal part. + q = 1; + do { + c = context.str[context.cur]; + d = unicode(c) - unicode('0'); + q = q / 10; + buffer = buffer + q * d; + context.cur = context.cur + 1; + } while context.cur < count(context) + and isDigit(context); + + return buffer; +} + +fun parseIntegerOrFloating(context) { + assert(context.cur < count(context) and isDigit(context.str[context.cur])); + + # Is the first digit sequence followed by a dot + # and then one or more digits? + + k = skip(context, isDigit); + if k+1 < count(context) and context.str[k] == "." and isDigit(context.str[k+1]): + # It's a floating point value, then! + return parseFloating(context); + else + # It's good old integer! + return parseInteger(context); +} + +fun parseValue(context) { + + if context.cur == count(context): + return none, "Source ended where a value was expected"; + + c = context.str[context.cur]; + if c == "{": + val, err = parseObject(context); + else if c == "[": + val, err = parseArray(context); + else if c == '"': + val, err = parseString(context); + else if isDigit(c): + val, err = parseIntegerOrFloating(context); + else if c == 'n': + val, err = parseNull(context); + else if c == 't': + val, err = parseTrue(context); + else if c == 'f': + val, err = parseFalse(context); + else { + val = none; + err = strcat("Unexepected character \"", c, "\" where a value was expected"); + } + return val, err; +} + +fun parse(str) { + context = {str: str, cur: 0}; + skipSpaces(context); + val, err = parseValue(context); + return val, err; +} + +val, err = parse('3.14'); +print("val=[", val, "]\n"); +print("err=[", err, "]\n"); \ No newline at end of file diff --git a/examples/json/test.noja b/examples/json/test.noja new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/builtins/basic.c b/src/lib/builtins/basic.c index a6b23ff..54ac6a5 100644 --- a/src/lib/builtins/basic.c +++ b/src/lib/builtins/basic.c @@ -32,11 +32,11 @@ #include #include #include +#include "math.h" #include "basic.h" #include "files.h" -#include "math.h" #include "../utils/utf8.h" - +#include "../objects/objects.h" static int bin_print(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) { (void) runtime; @@ -378,7 +378,31 @@ static int bin_bufferToString(Runtime *runtime, Object **argv, unsigned int argc return 1; } -const StaticMapSlot bins_basic[] = { +void bins_basic_init(StaticMapSlot slots[]) +{ + slots[0].as_type = Object_GetTypeType(); + slots[1].as_type = Object_GetNoneType(); + slots[2].as_type = Object_GetIntType(); + slots[3].as_type = Object_GetFloatType(); + slots[4].as_type = Object_GetStringType(); + slots[5].as_type = Object_GetListType(); + slots[6].as_type = Object_GetMapType(); + slots[7].as_type = Object_GetFileType(); + slots[8].as_type = Object_GetDirType(); +} + +StaticMapSlot bins_basic[] = { + + { "Type", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, + { "None", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, + { "int", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, + { "float", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, + { "String", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, + { "List", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, + { "Map", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, + { "File", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, + { "Dir", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, + { "math", SM_SMAP, .as_smap = bins_math, }, // { "files", SM_SMAP, .as_smap = bins_files, }, // { "net", SM_SMAP, .as_smap = bins_net, }, diff --git a/src/lib/builtins/basic.h b/src/lib/builtins/basic.h index 531cbd0..f60e3e4 100644 --- a/src/lib/builtins/basic.h +++ b/src/lib/builtins/basic.h @@ -29,4 +29,5 @@ */ #include "../runtime/runtime.h" -extern const StaticMapSlot bins_basic[]; \ No newline at end of file +void bins_basic_init(StaticMapSlot slots[]); +extern StaticMapSlot bins_basic[]; \ No newline at end of file diff --git a/src/lib/builtins/files.c b/src/lib/builtins/files.c index 5c38932..1e817f6 100644 --- a/src/lib/builtins/files.c +++ b/src/lib/builtins/files.c @@ -296,7 +296,7 @@ static Object *bin_nextDirItem(Runtime *runtime, Object **argv, unsigned int arg return Object_FromString(ent->d_name, -1, heap, error); } -const StaticMapSlot bins_files[] = { +StaticMapSlot bins_files[] = { { "READ", SM_INT, .as_int = MD_READ, }, { "WRITE", SM_INT, .as_int = MD_WRITE, }, { "APPEND", SM_INT, .as_int = MD_APPEND, }, diff --git a/src/lib/builtins/files.h b/src/lib/builtins/files.h index da4d94c..98b0bf0 100644 --- a/src/lib/builtins/files.h +++ b/src/lib/builtins/files.h @@ -29,4 +29,4 @@ */ #include "../runtime/runtime.h" -extern const StaticMapSlot bins_files[]; \ No newline at end of file +extern StaticMapSlot bins_files[]; \ No newline at end of file diff --git a/src/lib/builtins/math.c b/src/lib/builtins/math.c index 56b01ff..01d66e3 100644 --- a/src/lib/builtins/math.c +++ b/src/lib/builtins/math.c @@ -109,7 +109,7 @@ WRAP_FUNC(sqrt) WRAP_FUNC_2(atan2) WRAP_FUNC_2(pow) -const StaticMapSlot bins_math[] = { +StaticMapSlot bins_math[] = { { "PI", SM_FLOAT, .as_float = M_PI }, { "E", SM_FLOAT, .as_float = M_E }, diff --git a/src/lib/builtins/math.h b/src/lib/builtins/math.h index e88eec4..560d8fc 100644 --- a/src/lib/builtins/math.h +++ b/src/lib/builtins/math.h @@ -29,4 +29,4 @@ */ #include "../runtime/runtime.h" -extern const StaticMapSlot bins_math[]; \ No newline at end of file +extern StaticMapSlot bins_math[]; \ No newline at end of file diff --git a/src/lib/compiler/codegen.c b/src/lib/compiler/codegen.c index 9cbc85d..ddac83a 100644 --- a/src/lib/compiler/codegen.c +++ b/src/lib/compiler/codegen.c @@ -504,7 +504,7 @@ static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr, static void emitInstrForIfElseNode(CodegenContext *ctx, IfElseNode *ifelse, Label *label_break) { - emitInstrForExprNode(ctx, ifelse->condition, label_break); + emitInstrForNode(ctx, ifelse->condition, label_break); if(ifelse->false_branch) { Label *label_else = Label_New(ctx); diff --git a/src/lib/noja.c b/src/lib/noja.c index f096a6b..8b423a3 100644 --- a/src/lib/noja.c +++ b/src/lib/noja.c @@ -56,7 +56,7 @@ static _Bool interpret(Executable *exe) RuntimeError error; RuntimeError_Init(&error, runt); // Here we specify the runtime to snapshot in case of failure. - Object *bins = Object_NewStaticMap(bins_basic, runt, (Error*) &error); + Object *bins = Object_NewStaticMap(bins_basic, bins_basic_init, runt, (Error*) &error); if(bins == NULL) { diff --git a/src/lib/objects/o_bool.c b/src/lib/objects/o_bool.c index 2bbd123..1eae4a4 100644 --- a/src/lib/objects/o_bool.c +++ b/src/lib/objects/o_bool.c @@ -97,6 +97,11 @@ static _Bool to_bool(Object *obj, Error *err) return obj == &the_true_object; } +TypeObject *Object_GetBoolType() +{ + return &t_bool; +} + Object *Object_FromBool(_Bool val, Heap *heap, Error *error) { (void) heap; diff --git a/src/lib/objects/o_buffer.c b/src/lib/objects/o_buffer.c index 25c30a3..5df5781 100644 --- a/src/lib/objects/o_buffer.c +++ b/src/lib/objects/o_buffer.c @@ -82,6 +82,11 @@ static TypeObject t_buffer_slice = { #define THRESHOLD 128 +TypeObject *Object_GetBufferType() +{ + return &t_buffer; +} + _Bool Object_IsBuffer(Object *obj) { return obj->type == &t_buffer_slice || obj->type == &t_buffer; diff --git a/src/lib/objects/o_dir.c b/src/lib/objects/o_dir.c index 85f66ee..0110d74 100644 --- a/src/lib/objects/o_dir.c +++ b/src/lib/objects/o_dir.c @@ -44,6 +44,11 @@ static TypeObject t_dir = { .free = dir_free, }; +TypeObject *Object_GetDirType() +{ + return &t_dir; +} + _Bool Object_IsDir(Object *obj) { return obj->type == &t_dir; diff --git a/src/lib/objects/o_file.c b/src/lib/objects/o_file.c index b776d93..fa25cc3 100644 --- a/src/lib/objects/o_file.c +++ b/src/lib/objects/o_file.c @@ -44,6 +44,11 @@ static TypeObject t_file = { .free = file_free, }; +TypeObject *Object_GetFileType() +{ + return &t_file; +} + _Bool Object_IsFile(Object *obj) { return obj->type == &t_file; diff --git a/src/lib/objects/o_float.c b/src/lib/objects/o_float.c index bcf6edb..8805deb 100644 --- a/src/lib/objects/o_float.c +++ b/src/lib/objects/o_float.c @@ -98,6 +98,11 @@ static double to_float(Object *obj, Error *err) return ((FloatObject*) obj)->val; } +TypeObject *Object_GetFloatType() +{ + return &t_float; +} + Object *Object_FromFloat(double val, Heap *heap, Error *error) { FloatObject *obj = (FloatObject*) Heap_Malloc(heap, &t_float, error); diff --git a/src/lib/objects/o_int.c b/src/lib/objects/o_int.c index d1888f9..7742b2f 100644 --- a/src/lib/objects/o_int.c +++ b/src/lib/objects/o_int.c @@ -84,6 +84,11 @@ static long long int to_int(Object *obj, Error *err) return ((IntObject*) obj)->val; } +TypeObject *Object_GetIntType() +{ + return &t_int; +} + Object *Object_FromInt(long long int val, Heap *heap, Error *error) { assert(heap != NULL); diff --git a/src/lib/objects/o_list.c b/src/lib/objects/o_list.c index 7df3e04..53bbe6f 100644 --- a/src/lib/objects/o_list.c +++ b/src/lib/objects/o_list.c @@ -99,6 +99,11 @@ static Object *copy(Object *self, Heap *heap, Error *err) return (Object*) ls2; } +TypeObject *Object_GetListType() +{ + return &t_list; +} + Object *Object_NewList(int capacity, Heap *heap, Error *error) { // Handle default args. diff --git a/src/lib/objects/o_map.c b/src/lib/objects/o_map.c index 003b31d..a4ae59b 100644 --- a/src/lib/objects/o_map.c +++ b/src/lib/objects/o_map.c @@ -108,6 +108,11 @@ static int hash(Object *self) return h; } +TypeObject *Object_GetMapType() +{ + return &t_map; +} + Object *Object_NewMap(int num, Heap *heap, Error *error) { // Handle default args. diff --git a/src/lib/objects/o_none.c b/src/lib/objects/o_none.c index 1bbfa98..5bbe450 100644 --- a/src/lib/objects/o_none.c +++ b/src/lib/objects/o_none.c @@ -52,6 +52,11 @@ static Object the_none_object = { .flags = Object_STATIC, }; +TypeObject *Object_GetNoneType() +{ + return &t_none; +} + _Bool Object_IsNone(Object *obj) { return obj == &the_none_object; diff --git a/src/lib/objects/o_string.c b/src/lib/objects/o_string.c index 175c083..2936c77 100644 --- a/src/lib/objects/o_string.c +++ b/src/lib/objects/o_string.c @@ -134,6 +134,11 @@ static char *to_string(Object *self, int *size, Heap *heap, Error *err) return s->body; } +TypeObject *Object_GetStringType() +{ + return &t_string; +} + Object *Object_FromString(const char *str, int len, Heap *heap, Error *error) { assert(str != NULL); diff --git a/src/lib/objects/objects.c b/src/lib/objects/objects.c index 1981938..57d32a9 100644 --- a/src/lib/objects/objects.c +++ b/src/lib/objects/objects.c @@ -41,6 +41,11 @@ TypeObject t_type = { .op_eql = op_eql, }; +TypeObject *Object_GetTypeType() +{ + return &t_type; +} + static _Bool op_eql(Object *self, Object *other) { return self == other; diff --git a/src/lib/objects/objects.h b/src/lib/objects/objects.h index 9a78b4a..d1d1989 100644 --- a/src/lib/objects/objects.h +++ b/src/lib/objects/objects.h @@ -149,6 +149,18 @@ Object* Object_FromString(const char *str, int len, Heap *heap, Error *error); Object* Object_FromStream(FILE *fp, Heap *heap, Error *error); Object* Object_FromDIR(DIR *handle, Heap *heap, Error *error); +TypeObject *Object_GetTypeType(); +TypeObject *Object_GetNoneType(); +TypeObject *Object_GetIntType(); +TypeObject *Object_GetBoolType(); +TypeObject *Object_GetFloatType(); +TypeObject *Object_GetStringType(); +TypeObject *Object_GetListType(); +TypeObject *Object_GetMapType(); +TypeObject *Object_GetBufferType(); +TypeObject *Object_GetFileType(); +TypeObject *Object_GetDirType(); + _Bool Object_IsNone(Object *obj); _Bool Object_IsInt(Object *obj); _Bool Object_IsBool(Object *obj); diff --git a/src/lib/runtime/o_staticmap.c b/src/lib/runtime/o_staticmap.c index 749a76f..c2a7008 100644 --- a/src/lib/runtime/o_staticmap.c +++ b/src/lib/runtime/o_staticmap.c @@ -83,7 +83,7 @@ static int hash(Object *self) return 0; } -Object *Object_NewStaticMap(const StaticMapSlot *slots, Runtime *runt, Error *error) +Object *Object_NewStaticMap(StaticMapSlot slots[], void (*initfn)(StaticMapSlot[]), Runtime *runt, Error *error) { Heap *heap = Runtime_GetHeap(runt); @@ -97,6 +97,9 @@ Object *Object_NewStaticMap(const StaticMapSlot *slots, Runtime *runt, Error *er obj->slots = slots; } + if(initfn != NULL) + initfn(slots); + return (Object*) obj; } @@ -130,7 +133,7 @@ static Object *select(Object *self, Object *key, Heap *heap, Error *error) case SM_FLOAT: return Object_FromFloat(slot.as_float, heap, error); case SM_FUNCT: return Object_FromNativeFunction(map->runt, slot.as_funct, slot.argc, heap, error); case SM_STRING: return Object_FromString(slot.as_string, slot.length, heap, error); - case SM_SMAP: return Object_NewStaticMap(slot.as_smap, map->runt, error); + case SM_SMAP: return Object_NewStaticMap(slot.as_smap, NULL, map->runt, error); case SM_NONE: return Object_NewNone(heap, error); case SM_TYPE: return (Object*) slot.as_type; default: assert(0); break; diff --git a/src/lib/runtime/runtime.h b/src/lib/runtime/runtime.h index c62ee43..a8377c8 100644 --- a/src/lib/runtime/runtime.h +++ b/src/lib/runtime/runtime.h @@ -74,18 +74,18 @@ struct StaticMapSlot { const char *name; StaticMapSlotKind kind; union { - const StaticMapSlot *as_smap; - const char *as_string; - _Bool as_bool; - long long int as_int; - double as_float; - int (*as_funct)(Runtime*, Object**, unsigned int, Object**, unsigned int, Error*); - TypeObject *as_type; + StaticMapSlot *as_smap; + const char *as_string; + _Bool as_bool; + long long int as_int; + double as_float; + int (*as_funct)(Runtime*, Object**, unsigned int, Object**, unsigned int, Error*); + TypeObject *as_type; }; union { int argc; int length; }; }; -Object *Object_NewStaticMap(const StaticMapSlot *slots, Runtime *runt, Error *error); +Object *Object_NewStaticMap(StaticMapSlot slots[], void (*initfn)(StaticMapSlot[]), Runtime *runt, Error *error); Object *Object_FromNojaFunction(Runtime *runtime, Executable *exe, int index, int argc, Object *closure, Heap *heap, Error *error); Object *Object_FromNativeFunction(Runtime *runtime, int (*callback)(Runtime*, Object**, unsigned int, Object**, unsigned int, Error*), int argc, Heap *heap, Error *error); typedef struct { diff --git a/tests/test.noja b/tests/test.noja index dcbd197..a1aa511 100644 --- a/tests/test.noja +++ b/tests/test.noja @@ -116,4 +116,14 @@ assert(r); } +# Test type variables +{ + assert(type(none) == None); + assert(type(1) == int); + assert(type(10.4) == float); + assert(type("Hello, world!") == String); + assert(type([]) == List); + assert(type({}) == Map); +} + print('No assertion failed.\n'); \ No newline at end of file