diff --git a/chunk.h b/chunk.h index 60e857d..e9a9185 100644 --- a/chunk.h +++ b/chunk.h @@ -56,6 +56,7 @@ typedef enum { OP_DUP, OP_SWAP, OP_KWARGS, + OP_POW, OP_BITOR, OP_BITXOR, diff --git a/compiler.c b/compiler.c index 0ca9a84..4404021 100644 --- a/compiler.c +++ b/compiler.c @@ -52,15 +52,15 @@ typedef enum { PREC_TERNARY, PREC_OR, /* or */ PREC_AND, /* and */ + PREC_COMPARISON, /* < > <= >= in 'not in' */ PREC_BITOR, /* | */ PREC_BITXOR, /* ^ */ PREC_BITAND, /* & */ - PREC_EQUALITY, /* == != in */ - PREC_COMPARISON, /* < > <= >= */ PREC_SHIFT, /* << >> */ PREC_TERM, /* + - */ PREC_FACTOR, /* * / % */ PREC_UNARY, /* ! - not */ + PREC_EXPONENT, /* ** */ PREC_CALL, /* . () */ PREC_PRIMARY } Precedence; @@ -225,8 +225,9 @@ static void advance() { #ifdef ENABLE_SCAN_TRACING if (vm.flags & KRK_ENABLE_SCAN_TRACING) { - fprintf(stderr, "[%s %d:%d '%.*s'] ", + fprintf(stderr, "[%s<%d> %d:%d '%.*s'] ", getRule(parser.current.type)->name, + (int)parser.current.type, (int)parser.current.line, (int)parser.current.col, (int)parser.current.length, @@ -451,6 +452,7 @@ static void binary(int canAssign) { case TOKEN_PLUS: emitByte(OP_ADD); break; case TOKEN_MINUS: emitByte(OP_SUBTRACT); break; case TOKEN_ASTERISK: emitByte(OP_MULTIPLY); break; + case TOKEN_POW: emitByte(OP_POW); break; case TOKEN_SOLIDUS: emitByte(OP_DIVIDE); break; case TOKEN_MODULO: emitByte(OP_MODULO); break; case TOKEN_IN: emitByte(OP_EQUAL); break; @@ -459,8 +461,7 @@ static void binary(int canAssign) { } static int matchAssignment(void) { - return match(TOKEN_EQUAL) || match(TOKEN_PLUS_EQUAL) || match(TOKEN_MINUS_EQUAL) || - match(TOKEN_PLUS_PLUS) || match(TOKEN_MINUS_MINUS); + return (parser.current.type >= TOKEN_EQUAL && parser.current.type <= TOKEN_MODULO_EQUAL) ? (advance(), 1) : 0; } static int matchEndOfDel(void) { @@ -468,23 +469,29 @@ static int matchEndOfDel(void) { } static void assignmentValue(void) { - switch (parser.previous.type) { - case TOKEN_PLUS_EQUAL: - expression(); - emitByte(OP_ADD); - break; - case TOKEN_MINUS_EQUAL: - expression(); - emitByte(OP_SUBTRACT); - break; - case TOKEN_PLUS_PLUS: - emitConstant(INTEGER_VAL(1)); - emitByte(OP_ADD); - break; - case TOKEN_MINUS_MINUS: - emitConstant(INTEGER_VAL(1)); - emitByte(OP_SUBTRACT); - break; + KrkTokenType type = parser.previous.type; + if (type == TOKEN_PLUS_PLUS || type == TOKEN_MINUS_MINUS) { + emitConstant(INTEGER_VAL(1)); + } else { + expression(); + } + + switch (type) { + case TOKEN_PIPE_EQUAL: emitByte(OP_BITOR); break; + case TOKEN_CARET_EQUAL: emitByte(OP_BITXOR); break; + case TOKEN_AMP_EQUAL: emitByte(OP_BITAND); break; + case TOKEN_LSHIFT_EQUAL: emitByte(OP_SHIFTLEFT); break; + case TOKEN_RSHIFT_EQUAL: emitByte(OP_SHIFTRIGHT); break; + + case TOKEN_PLUS_EQUAL: emitByte(OP_ADD); break; + case TOKEN_PLUS_PLUS: emitByte(OP_ADD); break; + case TOKEN_MINUS_EQUAL: emitByte(OP_SUBTRACT); break; + case TOKEN_MINUS_MINUS: emitByte(OP_SUBTRACT); break; + case TOKEN_ASTERISK_EQUAL: emitByte(OP_MULTIPLY); break; + case TOKEN_POW_EQUAL: emitByte(OP_POW); break; + case TOKEN_SOLIDUS_EQUAL: emitByte(OP_DIVIDE); break; + case TOKEN_MODULO_EQUAL: emitByte(OP_MODULO); break; + default: error("Unexpected operand in assignment"); break; @@ -881,8 +888,8 @@ static void function(FunctionType type, size_t blockWidth) { } continue; } - if (match(TOKEN_ASTERISK)) { - if (match(TOKEN_ASTERISK)) { + if (match(TOKEN_ASTERISK) || check(TOKEN_POW)) { + if (match(TOKEN_POW)) { if (hasCollectors == 2) { error("Duplicate ** in parameter list."); return; @@ -2106,11 +2113,12 @@ ParseRule krk_parseRules[] = { RULE(TOKEN_SEMICOLON, NULL, NULL, PREC_NONE), RULE(TOKEN_SOLIDUS, NULL, binary, PREC_FACTOR), RULE(TOKEN_ASTERISK, NULL, binary, PREC_FACTOR), + RULE(TOKEN_POW, NULL, binary, PREC_EXPONENT), RULE(TOKEN_MODULO, NULL, binary, PREC_FACTOR), RULE(TOKEN_BANG, unary, NULL, PREC_NONE), - RULE(TOKEN_BANG_EQUAL, NULL, binary, PREC_EQUALITY), + RULE(TOKEN_BANG_EQUAL, NULL, binary, PREC_COMPARISON), RULE(TOKEN_EQUAL, NULL, NULL, PREC_NONE), - RULE(TOKEN_EQUAL_EQUAL, NULL, binary, PREC_EQUALITY), + 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), @@ -2141,6 +2149,8 @@ ParseRule krk_parseRules[] = { RULE(TOKEN_WHILE, NULL, NULL, PREC_NONE), RULE(TOKEN_BREAK, NULL, NULL, PREC_NONE), RULE(TOKEN_CONTINUE, NULL, NULL, PREC_NONE), + RULE(TOKEN_IMPORT, NULL, NULL, PREC_NONE), + RULE(TOKEN_RAISE, NULL, NULL, PREC_NONE), RULE(TOKEN_AT, NULL, NULL, PREC_NONE), @@ -2155,6 +2165,14 @@ ParseRule krk_parseRules[] = { RULE(TOKEN_MINUS_EQUAL, NULL, NULL, PREC_NONE), RULE(TOKEN_PLUS_PLUS, NULL, NULL, PREC_NONE), RULE(TOKEN_MINUS_MINUS, NULL, NULL, PREC_NONE), + RULE(TOKEN_CARET_EQUAL, NULL, NULL, PREC_NONE), + RULE(TOKEN_PIPE_EQUAL, NULL, NULL, PREC_NONE), + RULE(TOKEN_LSHIFT_EQUAL, NULL, NULL, PREC_NONE), + RULE(TOKEN_RSHIFT_EQUAL, NULL, NULL, PREC_NONE), + RULE(TOKEN_AMP_EQUAL, NULL, NULL, PREC_NONE), + RULE(TOKEN_SOLIDUS_EQUAL, NULL, NULL, PREC_NONE), + RULE(TOKEN_ASTERISK_EQUAL,NULL, NULL, PREC_NONE), + RULE(TOKEN_MODULO_EQUAL, NULL, NULL, PREC_NONE), RULE(TOKEN_LAMBDA, lambda, NULL, PREC_NONE), @@ -2163,6 +2181,7 @@ ParseRule krk_parseRules[] = { RULE(TOKEN_ERROR, NULL, NULL, PREC_NONE), RULE(TOKEN_EOL, NULL, NULL, PREC_NONE), RULE(TOKEN_EOF, NULL, NULL, PREC_NONE), + RULE(TOKEN_RETRY, NULL, NULL, PREC_NONE), }; static void actualTernary(size_t count, KrkScanner oldScanner, Parser oldParser) { @@ -2300,9 +2319,9 @@ static void call(int canAssign) { size_t argCount = 0, specialArgs = 0, keywordArgs = 0, seenKeywordUnpacking = 0; if (!check(TOKEN_RIGHT_PAREN)) { do { - if (match(TOKEN_ASTERISK)) { + if (match(TOKEN_ASTERISK) || check(TOKEN_POW)) { specialArgs++; - if (match(TOKEN_ASTERISK)) { + if (match(TOKEN_POW)) { seenKeywordUnpacking = 1; emitBytes(OP_EXPAND_ARGS, 2); /* Outputs something special */ expression(); /* Expect dict */ diff --git a/debug.c b/debug.c index e77f919..483ad30 100644 --- a/debug.c +++ b/debug.c @@ -121,6 +121,7 @@ size_t krk_disassembleInstruction(FILE * f, KrkFunction * func, size_t offset) { SIMPLE(OP_SWAP) SIMPLE(OP_FINALIZE) SIMPLE(OP_IS) + SIMPLE(OP_POW) OPERANDB(OP_DUP,(void)0) OPERANDB(OP_EXPAND_ARGS,EXPAND_ARGS_MORE) CONSTANT(OP_DEFINE_GLOBAL,(void)0) diff --git a/scanner.c b/scanner.c index 959d420..dfcadb3 100644 --- a/scanner.c +++ b/scanner.c @@ -337,21 +337,21 @@ KrkToken krk_scanToken() { case ',': return makeToken(TOKEN_COMMA); case '.': return makeToken(TOKEN_DOT); case ';': return makeToken(TOKEN_SEMICOLON); - case '/': return makeToken(TOKEN_SOLIDUS); - case '*': return makeToken(TOKEN_ASTERISK); - case '%': return makeToken(TOKEN_MODULO); case '@': return makeToken(TOKEN_AT); case '~': return makeToken(TOKEN_TILDE); - case '^': return makeToken(TOKEN_CARET); - case '|': return makeToken(TOKEN_PIPE); - case '&': return makeToken(TOKEN_AMPERSAND); - case '!': return makeToken(match('=') ? TOKEN_BANG_EQUAL : TOKEN_BANG); - case '=': return makeToken(match('=') ? TOKEN_EQUAL_EQUAL : TOKEN_EQUAL); - case '<': return makeToken(match('=') ? TOKEN_LESS_EQUAL : (match('<') ? TOKEN_LEFT_SHIFT : TOKEN_LESS)); - case '>': return makeToken(match('=') ? TOKEN_GREATER_EQUAL : (match('>') ? TOKEN_RIGHT_SHIFT : TOKEN_GREATER)); - case '-': return makeToken(match('=') ? TOKEN_MINUS_EQUAL : (match('-') ? TOKEN_MINUS_MINUS : TOKEN_MINUS)); - case '+': return makeToken(match('=') ? TOKEN_PLUS_EQUAL : (match('+') ? TOKEN_PLUS_PLUS : TOKEN_PLUS)); + case '!': return makeToken(match('=') ? TOKEN_BANG_EQUAL : TOKEN_BANG); + case '=': return makeToken(match('=') ? TOKEN_EQUAL_EQUAL : TOKEN_EQUAL); + case '<': return makeToken(match('=') ? TOKEN_LESS_EQUAL : (match('<') ? (match('=') ? TOKEN_LSHIFT_EQUAL : TOKEN_LEFT_SHIFT) : TOKEN_LESS)); + case '>': return makeToken(match('=') ? TOKEN_GREATER_EQUAL : (match('>') ? (match('=') ? TOKEN_RSHIFT_EQUAL : TOKEN_RIGHT_SHIFT) : TOKEN_GREATER)); + case '-': return makeToken(match('=') ? TOKEN_MINUS_EQUAL : (match('-') ? TOKEN_MINUS_MINUS : TOKEN_MINUS)); + case '+': return makeToken(match('=') ? TOKEN_PLUS_EQUAL : (match('+') ? TOKEN_PLUS_PLUS : TOKEN_PLUS)); + case '^': return makeToken(match('=') ? TOKEN_CARET_EQUAL : TOKEN_CARET); + case '|': return makeToken(match('=') ? TOKEN_PIPE_EQUAL : TOKEN_PIPE); + case '&': return makeToken(match('=') ? TOKEN_AMP_EQUAL : TOKEN_AMPERSAND); + case '/': return makeToken(match('=') ? TOKEN_SOLIDUS_EQUAL : TOKEN_SOLIDUS); + case '*': return makeToken(match('=') ? TOKEN_ASTERISK_EQUAL: (match('*') ? (match('=') ? TOKEN_POW_EQUAL : TOKEN_POW) : TOKEN_ASTERISK)); + case '%': return makeToken(match('=') ? TOKEN_MODULO_EQUAL : TOKEN_MODULO); case '"': return string('"'); case '\'': return string('\''); diff --git a/scanner.h b/scanner.h index 22bcf72..75b0152 100644 --- a/scanner.h +++ b/scanner.h @@ -12,6 +12,7 @@ typedef enum { TOKEN_SEMICOLON, TOKEN_SOLIDUS, TOKEN_ASTERISK, + TOKEN_POW, TOKEN_MODULO, TOKEN_AT, TOKEN_CARET, /* ^ (xor) */ @@ -20,15 +21,31 @@ typedef enum { TOKEN_TILDE, /* ~ (negate) */ TOKEN_LEFT_SHIFT, /* << */ TOKEN_RIGHT_SHIFT,/* >> */ - TOKEN_PLUS_EQUAL, /* += */ - TOKEN_MINUS_EQUAL,/* -= */ - TOKEN_PLUS_PLUS, /* ++ */ - TOKEN_MINUS_MINUS,/* -- */ + TOKEN_BANG, + TOKEN_GREATER, + TOKEN_LESS, - TOKEN_BANG, TOKEN_BANG_EQUAL, - TOKEN_EQUAL, TOKEN_EQUAL_EQUAL, - TOKEN_GREATER, TOKEN_GREATER_EQUAL, - TOKEN_LESS, TOKEN_LESS_EQUAL, + /* Comparisons */ + TOKEN_GREATER_EQUAL, + TOKEN_LESS_EQUAL, + TOKEN_BANG_EQUAL, + TOKEN_EQUAL_EQUAL, + + /* Assignments */ + TOKEN_EQUAL, + TOKEN_LSHIFT_EQUAL, /* <<= */ + TOKEN_RSHIFT_EQUAL, /* >>= */ + TOKEN_PLUS_EQUAL, /* += */ + TOKEN_MINUS_EQUAL, /* -= */ + TOKEN_PLUS_PLUS, /* ++ */ + TOKEN_MINUS_MINUS, /* -- */ + TOKEN_CARET_EQUAL, + TOKEN_PIPE_EQUAL, + TOKEN_AMP_EQUAL, + TOKEN_SOLIDUS_EQUAL, + TOKEN_ASTERISK_EQUAL, + TOKEN_POW_EQUAL, + TOKEN_MODULO_EQUAL, TOKEN_STRING, TOKEN_BIG_STRING, diff --git a/src/math.c b/src/math.c index f5dc7e0..c0a8da6 100644 --- a/src/math.c +++ b/src/math.c @@ -205,6 +205,13 @@ KrkValue krk_module_onload_math(void) { bind(isnan); #endif + /** + * Maybe the math library should be a core one, but I'm not sure if I want + * to have to depend on -lm in the main interpreter, so instead if we have + * imported math, we'll just quietly give floats a __pow__ method... + */ + krk_defineNative(&vm.baseClasses.floatClass->methods, "__pow__", _math_pow); + krk_attachNamedValue(&module->fields, "pi", FLOATING_VAL(M_PI)); #ifndef __toaru__ /* TODO: Add these to toaru... */ diff --git a/test/testOperators.krk b/test/testOperators.krk new file mode 100644 index 0000000..ae8861a --- /dev/null +++ b/test/testOperators.krk @@ -0,0 +1,31 @@ +import math +# Import is needed or we don't have pow. Dunno if I care to fix that. + +let x = 1.27 +print(x ** 2.7) +x **= 2.7 +print(x) + +let x = 123 +print(x << 1) +x <<= 1 +print(x) + +x >>= 1 +print(x) + +x |= 321 +print(x) +x ^= 321 +print(x) +x ^= 321 +print(x) + +x++ +print(x) +x+=2 +print(x) +x-- +print(x) + + diff --git a/test/testOperators.krk.expect b/test/testOperators.krk.expect new file mode 100644 index 0000000..0e7240f --- /dev/null +++ b/test/testOperators.krk.expect @@ -0,0 +1,11 @@ +1.90665 +1.90665 +246 +246 +123 +379 +58 +379 +380 +382 +381 diff --git a/vm.c b/vm.c index 004a554..29db740 100644 --- a/vm.c +++ b/vm.c @@ -3536,6 +3536,13 @@ MAKE_BIN_OP(sub,-) MAKE_BIN_OP(mul,*) MAKE_BIN_OP(div,/) +#define MAKE_UNOPTIMIZED_BIN_OP(name,operator) \ + static KrkValue operator_ ## name (KrkValue a, KrkValue b) { \ + return tryBind("__" #name "__", a, b, "unsupported operand types for " #operator ": '%s' and '%s'"); \ + } + +MAKE_UNOPTIMIZED_BIN_OP(pow,**) + /* Bit ops are invalid on doubles in C, so we can't use the same set of macros for them; * they should be invalid in Kuroko as well. */ #define MAKE_BIT_OP(name,operator) \ @@ -4091,6 +4098,7 @@ static KrkValue run() { case OP_BITAND: BINARY_OP(and) case OP_SHIFTLEFT: BINARY_OP(lshift) case OP_SHIFTRIGHT: BINARY_OP(rshift) + case OP_POW: BINARY_OP(pow) case OP_BITNEGATE: { KrkValue value = krk_pop(); if (IS_INTEGER(value)) krk_push(INTEGER_VAL(~AS_INTEGER(value)));