long type

This commit is contained in:
K. Lange 2022-06-27 20:40:24 +09:00
parent 635dd442ba
commit f61922d155
15 changed files with 2963 additions and 93 deletions

View File

@ -705,27 +705,7 @@ static void defineVariable(size_t global) {
static void number(int exprType) {
const char * start = parser.previous.start;
invalidTarget(exprType, "literal");
int base = 10;
/*
* Handle base prefixes:
* 0x Hexadecimal
* 0b Binary
* 0o Octal
*/
if (start[0] == '0' && (start[1] == 'x' || start[1] == 'X')) {
base = 16;
start += 2;
} else if (start[0] == '0' && (start[1] == 'b' || start[1] == 'B')) {
base = 2;
start += 2;
} else if (start[0] == '0' && (start[1] == 'o' || start[1] == 'O')) {
base = 8;
start += 2;
}
/* If it wasn't a special base, it may be a floating point value. */
if (base == 10) {
for (size_t j = 0; j < parser.previous.length; ++j) {
if (parser.previous.start[j] == '.') {
double value = strtod(start, NULL);
@ -733,11 +713,14 @@ static void number(int exprType) {
return;
}
}
}
/* If we got here, it's an integer of some sort. */
krk_integer_type value = parseStrInt(start, NULL, base);
emitConstant(INTEGER_VAL(value));
KrkValue result = krk_parse_int(start, parser.previous.literalWidth, 0);
if (IS_NONE(result)) {
error("invalid numeric literal");
return;
}
emitConstant(result);
}
static int emitJump(uint8_t opcode) {
@ -2566,7 +2549,7 @@ static void string(int exprType) {
} \
tmpbuf[i] = c[i+2]; \
} \
unsigned long value = parseStrInt(tmpbuf, NULL, 16); \
unsigned long value = strtoul(tmpbuf, NULL, 16); \
if (value >= 0x110000) { \
error("invalid codepoint in \\%c escape", type); \
} \

View File

@ -11,9 +11,6 @@
typedef int64_t krk_integer_type;
#define ENABLE_THREADING
#define PRIkrk_int "%" PRId64
#define PRIkrk_hex "%" PRIx64
#define parseStrInt strtoll
#if defined(KRK_DISABLE_THREADS) || defined(__EMSCRIPTEN__) || defined(EFI_PLATFORM)
# undef ENABLE_THREADING

View File

@ -233,6 +233,7 @@ typedef struct KrkClass {
KrkObj * _floordiv, * _rfloordiv, * _ifloordiv;
KrkObj * _lt, * _gt, * _le, * _ge;
KrkObj * _invert, * _negate;
} KrkClass;
/**

View File

@ -180,6 +180,8 @@ extern int krk_valuesEqual(KrkValue a, KrkValue b);
*/
extern int krk_valuesSame(KrkValue a, KrkValue b);
extern KrkValue krk_parse_int(const char * start, size_t width, unsigned int base);
typedef union {
KrkValue val;
double dbl;
@ -233,3 +235,7 @@ typedef union {
#define KWARGS_DICT (INT32_MAX-2)
#define KWARGS_NIL (INT32_MAX-3)
#define KWARGS_UNSET (0)
#define PRIkrk_int "%" PRId64
#define PRIkrk_hex "%" PRIx64

View File

@ -122,6 +122,7 @@ struct BaseClasses {
KrkClass * bytearrayClass; /**< Mutable array of bytes */
KrkClass * dictvaluesClass; /**< Iterator over values of a dict */
KrkClass * sliceClass; /**< Slice object */
KrkClass * longClass; /**< Arbitrary precision integer */
};
/**

View File

@ -40,6 +40,8 @@ CACHED_METHOD(LT, "__lt__", _lt)
CACHED_METHOD(GT, "__gt__", _gt)
CACHED_METHOD(LE, "__le__", _le)
CACHED_METHOD(GE, "__ge__", _ge)
CACHED_METHOD(INVERT, "__invert__", _invert)
CACHED_METHOD(NEGATE, "__neg__", _negate)
/* These are not methods */
SPECIAL_ATTRS(CLASS, "__class__")

1358
src/obj_long.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,7 @@
FUNC_SIG(int,__init__) {
static __attribute__ ((unused)) const char* _method_name = "__init__";
METHOD_TAKES_AT_MOST(1);
METHOD_TAKES_AT_MOST(2);
if (argc < 2) return INTEGER_VAL(0);
if (IS_BOOLEAN(argv[1])) return INTEGER_VAL(AS_INTEGER(argv[1]));
if (IS_INTEGER(argv[1])) return argv[1];
@ -52,14 +52,40 @@ KRK_METHOD(int,__hash__,{
return INTEGER_VAL((uint32_t)AS_INTEGER(argv[0]));
})
/**
* We _could_ use the __builtin_XXX_overflow(_p) functions gcc+clang provide,
* but let's just do this ourselves, I guess?
*
* We cheat: We only bother calculating + checking if both values would fit
* in int32_t's. This ensures multiplication works fine.
*
* For any case where an int32_t would overflow, we do the 'long' operation
* and then reduce if that still yields something that would fit in our 'int48'.
*/
#define OVERFLOW_CHECKED_INT_OPERATION(name,operator) \
extern KrkValue krk_long_coerced_ ## name (krk_integer_type a, krk_integer_type b); \
_noexport \
KrkValue krk_int_op_ ## name (krk_integer_type a, krk_integer_type b) { \
if (likely((int32_t)a == a && (int32_t)b == b)) { \
int32_t result_one = a operator b; \
int64_t result_two = a operator b; \
if (likely(result_one == result_two)) return INTEGER_VAL(result_two); \
} \
return krk_long_coerced_ ## name (a, b); \
}
OVERFLOW_CHECKED_INT_OPERATION(add,+)
OVERFLOW_CHECKED_INT_OPERATION(sub,-)
OVERFLOW_CHECKED_INT_OPERATION(mul,*)
#define BASIC_BIN_OP(name,operator) \
KRK_METHOD(int,__ ## name ## __,{ \
if (likely(IS_INTEGER(argv[1]))) return INTEGER_VAL(self operator AS_INTEGER(argv[1])); \
if (likely(IS_INTEGER(argv[1]))) return krk_int_op_ ## name(self, AS_INTEGER(argv[1])); \
else if (likely(IS_FLOATING(argv[1]))) return FLOATING_VAL((double)self operator AS_FLOATING(argv[1])); \
return NOTIMPL_VAL(); \
}) \
KRK_METHOD(int,__r ## name ## __,{ \
if (likely(IS_INTEGER(argv[1]))) return INTEGER_VAL(AS_INTEGER(argv[1]) operator self); \
if (likely(IS_INTEGER(argv[1]))) return krk_int_op_ ## name(AS_INTEGER(argv[1]), self); \
else if (likely(IS_FLOATING(argv[1]))) return FLOATING_VAL(AS_FLOATING(argv[1]) operator (double)self); \
return NOTIMPL_VAL(); \
})
@ -87,26 +113,20 @@ BASIC_BIN_OP(mul,*)
INT_ONLY_BIN_OP(or,|)
INT_ONLY_BIN_OP(xor,^)
INT_ONLY_BIN_OP(and,&)
INT_ONLY_BIN_OP(lshift,<<)
INT_ONLY_BIN_OP(rshift,>>)
KRK_METHOD(int,__mod__,{
METHOD_TAKES_EXACTLY(1);
if (likely(IS_INTEGER(argv[1]))) {
if (unlikely(AS_INTEGER(argv[1]) == 0)) return krk_runtimeError(vm.exceptions->zeroDivisionError, "integer modulo by zero");
return INTEGER_VAL(self % AS_INTEGER(argv[1]));
}
return NOTIMPL_VAL();
})
#define DEFER_TO_LONG(name) \
extern KrkValue krk_long_coerced_ ## name (krk_integer_type a, krk_integer_type b); \
KRK_METHOD(int,__ ## name ## __,{ \
if (likely(IS_INTEGER(argv[1]))) return krk_long_coerced_ ## name (self, AS_INTEGER(argv[1])); \
return NOTIMPL_VAL(); \
}) \
KRK_METHOD(int,__r ## name ## __,{ \
if (likely(IS_INTEGER(argv[1]))) return krk_long_coerced_ ## name (AS_INTEGER(argv[1]), self); \
return NOTIMPL_VAL(); \
})
KRK_METHOD(int,__rmod__,{
METHOD_TAKES_EXACTLY(1);
if (unlikely(self == 0)) return krk_runtimeError(vm.exceptions->zeroDivisionError, "integer modulo by zero");
if (likely(IS_INTEGER(argv[1]))) {
return INTEGER_VAL(AS_INTEGER(argv[1]) % self);
}
return NOTIMPL_VAL();
})
DEFER_TO_LONG(lshift)
DEFER_TO_LONG(rshift)
COMPARE_OP(lt, <)
COMPARE_OP(gt, >)
@ -144,12 +164,58 @@ KRK_METHOD(int,__rtruediv__,{
#define __builtin_floor floor
#endif
/**
* These have been corrected to match the behavior with negatives
* that Python produces, for compatibility, and also because that's
* what our 'long' type does...
*/
static KrkValue _krk_int_div(krk_integer_type a, krk_integer_type b) {
if (unlikely(b == 0)) return krk_runtimeError(vm.exceptions->zeroDivisionError, "integer division or modulo by zero");
if (a == 0) return INTEGER_VAL(0);
int64_t abs_a = a < 0 ? -a : a;
int64_t abs_b = b < 0 ? -b : b;
if ((a < 0) != (b < 0)) {
/* If signs don't match, the result is negative, and rounding down means away from 0... */
int64_t res = -1 - (abs_a - 1) / abs_b;
return INTEGER_VAL(res);
}
return INTEGER_VAL((abs_a / abs_b));
}
static KrkValue _krk_int_mod(krk_integer_type a, krk_integer_type b) {
if (unlikely(b == 0)) return krk_runtimeError(vm.exceptions->zeroDivisionError, "integer division or modulo by zero");
if (a == 0) return INTEGER_VAL(0);
int64_t abs_a = a < 0 ? -a : a;
int64_t abs_b = b < 0 ? -b : b;
int64_t res;
if ((a < 0) != (b < 0)) {
/* If quotient would be negative, then remainder is inverted against the divisor. */
res = (abs_b - 1 - (abs_a - 1) % abs_b);
} else {
res = abs_a % abs_b;
}
/* Negative divisor always yields negative remainder, except when it's 0... */
return INTEGER_VAL((b < 0) ? -res : res);
}
KRK_METHOD(int,__mod__,{
METHOD_TAKES_EXACTLY(1);
if (likely(IS_INTEGER(argv[1]))) return _krk_int_mod(self, AS_INTEGER(argv[1]));
return NOTIMPL_VAL();
})
KRK_METHOD(int,__rmod__,{
METHOD_TAKES_EXACTLY(1);
if (likely(IS_INTEGER(argv[1]))) return _krk_int_mod(AS_INTEGER(argv[1]), self);
return NOTIMPL_VAL();
})
KRK_METHOD(int,__floordiv__,{
METHOD_TAKES_EXACTLY(1);
if (likely(IS_INTEGER(argv[1]))) {
krk_integer_type b = AS_INTEGER(argv[1]);
if (unlikely(b == 0)) return krk_runtimeError(vm.exceptions->zeroDivisionError, "integer division by zero");
return INTEGER_VAL(self / b);
return _krk_int_div(self,AS_INTEGER(argv[1]));
} else if (likely(IS_FLOATING(argv[1]))) {
double b = AS_FLOATING(argv[1]);
if (unlikely(b == 0.0)) return krk_runtimeError(vm.exceptions->zeroDivisionError, "float division by zero");
@ -161,7 +227,7 @@ KRK_METHOD(int,__floordiv__,{
KRK_METHOD(int,__rfloordiv__,{
METHOD_TAKES_EXACTLY(1);
if (unlikely(self == 0)) return krk_runtimeError(vm.exceptions->zeroDivisionError, "integer division by zero");
else if (likely(IS_INTEGER(argv[1]))) return INTEGER_VAL(AS_INTEGER(argv[1]) / self);
else if (likely(IS_INTEGER(argv[1]))) return _krk_int_div(AS_INTEGER(argv[1]), self);
else if (likely(IS_FLOATING(argv[1]))) return FLOATING_VAL(__builtin_floor(AS_FLOATING(argv[1]) / (double)self));
return NOTIMPL_VAL();
})

View File

@ -99,22 +99,8 @@ KRK_METHOD(str,__setitem__,{
/* str.__int__(base=10) */
KRK_METHOD(str,__int__,{
METHOD_TAKES_AT_MOST(1);
int base = (argc < 2 || !IS_INTEGER(argv[1])) ? 10 : (int)AS_INTEGER(argv[1]);
char * start = AS_CSTRING(argv[0]);
/* These special cases for hexadecimal, binary, octal values. */
if (start[0] == '0' && (start[1] == 'x' || start[1] == 'X')) {
base = 16;
start += 2;
} else if (start[0] == '0' && (start[1] == 'b' || start[1] == 'B')) {
base = 2;
start += 2;
} else if (start[0] == '0' && (start[1] == 'o' || start[1] == 'O')) {
base = 8;
start += 2;
}
krk_integer_type value = parseStrInt(start, NULL, base);
return INTEGER_VAL(value);
int base = (argc < 2 || !IS_INTEGER(argv[1])) ? 0 : (int)AS_INTEGER(argv[1]);
return krk_parse_int(AS_CSTRING(argv[0]), AS_STRING(argv[0])->length, base);
})
/* str.__float__() */
@ -242,7 +228,7 @@ KRK_METHOD(str,format,{
} else if (counterOffset) {
goto _formatSwitchedNumbering;
} else {
positionalOffset = parseStrInt(fieldStart,NULL,10);
positionalOffset = strtoul(fieldStart,NULL,10);
}
if (positionalOffset >= argc - 1) {
erroneousIndex = positionalOffset;

View File

@ -21,6 +21,7 @@ extern void _createAndBind_sliceClass(void);
extern void _createAndBind_builtins(void);
extern void _createAndBind_type(void);
extern void _createAndBind_exceptions(void);
extern void _createAndBind_longClass(void);
extern void _createAndBind_gcMod(void);
extern void _createAndBind_timeMod(void);
extern void _createAndBind_osMod(void);

View File

@ -154,24 +154,24 @@ static KrkToken number(char c) {
/* Hexadecimal */
advance();
while (isDigit(peek()) || (peek() >= 'a' && peek() <= 'f') ||
(peek() >= 'A' && peek() <= 'F')) advance();
(peek() >= 'A' && peek() <= 'F') || (peek() == '_')) advance();
return makeToken(TOKEN_NUMBER);
} else if (peek() == 'b' || peek() == 'B') {
/* Binary */
advance();
while (peek() == '0' || peek() == '1') advance();
while (peek() == '0' || peek() == '1' || (peek() == '_')) advance();
return makeToken(TOKEN_NUMBER);
} if (peek() == 'o' || peek() == 'O') {
/* Octal - must be 0o, none of those silly 0123 things */
advance();
while (peek() >= '0' && peek() <= '7') advance();
while ((peek() >= '0' && peek() <= '7') || (peek() == '_')) advance();
return makeToken(TOKEN_NUMBER);
}
/* Otherwise, decimal and maybe 0.123 floating */
}
/* Decimal */
while (isDigit(peek())) advance();
while (isDigit(peek()) || peek() == '_') advance();
/* Floating point */
if (peek() == '.' && isDigit(peekNext(1))) {

8
src/vendor/rline.c vendored
View File

@ -654,15 +654,15 @@ void paint_krk_string(struct syntax_state * state, int type, int isFormat) {
int paint_krk_numeral(struct syntax_state * state) {
if (charat() == '0' && (nextchar() == 'x' || nextchar() == 'X')) {
paint(2, FLAG_NUMERAL);
while (isxdigit(charat())) paint(1, FLAG_NUMERAL);
while (isxdigit(charat()) || charat() == '_') paint(1, FLAG_NUMERAL);
} else if (charat() == '0' && (nextchar() == 'o' || nextchar() == 'O')) {
paint(2, FLAG_NUMERAL);
while (charat() >= '0' && charat() <= '7') paint(1, FLAG_NUMERAL);
while ((charat() >= '0' && charat() <= '7') || charat() == '_') paint(1, FLAG_NUMERAL);
} else if (charat() == '0' && (nextchar() == 'b' || nextchar() == 'B')) {
paint(2, FLAG_NUMERAL);
while (charat() == '0' || charat() == '1') paint(1, FLAG_NUMERAL);
while (charat() == '0' || charat() == '1' || charat() == '_') paint(1, FLAG_NUMERAL);
} else {
while (isdigit(charat())) paint(1, FLAG_NUMERAL);
while (isdigit(charat()) || charat() == '_') paint(1, FLAG_NUMERAL);
if (charat() == '.' && isdigit(nextchar())) {
paint(1, FLAG_NUMERAL);
while (isdigit(charat())) paint(1, FLAG_NUMERAL);

View File

@ -1341,6 +1341,7 @@ void krk_initVM(int flags) {
_createAndBind_sliceClass();
_createAndBind_exceptions();
_createAndBind_generatorClass();
_createAndBind_longClass();
_createAndBind_gcMod();
_createAndBind_timeMod();
_createAndBind_osMod();
@ -2253,9 +2254,12 @@ KrkValue krk_valueSetAttribute(KrkValue owner, char * name, KrkValue to) {
a = krk_operator_i ## op (a,b); \
krk_currentThread.stackTop[-2] = a; krk_pop(); break; }
extern KrkValue krk_int_op_add(krk_integer_type a, krk_integer_type b);
extern KrkValue krk_int_op_sub(krk_integer_type a, krk_integer_type b);
/* These operations are most likely to occur on integers, so we special case them */
#define LIKELY_INT_BINARY_OP(op,operator) { KrkValue b = krk_peek(0); KrkValue a = krk_peek(1); \
if (likely(IS_INTEGER(a) && IS_INTEGER(b))) a = INTEGER_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \
#define LIKELY_INT_BINARY_OP(op) { KrkValue b = krk_peek(0); KrkValue a = krk_peek(1); \
if (likely(IS_INTEGER(a) && IS_INTEGER(b))) a = krk_int_op_ ## op (AS_INTEGER(a), AS_INTEGER(b)); \
else a = krk_operator_ ## op (a,b); \
krk_currentThread.stackTop[-2] = a; krk_pop(); break; }
@ -2402,8 +2406,8 @@ _finishReturn: (void)0;
case OP_GREATER: LIKELY_INT_COMPARE_OP(gt,>)
case OP_LESS_EQUAL: LIKELY_INT_COMPARE_OP(le,<=)
case OP_GREATER_EQUAL: LIKELY_INT_COMPARE_OP(ge,>=)
case OP_ADD: LIKELY_INT_BINARY_OP(add,+)
case OP_SUBTRACT: LIKELY_INT_BINARY_OP(sub,-)
case OP_ADD: LIKELY_INT_BINARY_OP(add)
case OP_SUBTRACT: LIKELY_INT_BINARY_OP(sub)
case OP_MULTIPLY: BINARY_OP(mul)
case OP_DIVIDE: BINARY_OP(truediv)
case OP_FLOORDIV: BINARY_OP(floordiv)
@ -2415,16 +2419,35 @@ _finishReturn: (void)0;
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)));
else { krk_runtimeError(vm.exceptions->typeError, "Incompatible operand type for %s negation.", "bit"); goto _finishException; }
KrkValue value = krk_peek(0);
if (IS_INTEGER(value)) {
krk_currentThread.stackTop[-1] = INTEGER_VAL(~AS_INTEGER(value));
} else {
KrkClass * type = krk_getType(krk_peek(0));
if (likely(type->_invert != NULL)) {
krk_push(krk_callDirect(type->_invert, 1));
} else {
krk_runtimeError(vm.exceptions->typeError, "Incompatible operand type for %s negation.", "bit");
goto _finishException;
}
}
break;
}
case OP_NEGATE: {
KrkValue value = krk_pop();
if (IS_INTEGER(value)) krk_push(INTEGER_VAL(-AS_INTEGER(value)));
else if (IS_FLOATING(value)) krk_push(FLOATING_VAL(-AS_FLOATING(value)));
else { krk_runtimeError(vm.exceptions->typeError, "Incompatible operand type for %s negation.", "prefix"); goto _finishException; }
KrkValue value = krk_peek(0);
if (IS_INTEGER(value)) {
krk_currentThread.stackTop[-1] = INTEGER_VAL(-AS_INTEGER(value));
} else if (IS_FLOATING(value)) {
krk_currentThread.stackTop[-1] = FLOATING_VAL(-AS_FLOATING(value));
} else {
KrkClass * type = krk_getType(krk_peek(0));
if (likely(type->_negate != NULL)) {
krk_push(krk_callDirect(type->_negate, 1));
} else {
krk_runtimeError(vm.exceptions->typeError, "Incompatible operand type for %s negation.", "prefix");
goto _finishException;
}
}
break;
}
case OP_NONE: krk_push(NONE_VAL()); break;

53
test/testBigIntegers.krk Normal file
View File

@ -0,0 +1,53 @@
def test(thing, operations=None, numbers=None, printers=None, shifts=None, shiftops=None):
print('hello world')
operations = [
('+', lambda a, b: a + b),
('-', lambda a, b: a - b),
('*', lambda a, b: a * b),
('|', lambda a, b: a | b),
('^', lambda a, b: a ^ b),
('&', lambda a, b: a & b),
('//', lambda a, b: a // b),
('%', lambda a, b: a % b),
]
numbers = [
42, 7, 0, -2, -53, -25932, '-30250320993256832943892058390285932532',
'29394294398256832432748937248937198578921421', '0x29589239862',
'0b1000101010101010000101010010101010001001001000101010001010010',
'32932583298439028439285392', '-5392583232948329853251521'
]
shifts = [
0, 3, 23, 47, 68, 135, 1035, -4
]
shiftops = [
('<<', lambda a, b: a << b),
('>>', lambda a, b: a >> b),
]
printers = [
str, hex, oct, bin
]
for a in numbers:
for printer in printers:
print(printer.__name__,printer(thing(a)))
for b in numbers:
for opname, op in operations:
try:
print(a, opname, b, '=', op(thing(a), thing(b)))
except Exception as e:
print(a, opname, b, '=', str(e))
for shift in shifts:
for opname, op in shiftops:
try:
print(a, opname, shift, '=', op(thing(a), shift))
except Exception as e:
print(a, opname, shift, '=', str(e))
if __name__ == '__main__':
test(lambda a: int(a,0) if isinstance(a,str) else int(a))

File diff suppressed because it is too large Load Diff