From a25315de92ed06db12c99fe97ca073d2e6f938ee Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Sun, 21 Mar 2021 12:41:54 +0900 Subject: [PATCH] Implement chained comparison operators --- src/chunk.h | 1 + src/compiler.c | 112 +++++++++++------- src/opcodes.h | 1 + src/vm.c | 6 + test/testChainedComparisonOperators.krk | 18 +++ .../testChainedComparisonOperators.krk.expect | 13 ++ 6 files changed, 107 insertions(+), 44 deletions(-) create mode 100644 test/testChainedComparisonOperators.krk create mode 100644 test/testChainedComparisonOperators.krk.expect diff --git a/src/chunk.h b/src/chunk.h index ece910b..e27a828 100644 --- a/src/chunk.h +++ b/src/chunk.h @@ -110,6 +110,7 @@ typedef enum { OP_PUSH_TRY, OP_PUSH_WITH, OP_YIELD_FROM, + OP_JUMP_IF_FALSE_OR_POP, OP_CALL_LONG = 192, OP_CLASS_LONG, diff --git a/src/compiler.c b/src/compiler.c index c4d2af7..24b247e 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -494,11 +494,35 @@ static void number(int canAssign) { emitConstant(INTEGER_VAL(value)); } -static void binary(int canAssign) { +static int emitJump(uint8_t opcode) { + emitByte(opcode); + emitBytes(0xFF, 0xFF); + return currentChunk()->count - 2; +} + +static void patchJump(int offset) { + int jump = currentChunk()->count - offset - 2; + if (jump > 0xFFFF) { + error("Unsupported far jump (we'll get there)"); + } + + currentChunk()->code[offset] = (jump >> 8) & 0xFF; + currentChunk()->code[offset + 1] = (jump) & 0xFF; +} + +static void compareChained(int inner) { KrkTokenType operatorType = parser.previous.type; + if (operatorType == TOKEN_NOT) consume(TOKEN_IN, "'in' must follow infix 'not'"); + int invert = (operatorType == TOKEN_IS && match(TOKEN_NOT)); + ParseRule * rule = getRule(operatorType); parsePrecedence((Precedence)(rule->precedence + 1)); + if (getRule(parser.current.type)->precedence == PREC_COMPARISON) { + emitByte(OP_SWAP); + emitBytes(OP_DUP, 1); + } + switch (operatorType) { case TOKEN_BANG_EQUAL: emitBytes(OP_EQUAL, OP_NOT); break; case TOKEN_EQUAL_EQUAL: emitByte(OP_EQUAL); break; @@ -507,6 +531,40 @@ static void binary(int canAssign) { case TOKEN_LESS: emitByte(OP_LESS); break; case TOKEN_LESS_EQUAL: emitBytes(OP_GREATER, OP_NOT); break; + case TOKEN_IS: emitByte(OP_IS); if (invert) emitByte(OP_NOT); break; + + case TOKEN_IN: emitByte(OP_INVOKE_CONTAINS); break; + case TOKEN_NOT: emitBytes(OP_INVOKE_CONTAINS, OP_NOT); break; + + default: error("Invalid binary comparison operator?"); break; + } + + if (getRule(parser.current.type)->precedence == PREC_COMPARISON) { + size_t exitJump = emitJump(OP_JUMP_IF_FALSE_OR_POP); + advance(); + compareChained(1); + patchJump(exitJump); + if (getRule(parser.current.type)->precedence != PREC_COMPARISON) { + if (!inner) { + emitBytes(OP_SWAP,OP_POP); + } + } + } else if (inner) { + emitByte(OP_JUMP); + emitBytes(0,2); + } +} + +static void compare(int canAssign) { + compareChained(0); +} + +static void binary(int canAssign) { + KrkTokenType operatorType = parser.previous.type; + ParseRule * rule = getRule(operatorType); + parsePrecedence((Precedence)(rule->precedence + 1)); + + switch (operatorType) { case TOKEN_PIPE: emitByte(OP_BITOR); break; case TOKEN_CARET: emitByte(OP_BITXOR); break; case TOKEN_AMPERSAND: emitByte(OP_BITAND); break; @@ -743,24 +801,6 @@ _dotDone: } } -static void in_(int canAssign) { - parsePrecedence(PREC_COMPARISON); - emitByte(OP_INVOKE_CONTAINS); -} - -static void not_(int canAssign) { - consume(TOKEN_IN, "infix not must be followed by 'in'"); - in_(canAssign); - emitByte(OP_NOT); -} - -static void is_(int canAssign) { - int invert = match(TOKEN_NOT); - parsePrecedence(PREC_COMPARISON); - emitByte(OP_IS); - if (invert) emitByte(OP_NOT); -} - static void literal(int canAssign) { switch (parser.previous.type) { case TOKEN_FALSE: emitByte(OP_FALSE); break; @@ -924,22 +964,6 @@ static void endScope() { } } -static int emitJump(uint8_t opcode) { - emitByte(opcode); - emitBytes(0xFF, 0xFF); - return currentChunk()->count - 2; -} - -static void patchJump(int offset) { - int jump = currentChunk()->count - offset - 2; - if (jump > 0xFFFF) { - error("Unsupported far jump (we'll get there)"); - } - - currentChunk()->code[offset] = (jump >> 8) & 0xFF; - currentChunk()->code[offset + 1] = (jump) & 0xFF; -} - static void block(size_t indentation, const char * blockName) { if (match(TOKEN_EOL)) { if (check(TOKEN_INDENTATION)) { @@ -2682,14 +2706,17 @@ ParseRule krk_parseRules[] = { RULE(TOKEN_POW, NULL, binary, PREC_EXPONENT), RULE(TOKEN_MODULO, NULL, binary, PREC_FACTOR), RULE(TOKEN_BANG, bitunary, NULL, PREC_NONE), - RULE(TOKEN_BANG_EQUAL, NULL, binary, PREC_COMPARISON), RULE(TOKEN_EQUAL, NULL, NULL, PREC_NONE), RULE(TOKEN_WALRUS, NULL, NULL, PREC_NONE), - RULE(TOKEN_EQUAL_EQUAL, NULL, binary, PREC_COMPARISON), - RULE(TOKEN_GREATER, NULL, binary, PREC_COMPARISON), - RULE(TOKEN_GREATER_EQUAL, NULL, binary, PREC_COMPARISON), - RULE(TOKEN_LESS, NULL, binary, PREC_COMPARISON), - RULE(TOKEN_LESS_EQUAL, NULL, binary, PREC_COMPARISON), + RULE(TOKEN_BANG_EQUAL, NULL, compare, PREC_COMPARISON), + RULE(TOKEN_EQUAL_EQUAL, NULL, compare, PREC_COMPARISON), + RULE(TOKEN_GREATER, NULL, compare, PREC_COMPARISON), + RULE(TOKEN_GREATER_EQUAL, NULL, compare, PREC_COMPARISON), + RULE(TOKEN_LESS, NULL, compare, PREC_COMPARISON), + RULE(TOKEN_LESS_EQUAL, NULL, compare, PREC_COMPARISON), + RULE(TOKEN_IN, NULL, compare, PREC_COMPARISON), + RULE(TOKEN_NOT, unot_, compare, PREC_COMPARISON), + RULE(TOKEN_IS, NULL, compare, PREC_COMPARISON), RULE(TOKEN_IDENTIFIER, variable, NULL, PREC_NONE), RULE(TOKEN_STRING, string, NULL, PREC_NONE), RULE(TOKEN_BIG_STRING, string, NULL, PREC_NONE), @@ -2704,11 +2731,8 @@ ParseRule krk_parseRules[] = { RULE(TOKEN_DEF, NULL, NULL, PREC_NONE), RULE(TOKEN_DEL, NULL, NULL, PREC_NONE), RULE(TOKEN_IF, NULL, ternary,PREC_TERNARY), - RULE(TOKEN_IN, NULL, in_, PREC_COMPARISON), RULE(TOKEN_LET, NULL, NULL, PREC_NONE), RULE(TOKEN_NONE, literal, NULL, PREC_NONE), - RULE(TOKEN_NOT, unot_, not_, PREC_COMPARISON), - RULE(TOKEN_IS, NULL, is_, PREC_COMPARISON), RULE(TOKEN_OR, NULL, or_, PREC_OR), RULE(TOKEN_RETURN, NULL, NULL, PREC_NONE), RULE(TOKEN_SELF, self, NULL, PREC_NONE), diff --git a/src/opcodes.h b/src/opcodes.h index 2c862bb..c6917a7 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -81,3 +81,4 @@ JUMP(OP_LOOP,-) JUMP(OP_PUSH_TRY,+) JUMP(OP_PUSH_WITH,+) JUMP(OP_YIELD_FROM,+) +JUMP(OP_JUMP_IF_FALSE_OR_POP,+) diff --git a/src/vm.c b/src/vm.c index e4da3af..3fbd321 100644 --- a/src/vm.c +++ b/src/vm.c @@ -2305,6 +2305,12 @@ _finishReturn: (void)0; if (krk_isFalsey(krk_peek(0))) frame->ip += offset; break; } + case OP_JUMP_IF_FALSE_OR_POP: { + uint16_t offset = OPERAND; + if (krk_isFalsey(krk_peek(0))) frame->ip += offset; + else krk_pop(); + break; + } case OP_JUMP_IF_TRUE: { uint16_t offset = OPERAND; if (!krk_isFalsey(krk_peek(0))) frame->ip += offset; diff --git a/test/testChainedComparisonOperators.krk b/test/testChainedComparisonOperators.krk new file mode 100644 index 0000000..98cfeaa --- /dev/null +++ b/test/testChainedComparisonOperators.krk @@ -0,0 +1,18 @@ +def foo(a,b,lst=[30]): + return 1 < a < 100 > b < 30 in lst is not None + +print(foo(1,2)) +print(foo(0,2)) +print(foo(101,2)) +print(foo(40,50)) +print(foo(40,29)) +print(foo(40,29,[40])) +print(foo(40,29,(20,30,40))) + +print(1 > 2 < 0 > 4 < 7) +print(1 > 0 < 0 > 4 < 7) +print(1 > 0 < 1 > 4 < 7) +print(1 > 0 < 7 > 4 < 7) + +print(1 < 0 > -1) +print(1 < 2 > -1) diff --git a/test/testChainedComparisonOperators.krk.expect b/test/testChainedComparisonOperators.krk.expect new file mode 100644 index 0000000..1ee29ad --- /dev/null +++ b/test/testChainedComparisonOperators.krk.expect @@ -0,0 +1,13 @@ +False +False +False +False +True +False +True +False +False +False +True +False +True