Implement chained comparison operators

This commit is contained in:
K. Lange 2021-03-21 12:41:54 +09:00
parent d0ea1278a6
commit a25315de92
6 changed files with 107 additions and 44 deletions

View File

@ -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,

View File

@ -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),

View File

@ -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,+)

View File

@ -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;

View File

@ -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)

View File

@ -0,0 +1,13 @@
False
False
False
False
True
False
True
False
False
False
True
False
True