made and and or operators short circuits

This commit is contained in:
cozis
2022-08-13 23:37:22 +02:00
parent 8565722bab
commit d24877c5f3
9 changed files with 240 additions and 120 deletions
-11
View File
@@ -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.
+168 -91
View File
@@ -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:
* <condition>
* JUMPIFNOTANDPOP end
* <body>
* 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:
* <body>
* <condition>
* 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;
/*
* <left_oper>
* JUMPIFNOTANDPOP false;
* <right_oper>
* 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;
/*
* <left_oper>
* JUMPIFANDPOP true;
* <right_oper>
* 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:
* <condition>
* JUMPIFNOTANDPOP end
* <body>
* 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:
* <body>
* <condition>
* 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);
+1
View File
@@ -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;
}
+7 -1
View File
@@ -6,7 +6,13 @@
#bytecode
PUSHINT 1;
JUMPIFNOTANDPOP false;
PUSHINT 2;
AND;
JUMPIFNOTANDPOP false;
PUSHTRU;
JUMP end;
false:
PUSHFLS;
end:
POP 1;
RETURN 0;
+7 -1
View File
@@ -6,7 +6,13 @@
#bytecode
PUSHINT 1;
JUMPIFANDPOP true;
PUSHINT 2;
OR;
JUMPIFANDPOP true;
PUSHFLS;
JUMP end;
true:
PUSHTRU;
end:
POP 1;
RETURN 0;
+21 -4
View File
@@ -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;
+21 -4
View File
@@ -6,12 +6,29 @@
#bytecode
PUSHVAR "X";
JUMPIFANDPOP true_0;
PUSHVAR "Y";
JUMPIFNOTANDPOP false_1;
PUSHVAR "Z";
AND;
OR;
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";
OR;
JUMPIFANDPOP true_2;
PUSHFLS;
JUMP end_2;
true_2:
PUSHTRU;
end_2:
POP 1;
RETURN 0;
+7
View File
@@ -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'];