diff --git a/samples/gang2.noja b/samples/gang2.noja new file mode 100644 index 0000000..6deb60e --- /dev/null +++ b/samples/gang2.noja @@ -0,0 +1,36 @@ + +stack = {count: 0, head: none}; + +fun push(stack, value) +{ + node = {prev: none, item: value}; + + node.prev = stack.head; + stack.head = node; + stack.count = stack.count + 1; +} + +fun pop(stack) +{ + if stack.head == none: + return none; # return it; ? + + value = stack.head.item; + stack.head = stack.head.prev; + stack.count = stack.count - 1; + + return value; +} + +push(stack, 1); +push(stack, 2); +push(stack, 3); +x = pop(stack); +y = pop(stack); +z = pop(stack); +w = pop(stack); + +assert(x == 3); +assert(y == 2); +assert(z == 1); +assert(w == none); \ No newline at end of file diff --git a/src/common/executable.c b/src/common/executable.c index 14aa7e3..b671d80 100644 --- a/src/common/executable.c +++ b/src/common/executable.c @@ -58,8 +58,9 @@ static const InstrInfo instr_table[] = { [OPCODE_ASS] = {"ASS", 1, (OperandType[]) {OPTP_STRING}}, [OPCODE_POP] = {"POP", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_CALL] = {"CALL", 1, (OperandType[]) {OPTP_INT}}, - [OPCODE_INSERT] = {"INSERT", 0, NULL}, [OPCODE_SELECT] = {"SELECT", 0, NULL}, + [OPCODE_INSERT] = {"INSERT", 0, NULL}, + [OPCODE_INSERT2] = {"INSERT2", 0, NULL}, [OPCODE_PUSHINT] = {"PUSHINT", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_PUSHFLT] = {"PUSHFLT", 1, (OperandType[]) {OPTP_FLOAT}}, [OPCODE_PUSHSTR] = {"PUSHSTR", 1, (OperandType[]) {OPTP_STRING}}, diff --git a/src/common/executable.h b/src/common/executable.h index 4e58056..8e1d617 100644 --- a/src/common/executable.h +++ b/src/common/executable.h @@ -37,8 +37,9 @@ typedef enum { OPCODE_ASS, OPCODE_POP, OPCODE_CALL, - OPCODE_INSERT, OPCODE_SELECT, + OPCODE_INSERT, + OPCODE_INSERT2, OPCODE_PUSHINT, OPCODE_PUSHFLT, OPCODE_PUSHSTR, diff --git a/src/compiler/compile.c b/src/compiler/compile.c index 5a1063c..191e1ff 100644 --- a/src/compiler/compile.c +++ b/src/compiler/compile.c @@ -88,19 +88,38 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error) lop = oper->head; rop = lop->next; - if(((ExprNode*) lop)->kind != EXPR_IDENT) - { - Error_Report(error, 0, "Assignment left operand must be an identifier"); - return 0; - } - if(!emit_instr_for_node(exeb, rop, error)) return 0; - const char *name = ((IdentExprNode*) lop)->val; + if(((ExprNode*) lop)->kind == EXPR_IDENT) + { + const char *name = ((IdentExprNode*) lop)->val; - Operand op = { .type = OPTP_STRING, .as_string = name }; - return ExeBuilder_Append(exeb, error, OPCODE_ASS, &op, 1, node->offset, node->length); + Operand op = { .type = OPTP_STRING, .as_string = name }; + if(!ExeBuilder_Append(exeb, error, OPCODE_ASS, &op, 1, node->offset, node->length)) + return 0; + } + else if(((ExprNode*) lop)->kind == EXPR_SELECT) + { + Node *idx = ((IndexSelectionExprNode*) lop)->idx; + Node *set = ((IndexSelectionExprNode*) lop)->set; + + if(!emit_instr_for_node(exeb, set, error)) + return 0; + + if(!emit_instr_for_node(exeb, idx, error)) + return 0; + + if(!ExeBuilder_Append(exeb, error, OPCODE_INSERT2, NULL, 0, node->offset, node->length)) + return 0; + } + else + { + Error_Report(error, 0, "Assignment left operand can't be assigned to"); + return 0; + } + + return 1; } case EXPR_INT: diff --git a/src/compiler/parse.c b/src/compiler/parse.c index 99d2084..c22f7e8 100644 --- a/src/compiler/parse.c +++ b/src/compiler/parse.c @@ -631,6 +631,52 @@ static Node *parse_expression_statement(Context *ctx) return expr; } +static Node *makeStringExprNode(Context *ctx, const char *str, int len) +{ + if(str == NULL) + str = ""; + + if(len < 0) + len = strlen(str); + + char *copy; + int copyl; + { + copy = BPAlloc_Malloc(ctx->alloc, len + 1); + + if(copy == NULL) + { + Error_Report(ctx->error, 1, "No memory"); + return NULL; + } + + memcpy(copy, str, len); + copy[len] = '\0'; + copyl = len; + } + + StringExprNode *node; + { + node = BPAlloc_Malloc(ctx->alloc, sizeof(StringExprNode)); + + if(node == NULL) + { + Error_Report(ctx->error, 1, "No memory"); + return NULL; + } + + node->base.base.kind = NODE_EXPR; + node->base.base.next = NULL; + node->base.base.offset = ctx->token->offset; + node->base.base.length = ctx->token->length; + node->base.kind = EXPR_STRING; + node->val = copy; + node->len = copyl; + } + + return (Node*) node; +} + static Node *parse_string_primary_expression(Context *ctx) { assert(ctx != NULL); @@ -719,43 +765,9 @@ static Node *parse_string_primary_expression(Context *ctx) temp[temp_used] = '\0'; - char *copy; - int copyl; - - { - copy = BPAlloc_Malloc(ctx->alloc, temp_used + 1); - - if(copy == NULL) - { - Error_Report(ctx->error, 1, "No memory"); - return NULL; - } - - strcpy(copy, temp); - copyl = temp_used; - } - - StringExprNode *node; - { - node = BPAlloc_Malloc(ctx->alloc, sizeof(StringExprNode)); - - if(node == NULL) - { - Error_Report(ctx->error, 1, "No memory"); - return NULL; - } - - node->base.base.kind = NODE_EXPR; - node->base.base.next = NULL; - node->base.base.offset = ctx->token->offset; - node->base.base.length = ctx->token->length; - node->base.kind = EXPR_STRING; - node->val = copy; - node->len = copyl; - } - next(ctx); - return (Node*) node; + + return makeStringExprNode(ctx, temp, temp_used); } static Node *parse_list_primary_expression(Context *ctx) @@ -879,7 +891,30 @@ static Node *parse_map_primary_expression(Context *ctx) while(1) { - Node *key = parse_expression(ctx); + Node *key; + + _Bool key_is_a_single_ident; + { + _Bool key_starts_with_an_ident = current(ctx) == TIDENT; + + next(ctx); + + key_is_a_single_ident = key_starts_with_an_ident && (current(ctx) == TDONE || current(ctx) == ':'); + + prev(ctx); + } + + if(key_is_a_single_ident) + { + assert(current(ctx) == TIDENT); + key = makeStringExprNode(ctx, ctx->src + ctx->token->offset, ctx->token->length); + + next(ctx); + } + else + { + key = parse_expression(ctx); + } if(key == NULL) return NULL; @@ -982,6 +1017,39 @@ static char *copy_token_text(Context *ctx) return copy; } +static Node *makeIdentExprNode(Context *ctx) +{ + char *copy = copy_token_text(ctx); + int copyl = ctx->token->length; + + if(copy == NULL) + { + Error_Report(ctx->error, 1, "No memory"); + return NULL; + } + + IdentExprNode *node; + { + node = BPAlloc_Malloc(ctx->alloc, sizeof(IdentExprNode)); + + if(node == NULL) + { + Error_Report(ctx->error, 1, "No memory"); + return NULL; + } + + node->base.base.kind = NODE_EXPR; + node->base.base.next = NULL; + node->base.base.offset = ctx->token->offset; + node->base.base.length = ctx->token->length; + node->base.kind = EXPR_IDENT; + node->val = copy; + node->len = copyl; + } + + return (Node*) node; +} + static Node *parse_primary_expresion(Context *ctx) { assert(ctx != NULL); @@ -1224,33 +1292,10 @@ static Node *parse_primary_expresion(Context *ctx) case TIDENT: { - char *copy = copy_token_text(ctx); - int copyl = ctx->token->length; + Node *node = makeIdentExprNode(ctx); - if(copy == NULL) - { - Error_Report(ctx->error, 1, "No memory"); - return NULL; - } - - IdentExprNode *node; - { - node = BPAlloc_Malloc(ctx->alloc, sizeof(IdentExprNode)); - - if(node == NULL) - { - Error_Report(ctx->error, 1, "No memory"); - return NULL; - } - - node->base.base.kind = NODE_EXPR; - node->base.base.next = NULL; - node->base.base.offset = ctx->token->offset; - node->base.base.length = ctx->token->length; - node->base.kind = EXPR_IDENT; - node->val = copy; - node->len = copyl; - } + if(node == NULL) + return NULL; next(ctx); @@ -1270,6 +1315,26 @@ static Node *parse_primary_expresion(Context *ctx) return NULL; } +static Node *makeIndexSelectionExprNode(Context *ctx, Node *set, Node *idx) +{ + IndexSelectionExprNode *sel = BPAlloc_Malloc(ctx->alloc, sizeof(IndexSelectionExprNode)); + + if(sel == NULL) + { + Error_Report(ctx->error, 1, "No memory"); + return NULL; + } + + sel->base.base.kind = NODE_EXPR; + sel->base.base.next = NULL; + sel->base.base.offset = -1; + sel->base.base.length = -1; + sel->base.kind = EXPR_SELECT; + sel->set = set; + sel->idx = idx; + return (Node*) sel; +} + static Node *parse_postfix_expression(Context *ctx) { assert(ctx != NULL); @@ -1283,6 +1348,43 @@ static Node *parse_postfix_expression(Context *ctx) { switch(current(ctx)) { + case '.': + { + next(ctx); + + // We expect an identifier after the dot. + + if(done(ctx)) + { + Error_Report(ctx->error, 0, "Source ended after dot of dot selection expression"); + return NULL; + } + + if(current(ctx) != TIDENT) + { + Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" after fot of dot selection expression where an identifier was expected", ctx->token->length, ctx->src + ctx->token->offset); + return NULL; + } + + Node *idx = makeStringExprNode(ctx, ctx->src + ctx->token->offset, ctx->token->length); + + if(idx == NULL) + return NULL; + + Node *sel = makeIndexSelectionExprNode(ctx, node, idx); + + if(sel == NULL) + return NULL; + + sel->offset = node->offset; + sel->length = idx->offset + idx->length - node->offset; + + next(ctx); // Get to the token after the identifier. + + node = (Node*) sel; + break; + } + case '[': { Node *idx = parse_list_primary_expression(ctx); @@ -1298,24 +1400,13 @@ static Node *parse_postfix_expression(Context *ctx) return NULL; } - IndexSelectionExprNode *sel; - { - sel = BPAlloc_Malloc(ctx->alloc, sizeof(IndexSelectionExprNode)); + Node *sel = makeIndexSelectionExprNode(ctx, node, ls->itemc == 1 ? ls->items : (Node*) ls); - if(sel == NULL) - { - Error_Report(ctx->error, 1, "No memory"); - return NULL; - } + if(sel == NULL) + return NULL; - sel->base.base.kind = NODE_EXPR; - sel->base.base.next = NULL; - sel->base.base.offset = node->offset; - sel->base.base.length = idx->offset + idx->length - node->offset; - sel->base.kind = EXPR_SELECT; - sel->set = node; - sel->idx = ls->itemc == 1 ? ls->items : (Node*) ls; - } + sel->offset = node->offset; + sel->length = idx->offset + idx->length - node->offset; node = (Node*) sel; break; diff --git a/src/objects/o_none.c b/src/objects/o_none.c index 3796a71..db15911 100644 --- a/src/objects/o_none.c +++ b/src/objects/o_none.c @@ -2,6 +2,7 @@ #include #include "objects.h" +static _Bool op_eql(Object *self, Object *other); static void print(Object *obj, FILE *fp); static const Type t_none = { @@ -9,6 +10,7 @@ static const Type t_none = { .name = "none", .size = sizeof (Object), .print = print, + .op_eql = op_eql, }; static Object the_none_object = { @@ -31,4 +33,12 @@ static void print(Object *obj, FILE *fp) assert(obj->type == &t_none); fprintf(fp, "none"); +} + +static _Bool op_eql(Object *self, Object *other) +{ + (void) self; + + assert(other->type == &t_none); + return 1; } \ No newline at end of file diff --git a/src/runtime/builtins.c b/src/runtime/builtins.c index 857131d..98cd7c8 100644 --- a/src/runtime/builtins.c +++ b/src/runtime/builtins.c @@ -21,6 +21,22 @@ static Object *bin_count(Runtime *runtime, Object **argv, unsigned int argc, Err return Object_FromInt(n, Runtime_GetHeap(runtime), error); } +static Object *bin_assert(Runtime *runtime, Object **argv, unsigned int argc, Error *error) +{ + assert(argc == 1); + + if(Object_ToBool(argv[0], error)) + { + return Object_NewNone(Runtime_GetHeap(runtime), error); + } + else + { + if(!error->occurred) + Error_Report(error, 0, "Assertion failed"); + return NULL; + } +} + typedef struct { const char *name; int argc; @@ -28,8 +44,9 @@ typedef struct { } BuiltinNativeFunctions; BuiltinNativeFunctions builtins[] = { - {"print", -1, bin_print}, - {"count", 1, bin_count}, + {"print", -1, bin_print}, + {"count", 1, bin_count}, + {"assert", 1, bin_assert}, }; _Bool add_builtins(Runtime *runtime, Error *error) diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index 65165cf..fa6ffb6 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -456,7 +456,19 @@ static _Bool step(Runtime *runtime, Error *error) Error_Report(error, 1, "Invalid instruction index"); return 0; } +/* +#warning "TEMPORARY" + { + int index = runtime->frame->index; + Executable *exe = runtime->frame->exe; + Source *src = Executable_GetSource(exe); + int offset = Executable_GetInstrOffset(exe, index); + int length = Executable_GetInstrLength(exe, index); + const char *str = Source_GetBody(src); + printf("Executing [%.*s]\n", length, str + offset); + } +*/ runtime->frame->index += 1; switch(opcode) @@ -641,30 +653,6 @@ static _Bool step(Runtime *runtime, Error *error) return 1; } - case OPCODE_INSERT: - { - assert(opc == 0); - - if(runtime->frame->used < 3) - { - Error_Report(error, 1, "Frame has not enough values on the stack to run INSERT instruction"); - return NULL; - } - - Object *col = Stack_Top(runtime->stack, -2); - Object *key = Stack_Top(runtime->stack, -1); - Object *val = Stack_Top(runtime->stack, 0); - - assert(col != NULL && key != NULL && val != NULL); - - if(!Runtime_Pop(runtime, error, 2)) - return 0; - - if(!Object_Insert(col, key, val, runtime->heap, error)) - return 0; - return 1; - } - case OPCODE_SELECT: { assert(opc == 0); @@ -693,6 +681,54 @@ static _Bool step(Runtime *runtime, Error *error) return 1; } + case OPCODE_INSERT: + { + assert(opc == 0); + + if(runtime->frame->used < 3) + { + Error_Report(error, 1, "Frame has not enough values on the stack to run INSERT instruction"); + return NULL; + } + + Object *col = Stack_Top(runtime->stack, -2); + Object *key = Stack_Top(runtime->stack, -1); + Object *val = Stack_Top(runtime->stack, 0); + + assert(col != NULL && key != NULL && val != NULL); + + if(!Runtime_Pop(runtime, error, 2)) + return 0; + + if(!Object_Insert(col, key, val, runtime->heap, error)) + return 0; + return 1; + } + + case OPCODE_INSERT2: + { + assert(opc == 0); + + if(runtime->frame->used < 3) + { + Error_Report(error, 1, "Frame has not enough values on the stack to run INSERT2 instruction"); + return NULL; + } + + Object *val = Stack_Top(runtime->stack, -2); + Object *col = Stack_Top(runtime->stack, -1); + Object *key = Stack_Top(runtime->stack, 0); + + assert(col != NULL && key != NULL && val != NULL); + + if(!Runtime_Pop(runtime, error, 2)) + return 0; + + if(!Object_Insert(col, key, val, runtime->heap, error)) + return 0; + return 1; + } + case OPCODE_PUSHINT: { assert(opc == 1);