Add more operators.

This commit is contained in:
K. Lange 2021-01-19 19:29:29 +09:00
parent bcccaa09bf
commit 851d3df8cd
9 changed files with 143 additions and 48 deletions

View File

@ -56,6 +56,7 @@ typedef enum {
OP_DUP,
OP_SWAP,
OP_KWARGS,
OP_POW,
OP_BITOR,
OP_BITXOR,

View File

@ -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 */

View File

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

View File

@ -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('\'');

View File

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

View File

@ -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... */

31
test/testOperators.krk Normal file
View File

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

View File

@ -0,0 +1,11 @@
1.90665
1.90665
246
246
123
379
58
379
380
382
381

8
vm.c
View File

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