From 930b599e8264b9211a6b98c22c037e8d72412396 Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Fri, 5 Feb 2021 19:05:08 +0900 Subject: [PATCH] Expose basic operators and add more builtins, list functions --- src/builtins.c | 4 -- src/obj_dict.c | 2 + src/obj_list.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ src/obj_str.c | 2 + src/obj_tuple.c | 2 + src/rline.c | 1 + src/vm.c | 12 ++--- src/vm.h | 3 ++ 8 files changed, 132 insertions(+), 10 deletions(-) diff --git a/src/builtins.c b/src/builtins.c index 3335faf..e7d849b 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -426,10 +426,6 @@ void _createAndBind_builtins(void) { krk_finalizeClass(LicenseReader); krk_attachNamedObject(&vm.builtins->fields, "license", (KrkObj*)krk_newInstance(LicenseReader)); - BUILTIN_FUNCTION("listOf", krk_list_of, "Convert argument sequence to list object."); - BUILTIN_FUNCTION("dictOf", krk_dict_of, "Convert argument sequence to dict object."); - BUILTIN_FUNCTION("tupleOf",krk_tuple_of,"Convert argument sequence to tuple object."); - BUILTIN_FUNCTION("isinstance", _isinstance, "Determine if an object is an instance of the given class or one if its subclasses."); BUILTIN_FUNCTION("globals", _globals, "Return a mapping of names in the current global namespace."); BUILTIN_FUNCTION("dir", _dir, "Return a list of known property names for a given object."); diff --git a/src/obj_dict.c b/src/obj_dict.c index 1119dd5..d2471c2 100644 --- a/src/obj_dict.c +++ b/src/obj_dict.c @@ -335,6 +335,8 @@ void _createAndBind_dictClass(void) { krk_finalizeClass(dict); dict->docstring = S("Mapping of arbitrary keys to values."); + BUILTIN_FUNCTION("dictOf", krk_dict_of, "Convert argument sequence to dict object."); + KrkClass * dictitems = ADD_BASE_CLASS(vm.baseClasses.dictitemsClass, "dictitems", vm.objectClass); dictitems->allocSize = sizeof(struct DictItems); dictitems->_ongcscan = _dictitems_gcscan; diff --git a/src/obj_list.c b/src/obj_list.c index 183ed08..32554f4 100644 --- a/src/obj_list.c +++ b/src/obj_list.c @@ -8,6 +8,11 @@ if (index < 0) index += self->values.count; \ if (index < 0 || index >= (krk_integer_type)self->values.count) return krk_runtimeError(vm.exceptions.indexError, "list index out of range: " PRIkrk_int, index) +#define LIST_WRAP_SOFT(val) \ + if (val < 0) val += self->values.count; \ + if (val < 0) val = 0; \ + if (val > (krk_integer_type)self->values.count) val = self->values.count + static void _list_gcscan(KrkInstance * self) { for (size_t i = 0; i < ((KrkList*)self)->values.count; ++i) { krk_markValue(((KrkList*)self)->values.values[i]); @@ -227,6 +232,95 @@ KRK_METHOD(list,pop,{ } }) +KRK_METHOD(list,remove,{ + METHOD_TAKES_EXACTLY(1); + for (size_t i = 0; i < self->values.count; ++i) { + if (krk_valuesEqual(self->values.values[i], argv[1])) { + return FUNC_NAME(list,pop)(2,(KrkValue[]){argv[0], INTEGER_VAL(i)},0); + } + } + return krk_runtimeError(vm.exceptions.valueError, "not found"); +}) + +KRK_METHOD(list,clear,{ + METHOD_TAKES_NONE(); + krk_freeValueArray(&self->values); +}) + +KRK_METHOD(list,index,{ + METHOD_TAKES_AT_LEAST(1); + METHOD_TAKES_AT_MOST(3); + + krk_integer_type min = 0; + krk_integer_type max = self->values.count; + + if (argc > 2) { + if (IS_INTEGER(argv[2])) + min = AS_INTEGER(argv[2]); + else + return krk_runtimeError(vm.exceptions.typeError, "min must be int, not '%s'", krk_typeName(argv[2])); + } + + if (argc > 3) { + if (IS_INTEGER(argv[3])) + max = AS_INTEGER(argv[3]); + else + return krk_runtimeError(vm.exceptions.typeError, "max must be int, not '%s'", krk_typeName(argv[3])); + } + + LIST_WRAP_SOFT(min); + LIST_WRAP_SOFT(max); + + for (krk_integer_type i = min; i < max; ++i) { + if (krk_valuesEqual(self->values.values[i], argv[1])) return INTEGER_VAL(i); + } + + return krk_runtimeError(vm.exceptions.valueError, "not found"); +}) + +KRK_METHOD(list,count,{ + METHOD_TAKES_EXACTLY(1); + krk_integer_type count = 0; + + for (size_t i = 0; i < self->values.count; ++i) { + if (krk_valuesEqual(self->values.values[i], argv[1])) count++; + } + + return INTEGER_VAL(count); +}) + +KRK_METHOD(list,copy,{ + METHOD_TAKES_NONE(); + return krk_list_of(self->values.count, self->values.values); +}) + +KRK_METHOD(list,reverse,{ + METHOD_TAKES_NONE(); + for (size_t i = 0; i < (self->values.count) / 2; i++) { + KrkValue tmp = self->values.values[i]; + self->values.values[i] = self->values.values[self->values.count-i-1]; + self->values.values[self->values.count-i-1] = tmp; + } + return NONE_VAL(); +}) + +static int _list_sorter(const void * _a, const void * _b) { + KrkValue a = *(KrkValue*)_a; + KrkValue b = *(KrkValue*)_b; + + KrkValue ltComp = krk_operator_lt(a,b); + if (IS_NONE(ltComp) || (IS_BOOLEAN(ltComp) && AS_BOOLEAN(ltComp))) return -1; + KrkValue gtComp = krk_operator_gt(a,b); + if (IS_NONE(gtComp) || (IS_BOOLEAN(gtComp) && AS_BOOLEAN(gtComp))) return 1; + return 0; +} + +KRK_METHOD(list,sort,{ + METHOD_TAKES_NONE(); + + qsort(self->values.values, self->values.count, sizeof(KrkValue), _list_sorter); +}) + FUNC_SIG(listiterator,__init__); KRK_METHOD(list,__iter__,{ @@ -280,6 +374,18 @@ _corrupt: return krk_runtimeError(vm.exceptions.typeError, "Corrupt list iterator: %s", errorStr); }) + +static KrkValue _sorted(int argc, KrkValue argv[], int hasKw) { + if (argc != 1) return krk_runtimeError(vm.exceptions.argumentError,"%s() takes %s %d argument%s (%d given)","sorted","exactly",1,argc); + KrkValue listOut = krk_list_of(0,NULL); + krk_push(listOut); + FUNC_NAME(list,extend)(2,(KrkValue[]){listOut,argv[0]},0); + if (!IS_NONE(vm.currentException)) return NONE_VAL(); + FUNC_NAME(list,sort)(1,&listOut,0); + if (!IS_NONE(vm.currentException)) return NONE_VAL(); + return krk_pop(); +} + _noexport void _createAndBind_listClass(void) { KrkClass * list = ADD_BASE_CLASS(vm.baseClasses.listClass, "list", vm.objectClass); @@ -299,11 +405,21 @@ void _createAndBind_listClass(void) { BIND_METHOD(list,extend); BIND_METHOD(list,pop); BIND_METHOD(list,insert); + BIND_METHOD(list,clear); + BIND_METHOD(list,index); + BIND_METHOD(list,count); + BIND_METHOD(list,copy); + BIND_METHOD(list,remove); + BIND_METHOD(list,reverse); + BIND_METHOD(list,sort); krk_defineNative(&list->methods, ".__delitem__", FUNC_NAME(list,pop)); krk_defineNative(&list->methods, ".__str__", FUNC_NAME(list,__repr__)); krk_finalizeClass(list); list->docstring = S("Mutable sequence of arbitrary values."); + BUILTIN_FUNCTION("listOf", krk_list_of, "Convert argument sequence to list object."); + BUILTIN_FUNCTION("sorted", _sorted, "Return a sorted representation of an iterable."); + KrkClass * listiterator = ADD_BASE_CLASS(vm.baseClasses.listiteratorClass, "listiterator", vm.objectClass); BIND_METHOD(listiterator,__init__); BIND_METHOD(listiterator,__call__); diff --git a/src/obj_str.c b/src/obj_str.c index 8e75cb8..94acef6 100644 --- a/src/obj_str.c +++ b/src/obj_str.c @@ -446,6 +446,7 @@ KRK_METHOD(str,__lt__,{ if (!IS_STRING(argv[1])) { return KWARGS_VAL(0); /* represents 'not implemented' */ } + if (AS_STRING(argv[0]) == AS_STRING(argv[1])) return BOOLEAN_VAL(0); size_t aLen = AS_STRING(argv[0])->length; size_t bLen = AS_STRING(argv[1])->length; @@ -465,6 +466,7 @@ KRK_METHOD(str,__gt__,{ if (!IS_STRING(argv[1])) { return KWARGS_VAL(0); /* represents 'not implemented' */ } + if (AS_STRING(argv[0]) == AS_STRING(argv[1])) return BOOLEAN_VAL(0); size_t aLen = AS_STRING(argv[0])->length; size_t bLen = AS_STRING(argv[1])->length; diff --git a/src/obj_tuple.c b/src/obj_tuple.c index def2f88..d72fa4d 100644 --- a/src/obj_tuple.c +++ b/src/obj_tuple.c @@ -141,6 +141,8 @@ void _createAndBind_tupleClass(void) { krk_defineNative(&vm.baseClasses.tupleClass->methods, ".__eq__", _tuple_eq); krk_finalizeClass(vm.baseClasses.tupleClass); + BUILTIN_FUNCTION("tupleOf",krk_tuple_of,"Convert argument sequence to tuple object."); + ADD_BASE_CLASS(vm.baseClasses.tupleiteratorClass, "tupleiterator", vm.objectClass); vm.baseClasses.tupleiteratorClass->allocSize = sizeof(struct TupleIter); vm.baseClasses.tupleiteratorClass->_ongcscan = _tuple_iter_gcscan; diff --git a/src/rline.c b/src/rline.c index 65d18cd..e04454f 100644 --- a/src/rline.c +++ b/src/rline.c @@ -554,6 +554,7 @@ char * syn_krk_types[] = { "list","dict","range", /* builtin classes */ "object","exception","isinstance","type","tuple", "print","set","any","all","bool","ord","chr","hex", + "sorted","bytes", NULL }; diff --git a/src/vm.c b/src/vm.c index 2a7a7a6..7996477 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1199,7 +1199,7 @@ static KrkValue tryBind(const char * name, KrkValue a, KrkValue b, const char * */ #define MAKE_BIN_OP(name,operator) \ - static KrkValue operator_ ## name (KrkValue a, KrkValue b) { \ + KrkValue krk_operator_ ## name (KrkValue a, KrkValue b) { \ if (IS_INTEGER(a) && IS_INTEGER(b)) return INTEGER_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \ if (IS_FLOATING(a)) { \ if (IS_INTEGER(b)) return FLOATING_VAL(AS_FLOATING(a) operator (double)AS_INTEGER(b)); \ @@ -1216,7 +1216,7 @@ MAKE_BIN_OP(mul,*) MAKE_BIN_OP(div,/) #define MAKE_UNOPTIMIZED_BIN_OP(name,operator) \ - static KrkValue operator_ ## name (KrkValue a, KrkValue b) { \ + KrkValue krk_operator_ ## name (KrkValue a, KrkValue b) { \ return tryBind("__" #name "__", a, b, "unsupported operand types for " #operator ": '%s' and '%s'"); \ } @@ -1225,7 +1225,7 @@ 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) \ - static KrkValue operator_ ## name (KrkValue a, KrkValue b) { \ + KrkValue krk_operator_ ## name (KrkValue a, KrkValue b) { \ if (IS_INTEGER(a) && IS_INTEGER(b)) return INTEGER_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \ return tryBind("__" #name "__", a, b, "unsupported operand types for " #operator ": '%s' and '%s'"); \ } @@ -1238,7 +1238,7 @@ MAKE_BIT_OP(rshift,>>) MAKE_BIT_OP(mod,%) /* not a bit op, but doesn't work on floating point */ #define MAKE_COMPARATOR(name, operator) \ - static KrkValue operator_ ## name (KrkValue a, KrkValue b) { \ + KrkValue krk_operator_ ## name (KrkValue a, KrkValue b) { \ if (IS_INTEGER(a) && IS_INTEGER(b)) return BOOLEAN_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \ if (IS_FLOATING(a)) { \ if (IS_INTEGER(b)) return BOOLEAN_VAL(AS_FLOATING(a) operator AS_INTEGER(b)); \ @@ -1636,11 +1636,11 @@ static int valueDelProperty(KrkString * name) { } #define READ_BYTE() (*frame->ip++) -#define BINARY_OP(op) { KrkValue b = krk_pop(); KrkValue a = krk_pop(); krk_push(operator_ ## op (a,b)); break; } +#define BINARY_OP(op) { KrkValue b = krk_pop(); KrkValue a = krk_pop(); krk_push(krk_operator_ ## op (a,b)); break; } #define BINARY_OP_CHECK_ZERO(op) { KrkValue b = krk_pop(); KrkValue a = krk_pop(); \ if ((IS_INTEGER(b) && AS_INTEGER(b) == 0)) { krk_runtimeError(vm.exceptions.zeroDivisionError, "integer division or modulo by zero"); goto _finishException; } \ else if ((IS_FLOATING(b) && AS_FLOATING(b) == 0.0)) { krk_runtimeError(vm.exceptions.zeroDivisionError, "float division by zero"); goto _finishException; } \ - krk_push(operator_ ## op (a,b)); break; } + krk_push(krk_operator_ ## op (a,b)); break; } #define READ_CONSTANT(s) (frame->closure->function->chunk.constants.values[readBytes(frame,s)]) #define READ_STRING(s) AS_STRING(READ_CONSTANT(s)) diff --git a/src/vm.h b/src/vm.h index be3fd51..1822b3d 100644 --- a/src/vm.h +++ b/src/vm.h @@ -198,3 +198,6 @@ extern void _createAndBind_exceptions(void); extern void _createAndBind_gcMod(void); extern int krk_doRecursiveModuleLoad(KrkString * name); + +extern KrkValue krk_operator_lt(KrkValue,KrkValue); +extern KrkValue krk_operator_gt(KrkValue,KrkValue);