Make modules work like in Python. TODO: module class for better repring

This commit is contained in:
K. Lange 2021-01-07 09:38:07 +09:00
parent e19e45c59b
commit 902d2222b5
12 changed files with 95 additions and 66 deletions

View File

@ -404,9 +404,7 @@ Modules allow scripts to call other scripts.
```py
# modules/demomodule.krk
let module = object()
module.foo = "bar"
return module
foo = "bar"
```
```py
@ -416,25 +414,7 @@ print(demomodule.foo)
# → bar
```
When modules are imported, they run in a _function local_ context and variables they declare do not live in the global namespace.
To put variables into the global namespace, use the `export` keyword:
```py
# modules/demomodule.krk
let module = object()
foo = "bar"
export foo
return module
```
```py
# demo.krk
import demomodule
print(foo)
# → bar
```
Modules are run once and then cached, so if they preform actions like printing or complex computation this will happen once when first imported. The globals table from the module is the fields table of an object. Further imports of the same module will return the same object.
### Loops

View File

@ -74,8 +74,9 @@ const char _builtins_src[] =
"\n"
"let help = Helper()\n"
"\n"
"export list,dict,help\n"
"\n"
"__builtins__.list = list\n"
"__builtins__.dict = dict\n"
"__builtins__.help = help\n"
"__builtins__.module_paths = ['./','./modules/','/home/klange/Projects/kuroko/modules/','/usr/share/kuroko/']\n"
"\n"
"return object()\n"

View File

@ -73,8 +73,9 @@ class Helper():
let help = Helper()
export list,dict,help
__builtins__.list = list
__builtins__.dict = dict
__builtins__.help = help
__builtins__.module_paths = ['./','./modules/','/home/klange/Projects/kuroko/modules/','/usr/share/kuroko/']
return object()

View File

@ -138,6 +138,7 @@ static void initCompiler(Compiler * compiler, FunctionType type) {
compiler->type = type;
compiler->scopeDepth = 0;
compiler->function = krk_newFunction();
compiler->function->globalsContext = vm.globals;
compiler->localCount = 0;
compiler->localsSpace = 8;
compiler->locals = GROW_ARRAY(Local,NULL,0,8);

View File

@ -65,8 +65,9 @@ int main(int argc, char * argv[]) {
KrkValue result = INTEGER_VAL(0);
if (optind == argc) {
krk_defineNative(&vm.globals, "exit", exitFunc);
krk_defineNative(&vm.globals, "paste", paste);
krk_defineNative(&vm.builtins->fields, "exit", exitFunc);
krk_defineNative(&vm.builtins->fields, "paste", paste);
krk_startModule("<module>");
/* Set ^D to send EOF */
rline_exit_string="";
@ -203,7 +204,7 @@ int main(int argc, char * argv[]) {
* collect the result of the last one and use it as the
* exit code if it's an integer. */
for (int i = optind; i < argc; ++i) {
KrkValue out = krk_runfile(argv[i],0,"<module>",argv[i]);
KrkValue out = krk_runfile(argv[i],1,"<module>",argv[i]);
if (i + 1 == argc) result = out;
}
}

View File

@ -224,7 +224,8 @@ static void markRoots() {
for (KrkUpvalue * upvalue = vm.openUpvalues; upvalue; upvalue = upvalue->next) {
krk_markObject((KrkObj*)upvalue);
}
krk_markTable(&vm.globals);
krk_markObject((KrkObj*)vm.builtins);
if (vm.module) krk_markObject((KrkObj*)vm.module);
krk_markTable(&vm.modules);
krk_markCompilerRoots();
for (int i = 0; i < METHOD__MAX; ++i) {

View File

@ -1,2 +1,9 @@
# Just in case there are no modules sources available...
return object()
let __doc__ = "I am a module."
def foo():
print("Hello, world:", __doc__)
let x = 5
let y = 7
let theMeaningOfLifeTheUniverseAndEverything = 42

View File

@ -72,6 +72,7 @@ KrkFunction * krk_newFunction() {
function->collectsKeywords = 0;
function->localNameCount = 0;
function->localNames = NULL;
function->globalsContext = NULL;
krk_initValueArray(&function->requiredArgNames);
krk_initValueArray(&function->keywordArgNames);
krk_initChunk(&function->chunk);

View File

@ -81,6 +81,7 @@ typedef struct {
KrkLocalEntry * localNames;
unsigned char collectsArguments:1;
unsigned char collectsKeywords:1;
KrkTable * globalsContext;
} KrkFunction;
typedef struct {

View File

@ -1,2 +1,2 @@
772 lines
752 lines
['', 'Kuroko is a bytecode-interpreted, dynamic, strongly-typed language with syntax similar to Python.', '', 'The bytecode VM / compiler is substantially based on Robert Nystrom\'s [_Crafting Interpreters_](https://craftinginterpreters.com/).']

99
vm.c
View File

@ -485,7 +485,7 @@ KrkValue krk_runNext(void) {
*/
static KrkValue krk_list_of(int argc, KrkValue argv[]) {
KrkValue Class;
krk_tableGet(&vm.globals,OBJECT_VAL(S("list")), &Class);
krk_tableGet(&vm.builtins->fields,OBJECT_VAL(S("list")), &Class);
KrkInstance * outList = krk_newInstance(AS_CLASS(Class));
krk_push(OBJECT_VAL(outList));
KrkFunction * listContents = krk_newFunction(NULL);
@ -512,7 +512,7 @@ KrkValue krk_dict_of(int argc, KrkValue argv[]) {
return NONE_VAL();
}
KrkValue Class;
krk_tableGet(&vm.globals,OBJECT_VAL(S("dict")), &Class);
krk_tableGet(&vm.builtins->fields,OBJECT_VAL(S("dict")), &Class);
KrkInstance * outDict = krk_newInstance(AS_CLASS(Class));
krk_push(OBJECT_VAL(outDict));
KrkClass * dictContents = krk_newClass(NULL);
@ -770,7 +770,7 @@ static KrkValue krk_globals(int argc, KrkValue argv[]) {
KrkValue _dict_internal;
krk_tableGet(&AS_INSTANCE(dict)->fields, vm.specialMethodNames[METHOD_DICT_INT], &_dict_internal);
/* Copy the globals table into it */
krk_tableAddAll(&vm.globals, &AS_CLASS(_dict_internal)->methods);
krk_tableAddAll(vm.globals, &AS_CLASS(_dict_internal)->methods);
krk_pop();
return dict;
@ -1057,6 +1057,7 @@ _finishArg:
frame->ip = closure->function->chunk.code;
frame->slots = (vm.stackTop - argCount) - vm.stack;
frame->outSlots = (vm.stackTop - argCount - extra) - vm.stack;
vm.globals = closure->function->globalsContext;
return 1;
}
@ -1296,11 +1297,16 @@ static KrkValue _string_init(int argc, KrkValue argv[]) {
#define ADD_EXCEPTION_CLASS(obj, name, baseClass) do { \
obj = krk_newClass(S(name)); \
krk_attachNamedObject(&vm.globals, name, (KrkObj*)obj); \
krk_attachNamedObject(&vm.builtins->fields, name, (KrkObj*)obj); \
obj->base = baseClass; \
krk_tableAddAll(&baseClass->methods, &obj->methods); \
} while (0)
#define BUILTIN_FUNCTION(name, func) do { \
krk_defineNative(&vm.builtins->fields, name, func); \
} while (0)
/** native method that returns its first arg; useful for int(INT), etc. */
static KrkValue _noop(int argc, KrkValue argv[]) {
return argv[0];
@ -2430,6 +2436,10 @@ void krk_initVM(int flags) {
vm.flags = flags;
KRK_PAUSE_GC();
/* No active module or globals table */
vm.module = NULL;
vm.globals = NULL;
krk_resetStack();
vm.objects = NULL;
vm.bytesAllocated = 0;
@ -2437,7 +2447,6 @@ void krk_initVM(int flags) {
vm.grayCount = 0;
vm.grayCapacity = 0;
vm.grayStack = NULL;
krk_initTable(&vm.globals);
krk_initTable(&vm.strings);
memset(vm.specialMethodNames,0,sizeof(vm.specialMethodNames));
vm.watchdog = 0;
@ -2468,7 +2477,7 @@ void krk_initVM(int flags) {
/* Create built-in class `object` */
vm.objectClass = krk_newClass(S("object"));
krk_attachNamedObject(&vm.globals, "object", (KrkObj*)vm.objectClass);
krk_defineNative(&vm.objectClass->methods, ":__class__", krk_typeOf);
krk_defineNative(&vm.objectClass->methods, ".__dir__", krk_dirObject);
krk_defineNative(&vm.objectClass->methods, ".__str__", _strBase);
@ -2477,7 +2486,8 @@ void krk_initVM(int flags) {
/* Build a __builtins__ namespace for some extra functions. */
vm.builtins = krk_newInstance(vm.objectClass);
krk_attachNamedObject(&vm.globals, "__builtins__", (KrkObj*)vm.builtins);
krk_attachNamedObject(&vm.builtins->fields, "object", (KrkObj*)vm.objectClass);
krk_attachNamedObject(&vm.builtins->fields, "__name__", (KrkObj*)S("__builtins__"));
/* Add exception classes */
ADD_EXCEPTION_CLASS(vm.exceptions.baseException, "Exception", vm.objectClass);
@ -2495,7 +2505,6 @@ void krk_initVM(int flags) {
/* Build classes for basic types */
ADD_BASE_CLASS(vm.baseClasses.typeClass, "type", vm.objectClass);
krk_attachNamedObject(&vm.globals, "type", (KrkObj*)vm.baseClasses.typeClass);
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);
@ -2505,7 +2514,6 @@ void krk_initVM(int flags) {
krk_defineNative(&vm.baseClasses.typeClass->methods, ".__repr__", _class_to_str);
krk_finalizeClass(vm.baseClasses.typeClass);
ADD_BASE_CLASS(vm.baseClasses.intClass, "int", vm.objectClass);
krk_attachNamedObject(&vm.globals, "int", (KrkObj*)vm.baseClasses.intClass);
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);
@ -2514,7 +2522,6 @@ void krk_initVM(int flags) {
krk_defineNative(&vm.baseClasses.intClass->methods, ".__repr__", _int_to_str);
krk_finalizeClass(vm.baseClasses.intClass);
ADD_BASE_CLASS(vm.baseClasses.floatClass, "float", vm.objectClass);
krk_attachNamedObject(&vm.globals, "float", (KrkObj*)vm.baseClasses.floatClass);
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);
@ -2522,17 +2529,16 @@ void krk_initVM(int flags) {
krk_defineNative(&vm.baseClasses.floatClass->methods, ".__repr__", _float_to_str);
krk_finalizeClass(vm.baseClasses.floatClass);
ADD_BASE_CLASS(vm.baseClasses.boolClass, "bool", vm.objectClass);
krk_attachNamedObject(&vm.globals, "bool", (KrkObj*)vm.baseClasses.boolClass);
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);
/* 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);
ADD_BASE_CLASS(vm.baseClasses.strClass, "str", vm.objectClass);
krk_attachNamedObject(&vm.globals, "str", (KrkObj*)vm.baseClasses.strClass);
krk_defineNative(&vm.baseClasses.strClass->methods, ".__init__", _string_init);
krk_defineNative(&vm.baseClasses.strClass->methods, ".__str__", _noop);
krk_defineNative(&vm.baseClasses.strClass->methods, ".__repr__", _repr_str);
@ -2549,6 +2555,7 @@ void krk_initVM(int flags) {
krk_defineNative(&vm.baseClasses.strClass->methods, ".join", _string_join);
krk_defineNative(&vm.baseClasses.strClass->methods, ".split", _string_split);
krk_finalizeClass(vm.baseClasses.strClass);
/* TODO: Don't attach */
ADD_BASE_CLASS(vm.baseClasses.functionClass, "function", vm.objectClass);
krk_defineNative(&vm.baseClasses.functionClass->methods, ".__str__", _closure_str);
krk_defineNative(&vm.baseClasses.functionClass->methods, ".__repr__", _closure_str);
@ -2557,6 +2564,7 @@ void krk_initVM(int flags) {
krk_defineNative(&vm.baseClasses.functionClass->methods, ":__file__", _closure_get_file);
krk_defineNative(&vm.baseClasses.functionClass->methods, ":__args__", _closure_get_argnames);
krk_finalizeClass(vm.baseClasses.functionClass);
/* TODO: Don't attach */
ADD_BASE_CLASS(vm.baseClasses.methodClass, "method", vm.objectClass);
krk_defineNative(&vm.baseClasses.methodClass->methods, ".__str__", _bound_str);
krk_defineNative(&vm.baseClasses.methodClass->methods, ".__repr__", _bound_str);
@ -2566,7 +2574,6 @@ void krk_initVM(int flags) {
krk_defineNative(&vm.baseClasses.methodClass->methods, ":__args__", _bound_get_argnames);
krk_finalizeClass(vm.baseClasses.methodClass);
ADD_BASE_CLASS(vm.baseClasses.tupleClass, "tuple", vm.objectClass);
krk_attachNamedObject(&vm.globals, "tuple", (KrkObj*)vm.baseClasses.tupleClass);
krk_defineNative(&vm.baseClasses.tupleClass->methods, ".__init__", _tuple_init);
krk_defineNative(&vm.baseClasses.tupleClass->methods, ".__str__", _tuple_repr);
krk_defineNative(&vm.baseClasses.tupleClass->methods, ".__repr__", _tuple_repr);
@ -2577,40 +2584,43 @@ void krk_initVM(int flags) {
krk_finalizeClass(vm.baseClasses.tupleClass);
/* Build global builtin functions. */
krk_defineNative(&vm.globals, "listOf", krk_list_of); /* Equivalent to list() */
krk_defineNative(&vm.globals, "dictOf", krk_dict_of); /* Equivalent to dict() */
krk_defineNative(&vm.globals, "isinstance", krk_isinstance);
krk_defineNative(&vm.globals, "globals", krk_globals);
krk_defineNative(&vm.globals, "dir", krk_dirObject);
krk_defineNative(&vm.globals, "len", _len);
krk_defineNative(&vm.globals, "repr", _repr);
krk_defineNative(&vm.globals, "print", _print);
BUILTIN_FUNCTION("listOf", krk_list_of); /* Equivalent to list() */
BUILTIN_FUNCTION("dictOf", krk_dict_of); /* Equivalent to dict() */
BUILTIN_FUNCTION("isinstance", krk_isinstance);
BUILTIN_FUNCTION("globals", krk_globals);
BUILTIN_FUNCTION("dir", krk_dirObject);
BUILTIN_FUNCTION("len", _len);
BUILTIN_FUNCTION("repr", _repr);
BUILTIN_FUNCTION("print", _print);
/* __builtins__.set_tracing is namespaced */
krk_defineNative(&vm.builtins->fields, "set_tracing", krk_set_tracing);
/* TODO: Don't attach */
ADD_BASE_CLASS(vm.baseClasses.listiteratorClass, "listiterator", vm.objectClass);
krk_defineNative(&vm.baseClasses.listiteratorClass->methods, ".__init__", _listiter_init);
krk_defineNative(&vm.baseClasses.listiteratorClass->methods, ".__call__", _listiter_call);
krk_finalizeClass(vm.baseClasses.listiteratorClass);
/* TODO: Don't attach */
ADD_BASE_CLASS(vm.baseClasses.striteratorClass, "striterator", vm.objectClass);
krk_defineNative(&vm.baseClasses.striteratorClass->methods, ".__init__", _striter_init);
krk_defineNative(&vm.baseClasses.striteratorClass->methods, ".__call__", _striter_call);
krk_finalizeClass(vm.baseClasses.striteratorClass);
ADD_BASE_CLASS(vm.baseClasses.rangeClass, "range", vm.objectClass);
krk_attachNamedObject(&vm.globals, "range", (KrkObj*)vm.baseClasses.rangeClass);
krk_defineNative(&vm.baseClasses.rangeClass->methods, ".__init__", _range_init);
krk_defineNative(&vm.baseClasses.rangeClass->methods, ".__iter__", _range_iter);
krk_defineNative(&vm.baseClasses.rangeClass->methods, ".__repr__", _range_repr);
krk_finalizeClass(vm.baseClasses.rangeClass);
/* TODO: Don't attach */
ADD_BASE_CLASS(vm.baseClasses.rangeiteratorClass, "rangeiterator", vm.objectClass);
krk_defineNative(&vm.baseClasses.rangeiteratorClass->methods, ".__init__", _rangeiterator_init);
krk_defineNative(&vm.baseClasses.rangeiteratorClass->methods, ".__call__", _rangeiterator_call);
krk_finalizeClass(vm.baseClasses.rangeiteratorClass);
/* TODO: Don't attach */
ADD_BASE_CLASS(vm.baseClasses.tupleiteratorClass, "tupleiterator", vm.objectClass);
krk_defineNative(&vm.baseClasses.tupleiteratorClass->methods, ".__init__", _tuple_iter_init);
krk_defineNative(&vm.baseClasses.tupleiteratorClass->methods, ".__call__", _tuple_iter_call);
@ -2633,7 +2643,7 @@ void krk_initVM(int flags) {
/* Now we can attach the native initializers and getters/setters to
* the list and dict types by pulling them out of the global namespace,
* as they were exported by builtins.krk */
krk_tableGet(&vm.globals,OBJECT_VAL(S("list")),&val);
krk_tableGet(&vm.builtins->fields,OBJECT_VAL(S("list")),&val);
KrkClass * _class = AS_CLASS(val);
krk_defineNative(&_class->methods, ".__init__", _list_init);
krk_defineNative(&_class->methods, ".__get__", _list_get);
@ -2645,7 +2655,7 @@ void krk_initVM(int flags) {
krk_defineNative(&_class->methods, ".append", _list_append);
krk_finalizeClass(_class);
krk_tableGet(&vm.globals,OBJECT_VAL(S("dict")),&val);
krk_tableGet(&vm.builtins->fields,OBJECT_VAL(S("dict")),&val);
_class = AS_CLASS(val);
krk_defineNative(&_class->methods, ".__init__", _dict_init);
krk_defineNative(&_class->methods, ".__get__", _dict_get);
@ -2668,7 +2678,6 @@ void krk_initVM(int flags) {
* Reclaim resources used by the VM.
*/
void krk_freeVM() {
krk_freeTable(&vm.globals);
krk_freeTable(&vm.strings);
krk_freeTable(&vm.modules);
memset(vm.specialMethodNames,0,sizeof(vm.specialMethodNames));
@ -3092,6 +3101,7 @@ static KrkValue run() {
}
krk_push(result);
frame = &vm.frames[vm.frameCount - 1];
vm.globals = frame->closure->function->globalsContext;
break;
}
case OP_EQUAL: {
@ -3143,7 +3153,7 @@ static KrkValue run() {
case OP_DEFINE_GLOBAL_LONG:
case OP_DEFINE_GLOBAL: {
KrkString * name = READ_STRING(operandWidth);
krk_tableSet(&vm.globals, OBJECT_VAL(name), krk_peek(0));
krk_tableSet(vm.globals, OBJECT_VAL(name), krk_peek(0));
krk_pop();
break;
}
@ -3151,9 +3161,11 @@ static KrkValue run() {
case OP_GET_GLOBAL: {
KrkString * name = READ_STRING(operandWidth);
KrkValue value;
if (!krk_tableGet(&vm.globals, OBJECT_VAL(name), &value)) {
krk_runtimeError(vm.exceptions.nameError, "Undefined variable '%s'.", name->chars);
goto _finishException;
if (!krk_tableGet(vm.globals, OBJECT_VAL(name), &value)) {
if (!krk_tableGet(&vm.builtins->fields, OBJECT_VAL(name), &value)) {
krk_runtimeError(vm.exceptions.nameError, "Undefined variable '%s'.", name->chars);
goto _finishException;
}
}
krk_push(value);
break;
@ -3161,8 +3173,8 @@ static KrkValue run() {
case OP_SET_GLOBAL_LONG:
case OP_SET_GLOBAL: {
KrkString * name = READ_STRING(operandWidth);
if (krk_tableSet(&vm.globals, OBJECT_VAL(name), krk_peek(0))) {
krk_tableDelete(&vm.globals, OBJECT_VAL(name));
if (krk_tableSet(vm.globals, OBJECT_VAL(name), krk_peek(0))) {
krk_tableDelete(vm.globals, OBJECT_VAL(name));
/* TODO: This should probably just work as an assignment? */
krk_runtimeError(vm.exceptions.nameError, "Undefined variable '%s'.", name->chars);
goto _finishException;
@ -3437,8 +3449,20 @@ _finishException:
#undef READ_BYTE
}
KrkInstance * krk_startModule(const char * name) {
KrkInstance * module = krk_newInstance(vm.objectClass);
vm.module = (KrkObj*)module;
vm.globals = &module->fields;
krk_attachNamedObject(vm.globals, "__builtins__", (KrkObj*)vm.builtins);
krk_attachNamedObject(vm.globals, "__name__", (KrkObj*)krk_copyString(name,strlen(name)));
return module;
}
KrkValue krk_interpret(const char * src, int newScope, char * fromName, char * fromFile) {
KrkFunction * function = krk_compile(src, newScope, fromFile);
KrkObj * enclosing = vm.module;
if (newScope) krk_startModule(fromName);
KrkFunction * function = krk_compile(src, 0, fromFile);
if (!function) return NONE_VAL();
@ -3452,7 +3476,16 @@ KrkValue krk_interpret(const char * src, int newScope, char * fromName, char * f
krk_push(OBJECT_VAL(closure));
krk_callValue(OBJECT_VAL(closure), 0, 1);
return run();
KrkValue result = run();
if (newScope) {
KrkValue out = OBJECT_VAL(vm.module);
vm.module = enclosing;
vm.globals = vm.module ? &((KrkInstance*)vm.module)->fields : NULL;
return out;
} else {
return result;
}
}

4
vm.h
View File

@ -48,7 +48,8 @@ typedef struct {
size_t stackSize;
KrkValue * stack;
KrkValue * stackTop;
KrkTable globals;
KrkObj * module;
KrkTable * globals;
KrkTable strings;
KrkTable modules;
KrkUpvalue * openUpvalues;
@ -139,3 +140,4 @@ extern KrkValue krk_dict_of(int argc, KrkValue argv[]);
extern KrkValue krk_callSimple(KrkValue value, int argCount, int isMethod);
extern void krk_finalizeClass(KrkClass * _class);
extern void krk_dumpTraceback();
extern KrkInstance * krk_startModule(const char * name);