Eliminate builtins.krk

This commit is contained in:
K. Lange 2021-02-05 17:21:57 +09:00
parent 689cee075e
commit 90b219cdce
10 changed files with 683 additions and 685 deletions

View File

@ -64,11 +64,6 @@ modules/math.so: src/module_math.c libkuroko.so
libkuroko.so: ${OBJS}
${CC} ${CFLAGS} ${LDFLAGS} -shared -o $@ ${OBJS}
src/builtins.c: src/builtins.krk
echo "const char krk_builtinsSrc[] = " > $@
cat $< | sed s'/\(.*\)/\"\0\\n\"/' >> $@
echo ";" >> $@
.PHONY: clean
clean:
@rm -f ${OBJS} ${TARGET} ${MODULES} libkuroko.so src/*.o kuroko.exe

View File

@ -40,8 +40,6 @@ The interpreter binary is a thin wrapper and lives in `kuroko.c`; `rline.c` prov
C module sources are found in `src/` and provide optional added functionality. Each module source file corresponds to a resulting shared object of the same name that will be built to the `modules/` directory, which itself also contains modules written in Kuroko.
The core builtins, `builtins.krk` are embedded in `builtins.c` so they are always available to the interpreter; `builtins.c` is provided in the repository, but can be updated by the Makefile when changes to `builtins.krk` are made.
### Building as a Single Static Binary
Configuration options are available in the Makefile to build Kuroko as a static binary.

View File

@ -21,6 +21,15 @@ def interactive():
'''Runs the detailed help tool. Currently that means just printing a longer help string.'''
print(__introText)
def simple(obj):
try:
print(obj.__doc__)
except:
try:
print(obj.__class__.__doc__)
except:
print('No docstring avaialble for', obj)
let __licenseText = '''
Copyright (c) 2020-2021 K. Lange <klange@toaruos.org>

View File

@ -1,49 +1,450 @@
const char krk_builtinsSrc[] =
"# Please avoid using double quotes or escape sequences\n"
"# in this file to allow it to be easily converted to C.\n"
"class Helper():\n"
" '''You seem to already know how to use this.'''\n"
" def __call__(self,obj=None):\n"
" if obj is not None:\n"
" try:\n"
" print(obj.__doc__)\n"
" except:\n"
" try:\n"
" print(obj.__class__.__doc__)\n"
" except:\n"
" print('No docstring avaialble for', obj)\n"
" else:\n"
" from help import interactive\n"
" interactive()\n"
" def __repr__(self):\n"
" return 'Type help() for more help, or help(obj) to describe an object.'\n"
"\n"
"let help = Helper()\n"
"\n"
"class LicenseReader():\n"
" def __call__(self):\n"
" from help import __licenseText\n"
" print(__licenseText)\n"
" def __repr__(self):\n"
" return 'Copyright 2020-2021 K. Lange <klange@toaruos.org>. Type `license()` for more information.'\n"
"\n"
"let license = LicenseReader()\n"
"\n"
"__builtins__.help = help\n"
"__builtins__.license = license\n"
"\n"
"# this works because `kuroko` is always a built-in\n"
"import kuroko\n"
"kuroko.module_paths = ['./']\n"
"if 'executable_path' in dir(kuroko):\n"
" let pathunits = kuroko.executable_path.split(kuroko.path_sep)[:-1]\n"
" let dirname = pathunits[-1]\n"
" if dirname == 'bin':\n"
" pathunits.pop(-1)\n"
" pathunits.extend(['lib','kuroko',''])\n"
" else:\n"
" pathunits.extend(['modules',''])\n"
" kuroko.module_paths.append(kuroko.path_sep.join(pathunits))\n"
"\n"
"return object()\n"
;
#include <string.h>
#include "vm.h"
#include "value.h"
#include "memory.h"
#include "util.h"
KrkClass * Helper;
KrkClass * LicenseReader;
KrkValue krk_dirObject(int argc, KrkValue argv[]) {
if (argc != 1) return krk_runtimeError(vm.exceptions.argumentError, "wrong number of arguments or bad type, got %d\n", argc);
/* Create a new list instance */
KrkValue myList = krk_list_of(0,NULL);
krk_push(myList);
if (IS_INSTANCE(argv[0])) {
/* Obtain self-reference */
KrkInstance * self = AS_INSTANCE(argv[0]);
/* First add each method of the class */
for (size_t i = 0; i < self->_class->methods.capacity; ++i) {
if (self->_class->methods.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
self->_class->methods.entries[i].key);
}
}
/* Then add each field of the instance */
for (size_t i = 0; i < self->fields.capacity; ++i) {
if (self->fields.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
self->fields.entries[i].key);
}
}
} else {
if (IS_CLASS(argv[0])) {
KrkClass * _class = AS_CLASS(argv[0]);
for (size_t i = 0; i < _class->methods.capacity; ++i) {
if (_class->methods.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
_class->methods.entries[i].key);
}
}
for (size_t i = 0; i < _class->fields.capacity; ++i) {
if (_class->fields.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
_class->fields.entries[i].key);
}
}
}
KrkClass * type = krk_getType(argv[0]);
for (size_t i = 0; i < type->methods.capacity; ++i) {
if (type->methods.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
type->methods.entries[i].key);
}
}
}
/* Prepare output value */
krk_pop();
return myList;
}
static KrkValue _len(int argc, KrkValue argv[]) {
if (argc != 1) return krk_runtimeError(vm.exceptions.argumentError, "len() takes exactly one argument");
/* Shortcuts */
if (IS_STRING(argv[0])) return INTEGER_VAL(AS_STRING(argv[0])->codesLength);
if (IS_TUPLE(argv[0])) return INTEGER_VAL(AS_TUPLE(argv[0])->values.count);
KrkClass * type = krk_getType(argv[0]);
if (!type->_len) return krk_runtimeError(vm.exceptions.typeError, "object of type '%s' has no len()", krk_typeName(argv[0]));
krk_push(argv[0]);
return krk_callSimple(OBJECT_VAL(type->_len), 1, 0);
}
static KrkValue _dir(int argc, KrkValue argv[]) {
if (argc != 1) return krk_runtimeError(vm.exceptions.argumentError, "dir() takes exactly one argument");
KrkClass * type = krk_getType(argv[0]);
if (!type->_dir) {
return krk_dirObject(argc,argv); /* Fallback */
}
krk_push(argv[0]);
return krk_callSimple(OBJECT_VAL(type->_dir), 1, 0);
}
static KrkValue _repr(int argc, KrkValue argv[]) {
if (argc != 1) return krk_runtimeError(vm.exceptions.argumentError, "repr() takes exactly one argument");
/* Everything should have a __repr__ */
KrkClass * type = krk_getType(argv[0]);
krk_push(argv[0]);
return krk_callSimple(OBJECT_VAL(type->_reprer), 1, 0);
}
static KrkValue _ord(int argc, KrkValue argv[]) {
if (argc != 1) return krk_runtimeError(vm.exceptions.argumentError, "ord() takes exactly one argument");
KrkClass * type = krk_getType(argv[0]);
KrkValue method;
if (krk_tableGet(&type->methods, vm.specialMethodNames[METHOD_ORD], &method)) {
krk_push(argv[0]);
return krk_callSimple(method, 1, 0);
}
return krk_runtimeError(vm.exceptions.argumentError, "ord() expected string of length 1, but got %s", krk_typeName(argv[0]));
}
static KrkValue _chr(int argc, KrkValue argv[]) {
if (argc != 1) return krk_runtimeError(vm.exceptions.argumentError, "chr() takes exactly one argument");
KrkClass * type = krk_getType(argv[0]);
KrkValue method;
if (krk_tableGet(&type->methods, vm.specialMethodNames[METHOD_CHR], &method)) {
krk_push(argv[0]);
return krk_callSimple(method, 1, 0);
}
return krk_runtimeError(vm.exceptions.argumentError, "chr() expected an integer, but got %s", krk_typeName(argv[0]));
}
static KrkValue _hex(int argc, KrkValue argv[]) {
if (argc != 1 || !IS_INTEGER(argv[0])) return krk_runtimeError(vm.exceptions.argumentError, "hex() expects one int argument");
char tmp[20];
krk_integer_type x = AS_INTEGER(argv[0]);
size_t len = sprintf(tmp, "%s0x" PRIkrk_hex, x < 0 ? "-" : "", x < 0 ? -x : x);
return OBJECT_VAL(krk_copyString(tmp,len));
}
static KrkValue _any(int argc, KrkValue argv[]) {
#define unpackArray(counter, indexer) do { \
for (size_t i = 0; i < counter; ++i) { \
if (!krk_isFalsey(indexer)) return BOOLEAN_VAL(1); \
} \
} while (0)
KrkValue value = argv[0];
if (IS_TUPLE(value)) {
unpackArray(AS_TUPLE(value)->values.count, AS_TUPLE(value)->values.values[i]);
} else if (IS_INSTANCE(value) && AS_INSTANCE(value)->_class == vm.baseClasses.listClass) {
unpackArray(AS_LIST(value)->count, AS_LIST(value)->values[i]);
} else if (IS_INSTANCE(value) && AS_INSTANCE(value)->_class == vm.baseClasses.dictClass) {
unpackArray(AS_DICT(value)->count, krk_dict_nth_key_fast(AS_DICT(value)->capacity, AS_DICT(value)->entries, i));
} else if (IS_STRING(value)) {
unpackArray(AS_STRING(value)->codesLength, krk_string_get(2,(KrkValue[]){value,INTEGER_VAL(i)},0));
} else {
KrkClass * type = krk_getType(argv[0]);
if (type->_iter) {
/* Create the iterator */
size_t stackOffset = vm.stackTop - vm.stack;
krk_push(argv[1]);
krk_push(krk_callSimple(OBJECT_VAL(type->_iter), 1, 0));
do {
/* Call it until it gives us itself */
krk_push(vm.stack[stackOffset]);
krk_push(krk_callSimple(krk_peek(0), 0, 1));
if (krk_valuesSame(vm.stack[stackOffset], krk_peek(0))) {
/* We're done. */
krk_pop(); /* The result of iteration */
krk_pop(); /* The iterator */
break;
}
if (!krk_isFalsey(krk_peek(0))) {
krk_pop();
krk_pop();
return BOOLEAN_VAL(1);
}
krk_pop();
} while (1);
} else {
return krk_runtimeError(vm.exceptions.typeError, "'%s' object is not iterable", krk_typeName(value));
}
}
#undef unpackArray
return BOOLEAN_VAL(0);
}
static KrkValue _all(int argc, KrkValue argv[]) {
#define unpackArray(counter, indexer) do { \
for (size_t i = 0; i < counter; ++i) { \
if (krk_isFalsey(indexer)) return BOOLEAN_VAL(0); \
} \
} while (0)
KrkValue value = argv[0];
if (IS_TUPLE(value)) {
unpackArray(AS_TUPLE(value)->values.count, AS_TUPLE(value)->values.values[i]);
} else if (IS_INSTANCE(value) && AS_INSTANCE(value)->_class == vm.baseClasses.listClass) {
unpackArray(AS_LIST(value)->count, AS_LIST(value)->values[i]);
} else if (IS_INSTANCE(value) && AS_INSTANCE(value)->_class == vm.baseClasses.dictClass) {
unpackArray(AS_DICT(value)->count, krk_dict_nth_key_fast(AS_DICT(value)->capacity, AS_DICT(value)->entries, i));
} else if (IS_STRING(value)) {
unpackArray(AS_STRING(value)->codesLength, krk_string_get(2,(KrkValue[]){value,INTEGER_VAL(i)},0));
} else {
KrkClass * type = krk_getType(argv[0]);
if (type->_iter) {
/* Create the iterator */
size_t stackOffset = vm.stackTop - vm.stack;
krk_push(argv[1]);
krk_push(krk_callSimple(OBJECT_VAL(type->_iter), 1, 0));
do {
/* Call it until it gives us itself */
krk_push(vm.stack[stackOffset]);
krk_push(krk_callSimple(krk_peek(0), 0, 1));
if (krk_valuesSame(vm.stack[stackOffset], krk_peek(0))) {
/* We're done. */
krk_pop(); /* The result of iteration */
krk_pop(); /* The iterator */
break;
}
if (krk_isFalsey(krk_peek(0))) {
krk_pop();
krk_pop();
return BOOLEAN_VAL(0);
}
krk_pop();
} while (1);
} else {
return krk_runtimeError(vm.exceptions.typeError, "'%s' object is not iterable", krk_typeName(value));
}
}
#undef unpackArray
return BOOLEAN_VAL(1);
}
static KrkValue _print(int argc, KrkValue argv[], int hasKw) {
KrkValue sepVal, endVal;
char * sep = " "; size_t sepLen = 1;
char * end = "\n"; size_t endLen = 1;
if (hasKw) {
if (krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("sep")), &sepVal)) {
if (!IS_STRING(sepVal)) return krk_runtimeError(vm.exceptions.typeError, "'sep' should be a string, not '%s'", krk_typeName(sepVal));
sep = AS_CSTRING(sepVal);
sepLen = AS_STRING(sepVal)->length;
}
if (krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("end")), &endVal)) {
if (!IS_STRING(endVal)) return krk_runtimeError(vm.exceptions.typeError, "'end' should be a string, not '%s'", krk_typeName(endVal));
end = AS_CSTRING(endVal);
endLen = AS_STRING(endVal)->length;
}
}
for (int i = 0; i < argc; ++i) {
KrkValue printable = argv[i];
if (IS_STRING(printable)) { /* krk_printValue runs repr */
/* Make sure we handle nil bits correctly. */
for (size_t j = 0; j < AS_STRING(printable)->length; ++j) {
fputc(AS_CSTRING(printable)[j], stdout);
}
} else {
krk_printValue(stdout, printable);
}
char * thingToPrint = (i == argc - 1) ? end : sep;
for (size_t j = 0; j < ((i == argc - 1) ? endLen : sepLen); ++j) {
fputc(thingToPrint[j], stdout);
}
}
return NONE_VAL();
}
/**
* globals()
*
* Returns a dict of names -> values for all the globals.
*/
static KrkValue _globals(int argc, KrkValue argv[]) {
/* Make a new empty dict */
KrkValue dict = krk_dict_of(0, NULL);
krk_push(dict);
/* Copy the globals table into it */
krk_tableAddAll(vm.frames[vm.frameCount-1].globals, AS_DICT(dict));
krk_pop();
return dict;
}
static KrkValue _isinstance(int argc, KrkValue argv[]) {
if (argc != 2) return krk_runtimeError(vm.exceptions.argumentError, "isinstance expects 2 arguments, got %d", argc);
if (!IS_CLASS(argv[1])) return krk_runtimeError(vm.exceptions.typeError, "isinstance() arg 2 must be class");
return BOOLEAN_VAL(krk_isInstanceOf(argv[0], AS_CLASS(argv[1])));
}
static KrkValue _module_repr(int argc, KrkValue argv[]) {
KrkInstance * self = AS_INSTANCE(argv[0]);
KrkValue name = NONE_VAL();
krk_tableGet(&self->fields, vm.specialMethodNames[METHOD_NAME], &name);
if (!IS_STRING(name)) {
return OBJECT_VAL(S("<module>"));
}
KrkValue file = NONE_VAL();
krk_tableGet(&self->fields, vm.specialMethodNames[METHOD_FILE], &file);
char * tmp = malloc(50 + AS_STRING(name)->length + (IS_STRING(file) ? AS_STRING(file)->length : 20));
if (IS_STRING(file)) {
sprintf(tmp, "<module '%s' from '%s'>", AS_CSTRING(name), AS_CSTRING(file));
} else {
sprintf(tmp, "<module '%s' (built-in)>", AS_CSTRING(name));
}
KrkValue out = OBJECT_VAL(krk_copyString(tmp, strlen(tmp)));
free(tmp);
return out;
}
/**
* object.__str__() / object.__repr__()
*
* Base method for all objects to implement __str__ and __repr__.
* Generally converts to <instance of [TYPE]> and for actual object
* types (functions, classes, instances, strings...) also adds the pointer
* address of the object on the heap.
*
* Since all types have at least a pseudo-class that should eventually
* inheret from object() and this is object.__str__ / object.__repr__,
* all types should have a string representation available through
* those methods.
*/
static KrkValue _strBase(int argc, KrkValue argv[]) {
KrkClass * type = krk_getType(argv[0]);
size_t len = sizeof("<instance of . at 0x1234567812345678>") + type->name->length;
char * tmp = malloc(len);
if (IS_OBJECT(argv[0])) {
sprintf(tmp, "<instance of %s at %p>", type->name->chars, (void*)AS_OBJECT(argv[0]));
} else {
sprintf(tmp, "<instance of %s>", type->name->chars);
}
KrkValue out = OBJECT_VAL(krk_copyString(tmp, strlen(tmp)));
free(tmp);
return out;
}
static KrkValue _type(int argc, KrkValue argv[]) {
return OBJECT_VAL(krk_getType(argv[0]));
}
#define IS_Helper(o) (krk_isInstanceOf(o, Helper))
#define AS_Helper(o) (AS_INSTANCE(o))
#define IS_LicenseReader(o) (krk_isInstanceOf(o, LicenseReader))
#define AS_LicenseReader(o) (AS_INSTANCE(o))
#define CURRENT_CTYPE KrkInstance *
#define CURRENT_NAME self
KRK_METHOD(Helper,__repr__,{
return OBJECT_VAL(S("Type help() for more help, or help(obj) to describe an object."));
})
KRK_METHOD(Helper,__call__,{
METHOD_TAKES_AT_MOST(1);
if (!krk_doRecursiveModuleLoad(S("help"))) return NONE_VAL();
KrkValue helpModule;
if (!krk_tableGet(&vm.modules, OBJECT_VAL(S("help")), &helpModule) || !IS_INSTANCE(helpModule))
return krk_runtimeError(vm.exceptions.importError, "Could not import help module");
KrkValue callable = NONE_VAL();
if (argc == 2) {
krk_tableGet(&AS_INSTANCE(helpModule)->fields, OBJECT_VAL(S("simple")), &callable);
krk_push(argv[1]);
} else {
krk_tableGet(&AS_INSTANCE(helpModule)->fields, OBJECT_VAL(S("interactive")), &callable);
}
if (!IS_NONE(callable)) {
return krk_callSimple(callable, argc == 2, 0);
}
return krk_runtimeError(vm.exceptions.typeError, "unexpected error");
})
KRK_METHOD(LicenseReader,__repr__,{
return OBJECT_VAL(S("Copyright 2020-2021 K. Lange <klange@toaruos.org>. Type `license()` for more information."));
})
KRK_METHOD(LicenseReader,__call__,{
METHOD_TAKES_NONE();
if (!krk_doRecursiveModuleLoad(S("help"))) return NONE_VAL();
KrkValue helpModule;
if (!krk_tableGet(&vm.modules, OBJECT_VAL(S("help")), &helpModule) || !IS_INSTANCE(helpModule))
return krk_runtimeError(vm.exceptions.importError, "Could not import help module");
KrkValue text = NONE_VAL();
krk_tableGet(&AS_INSTANCE(helpModule)->fields, OBJECT_VAL(S("__licenseText")), &text);
if (IS_STRING(text)) {
printf("%s\n", AS_CSTRING(text));
return NONE_VAL();
}
return krk_runtimeError(vm.exceptions.typeError, "unexpected error");
})
_noexport
void _createAndBind_builtins(void) {
vm.objectClass = krk_newClass(S("object"), NULL);
krk_defineNative(&vm.objectClass->methods, ":__class__", _type);
krk_defineNative(&vm.objectClass->methods, ".__dir__", krk_dirObject);
krk_defineNative(&vm.objectClass->methods, ".__str__", _strBase);
krk_defineNative(&vm.objectClass->methods, ".__repr__", _strBase); /* Override if necesary */
krk_finalizeClass(vm.objectClass);
vm.objectClass->docstring = S("Base class for all types.");
vm.moduleClass = krk_newClass(S("module"), vm.objectClass);
krk_defineNative(&vm.moduleClass->methods, ".__repr__", _module_repr);
krk_defineNative(&vm.moduleClass->methods, ".__str__", _module_repr);
krk_finalizeClass(vm.moduleClass);
vm.moduleClass->docstring = S("");
vm.builtins = krk_newInstance(vm.moduleClass);
krk_attachNamedObject(&vm.modules, "__builtins__", (KrkObj*)vm.builtins);
krk_attachNamedObject(&vm.builtins->fields, "object", (KrkObj*)vm.objectClass);
krk_attachNamedObject(&vm.builtins->fields, "__name__", (KrkObj*)S("__builtins__"));
krk_attachNamedValue(&vm.builtins->fields, "__file__", NONE_VAL());
krk_attachNamedObject(&vm.builtins->fields, "__doc__",
(KrkObj*)S("Internal module containing built-in functions and classes."));
krk_makeClass(vm.builtins, &Helper, "Helper", vm.objectClass);
BIND_METHOD(Helper,__call__);
BIND_METHOD(Helper,__repr__);
krk_finalizeClass(Helper);
krk_attachNamedObject(&vm.builtins->fields, "help", (KrkObj*)krk_newInstance(Helper));
krk_makeClass(vm.builtins, &LicenseReader, "LicenseReader", vm.objectClass);
BIND_METHOD(LicenseReader,__call__);
BIND_METHOD(LicenseReader,__repr__);
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.");
BUILTIN_FUNCTION("len", _len, "Return the length of a given sequence object.");
BUILTIN_FUNCTION("repr", _repr, "Produce a string representation of the given object.");
BUILTIN_FUNCTION("print", _print, "Print values to the standard output descriptor.");
BUILTIN_FUNCTION("ord", _ord, "Obtain the ordinal integer value of a codepoint or byte.");
BUILTIN_FUNCTION("chr", _chr, "Convert an integer codepoint to its string representation.");
BUILTIN_FUNCTION("hex", _hex, "Convert an integer value to a hexadecimal string.");
BUILTIN_FUNCTION("any", _any, "Returns True if at least one element in the given iterable is truthy, False otherwise.");
BUILTIN_FUNCTION("all", _all, "Returns True if every element in the given iterable is truthy, False otherwise.");
}

View File

@ -1,47 +0,0 @@
# Please avoid using double quotes or escape sequences
# in this file to allow it to be easily converted to C.
class Helper():
'''You seem to already know how to use this.'''
def __call__(self,obj=None):
if obj is not None:
try:
print(obj.__doc__)
except:
try:
print(obj.__class__.__doc__)
except:
print('No docstring avaialble for', obj)
else:
from help import interactive
interactive()
def __repr__(self):
return 'Type help() for more help, or help(obj) to describe an object.'
let help = Helper()
class LicenseReader():
def __call__(self):
from help import __licenseText
print(__licenseText)
def __repr__(self):
return 'Copyright 2020-2021 K. Lange <klange@toaruos.org>. Type `license()` for more information.'
let license = LicenseReader()
__builtins__.help = help
__builtins__.license = license
# this works because `kuroko` is always a built-in
import kuroko
kuroko.module_paths = ['./']
if 'executable_path' in dir(kuroko):
let pathunits = kuroko.executable_path.split(kuroko.path_sep)[:-1]
let dirname = pathunits[-1]
if dirname == 'bin':
pathunits.pop(-1)
pathunits.extend(['lib','kuroko',''])
else:
pathunits.extend(['modules',''])
kuroko.module_paths.append(kuroko.path_sep.join(pathunits))
return object()

57
src/obj_base.c Normal file
View File

@ -0,0 +1,57 @@
#include <string.h>
#include "vm.h"
#include "value.h"
#include "memory.h"
#include "util.h"
static KrkValue _type_init(int argc, KrkValue argv[]) {
if (argc != 2) return krk_runtimeError(vm.exceptions.argumentError, "type() takes 1 argument");
return OBJECT_VAL(krk_getType(argv[1]));
}
/* Class.__base__ */
static KrkValue krk_baseOfClass(int argc, KrkValue argv[]) {
if (!IS_CLASS(argv[0])) return krk_runtimeError(vm.exceptions.typeError, "expected class");
return AS_CLASS(argv[0])->base ? OBJECT_VAL(AS_CLASS(argv[0])->base) : NONE_VAL();
}
/* Class.__name */
static KrkValue krk_nameOfClass(int argc, KrkValue argv[]) {
if (!IS_CLASS(argv[0])) return krk_runtimeError(vm.exceptions.typeError, "expected class");
return AS_CLASS(argv[0])->name ? OBJECT_VAL(AS_CLASS(argv[0])->name) : NONE_VAL();
}
/* Class.__file__ */
static KrkValue krk_fileOfClass(int argc, KrkValue argv[]) {
if (!IS_CLASS(argv[0])) return krk_runtimeError(vm.exceptions.typeError, "expected class");
return AS_CLASS(argv[0])->filename ? OBJECT_VAL(AS_CLASS(argv[0])->filename) : NONE_VAL();
}
/* Class.__doc__ */
static KrkValue krk_docOfClass(int argc, KrkValue argv[]) {
if (!IS_CLASS(argv[0])) return krk_runtimeError(vm.exceptions.typeError, "expected class");
return AS_CLASS(argv[0])->docstring ? OBJECT_VAL(AS_CLASS(argv[0])->docstring) : NONE_VAL();
}
/* Class.__str__() (and Class.__repr__) */
static KrkValue _class_to_str(int argc, KrkValue argv[]) {
if (!IS_CLASS(argv[0])) return krk_runtimeError(vm.exceptions.typeError, "expected class");
char * tmp = malloc(sizeof("<type ''>") + AS_CLASS(argv[0])->name->length);
size_t l = sprintf(tmp, "<type '%s'>", AS_CLASS(argv[0])->name->chars);
KrkString * out = krk_copyString(tmp,l);
free(tmp);
return OBJECT_VAL(out);
}
void _createAndBind_type(void) {
ADD_BASE_CLASS(vm.baseClasses.typeClass, "type", vm.objectClass);
krk_defineNative(&vm.baseClasses.typeClass->methods, ":__base__", krk_baseOfClass);
krk_defineNative(&vm.baseClasses.typeClass->methods, ":__file__", krk_fileOfClass);
krk_defineNative(&vm.baseClasses.typeClass->methods, ":__doc__", krk_docOfClass);
krk_defineNative(&vm.baseClasses.typeClass->methods, ":__name__", krk_nameOfClass);
krk_defineNative(&vm.baseClasses.typeClass->methods, ".__init__", _type_init);
krk_defineNative(&vm.baseClasses.typeClass->methods, ".__str__", _class_to_str);
krk_defineNative(&vm.baseClasses.typeClass->methods, ".__repr__", _class_to_str);
krk_finalizeClass(vm.baseClasses.typeClass);
vm.baseClasses.typeClass->docstring = S("Obtain the object representation of the class of an object.");
}

120
src/obj_numeric.c Normal file
View File

@ -0,0 +1,120 @@
#include <string.h>
#include "vm.h"
#include "value.h"
#include "memory.h"
#include "util.h"
#undef IS_int
#undef IS_bool
#undef IS_float
#define IS_int(o) (IS_INTEGER(o) || krk_isInstanceOf(o,vm.baseClasses.intClass))
#define IS_bool(o) (IS_BOOLEAN(o) || krk_isInstanceOf(o,vm.baseClasses.boolClass))
#define IS_float(o) (IS_FLOATING(o) || krk_isInstanceOf(o,vm.baseClasses.floatClass))
#define IS_NoneType(o) (IS_NONE(o))
#define AS_NoneType(o) ((char)0)
#define CURRENT_CTYPE krk_integer_type
#define CURRENT_NAME self
KRK_METHOD(int,__init__,{
METHOD_TAKES_AT_MOST(1);
if (argc < 2) return INTEGER_VAL(0);
if (IS_INTEGER(argv[1])) return argv[1];
if (IS_STRING(argv[1])) return krk_string_int(argc-1,&argv[1],0);
if (IS_FLOATING(argv[1])) return INTEGER_VAL(AS_FLOATING(argv[1]));
if (IS_BOOLEAN(argv[1])) return INTEGER_VAL(AS_BOOLEAN(argv[1]));
return krk_runtimeError(vm.exceptions.typeError, "int() argument must be a string or a number, not '%s'", krk_typeName(argv[1]));
})
KRK_METHOD(int,__str__,{
char tmp[100];
size_t l = sprintf(tmp, PRIkrk_int, self);
return OBJECT_VAL(krk_copyString(tmp, l));
})
KRK_METHOD(int,__int__,{ return argv[0]; })
KRK_METHOD(int,__float__,{ return FLOATING_VAL(self); })
KRK_METHOD(int,__chr__,{
unsigned char bytes[5] = {0};
size_t len = krk_codepointToBytes(self, bytes);
return OBJECT_VAL(krk_copyString((char*)bytes, len));
})
#undef CURRENT_CTYPE
#define CURRENT_CTYPE double
KRK_METHOD(float,__init__,{
METHOD_TAKES_AT_MOST(1);
if (argc < 2) return FLOATING_VAL(0.0);
if (argc > 2) return krk_runtimeError(vm.exceptions.argumentError, "float() takes at most 1 argument");
if (IS_STRING(argv[1])) return krk_string_float(1,&argv[1],0);
if (IS_FLOATING(argv[1])) return argv[1];
if (IS_INTEGER(argv[1])) return FLOATING_VAL(AS_INTEGER(argv[1]));
if (IS_BOOLEAN(argv[1])) return FLOATING_VAL(AS_BOOLEAN(argv[1]));
return krk_runtimeError(vm.exceptions.typeError, "float() argument must be a string or a number, not '%s'", krk_typeName(argv[1]));
})
KRK_METHOD(float,__int__,{ return INTEGER_VAL(self); })
KRK_METHOD(float,__float__,{ return argv[0]; })
KRK_METHOD(float,__str__,{
char tmp[100];
size_t l = sprintf(tmp, "%g", self);
return OBJECT_VAL(krk_copyString(tmp, l));
})
#undef CURRENT_CTYPE
#define CURRENT_CTYPE char
KRK_METHOD(bool,__init__,{
METHOD_TAKES_AT_MOST(1);
if (argc < 2) return BOOLEAN_VAL(0);
return BOOLEAN_VAL(!krk_isFalsey(argv[1]));
})
KRK_METHOD(bool,__str__,{
return OBJECT_VAL((self ? S("True") : S("False")));
})
KRK_METHOD(NoneType,__str__,{
return OBJECT_VAL(S("None"));
})
#undef BIND_METHOD
#define BIND_METHOD(klass,method) do { krk_defineNative(& _ ## klass->methods, "." #method, _ ## klass ## _ ## method); } while (0)
_noexport
void _createAndBind_numericClasses(void) {
KrkClass * _int = ADD_BASE_CLASS(vm.baseClasses.intClass, "int", vm.objectClass);
BIND_METHOD(int,__init__);
BIND_METHOD(int,__str__);
BIND_METHOD(int,__int__);
BIND_METHOD(int,__chr__);
BIND_METHOD(int,__float__);
krk_defineNative(&_int->methods, ".__repr__", FUNC_NAME(int,__str__));
krk_finalizeClass(_int);
_int->docstring = S("Convert a number or string type to an integer representation.");
KrkClass * _float = ADD_BASE_CLASS(vm.baseClasses.floatClass, "float", vm.objectClass);
BIND_METHOD(float,__init__);
BIND_METHOD(float,__int__);
BIND_METHOD(float,__float__);
BIND_METHOD(float,__str__);
krk_defineNative(&_float->methods, ".__repr__", FUNC_NAME(float,__str__));
krk_finalizeClass(_float);
_float->docstring = S("Convert a number or string type to a float representation.");
KrkClass * _bool = ADD_BASE_CLASS(vm.baseClasses.boolClass, "bool", vm.objectClass);
BIND_METHOD(bool,__init__);
BIND_METHOD(bool,__str__);
krk_defineNative(&_bool->methods, ".__repr__", FUNC_NAME(bool,__str__));
krk_finalizeClass(_bool);
_bool->docstring = S("Returns False if the argument is 'falsey', otherwise True.");
KrkClass * _NoneType = ADD_BASE_CLASS(vm.baseClasses.noneTypeClass, "NoneType", vm.objectClass);
BIND_METHOD(NoneType, __str__);
krk_defineNative(&_NoneType->methods, ".__repr__", FUNC_NAME(NoneType,__str__));
krk_finalizeClass(_NoneType);
}

View File

@ -114,6 +114,12 @@ static inline KrkValue discardStringBuilder(struct StringBuilder * sb) {
#define IS_int(o) (IS_INTEGER(o))
#define AS_int(o) (AS_INTEGER(o))
#define IS_bool(o) (IS_BOOLEAN(o))
#define AS_bool(o) (AS_BOOLEAN(o))
#define IS_float(o) (IS_FLOATING(o))
#define AS_float(o) (AS_FLOATING(o))
#define IS_list(o) krk_isInstanceOf(o,vm.baseClasses.listClass)
#define AS_list(o) (KrkList*)AS_OBJECT(o)

616
src/vm.c
View File

@ -56,9 +56,6 @@
/* This is macro'd to krk_vm for namespacing reasons. */
KrkVM vm = {0};
/* Embedded script for extensions to builtin-ins; see builtins.c/builtins.krk */
extern const char krk_builtinsSrc[];
/**
* Reset the stack pointers, frame, upvalue list,
* clear the exception flag and current exception;
@ -404,66 +401,6 @@ static KrkValue krk_set_tracing(int argc, KrkValue argv[], int hasKw) {
#endif
}
/**
* object.__dir__()
*/
KrkValue krk_dirObject(int argc, KrkValue argv[]) {
if (argc != 1) return krk_runtimeError(vm.exceptions.argumentError, "wrong number of arguments or bad type, got %d\n", argc);
/* Create a new list instance */
KrkValue myList = krk_list_of(0,NULL);
krk_push(myList);
if (IS_INSTANCE(argv[0])) {
/* Obtain self-reference */
KrkInstance * self = AS_INSTANCE(argv[0]);
/* First add each method of the class */
for (size_t i = 0; i < self->_class->methods.capacity; ++i) {
if (self->_class->methods.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
self->_class->methods.entries[i].key);
}
}
/* Then add each field of the instance */
for (size_t i = 0; i < self->fields.capacity; ++i) {
if (self->fields.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
self->fields.entries[i].key);
}
}
} else {
if (IS_CLASS(argv[0])) {
KrkClass * _class = AS_CLASS(argv[0]);
for (size_t i = 0; i < _class->methods.capacity; ++i) {
if (_class->methods.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
_class->methods.entries[i].key);
}
}
for (size_t i = 0; i < _class->fields.capacity; ++i) {
if (_class->fields.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
_class->fields.entries[i].key);
}
}
}
KrkClass * type = krk_getType(argv[0]);
for (size_t i = 0; i < type->methods.capacity; ++i) {
if (type->methods.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
type->methods.entries[i].key);
}
}
}
/* Prepare output value */
krk_pop();
return myList;
}
/**
* Maps values to their base classes.
* Internal version of type().
@ -504,52 +441,6 @@ inline KrkClass * krk_getType(KrkValue of) {
}
}
/**
* type()
*/
KrkValue _type(int argc, KrkValue argv[]) {
return OBJECT_VAL(krk_getType(argv[0]));
}
static KrkValue _type_init(int argc, KrkValue argv[]) {
if (argc != 2) return krk_runtimeError(vm.exceptions.argumentError, "type() takes 1 argument");
return OBJECT_VAL(krk_getType(argv[1]));
}
/* Class.__base__ */
static KrkValue krk_baseOfClass(int argc, KrkValue argv[]) {
if (!IS_CLASS(argv[0])) return krk_runtimeError(vm.exceptions.typeError, "expected class");
return AS_CLASS(argv[0])->base ? OBJECT_VAL(AS_CLASS(argv[0])->base) : NONE_VAL();
}
/* Class.__name */
static KrkValue krk_nameOfClass(int argc, KrkValue argv[]) {
if (!IS_CLASS(argv[0])) return krk_runtimeError(vm.exceptions.typeError, "expected class");
return AS_CLASS(argv[0])->name ? OBJECT_VAL(AS_CLASS(argv[0])->name) : NONE_VAL();
}
/* Class.__file__ */
static KrkValue krk_fileOfClass(int argc, KrkValue argv[]) {
if (!IS_CLASS(argv[0])) return krk_runtimeError(vm.exceptions.typeError, "expected class");
return AS_CLASS(argv[0])->filename ? OBJECT_VAL(AS_CLASS(argv[0])->filename) : NONE_VAL();
}
/* Class.__doc__ */
static KrkValue krk_docOfClass(int argc, KrkValue argv[]) {
if (!IS_CLASS(argv[0])) return krk_runtimeError(vm.exceptions.typeError, "expected class");
return AS_CLASS(argv[0])->docstring ? OBJECT_VAL(AS_CLASS(argv[0])->docstring) : NONE_VAL();
}
/* Class.__str__() (and Class.__repr__) */
static KrkValue _class_to_str(int argc, KrkValue argv[]) {
if (!IS_CLASS(argv[0])) return krk_runtimeError(vm.exceptions.typeError, "expected class");
char * tmp = malloc(sizeof("<type ''>") + AS_CLASS(argv[0])->name->length);
size_t l = sprintf(tmp, "<type '%s'>", AS_CLASS(argv[0])->name->chars);
KrkString * out = krk_copyString(tmp,l);
free(tmp);
return OBJECT_VAL(out);
}
/**
* isinstance(obj,Class)
*
@ -566,28 +457,6 @@ int krk_isInstanceOf(KrkValue obj, KrkClass * type) {
}
return 0;
}
static KrkValue _isinstance(int argc, KrkValue argv[]) {
if (argc != 2) return krk_runtimeError(vm.exceptions.argumentError, "isinstance expects 2 arguments, got %d", argc);
if (!IS_CLASS(argv[1])) return krk_runtimeError(vm.exceptions.typeError, "isinstance() arg 2 must be class");
return BOOLEAN_VAL(krk_isInstanceOf(argv[0], AS_CLASS(argv[1])));
}
/**
* globals()
*
* Returns a dict of names -> values for all the globals.
*/
static KrkValue krk_globals(int argc, KrkValue argv[]) {
/* Make a new empty dict */
KrkValue dict = krk_dict_of(0, NULL);
krk_push(dict);
/* Copy the globals table into it */
krk_tableAddAll(vm.frames[vm.frameCount-1].globals, AS_DICT(dict));
krk_pop();
return dict;
}
static int checkArgumentCount(KrkClosure * closure, int argCount) {
int minArgs = closure->function->requiredArgs;
@ -1147,165 +1016,6 @@ _badSyntaxError:
krk_finalizeClass(obj); \
} while (0)
/** native method that returns its first arg; useful for int(INT), etc. */
KrkValue _noop(int argc, KrkValue argv[]) {
return argv[0];
}
/* float.__int__() */
static KrkValue _floating_to_int(int argc, KrkValue argv[]) {
return INTEGER_VAL((long)AS_FLOATING(argv[0]));
}
/* int.__float__() */
static KrkValue _int_to_floating(int argc, KrkValue argv[]) {
return FLOATING_VAL((double)AS_INTEGER(argv[0]));
}
/* int.__chr__() */
static KrkValue _int_to_char(int argc, KrkValue argv[]) {
krk_integer_type value = AS_INTEGER(argv[0]);
unsigned char bytes[5] = {0};
size_t len = krk_codepointToBytes(value, bytes);
return OBJECT_VAL(krk_copyString((char*)bytes,len));
}
static KrkValue _print(int argc, KrkValue argv[], int hasKw) {
KrkValue sepVal, endVal;
char * sep = " "; size_t sepLen = 1;
char * end = "\n"; size_t endLen = 1;
if (hasKw) {
if (krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("sep")), &sepVal)) {
if (!IS_STRING(sepVal)) return krk_runtimeError(vm.exceptions.typeError, "'sep' should be a string, not '%s'", krk_typeName(sepVal));
sep = AS_CSTRING(sepVal);
sepLen = AS_STRING(sepVal)->length;
}
if (krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("end")), &endVal)) {
if (!IS_STRING(endVal)) return krk_runtimeError(vm.exceptions.typeError, "'end' should be a string, not '%s'", krk_typeName(endVal));
end = AS_CSTRING(endVal);
endLen = AS_STRING(endVal)->length;
}
}
for (int i = 0; i < argc; ++i) {
KrkValue printable = argv[i];
if (IS_STRING(printable)) { /* krk_printValue runs repr */
/* Make sure we handle nil bits correctly. */
for (size_t j = 0; j < AS_STRING(printable)->length; ++j) {
fputc(AS_CSTRING(printable)[j], stdout);
}
} else {
krk_printValue(stdout, printable);
}
char * thingToPrint = (i == argc - 1) ? end : sep;
for (size_t j = 0; j < ((i == argc - 1) ? endLen : sepLen); ++j) {
fputc(thingToPrint[j], stdout);
}
}
return NONE_VAL();
}
static KrkValue _int_init(int argc, KrkValue argv[]) {
if (argc < 2) return INTEGER_VAL(0);
if (IS_INTEGER(argv[1])) return argv[1];
if (IS_STRING(argv[1])) return krk_string_int(argc-1,&argv[1],0);
if (IS_FLOATING(argv[1])) return INTEGER_VAL(AS_FLOATING(argv[1]));
if (IS_BOOLEAN(argv[1])) return INTEGER_VAL(AS_BOOLEAN(argv[1]));
return krk_runtimeError(vm.exceptions.typeError, "int() argument must be a string or a number, not '%s'", krk_typeName(argv[1]));
}
static KrkValue _float_init(int argc, KrkValue argv[]) {
if (argc < 1) return FLOATING_VAL(0.0);
if (argc > 2) return krk_runtimeError(vm.exceptions.argumentError, "float() takes at most 1 argument");
if (IS_STRING(argv[1])) return krk_string_float(1,&argv[1],0);
if (IS_FLOATING(argv[1])) return argv[1];
if (IS_INTEGER(argv[1])) return FLOATING_VAL(AS_INTEGER(argv[1]));
if (IS_BOOLEAN(argv[1])) return FLOATING_VAL(AS_BOOLEAN(argv[1]));
return krk_runtimeError(vm.exceptions.typeError, "float() argument must be a string or a number, not '%s'", krk_typeName(argv[1]));
}
/**
* object.__str__() / object.__repr__()
*
* Base method for all objects to implement __str__ and __repr__.
* Generally converts to <instance of [TYPE]> and for actual object
* types (functions, classes, instances, strings...) also adds the pointer
* address of the object on the heap.
*
* Since all types have at least a pseudo-class that should eventually
* inheret from object() and this is object.__str__ / object.__repr__,
* all types should have a string representation available through
* those methods.
*/
static KrkValue _strBase(int argc, KrkValue argv[]) {
KrkClass * type = krk_getType(argv[0]);
size_t len = sizeof("<instance of . at 0x1234567812345678>") + type->name->length;
char * tmp = malloc(len);
if (IS_OBJECT(argv[0])) {
sprintf(tmp, "<instance of %s at %p>", type->name->chars, (void*)AS_OBJECT(argv[0]));
} else {
sprintf(tmp, "<instance of %s>", type->name->chars);
}
KrkValue out = OBJECT_VAL(krk_copyString(tmp, strlen(tmp)));
free(tmp);
return out;
}
static KrkValue _module_repr(int argc, KrkValue argv[]) {
KrkInstance * self = AS_INSTANCE(argv[0]);
KrkValue name = NONE_VAL();
krk_tableGet(&self->fields, vm.specialMethodNames[METHOD_NAME], &name);
if (!IS_STRING(name)) {
return OBJECT_VAL(S("<module>"));
}
KrkValue file = NONE_VAL();
krk_tableGet(&self->fields, vm.specialMethodNames[METHOD_FILE], &file);
char * tmp = malloc(50 + AS_STRING(name)->length + (IS_STRING(file) ? AS_STRING(file)->length : 20));
if (IS_STRING(file)) {
sprintf(tmp, "<module '%s' from '%s'>", AS_CSTRING(name), AS_CSTRING(file));
} else {
sprintf(tmp, "<module '%s' (built-in)>", AS_CSTRING(name));
}
KrkValue out = OBJECT_VAL(krk_copyString(tmp, strlen(tmp)));
free(tmp);
return out;
}
/**
* int.__str__()
*
* Unlike Python, dot accessors are perfectly valid and work as you'd expect
* them to in Kuroko, so we can do 123.__str__() and get the string "123".
*
* TODO: Implement format options here so we can get different widths,
* hex/octal/binary representations, etc.
*/
static KrkValue _int_to_str(int argc, KrkValue argv[]) {
char tmp[100];
size_t l = sprintf(tmp, PRIkrk_int, (krk_integer_type)AS_INTEGER(argv[0]));
return OBJECT_VAL(krk_copyString(tmp, l));
}
/**
* float.__str__()
*/
static KrkValue _float_to_str(int argc, KrkValue argv[]) {
char tmp[100];
size_t l = sprintf(tmp, "%g", AS_FLOATING(argv[0]));
return OBJECT_VAL(krk_copyString(tmp, l));
}
/**
* bool.__str__() -> "True" or "False"
*/
static KrkValue _bool_to_str(int argc, KrkValue argv[]) {
return OBJECT_VAL((AS_BOOLEAN(argv[0]) ? S("True") : S("False")));
}
/**
* Inverse of truthiness.
*
@ -1315,7 +1025,7 @@ static KrkValue _bool_to_str(int argc, KrkValue argv[]) {
* Or in more managed code terms, `if None`, `if False`, and `if 0` are all
* going to take the else branch.
*/
static int isFalsey(KrkValue value) {
int krk_isFalsey(KrkValue value) {
switch (value.type) {
case VAL_NONE: return 1;
case VAL_BOOLEAN: return !AS_BOOLEAN(value);
@ -1340,179 +1050,6 @@ static int isFalsey(KrkValue value) {
return 0; /* Assume anything else is truthy */
}
static KrkValue _bool_init(int argc, KrkValue argv[]) {
if (argc < 2) return BOOLEAN_VAL(0);
if (argc > 2) return krk_runtimeError(vm.exceptions.argumentError, "bool() takes at most 1 argument");
return BOOLEAN_VAL(!isFalsey(argv[1]));
}
/**
* None.__str__() -> "None"
*/
static KrkValue _none_to_str(int argc, KrkValue argv[]) {
return OBJECT_VAL(S("None"));
}
static KrkValue _len(int argc, KrkValue argv[]) {
if (argc != 1) return krk_runtimeError(vm.exceptions.argumentError, "len() takes exactly one argument");
/* Shortcuts */
if (IS_STRING(argv[0])) return INTEGER_VAL(AS_STRING(argv[0])->codesLength);
if (IS_TUPLE(argv[0])) return INTEGER_VAL(AS_TUPLE(argv[0])->values.count);
KrkClass * type = krk_getType(argv[0]);
if (!type->_len) return krk_runtimeError(vm.exceptions.typeError, "object of type '%s' has no len()", krk_typeName(argv[0]));
krk_push(argv[0]);
return krk_callSimple(OBJECT_VAL(type->_len), 1, 0);
}
static KrkValue _dir(int argc, KrkValue argv[]) {
if (argc != 1) return krk_runtimeError(vm.exceptions.argumentError, "dir() takes exactly one argument");
KrkClass * type = krk_getType(argv[0]);
if (!type->_dir) {
return krk_dirObject(argc,argv); /* Fallback */
}
krk_push(argv[0]);
return krk_callSimple(OBJECT_VAL(type->_dir), 1, 0);
}
static KrkValue _repr(int argc, KrkValue argv[]) {
if (argc != 1) return krk_runtimeError(vm.exceptions.argumentError, "repr() takes exactly one argument");
/* Everything should have a __repr__ */
KrkClass * type = krk_getType(argv[0]);
krk_push(argv[0]);
return krk_callSimple(OBJECT_VAL(type->_reprer), 1, 0);
}
static KrkValue _ord(int argc, KrkValue argv[]) {
if (argc != 1) return krk_runtimeError(vm.exceptions.argumentError, "ord() takes exactly one argument");
KrkClass * type = krk_getType(argv[0]);
KrkValue method;
if (krk_tableGet(&type->methods, vm.specialMethodNames[METHOD_ORD], &method)) {
krk_push(argv[0]);
return krk_callSimple(method, 1, 0);
}
return krk_runtimeError(vm.exceptions.argumentError, "ord() expected string of length 1, but got %s", krk_typeName(argv[0]));
}
static KrkValue _chr(int argc, KrkValue argv[]) {
if (argc != 1) return krk_runtimeError(vm.exceptions.argumentError, "chr() takes exactly one argument");
KrkClass * type = krk_getType(argv[0]);
KrkValue method;
if (krk_tableGet(&type->methods, vm.specialMethodNames[METHOD_CHR], &method)) {
krk_push(argv[0]);
return krk_callSimple(method, 1, 0);
}
return krk_runtimeError(vm.exceptions.argumentError, "chr() expected an integer, but got %s", krk_typeName(argv[0]));
}
static KrkValue _hex(int argc, KrkValue argv[]) {
if (argc != 1 || !IS_INTEGER(argv[0])) return krk_runtimeError(vm.exceptions.argumentError, "hex() expects one int argument");
char tmp[20];
krk_integer_type x = AS_INTEGER(argv[0]);
size_t len = sprintf(tmp, "%s0x" PRIkrk_hex, x < 0 ? "-" : "", x < 0 ? -x : x);
return OBJECT_VAL(krk_copyString(tmp,len));
}
static KrkValue _any(int argc, KrkValue argv[]) {
#define unpackArray(counter, indexer) do { \
for (size_t i = 0; i < counter; ++i) { \
if (!isFalsey(indexer)) return BOOLEAN_VAL(1); \
} \
} while (0)
KrkValue value = argv[0];
if (IS_TUPLE(value)) {
unpackArray(AS_TUPLE(value)->values.count, AS_TUPLE(value)->values.values[i]);
} else if (IS_INSTANCE(value) && AS_INSTANCE(value)->_class == vm.baseClasses.listClass) {
unpackArray(AS_LIST(value)->count, AS_LIST(value)->values[i]);
} else if (IS_INSTANCE(value) && AS_INSTANCE(value)->_class == vm.baseClasses.dictClass) {
unpackArray(AS_DICT(value)->count, krk_dict_nth_key_fast(AS_DICT(value)->capacity, AS_DICT(value)->entries, i));
} else if (IS_STRING(value)) {
unpackArray(AS_STRING(value)->codesLength, krk_string_get(2,(KrkValue[]){value,INTEGER_VAL(i)},0));
} else {
KrkClass * type = krk_getType(argv[0]);
if (type->_iter) {
/* Create the iterator */
size_t stackOffset = vm.stackTop - vm.stack;
krk_push(argv[1]);
krk_push(krk_callSimple(OBJECT_VAL(type->_iter), 1, 0));
do {
/* Call it until it gives us itself */
krk_push(vm.stack[stackOffset]);
krk_push(krk_callSimple(krk_peek(0), 0, 1));
if (krk_valuesSame(vm.stack[stackOffset], krk_peek(0))) {
/* We're done. */
krk_pop(); /* The result of iteration */
krk_pop(); /* The iterator */
break;
}
if (!isFalsey(krk_peek(0))) {
krk_pop();
krk_pop();
return BOOLEAN_VAL(1);
}
krk_pop();
} while (1);
} else {
return krk_runtimeError(vm.exceptions.typeError, "'%s' object is not iterable", krk_typeName(value));
}
}
#undef unpackArray
return BOOLEAN_VAL(0);
}
static KrkValue _all(int argc, KrkValue argv[]) {
#define unpackArray(counter, indexer) do { \
for (size_t i = 0; i < counter; ++i) { \
if (isFalsey(indexer)) return BOOLEAN_VAL(0); \
} \
} while (0)
KrkValue value = argv[0];
if (IS_TUPLE(value)) {
unpackArray(AS_TUPLE(value)->values.count, AS_TUPLE(value)->values.values[i]);
} else if (IS_INSTANCE(value) && AS_INSTANCE(value)->_class == vm.baseClasses.listClass) {
unpackArray(AS_LIST(value)->count, AS_LIST(value)->values[i]);
} else if (IS_INSTANCE(value) && AS_INSTANCE(value)->_class == vm.baseClasses.dictClass) {
unpackArray(AS_DICT(value)->count, krk_dict_nth_key_fast(AS_DICT(value)->capacity, AS_DICT(value)->entries, i));
} else if (IS_STRING(value)) {
unpackArray(AS_STRING(value)->codesLength, krk_string_get(2,(KrkValue[]){value,INTEGER_VAL(i)},0));
} else {
KrkClass * type = krk_getType(argv[0]);
if (type->_iter) {
/* Create the iterator */
size_t stackOffset = vm.stackTop - vm.stack;
krk_push(argv[1]);
krk_push(krk_callSimple(OBJECT_VAL(type->_iter), 1, 0));
do {
/* Call it until it gives us itself */
krk_push(vm.stack[stackOffset]);
krk_push(krk_callSimple(krk_peek(0), 0, 1));
if (krk_valuesSame(vm.stack[stackOffset], krk_peek(0))) {
/* We're done. */
krk_pop(); /* The result of iteration */
krk_pop(); /* The iterator */
break;
}
if (isFalsey(krk_peek(0))) {
krk_pop();
krk_pop();
return BOOLEAN_VAL(0);
}
krk_pop();
} while (1);
} else {
return krk_runtimeError(vm.exceptions.typeError, "'%s' object is not iterable", krk_typeName(value));
}
}
#undef unpackArray
return BOOLEAN_VAL(1);
}
static KrkValue krk_collectGarbage_wrapper(int argc, KrkValue argv[]) {
return INTEGER_VAL(krk_collectGarbage());
}
@ -1636,49 +1173,18 @@ void krk_initVM(int flags) {
vm.specialMethodNames[i] = OBJECT_VAL(krk_copyString(_methods[i].s, _methods[i].len));
}
/**
* class object()
*
* The base class for all types.
* Defines the last-resort implementation of __str__, __repr__, and __dir__.
*/
vm.objectClass = krk_newClass(S("object"), NULL);
krk_defineNative(&vm.objectClass->methods, ":__class__", _type);
krk_defineNative(&vm.objectClass->methods, ".__dir__", krk_dirObject);
krk_defineNative(&vm.objectClass->methods, ".__str__", _strBase);
krk_defineNative(&vm.objectClass->methods, ".__repr__", _strBase); /* Override if necesary */
krk_finalizeClass(vm.objectClass);
vm.objectClass->docstring = S("Base class for all types.");
/**
* class module(object)
*
* When files are imported as modules, their global namespace is the fields
* table of an instance of this class. All modules also end up with their
* names and file paths as __name__ and __file__.
*/
vm.moduleClass = krk_newClass(S("module"), vm.objectClass);
krk_defineNative(&vm.moduleClass->methods, ".__repr__", _module_repr);
krk_defineNative(&vm.moduleClass->methods, ".__str__", _module_repr);
krk_finalizeClass(vm.moduleClass);
vm.moduleClass->docstring = S("");
/**
* __builtins__ = module()
*
* The builtins namespace is always available underneath the current
* globals namespace, and is also added to all modules as __builtins__
* for direct references (eg., in case one of the names is shadowed
* by a global).
*/
vm.builtins = krk_newInstance(vm.moduleClass);
krk_attachNamedObject(&vm.modules, "__builtins__", (KrkObj*)vm.builtins);
krk_attachNamedObject(&vm.builtins->fields, "object", (KrkObj*)vm.objectClass);
krk_attachNamedObject(&vm.builtins->fields, "__name__", (KrkObj*)S("__builtins__"));
krk_attachNamedValue(&vm.builtins->fields, "__file__", NONE_VAL());
krk_attachNamedObject(&vm.builtins->fields, "__doc__",
(KrkObj*)S("Internal module containing built-in functions and classes."));
/* Build classes for basic types */
_createAndBind_builtins();
_createAndBind_type();
_createAndBind_numericClasses();
_createAndBind_strClass();
_createAndBind_listClass();
_createAndBind_tupleClass();
_createAndBind_bytesClass();
_createAndBind_dictClass();
_createAndBind_functionClass();
_createAndBind_rangeClass();
_createAndBind_setClass();
/**
* kuroko = module()
@ -1699,10 +1205,29 @@ void krk_initVM(int flags) {
krk_defineNative(&vm.system->fields, "set_clean_output", krk_setclean);
krk_defineNative(&vm.system->fields, "set_tracing", krk_set_tracing)->doc = "Toggle debugging modes.";
krk_attachNamedObject(&vm.system->fields, "path_sep", (KrkObj*)S(PATH_SEP));
KrkValue module_paths = krk_list_of(0,NULL);
krk_attachNamedValue(&vm.system->fields, "module_paths", module_paths);
krk_writeValueArray(AS_LIST(module_paths), OBJECT_VAL(S("./")));
if (vm.binpath) {
krk_attachNamedObject(&vm.system->fields, "executable_path", (KrkObj*)krk_takeString(vm.binpath, strlen(vm.binpath)));
char * dir = strdup(vm.binpath);
char * slash = strrchr(dir,'/');
if (slash) *slash = '\0';
if (strstr(dir,"/bin") == (dir + strlen(dir) - 4)) {
slash = strrchr(dir,'/');
if (slash) *slash = '\0';
char * out = malloc(sizeof("/lib/kuroko/") + strlen(dir));
size_t len = sprintf(out,"%s/lib/kuroko/", dir);
krk_writeValueArray(AS_LIST(module_paths), OBJECT_VAL(krk_takeString(out, len)));
} else {
char * out = malloc(sizeof("/modules/") + strlen(dir));
size_t len = sprintf(out,"%s/modules/", dir);
krk_writeValueArray(AS_LIST(module_paths), OBJECT_VAL(krk_takeString(out, len)));
}
free(dir);
}
/**
* gc = module()
*
@ -1738,77 +1263,6 @@ void krk_initVM(int flags) {
krk_defineNative(&vm.exceptions.syntaxError->methods, ".__repr__", _syntaxerror_repr);
krk_finalizeClass(vm.exceptions.syntaxError);
/* Build classes for basic types */
ADD_BASE_CLASS(vm.baseClasses.typeClass, "type", vm.objectClass);
krk_defineNative(&vm.baseClasses.typeClass->methods, ":__base__", krk_baseOfClass);
krk_defineNative(&vm.baseClasses.typeClass->methods, ":__file__", krk_fileOfClass);
krk_defineNative(&vm.baseClasses.typeClass->methods, ":__doc__", krk_docOfClass);
krk_defineNative(&vm.baseClasses.typeClass->methods, ":__name__", krk_nameOfClass);
krk_defineNative(&vm.baseClasses.typeClass->methods, ".__init__", _type_init);
krk_defineNative(&vm.baseClasses.typeClass->methods, ".__str__", _class_to_str);
krk_defineNative(&vm.baseClasses.typeClass->methods, ".__repr__", _class_to_str);
krk_finalizeClass(vm.baseClasses.typeClass);
vm.baseClasses.typeClass->docstring = S("Obtain the object representation of the class of an object.");
ADD_BASE_CLASS(vm.baseClasses.intClass, "int", vm.objectClass);
krk_defineNative(&vm.baseClasses.intClass->methods, ".__init__", _int_init);
krk_defineNative(&vm.baseClasses.intClass->methods, ".__int__", _noop);
krk_defineNative(&vm.baseClasses.intClass->methods, ".__float__", _int_to_floating);
krk_defineNative(&vm.baseClasses.intClass->methods, ".__chr__", _int_to_char);
krk_defineNative(&vm.baseClasses.intClass->methods, ".__str__", _int_to_str);
krk_defineNative(&vm.baseClasses.intClass->methods, ".__repr__", _int_to_str);
krk_finalizeClass(vm.baseClasses.intClass);
vm.baseClasses.intClass->docstring = S("Convert a number or string type to an integer representation.");
ADD_BASE_CLASS(vm.baseClasses.floatClass, "float", vm.objectClass);
krk_defineNative(&vm.baseClasses.floatClass->methods, ".__init__", _float_init);
krk_defineNative(&vm.baseClasses.floatClass->methods, ".__int__", _floating_to_int);
krk_defineNative(&vm.baseClasses.floatClass->methods, ".__float__", _noop);
krk_defineNative(&vm.baseClasses.floatClass->methods, ".__str__", _float_to_str);
krk_defineNative(&vm.baseClasses.floatClass->methods, ".__repr__", _float_to_str);
krk_finalizeClass(vm.baseClasses.floatClass);
vm.baseClasses.floatClass->docstring = S("Convert a number or string type to a float representation.");
ADD_BASE_CLASS(vm.baseClasses.boolClass, "bool", vm.objectClass);
krk_defineNative(&vm.baseClasses.boolClass->methods, ".__init__", _bool_init);
krk_defineNative(&vm.baseClasses.boolClass->methods, ".__str__", _bool_to_str);
krk_defineNative(&vm.baseClasses.boolClass->methods, ".__repr__", _bool_to_str);
krk_finalizeClass(vm.baseClasses.boolClass);
vm.baseClasses.floatClass->docstring = S("Returns False if the argument is 'falsey', otherwise True.");
/* TODO: Don't attach */
ADD_BASE_CLASS(vm.baseClasses.noneTypeClass, "NoneType", vm.objectClass);
krk_defineNative(&vm.baseClasses.noneTypeClass->methods, ".__str__", _none_to_str);
krk_defineNative(&vm.baseClasses.noneTypeClass->methods, ".__repr__", _none_to_str);
krk_finalizeClass(vm.baseClasses.noneTypeClass);
_createAndBind_strClass();
_createAndBind_listClass();
_createAndBind_tupleClass();
_createAndBind_bytesClass();
_createAndBind_dictClass();
_createAndBind_functionClass();
_createAndBind_rangeClass();
_createAndBind_setClass();
/* Build global builtin functions. */
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", krk_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.");
BUILTIN_FUNCTION("len", _len, "Return the length of a given sequence object.");
BUILTIN_FUNCTION("repr", _repr, "Produce a string representation of the given object.");
BUILTIN_FUNCTION("print", _print, "Print values to the standard output descriptor.");
BUILTIN_FUNCTION("ord", _ord, "Obtain the ordinal integer value of a codepoint or byte.");
BUILTIN_FUNCTION("chr", _chr, "Convert an integer codepoint to its string representation.");
BUILTIN_FUNCTION("hex", _hex, "Convert an integer value to a hexadecimal string.");
BUILTIN_FUNCTION("any", _any, "Returns True if at least one element in the given iterable is truthy, False otherwise.");
BUILTIN_FUNCTION("all", _all, "Returns True if every element in the given iterable is truthy, False otherwise.");
/* This module is slowly being deprecated. */
KrkValue builtinsModule = krk_interpret(krk_builtinsSrc,1,"__builtins__","__builtins__");
if (!IS_OBJECT(builtinsModule)) {
/* ... hence, this is a warning and not a complete failure. */
fprintf(stderr, "VM startup failure: Failed to load __builtins__ module.\n");
}
/* The VM is now ready to start executing code. */
krk_resetStack();
KRK_RESUME_GC();
@ -2453,7 +1907,7 @@ static KrkValue run() {
case OP_NONE: krk_push(NONE_VAL()); break;
case OP_TRUE: krk_push(BOOLEAN_VAL(1)); break;
case OP_FALSE: krk_push(BOOLEAN_VAL(0)); break;
case OP_NOT: krk_push(BOOLEAN_VAL(isFalsey(krk_pop()))); break;
case OP_NOT: krk_push(BOOLEAN_VAL(krk_isFalsey(krk_pop()))); break;
case OP_POP: krk_pop(); break;
case OP_DEFINE_GLOBAL_LONG:
case OP_DEFINE_GLOBAL: {
@ -2516,12 +1970,12 @@ static KrkValue run() {
}
case OP_JUMP_IF_FALSE: {
uint16_t offset = readBytes(frame, 2);
if (isFalsey(krk_peek(0))) frame->ip += offset;
if (krk_isFalsey(krk_peek(0))) frame->ip += offset;
break;
}
case OP_JUMP_IF_TRUE: {
uint16_t offset = readBytes(frame, 2);
if (!isFalsey(krk_peek(0))) frame->ip += offset;
if (!krk_isFalsey(krk_peek(0))) frame->ip += offset;
break;
}
case OP_JUMP: {

View File

@ -181,8 +181,9 @@ extern KrkValue krk_dict_nth_key_fast(size_t capacity, KrkTableEntry * entries,
extern KrkValue krk_tuple_of(int argc, KrkValue argv[]);
extern KrkValue _noop(int argc, KrkValue argv[]);
extern int krk_isFalsey(KrkValue value);
extern void _createAndBind_numericClasses(void);
extern void _createAndBind_strClass(void);
extern void _createAndBind_listClass(void);
extern void _createAndBind_tupleClass(void);
@ -191,3 +192,7 @@ extern void _createAndBind_dictClass(void);
extern void _createAndBind_functionClass(void);
extern void _createAndBind_rangeClass(void);
extern void _createAndBind_setClass(void);
extern void _createAndBind_builtins(void);
extern void _createAndBind_type(void);
extern int krk_doRecursiveModuleLoad(KrkString * name);