diff --git a/TODO.txt b/TODO.txt deleted file mode 100644 index 548ad18..0000000 --- a/TODO.txt +++ /dev/null @@ -1,11 +0,0 @@ -- 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. - -- Implement the `count` method for static maps. \ No newline at end of file diff --git a/src/lib/compiler/codegen.c b/src/lib/compiler/codegen.c index e212d62..9cbc85d 100644 --- a/src/lib/compiler/codegen.c +++ b/src/lib/compiler/codegen.c @@ -114,6 +114,26 @@ static void emitInstr_JUMPIFANDPOP(CodegenContext *ctx, CodegenContext_EmitInstr(ctx, OPCODE_JUMPIFANDPOP, opv, 1, off, len); } +static void emitInstr_JUMPIFANDPOP_2(CodegenContext *ctx, + Label *op0, + int off, int len) +{ + Operand opv[1] = { + { .type = OPTP_PROMISE, .as_promise = Label_ToPromise(op0) } + }; + CodegenContext_EmitInstr(ctx, OPCODE_JUMPIFANDPOP, opv, 1, off, len); +} + +static void emitInstr_PUSHTRU(CodegenContext *ctx, int off, int len) +{ + CodegenContext_EmitInstr(ctx, OPCODE_PUSHTRU, NULL, 0, off, len); +} + +static void emitInstr_PUSHFLS(CodegenContext *ctx, int off, int len) +{ + CodegenContext_EmitInstr(ctx, OPCODE_PUSHFLS, NULL, 0, off, len); +} + static void emitInstrForNode(CodegenContext *ctx, Node *node, Label *label_break); static Opcode exprkind_to_opcode(ExprKind kind) @@ -208,37 +228,6 @@ static void emitInstrForFuncNode(CodegenContext *ctx, FunctionNode *func) Label_Free(label_jump); } -static void emitInstrForIfElseNode(CodegenContext *ctx, IfElseNode *ifelse, Label *label_break) -{ - emitInstrForNode(ctx, ifelse->condition, label_break); - if(ifelse->false_branch) - { - Label *label_else = Label_New(ctx); - Label *label_done = Label_New(ctx); - emitInstr_JUMPIFNOTANDPOP(ctx, label_else, ifelse->base.offset, ifelse->base.length); - emitInstrForNode(ctx, ifelse->true_branch, label_break); - if(ifelse->true_branch->kind == NODE_EXPR) - emitInstr_POP(ctx, 1, ifelse->true_branch->offset, 0); - emitInstr_JUMP(ctx, label_done, ifelse->base.offset, ifelse->base.length); - Label_SetHere(label_else, ctx); - emitInstrForNode(ctx, ifelse->false_branch, label_break); - if(ifelse->false_branch->kind == NODE_EXPR) - emitInstr_POP1(ctx, ifelse->false_branch->offset, 0); - Label_SetHere(label_done, ctx); - Label_Free(label_else); - Label_Free(label_done); - } - else - { - Label *label_done = Label_New(ctx); - emitInstr_JUMPIFNOTANDPOP(ctx, label_done, ifelse->base.offset, ifelse->base.length); - emitInstrForNode(ctx, ifelse->true_branch, label_break); - if(ifelse->true_branch->kind == NODE_EXPR) - emitInstr_POP1(ctx, ifelse->true_branch->offset, 0); - Label_SetHere(label_done, ctx); - Label_Free(label_done); - } -} static void flattenTupleTree(CodegenContext *ctx, ExprNode *root, ExprNode *tuple[], int max, int *count) { @@ -317,52 +306,8 @@ static void emitInstrForAssignmentNode(CodegenContext *ctx, OperExprNode *asgn, } } -static void emitInstrForWhileLoopNode(CodegenContext *ctx, WhileNode *loop, Label *label_break) -{ - /* - * start: - * - * JUMPIFNOTANDPOP end - * - * JUMP start - * end: - */ - - Label *label_start = Label_New(ctx); - Label *label_end = Label_New(ctx); - Label_SetHere(label_start, ctx); - emitInstrForNode(ctx, loop->condition, label_break); - emitInstr_JUMPIFNOTANDPOP(ctx, label_end, loop->condition->offset, loop->condition->length); - emitInstrForNode(ctx, loop->body, label_end); - if(loop->body->kind == NODE_EXPR) - emitInstr_POP1(ctx, loop->body->offset, 0); - emitInstr_JUMP(ctx, label_start, loop->base.offset, loop->base.length); - Label_SetHere(label_end, ctx); - Label_Free(label_start); - Label_Free(label_end); -} - -static void emitInstrForDoWhileLoopNode(CodegenContext *ctx, DoWhileNode *loop, Label *label_break) -{ - /* - * start: - * - * - * JUMPIFANDPOP start - */ - - Label *label_end = Label_New(ctx); - long long int start = CodegenContext_InstrCount(ctx); - emitInstrForNode(ctx, loop->body, label_end); - if(loop->body->kind == NODE_EXPR) - emitInstr_POP1(ctx, loop->body->offset, 0); - emitInstrForNode(ctx, loop->condition, label_break); - emitInstr_JUMPIFANDPOP(ctx, start, loop->condition->offset, loop->condition->length); - Label_SetHere(label_end, ctx); - Label_Free(label_end); -} - -static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr, Label *label_break) +static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr, + Label *label_break) { switch(expr->kind) { @@ -372,20 +317,12 @@ static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr, Label *lab return; // For the compiler warning. case EXPR_NOT: - case EXPR_POS: - case EXPR_NEG: - case EXPR_ADD: - case EXPR_SUB: - case EXPR_MUL: - case EXPR_DIV: - case EXPR_EQL: - case EXPR_NQL: - case EXPR_LSS: - case EXPR_LEQ: - case EXPR_GRT: - case EXPR_GEQ: - case EXPR_AND: - case EXPR_OR: + case EXPR_POS: case EXPR_NEG: + case EXPR_ADD: case EXPR_SUB: + case EXPR_MUL: case EXPR_DIV: + case EXPR_EQL: case EXPR_NQL: + case EXPR_LSS: case EXPR_LEQ: + case EXPR_GRT: case EXPR_GEQ: { OperExprNode *oper = (OperExprNode*) expr; for(Node *operand = oper->head; operand; operand = operand->next) @@ -394,6 +331,69 @@ static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr, Label *lab return; } + case EXPR_AND: + { + OperExprNode *oper = (OperExprNode*) expr; + + /* + * + * JUMPIFNOTANDPOP false; + * + * JUMPIFNOTANDPOP false; + * PUSHTRU; + * JUMP end; + * false: + * PUSHFLS; + * end: + * + */ + + Label *label_end = Label_New(ctx); + Label *label_false = Label_New(ctx); + for(Node *operand = oper->head; operand; operand = operand->next) { + emitInstrForNode(ctx, operand, label_break); + emitInstr_JUMPIFNOTANDPOP(ctx, label_false, expr->base.offset, expr->base.length); + } + emitInstr_PUSHTRU(ctx, expr->base.offset, expr->base.length); + emitInstr_JUMP(ctx, label_end, expr->base.offset, expr->base.length); + Label_SetHere(label_false, ctx); + emitInstr_PUSHFLS(ctx, expr->base.offset, expr->base.length); + Label_SetHere(label_end, ctx); + return; + } + + case EXPR_OR: + { + OperExprNode *oper = (OperExprNode*) expr; + + /* + * + * JUMPIFANDPOP true; + * + * JUMPIFANDPOP true; + * PUSHFLS; + * JUMP end; + * true: + * PUSHTRU; + * JUMP end; + * end: + * + */ + + Label *label_end = Label_New(ctx); + Label *label_true = Label_New(ctx); + for(Node *operand = oper->head; operand; operand = operand->next) { + emitInstrForNode(ctx, operand, label_break); + emitInstr_JUMPIFANDPOP_2(ctx, label_true, expr->base.offset, expr->base.length); + } + emitInstr_PUSHFLS(ctx, expr->base.offset, expr->base.length); + emitInstr_JUMP(ctx, label_end, expr->base.offset, expr->base.length); + Label_SetHere(label_true, ctx); + emitInstr_PUSHTRU(ctx, expr->base.offset, expr->base.length); + Label_SetHere(label_end, ctx); + return; + } + case EXPR_ASS: emitInstrForAssignmentNode(ctx, (OperExprNode*) expr, label_break); return; @@ -502,6 +502,83 @@ static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr, Label *lab } } +static void emitInstrForIfElseNode(CodegenContext *ctx, IfElseNode *ifelse, Label *label_break) +{ + emitInstrForExprNode(ctx, ifelse->condition, label_break); + if(ifelse->false_branch) + { + Label *label_else = Label_New(ctx); + Label *label_done = Label_New(ctx); + emitInstr_JUMPIFNOTANDPOP(ctx, label_else, ifelse->condition->offset, ifelse->condition->length); + emitInstrForNode(ctx, ifelse->true_branch, label_break); + if(ifelse->true_branch->kind == NODE_EXPR) + emitInstr_POP(ctx, 1, ifelse->true_branch->offset, 0); + emitInstr_JUMP(ctx, label_done, ifelse->base.offset, ifelse->base.length); + Label_SetHere(label_else, ctx); + emitInstrForNode(ctx, ifelse->false_branch, label_break); + if(ifelse->false_branch->kind == NODE_EXPR) + emitInstr_POP1(ctx, ifelse->false_branch->offset, 0); + Label_SetHere(label_done, ctx); + Label_Free(label_else); + Label_Free(label_done); + } + else + { + Label *label_done = Label_New(ctx); + emitInstr_JUMPIFNOTANDPOP(ctx, label_done, ifelse->condition->offset, ifelse->condition->length); + emitInstrForNode(ctx, ifelse->true_branch, label_break); + if(ifelse->true_branch->kind == NODE_EXPR) + emitInstr_POP1(ctx, ifelse->true_branch->offset, 0); + Label_SetHere(label_done, ctx); + Label_Free(label_done); + } +} + +static void emitInstrForWhileLoopNode(CodegenContext *ctx, WhileNode *loop, Label *label_break) +{ + /* + * start: + * + * JUMPIFNOTANDPOP end + * + * JUMP start + * end: + */ + + Label *label_start = Label_New(ctx); + Label *label_end = Label_New(ctx); + Label_SetHere(label_start, ctx); + emitInstrForNode(ctx, loop->condition, label_break); + emitInstr_JUMPIFNOTANDPOP(ctx, label_end, loop->condition->offset, loop->condition->length); + emitInstrForNode(ctx, loop->body, label_end); + if(loop->body->kind == NODE_EXPR) + emitInstr_POP1(ctx, loop->body->offset, 0); + emitInstr_JUMP(ctx, label_start, loop->base.offset, loop->base.length); + Label_SetHere(label_end, ctx); + Label_Free(label_start); + Label_Free(label_end); +} + +static void emitInstrForDoWhileLoopNode(CodegenContext *ctx, DoWhileNode *loop, Label *label_break) +{ + /* + * start: + * + * + * JUMPIFANDPOP start + */ + + Label *label_end = Label_New(ctx); + long long int start = CodegenContext_InstrCount(ctx); + emitInstrForNode(ctx, loop->body, label_end); + if(loop->body->kind == NODE_EXPR) + emitInstr_POP1(ctx, loop->body->offset, 0); + emitInstrForNode(ctx, loop->condition, label_break); + emitInstr_JUMPIFANDPOP(ctx, start, loop->condition->offset, loop->condition->length); + Label_SetHere(label_end, ctx); + Label_Free(label_end); +} + static void emitInstrForNode(CodegenContext *ctx, Node *node, Label *label_break) { assert(node != NULL); diff --git a/src/lib/noja.c b/src/lib/noja.c index 35e9e0f..f096a6b 100644 --- a/src/lib/noja.c +++ b/src/lib/noja.c @@ -120,6 +120,7 @@ static _Bool disassemble(Source *src) Executable *exe = compile_source_and_print_error_on_failure(src); if(exe == NULL) return 0; Executable_Dump(exe); + Executable_Free(exe); return 1; } diff --git a/tests/suite/Testcase_002_021 b/tests/suite/Testcase_002_021 index 24cc952..6387654 100644 --- a/tests/suite/Testcase_002_021 +++ b/tests/suite/Testcase_002_021 @@ -4,9 +4,15 @@ 1 and 2; #bytecode - + PUSHINT 1; + JUMPIFNOTANDPOP false; PUSHINT 2; - AND; + JUMPIFNOTANDPOP false; + PUSHTRU; + JUMP end; +false: + PUSHFLS; +end: POP 1; RETURN 0; \ No newline at end of file diff --git a/tests/suite/Testcase_002_022 b/tests/suite/Testcase_002_022 index eb1e6b9..df0592f 100644 --- a/tests/suite/Testcase_002_022 +++ b/tests/suite/Testcase_002_022 @@ -6,7 +6,13 @@ #bytecode PUSHINT 1; + JUMPIFANDPOP true; PUSHINT 2; - OR; + JUMPIFANDPOP true; + PUSHFLS; + JUMP end; +true: + PUSHTRU; +end: POP 1; RETURN 0; \ No newline at end of file diff --git a/tests/suite/Testcase_002_032 b/tests/suite/Testcase_002_032 index d5327c7..09ec42c 100644 --- a/tests/suite/Testcase_002_032 +++ b/tests/suite/Testcase_002_032 @@ -6,12 +6,29 @@ #bytecode PUSHVAR "A"; + JUMPIFNOTANDPOP false_0; PUSHVAR "B"; - AND; + JUMPIFNOTANDPOP false_0; + PUSHTRU; + JUMP end_0; +false_0: + PUSHFLS; +end_0: + JUMPIFANDPOP true_2; PUSHVAR "C"; + JUMPIFNOTANDPOP false_1; PUSHVAR "D"; - AND; - OR; + JUMPIFNOTANDPOP false_1; + PUSHTRU; + JUMP end_1; +false_1: + PUSHFLS; +end_1: + JUMPIFANDPOP true_2; + PUSHFLS; + JUMP end_2; +true_2: + PUSHTRU; +end_2: POP 1; - - RETURN 0; \ No newline at end of file + RETURN 0; \ No newline at end of file diff --git a/tests/suite/Testcase_002_033 b/tests/suite/Testcase_002_033 index bff0dd5..a9315ed 100644 --- a/tests/suite/Testcase_002_033 +++ b/tests/suite/Testcase_002_033 @@ -4,14 +4,31 @@ X or Y and Z or W; #bytecode - - PUSHVAR "X"; - PUSHVAR "Y"; - PUSHVAR "Z"; - AND; - OR; - PUSHVAR "W"; - OR; - POP 1; - RETURN 0; \ No newline at end of file + PUSHVAR "X"; + JUMPIFANDPOP true_0; + PUSHVAR "Y"; + JUMPIFNOTANDPOP false_1; + PUSHVAR "Z"; + JUMPIFNOTANDPOP false_1; + PUSHTRU; + JUMP end_1; +false_1: + PUSHFLS; +end_1: + JUMPIFANDPOP true_0; + PUSHFLS; + JUMP end_0; +true_0: + PUSHTRU; +end_0: + JUMPIFANDPOP true_2; + PUSHVAR "W"; + JUMPIFANDPOP true_2; + PUSHFLS; + JUMP end_2; +true_2: + PUSHTRU; +end_2: + POP 1; + RETURN 0; \ No newline at end of file diff --git a/tests/suite/Testcase_002_34 b/tests/suite/Testcase_002_034 similarity index 100% rename from tests/suite/Testcase_002_34 rename to tests/suite/Testcase_002_034 diff --git a/tests/test.noja b/tests/test.noja index 21a5bed..dcbd197 100644 --- a/tests/test.noja +++ b/tests/test.noja @@ -69,6 +69,13 @@ assert((false or false) == false); } +# Test short-circuitness of the logical operators +{ + # The assertions shouldn't trigger. + true or assert(false); + false and assert(false); +} + # Test lists. { [1]; [1.0]; ['x']; [1, 1]; [1.0, 1.0]; ['x', 'x'];