From dbf1c325b093b4e1ea87b3da155fe0312cbf8fae Mon Sep 17 00:00:00 2001 From: cozis Date: Wed, 8 Dec 2021 13:20:34 +0100 Subject: [PATCH] added not operator --- TODO.txt | 9 +++ samples/tree.noja | 131 ++++++++++++++++++++++++++++++++++++++++ src/common/executable.c | 1 + src/common/executable.h | 1 + src/compiler/ASTi.h | 2 + src/compiler/compile.c | 2 + src/compiler/parse.c | 15 ++++- src/o_builtins.c | 16 +++++ src/objects/objects.c | 8 +++ src/runtime/runtime.c | 32 ++++++++++ 10 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 TODO.txt create mode 100644 samples/tree.noja diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..899a63b --- /dev/null +++ b/TODO.txt @@ -0,0 +1,9 @@ +- make it so that when a branch condition's and-ed sub-expression fails, + the other sub-expressions aren't evaluated. Like this: + + if type(A) == type({}) and A.name != none: + .. do stuff .. + + the second sub-expr. "A.name != none" raises an error if the first one + "type(A) == type({})" isn't true, but doesn't need to be evaluated in + that case. \ No newline at end of file diff --git a/samples/tree.noja b/samples/tree.noja new file mode 100644 index 0000000..8210df9 --- /dev/null +++ b/samples/tree.noja @@ -0,0 +1,131 @@ + +fun _count(elm) +{ + if type(elm) == type({}): + + if type(elm._count) == type(_count): + + return elm._count(elm); + + else if type(elm._count) == type(1): + + return elm._count; + + return count(elm); +} + +fun newTree() +{ + return {root: none, _count: 0}; +} + +fun locate(tree, index, path) +{ + # This function walks the tree to find the + # leaf that is referred to by the specified + # index. + # The traversed nodes are stored in the path + # argument. The last one is the leaf. + # The return value is the index relative to + # the start of the leaf. + assert(type(index) == type(1)); + + path = []; # List of traversed nodes (without the leaf). + rindex = index; # index relative to the found leaf. + cursor = tree; # Tree cursor. At the end it will refer to the leaf. + + path[count(path)] = cursor; + + while cursor.is_leaf == false: + { + if rindex < _count(cursor.left): + cursor = cursor.left; + else + { + rindex = rindex - _count(cursor.left); + cursor = cursor.right; + } + + path[count(path)] = cursor; + } + return rindex; +} + +fun insert(tree, data, index) +{ + if index == none: + # Insert at the end. + index = _count(tree); + + path = []; + rindex = locate(tree, index, path); + assert(count(path) > 1); + + leaf = path[count(path)-1]; + assert(type(leaf) == type(newBuffer(0))); + + if rindex == 0: + # Want to store the data before + # the leaf buffer. + {} + else if rindex < leaf.used: + # Want to store the data in the + # middle of the leaf. + { + fun min(a, b) + { + if a < b: + return a; + return b; + } + + fun copy(dst, src, num) + { + i = 0; + while i < num: + { + dst[i] = src[i]; + i = i + 1; + } + } + + # Insert in the current leaf what's + # possible. + + unused = count(leaf.buffer) - leaf.used; + copied = min(unused, count(data)); + copy(leaf.buffer, data, copied); + leaf.used = leaf.used + copied; + + if copied == count(data): + { + # We're done. The unused portion of + # the leaf was enough to store the + # data. + + # Update the path. + i = count(path)-2; + while i > 0: + { + if path[i] == path[i-1].left: + path[i-1]._count = path[i-1]._count + copied; + i = i - 1; + } + return none; + } + + # There's more data that needs to be + # stored, but now we don't need to + # insert in the middle of the leaf. + + data = sliceBuffer(data, copying, count(data)); + } + + if rindex == leaf.used: + # Want to append data to the + # end of the contents. + {} +} + +print( count(newTree()), '\n'); +print(_count(newTree()), '\n'); \ No newline at end of file diff --git a/src/common/executable.c b/src/common/executable.c index b671d80..7c54343 100644 --- a/src/common/executable.c +++ b/src/common/executable.c @@ -42,6 +42,7 @@ static const InstrInfo instr_table[] = { [OPCODE_POS] = {"POS", 0, NULL}, [OPCODE_NEG] = {"NEG", 0, NULL}, + [OPCODE_NOT] = {"NOT", 0, NULL}, [OPCODE_ADD] = {"ADD", 0, NULL}, [OPCODE_SUB] = {"SUB", 0, NULL}, diff --git a/src/common/executable.h b/src/common/executable.h index 8e1d617..37b9478 100644 --- a/src/common/executable.h +++ b/src/common/executable.h @@ -24,6 +24,7 @@ typedef enum { OPCODE_NOPE, OPCODE_POS, OPCODE_NEG, + OPCODE_NOT, OPCODE_ADD, OPCODE_SUB, OPCODE_MUL, diff --git a/src/compiler/ASTi.h b/src/compiler/ASTi.h index 379fcf9..9ee0ffd 100644 --- a/src/compiler/ASTi.h +++ b/src/compiler/ASTi.h @@ -30,6 +30,8 @@ typedef enum { EXPR_GRT, EXPR_GEQ, + EXPR_NOT, + EXPR_ASS, EXPR_INT, EXPR_MAP, diff --git a/src/compiler/compile.c b/src/compiler/compile.c index 191e1ff..1fd4fb1 100644 --- a/src/compiler/compile.c +++ b/src/compiler/compile.c @@ -27,6 +27,7 @@ static Opcode exprkind_to_opcode(ExprKind kind) { switch(kind) { + case EXPR_NOT: return OPCODE_NOT; case EXPR_POS: return OPCODE_POS; case EXPR_NEG: return OPCODE_NEG; case EXPR_ADD: return OPCODE_ADD; @@ -56,6 +57,7 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error) ExprNode *expr = (ExprNode*) node; switch(expr->kind) { + case EXPR_NOT: case EXPR_POS: case EXPR_NEG: case EXPR_ADD: diff --git a/src/compiler/parse.c b/src/compiler/parse.c index c22f7e8..7b412db 100644 --- a/src/compiler/parse.c +++ b/src/compiler/parse.c @@ -58,6 +58,7 @@ typedef enum { TKWIF, TKWFUN, + TKWNOT, TKWELSE, TKWNONE, TKWTRUE, @@ -192,6 +193,10 @@ static Token *tokenize(Source *src, BPAlloc *alloc, Error *error) { tok->kind = TKWFUN; } + else if(matchstr(str + tok->offset, tok->length, "not")) + { + tok->kind = TKWNOT; + } else if(matchstr(str + tok->offset, tok->length, "else")) { tok->kind = TKWELSE; @@ -541,6 +546,7 @@ static Node *parse_statement(Context *ctx) case TFLOAT: case TSTRING: case TIDENT: + case TKWNOT: case TKWNONE: case TKWTRUE: case TKWFALSE: @@ -1064,6 +1070,7 @@ static Node *parse_primary_expresion(Context *ctx) { case '+': case '-': + case TKWNOT: { Token *unary_operator = current_token(ctx); @@ -1083,9 +1090,15 @@ static Node *parse_primary_expresion(Context *ctx) temp->base.base.next = NULL; temp->base.base.offset = unary_operator->offset; temp->base.base.length = operand->offset + operand->length - unary_operator->offset; - temp->base.kind = unary_operator->kind == '+' ? EXPR_POS : EXPR_NEG; temp->head = operand; temp->count = 1; + + switch(unary_operator->kind) + { + case '+': temp->base.kind = EXPR_POS; break; + case '-': temp->base.kind = EXPR_NEG; break; + case TKWNOT: temp->base.kind = EXPR_NOT; break; + } } return (Node*) temp; } diff --git a/src/o_builtins.c b/src/o_builtins.c index 3276cb6..caa403e 100644 --- a/src/o_builtins.c +++ b/src/o_builtins.c @@ -9,6 +9,7 @@ Object *Object_NewNetworkBuiltinsMap(Runtime *runtime, Heap *heap, Error *err); static Object *select_(Object *self, Object *key, Heap *heap, Error *err); +static Object *bin_type (Runtime *runtime, Object **argv, unsigned int argc, Error *error); static Object *bin_print (Runtime *runtime, Object **argv, unsigned int argc, Error *error); static Object *bin_count (Runtime *runtime, Object **argv, unsigned int argc, Error *error); static Object *bin_assert(Runtime *runtime, Object **argv, unsigned int argc, Error *error); @@ -51,6 +52,13 @@ static Object *select_(Object *self, Object *key, Heap *heap, Error *err) switch(PAIR(n, s[0])) { + case PAIR(4, 't'): + { + if(!strcmp(s, "type")) + return Object_FromNativeFunction(bm->runtime, bin_type, 1, heap, err); + return NULL; + } + case PAIR(sizeof("count")-1, 'c'): { if(!strcmp(s, "count")) @@ -129,6 +137,14 @@ static Object *bin_print(Runtime *runtime, Object **argv, unsigned int argc, Err return Object_NewNone(Runtime_GetHeap(runtime), error); } +static Object *bin_type(Runtime *runtime, Object **argv, unsigned int argc, Error *error) +{ + assert(argc == 1); + (void) runtime; + (void) error; + return (Object*) argv[0]->type; +} + static Object *bin_count(Runtime *runtime, Object **argv, unsigned int argc, Error *error) { assert(argc == 1); diff --git a/src/objects/objects.c b/src/objects/objects.c index bac1fee..2f3c5ea 100644 --- a/src/objects/objects.c +++ b/src/objects/objects.c @@ -2,12 +2,20 @@ #include "../utils/defs.h" #include "objects.h" +static _Bool op_eql(Object *self, Object *other); + TypeObject t_type = { .base = (Object) { .type = &t_type, .flags = Object_STATIC }, .name = "type", .size = sizeof (TypeObject), + .op_eql = op_eql, }; +static _Bool op_eql(Object *self, Object *other) +{ + return self == other; +} + const char *Object_GetName(const Object *obj) { assert(obj != NULL); diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index c9b6a7e..a9fb483 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -557,6 +557,38 @@ static _Bool step(Runtime *runtime, Error *error) Error_Report(error, 1, "NEG not implemented"); return 0; + case OPCODE_NOT: + { + assert(opc == 0); + + if(runtime->frame->used == 0) + { + Error_Report(error, 1, "Frame doesn't have enough items on the stack to execute NOT"); + return 0; + } + + Object *top = Stack_Top(runtime->stack, 0); + + if(!Runtime_Pop(runtime, error, 1)) + return 0; + + assert(top != NULL); + + _Bool v = Object_ToBool(top, error); + + if(error->occurred) + return 0; + + Object *negated = Object_FromBool(!v, runtime->heap, error); + + if(negated == NULL) + return 0; + + if(!Runtime_Push(runtime, error, negated)) + return 0; + return 1; + } + case OPCODE_ADD: case OPCODE_SUB: case OPCODE_MUL: