diff --git a/src/obj_bytes.c b/src/obj_bytes.c index 308eab7..fddead7 100644 --- a/src/obj_bytes.c +++ b/src/obj_bytes.c @@ -4,11 +4,18 @@ #include "memory.h" #include "util.h" -static KrkValue _bytes_init(int argc, KrkValue argv[], int hasKw) { - if (argc == 1) { +#define AS_bytes(o) AS_BYTES(o) +#define CURRENT_CTYPE KrkBytes * +#define CURRENT_NAME self + +#define IS_bytes(o) (IS_BYTES(o) || krk_isInstanceOf(o, vm.baseClasses->bytesClass)) +KRK_METHOD(bytes,__init__,{ + if (argc < 2) { return OBJECT_VAL(krk_newBytes(0,NULL)); } + METHOD_TAKES_AT_MOST(1); + /* TODO: Use generic unpacker */ if (IS_TUPLE(argv[1])) { KrkBytes * out = krk_newBytes(AS_TUPLE(argv[1])->values.count, NULL); krk_push(OBJECT_VAL(out)); @@ -20,13 +27,27 @@ static KrkValue _bytes_init(int argc, KrkValue argv[], int hasKw) { } krk_bytesUpdateHash(out); return krk_pop(); + } else if (IS_list(argv[1])) { + KrkBytes * out = krk_newBytes(AS_LIST(argv[1])->count, NULL); + krk_push(OBJECT_VAL(out)); + for (size_t i = 0; i < AS_LIST(argv[1])->count; ++i) { + if (!IS_INTEGER(AS_LIST(argv[1])->values[i])) { + return krk_runtimeError(vm.exceptions->typeError, "bytes(): expected list of ints, not of '%s'", krk_typeName(AS_LIST(argv[1])->values[i])); + } + out->bytes[i] = AS_INTEGER(AS_LIST(argv[1])->values[i]); + } + krk_bytesUpdateHash(out); + return krk_pop(); } return krk_runtimeError(vm.exceptions->typeError, "Can not convert '%s' to bytes", krk_typeName(argv[1])); -} +}) + +#undef IS_bytes +#define IS_bytes(o) IS_BYTES(o) /* bytes objects are not interned; need to do this the old-fashioned way. */ -static KrkValue _bytes_eq(int argc, KrkValue argv[], int hasKw) { +KRK_METHOD(bytes,__eq__,{ if (!IS_BYTES(argv[1])) return BOOLEAN_VAL(0); KrkBytes * self = AS_BYTES(argv[0]); KrkBytes * them = AS_BYTES(argv[1]); @@ -36,61 +57,52 @@ static KrkValue _bytes_eq(int argc, KrkValue argv[], int hasKw) { if (self->bytes[i] != them->bytes[i]) return BOOLEAN_VAL(0); } return BOOLEAN_VAL(1); -} +}) -#define PUSH_CHAR(c) do { if (stringCapacity < stringLength + 1) { \ - size_t old = stringCapacity; stringCapacity = GROW_CAPACITY(old); \ - stringBytes = GROW_ARRAY(char, stringBytes, old, stringCapacity); \ - } stringBytes[stringLength++] = c; } while (0) #define AT_END() (self->length == 0 || i == self->length - 1) -static KrkValue _bytes_repr(int argc, KrkValue argv[], int hasKw) { - size_t stringCapacity = 0; - size_t stringLength = 0; - char * stringBytes = NULL; +KRK_METHOD(bytes,__repr__,{ + struct StringBuilder sb = {0}; - PUSH_CHAR('b'); - PUSH_CHAR('\''); + pushStringBuilder(&sb, 'b'); + pushStringBuilder(&sb, '\''); for (size_t i = 0; i < AS_BYTES(argv[0])->length; ++i) { uint8_t ch = AS_BYTES(argv[0])->bytes[i]; switch (ch) { - case '\\': PUSH_CHAR('\\'); PUSH_CHAR('\\'); break; - case '\'': PUSH_CHAR('\\'); PUSH_CHAR('\''); break; - case '\a': PUSH_CHAR('\\'); PUSH_CHAR('a'); break; - case '\b': PUSH_CHAR('\\'); PUSH_CHAR('b'); break; - case '\f': PUSH_CHAR('\\'); PUSH_CHAR('f'); break; - case '\n': PUSH_CHAR('\\'); PUSH_CHAR('n'); break; - case '\r': PUSH_CHAR('\\'); PUSH_CHAR('r'); break; - case '\t': PUSH_CHAR('\\'); PUSH_CHAR('t'); break; - case '\v': PUSH_CHAR('\\'); PUSH_CHAR('v'); break; + case '\\': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, '\\'); break; + case '\'': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, '\''); break; + case '\a': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'a'); break; + case '\b': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'b'); break; + case '\f': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'f'); break; + case '\n': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'n'); break; + case '\r': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'r'); break; + case '\t': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 't'); break; + case '\v': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'v'); break; default: { if (ch < ' ' || ch >= 0x7F) { - PUSH_CHAR('\\'); - PUSH_CHAR('x'); + pushStringBuilder(&sb, '\\'); + pushStringBuilder(&sb, 'x'); char hex[3]; sprintf(hex,"%02x", ch); - PUSH_CHAR(hex[0]); - PUSH_CHAR(hex[1]); + pushStringBuilder(&sb, hex[0]); + pushStringBuilder(&sb, hex[1]); } else { - PUSH_CHAR(ch); + pushStringBuilder(&sb, ch); } break; } } } - PUSH_CHAR('\''); + pushStringBuilder(&sb, '\''); - KrkValue tmp = OBJECT_VAL(krk_copyString(stringBytes, stringLength)); - if (stringBytes) FREE_ARRAY(char,stringBytes,stringCapacity); - return tmp; -} + return finishStringBuilder(&sb); +}) -static KrkValue _bytes_get(int argc, KrkValue argv[], int hasKw) { - if (argc < 2) return krk_runtimeError(vm.exceptions->argumentError, "bytes.__get__(): expected one argument"); - KrkBytes * self = AS_BYTES(argv[0]); - long asInt = AS_INTEGER(argv[1]); +KRK_METHOD(bytes,__get__,{ + METHOD_TAKES_EXACTLY(1); + CHECK_ARG(1,int,krk_integer_type,asInt); if (asInt < 0) asInt += (long)self->length; if (asInt < 0 || asInt >= (long)self->length) { @@ -98,34 +110,77 @@ static KrkValue _bytes_get(int argc, KrkValue argv[], int hasKw) { } return INTEGER_VAL(self->bytes[asInt]); -} +}) -static KrkValue _bytes_len(int argc, KrkValue argv[], int hasKw) { +KRK_METHOD(bytes,__len__,{ return INTEGER_VAL(AS_BYTES(argv[0])->length); -} +}) -static KrkValue _bytes_contains(int argc, KrkValue argv[], int hasKw) { - if (argc < 2) krk_runtimeError(vm.exceptions->argumentError, "bytes.__contains__(): expected one argument"); +KRK_METHOD(bytes,__contains__,{ + METHOD_TAKES_EXACTLY(1); return krk_runtimeError(vm.exceptions->notImplementedError, "not implemented"); -} +}) -static KrkValue _bytes_decode(int argc, KrkValue argv[], int hasKw) { - /* TODO: Actually bother checking if this explodes, or support other encodings... */ +KRK_METHOD(bytes,decode,{ + METHOD_TAKES_NONE(); return OBJECT_VAL(krk_copyString((char*)AS_BYTES(argv[0])->bytes, AS_BYTES(argv[0])->length)); -} +}) -#undef PUSH_CHAR +KRK_METHOD(bytes,join,{ + METHOD_TAKES_EXACTLY(1); + CHECK_ARG(1,list,KrkList*,iterable); + + const char * errorStr = NULL; + struct StringBuilder sb = {0}; + + for (size_t i = 0; i < iterable->values.count; ++i) { + KrkValue value = iterable->values.values[i]; + if (!IS_BYTES(iterable->values.values[i])) { + errorStr = krk_typeName(value); + goto _expectedBytes; + } + krk_push(value); + if (i > 0) { + for (size_t j = 0; j < self->length; ++j) { + pushStringBuilder(&sb, self->bytes[j]); + } + } + for (size_t j = 0; j < AS_STRING(value)->length; ++j) { + pushStringBuilder(&sb, AS_BYTES(value)->bytes[j]); + } + krk_pop(); + } + + return finishStringBuilderBytes(&sb); + +_expectedBytes: + krk_runtimeError(vm.exceptions->typeError, "Expected bytes, got %s.", errorStr); + discardStringBuilder(&sb); +}) + +KRK_METHOD(bytes,__add__,{ + METHOD_TAKES_EXACTLY(1); + CHECK_ARG(1,bytes,KrkBytes*,them); + + struct StringBuilder sb = {0}; + pushStringBuilderStr(&sb, (char*)self->bytes, self->length); + pushStringBuilderStr(&sb, (char*)them->bytes, them->length); + + return finishStringBuilderBytes(&sb); +}) _noexport void _createAndBind_bytesClass(void) { - ADD_BASE_CLASS(vm.baseClasses->bytesClass, "bytes", vm.baseClasses->objectClass); - krk_defineNative(&vm.baseClasses->bytesClass->methods, ".__init__", _bytes_init); - krk_defineNative(&vm.baseClasses->bytesClass->methods, ".__str__", _bytes_repr); - krk_defineNative(&vm.baseClasses->bytesClass->methods, ".__repr__", _bytes_repr); - krk_defineNative(&vm.baseClasses->bytesClass->methods, ".decode", _bytes_decode); - krk_defineNative(&vm.baseClasses->bytesClass->methods, ".__len__", _bytes_len); - krk_defineNative(&vm.baseClasses->bytesClass->methods, ".__contains__", _bytes_contains); - krk_defineNative(&vm.baseClasses->bytesClass->methods, ".__get__", _bytes_get); - krk_defineNative(&vm.baseClasses->bytesClass->methods, ".__eq__", _bytes_eq); - krk_finalizeClass(vm.baseClasses->bytesClass); + KrkClass * bytes = ADD_BASE_CLASS(vm.baseClasses->bytesClass, "bytes", vm.baseClasses->objectClass); + BIND_METHOD(bytes,__init__); + BIND_METHOD(bytes,__repr__); + BIND_METHOD(bytes,__len__); + BIND_METHOD(bytes,__contains__); + BIND_METHOD(bytes,__get__); + BIND_METHOD(bytes,__eq__); + BIND_METHOD(bytes,__add__); + BIND_METHOD(bytes,decode); + BIND_METHOD(bytes,join); + krk_defineNative(&bytes->methods,".__str__",FUNC_NAME(bytes,__repr__)); /* alias */ + krk_finalizeClass(bytes); } diff --git a/src/util.h b/src/util.h index 646e184..9992319 100644 --- a/src/util.h +++ b/src/util.h @@ -127,6 +127,12 @@ static inline KrkValue finishStringBuilder(struct StringBuilder * sb) { return out; } +static inline KrkValue finishStringBuilderBytes(struct StringBuilder * sb) { + KrkValue out = OBJECT_VAL(krk_newBytes(sb->length, (uint8_t*)sb->bytes)); + FREE_ARRAY(char,sb->bytes, sb->capacity); + return out; +} + static inline KrkValue discardStringBuilder(struct StringBuilder * sb) { FREE_ARRAY(char,sb->bytes, sb->capacity); return NONE_VAL();