made and and or operators short circuits
This commit is contained in:
@@ -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
@@ -114,6 +114,26 @@ static void emitInstr_JUMPIFANDPOP(CodegenContext *ctx,
|
|||||||
CodegenContext_EmitInstr(ctx, OPCODE_JUMPIFANDPOP, opv, 1, off, len);
|
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 void emitInstrForNode(CodegenContext *ctx, Node *node, Label *label_break);
|
||||||
|
|
||||||
static Opcode exprkind_to_opcode(ExprKind kind)
|
static Opcode exprkind_to_opcode(ExprKind kind)
|
||||||
@@ -208,37 +228,6 @@ static void emitInstrForFuncNode(CodegenContext *ctx, FunctionNode *func)
|
|||||||
Label_Free(label_jump);
|
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)
|
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)
|
static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr,
|
||||||
{
|
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)
|
|
||||||
{
|
{
|
||||||
switch(expr->kind)
|
switch(expr->kind)
|
||||||
{
|
{
|
||||||
@@ -372,20 +317,12 @@ static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr, Label *lab
|
|||||||
return; // For the compiler warning.
|
return; // For the compiler warning.
|
||||||
|
|
||||||
case EXPR_NOT:
|
case EXPR_NOT:
|
||||||
case EXPR_POS:
|
case EXPR_POS: case EXPR_NEG:
|
||||||
case EXPR_NEG:
|
case EXPR_ADD: case EXPR_SUB:
|
||||||
case EXPR_ADD:
|
case EXPR_MUL: case EXPR_DIV:
|
||||||
case EXPR_SUB:
|
case EXPR_EQL: case EXPR_NQL:
|
||||||
case EXPR_MUL:
|
case EXPR_LSS: case EXPR_LEQ:
|
||||||
case EXPR_DIV:
|
case EXPR_GRT: case EXPR_GEQ:
|
||||||
case EXPR_EQL:
|
|
||||||
case EXPR_NQL:
|
|
||||||
case EXPR_LSS:
|
|
||||||
case EXPR_LEQ:
|
|
||||||
case EXPR_GRT:
|
|
||||||
case EXPR_GEQ:
|
|
||||||
case EXPR_AND:
|
|
||||||
case EXPR_OR:
|
|
||||||
{
|
{
|
||||||
OperExprNode *oper = (OperExprNode*) expr;
|
OperExprNode *oper = (OperExprNode*) expr;
|
||||||
for(Node *operand = oper->head; operand; operand = operand->next)
|
for(Node *operand = oper->head; operand; operand = operand->next)
|
||||||
@@ -394,6 +331,69 @@ static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr, Label *lab
|
|||||||
return;
|
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:
|
case EXPR_ASS:
|
||||||
emitInstrForAssignmentNode(ctx, (OperExprNode*) expr, label_break);
|
emitInstrForAssignmentNode(ctx, (OperExprNode*) expr, label_break);
|
||||||
return;
|
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)
|
static void emitInstrForNode(CodegenContext *ctx, Node *node, Label *label_break)
|
||||||
{
|
{
|
||||||
assert(node != NULL);
|
assert(node != NULL);
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ static _Bool disassemble(Source *src)
|
|||||||
Executable *exe = compile_source_and_print_error_on_failure(src);
|
Executable *exe = compile_source_and_print_error_on_failure(src);
|
||||||
if(exe == NULL) return 0;
|
if(exe == NULL) return 0;
|
||||||
Executable_Dump(exe);
|
Executable_Dump(exe);
|
||||||
|
Executable_Free(exe);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,13 @@
|
|||||||
#bytecode
|
#bytecode
|
||||||
|
|
||||||
PUSHINT 1;
|
PUSHINT 1;
|
||||||
|
JUMPIFNOTANDPOP false;
|
||||||
PUSHINT 2;
|
PUSHINT 2;
|
||||||
AND;
|
JUMPIFNOTANDPOP false;
|
||||||
|
PUSHTRU;
|
||||||
|
JUMP end;
|
||||||
|
false:
|
||||||
|
PUSHFLS;
|
||||||
|
end:
|
||||||
POP 1;
|
POP 1;
|
||||||
RETURN 0;
|
RETURN 0;
|
||||||
@@ -6,7 +6,13 @@
|
|||||||
#bytecode
|
#bytecode
|
||||||
|
|
||||||
PUSHINT 1;
|
PUSHINT 1;
|
||||||
|
JUMPIFANDPOP true;
|
||||||
PUSHINT 2;
|
PUSHINT 2;
|
||||||
OR;
|
JUMPIFANDPOP true;
|
||||||
|
PUSHFLS;
|
||||||
|
JUMP end;
|
||||||
|
true:
|
||||||
|
PUSHTRU;
|
||||||
|
end:
|
||||||
POP 1;
|
POP 1;
|
||||||
RETURN 0;
|
RETURN 0;
|
||||||
@@ -6,12 +6,29 @@
|
|||||||
#bytecode
|
#bytecode
|
||||||
|
|
||||||
PUSHVAR "A";
|
PUSHVAR "A";
|
||||||
|
JUMPIFNOTANDPOP false_0;
|
||||||
PUSHVAR "B";
|
PUSHVAR "B";
|
||||||
AND;
|
JUMPIFNOTANDPOP false_0;
|
||||||
|
PUSHTRU;
|
||||||
|
JUMP end_0;
|
||||||
|
false_0:
|
||||||
|
PUSHFLS;
|
||||||
|
end_0:
|
||||||
|
JUMPIFANDPOP true_2;
|
||||||
PUSHVAR "C";
|
PUSHVAR "C";
|
||||||
|
JUMPIFNOTANDPOP false_1;
|
||||||
PUSHVAR "D";
|
PUSHVAR "D";
|
||||||
AND;
|
JUMPIFNOTANDPOP false_1;
|
||||||
OR;
|
PUSHTRU;
|
||||||
|
JUMP end_1;
|
||||||
|
false_1:
|
||||||
|
PUSHFLS;
|
||||||
|
end_1:
|
||||||
|
JUMPIFANDPOP true_2;
|
||||||
|
PUSHFLS;
|
||||||
|
JUMP end_2;
|
||||||
|
true_2:
|
||||||
|
PUSHTRU;
|
||||||
|
end_2:
|
||||||
POP 1;
|
POP 1;
|
||||||
|
|
||||||
RETURN 0;
|
RETURN 0;
|
||||||
@@ -6,12 +6,29 @@
|
|||||||
#bytecode
|
#bytecode
|
||||||
|
|
||||||
PUSHVAR "X";
|
PUSHVAR "X";
|
||||||
|
JUMPIFANDPOP true_0;
|
||||||
PUSHVAR "Y";
|
PUSHVAR "Y";
|
||||||
|
JUMPIFNOTANDPOP false_1;
|
||||||
PUSHVAR "Z";
|
PUSHVAR "Z";
|
||||||
AND;
|
JUMPIFNOTANDPOP false_1;
|
||||||
OR;
|
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";
|
PUSHVAR "W";
|
||||||
OR;
|
JUMPIFANDPOP true_2;
|
||||||
|
PUSHFLS;
|
||||||
|
JUMP end_2;
|
||||||
|
true_2:
|
||||||
|
PUSHTRU;
|
||||||
|
end_2:
|
||||||
POP 1;
|
POP 1;
|
||||||
|
|
||||||
RETURN 0;
|
RETURN 0;
|
||||||
@@ -69,6 +69,13 @@
|
|||||||
assert((false or false) == false);
|
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.
|
# Test lists.
|
||||||
{
|
{
|
||||||
[1]; [1.0]; ['x']; [1, 1]; [1.0, 1.0]; ['x', 'x'];
|
[1]; [1.0]; ['x']; [1, 1]; [1.0, 1.0]; ['x', 'x'];
|
||||||
|
|||||||
Reference in New Issue
Block a user