2021-01-03 11:59:50 +03:00
# include <limits.h>
2020-12-26 03:32:21 +03:00
# include <stdarg.h>
# include <string.h>
2020-12-27 03:33:28 +03:00
# include <unistd.h>
2020-12-31 09:48:39 +03:00
# include <dlfcn.h>
2021-01-02 01:47:00 +03:00
# include <errno.h>
2020-12-31 09:48:39 +03:00
# include <sys/stat.h>
2020-12-26 03:32:21 +03:00
# include "vm.h"
# include "debug.h"
# include "memory.h"
# include "compiler.h"
# include "object.h"
2020-12-26 08:33:34 +03:00
# include "table.h"
2020-12-26 03:32:21 +03:00
2020-12-28 14:38:26 +03:00
# define S(c) (krk_copyString(c,sizeof(c)-1))
2020-12-28 13:01:28 +03:00
2021-01-02 06:21:11 +03:00
/* This is macro'd to krk_vm for namespacing reasons. */
2020-12-26 03:32:21 +03:00
KrkVM vm ;
2020-12-27 16:40:35 +03:00
static KrkValue run ( ) ;
2021-01-01 06:04:58 +03:00
static KrkValue krk_isinstance ( int argc , KrkValue argv [ ] ) ;
2021-01-05 05:38:11 +03:00
static void addObjects ( ) ;
2020-12-27 16:40:35 +03:00
2021-01-02 06:21:11 +03:00
/* Embedded script for extensions to builtin-ins; see builtins.c/builtins.krk */
2020-12-29 12:04:02 +03:00
extern const char _builtins_src [ ] ;
2021-01-02 06:21:11 +03:00
/**
* Reset the stack pointers , frame , upvalue list ,
* clear the exception flag and current exception ;
* happens on startup ( twice ) and after an exception .
*/
2021-01-04 11:47:53 +03:00
void krk_resetStack ( ) {
2020-12-26 03:32:21 +03:00
vm . stackTop = vm . stack ;
2020-12-27 03:33:28 +03:00
vm . frameCount = 0 ;
2020-12-27 07:02:26 +03:00
vm . openUpvalues = NULL ;
2020-12-29 05:00:12 +03:00
vm . flags & = ~ KRK_HAS_EXCEPTION ;
vm . currentException = NONE_VAL ( ) ;
2020-12-26 03:32:21 +03:00
}
2021-01-03 02:31:28 +03:00
# ifdef ENABLE_TRACING
/**
* When tracing is enabled , we will present the elements on the stack with
* a safe printer ; the format of values printed by krk_printValueSafe will
* look different from those printed by printValue , but they guarantee that
* the VM will never be called to produce a string , which would result in
* a nasty infinite recursion if we did it while trying to trace the VM !
*/
static void dumpStack ( CallFrame * frame ) {
fprintf ( stderr , " | " ) ;
size_t i = 0 ;
for ( KrkValue * slot = vm . stack ; slot < vm . stackTop ; slot + + ) {
fprintf ( stderr , " [ " ) ;
if ( i = = frame - > slots ) fprintf ( stderr , " * " ) ;
krk_printValueSafe ( stderr , * slot ) ;
fprintf ( stderr , " ] " ) ;
i + + ;
}
if ( i = = frame - > slots ) {
fprintf ( stderr , " * " ) ;
}
fprintf ( stderr , " \n " ) ;
}
# endif
2021-01-02 06:21:11 +03:00
/**
* Display a traceback by working through call frames .
* Called when no exception handler was available and
* an exception was thrown . If there the exception value
* is not None , it will also be printed using safe methods .
*/
2021-01-05 17:23:16 +03:00
void krk_dumpTraceback ( ) {
2021-01-02 06:21:11 +03:00
fprintf ( stderr , " Traceback, most recent first, %d call frame%s: \n " , ( int ) vm . frameCount , vm . frameCount = = 1 ? " " : " s " ) ;
2020-12-28 05:11:50 +03:00
for ( size_t i = 0 ; i < = vm . frameCount - 1 ; i + + ) {
2020-12-27 03:33:28 +03:00
CallFrame * frame = & vm . frames [ i ] ;
2020-12-27 07:02:26 +03:00
KrkFunction * function = frame - > closure - > function ;
2020-12-27 03:33:28 +03:00
size_t instruction = frame - > ip - function - > chunk . code - 1 ;
2020-12-28 04:54:25 +03:00
fprintf ( stderr , " File \" %s \" , line %d, in %s \n " ,
( function - > chunk . filename ? function - > chunk . filename - > chars : " ? " ) ,
2021-01-02 07:42:07 +03:00
( int ) krk_lineNumber ( & function - > chunk , instruction ) ,
2020-12-28 04:54:25 +03:00
( function - > name ? function - > name - > chars : " (unnamed) " ) ) ;
2020-12-27 03:33:28 +03:00
}
2021-01-01 06:04:58 +03:00
2021-01-02 06:21:11 +03:00
if ( ! krk_valuesEqual ( vm . currentException , NONE_VAL ( ) ) ) {
if ( IS_STRING ( vm . currentException ) ) {
2021-01-01 06:04:58 +03:00
/* Make sure strings are printed without quotes */
2021-01-02 06:21:11 +03:00
fprintf ( stderr , " %s " , AS_CSTRING ( vm . currentException ) ) ;
} else if ( AS_BOOLEAN ( krk_isinstance ( 2 , ( KrkValue [ ] ) { vm . currentException , OBJECT_VAL ( vm . exceptions . baseException ) } ) ) ) {
/* ErrorClass: arg... */
fprintf ( stderr , " %s: " , AS_INSTANCE ( vm . currentException ) - > _class - > name - > chars ) ;
KrkValue exceptionArg ;
krk_tableGet ( & AS_INSTANCE ( vm . currentException ) - > fields , OBJECT_VAL ( S ( " arg " ) ) , & exceptionArg ) ;
if ( IS_STRING ( exceptionArg ) ) {
/* Make sure strings are printed without quotes */
fprintf ( stderr , " %s " , AS_CSTRING ( exceptionArg ) ) ;
} else {
krk_printValueSafe ( stderr , exceptionArg ) ;
}
2021-01-01 06:04:58 +03:00
} else {
2021-01-02 06:21:11 +03:00
/* Whatever, just print it. */
krk_printValueSafe ( stderr , vm . currentException ) ;
2021-01-01 06:04:58 +03:00
}
2021-01-02 06:21:11 +03:00
fprintf ( stderr , " \n " ) ;
}
2020-12-29 05:00:12 +03:00
}
2020-12-27 03:33:28 +03:00
2021-01-02 06:21:11 +03:00
/**
* Raise an exception . Creates an exception object of the requested type
* and formats a message string to attach to it . Exception classes are
* found in vm . exceptions and are initialized on startup .
*/
2021-01-01 06:04:58 +03:00
void krk_runtimeError ( KrkClass * type , const char * fmt , . . . ) {
2020-12-29 05:00:12 +03:00
char buf [ 1024 ] = { 0 } ;
va_list args ;
va_start ( args , fmt ) ;
size_t len = vsnprintf ( buf , 1024 , fmt , args ) ;
va_end ( args ) ;
vm . flags | = KRK_HAS_EXCEPTION ;
2021-01-01 06:04:58 +03:00
/* Try to allocate an instance of __builtins__. */
KrkInstance * exceptionObject = krk_newInstance ( type ) ;
krk_push ( OBJECT_VAL ( exceptionObject ) ) ;
KrkString * strArg = S ( " arg " ) ;
krk_push ( OBJECT_VAL ( strArg ) ) ;
KrkString * strVal = krk_copyString ( buf , len ) ;
krk_push ( OBJECT_VAL ( strVal ) ) ;
krk_tableSet ( & exceptionObject - > fields , OBJECT_VAL ( strArg ) , OBJECT_VAL ( strVal ) ) ;
krk_pop ( ) ;
krk_pop ( ) ;
krk_pop ( ) ;
vm . currentException = OBJECT_VAL ( exceptionObject ) ;
2020-12-26 03:32:21 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* Push a value onto the stack , and grow the stack if necessary .
* Note that growing the stack can involve the stack _moving_ , so
* do not rely on the memory offset of a stack value if you expect
* the stack to grow - eg . if you are calling into managed code
* to do anything , or if you are pushing anything .
*/
2021-01-01 10:01:58 +03:00
inline void krk_push ( KrkValue value ) {
2020-12-26 03:32:21 +03:00
if ( ( size_t ) ( vm . stackTop - vm . stack ) + 1 > vm . stackSize ) {
size_t old = vm . stackSize ;
size_t old_offset = vm . stackTop - vm . stack ;
vm . stackSize = GROW_CAPACITY ( old ) ;
vm . stack = GROW_ARRAY ( KrkValue , vm . stack , old , vm . stackSize ) ;
vm . stackTop = vm . stack + old_offset ;
}
* vm . stackTop = value ;
vm . stackTop + + ;
}
2021-01-02 06:21:11 +03:00
/**
* Pop the top of the stack . We never reclaim space used by the stack ,
* so anything that is popped can be safely pushed back on without
* the stack moving , and you an also generally rely on a popped item
* still being where it was if you don ' t allocate anything in between ;
* the repl relies on this it expects to be able to get the last
* pushed value and display it ( if it ' s not None ) .
*/
2020-12-26 03:32:21 +03:00
KrkValue krk_pop ( ) {
vm . stackTop - - ;
if ( vm . stackTop < vm . stack ) {
2020-12-31 08:46:02 +03:00
fprintf ( stderr , " Fatal error: stack underflow detected in VM, issuing breakpoint. \n " ) ;
__asm__ ( " int $3 " ) ;
2020-12-27 10:45:34 +03:00
return NONE_VAL ( ) ;
2020-12-26 03:32:21 +03:00
}
return * vm . stackTop ;
}
2021-01-02 06:21:11 +03:00
/* Read a value `distance` units from the top of the stack without poping it. */
inline KrkValue krk_peek ( int distance ) {
2020-12-26 03:32:21 +03:00
return vm . stackTop [ - 1 - distance ] ;
}
2021-01-02 06:21:11 +03:00
/* Exchange the value `distance` units down from the top of the stack with
* the value at the top of the stack . */
2021-01-01 04:42:16 +03:00
void krk_swap ( int distance ) {
KrkValue top = vm . stackTop [ - 1 ] ;
vm . stackTop [ - 1 ] = vm . stackTop [ - 1 - distance ] ;
vm . stackTop [ - 1 - distance ] = top ;
}
2021-01-02 06:21:11 +03:00
/**
* Bind a native function to the given table ( eg . vm . globals , or _class - > methods )
* GC safe : pushes allocated values .
*/
2020-12-28 13:01:28 +03:00
void krk_defineNative ( KrkTable * table , const char * name , NativeFn function ) {
2021-01-01 10:01:58 +03:00
int functionType = 0 ;
2020-12-30 06:28:18 +03:00
if ( * name = = ' . ' ) {
name + + ;
2021-01-01 10:01:58 +03:00
functionType = 1 ;
2020-12-30 06:28:18 +03:00
}
2021-01-01 10:01:58 +03:00
if ( * name = = ' : ' ) {
name + + ;
functionType = 2 ;
}
KrkNative * func = krk_newNative ( function , name , functionType ) ;
2020-12-30 06:28:18 +03:00
krk_push ( OBJECT_VAL ( func ) ) ;
2020-12-28 05:11:50 +03:00
krk_push ( OBJECT_VAL ( krk_copyString ( name , ( int ) strlen ( name ) ) ) ) ;
2020-12-31 08:46:02 +03:00
krk_tableSet ( table , krk_peek ( 0 ) , krk_peek ( 1 ) ) ;
2020-12-27 03:33:28 +03:00
krk_pop ( ) ;
krk_pop ( ) ;
}
2021-01-05 03:30:23 +03:00
/**
* For a class built by native code , call this after attaching methods to
* finalize the attachment of special methods for quicker accessn .
*
* For a class built by managed code , called by OP_FINALIZE
*/
void krk_finalizeClass ( KrkClass * _class ) {
KrkValue tmp ;
struct TypeMap {
KrkObj * * method ;
KrkSpecialMethods index ;
} ;
struct TypeMap specials [ ] = {
{ & _class - > _getter , METHOD_GET } ,
{ & _class - > _setter , METHOD_SET } ,
{ & _class - > _slicer , METHOD_GETSLICE } ,
{ & _class - > _reprer , METHOD_REPR } ,
{ & _class - > _tostr , METHOD_STR } ,
{ & _class - > _call , METHOD_CALL } ,
{ & _class - > _init , METHOD_INIT } ,
2021-01-05 11:41:32 +03:00
{ & _class - > _eq , METHOD_EQ } ,
2021-01-05 03:30:23 +03:00
{ NULL , 0 } ,
} ;
for ( struct TypeMap * entry = specials ; entry - > method ; + + entry ) {
if ( krk_tableGet ( & _class - > methods , vm . specialMethodNames [ entry - > index ] , & tmp ) ) {
* entry - > method = AS_OBJECT ( tmp ) ;
}
}
}
2021-01-02 06:21:11 +03:00
/***************
* Collections *
* * * * * * * * * * * * * * * */
/**
* dict . __init__ ( )
*/
2021-01-01 14:52:18 +03:00
static KrkValue _dict_init ( int argc , KrkValue argv [ ] ) {
KrkClass * dict = krk_newClass ( NULL ) ;
krk_push ( OBJECT_VAL ( dict ) ) ;
krk_tableSet ( & AS_INSTANCE ( argv [ 0 ] ) - > fields , vm . specialMethodNames [ METHOD_DICT_INT ] , OBJECT_VAL ( dict ) ) ;
2021-01-05 03:30:23 +03:00
AS_INSTANCE ( argv [ 0 ] ) - > _internal = ( KrkObj * ) dict ;
2021-01-02 13:41:51 +03:00
krk_tableSet ( & AS_INSTANCE ( argv [ 0 ] ) - > fields , vm . specialMethodNames [ METHOD_INREPR ] , INTEGER_VAL ( 0 ) ) ;
2021-01-01 14:52:18 +03:00
krk_pop ( ) ;
return argv [ 0 ] ;
2020-12-27 16:40:35 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* dict . __get__ ( key )
*/
2021-01-01 14:52:18 +03:00
static KrkValue _dict_get ( int argc , KrkValue argv [ ] ) {
if ( argc < 2 ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . argumentError , " wrong number of arguments " ) ;
2020-12-29 05:00:12 +03:00
return NONE_VAL ( ) ;
}
2021-01-01 14:52:18 +03:00
KrkValue _dict_internal ;
krk_tableGet ( & AS_INSTANCE ( argv [ 0 ] ) - > fields , vm . specialMethodNames [ METHOD_DICT_INT ] , & _dict_internal ) ;
KrkValue out ;
2021-01-02 13:41:51 +03:00
if ( ! krk_tableGet ( AS_DICT ( _dict_internal ) , argv [ 1 ] , & out ) ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . keyError , " key error " ) ;
2020-12-29 05:00:12 +03:00
}
2020-12-27 16:40:35 +03:00
return out ;
}
2021-01-02 06:21:11 +03:00
/**
* dict . __set__ ( key , value )
*/
2021-01-01 14:52:18 +03:00
static KrkValue _dict_set ( int argc , KrkValue argv [ ] ) {
if ( argc < 3 ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . argumentError , " wrong number of arguments " ) ;
2020-12-29 05:00:12 +03:00
return NONE_VAL ( ) ;
}
2021-01-01 14:52:18 +03:00
KrkValue _dict_internal ;
krk_tableGet ( & AS_INSTANCE ( argv [ 0 ] ) - > fields , vm . specialMethodNames [ METHOD_DICT_INT ] , & _dict_internal ) ;
2021-01-02 13:41:51 +03:00
krk_tableSet ( AS_DICT ( _dict_internal ) , argv [ 1 ] , argv [ 2 ] ) ;
2021-01-01 14:52:18 +03:00
return NONE_VAL ( ) ;
2020-12-27 16:40:35 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* dict . __len__ ( )
*/
2021-01-01 14:52:18 +03:00
static KrkValue _dict_len ( int argc , KrkValue argv [ ] ) {
if ( argc < 1 ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . argumentError , " wrong number of arguments " ) ;
2020-12-29 05:00:12 +03:00
return NONE_VAL ( ) ;
}
2021-01-01 14:52:18 +03:00
KrkValue _dict_internal ;
krk_tableGet ( & AS_INSTANCE ( argv [ 0 ] ) - > fields , vm . specialMethodNames [ METHOD_DICT_INT ] , & _dict_internal ) ;
2021-01-02 13:41:51 +03:00
return INTEGER_VAL ( AS_DICT ( _dict_internal ) - > count ) ;
}
/**
* dict . __contains__ ( )
*/
static KrkValue _dict_contains ( int argc , KrkValue argv [ ] ) {
KrkValue _unused ;
KrkValue _dict_internal ;
krk_tableGet ( & AS_INSTANCE ( argv [ 0 ] ) - > fields , vm . specialMethodNames [ METHOD_DICT_INT ] , & _dict_internal ) ;
return BOOLEAN_VAL ( krk_tableGet ( AS_DICT ( _dict_internal ) , argv [ 1 ] , & _unused ) ) ;
2020-12-28 13:01:28 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* dict . capacity ( )
*/
2021-01-01 14:52:18 +03:00
static KrkValue _dict_capacity ( int argc , KrkValue argv [ ] ) {
if ( argc < 1 ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . argumentError , " wrong number of arguments " ) ;
2020-12-29 05:00:12 +03:00
return NONE_VAL ( ) ;
}
2021-01-01 14:52:18 +03:00
KrkValue _dict_internal ;
krk_tableGet ( & AS_INSTANCE ( argv [ 0 ] ) - > fields , vm . specialMethodNames [ METHOD_DICT_INT ] , & _dict_internal ) ;
2021-01-02 13:41:51 +03:00
return INTEGER_VAL ( AS_DICT ( _dict_internal ) - > capacity ) ;
2020-12-28 13:01:28 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* dict . _key_at_index ( internalIndex )
*/
2021-01-01 14:52:18 +03:00
static KrkValue _dict_key_at_index ( int argc , KrkValue argv [ ] ) {
if ( argc < 2 ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . argumentError , " wrong number of arguments " ) ;
2020-12-29 05:00:12 +03:00
return NONE_VAL ( ) ;
}
if ( ! IS_INTEGER ( argv [ 1 ] ) ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . typeError , " expected integer index but got %s " , krk_typeName ( argv [ 1 ] ) ) ;
2020-12-29 05:00:12 +03:00
return NONE_VAL ( ) ;
}
2020-12-28 13:01:28 +03:00
int i = AS_INTEGER ( argv [ 1 ] ) ;
2021-01-01 14:52:18 +03:00
KrkValue _dict_internal ;
krk_tableGet ( & AS_INSTANCE ( argv [ 0 ] ) - > fields , vm . specialMethodNames [ METHOD_DICT_INT ] , & _dict_internal ) ;
2021-01-02 13:41:51 +03:00
if ( i < 0 | | i > ( int ) AS_DICT ( _dict_internal ) - > capacity ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . indexError , " hash table index is out of range: %d " , i ) ;
2020-12-29 05:00:12 +03:00
return NONE_VAL ( ) ;
}
2021-01-02 13:41:51 +03:00
KrkTableEntry entry = AS_DICT ( _dict_internal ) - > entries [ i ] ;
2020-12-28 13:01:28 +03:00
return entry . key ;
}
2021-01-02 06:21:11 +03:00
/**
* list . __init__ ( )
*/
2021-01-01 14:52:18 +03:00
static KrkValue _list_init ( int argc , KrkValue argv [ ] ) {
2020-12-28 05:11:50 +03:00
KrkFunction * list = krk_newFunction ( NULL ) ;
2021-01-01 14:52:18 +03:00
krk_push ( OBJECT_VAL ( list ) ) ;
krk_tableSet ( & AS_INSTANCE ( argv [ 0 ] ) - > fields , vm . specialMethodNames [ METHOD_LIST_INT ] , OBJECT_VAL ( list ) ) ;
2021-01-05 03:30:23 +03:00
AS_INSTANCE ( argv [ 0 ] ) - > _internal = ( KrkObj * ) list ;
2021-01-02 08:28:37 +03:00
krk_tableSet ( & AS_INSTANCE ( argv [ 0 ] ) - > fields , vm . specialMethodNames [ METHOD_INREPR ] , INTEGER_VAL ( 0 ) ) ;
2021-01-01 14:52:18 +03:00
krk_pop ( ) ;
return argv [ 0 ] ;
2020-12-27 16:40:35 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* list . __get__ ( index )
*/
2021-01-01 14:52:18 +03:00
static KrkValue _list_get ( int argc , KrkValue argv [ ] ) {
if ( argc < 2 | | ! IS_INTEGER ( argv [ 1 ] ) ) {
2021-01-04 11:29:06 +03:00
krk_runtimeError ( vm . exceptions . argumentError , " wrong number or type of arguments in get %d, (%s, %s) " , argc , krk_typeName ( argv [ 0 ] ) , krk_typeName ( argv [ 1 ] ) ) ;
2020-12-29 05:00:12 +03:00
return NONE_VAL ( ) ;
}
2021-01-05 03:30:23 +03:00
KrkValue _list_internal = OBJECT_VAL ( AS_INSTANCE ( argv [ 0 ] ) - > _internal ) ;
2020-12-27 16:40:35 +03:00
int index = AS_INTEGER ( argv [ 1 ] ) ;
2021-01-04 13:07:39 +03:00
if ( index < 0 ) index + = AS_LIST ( _list_internal ) - > count ;
2021-01-02 13:41:51 +03:00
if ( index < 0 | | index > = ( int ) AS_LIST ( _list_internal ) - > count ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . indexError , " index is out of range: %d " , index ) ;
2020-12-29 05:00:12 +03:00
return NONE_VAL ( ) ;
}
2021-01-02 13:41:51 +03:00
return AS_LIST ( _list_internal ) - > values [ index ] ;
2020-12-27 16:40:35 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* list . __set__ ( index , value )
*/
2021-01-01 14:52:18 +03:00
static KrkValue _list_set ( int argc , KrkValue argv [ ] ) {
if ( argc < 3 | | ! IS_INTEGER ( argv [ 1 ] ) ) {
2021-01-04 11:29:06 +03:00
krk_runtimeError ( vm . exceptions . argumentError , " wrong number or type of arguments in set %d, (%s, %s, %s) " , argc , krk_typeName ( argv [ 0 ] ) , krk_typeName ( argv [ 1 ] ) , krk_typeName ( argv [ 2 ] ) ) ;
2020-12-29 05:00:12 +03:00
return NONE_VAL ( ) ;
}
2021-01-05 03:30:23 +03:00
KrkValue _list_internal = OBJECT_VAL ( AS_INSTANCE ( argv [ 0 ] ) - > _internal ) ;
2020-12-27 16:40:35 +03:00
int index = AS_INTEGER ( argv [ 1 ] ) ;
2021-01-04 13:07:39 +03:00
if ( index < 0 ) index + = AS_LIST ( _list_internal ) - > count ;
2021-01-02 13:41:51 +03:00
if ( index < 0 | | index > = ( int ) AS_LIST ( _list_internal ) - > count ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . indexError , " index is out of range: %d " , index ) ;
2020-12-29 05:00:12 +03:00
return NONE_VAL ( ) ;
}
2021-01-02 13:41:51 +03:00
AS_LIST ( _list_internal ) - > values [ index ] = argv [ 2 ] ;
2021-01-01 14:52:18 +03:00
return NONE_VAL ( ) ;
2020-12-27 16:40:35 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* list . append ( value )
*/
2021-01-01 14:52:18 +03:00
static KrkValue _list_append ( int argc , KrkValue argv [ ] ) {
if ( argc < 2 ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . argumentError , " wrong number or type of arguments " ) ;
2020-12-29 05:00:12 +03:00
return NONE_VAL ( ) ;
}
2021-01-05 03:30:23 +03:00
KrkValue _list_internal = OBJECT_VAL ( AS_INSTANCE ( argv [ 0 ] ) - > _internal ) ;
2021-01-02 13:41:51 +03:00
krk_writeValueArray ( AS_LIST ( _list_internal ) , argv [ 1 ] ) ;
2021-01-01 14:52:18 +03:00
return NONE_VAL ( ) ;
2020-12-27 16:40:35 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* list . __len__
*/
2021-01-01 14:52:18 +03:00
static KrkValue _list_len ( int argc , KrkValue argv [ ] ) {
if ( argc < 1 ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . argumentError , " wrong number or type of arguments " ) ;
2020-12-29 05:00:12 +03:00
return NONE_VAL ( ) ;
}
2021-01-05 03:30:23 +03:00
KrkValue _list_internal = OBJECT_VAL ( AS_INSTANCE ( argv [ 0 ] ) - > _internal ) ;
2021-01-02 13:41:51 +03:00
return INTEGER_VAL ( AS_LIST ( _list_internal ) - > count ) ;
}
/**
* list . __contains__
*/
static KrkValue _list_contains ( int argc , KrkValue argv [ ] ) {
if ( argc < 2 ) {
krk_runtimeError ( vm . exceptions . argumentError , " wrong number or type of arguments " ) ;
return NONE_VAL ( ) ;
}
2021-01-05 03:30:23 +03:00
KrkValue _list_internal = OBJECT_VAL ( AS_INSTANCE ( argv [ 0 ] ) - > _internal ) ;
2021-01-02 13:41:51 +03:00
for ( size_t i = 0 ; i < AS_LIST ( _list_internal ) - > count ; + + i ) {
if ( krk_valuesEqual ( argv [ 1 ] , AS_LIST ( _list_internal ) - > values [ i ] ) ) return BOOLEAN_VAL ( 1 ) ;
}
return BOOLEAN_VAL ( 0 ) ;
2020-12-27 16:40:35 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* Run the VM until it returns from the current call frame ;
* used by native methods to call into managed methods .
* Returns the value returned by the RETURN instruction that
* exited the call frame . Should be nestable so a managed method
* can call a native method can call a managed can call a native
* and so on ( hopefully ) .
*/
2020-12-29 14:25:34 +03:00
KrkValue krk_runNext ( void ) {
2020-12-28 14:38:26 +03:00
size_t oldExit = vm . exitOnFrame ;
vm . exitOnFrame = vm . frameCount - 1 ;
2020-12-29 14:25:34 +03:00
KrkValue result = run ( ) ;
2020-12-28 14:38:26 +03:00
vm . exitOnFrame = oldExit ;
2020-12-29 14:25:34 +03:00
return result ;
2020-12-28 14:38:26 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* Exposed method called to produce lists from [ expr , . . . ] sequences in managed code .
* Presented in the global namespace as listOf ( . . . )
*/
2020-12-30 03:01:52 +03:00
static KrkValue krk_list_of ( int argc , KrkValue argv [ ] ) {
KrkValue Class ;
krk_tableGet ( & vm . globals , OBJECT_VAL ( S ( " list " ) ) , & Class ) ;
KrkInstance * outList = krk_newInstance ( AS_CLASS ( Class ) ) ;
krk_push ( OBJECT_VAL ( outList ) ) ;
KrkFunction * listContents = krk_newFunction ( NULL ) ;
krk_push ( OBJECT_VAL ( listContents ) ) ;
2021-01-01 14:52:18 +03:00
krk_tableSet ( & outList - > fields , vm . specialMethodNames [ METHOD_LIST_INT ] , OBJECT_VAL ( listContents ) ) ;
2021-01-05 03:30:23 +03:00
outList - > _internal = ( KrkObj * ) listContents ;
2021-01-02 08:28:37 +03:00
krk_tableSet ( & outList - > fields , vm . specialMethodNames [ METHOD_INREPR ] , INTEGER_VAL ( 0 ) ) ;
2020-12-30 03:01:52 +03:00
for ( int ind = 0 ; ind < argc ; + + ind ) {
krk_writeValueArray ( & listContents - > chunk . constants , argv [ ind ] ) ;
}
KrkValue out = OBJECT_VAL ( outList ) ;
2021-01-02 06:21:11 +03:00
krk_pop ( ) ; /* listContents */
krk_pop ( ) ; /* outList */
2020-12-30 03:01:52 +03:00
return out ;
}
2021-01-02 06:21:11 +03:00
/**
* Exposed method called to produce dictionaries from { expr : expr , . . . } sequences in managed code .
* Presented in the global namespace as dictOf ( . . . ) . Expects arguments as key , value , key , value . . .
*/
2021-01-03 03:49:19 +03:00
KrkValue krk_dict_of ( int argc , KrkValue argv [ ] ) {
2020-12-30 03:01:52 +03:00
if ( argc % 2 ! = 0 ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . argumentError , " Expected even number of arguments to dictOf " ) ;
2020-12-30 03:01:52 +03:00
return NONE_VAL ( ) ;
}
KrkValue Class ;
krk_tableGet ( & vm . globals , OBJECT_VAL ( S ( " dict " ) ) , & Class ) ;
KrkInstance * outDict = krk_newInstance ( AS_CLASS ( Class ) ) ;
krk_push ( OBJECT_VAL ( outDict ) ) ;
KrkClass * dictContents = krk_newClass ( NULL ) ;
krk_push ( OBJECT_VAL ( dictContents ) ) ;
2021-01-01 14:52:18 +03:00
krk_tableSet ( & outDict - > fields , vm . specialMethodNames [ METHOD_DICT_INT ] , OBJECT_VAL ( dictContents ) ) ;
2021-01-05 03:30:23 +03:00
outDict - > _internal = ( KrkObj * ) dictContents ;
2021-01-02 08:28:37 +03:00
krk_tableSet ( & outDict - > fields , vm . specialMethodNames [ METHOD_INREPR ] , INTEGER_VAL ( 0 ) ) ;
2020-12-30 03:01:52 +03:00
for ( int ind = 0 ; ind < argc ; ind + = 2 ) {
krk_tableSet ( & dictContents - > methods , argv [ ind ] , argv [ ind + 1 ] ) ;
}
KrkValue out = OBJECT_VAL ( outDict ) ;
2021-01-02 06:21:11 +03:00
krk_pop ( ) ; /* dictContents */
krk_pop ( ) ; /* outDict */
2020-12-30 03:01:52 +03:00
return out ;
}
2021-01-04 13:07:39 +03:00
/**
* list . __getslice__
*/
static KrkValue _list_slice ( int argc , KrkValue argv [ ] ) {
if ( argc < 3 ) { /* 3 because first is us */
krk_runtimeError ( vm . exceptions . argumentError , " slice: expected 2 arguments, got %d " , argc - 1 ) ;
return NONE_VAL ( ) ;
}
if ( ! IS_INSTANCE ( argv [ 0 ] ) | |
! ( IS_INTEGER ( argv [ 1 ] ) | | IS_NONE ( argv [ 1 ] ) ) | |
! ( IS_INTEGER ( argv [ 2 ] ) | | IS_NONE ( argv [ 2 ] ) ) ) {
krk_runtimeError ( vm . exceptions . typeError , " slice: expected two integer arguments " ) ;
return NONE_VAL ( ) ;
}
2021-01-05 03:30:23 +03:00
KrkValue _list_internal = OBJECT_VAL ( AS_INSTANCE ( argv [ 0 ] ) - > _internal ) ;
2021-01-04 13:07:39 +03:00
int start = IS_NONE ( argv [ 1 ] ) ? 0 : AS_INTEGER ( argv [ 1 ] ) ;
int end = IS_NONE ( argv [ 2 ] ) ? ( int ) AS_LIST ( _list_internal ) - > count : AS_INTEGER ( argv [ 2 ] ) ;
if ( start < 0 ) start = ( int ) AS_LIST ( _list_internal ) - > count + start ;
if ( start < 0 ) start = 0 ;
if ( end < 0 ) end = ( int ) AS_LIST ( _list_internal ) - > count + end ;
if ( start > ( int ) AS_LIST ( _list_internal ) - > count ) start = ( int ) AS_LIST ( _list_internal ) - > count ;
if ( end > ( int ) AS_LIST ( _list_internal ) - > count ) end = ( int ) AS_LIST ( _list_internal ) - > count ;
if ( end < start ) end = start ;
int len = end - start ;
return krk_list_of ( len , & AS_LIST ( _list_internal ) - > values [ start ] ) ;
}
2021-01-02 06:21:11 +03:00
/**
* __builtins__ . set_tracing ( mode )
2021-01-03 10:23:22 +03:00
*
* Takes either one string " mode=value " or ` n ` keyword args mode = value .
2021-01-02 06:21:11 +03:00
*/
2021-01-03 10:23:22 +03:00
static KrkValue krk_set_tracing ( int argc , KrkValue argv [ ] , int hasKw ) {
2021-01-01 11:38:09 +03:00
# ifdef DEBUG
2021-01-03 10:23:22 +03:00
if ( argc ! = 1 ) return NONE_VAL ( ) ;
if ( hasKw ) {
KrkValue _dict_internal ;
krk_tableGet ( & AS_INSTANCE ( argv [ 0 ] ) - > fields , vm . specialMethodNames [ METHOD_DICT_INT ] , & _dict_internal ) ;
KrkValue test ;
if ( krk_tableGet ( AS_DICT ( _dict_internal ) , OBJECT_VAL ( S ( " tracing " ) ) , & test ) ) {
if ( AS_INTEGER ( test ) = = 1 ) vm . flags | = KRK_ENABLE_TRACING ; else vm . flags & = ~ KRK_ENABLE_TRACING ; }
if ( krk_tableGet ( AS_DICT ( _dict_internal ) , OBJECT_VAL ( S ( " disassembly " ) ) , & test ) ) {
if ( AS_INTEGER ( test ) = = 1 ) vm . flags | = KRK_ENABLE_DISASSEMBLY ; else vm . flags & = ~ KRK_ENABLE_DISASSEMBLY ; }
if ( krk_tableGet ( AS_DICT ( _dict_internal ) , OBJECT_VAL ( S ( " stressgc " ) ) , & test ) ) {
if ( AS_INTEGER ( test ) = = 1 ) vm . flags | = KRK_ENABLE_STRESS_GC ; else vm . flags & = ~ KRK_ENABLE_STRESS_GC ; }
if ( krk_tableGet ( AS_DICT ( _dict_internal ) , OBJECT_VAL ( S ( " scantracing " ) ) , & test ) ) {
if ( AS_INTEGER ( test ) = = 1 ) vm . flags | = KRK_ENABLE_SCAN_TRACING ; else vm . flags & = ~ KRK_ENABLE_SCAN_TRACING ; }
return BOOLEAN_VAL ( 1 ) ;
} else {
if ( ! strcmp ( AS_CSTRING ( argv [ 0 ] ) , " tracing=1 " ) ) vm . flags | = KRK_ENABLE_TRACING ;
else if ( ! strcmp ( AS_CSTRING ( argv [ 0 ] ) , " disassembly=1 " ) ) vm . flags | = KRK_ENABLE_DISASSEMBLY ;
else if ( ! strcmp ( AS_CSTRING ( argv [ 0 ] ) , " scantracing=1 " ) ) vm . flags | = KRK_ENABLE_SCAN_TRACING ;
else if ( ! strcmp ( AS_CSTRING ( argv [ 0 ] ) , " stressgc=1 " ) ) vm . flags | = KRK_ENABLE_STRESS_GC ;
else if ( ! strcmp ( AS_CSTRING ( argv [ 0 ] ) , " tracing=0 " ) ) vm . flags & = ~ KRK_ENABLE_TRACING ;
else if ( ! strcmp ( AS_CSTRING ( argv [ 0 ] ) , " disassembly=0 " ) ) vm . flags & = ~ KRK_ENABLE_DISASSEMBLY ;
else if ( ! strcmp ( AS_CSTRING ( argv [ 0 ] ) , " scantracing=0 " ) ) vm . flags & = ~ KRK_ENABLE_SCAN_TRACING ;
else if ( ! strcmp ( AS_CSTRING ( argv [ 0 ] ) , " stressgc=0 " ) ) vm . flags & = ~ KRK_ENABLE_STRESS_GC ;
return BOOLEAN_VAL ( 1 ) ;
}
2021-01-01 11:38:09 +03:00
# else
krk_runtimeError ( vm . exceptions . typeError , " Debugging is not enabled in this build. " ) ;
2021-01-01 11:47:30 +03:00
return NONE_VAL ( ) ;
2021-01-01 11:38:09 +03:00
# endif
2020-12-29 02:22:54 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* object . __dir__ ( )
*/
2020-12-30 06:28:18 +03:00
static KrkValue krk_dirObject ( int argc , KrkValue argv [ ] ) {
2021-01-01 10:01:58 +03:00
if ( argc ! = 1 ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . argumentError , " wrong number of arguments or bad type, got %d \n " , argc ) ;
2020-12-30 06:28:18 +03:00
return NONE_VAL ( ) ;
}
/* Create a new list instance */
2021-01-04 10:20:50 +03:00
KrkValue myList = krk_list_of ( 0 , NULL ) ;
krk_push ( myList ) ;
2021-01-05 03:30:23 +03:00
KrkValue _list_internal = OBJECT_VAL ( AS_INSTANCE ( myList ) - > _internal ) ;
2021-01-01 10:01:58 +03:00
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_NONE ) {
2021-01-04 10:20:50 +03:00
krk_writeValueArray ( AS_LIST ( _list_internal ) ,
2021-01-01 10:01:58 +03:00
self - > _class - > methods . entries [ i ] . key ) ;
}
2020-12-30 06:28:18 +03:00
}
2021-01-01 10:01:58 +03:00
/* 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_NONE ) {
2021-01-04 10:20:50 +03:00
krk_writeValueArray ( AS_LIST ( _list_internal ) ,
2021-01-01 10:01:58 +03:00
self - > fields . entries [ i ] . key ) ;
}
}
} else {
KrkClass * type = AS_CLASS ( krk_typeOf ( 1 , ( KrkValue [ ] ) { argv [ 0 ] } ) ) ;
for ( size_t i = 0 ; i < type - > methods . capacity ; + + i ) {
if ( type - > methods . entries [ i ] . key . type ! = VAL_NONE ) {
2021-01-04 10:20:50 +03:00
krk_writeValueArray ( AS_LIST ( _list_internal ) ,
2021-01-01 10:01:58 +03:00
type - > methods . entries [ i ] . key ) ;
}
2020-12-30 06:28:18 +03:00
}
}
/* Prepare output value */
krk_pop ( ) ;
2021-01-04 10:20:50 +03:00
return myList ;
2020-12-30 06:28:18 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* type ( obj )
*
* For basic types ( non - instances ) , finds the associated pseudo - class ;
* for instances , returns the associated real class .
*
* Called often in native code as krk_typeOf ( 1 , ( KrkValue [ ] ) { value } )
*/
2021-01-01 10:01:58 +03:00
KrkValue krk_typeOf ( int argc , KrkValue argv [ ] ) {
2021-01-01 06:04:58 +03:00
switch ( argv [ 0 ] . type ) {
case VAL_INTEGER :
return OBJECT_VAL ( vm . baseClasses . intClass ) ;
case VAL_FLOATING :
return OBJECT_VAL ( vm . baseClasses . floatClass ) ;
case VAL_BOOLEAN :
return OBJECT_VAL ( vm . baseClasses . boolClass ) ;
case VAL_NONE :
return OBJECT_VAL ( vm . baseClasses . noneTypeClass ) ;
case VAL_OBJECT :
switch ( AS_OBJECT ( argv [ 0 ] ) - > type ) {
case OBJ_CLASS :
return OBJECT_VAL ( vm . baseClasses . typeClass ) ;
case OBJ_NATIVE :
case OBJ_FUNCTION :
case OBJ_CLOSURE :
return OBJECT_VAL ( vm . baseClasses . functionClass ) ;
case OBJ_BOUND_METHOD :
return OBJECT_VAL ( vm . baseClasses . methodClass ) ;
case OBJ_STRING :
return OBJECT_VAL ( vm . baseClasses . strClass ) ;
2021-01-05 05:38:11 +03:00
case OBJ_TUPLE :
return OBJECT_VAL ( vm . baseClasses . tupleClass ) ;
2021-01-01 06:04:58 +03:00
case OBJ_INSTANCE :
return OBJECT_VAL ( AS_INSTANCE ( argv [ 0 ] ) - > _class ) ;
default :
return OBJECT_VAL ( vm . objectClass ) ;
} break ;
default :
return OBJECT_VAL ( vm . objectClass ) ;
}
}
2021-01-04 10:21:59 +03:00
static KrkValue _type_init ( int argc , KrkValue argv [ ] ) {
if ( argc ! = 2 ) {
krk_runtimeError ( vm . exceptions . argumentError , " type() takes 1 argument " ) ;
return NONE_VAL ( ) ;
}
return krk_typeOf ( 1 , & argv [ 1 ] ) ;
}
2021-01-02 06:21:11 +03:00
/* Class.__base__ */
2021-01-01 10:01:58 +03:00
static KrkValue krk_baseOfClass ( int argc , KrkValue argv [ ] ) {
return AS_CLASS ( argv [ 0 ] ) - > base ? OBJECT_VAL ( AS_CLASS ( argv [ 0 ] ) - > base ) : NONE_VAL ( ) ;
}
2021-01-02 06:21:11 +03:00
/* Class.__name */
2021-01-01 10:01:58 +03:00
static KrkValue krk_nameOfClass ( int argc , KrkValue argv [ ] ) {
return AS_CLASS ( argv [ 0 ] ) - > name ? OBJECT_VAL ( AS_CLASS ( argv [ 0 ] ) - > name ) : NONE_VAL ( ) ;
}
2021-01-02 06:21:11 +03:00
/* Class.__file__ */
2021-01-01 10:01:58 +03:00
static KrkValue krk_fileOfClass ( int argc , KrkValue argv [ ] ) {
return AS_CLASS ( argv [ 0 ] ) - > filename ? OBJECT_VAL ( AS_CLASS ( argv [ 0 ] ) - > filename ) : NONE_VAL ( ) ;
}
2021-01-02 06:21:11 +03:00
/* Class.__doc__ */
2021-01-01 10:01:58 +03:00
static KrkValue krk_docOfClass ( int argc , KrkValue argv [ ] ) {
return AS_CLASS ( argv [ 0 ] ) - > docstring ? OBJECT_VAL ( AS_CLASS ( argv [ 0 ] ) - > docstring ) : NONE_VAL ( ) ;
}
2021-01-02 06:21:11 +03:00
/* Class.__str__() (and Class.__repr__) */
2021-01-01 10:01:58 +03:00
static KrkValue _class_to_str ( int argc , KrkValue argv [ ] ) {
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 ) ;
}
2021-01-02 06:21:11 +03:00
/**
* isinstance ( obj , Class )
*
* Searches from type ( obj ) up the inheritence tree to see if obj
* is an eventual descendant of Class . Unless someone made a new
* type and didn ' t inherit from object ( ) , everything is eventually
* an object - even basic types like INTEGERs and FLOATINGs .
*/
2020-12-31 03:15:53 +03:00
static KrkValue krk_isinstance ( int argc , KrkValue argv [ ] ) {
if ( argc ! = 2 ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . argumentError , " isinstance expects 2 arguments, got %d " , argc ) ;
2020-12-31 03:15:53 +03:00
return NONE_VAL ( ) ;
}
if ( ! IS_CLASS ( argv [ 1 ] ) ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . typeError , " isinstance() arg 2 must be class " ) ;
2020-12-31 03:15:53 +03:00
return NONE_VAL ( ) ;
}
2021-01-01 06:04:58 +03:00
KrkValue obj_type = krk_typeOf ( 1 , ( KrkValue [ ] ) { argv [ 0 ] } ) ;
KrkClass * obj_class = AS_CLASS ( obj_type ) ;
2020-12-31 03:15:53 +03:00
KrkClass * _class = AS_CLASS ( argv [ 1 ] ) ;
while ( obj_class ) {
if ( obj_class = = _class ) return BOOLEAN_VAL ( 1 ) ;
obj_class = obj_class - > base ;
}
return BOOLEAN_VAL ( 0 ) ;
}
2021-01-02 07:58:16 +03:00
/**
* 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 ) ;
/* Get its internal table */
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_pop ( ) ;
return dict ;
}
2021-01-03 03:19:18 +03:00
static int checkArgumentCount ( KrkClosure * closure , int argCount ) {
int minArgs = closure - > function - > requiredArgs ;
2021-01-03 06:09:41 +03:00
int maxArgs = minArgs + closure - > function - > keywordArgs ;
2021-01-03 03:19:18 +03:00
if ( argCount < minArgs | | argCount > maxArgs ) {
krk_runtimeError ( vm . exceptions . argumentError , " %s() takes %s %d argument%s (%d given) " ,
closure - > function - > name ? closure - > function - > name - > chars : " <unnamed function> " ,
( minArgs = = maxArgs ) ? " exactly " : ( argCount < minArgs ? " at least " : " at most " ) ,
( argCount < minArgs ) ? minArgs : maxArgs ,
( ( argCount < minArgs ) ? minArgs : maxArgs ) = = 1 ? " " : " s " ,
argCount ) ;
return 0 ;
}
return 1 ;
}
2021-01-03 11:59:50 +03:00
static void multipleDefs ( KrkClosure * closure , int destination ) {
krk_runtimeError ( vm . exceptions . typeError , " %s() got multiple values for argument '%s' " ,
closure - > function - > name ? closure - > function - > name - > chars : " <unnamed function> " ,
( destination < closure - > function - > requiredArgs ? AS_CSTRING ( closure - > function - > requiredArgNames . values [ destination ] ) :
AS_CSTRING ( closure - > function - > keywordArgNames . values [ destination - closure - > function - > requiredArgs ] ) ) ) ;
}
2021-01-02 06:21:11 +03:00
/**
* Call a managed method .
* Takes care of argument count checking , default argument filling ,
* sets up a new call frame , and then resumes the VM to run the function .
2021-01-03 02:31:28 +03:00
*
* Methods are called with their receivers on the stack as the first argument .
* Non - methods are called with themselves on the stack before the first argument .
* ` extra ` is passed by ` callValue ` to tell us which case we have , and thus
* where we need to restore the stack to when we return from this call .
2021-01-02 06:21:11 +03:00
*/
2021-01-03 02:31:28 +03:00
static int call ( KrkClosure * closure , int argCount , int extra ) {
2021-01-03 10:02:50 +03:00
KrkValue * startOfPositionals = & vm . stackTop [ - argCount ] ;
size_t potentialPositionalArgs = closure - > function - > requiredArgs + closure - > function - > keywordArgs ;
size_t totalArguments = closure - > function - > requiredArgs + closure - > function - > keywordArgs + closure - > function - > collectsArguments + closure - > function - > collectsKeywords ;
size_t offsetOfExtraArgs = closure - > function - > requiredArgs + closure - > function - > keywordArgs ;
size_t offsetOfExtraKeys = offsetOfExtraArgs + closure - > function - > collectsArguments ;
size_t argCountX = argCount ;
2021-01-03 06:09:41 +03:00
if ( argCount & & IS_KWARGS ( vm . stackTop [ - 1 ] ) ) {
/**
* Process keyword arguments .
* First , we make sure there is enough space on the stack to fit all of
* the potential arguments to this function . We need to call it with
* all of its arguments - positional and keyword - ready to go , even
* if they weren ' t specified .
*
* Then we go through all of the kwargs and figure out where they go ,
* building a table at the top of the stack of final offsets and values .
*
* Then we clear through all of the spaces that were previously
* kwarg name / value pairs and replace them with a sentinel value .
*
* Then we go through our table and place values into their destination
* spots . If we find that something is already there ( because it ' s not
* the expected sentinel value ) , we raise a TypeError indicating a
* duplicate argument .
*
* Finally , we do one last pass to see if any of the sentinel values
* indicating missing positional arguments is still there and raise
2021-01-03 06:32:04 +03:00
* another TypeError to indicate missing required arguments .
2021-01-03 06:09:41 +03:00
*
* At this point we can reset the stack head and continue to the actual
* call with all of the arguments , including the defaults , in the right
* place for the function to pull them as locals .
*/
long kwargsCount = AS_INTEGER ( vm . stackTop [ - 1 ] ) ;
krk_pop ( ) ; /* Pop the arg counter */
argCount - - ;
2021-01-03 10:02:50 +03:00
size_t existingPositionalArgs = argCount - kwargsCount * 2 ;
2021-01-03 06:09:41 +03:00
int found = 0 ;
2021-01-03 10:02:50 +03:00
int extraKwargs = 0 ;
2021-01-04 15:39:18 +03:00
intptr_t positionalsOffset = & vm . stackTop [ - argCount ] - vm . stack ;
intptr_t endOffset = & vm . stackTop [ - kwargsCount * 2 ] - vm . stack ;
2021-01-03 11:59:50 +03:00
2021-01-03 10:02:50 +03:00
for ( size_t availableSlots = argCount ; availableSlots < ( totalArguments ) ; + + availableSlots ) {
2021-01-03 06:09:41 +03:00
krk_push ( KWARGS_VAL ( 0 ) ) ; /* Make sure we definitely have enough space */
}
2021-01-04 15:39:18 +03:00
/* Expand the stack a bunch to make sure we have space */
2021-01-04 15:43:05 +03:00
for ( int i = 0 ; i < argCount * 2 ; + + i ) {
2021-01-04 15:39:18 +03:00
krk_push ( KWARGS_VAL ( 0 ) ) ;
}
2021-01-04 15:43:05 +03:00
for ( int i = 0 ; i < argCount * 2 ; + + i ) {
2021-01-04 15:39:18 +03:00
krk_pop ( ) ;
}
/* We may have moved the stack, recalculate positions. */
startOfPositionals = vm . stack + positionalsOffset ;
KrkValue * endOfPositionals = vm . stack + endOffset ;
2021-01-03 06:09:41 +03:00
KrkValue * startOfExtras = vm . stackTop ;
for ( long i = 0 ; i < kwargsCount ; + + i ) {
KrkValue name = endOfPositionals [ i * 2 ] ;
KrkValue value = endOfPositionals [ i * 2 + 1 ] ;
2021-01-03 11:59:50 +03:00
if ( IS_KWARGS ( name ) ) {
krk_push ( name ) ;
krk_push ( value ) ;
found + + ;
goto _finishArg ;
}
2021-01-03 06:09:41 +03:00
/* First, see if it's a positional arg. */
2021-01-03 11:59:50 +03:00
for ( int j = 0 ; j < ( int ) closure - > function - > requiredArgs ; + + j ) {
2021-01-03 06:09:41 +03:00
if ( krk_valuesEqual ( name , closure - > function - > requiredArgNames . values [ j ] ) ) {
krk_push ( INTEGER_VAL ( j ) ) ;
krk_push ( value ) ;
found + + ;
goto _finishArg ;
}
}
/* See if it's a keyword arg. */
2021-01-03 11:59:50 +03:00
for ( int j = 0 ; j < ( int ) closure - > function - > keywordArgs ; + + j ) {
2021-01-03 06:09:41 +03:00
if ( krk_valuesEqual ( name , closure - > function - > keywordArgNames . values [ j ] ) ) {
krk_push ( INTEGER_VAL ( j + closure - > function - > requiredArgs ) ) ;
krk_push ( value ) ;
found + + ;
goto _finishArg ;
}
}
/* If we got to this point, it's not a recognized argument for this function. */
2021-01-03 10:02:50 +03:00
if ( closure - > function - > collectsKeywords ) {
krk_push ( name ) ;
krk_push ( value ) ;
found + + ;
extraKwargs + + ;
continue ;
}
2021-01-03 06:09:41 +03:00
krk_runtimeError ( vm . exceptions . typeError , " %s() got an unexpected keyword argument '%s' " ,
closure - > function - > name ? closure - > function - > name - > chars : " <unnamed function> " ,
AS_CSTRING ( name ) ) ;
return 0 ;
_finishArg :
continue ;
}
2021-01-03 11:59:50 +03:00
size_t destination = existingPositionalArgs ;
for ( long i = 0 ; i < found ; + + i ) {
/* Check for specials */
KrkValue name = startOfExtras [ i * 2 ] ;
KrkValue value = startOfExtras [ i * 2 + 1 ] ;
if ( IS_KWARGS ( name ) ) {
if ( AS_INTEGER ( name ) = = LONG_MAX - 1 ) {
2021-01-05 05:39:20 +03:00
if ( ! IS_INSTANCE ( value ) | | ! AS_INSTANCE ( value ) - > _internal | | ! ( ( KrkObj * ) ( AS_INSTANCE ( value ) - > _internal ) ) - > type = = OBJ_FUNCTION ) {
2021-01-03 16:36:24 +03:00
krk_runtimeError ( vm . exceptions . typeError , " *expresssion value is not a list. " ) ;
return 0 ;
}
2021-01-05 03:30:23 +03:00
KrkValue _list_internal = OBJECT_VAL ( AS_INSTANCE ( value ) - > _internal ) ;
2021-01-03 11:59:50 +03:00
for ( size_t i = 0 ; i < AS_LIST ( _list_internal ) - > count ; + + i ) {
startOfPositionals [ destination ] = AS_LIST ( _list_internal ) - > values [ i ] ;
destination + + ;
}
2021-01-03 12:26:55 +03:00
startOfExtras [ i * 2 ] = KWARGS_VAL ( LONG_MAX - 3 ) ;
2021-01-03 11:59:50 +03:00
} else if ( AS_INTEGER ( name ) = = LONG_MAX ) {
startOfPositionals [ destination ] = value ;
destination + + ;
2021-01-03 12:26:55 +03:00
startOfExtras [ i * 2 ] = KWARGS_VAL ( LONG_MAX - 3 ) ;
2021-01-03 11:59:50 +03:00
}
}
}
if ( destination > potentialPositionalArgs ) {
if ( ! closure - > function - > collectsArguments ) {
checkArgumentCount ( closure , destination ) ;
return 0 ;
}
krk_push ( NONE_VAL ( ) ) ; krk_push ( NONE_VAL ( ) ) ; krk_pop ( ) ; krk_pop ( ) ;
startOfPositionals [ offsetOfExtraArgs ] = krk_list_of ( destination - potentialPositionalArgs ,
& startOfPositionals [ potentialPositionalArgs ] ) ;
destination = potentialPositionalArgs + 1 ;
}
for ( long clearSlots = destination ; clearSlots < startOfExtras - startOfPositionals ; + + clearSlots ) {
2021-01-03 06:09:41 +03:00
startOfPositionals [ clearSlots ] = KWARGS_VAL ( 0 ) ;
}
2021-01-03 11:59:50 +03:00
2021-01-03 06:09:41 +03:00
for ( int i = 0 ; i < found ; + + i ) {
2021-01-03 10:02:50 +03:00
if ( IS_INTEGER ( startOfExtras [ i * 2 ] ) ) {
int destination = AS_INTEGER ( startOfExtras [ i * 2 ] ) ;
if ( ! IS_KWARGS ( startOfPositionals [ destination ] ) ) {
2021-01-03 11:59:50 +03:00
multipleDefs ( closure , destination ) ;
2021-01-03 10:02:50 +03:00
return 0 ;
}
startOfPositionals [ destination ] = startOfExtras [ i * 2 + 1 ] ;
} else if ( IS_STRING ( startOfExtras [ i * 2 ] ) ) {
krk_push ( startOfExtras [ i * 2 ] ) ;
krk_push ( startOfExtras [ i * 2 + 1 ] ) ;
2021-01-03 11:59:50 +03:00
} else if ( IS_KWARGS ( startOfExtras [ i * 2 ] ) ) {
if ( AS_INTEGER ( startOfExtras [ i * 2 ] ) = = LONG_MAX - 2 ) {
KrkValue _dict_internal ;
2021-01-03 16:36:24 +03:00
if ( ! IS_INSTANCE ( startOfExtras [ i * 2 + 1 ] ) | | ! krk_tableGet ( & AS_INSTANCE ( startOfExtras [ i * 2 + 1 ] ) - > fields , vm . specialMethodNames [ METHOD_DICT_INT ] , & _dict_internal ) ) {
krk_runtimeError ( vm . exceptions . typeError , " **expresssion value is not a dict. " ) ;
return 0 ;
}
2021-01-03 11:59:50 +03:00
for ( size_t j = 0 ; j < AS_DICT ( _dict_internal ) - > capacity ; + + j ) {
KrkTableEntry entry = AS_DICT ( _dict_internal ) - > entries [ j ] ;
if ( entry . key . type = = VAL_NONE ) continue ;
KrkValue name = entry . key ;
KrkValue value = entry . value ;
for ( int j = 0 ; j < ( int ) closure - > function - > requiredArgNames . count ; + + j ) {
if ( krk_valuesEqual ( name , closure - > function - > requiredArgNames . values [ j ] ) ) {
int destination = j ;
if ( ! IS_KWARGS ( startOfPositionals [ destination ] ) ) {
multipleDefs ( closure , destination ) ;
}
startOfPositionals [ destination ] = value ;
goto _finishDictEntry ;
}
}
/* See if it's a keyword arg. */
for ( int j = 0 ; j < ( int ) closure - > function - > keywordArgNames . count ; + + j ) {
if ( krk_valuesEqual ( name , closure - > function - > keywordArgNames . values [ j ] ) ) {
int destination = j + closure - > function - > requiredArgs ;
if ( ! IS_KWARGS ( startOfPositionals [ destination ] ) ) {
multipleDefs ( closure , destination ) ;
}
startOfPositionals [ destination ] = value ;
goto _finishDictEntry ;
}
}
krk_push ( name ) ;
krk_push ( value ) ;
extraKwargs + + ;
_finishDictEntry : continue ;
}
}
2021-01-03 10:02:50 +03:00
} else {
2021-01-05 09:33:33 +03:00
# ifdef ENABLE_TRACING
2021-01-04 15:39:18 +03:00
dumpStack ( & vm . frames [ vm . frameCount - 1 ] ) ;
krk_runtimeError ( vm . exceptions . typeError , " Internal error? Item at index %d from %d found is %s " , i * 2 , found , krk_typeName ( startOfExtras [ i * 2 ] ) ) ;
2021-01-05 09:33:33 +03:00
# endif
2021-01-03 06:09:41 +03:00
return 0 ;
}
2021-01-03 10:02:50 +03:00
}
if ( extraKwargs ) {
2021-01-03 12:26:55 +03:00
if ( ! closure - > function - > collectsKeywords ) {
krk_runtimeError ( vm . exceptions . typeError , " %s() got an unexpected keyword argument '%s' " ,
closure - > function - > name ? closure - > function - > name - > chars : " <unnamed function> " ,
AS_CSTRING ( startOfExtras [ found * 2 ] ) ) ;
}
2021-01-03 10:02:50 +03:00
krk_push ( NONE_VAL ( ) ) ; krk_push ( NONE_VAL ( ) ) ; krk_pop ( ) ; krk_pop ( ) ;
startOfPositionals [ offsetOfExtraKeys ] = krk_dict_of ( extraKwargs * 2 , & startOfExtras [ found * 2 ] ) ;
2021-01-03 06:09:41 +03:00
}
long clearSlots ;
2021-01-03 11:59:50 +03:00
for ( clearSlots = destination ; clearSlots < closure - > function - > requiredArgs ; + + clearSlots ) {
2021-01-03 06:09:41 +03:00
if ( IS_KWARGS ( startOfPositionals [ clearSlots ] ) ) {
krk_runtimeError ( vm . exceptions . typeError , " %s() missing required positional argument: '%s' " ,
closure - > function - > name ? closure - > function - > name - > chars : " <unnamed function> " ,
AS_CSTRING ( closure - > function - > requiredArgNames . values [ clearSlots ] ) ) ;
return 0 ;
}
}
2021-01-03 10:02:50 +03:00
argCount = totalArguments ;
argCountX = argCount - ( closure - > function - > collectsArguments + closure - > function - > collectsKeywords ) ;
2021-01-03 06:09:41 +03:00
while ( vm . stackTop > startOfPositionals + argCount ) krk_pop ( ) ;
2021-01-03 10:02:50 +03:00
} else {
/* We can't have had any kwargs. */
if ( ( size_t ) argCount > potentialPositionalArgs & & closure - > function - > collectsArguments ) {
krk_push ( NONE_VAL ( ) ) ; krk_push ( NONE_VAL ( ) ) ; krk_pop ( ) ; krk_pop ( ) ;
startOfPositionals [ offsetOfExtraArgs ] = krk_list_of ( argCount - potentialPositionalArgs ,
& startOfPositionals [ potentialPositionalArgs ] ) ;
argCount = closure - > function - > requiredArgs + 1 ;
argCountX = argCount - 1 ;
while ( vm . stackTop > startOfPositionals + argCount ) krk_pop ( ) ;
}
2021-01-03 06:09:41 +03:00
}
2021-01-03 10:02:50 +03:00
if ( ! checkArgumentCount ( closure , argCountX ) ) {
2020-12-27 03:33:28 +03:00
return 0 ;
}
2021-01-03 11:59:50 +03:00
while ( argCount < ( int ) totalArguments ) {
2021-01-03 06:32:04 +03:00
krk_push ( KWARGS_VAL ( 0 ) ) ;
2020-12-30 02:00:48 +03:00
argCount + + ;
}
2020-12-27 03:33:28 +03:00
if ( vm . frameCount = = FRAMES_MAX ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . baseException , " Too many call frames. " ) ;
2020-12-27 03:33:28 +03:00
return 0 ;
}
CallFrame * frame = & vm . frames [ vm . frameCount + + ] ;
2020-12-27 07:02:26 +03:00
frame - > closure = closure ;
frame - > ip = closure - > function - > chunk . code ;
2021-01-03 02:31:28 +03:00
frame - > slots = ( vm . stackTop - argCount ) - vm . stack ;
frame - > outSlots = ( vm . stackTop - argCount - extra ) - vm . stack ;
2020-12-27 03:33:28 +03:00
return 1 ;
}
2021-01-02 06:21:11 +03:00
/**
* Call a callable .
*
* For native methods , the result is available " immediately " upon return
* and the return value is set to 2 to indicate this - just krk_pop ( )
* to get the result . If an exception is thrown during a native method call ,
* callValue will return 0 and the VM should be allowed to handle the exception .
*
* For managed code , the VM needs to be resumed . Returns 1 to indicate this .
* If you want a result in a native method , call ` krk_runNext ( ) ` and the
* result will be returned directly from that function .
*
* Works for closures , classes , natives , and bound methods .
* If called with a non - callable , raises TypeError ; this includes
* attempts to call a Class with no __init__ while using arguments .
*
* If callValue returns 0 , the VM should already be in the exception state
* and it is not necessary to raise another exception .
*
* TODO : Instances with __call__ method .
*/
2021-01-03 02:31:28 +03:00
int krk_callValue ( KrkValue callee , int argCount , int extra ) {
2020-12-27 03:33:28 +03:00
if ( IS_OBJECT ( callee ) ) {
switch ( OBJECT_TYPE ( callee ) ) {
2020-12-27 07:02:26 +03:00
case OBJ_CLOSURE :
2021-01-03 02:31:28 +03:00
return call ( AS_CLOSURE ( callee ) , argCount , extra ) ;
2020-12-27 03:33:28 +03:00
case OBJ_NATIVE : {
2021-01-03 10:23:22 +03:00
NativeFnKw native = ( NativeFnKw ) AS_NATIVE ( callee ) ;
int hasKw = 0 ;
2021-01-03 06:09:41 +03:00
if ( argCount & & IS_KWARGS ( vm . stackTop [ - 1 ] ) ) {
2021-01-03 10:23:22 +03:00
long count = AS_INTEGER ( vm . stackTop [ - 1 ] ) ;
2021-01-03 12:38:27 +03:00
for ( long i = 0 ; i < count ; + + i ) {
if ( IS_KWARGS ( vm . stackTop [ - 1 - count * 2 + i * 2 ] ) ) {
krk_runtimeError ( vm . exceptions . typeError , " Unsupported use of argument expansion in native function call. " ) ;
return 0 ;
}
}
2021-01-03 10:23:22 +03:00
/* Dict it all up */
* ( vm . stackTop - count * 2 - 1 ) = krk_dict_of ( count * 2 , ( vm . stackTop - count * 2 - 1 ) ) ;
vm . stackTop = vm . stackTop - count * 2 ;
argCount - = count * 2 ;
hasKw = 1 ;
2021-01-03 06:09:41 +03:00
}
2021-01-03 02:31:28 +03:00
KrkValue * stackCopy = malloc ( argCount * sizeof ( KrkValue ) ) ;
memcpy ( stackCopy , vm . stackTop - argCount , argCount * sizeof ( KrkValue ) ) ;
2021-01-03 10:23:22 +03:00
KrkValue result = native ( argCount , stackCopy , hasKw ) ;
2020-12-31 09:48:39 +03:00
free ( stackCopy ) ;
2020-12-28 04:54:25 +03:00
if ( vm . stackTop = = vm . stack ) {
/* Runtime error returned from native method */
return 0 ;
}
2021-01-03 02:31:28 +03:00
vm . stackTop - = argCount + extra ;
2020-12-27 03:33:28 +03:00
krk_push ( result ) ;
2020-12-29 14:25:34 +03:00
return 2 ;
2020-12-27 03:33:28 +03:00
}
2021-01-04 10:21:27 +03:00
case OBJ_INSTANCE : {
KrkClass * _class = AS_INSTANCE ( callee ) - > _class ;
KrkValue callFunction ;
2021-01-05 05:39:20 +03:00
if ( _class - > _call ) {
return krk_callValue ( OBJECT_VAL ( _class - > _call ) , argCount + 1 , 0 ) ;
} else if ( krk_tableGet ( & _class - > methods , vm . specialMethodNames [ METHOD_CALL ] , & callFunction ) ) {
2021-01-04 10:21:27 +03:00
return krk_callValue ( callFunction , argCount + 1 , 0 ) ;
} else {
krk_runtimeError ( vm . exceptions . typeError , " Attempted to call non-callable type: %s " , krk_typeName ( callee ) ) ;
return 0 ;
}
}
2020-12-27 10:45:34 +03:00
case OBJ_CLASS : {
KrkClass * _class = AS_CLASS ( callee ) ;
2020-12-28 05:11:50 +03:00
vm . stackTop [ - argCount - 1 ] = OBJECT_VAL ( krk_newInstance ( _class ) ) ;
2020-12-27 11:53:46 +03:00
KrkValue initializer ;
2021-01-05 05:39:20 +03:00
if ( _class - > _init ) {
return krk_callValue ( OBJECT_VAL ( _class - > _init ) , argCount + 1 , 0 ) ;
} else if ( krk_tableGet ( & _class - > methods , vm . specialMethodNames [ METHOD_INIT ] , & initializer ) ) {
2021-01-03 02:31:28 +03:00
return krk_callValue ( initializer , argCount + 1 , 0 ) ;
2020-12-27 11:53:46 +03:00
} else if ( argCount ! = 0 ) {
2021-01-03 06:09:41 +03:00
krk_runtimeError ( vm . exceptions . attributeError , " Class does not have an __init__ but arguments were passed to initializer: %d " , argCount ) ;
2020-12-27 11:53:46 +03:00
return 0 ;
}
2020-12-27 10:45:34 +03:00
return 1 ;
}
2020-12-27 11:53:46 +03:00
case OBJ_BOUND_METHOD : {
KrkBoundMethod * bound = AS_BOUND_METHOD ( callee ) ;
vm . stackTop [ - argCount - 1 ] = bound - > receiver ;
2021-01-03 03:19:18 +03:00
if ( ! bound - > method ) {
krk_runtimeError ( vm . exceptions . argumentError , " Attempted to call a method binding with no attached callable (did you forget to return something from a method decorator?) " ) ;
return 0 ;
}
2021-01-03 02:31:28 +03:00
return krk_callValue ( OBJECT_VAL ( bound - > method ) , argCount + 1 , 0 ) ;
2020-12-27 11:53:46 +03:00
}
2020-12-27 03:33:28 +03:00
default :
break ;
}
}
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . typeError , " Attempted to call non-callable type: %s " , krk_typeName ( callee ) ) ;
2020-12-27 03:33:28 +03:00
return 0 ;
}
2021-01-04 05:27:41 +03:00
/**
* Takes care of runnext / pop
*/
2021-01-04 12:15:17 +03:00
KrkValue krk_callSimple ( KrkValue value , int argCount , int isMethod ) {
int result = krk_callValue ( value , argCount , isMethod ) ;
2021-01-04 05:27:41 +03:00
if ( result = = 2 ) {
return krk_pop ( ) ;
} else if ( result = = 1 ) {
return krk_runNext ( ) ;
}
krk_runtimeError ( vm . exceptions . typeError , " Invalid internal method call. " ) ;
return NONE_VAL ( ) ;
}
2021-01-02 06:21:11 +03:00
/**
* Attach a method call to its callee and return a BoundMethod .
* Works for managed and native method calls .
*/
2021-01-01 10:01:58 +03:00
int krk_bindMethod ( KrkClass * _class , KrkString * name ) {
KrkValue method , out ;
2020-12-27 11:53:46 +03:00
if ( ! krk_tableGet ( & _class - > methods , OBJECT_VAL ( name ) , & method ) ) return 0 ;
2021-01-01 10:01:58 +03:00
if ( IS_NATIVE ( method ) & & ( ( KrkNative * ) AS_OBJECT ( method ) ) - > isMethod = = 2 ) {
out = AS_NATIVE ( method ) ( 1 , ( KrkValue [ ] ) { krk_peek ( 0 ) } ) ;
} else {
out = OBJECT_VAL ( krk_newBoundMethod ( krk_peek ( 0 ) , AS_OBJECT ( method ) ) ) ;
}
2020-12-27 11:53:46 +03:00
krk_pop ( ) ;
2021-01-01 10:01:58 +03:00
krk_push ( out ) ;
2020-12-27 11:53:46 +03:00
return 1 ;
}
2021-01-02 06:21:11 +03:00
/**
* Capture upvalues and mark them as open . Called upon closure creation to
* mark stack slots used by a function .
*/
2020-12-28 16:02:39 +03:00
static KrkUpvalue * captureUpvalue ( int index ) {
2020-12-27 07:02:26 +03:00
KrkUpvalue * prevUpvalue = NULL ;
KrkUpvalue * upvalue = vm . openUpvalues ;
2020-12-28 16:02:39 +03:00
while ( upvalue ! = NULL & & upvalue - > location > index ) {
2020-12-27 07:02:26 +03:00
prevUpvalue = upvalue ;
upvalue = upvalue - > next ;
}
2020-12-28 16:02:39 +03:00
if ( upvalue ! = NULL & & upvalue - > location = = index ) {
2020-12-27 07:02:26 +03:00
return upvalue ;
}
2020-12-28 16:02:39 +03:00
KrkUpvalue * createdUpvalue = krk_newUpvalue ( index ) ;
2020-12-27 07:02:26 +03:00
createdUpvalue - > next = upvalue ;
if ( prevUpvalue = = NULL ) {
vm . openUpvalues = createdUpvalue ;
} else {
prevUpvalue - > next = createdUpvalue ;
}
return createdUpvalue ;
}
2020-12-28 16:02:39 +03:00
# define UPVALUE_LOCATION(upvalue) (upvalue->location == -1 ? &upvalue->closed : &vm.stack[upvalue->location])
2021-01-02 06:21:11 +03:00
/**
* Close upvalues by moving them out of the stack and into the heap .
* Their location attribute is set to - 1 to indicate they now live on the heap .
*/
2020-12-28 16:02:39 +03:00
static void closeUpvalues ( int last ) {
2020-12-27 07:02:26 +03:00
while ( vm . openUpvalues ! = NULL & & vm . openUpvalues - > location > = last ) {
KrkUpvalue * upvalue = vm . openUpvalues ;
2020-12-28 16:02:39 +03:00
upvalue - > closed = vm . stack [ upvalue - > location ] ;
upvalue - > location = - 1 ;
2020-12-27 07:02:26 +03:00
vm . openUpvalues = upvalue - > next ;
}
}
2021-01-02 06:21:11 +03:00
/**
* Attach an object to a table .
*
* Generally used to attach classes or objects to the globals table , or to
* a native module ' s export object .
*/
2020-12-28 13:25:33 +03:00
void krk_attachNamedObject ( KrkTable * table , const char name [ ] , KrkObj * obj ) {
2020-12-28 13:01:28 +03:00
krk_push ( OBJECT_VAL ( krk_copyString ( name , strlen ( name ) ) ) ) ;
krk_push ( OBJECT_VAL ( obj ) ) ;
2020-12-31 08:46:02 +03:00
krk_tableSet ( table , krk_peek ( 1 ) , krk_peek ( 0 ) ) ;
2020-12-28 13:01:28 +03:00
krk_pop ( ) ;
krk_pop ( ) ;
}
2021-01-02 06:21:11 +03:00
/**
* Same as above , but the object has already been wrapped in a value .
*/
2020-12-31 09:48:39 +03:00
void krk_attachNamedValue ( KrkTable * table , const char name [ ] , KrkValue obj ) {
krk_push ( OBJECT_VAL ( krk_copyString ( name , strlen ( name ) ) ) ) ;
krk_push ( obj ) ;
krk_tableSet ( table , krk_peek ( 1 ) , krk_peek ( 0 ) ) ;
krk_pop ( ) ;
krk_pop ( ) ;
}
2021-01-02 06:21:11 +03:00
/**
* Exception . __init__ ( arg )
*/
2021-01-01 06:04:58 +03:00
static KrkValue krk_initException ( int argc , KrkValue argv [ ] ) {
KrkInstance * self = AS_INSTANCE ( argv [ 0 ] ) ;
if ( argc > 0 ) {
krk_attachNamedValue ( & self - > fields , " arg " , argv [ 1 ] ) ;
} else {
krk_attachNamedValue ( & self - > fields , " arg " , OBJECT_VAL ( S ( " " ) ) ) ;
}
return argv [ 0 ] ;
}
2021-01-04 10:21:59 +03:00
static KrkValue _string_init ( int argc , KrkValue argv [ ] ) {
/* Ignore argument which would have been an instance */
if ( argc < 2 ) {
return OBJECT_VAL ( S ( " " ) ) ;
}
if ( argc > 2 ) {
krk_runtimeError ( vm . exceptions . argumentError , " str() takes 1 argument " ) ;
return NONE_VAL ( ) ;
}
if ( IS_STRING ( argv [ 1 ] ) ) return argv [ 1 ] ; /* strings are immutable, so we can just return the arg */
/* Find the type of arg */
krk_push ( argv [ 1 ] ) ;
2021-01-05 03:30:23 +03:00
if ( ! AS_CLASS ( krk_typeOf ( 1 , & argv [ 1 ] ) ) - > _tostr ) {
2021-01-04 10:21:59 +03:00
krk_runtimeError ( vm . exceptions . typeError , " Can not convert %s to str " , krk_typeName ( argv [ 1 ] ) ) ;
return NONE_VAL ( ) ;
}
2021-01-05 03:30:23 +03:00
return krk_callSimple ( OBJECT_VAL ( AS_CLASS ( krk_typeOf ( 1 , & argv [ 1 ] ) ) - > _tostr ) , 1 , 0 ) ;
2021-01-04 10:21:59 +03:00
}
2021-01-01 06:04:58 +03:00
# define ADD_BASE_CLASS(obj, name, baseClass) do { \
obj = krk_newClass ( S ( name ) ) ; \
krk_attachNamedObject ( & vm . builtins - > fields , name , ( KrkObj * ) obj ) ; \
obj - > base = baseClass ; \
krk_tableAddAll ( & baseClass - > methods , & obj - > methods ) ; \
} while ( 0 )
# define ADD_EXCEPTION_CLASS(obj, name, baseClass) do { \
obj = krk_newClass ( S ( name ) ) ; \
krk_attachNamedObject ( & vm . globals , name , ( KrkObj * ) obj ) ; \
obj - > base = baseClass ; \
krk_tableAddAll ( & baseClass - > methods , & obj - > methods ) ; \
} while ( 0 )
2021-01-02 06:21:11 +03:00
/** native method that returns its first arg; useful for int(INT), etc. */
2021-01-01 10:01:58 +03:00
static KrkValue _noop ( int argc , KrkValue argv [ ] ) {
return argv [ 0 ] ;
}
2021-01-02 06:21:11 +03:00
/* float.__int__() */
2021-01-01 10:01:58 +03:00
static KrkValue _floating_to_int ( int argc , KrkValue argv [ ] ) {
return INTEGER_VAL ( ( long ) AS_FLOATING ( argv [ 0 ] ) ) ;
}
2021-01-02 06:21:11 +03:00
/* int.__float__() */
2021-01-01 10:01:58 +03:00
static KrkValue _int_to_floating ( int argc , KrkValue argv [ ] ) {
return FLOATING_VAL ( ( double ) AS_INTEGER ( argv [ 0 ] ) ) ;
}
2021-01-02 06:21:11 +03:00
/* int.__chr__() */
2021-01-01 10:01:58 +03:00
static KrkValue _int_to_char ( int argc , KrkValue argv [ ] ) {
char tmp [ 2 ] = { AS_INTEGER ( argv [ 0 ] ) , 0 } ;
return OBJECT_VAL ( krk_copyString ( tmp , 1 ) ) ;
}
2021-01-03 08:09:45 +03:00
/* str.__ord__() */
static KrkValue _char_to_int ( int argc , KrkValue argv [ ] ) {
if ( AS_STRING ( argv [ 0 ] ) - > length ! = 1 ) {
krk_runtimeError ( vm . exceptions . typeError , " ord() expected a character, but string of length %d found " ,
AS_STRING ( argv [ 0 ] ) - > length ) ;
return NONE_VAL ( ) ;
}
/* TODO unicode strings? Interpret as UTF-8 and return codepoint? */
return INTEGER_VAL ( AS_CSTRING ( argv [ 0 ] ) [ 0 ] ) ;
}
2021-01-04 17:33:43 +03:00
static KrkValue _print ( int argc , KrkValue argv [ ] , int hasKw ) {
KrkValue sepVal , endVal ;
char * sep = " " ;
char * end = " \n " ;
if ( hasKw ) {
argc - - ;
KrkValue _dict_internal ;
krk_tableGet ( & AS_INSTANCE ( argv [ argc ] ) - > fields , vm . specialMethodNames [ METHOD_DICT_INT ] , & _dict_internal ) ;
if ( krk_tableGet ( AS_DICT ( _dict_internal ) , OBJECT_VAL ( S ( " sep " ) ) , & sepVal ) ) {
if ( ! IS_STRING ( sepVal ) ) {
krk_runtimeError ( vm . exceptions . typeError , " 'sep' should be a string, not '%s' " , krk_typeName ( sepVal ) ) ;
return NONE_VAL ( ) ;
}
sep = AS_CSTRING ( sepVal ) ;
}
if ( krk_tableGet ( AS_DICT ( _dict_internal ) , OBJECT_VAL ( S ( " end " ) ) , & endVal ) ) {
if ( ! IS_STRING ( endVal ) ) {
krk_runtimeError ( vm . exceptions . typeError , " 'end' should be a string, not '%s' " , krk_typeName ( endVal ) ) ;
return NONE_VAL ( ) ;
}
end = AS_CSTRING ( endVal ) ;
}
}
for ( int i = 0 ; i < argc ; + + i ) {
KrkValue printable = argv [ i ] ;
if ( IS_STRING ( printable ) ) { /* krk_printValue runs repr */
fprintf ( stdout , " %s " , AS_CSTRING ( printable ) ) ;
} else {
krk_printValue ( stdout , printable ) ;
}
fprintf ( stdout , " %s " , ( i = = argc - 1 ) ? end : sep ) ;
}
return NONE_VAL ( ) ;
}
2021-01-02 06:21:11 +03:00
/* str.__len__() */
2021-01-01 10:01:58 +03:00
static KrkValue _string_length ( int argc , KrkValue argv [ ] ) {
if ( argc ! = 1 ) {
2021-01-03 10:23:22 +03:00
krk_runtimeError ( vm . exceptions . attributeError , " Unexpected arguments to str.__len__() " ) ;
2021-01-01 10:01:58 +03:00
return NONE_VAL ( ) ;
}
if ( ! IS_STRING ( argv [ 0 ] ) ) {
return NONE_VAL ( ) ;
}
return INTEGER_VAL ( AS_STRING ( argv [ 0 ] ) - > length ) ;
}
2021-01-02 06:21:11 +03:00
/* str.__set__(ind,val) - this is invalid, throw a nicer error than 'field does not exist'. */
2021-01-01 10:01:58 +03:00
static KrkValue _strings_are_immutable ( int argc , KrkValue argv [ ] ) {
krk_runtimeError ( vm . exceptions . typeError , " Strings are not mutable. " ) ;
return NONE_VAL ( ) ;
}
2021-01-02 06:21:11 +03:00
/**
* str . __getslice__ ( start , end )
*
* Unlike in Python , we actually handle negative values here rather than
* somewhere else ? I ' m not even sure where Python does do it , but a quick
* says not if you call __getslice__ directly . . .
*/
2021-01-01 10:01:58 +03:00
static KrkValue _string_get_slice ( int argc , KrkValue argv [ ] ) {
if ( argc < 3 ) { /* 3 because first is us */
krk_runtimeError ( vm . exceptions . argumentError , " slice: expected 2 arguments, got %d " , argc - 1 ) ;
return NONE_VAL ( ) ;
}
if ( ! IS_STRING ( argv [ 0 ] ) | |
! ( IS_INTEGER ( argv [ 1 ] ) | | IS_NONE ( argv [ 1 ] ) ) | |
! ( IS_INTEGER ( argv [ 2 ] ) | | IS_NONE ( argv [ 2 ] ) ) ) {
krk_runtimeError ( vm . exceptions . typeError , " slice: expected two integer arguments " ) ;
return NONE_VAL ( ) ;
}
/* bounds check */
KrkString * me = AS_STRING ( argv [ 0 ] ) ;
int start = IS_NONE ( argv [ 1 ] ) ? 0 : AS_INTEGER ( argv [ 1 ] ) ;
int end = IS_NONE ( argv [ 2 ] ) ? ( int ) me - > length : AS_INTEGER ( argv [ 2 ] ) ;
if ( start < 0 ) start = me - > length + start ;
if ( start < 0 ) start = 0 ;
if ( end < 0 ) end = me - > length + end ;
if ( start > ( int ) me - > length ) start = me - > length ;
if ( end > ( int ) me - > length ) end = me - > length ;
if ( end < start ) end = start ;
int len = end - start ;
return OBJECT_VAL ( krk_copyString ( me - > chars + start , len ) ) ;
}
2021-01-02 06:21:11 +03:00
/* str.__int__(base=10) */
2021-01-01 10:01:58 +03:00
static KrkValue _string_to_int ( int argc , KrkValue argv [ ] ) {
2021-01-02 06:21:11 +03:00
if ( argc < 1 | | argc > 2 | | ! IS_STRING ( argv [ 0 ] ) ) return NONE_VAL ( ) ;
int base = ( argc = = 1 ) ? 10 : ( int ) AS_INTEGER ( argv [ 1 ] ) ;
2021-01-01 10:01:58 +03:00
char * start = AS_CSTRING ( argv [ 0 ] ) ;
/* These special cases for hexadecimal, binary, octal values. */
if ( start [ 0 ] = = ' 0 ' & & ( start [ 1 ] = = ' x ' | | start [ 1 ] = = ' X ' ) ) {
base = 16 ;
start + = 2 ;
} else if ( start [ 0 ] = = ' 0 ' & & ( start [ 1 ] = = ' b ' | | start [ 1 ] = = ' B ' ) ) {
base = 2 ;
start + = 2 ;
} else if ( start [ 0 ] = = ' 0 ' & & ( start [ 1 ] = = ' o ' | | start [ 1 ] = = ' O ' ) ) {
base = 8 ;
start + = 2 ;
}
long value = strtol ( start , NULL , base ) ;
return INTEGER_VAL ( value ) ;
}
2021-01-02 06:21:11 +03:00
/* str.__float__() */
2021-01-01 10:01:58 +03:00
static KrkValue _string_to_float ( int argc , KrkValue argv [ ] ) {
if ( argc ! = 1 | | ! IS_STRING ( argv [ 0 ] ) ) return NONE_VAL ( ) ;
return FLOATING_VAL ( strtod ( AS_CSTRING ( argv [ 0 ] ) , NULL ) ) ;
}
2021-01-04 10:21:59 +03:00
static KrkValue _float_init ( int argc , KrkValue argv [ ] ) {
if ( argc < 1 ) return FLOATING_VAL ( 0.0 ) ;
if ( argc > 2 ) {
krk_runtimeError ( vm . exceptions . argumentError , " float() takes at most 1 argument " ) ;
return NONE_VAL ( ) ;
}
if ( IS_STRING ( argv [ 1 ] ) ) return _string_to_float ( 1 , & argv [ 1 ] ) ;
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 ] ) ) ;
krk_runtimeError ( vm . exceptions . typeError , " float() argument must be a string or a number, not '%s' " , krk_typeName ( argv [ 1 ] ) ) ;
return NONE_VAL ( ) ;
}
2021-01-02 06:21:11 +03:00
/* str.__get__(index) */
2021-01-01 10:01:58 +03:00
static KrkValue _string_get ( int argc , KrkValue argv [ ] ) {
if ( argc ! = 2 ) {
krk_runtimeError ( vm . exceptions . argumentError , " Wrong number of arguments to String.__get__ " ) ;
return NONE_VAL ( ) ;
}
if ( ! IS_STRING ( argv [ 0 ] ) ) {
krk_runtimeError ( vm . exceptions . typeError , " First argument to __get__ must be String " ) ;
return NONE_VAL ( ) ;
}
if ( ! IS_INTEGER ( argv [ 1 ] ) ) {
krk_runtimeError ( vm . exceptions . typeError , " String can not indexed by %s " , krk_typeName ( argv [ 1 ] ) ) ;
return NONE_VAL ( ) ;
}
2021-01-03 08:09:45 +03:00
KrkString * me = AS_STRING ( argv [ 0 ] ) ;
2021-01-01 10:01:58 +03:00
int asInt = AS_INTEGER ( argv [ 1 ] ) ;
if ( asInt < 0 ) asInt + = ( int ) AS_STRING ( argv [ 0 ] ) - > length ;
if ( asInt < 0 | | asInt > = ( int ) AS_STRING ( argv [ 0 ] ) - > length ) {
krk_runtimeError ( vm . exceptions . indexError , " String index out of range: %d " , asInt ) ;
return NONE_VAL ( ) ;
}
2021-01-03 08:09:45 +03:00
return OBJECT_VAL ( krk_copyString ( ( char [ ] ) { me - > chars [ asInt ] } , 1 ) ) ;
2021-01-01 10:01:58 +03:00
}
2021-01-04 06:47:57 +03:00
# 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)
2021-01-04 05:27:41 +03:00
/* str.format(**kwargs) */
static KrkValue _string_format ( int argc , KrkValue argv [ ] , int hasKw ) {
if ( ! IS_STRING ( argv [ 0 ] ) ) return NONE_VAL ( ) ;
KrkString * self = AS_STRING ( argv [ 0 ] ) ;
KrkValue kwargs = NONE_VAL ( ) ;
if ( hasKw ) {
argc - - ; /* last arg is the keyword dictionary */
krk_tableGet ( & AS_INSTANCE ( argv [ argc ] ) - > fields , vm . specialMethodNames [ METHOD_DICT_INT ] , & kwargs ) ;
}
/* Read through `self` until we find a field specifier. */
size_t stringCapacity = 0 ;
size_t stringLength = 0 ;
char * stringBytes = 0 ;
int counterOffset = 0 ;
char * erroneousField = NULL ;
int erroneousIndex = - 1 ;
const char * errorStr = " " ;
char * workSpace = strdup ( self - > chars ) ;
char * c = workSpace ;
for ( size_t i = 0 ; i < self - > length ; i + + , c + + ) {
if ( * c = = ' { ' ) {
if ( ! AT_END ( ) & & c [ 1 ] = = ' { ' ) {
PUSH_CHAR ( ' { ' ) ;
i + + ; c + + ; /* Skip both */
continue ;
} else {
/* Start field specifier */
i + + ; c + + ; /* Skip the { */
char * fieldStart = c ;
char * fieldStop = NULL ;
for ( ; i < self - > length ; i + + , c + + ) {
if ( * c = = ' } ' ) {
fieldStop = c ;
break ;
}
}
if ( ! fieldStop ) {
errorStr = " Unclosed { found. " ;
goto _formatError ;
}
size_t fieldLength = fieldStop - fieldStart ;
* fieldStop = ' \0 ' ;
/* fieldStart is now a nice little C string... */
int isDigits = 1 ;
for ( char * field = fieldStart ; * field ; + + field ) {
if ( ! ( * field > = ' 0 ' & & * field < = ' 9 ' ) ) {
isDigits = 0 ;
break ;
}
}
KrkValue value ;
if ( isDigits ) {
/* Must be positional */
int positionalOffset ;
if ( fieldLength = = 0 ) {
positionalOffset = counterOffset + + ;
} else if ( counterOffset ) {
goto _formatSwitchedNumbering ;
} else {
positionalOffset = atoi ( fieldStart ) ;
}
if ( positionalOffset > = argc - 1 ) {
erroneousIndex = positionalOffset ;
goto _formatOutOfRange ;
}
value = argv [ 1 + positionalOffset ] ;
} else if ( hasKw ) {
KrkValue fieldAsString = OBJECT_VAL ( krk_copyString ( fieldStart , fieldLength ) ) ;
krk_push ( fieldAsString ) ;
if ( ! krk_tableGet ( AS_DICT ( kwargs ) , fieldAsString , & value ) ) {
erroneousField = fieldStart ;
goto _formatKeyError ;
}
krk_pop ( ) ; /* fieldAsString */
} else {
erroneousField = fieldStart ;
goto _formatKeyError ;
}
KrkValue asString ;
if ( IS_STRING ( value ) ) {
asString = value ;
} else {
krk_push ( value ) ;
2021-01-05 05:39:20 +03:00
KrkClass * type = AS_CLASS ( krk_typeOf ( 1 , ( KrkValue [ ] ) { value } ) ) ;
if ( type - > _tostr ) {
asString = krk_callSimple ( OBJECT_VAL ( type - > _tostr ) , 1 , 0 ) ;
} else {
if ( ! krk_bindMethod ( type , AS_STRING ( vm . specialMethodNames [ METHOD_STR ] ) ) ) {
errorStr = " Failed to convert field to string. " ;
goto _formatError ;
}
asString = krk_callSimple ( krk_peek ( 0 ) , 0 , 1 ) ;
2021-01-04 05:27:41 +03:00
}
if ( ! IS_STRING ( asString ) ) goto _freeAndDone ;
}
krk_push ( asString ) ;
for ( size_t i = 0 ; i < AS_STRING ( asString ) - > length ; + + i ) {
PUSH_CHAR ( AS_CSTRING ( asString ) [ i ] ) ;
}
krk_pop ( ) ;
}
} else if ( * c = = ' } ' ) {
if ( ! AT_END ( ) & & c [ 1 ] = = ' } ' ) {
PUSH_CHAR ( ' } ' ) ;
i + + ; c + + ; /* Skip both */
continue ;
} else {
errorStr = " Single } found. " ;
goto _formatError ;
}
} else {
PUSH_CHAR ( * c ) ;
}
}
KrkValue out = OBJECT_VAL ( krk_copyString ( stringBytes , stringLength ) ) ;
free ( workSpace ) ;
FREE_ARRAY ( char , stringBytes , stringCapacity ) ;
return out ;
_formatError :
krk_runtimeError ( vm . exceptions . typeError , " Error parsing format string: %s " , errorStr ) ;
goto _freeAndDone ;
_formatSwitchedNumbering :
krk_runtimeError ( vm . exceptions . valueError , " Can not switch from automatic indexing to manual indexing " ) ;
goto _freeAndDone ;
_formatOutOfRange :
krk_runtimeError ( vm . exceptions . indexError , " Positional index out of range: %d " , erroneousIndex ) ;
goto _freeAndDone ;
_formatKeyError :
/* which one? */
krk_runtimeError ( vm . exceptions . keyError , " '%s' " , erroneousField ) ;
goto _freeAndDone ;
_freeAndDone :
FREE_ARRAY ( char , stringBytes , stringCapacity ) ;
free ( workSpace ) ;
return NONE_VAL ( ) ;
}
2021-01-04 06:01:17 +03:00
/* str.join(list) */
static KrkValue _string_join ( int argc , KrkValue argv [ ] , int hasKw ) {
if ( ! IS_STRING ( argv [ 0 ] ) ) return NONE_VAL ( ) ;
KrkString * self = AS_STRING ( argv [ 0 ] ) ;
if ( hasKw ) {
krk_runtimeError ( vm . exceptions . argumentError , " str.join() does not take keyword arguments " ) ;
return NONE_VAL ( ) ;
}
if ( argc < 2 ) {
krk_runtimeError ( vm . exceptions . argumentError , " str.join(): expected exactly one argument " ) ;
return NONE_VAL ( ) ;
}
2021-01-05 03:30:23 +03:00
/* TODO: Support any object with an __iter__ - kinda need an internal method to do that well. */
2021-01-05 05:39:20 +03:00
if ( ! IS_INSTANCE ( argv [ 1 ] ) | | ! AS_INSTANCE ( argv [ 1 ] ) - > _internal | | ! ( ( KrkObj * ) AS_INSTANCE ( argv [ 1 ] ) - > _internal ) - > type = = OBJ_FUNCTION ) {
2021-01-05 03:30:23 +03:00
krk_runtimeError ( vm . exceptions . typeError , " *expresssion value is not a list. " ) ;
2021-01-04 06:01:17 +03:00
return NONE_VAL ( ) ;
}
2021-01-05 03:30:23 +03:00
KrkValue _list_internal = OBJECT_VAL ( AS_INSTANCE ( argv [ 1 ] ) - > _internal ) ;
2021-01-04 06:01:17 +03:00
const char * errorStr = NULL ;
size_t stringCapacity = 0 ;
size_t stringLength = 0 ;
char * stringBytes = 0 ;
for ( size_t i = 0 ; i < AS_LIST ( _list_internal ) - > count ; + + i ) {
KrkValue value = AS_LIST ( _list_internal ) - > values [ i ] ;
if ( ! IS_STRING ( AS_LIST ( _list_internal ) - > values [ i ] ) ) {
errorStr = krk_typeName ( value ) ;
goto _expectedString ;
}
krk_push ( value ) ;
if ( i > 0 ) {
for ( size_t j = 0 ; j < self - > length ; + + j ) {
PUSH_CHAR ( self - > chars [ j ] ) ;
}
}
for ( size_t j = 0 ; j < AS_STRING ( value ) - > length ; + + j ) {
PUSH_CHAR ( AS_STRING ( value ) - > chars [ j ] ) ;
}
krk_pop ( ) ;
}
KrkValue out = OBJECT_VAL ( krk_copyString ( stringBytes , stringLength ) ) ;
FREE_ARRAY ( char , stringBytes , stringCapacity ) ;
return out ;
_expectedString :
krk_runtimeError ( vm . exceptions . typeError , " Expected string, got %s. " , errorStr ) ;
FREE_ARRAY ( char , stringBytes , stringCapacity ) ;
return NONE_VAL ( ) ;
}
2021-01-04 06:47:57 +03:00
static int isWhitespace ( char c ) {
return ( c = = ' ' | | c = = ' \t ' | | c = = ' \n ' | | c = = ' \r ' ) ;
}
static int substringMatch ( const char * haystack , size_t haystackLen , const char * needle , size_t needleLength ) {
if ( haystackLen < needleLength ) return 0 ;
for ( size_t i = 0 ; i < needleLength ; + + i ) {
if ( haystack [ i ] ! = needle [ i ] ) return 0 ;
}
return 1 ;
}
2021-01-04 08:03:19 +03:00
/* str.__contains__ */
static KrkValue _string_contains ( int argc , KrkValue argv [ ] ) {
if ( argc < 2 ) {
krk_runtimeError ( vm . exceptions . argumentError , " __contains__ expects an argument " ) ;
return NONE_VAL ( ) ;
}
if ( ! IS_STRING ( argv [ 0 ] ) | | ! IS_STRING ( argv [ 1 ] ) ) return BOOLEAN_VAL ( 0 ) ;
for ( size_t i = 0 ; i < AS_STRING ( argv [ 0 ] ) - > length ; + + i ) {
if ( substringMatch ( AS_CSTRING ( argv [ 0 ] ) + i , AS_STRING ( argv [ 0 ] ) - > length - i , AS_CSTRING ( argv [ 1 ] ) , AS_STRING ( argv [ 1 ] ) - > length ) ) {
return BOOLEAN_VAL ( 1 ) ;
}
}
return BOOLEAN_VAL ( 0 ) ;
}
2021-01-04 06:47:57 +03:00
/* str.split() */
static KrkValue _string_split ( int argc , KrkValue argv [ ] , int hasKw ) {
if ( ! IS_STRING ( argv [ 0 ] ) ) return NONE_VAL ( ) ;
KrkString * self = AS_STRING ( argv [ 0 ] ) ;
if ( argc > 1 ) {
if ( ! IS_STRING ( argv [ 1 ] ) ) {
krk_runtimeError ( vm . exceptions . typeError , " Expected separator to be a string " ) ;
return NONE_VAL ( ) ;
} else if ( AS_STRING ( argv [ 1 ] ) - > length = = 0 ) {
krk_runtimeError ( vm . exceptions . valueError , " Empty separator " ) ;
return NONE_VAL ( ) ;
}
if ( argc > 2 & & ! IS_INTEGER ( argv [ 2 ] ) ) {
krk_runtimeError ( vm . exceptions . typeError , " Expected maxsplit to be an integer. " ) ;
} else if ( argc > 2 & & AS_INTEGER ( argv [ 2 ] ) = = 0 ) {
return argv [ 0 ] ;
}
}
KrkValue myList = krk_list_of ( 0 , NULL ) ;
krk_push ( myList ) ;
2021-01-05 03:30:23 +03:00
KrkValue _list_internal = OBJECT_VAL ( AS_INSTANCE ( myList ) - > _internal ) ;
2021-01-04 06:47:57 +03:00
size_t i = 0 ;
char * c = self - > chars ;
size_t count = 0 ;
if ( argc < 2 ) {
while ( i ! = self - > length ) {
while ( i ! = self - > length & & isWhitespace ( * c ) ) {
i + + ; c + + ;
}
if ( i ! = self - > length ) {
size_t stringCapacity = 0 ;
size_t stringLength = 0 ;
char * stringBytes = NULL ;
while ( i ! = self - > length & & ! isWhitespace ( * c ) ) {
PUSH_CHAR ( * c ) ;
i + + ; c + + ;
}
2021-01-04 17:33:43 +03:00
KrkValue tmp = OBJECT_VAL ( krk_copyString ( stringBytes , stringLength ) ) ;
krk_push ( tmp ) ;
krk_writeValueArray ( AS_LIST ( _list_internal ) , tmp ) ;
krk_pop ( ) ;
2021-01-04 06:47:57 +03:00
FREE_ARRAY ( char , stringBytes , stringCapacity ) ;
#if 0
/* Need to parse kwargs to support this */
if ( argc > 2 & & i ! = self - > length & & count > = ( size_t ) AS_INTEGER ( argv [ 2 ] ) ) {
size_t stringCapacity = 0 ;
size_t stringLength = 0 ;
char * stringBytes = NULL ;
while ( i ! = self - > length ) {
PUSH_CHAR ( * c ) ;
i + + ; c + + ;
}
krk_writeValueArray ( AS_LIST ( _list_internal ) , OBJECT_VAL ( krk_copyString ( stringBytes , stringLength ) ) ) ;
if ( stringBytes ) FREE_ARRAY ( char , stringBytes , stringCapacity ) ;
break ;
}
# endif
}
}
} else {
while ( i ! = self - > length ) {
size_t stringCapacity = 0 ;
size_t stringLength = 0 ;
char * stringBytes = NULL ;
while ( i ! = self - > length & & ! substringMatch ( c , self - > length - i , AS_STRING ( argv [ 1 ] ) - > chars , AS_STRING ( argv [ 1 ] ) - > length ) ) {
PUSH_CHAR ( * c ) ;
i + + ; c + + ;
}
2021-01-04 17:33:43 +03:00
KrkValue tmp = OBJECT_VAL ( krk_copyString ( stringBytes , stringLength ) ) ;
krk_push ( tmp ) ;
krk_writeValueArray ( AS_LIST ( _list_internal ) , tmp ) ;
krk_pop ( ) ;
2021-01-04 06:47:57 +03:00
if ( substringMatch ( c , self - > length - i , AS_STRING ( argv [ 1 ] ) - > chars , AS_STRING ( argv [ 1 ] ) - > length ) ) {
i + = AS_STRING ( argv [ 1 ] ) - > length ;
c + = AS_STRING ( argv [ 1 ] ) - > length ;
count + + ;
if ( argc > 2 & & count = = ( size_t ) AS_INTEGER ( argv [ 2 ] ) ) {
size_t stringCapacity = 0 ;
size_t stringLength = 0 ;
char * stringBytes = NULL ;
while ( i ! = self - > length ) {
PUSH_CHAR ( * c ) ;
i + + ; c + + ;
}
2021-01-04 17:33:43 +03:00
KrkValue tmp = OBJECT_VAL ( krk_copyString ( stringBytes , stringLength ) ) ;
krk_push ( tmp ) ;
krk_writeValueArray ( AS_LIST ( _list_internal ) , tmp ) ;
krk_pop ( ) ;
2021-01-04 06:47:57 +03:00
if ( stringBytes ) FREE_ARRAY ( char , stringBytes , stringCapacity ) ;
break ;
}
if ( i = = self - > length ) {
2021-01-04 17:33:43 +03:00
KrkValue tmp = OBJECT_VAL ( S ( " " ) ) ;
krk_push ( tmp ) ;
krk_writeValueArray ( AS_LIST ( _list_internal ) , tmp ) ;
krk_pop ( ) ;
2021-01-04 06:47:57 +03:00
}
}
}
}
krk_pop ( ) ;
return myList ;
}
# undef PUSH_CHAR
2021-01-04 10:21:59 +03:00
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 _string_to_int ( argc - 1 , & argv [ 1 ] ) ;
if ( IS_FLOATING ( argv [ 1 ] ) ) return INTEGER_VAL ( AS_FLOATING ( argv [ 1 ] ) ) ;
if ( IS_BOOLEAN ( argv [ 1 ] ) ) return INTEGER_VAL ( AS_BOOLEAN ( argv [ 1 ] ) ) ;
krk_runtimeError ( vm . exceptions . typeError , " int() argument must be a string or a number, not '%s' " , krk_typeName ( argv [ 1 ] ) ) ;
return NONE_VAL ( ) ;
}
2021-01-02 06:21:11 +03:00
/* function.__doc__ */
2021-01-01 10:01:58 +03:00
static KrkValue _closure_get_doc ( int argc , KrkValue argv [ ] ) {
if ( ! IS_CLOSURE ( argv [ 0 ] ) ) return NONE_VAL ( ) ;
return AS_CLOSURE ( argv [ 0 ] ) - > function - > docstring ? OBJECT_VAL ( AS_CLOSURE ( argv [ 0 ] ) - > function - > docstring ) : NONE_VAL ( ) ;
}
2021-01-02 06:21:11 +03:00
/* method.__doc__ */
2021-01-01 10:01:58 +03:00
static KrkValue _bound_get_doc ( int argc , KrkValue argv [ ] ) {
KrkBoundMethod * boundMethod = AS_BOUND_METHOD ( argv [ 0 ] ) ;
return _closure_get_doc ( 1 , ( KrkValue [ ] ) { OBJECT_VAL ( boundMethod - > method ) } ) ;
}
2021-01-02 06:21:11 +03:00
/* Check for and return the name of a native function as a string object */
2021-01-01 10:01:58 +03:00
static KrkValue nativeFunctionName ( KrkValue func ) {
const char * string = ( ( KrkNative * ) AS_OBJECT ( func ) ) - > name ;
size_t len = strlen ( string ) ;
return OBJECT_VAL ( krk_copyString ( string , len ) ) ;
}
2021-01-02 06:21:11 +03:00
/* function.__name__ */
2021-01-01 10:01:58 +03:00
static KrkValue _closure_get_name ( int argc , KrkValue argv [ ] ) {
if ( ! IS_CLOSURE ( argv [ 0 ] ) ) return nativeFunctionName ( argv [ 0 ] ) ;
return AS_CLOSURE ( argv [ 0 ] ) - > function - > name ? OBJECT_VAL ( AS_CLOSURE ( argv [ 0 ] ) - > function - > name ) : OBJECT_VAL ( S ( " " ) ) ;
}
2021-01-02 06:21:11 +03:00
/* method.__name__ */
2021-01-01 10:01:58 +03:00
static KrkValue _bound_get_name ( int argc , KrkValue argv [ ] ) {
KrkBoundMethod * boundMethod = AS_BOUND_METHOD ( argv [ 0 ] ) ;
return _closure_get_name ( 1 , ( KrkValue [ ] ) { OBJECT_VAL ( boundMethod - > method ) } ) ;
}
2021-01-02 06:21:11 +03:00
/* function.__str__ / function.__repr__ */
2021-01-01 10:01:58 +03:00
static KrkValue _closure_str ( int argc , KrkValue argv [ ] ) {
KrkValue s = _closure_get_name ( argc , argv ) ;
krk_push ( s ) ;
size_t len = AS_STRING ( s ) - > length + sizeof ( " <function > " ) ;
char * tmp = malloc ( len ) ;
sprintf ( tmp , " <function %s> " , AS_CSTRING ( s ) ) ;
2021-01-01 14:52:18 +03:00
s = OBJECT_VAL ( krk_copyString ( tmp , len - 1 ) ) ;
2021-01-01 10:01:58 +03:00
free ( tmp ) ;
krk_pop ( ) ;
return s ;
}
2021-01-02 06:21:11 +03:00
/* method.__str__ / method.__repr__ */
2021-01-01 10:01:58 +03:00
static KrkValue _bound_str ( int argc , KrkValue argv [ ] ) {
KrkValue s = _bound_get_name ( argc , argv ) ;
krk_push ( s ) ;
2021-01-05 09:36:09 +03:00
const char * typeName = krk_typeName ( AS_BOUND_METHOD ( argv [ 0 ] ) - > receiver ) ;
size_t len = AS_STRING ( s ) - > length + sizeof ( " <method > " ) + strlen ( typeName ) + 1 ;
2021-01-01 10:01:58 +03:00
char * tmp = malloc ( len ) ;
2021-01-05 09:36:09 +03:00
sprintf ( tmp , " <method %s.%s> " , typeName , AS_CSTRING ( s ) ) ;
2021-01-01 14:52:18 +03:00
s = OBJECT_VAL ( krk_copyString ( tmp , len - 1 ) ) ;
2021-01-01 10:01:58 +03:00
free ( tmp ) ;
krk_pop ( ) ;
return s ;
}
2021-01-02 06:21:11 +03:00
/* function.__file__ */
2021-01-01 10:01:58 +03:00
static KrkValue _closure_get_file ( int argc , KrkValue argv [ ] ) {
if ( ! IS_CLOSURE ( argv [ 0 ] ) ) return OBJECT_VAL ( S ( " <builtin> " ) ) ;
return AS_CLOSURE ( argv [ 0 ] ) - > function - > chunk . filename ? OBJECT_VAL ( AS_CLOSURE ( argv [ 0 ] ) - > function - > chunk . filename ) : OBJECT_VAL ( S ( " " ) ) ;
}
2021-01-02 06:21:11 +03:00
/* method.__file__ */
2021-01-01 10:01:58 +03:00
static KrkValue _bound_get_file ( int argc , KrkValue argv [ ] ) {
KrkBoundMethod * boundMethod = AS_BOUND_METHOD ( argv [ 0 ] ) ;
return _closure_get_file ( 1 , ( KrkValue [ ] ) { OBJECT_VAL ( boundMethod - > method ) } ) ;
}
2021-01-05 07:37:26 +03:00
/* function.__args__ */
static KrkValue _closure_get_argnames ( int argc , KrkValue argv [ ] ) {
if ( ! IS_CLOSURE ( argv [ 0 ] ) ) return OBJECT_VAL ( krk_newTuple ( 0 ) ) ;
KrkFunction * self = AS_CLOSURE ( argv [ 0 ] ) - > function ;
KrkTuple * tuple = krk_newTuple ( self - > requiredArgs + self - > keywordArgs ) ;
krk_push ( OBJECT_VAL ( tuple ) ) ;
for ( short i = 0 ; i < self - > requiredArgs ; + + i ) {
tuple - > values . values [ tuple - > values . count + + ] = self - > requiredArgNames . values [ i ] ;
}
for ( short i = 0 ; i < self - > keywordArgs ; + + i ) {
tuple - > values . values [ tuple - > values . count + + ] = self - > keywordArgNames . values [ i ] ;
}
krk_pop ( ) ;
return OBJECT_VAL ( tuple ) ;
}
static KrkValue _bound_get_argnames ( int argc , KrkValue argv [ ] ) {
KrkBoundMethod * boundMethod = AS_BOUND_METHOD ( argv [ 0 ] ) ;
return _closure_get_argnames ( 1 , ( KrkValue [ ] ) { OBJECT_VAL ( boundMethod - > method ) } ) ;
}
2021-01-05 05:38:11 +03:00
static KrkValue _tuple_init ( int argc , KrkValue argv [ ] ) {
2021-01-05 06:03:21 +03:00
krk_runtimeError ( vm . exceptions . typeError , " tuple() initializier unsupported " ) ;
return NONE_VAL ( ) ;
}
/* tuple creator */
static KrkValue _tuple_of ( int argc , KrkValue argv [ ] ) {
KrkTuple * self = krk_newTuple ( argc ) ;
2021-01-05 05:38:11 +03:00
krk_push ( OBJECT_VAL ( self ) ) ;
2021-01-05 06:03:21 +03:00
for ( size_t i = 0 ; i < ( size_t ) argc ; + + i ) {
2021-01-05 05:38:11 +03:00
self - > values . values [ self - > values . count + + ] = argv [ i ] ;
}
krk_pop ( ) ;
return OBJECT_VAL ( self ) ;
}
2021-01-05 05:42:08 +03:00
static KrkValue _tuple_contains ( int argc , KrkValue argv [ ] ) {
if ( argc ! = 2 ) {
krk_runtimeError ( vm . exceptions . argumentError , " tuple.__contains__ expects one argument " ) ;
return NONE_VAL ( ) ;
}
KrkTuple * self = AS_TUPLE ( argv [ 0 ] ) ;
for ( size_t i = 0 ; i < self - > values . count ; + + i ) {
if ( krk_valuesEqual ( self - > values . values [ i ] , argv [ 1 ] ) ) return BOOLEAN_VAL ( 1 ) ;
}
return BOOLEAN_VAL ( 0 ) ;
}
2021-01-05 05:38:11 +03:00
/* tuple.__len__ */
static KrkValue _tuple_len ( int argc , KrkValue argv [ ] ) {
if ( argc ! = 1 ) {
krk_runtimeError ( vm . exceptions . argumentError , " tuple.__len__ does not expect arguments " ) ;
return NONE_VAL ( ) ;
}
KrkTuple * self = AS_TUPLE ( argv [ 0 ] ) ;
return INTEGER_VAL ( self - > values . count ) ;
}
/* tuple.__get__ */
static KrkValue _tuple_get ( int argc , KrkValue argv [ ] ) {
if ( argc ! = 2 ) {
krk_runtimeError ( vm . exceptions . argumentError , " tuple.__get__ expects one argument " ) ;
return NONE_VAL ( ) ;
} else if ( ! IS_INTEGER ( argv [ 1 ] ) ) {
krk_runtimeError ( vm . exceptions . typeError , " can not index by '%s', expected integer " , krk_typeName ( argv [ 1 ] ) ) ;
return NONE_VAL ( ) ;
}
KrkTuple * tuple = AS_TUPLE ( argv [ 0 ] ) ;
long index = AS_INTEGER ( argv [ 1 ] ) ;
if ( index < 0 ) index + = tuple - > values . count ;
if ( index < 0 | | index > = ( long ) tuple - > values . count ) {
krk_runtimeError ( vm . exceptions . indexError , " tuple index out of range " ) ;
return NONE_VAL ( ) ;
}
return tuple - > values . values [ index ] ;
}
static KrkValue _tuple_repr ( int argc , KrkValue argv [ ] ) {
if ( argc ! = 1 ) {
krk_runtimeError ( vm . exceptions . argumentError , " tuple.__repr__ does not expect arguments " ) ;
return NONE_VAL ( ) ;
}
KrkTuple * tuple = AS_TUPLE ( argv [ 0 ] ) ;
if ( tuple - > inrepr ) return OBJECT_VAL ( S ( " (...) " ) ) ;
tuple - > inrepr = 1 ;
/* String building time. */
krk_push ( OBJECT_VAL ( S ( " ( " ) ) ) ;
for ( size_t i = 0 ; i < tuple - > values . count ; + + i ) {
krk_push ( tuple - > values . values [ i ] ) ;
2021-01-05 07:37:39 +03:00
krk_push ( krk_callSimple ( OBJECT_VAL ( AS_CLASS ( krk_typeOf ( 1 , & tuple - > values . values [ i ] ) ) - > _reprer ) , 1 , 0 ) ) ;
2021-01-05 05:38:11 +03:00
addObjects ( ) ; /* pops both, pushes result */
if ( i ! = tuple - > values . count - 1 ) {
krk_push ( OBJECT_VAL ( S ( " , " ) ) ) ;
addObjects ( ) ;
}
}
2021-01-05 06:03:21 +03:00
if ( tuple - > values . count = = 1 ) {
krk_push ( OBJECT_VAL ( S ( " , " ) ) ) ;
addObjects ( ) ;
}
2021-01-05 05:38:11 +03:00
krk_push ( OBJECT_VAL ( S ( " ) " ) ) ) ;
addObjects ( ) ;
tuple - > inrepr = 0 ;
return krk_pop ( ) ;
}
2021-01-02 06:21:11 +03:00
/**
* 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 .
*/
2021-01-01 10:01:58 +03:00
static KrkValue _strBase ( int argc , KrkValue argv [ ] ) {
KrkClass * type = AS_CLASS ( krk_typeOf ( 1 , ( KrkValue [ ] ) { 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 ;
}
2021-01-02 06:21:11 +03:00
/**
* str . __repr__ ( )
*
* Strings are special because __str__ should do nothing but __repr__
* should escape characters like quotes .
*/
2021-01-01 10:01:58 +03:00
static KrkValue _repr_str ( int argc , KrkValue argv [ ] ) {
char * str = malloc ( 3 + AS_STRING ( argv [ 0 ] ) - > length * 2 ) ;
char * tmp = str ;
2021-01-03 08:09:45 +03:00
* ( tmp + + ) = ' \' ' ;
2021-01-01 10:01:58 +03:00
for ( char * c = AS_CSTRING ( argv [ 0 ] ) ; * c ; + + c ) {
switch ( * c ) {
/* XXX: Other non-printables should probably be escaped as well. */
case ' \n ' : * ( tmp + + ) = ' \\ ' ; * ( tmp + + ) = ' n ' ; break ;
case ' \r ' : * ( tmp + + ) = ' \\ ' ; * ( tmp + + ) = ' r ' ; break ;
case ' \t ' : * ( tmp + + ) = ' \\ ' ; * ( tmp + + ) = ' t ' ; break ;
2021-01-03 08:09:45 +03:00
case ' \' ' : * ( tmp + + ) = ' \\ ' ; * ( tmp + + ) = ' \' ' ; break ;
2021-01-01 10:01:58 +03:00
case 27 : * ( tmp + + ) = ' \\ ' ; * ( tmp + + ) = ' [ ' ; break ;
default : * ( tmp + + ) = * c ; break ;
}
}
2021-01-03 08:09:45 +03:00
* ( tmp + + ) = ' \' ' ;
2021-01-01 10:01:58 +03:00
* ( tmp + + ) = ' \0 ' ;
KrkString * out = krk_copyString ( str , tmp - str - 1 ) ;
free ( str ) ;
return OBJECT_VAL ( out ) ;
}
2021-01-02 06:21:11 +03:00
/**
* 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 .
*/
2021-01-01 10:01:58 +03:00
static KrkValue _int_to_str ( int argc , KrkValue argv [ ] ) {
char tmp [ 100 ] ;
size_t l = sprintf ( tmp , " %ld " , ( long ) AS_INTEGER ( argv [ 0 ] ) ) ;
return OBJECT_VAL ( krk_copyString ( tmp , l ) ) ;
}
2021-01-02 06:21:11 +03:00
/**
* float . __str__ ( )
*/
2021-01-01 10:01:58 +03:00
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 ) ) ;
}
2021-01-02 06:21:11 +03:00
/**
* bool . __str__ ( ) - > " True " or " False "
*/
2021-01-01 10:01:58 +03:00
static KrkValue _bool_to_str ( int argc , KrkValue argv [ ] ) {
return OBJECT_VAL ( ( AS_BOOLEAN ( argv [ 0 ] ) ? S ( " True " ) : S ( " False " ) ) ) ;
}
2021-01-04 10:21:59 +03:00
/**
* Inverse of truthiness .
*
* None , False , and 0 are all " falsey " , meaning they will trip JUMP_IF_FALSE
* instructions / not trip JUMP_IF_TRUE instructions .
*
* 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 ) {
return IS_NONE ( value ) | | ( IS_BOOLEAN ( value ) & & ! AS_BOOLEAN ( value ) ) | |
( IS_INTEGER ( value ) & & ! AS_INTEGER ( value ) ) ;
/* Objects in the future: */
/* IS_STRING && length == 0; IS_ARRAY && length == 0; IS_INSTANCE && __bool__ returns 0... */
}
static KrkValue _bool_init ( int argc , KrkValue argv [ ] ) {
if ( argc < 2 ) return BOOLEAN_VAL ( 0 ) ;
if ( argc > 2 ) {
krk_runtimeError ( vm . exceptions . argumentError , " bool() takes at most 1 argument " ) ;
return NONE_VAL ( ) ;
}
return BOOLEAN_VAL ( isFalsey ( argv [ 1 ] ) ) ;
}
2021-01-02 06:21:11 +03:00
/**
* None . __str__ ( ) - > " None "
*/
2021-01-01 10:01:58 +03:00
static KrkValue _none_to_str ( int argc , KrkValue argv [ ] ) {
return OBJECT_VAL ( S ( " None " ) ) ;
}
2021-01-04 10:21:59 +03:00
static KrkValue _len ( int argc , KrkValue argv [ ] ) {
if ( argc ! = 1 ) {
krk_runtimeError ( vm . exceptions . argumentError , " len() takes exactly one argument " ) ;
return NONE_VAL ( ) ;
}
if ( ! IS_OBJECT ( argv [ 0 ] ) ) {
krk_runtimeError ( vm . exceptions . typeError , " object of type '%s' has no len() " , krk_typeName ( argv [ 0 ] ) ) ;
return NONE_VAL ( ) ;
}
if ( IS_STRING ( argv [ 0 ] ) ) return INTEGER_VAL ( AS_STRING ( argv [ 0 ] ) - > length ) ;
krk_push ( argv [ 0 ] ) ;
if ( ! krk_bindMethod ( AS_CLASS ( krk_typeOf ( 1 , & argv [ 0 ] ) ) , AS_STRING ( vm . specialMethodNames [ METHOD_LEN ] ) ) ) {
krk_runtimeError ( vm . exceptions . typeError , " object of type '%s' has no len() " , krk_typeName ( argv [ 0 ] ) ) ;
return NONE_VAL ( ) ;
}
2021-01-04 12:15:17 +03:00
return krk_callSimple ( krk_peek ( 0 ) , 0 , 1 ) ;
2021-01-04 10:21:59 +03:00
}
static KrkValue _repr ( int argc , KrkValue argv [ ] ) {
if ( argc ! = 1 ) {
krk_runtimeError ( vm . exceptions . argumentError , " repr() takes exactly one argument " ) ;
return NONE_VAL ( ) ;
}
krk_push ( argv [ 0 ] ) ;
if ( ! krk_bindMethod ( AS_CLASS ( krk_typeOf ( 1 , & argv [ 0 ] ) ) , AS_STRING ( vm . specialMethodNames [ METHOD_REPR ] ) ) ) {
krk_runtimeError ( vm . exceptions . typeError , " internal error " ) ;
return NONE_VAL ( ) ;
}
2021-01-04 12:15:17 +03:00
return krk_callSimple ( krk_peek ( 0 ) , 0 , 1 ) ;
2021-01-04 10:21:59 +03:00
}
2021-01-04 13:34:56 +03:00
static KrkValue _striter_init ( int argc , KrkValue argv [ ] ) {
if ( ! IS_INSTANCE ( argv [ 0 ] ) | | AS_INSTANCE ( argv [ 0 ] ) - > _class ! = vm . baseClasses . striteratorClass ) {
krk_runtimeError ( vm . exceptions . typeError , " Tried to call striterator.__init__() on something not a str iterator " ) ;
}
if ( argc < 2 | | ! IS_STRING ( argv [ 1 ] ) ) {
krk_runtimeError ( vm . exceptions . argumentError , " Expected a str. " ) ;
}
KrkInstance * self = AS_INSTANCE ( argv [ 0 ] ) ;
krk_push ( argv [ 0 ] ) ;
krk_attachNamedValue ( & self - > fields , " s " , argv [ 1 ] ) ;
krk_attachNamedValue ( & self - > fields , " i " , INTEGER_VAL ( 0 ) ) ;
krk_pop ( ) ;
return argv [ 0 ] ;
}
static KrkValue _striter_call ( int argc , KrkValue argv [ ] ) {
if ( ! IS_INSTANCE ( argv [ 0 ] ) | | AS_INSTANCE ( argv [ 0 ] ) - > _class ! = vm . baseClasses . striteratorClass ) {
krk_runtimeError ( vm . exceptions . typeError , " Tried to call striterator.__call__() on something not a str iterator " ) ;
}
KrkInstance * self = AS_INSTANCE ( argv [ 0 ] ) ;
KrkValue _str ;
KrkValue _counter ;
const char * errorStr = NULL ;
if ( ! krk_tableGet ( & self - > fields , OBJECT_VAL ( S ( " s " ) ) , & _str ) ) {
errorStr = " no str pointer " ;
goto _corrupt ;
}
if ( ! krk_tableGet ( & self - > fields , OBJECT_VAL ( S ( " i " ) ) , & _counter ) ) {
errorStr = " no index " ;
goto _corrupt ;
}
if ( ( size_t ) AS_INTEGER ( _counter ) > = AS_STRING ( _str ) - > length ) {
return argv [ 0 ] ;
} else {
krk_attachNamedValue ( & self - > fields , " i " , INTEGER_VAL ( AS_INTEGER ( _counter ) + 1 ) ) ;
return OBJECT_VAL ( krk_copyString ( & AS_CSTRING ( _str ) [ AS_INTEGER ( _counter ) ] , 1 ) ) ;
}
_corrupt :
krk_runtimeError ( vm . exceptions . typeError , " Corrupt str iterator: %s " , errorStr ) ;
return NONE_VAL ( ) ;
}
static KrkValue _str_iter ( int argc , KrkValue argv [ ] ) {
KrkInstance * output = krk_newInstance ( vm . baseClasses . striteratorClass ) ;
krk_push ( OBJECT_VAL ( output ) ) ;
_striter_init ( 3 , ( KrkValue [ ] ) { krk_peek ( 0 ) , argv [ 0 ] } ) ;
krk_pop ( ) ;
return OBJECT_VAL ( output ) ;
}
2021-01-04 11:29:06 +03:00
static KrkValue _listiter_init ( int argc , KrkValue argv [ ] ) {
2021-01-04 13:07:39 +03:00
if ( ! IS_INSTANCE ( argv [ 0 ] ) | | AS_INSTANCE ( argv [ 0 ] ) - > _class ! = vm . baseClasses . listiteratorClass ) {
krk_runtimeError ( vm . exceptions . typeError , " Tried to call listiterator.__init__() on something not a list iterator " ) ;
}
if ( argc < 2 | | ! IS_INSTANCE ( argv [ 1 ] ) ) {
krk_runtimeError ( vm . exceptions . argumentError , " Expected a list. " ) ;
}
2021-01-04 11:29:06 +03:00
KrkInstance * self = AS_INSTANCE ( argv [ 0 ] ) ;
KrkValue _list = argv [ 1 ] ;
2021-01-04 13:07:39 +03:00
krk_push ( argv [ 0 ] ) ;
krk_attachNamedValue ( & self - > fields , " l " , _list ) ;
krk_attachNamedValue ( & self - > fields , " i " , INTEGER_VAL ( 0 ) ) ;
krk_pop ( ) ;
2021-01-04 11:29:06 +03:00
return argv [ 0 ] ;
}
static KrkValue _listiter_call ( int argc , KrkValue argv [ ] ) {
2021-01-04 13:07:39 +03:00
if ( ! IS_INSTANCE ( argv [ 0 ] ) | | AS_INSTANCE ( argv [ 0 ] ) - > _class ! = vm . baseClasses . listiteratorClass ) {
krk_runtimeError ( vm . exceptions . typeError , " Tried to call listiterator.__call__() on something not a list iterator " ) ;
}
2021-01-04 11:29:06 +03:00
KrkInstance * self = AS_INSTANCE ( argv [ 0 ] ) ;
KrkValue _list ;
KrkValue _counter ;
2021-01-04 13:07:39 +03:00
const char * errorStr = NULL ;
2021-01-04 11:29:06 +03:00
2021-01-04 13:07:39 +03:00
if ( ! krk_tableGet ( & self - > fields , OBJECT_VAL ( S ( " l " ) ) , & _list ) ) {
errorStr = " no list pointer " ;
goto _corrupt ;
}
if ( ! krk_tableGet ( & self - > fields , OBJECT_VAL ( S ( " i " ) ) , & _counter ) ) {
errorStr = " no index " ;
goto _corrupt ;
}
2021-01-05 03:30:23 +03:00
KrkValue _list_internal = OBJECT_VAL ( AS_INSTANCE ( _list ) - > _internal ) ;
2021-01-04 11:29:06 +03:00
if ( ( size_t ) AS_INTEGER ( _counter ) > = AS_LIST ( _list_internal ) - > count ) {
return argv [ 0 ] ;
} else {
2021-01-04 13:07:39 +03:00
krk_attachNamedValue ( & self - > fields , " i " , INTEGER_VAL ( AS_INTEGER ( _counter ) + 1 ) ) ;
2021-01-04 11:29:06 +03:00
return AS_LIST ( _list_internal ) - > values [ AS_INTEGER ( _counter ) ] ;
}
_corrupt :
2021-01-04 13:07:39 +03:00
krk_runtimeError ( vm . exceptions . typeError , " Corrupt list iterator: %s " , errorStr ) ;
2021-01-04 11:29:06 +03:00
return NONE_VAL ( ) ;
}
static KrkValue _range_init ( int argc , KrkValue argv [ ] ) {
KrkInstance * self = AS_INSTANCE ( argv [ 0 ] ) ;
if ( argc < 2 | | argc > 3 ) {
krk_runtimeError ( vm . exceptions . argumentError , " range expected at least 1 and and at most 2 arguments " ) ;
return NONE_VAL ( ) ;
}
KrkValue min = INTEGER_VAL ( 0 ) ;
KrkValue max ;
if ( argc = = 2 ) {
max = argv [ 1 ] ;
} else {
min = argv [ 1 ] ;
max = argv [ 2 ] ;
}
if ( ! IS_INTEGER ( min ) ) {
krk_runtimeError ( vm . exceptions . typeError , " range: expected int, but got '%s' " , krk_typeName ( min ) ) ;
return NONE_VAL ( ) ;
}
if ( ! IS_INTEGER ( max ) ) {
krk_runtimeError ( vm . exceptions . typeError , " range: expected int, but got '%s' " , krk_typeName ( max ) ) ;
return NONE_VAL ( ) ;
}
2021-01-05 05:39:20 +03:00
krk_push ( OBJECT_VAL ( self ) ) ;
2021-01-04 11:29:06 +03:00
/* Add them to ourselves */
2021-01-05 05:39:20 +03:00
KrkTuple * myTuple = krk_newTuple ( 2 ) ;
krk_push ( OBJECT_VAL ( myTuple ) ) ;
myTuple - > values . values [ 0 ] = min ;
myTuple - > values . values [ 1 ] = max ;
myTuple - > values . count = 2 ;
krk_attachNamedObject ( & self - > fields , " _tuple " , ( KrkObj * ) myTuple ) ;
self - > _internal = myTuple ;
krk_pop ( ) ; /* myTuple */
krk_pop ( ) ; /* self */
2021-01-04 11:29:06 +03:00
return argv [ 0 ] ;
}
static KrkValue _range_repr ( int argc , KrkValue argv [ ] ) {
KrkInstance * self = AS_INSTANCE ( argv [ 0 ] ) ;
2021-01-05 05:39:20 +03:00
KrkTuple * myTuple = self - > _internal ;
2021-01-04 11:29:06 +03:00
2021-01-05 05:39:20 +03:00
KrkValue min = myTuple - > values . values [ 0 ] ;
KrkValue max = myTuple - > values . values [ 1 ] ;
2021-01-04 11:29:06 +03:00
krk_push ( OBJECT_VAL ( S ( " range({},{}) " ) ) ) ;
KrkValue output = _string_format ( 3 , ( KrkValue [ ] ) { krk_peek ( 0 ) , min , max } , 0 ) ;
krk_pop ( ) ;
return output ;
}
static KrkValue _rangeiterator_init ( int argc , KrkValue argv [ ] ) {
KrkInstance * self = AS_INSTANCE ( argv [ 0 ] ) ;
2021-01-04 13:07:39 +03:00
krk_push ( argv [ 0 ] ) ;
2021-01-05 05:39:20 +03:00
KrkTuple * myTuple = krk_newTuple ( 2 ) ;
krk_push ( OBJECT_VAL ( myTuple ) ) ;
myTuple - > values . values [ 0 ] = argv [ 1 ] ;
myTuple - > values . values [ 1 ] = argv [ 2 ] ;
myTuple - > values . count = 2 ;
krk_attachNamedObject ( & self - > fields , " _tuple " , ( KrkObj * ) myTuple ) ;
self - > _internal = myTuple ;
krk_pop ( ) ; /* myTuple */
krk_pop ( ) ; /* self */
2021-01-04 11:29:06 +03:00
return argv [ 0 ] ;
}
static KrkValue _rangeiterator_call ( int argc , KrkValue argv [ ] ) {
KrkInstance * self = AS_INSTANCE ( argv [ 0 ] ) ;
2021-01-05 05:39:20 +03:00
KrkTuple * myTuple = self - > _internal ;
KrkValue i = myTuple - > values . values [ 0 ] ;
if ( AS_INTEGER ( i ) > = AS_INTEGER ( myTuple - > values . values [ 1 ] ) ) {
2021-01-04 11:29:06 +03:00
return argv [ 0 ] ;
} else {
2021-01-05 05:39:20 +03:00
myTuple - > values . values [ 0 ] = INTEGER_VAL ( AS_INTEGER ( i ) + 1 ) ;
2021-01-04 11:29:06 +03:00
return i ;
}
}
static KrkValue _range_iter ( int argc , KrkValue argv [ ] ) {
KrkInstance * self = AS_INSTANCE ( argv [ 0 ] ) ;
2021-01-05 05:39:20 +03:00
KrkTuple * myTuple = self - > _internal ;
KrkValue min = myTuple - > values . values [ 0 ] ;
KrkValue max = myTuple - > values . values [ 1 ] ;
2021-01-04 11:29:06 +03:00
KrkInstance * output = krk_newInstance ( vm . baseClasses . rangeiteratorClass ) ;
krk_push ( OBJECT_VAL ( output ) ) ;
_rangeiterator_init ( 3 , ( KrkValue [ ] ) { krk_peek ( 0 ) , min , max } ) ;
krk_pop ( ) ;
return OBJECT_VAL ( output ) ;
}
2020-12-28 14:38:26 +03:00
void krk_initVM ( int flags ) {
vm . flags = flags ;
2020-12-28 16:02:39 +03:00
KRK_PAUSE_GC ( ) ;
2020-12-28 03:08:35 +03:00
2021-01-04 11:47:53 +03:00
krk_resetStack ( ) ;
2020-12-26 03:32:21 +03:00
vm . objects = NULL ;
2020-12-27 09:58:32 +03:00
vm . bytesAllocated = 0 ;
vm . nextGC = 1024 * 1024 ;
vm . grayCount = 0 ;
vm . grayCapacity = 0 ;
vm . grayStack = NULL ;
2020-12-26 10:53:15 +03:00
krk_initTable ( & vm . globals ) ;
2020-12-26 08:33:34 +03:00
krk_initTable ( & vm . strings ) ;
2020-12-27 16:40:35 +03:00
memset ( vm . specialMethodNames , 0 , sizeof ( vm . specialMethodNames ) ) ;
2021-01-02 06:21:11 +03:00
/* To make lookup faster, store these so we can don't have to keep boxing
* and unboxing , copying / hashing etc . */
2020-12-28 13:01:28 +03:00
vm . specialMethodNames [ METHOD_INIT ] = OBJECT_VAL ( S ( " __init__ " ) ) ;
vm . specialMethodNames [ METHOD_STR ] = OBJECT_VAL ( S ( " __str__ " ) ) ;
2021-01-01 10:01:58 +03:00
vm . specialMethodNames [ METHOD_REPR ] = OBJECT_VAL ( S ( " __repr__ " ) ) ;
2020-12-28 13:01:28 +03:00
vm . specialMethodNames [ METHOD_GET ] = OBJECT_VAL ( S ( " __get__ " ) ) ;
vm . specialMethodNames [ METHOD_SET ] = OBJECT_VAL ( S ( " __set__ " ) ) ;
vm . specialMethodNames [ METHOD_CLASS ] = OBJECT_VAL ( S ( " __class__ " ) ) ;
vm . specialMethodNames [ METHOD_NAME ] = OBJECT_VAL ( S ( " __name__ " ) ) ;
vm . specialMethodNames [ METHOD_FILE ] = OBJECT_VAL ( S ( " __file__ " ) ) ;
2020-12-29 10:40:42 +03:00
vm . specialMethodNames [ METHOD_INT ] = OBJECT_VAL ( S ( " __int__ " ) ) ;
vm . specialMethodNames [ METHOD_CHR ] = OBJECT_VAL ( S ( " __chr__ " ) ) ;
2021-01-03 08:09:45 +03:00
vm . specialMethodNames [ METHOD_ORD ] = OBJECT_VAL ( S ( " __ord__ " ) ) ;
2020-12-29 10:40:42 +03:00
vm . specialMethodNames [ METHOD_FLOAT ] = OBJECT_VAL ( S ( " __float__ " ) ) ;
2020-12-30 09:50:26 +03:00
vm . specialMethodNames [ METHOD_LEN ] = OBJECT_VAL ( S ( " __len__ " ) ) ;
2020-12-30 10:59:21 +03:00
vm . specialMethodNames [ METHOD_DOC ] = OBJECT_VAL ( S ( " __doc__ " ) ) ;
2020-12-31 03:15:53 +03:00
vm . specialMethodNames [ METHOD_BASE ] = OBJECT_VAL ( S ( " __base__ " ) ) ;
2021-01-04 10:21:27 +03:00
vm . specialMethodNames [ METHOD_CALL ] = OBJECT_VAL ( S ( " __call__ " ) ) ;
2020-12-31 12:14:20 +03:00
vm . specialMethodNames [ METHOD_GETSLICE ] = OBJECT_VAL ( S ( " __getslice__ " ) ) ;
2021-01-01 14:52:18 +03:00
vm . specialMethodNames [ METHOD_LIST_INT ] = OBJECT_VAL ( S ( " __list " ) ) ;
vm . specialMethodNames [ METHOD_DICT_INT ] = OBJECT_VAL ( S ( " __dict " ) ) ;
2021-01-02 08:28:37 +03:00
vm . specialMethodNames [ METHOD_INREPR ] = OBJECT_VAL ( S ( " __inrepr " ) ) ;
2021-01-05 11:41:32 +03:00
vm . specialMethodNames [ METHOD_EQ ] = OBJECT_VAL ( S ( " __eq__ " ) ) ;
2020-12-28 13:01:28 +03:00
/* Create built-in class `object` */
2021-01-01 06:04:58 +03:00
vm . objectClass = krk_newClass ( S ( " object " ) ) ;
krk_attachNamedObject ( & vm . globals , " object " , ( KrkObj * ) vm . objectClass ) ;
2021-01-01 10:01:58 +03:00
krk_defineNative ( & vm . objectClass - > methods , " :__class__ " , krk_typeOf ) ;
2021-01-01 06:04:58 +03:00
krk_defineNative ( & vm . objectClass - > methods , " .__dir__ " , krk_dirObject ) ;
2021-01-01 10:01:58 +03:00
krk_defineNative ( & vm . objectClass - > methods , " .__str__ " , _strBase ) ;
krk_defineNative ( & vm . objectClass - > methods , " .__repr__ " , _strBase ) ; /* Override if necesary */
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( vm . objectClass ) ;
2020-12-28 13:01:28 +03:00
2021-01-02 06:21:11 +03:00
/* Build a __builtins__ namespace for some extra functions. */
2021-01-01 06:04:58 +03:00
vm . builtins = krk_newInstance ( vm . objectClass ) ;
2020-12-28 13:25:33 +03:00
krk_attachNamedObject ( & vm . globals , " __builtins__ " , ( KrkObj * ) vm . builtins ) ;
2020-12-28 13:01:28 +03:00
2021-01-01 06:04:58 +03:00
/* Add exception classes */
ADD_EXCEPTION_CLASS ( vm . exceptions . baseException , " Exception " , vm . objectClass ) ;
/* base exception class gets an init that takes an optional string */
krk_defineNative ( & vm . exceptions . baseException - > methods , " .__init__ " , krk_initException ) ;
ADD_EXCEPTION_CLASS ( vm . exceptions . typeError , " TypeError " , vm . exceptions . baseException ) ;
ADD_EXCEPTION_CLASS ( vm . exceptions . argumentError , " ArgumentError " , vm . exceptions . baseException ) ;
ADD_EXCEPTION_CLASS ( vm . exceptions . indexError , " IndexError " , vm . exceptions . baseException ) ;
ADD_EXCEPTION_CLASS ( vm . exceptions . keyError , " KeyError " , vm . exceptions . baseException ) ;
ADD_EXCEPTION_CLASS ( vm . exceptions . attributeError , " AttributeError " , vm . exceptions . baseException ) ;
ADD_EXCEPTION_CLASS ( vm . exceptions . nameError , " NameError " , vm . exceptions . baseException ) ;
ADD_EXCEPTION_CLASS ( vm . exceptions . importError , " ImportError " , vm . exceptions . baseException ) ;
ADD_EXCEPTION_CLASS ( vm . exceptions . ioError , " IOError " , vm . exceptions . baseException ) ;
2021-01-04 05:27:33 +03:00
ADD_EXCEPTION_CLASS ( vm . exceptions . valueError , " ValueError " , vm . exceptions . baseException ) ;
2021-01-01 06:04:58 +03:00
/* Build classes for basic types */
ADD_BASE_CLASS ( vm . baseClasses . typeClass , " type " , vm . objectClass ) ;
2021-01-04 10:21:59 +03:00
krk_attachNamedObject ( & vm . globals , " type " , ( KrkObj * ) vm . baseClasses . typeClass ) ;
2021-01-01 10:01:58 +03:00
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 ) ;
2021-01-04 10:21:59 +03:00
krk_defineNative ( & vm . baseClasses . typeClass - > methods , " .__init__ " , _type_init ) ;
2021-01-01 10:01:58 +03:00
krk_defineNative ( & vm . baseClasses . typeClass - > methods , " .__str__ " , _class_to_str ) ;
krk_defineNative ( & vm . baseClasses . typeClass - > methods , " .__repr__ " , _class_to_str ) ;
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( vm . baseClasses . typeClass ) ;
2021-01-01 06:04:58 +03:00
ADD_BASE_CLASS ( vm . baseClasses . intClass , " int " , vm . objectClass ) ;
2021-01-04 10:21:59 +03:00
krk_attachNamedObject ( & vm . globals , " int " , ( KrkObj * ) vm . baseClasses . intClass ) ;
krk_defineNative ( & vm . baseClasses . intClass - > methods , " .__init__ " , _int_init ) ;
2021-01-01 10:01:58 +03:00
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 ) ;
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( vm . baseClasses . intClass ) ;
2021-01-01 06:04:58 +03:00
ADD_BASE_CLASS ( vm . baseClasses . floatClass , " float " , vm . objectClass ) ;
2021-01-04 10:21:59 +03:00
krk_attachNamedObject ( & vm . globals , " float " , ( KrkObj * ) vm . baseClasses . floatClass ) ;
krk_defineNative ( & vm . baseClasses . floatClass - > methods , " .__init__ " , _float_init ) ;
2021-01-01 10:01:58 +03:00
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 ) ;
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( vm . baseClasses . floatClass ) ;
2021-01-01 06:04:58 +03:00
ADD_BASE_CLASS ( vm . baseClasses . boolClass , " bool " , vm . objectClass ) ;
2021-01-04 10:21:59 +03:00
krk_attachNamedObject ( & vm . globals , " bool " , ( KrkObj * ) vm . baseClasses . boolClass ) ;
krk_defineNative ( & vm . baseClasses . boolClass - > methods , " .__init__ " , _bool_init ) ;
2021-01-01 10:01:58 +03:00
krk_defineNative ( & vm . baseClasses . boolClass - > methods , " .__str__ " , _bool_to_str ) ;
krk_defineNative ( & vm . baseClasses . boolClass - > methods , " .__repr__ " , _bool_to_str ) ;
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( vm . baseClasses . boolClass ) ;
2021-01-01 06:04:58 +03:00
ADD_BASE_CLASS ( vm . baseClasses . noneTypeClass , " NoneType " , vm . objectClass ) ;
2021-01-01 10:01:58 +03:00
krk_defineNative ( & vm . baseClasses . noneTypeClass - > methods , " .__str__ " , _none_to_str ) ;
krk_defineNative ( & vm . baseClasses . noneTypeClass - > methods , " .__repr__ " , _none_to_str ) ;
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( vm . baseClasses . noneTypeClass ) ;
2021-01-01 06:04:58 +03:00
ADD_BASE_CLASS ( vm . baseClasses . strClass , " str " , vm . objectClass ) ;
2021-01-04 10:21:59 +03:00
krk_attachNamedObject ( & vm . globals , " str " , ( KrkObj * ) vm . baseClasses . strClass ) ;
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .__init__ " , _string_init ) ;
2021-01-01 10:01:58 +03:00
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .__str__ " , _noop ) ;
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .__repr__ " , _repr_str ) ;
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .__len__ " , _string_length ) ;
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .__get__ " , _string_get ) ;
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .__set__ " , _strings_are_immutable ) ;
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .__int__ " , _string_to_int ) ;
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .__float__ " , _string_to_float ) ;
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .__getslice__ " , _string_get_slice ) ;
2021-01-03 08:09:45 +03:00
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .__ord__ " , _char_to_int ) ;
2021-01-04 08:03:19 +03:00
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .__contains__ " , _string_contains ) ;
2021-01-04 13:34:56 +03:00
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .__iter__ " , _str_iter ) ;
2021-01-04 05:27:41 +03:00
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .format " , _string_format ) ;
2021-01-04 06:01:17 +03:00
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .join " , _string_join ) ;
2021-01-04 06:47:57 +03:00
krk_defineNative ( & vm . baseClasses . strClass - > methods , " .split " , _string_split ) ;
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( vm . baseClasses . strClass ) ;
2021-01-01 06:04:58 +03:00
ADD_BASE_CLASS ( vm . baseClasses . functionClass , " function " , vm . objectClass ) ;
2021-01-01 10:01:58 +03:00
krk_defineNative ( & vm . baseClasses . functionClass - > methods , " .__str__ " , _closure_str ) ;
krk_defineNative ( & vm . baseClasses . functionClass - > methods , " .__repr__ " , _closure_str ) ;
krk_defineNative ( & vm . baseClasses . functionClass - > methods , " .__doc__ " , _closure_get_doc ) ;
krk_defineNative ( & vm . baseClasses . functionClass - > methods , " :__name__ " , _closure_get_name ) ;
krk_defineNative ( & vm . baseClasses . functionClass - > methods , " :__file__ " , _closure_get_file ) ;
2021-01-05 07:37:26 +03:00
krk_defineNative ( & vm . baseClasses . functionClass - > methods , " :__args__ " , _closure_get_argnames ) ;
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( vm . baseClasses . functionClass ) ;
2021-01-01 06:04:58 +03:00
ADD_BASE_CLASS ( vm . baseClasses . methodClass , " method " , vm . objectClass ) ;
2021-01-01 10:01:58 +03:00
krk_defineNative ( & vm . baseClasses . methodClass - > methods , " .__str__ " , _bound_str ) ;
krk_defineNative ( & vm . baseClasses . methodClass - > methods , " .__repr__ " , _bound_str ) ;
krk_defineNative ( & vm . baseClasses . methodClass - > methods , " .__doc__ " , _bound_get_doc ) ;
krk_defineNative ( & vm . baseClasses . methodClass - > methods , " :__name__ " , _bound_get_name ) ;
krk_defineNative ( & vm . baseClasses . methodClass - > methods , " :__file__ " , _bound_get_file ) ;
2021-01-05 07:37:26 +03:00
krk_defineNative ( & vm . baseClasses . methodClass - > methods , " :__args__ " , _bound_get_argnames ) ;
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( vm . baseClasses . methodClass ) ;
2021-01-05 05:38:11 +03:00
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 ) ;
krk_defineNative ( & vm . baseClasses . tupleClass - > methods , " .__get__ " , _tuple_get ) ;
krk_defineNative ( & vm . baseClasses . tupleClass - > methods , " .__len__ " , _tuple_len ) ;
2021-01-05 05:42:08 +03:00
krk_defineNative ( & vm . baseClasses . tupleClass - > methods , " .__contains__ " , _tuple_contains ) ;
2021-01-05 05:38:11 +03:00
krk_finalizeClass ( vm . baseClasses . tupleClass ) ;
2021-01-01 06:04:58 +03:00
2021-01-02 06:21:11 +03:00
/* Build global builtin functions. */
2021-01-04 10:21:59 +03:00
krk_defineNative ( & vm . globals , " listOf " , krk_list_of ) ; /* Equivalent to list() */
krk_defineNative ( & vm . globals , " dictOf " , krk_dict_of ) ; /* Equivalent to dict() */
2021-01-01 14:52:18 +03:00
krk_defineNative ( & vm . globals , " isinstance " , krk_isinstance ) ;
2021-01-02 07:58:16 +03:00
krk_defineNative ( & vm . globals , " globals " , krk_globals ) ;
2021-01-04 10:21:59 +03:00
krk_defineNative ( & vm . globals , " dir " , krk_dirObject ) ;
krk_defineNative ( & vm . globals , " len " , _len ) ;
krk_defineNative ( & vm . globals , " repr " , _repr ) ;
2021-01-04 17:33:43 +03:00
krk_defineNative ( & vm . globals , " print " , _print ) ;
2020-12-28 13:25:33 +03:00
2021-01-02 06:21:11 +03:00
/* __builtins__.set_tracing is namespaced */
2020-12-29 12:51:03 +03:00
krk_defineNative ( & vm . builtins - > fields , " set_tracing " , krk_set_tracing ) ;
2021-01-04 11:29:06 +03:00
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 ) ;
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( vm . baseClasses . listiteratorClass ) ;
2021-01-04 11:29:06 +03:00
2021-01-04 13:34:56 +03:00
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 ) ;
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( vm . baseClasses . striteratorClass ) ;
2021-01-04 13:34:56 +03:00
2021-01-04 11:29:06 +03:00
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 ) ;
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( vm . baseClasses . rangeClass ) ;
2021-01-04 11:29:06 +03:00
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 ) ;
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( vm . baseClasses . rangeiteratorClass ) ;
2021-01-04 11:29:06 +03:00
2021-01-02 06:21:11 +03:00
/**
* Read the managed code builtins module , which contains the base
* definitions for collections so we can pull them into the global
* namespace and attach their __init__ / __get__ / __set__ , etc . methods .
*
* A significant subset of the VM ' s functionality is lost without
* these classes being available , but it should still work to some degree .
*/
2020-12-29 12:04:02 +03:00
KrkValue builtinsModule = krk_interpret ( _builtins_src , 1 , " __builtins__ " , " __builtins__ " ) ;
2020-12-28 13:25:33 +03:00
if ( ! IS_OBJECT ( builtinsModule ) ) {
2021-01-02 06:21:11 +03:00
/* ... hence, this is a warning and not a complete failure. */
2020-12-28 13:25:33 +03:00
fprintf ( stderr , " VM startup failure: Failed to load __builtins__ module. \n " ) ;
2021-01-01 14:52:18 +03:00
} else {
KrkValue val ;
2021-01-02 06:21:11 +03:00
/* 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 */
2021-01-01 14:52:18 +03:00
krk_tableGet ( & vm . globals , OBJECT_VAL ( S ( " list " ) ) , & val ) ;
KrkClass * _class = AS_CLASS ( val ) ;
krk_defineNative ( & _class - > methods , " .__init__ " , _list_init ) ;
krk_defineNative ( & _class - > methods , " .__get__ " , _list_get ) ;
krk_defineNative ( & _class - > methods , " .__set__ " , _list_set ) ;
krk_defineNative ( & _class - > methods , " .__len__ " , _list_len ) ;
2021-01-02 13:41:51 +03:00
krk_defineNative ( & _class - > methods , " .__contains__ " , _list_contains ) ;
2021-01-04 13:07:39 +03:00
krk_defineNative ( & _class - > methods , " .__getslice__ " , _list_slice ) ;
2021-01-01 14:52:18 +03:00
krk_defineNative ( & _class - > methods , " .append " , _list_append ) ;
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( _class ) ;
2021-01-01 14:52:18 +03:00
krk_tableGet ( & vm . globals , OBJECT_VAL ( S ( " dict " ) ) , & val ) ;
_class = AS_CLASS ( val ) ;
krk_defineNative ( & _class - > methods , " .__init__ " , _dict_init ) ;
krk_defineNative ( & _class - > methods , " .__get__ " , _dict_get ) ;
krk_defineNative ( & _class - > methods , " .__set__ " , _dict_set ) ;
krk_defineNative ( & _class - > methods , " .__len__ " , _dict_len ) ;
2021-01-02 13:41:51 +03:00
krk_defineNative ( & _class - > methods , " .__contains__ " , _dict_contains ) ;
2021-01-05 03:30:23 +03:00
krk_finalizeClass ( _class ) ;
2021-01-02 06:21:11 +03:00
/* These are used to for dict.keys() to create the iterators. */
2021-01-01 14:52:18 +03:00
krk_defineNative ( & _class - > methods , " .capacity " , _dict_capacity ) ;
krk_defineNative ( & _class - > methods , " ._key_at_index " , _dict_key_at_index ) ;
2020-12-28 13:25:33 +03:00
}
2020-12-28 16:02:39 +03:00
2021-01-02 06:21:11 +03:00
/* The VM is now ready to start executing code. */
2021-01-04 11:47:53 +03:00
krk_resetStack ( ) ;
2020-12-28 16:02:39 +03:00
KRK_RESUME_GC ( ) ;
2020-12-26 03:32:21 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* Reclaim resources used by the VM .
*/
2020-12-26 03:32:21 +03:00
void krk_freeVM ( ) {
2020-12-26 10:53:15 +03:00
krk_freeTable ( & vm . globals ) ;
2020-12-26 08:33:34 +03:00
krk_freeTable ( & vm . strings ) ;
2020-12-31 09:48:39 +03:00
krk_freeTable ( & vm . modules ) ;
2020-12-27 16:40:35 +03:00
memset ( vm . specialMethodNames , 0 , sizeof ( vm . specialMethodNames ) ) ;
2020-12-26 03:32:21 +03:00
krk_freeObjects ( ) ;
FREE_ARRAY ( size_t , vm . stack , vm . stackSize ) ;
}
2021-01-02 06:21:11 +03:00
/**
* Internal type ( value ) . __name__ call for use in debugging methods and
* creating exception strings .
*/
2020-12-28 05:11:50 +03:00
const char * krk_typeName ( KrkValue value ) {
2021-01-01 10:01:58 +03:00
return AS_CLASS ( krk_typeOf ( 1 , ( KrkValue [ ] ) { value } ) ) - > name - > chars ;
2020-12-26 03:32:21 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* Basic arithmetic and string functions follow .
*
* BIG TODO : All of these need corresponding __methods__ so that classes
* can override / implement them .
* __add__ , __sub__ , __mult__ , __div__ ,
* __or__ , __and__ , __xor__ , __lshift__ , __rshift__ , __remainder__ ?
*/
2020-12-26 03:32:21 +03:00
# define MAKE_BIN_OP(name,operator) \
static KrkValue 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 ) ) ; \
else if ( IS_FLOATING ( b ) ) return FLOATING_VAL ( AS_FLOATING ( a ) operator AS_FLOATING ( b ) ) ; \
} else if ( IS_FLOATING ( b ) ) { \
if ( IS_INTEGER ( a ) ) return FLOATING_VAL ( ( double ) AS_INTEGER ( a ) operator AS_FLOATING ( b ) ) ; \
} \
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . typeError , " Incompatible types for binary operand %s: %s and %s " , # operator , krk_typeName ( a ) , krk_typeName ( b ) ) ; \
2020-12-26 03:32:21 +03:00
return NONE_VAL ( ) ; \
}
MAKE_BIN_OP ( add , + )
MAKE_BIN_OP ( subtract , - )
MAKE_BIN_OP ( multiply , * )
MAKE_BIN_OP ( divide , / )
2021-01-02 06:21:11 +03:00
/* 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 . */
2021-01-01 02:55:39 +03:00
# define MAKE_BIT_OP(name,operator) \
static KrkValue name ( KrkValue a , KrkValue b ) { \
if ( IS_INTEGER ( a ) & & IS_INTEGER ( b ) ) return INTEGER_VAL ( AS_INTEGER ( a ) operator AS_INTEGER ( b ) ) ; \
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . typeError , " Incompatible types for binary operand %s: %s and %s " , # operator , krk_typeName ( a ) , krk_typeName ( b ) ) ; \
2021-01-01 02:55:39 +03:00
return NONE_VAL ( ) ; \
}
MAKE_BIT_OP ( bitor , | )
MAKE_BIT_OP ( bitxor , ^ )
MAKE_BIT_OP ( bitand , & )
MAKE_BIT_OP ( shiftleft , < < )
MAKE_BIT_OP ( shiftright , > > )
MAKE_BIT_OP ( modulo , % ) /* not a bit op, but doesn't work on floating point */
2020-12-28 10:32:27 +03:00
2020-12-26 03:32:21 +03:00
# define MAKE_COMPARATOR(name, operator) \
static KrkValue 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 ) ) ; \
else if ( IS_FLOATING ( b ) ) return BOOLEAN_VAL ( AS_FLOATING ( a ) operator AS_FLOATING ( b ) ) ; \
} else if ( IS_FLOATING ( b ) ) { \
if ( IS_INTEGER ( a ) ) return BOOLEAN_VAL ( AS_INTEGER ( a ) operator AS_INTEGER ( b ) ) ; \
} \
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . typeError , " Can not compare types %s and %s " , krk_typeName ( a ) , krk_typeName ( b ) ) ; \
2020-12-26 03:32:21 +03:00
return NONE_VAL ( ) ; \
}
MAKE_COMPARATOR ( less , < )
MAKE_COMPARATOR ( greater , > )
static void concatenate ( const char * a , const char * b , size_t al , size_t bl ) {
size_t length = al + bl ;
char * chars = ALLOCATE ( char , length + 1 ) ;
memcpy ( chars , a , al ) ;
memcpy ( chars + al , b , bl ) ;
chars [ length ] = ' \0 ' ;
2020-12-28 05:11:50 +03:00
KrkString * result = krk_takeString ( chars , length ) ;
2020-12-27 09:58:32 +03:00
krk_pop ( ) ;
krk_pop ( ) ;
2020-12-26 03:32:21 +03:00
krk_push ( OBJECT_VAL ( result ) ) ;
}
static void addObjects ( ) {
2020-12-27 09:58:32 +03:00
KrkValue _b = krk_peek ( 0 ) ;
KrkValue _a = krk_peek ( 1 ) ;
2020-12-26 03:32:21 +03:00
if ( IS_STRING ( _a ) ) {
KrkString * a = AS_STRING ( _a ) ;
if ( IS_STRING ( _b ) ) {
KrkString * b = AS_STRING ( _b ) ;
concatenate ( a - > chars , b - > chars , a - > length , b - > length ) ;
return ;
}
2021-01-01 10:01:58 +03:00
if ( krk_bindMethod ( AS_CLASS ( krk_typeOf ( 1 , ( KrkValue [ ] ) { _b } ) ) , AS_STRING ( vm . specialMethodNames [ METHOD_STR ] ) ) ) {
KrkValue result ;
2021-01-03 02:31:28 +03:00
int t = krk_callValue ( krk_peek ( 0 ) , 0 , 1 ) ;
2021-01-01 10:01:58 +03:00
if ( t = = 2 ) {
result = krk_pop ( ) ;
} else if ( t = = 1 ) {
result = krk_runNext ( ) ;
2020-12-27 16:40:35 +03:00
} else {
2021-01-01 10:01:58 +03:00
krk_runtimeError ( vm . exceptions . typeError , " __str__ failed to call str on %s " , krk_typeName ( _b ) ) ;
return ;
}
if ( ! IS_STRING ( result ) ) {
krk_runtimeError ( vm . exceptions . typeError , " __str__ produced something that wasn't a string: %s " , krk_typeName ( result ) ) ;
return ;
2020-12-27 16:40:35 +03:00
}
2021-01-01 10:01:58 +03:00
krk_push ( result ) ;
concatenate ( a - > chars , AS_STRING ( result ) - > chars , a - > length , AS_STRING ( result ) - > length ) ;
return ;
2020-12-26 03:32:21 +03:00
} else {
2021-01-01 10:01:58 +03:00
char tmp [ 256 ] = { 0 } ;
sprintf ( tmp , " <%s> " , krk_typeName ( _b ) ) ;
concatenate ( a - > chars , tmp , a - > length , strlen ( tmp ) ) ;
2020-12-26 03:32:21 +03:00
}
} else {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . typeError , " Can not concatenate types %s and %s " , krk_typeName ( _a ) , krk_typeName ( _b ) ) ; \
2020-12-26 03:32:21 +03:00
}
}
2021-01-02 06:21:11 +03:00
/**
* At the end of each instruction cycle , we check the exception flag to see
* if an error was raised during execution . If there is an exception , this
* function is called to scan up the stack to see if there is an exception
* handler value . Handlers live on the stack at the point where it should be
* reset to and keep an offset to the except branch of a try / except statement
* pair ( or the exit point of the try , if there is no except branch ) . These
* objects can ' t be built by ( text ) user code , but erroneous bytecode / module
* stack manipulation could result in a handler being in the wrong place ,
* at which point there ' s no guarantees about what happens .
*/
2020-12-29 07:19:22 +03:00
static int handleException ( ) {
2020-12-29 05:00:12 +03:00
int stackOffset , frameOffset ;
2021-01-03 02:31:28 +03:00
int exitSlot = ( vm . exitOnFrame > = 0 ) ? vm . frames [ vm . exitOnFrame ] . outSlots : 0 ;
2020-12-29 07:19:22 +03:00
for ( stackOffset = ( int ) ( vm . stackTop - vm . stack - 1 ) ; stackOffset > = exitSlot & & ! IS_HANDLER ( vm . stack [ stackOffset ] ) ; stackOffset - - ) ;
if ( stackOffset < exitSlot ) {
if ( exitSlot = = 0 ) {
2021-01-02 06:21:11 +03:00
/*
* No exception was found and we have reached the top of the call stack .
* Call dumpTraceback to present the exception to the user and reset the
* VM stack state . It should still be safe to execute more code after
* this reset , so the repl can throw errors and keep accepting new lines .
*/
2021-01-05 17:23:16 +03:00
krk_dumpTraceback ( ) ;
2021-01-04 11:47:53 +03:00
krk_resetStack ( ) ;
2020-12-29 07:19:22 +03:00
vm . frameCount = 0 ;
}
2021-01-02 06:21:11 +03:00
/* If exitSlot was not 0, there was an exception during a call to runNext();
* this is likely to be raised higher up the stack as an exception in the outer
* call , but we don ' t want to print the traceback here . */
2020-12-29 07:19:22 +03:00
return 1 ;
2020-12-29 05:00:12 +03:00
}
2021-01-02 06:21:11 +03:00
/* Find the call frame that owns this stack slot */
2020-12-29 05:00:12 +03:00
for ( frameOffset = vm . frameCount - 1 ; frameOffset > = 0 & & ( int ) vm . frames [ frameOffset ] . slots > stackOffset ; frameOffset - - ) ;
if ( frameOffset = = - 1 ) {
2021-01-02 06:21:11 +03:00
fprintf ( stderr , " Internal error: Call stack is corrupted - unable to find \n " ) ;
fprintf ( stderr , " call frame that owns exception handler. \n " ) ;
2020-12-29 07:19:22 +03:00
exit ( 1 ) ;
2020-12-29 05:00:12 +03:00
}
2021-01-02 06:21:11 +03:00
/* We found an exception handler and can reset the VM to its call frame. */
2020-12-29 05:00:12 +03:00
closeUpvalues ( stackOffset ) ;
vm . stackTop = vm . stack + stackOffset + 1 ;
vm . frameCount = frameOffset + 1 ;
2021-01-02 06:21:11 +03:00
/* Clear the exception flag so we can continue executing from the handler. */
2020-12-29 07:19:22 +03:00
vm . flags & = ~ KRK_HAS_EXCEPTION ;
return 0 ;
2020-12-29 05:00:12 +03:00
}
2021-01-02 06:21:11 +03:00
/**
* Load a module .
*
* The module search path is stored in __builtins__ . module_paths and should
* be a list of directories ( with trailing forward - slash ) to look at , in order ,
* to resolve module names . krk source files will always take priority , so if
* a later search path has a krk source and an earlier search path has a shared
* object module , the later search path will still win .
*/
2020-12-31 09:48:39 +03:00
int krk_loadModule ( KrkString * name , KrkValue * moduleOut ) {
KrkValue modulePaths , modulePathsInternal ;
/* See if the module is already loaded */
2021-01-04 01:38:44 +03:00
if ( krk_tableGet ( & vm . modules , OBJECT_VAL ( name ) , moduleOut ) ) {
krk_push ( * moduleOut ) ;
return 1 ;
}
2020-12-31 09:48:39 +03:00
/* Obtain __builtins__.module_paths */
if ( ! krk_tableGet ( & vm . builtins - > fields , OBJECT_VAL ( S ( " module_paths " ) ) , & modulePaths ) | | ! IS_INSTANCE ( modulePaths ) ) {
* moduleOut = NONE_VAL ( ) ;
2021-01-02 06:21:11 +03:00
krk_runtimeError ( vm . exceptions . baseException ,
" Internal error: __builtins__.module_paths not defined. " ) ;
2020-12-31 09:48:39 +03:00
return 0 ;
}
2021-01-02 06:21:11 +03:00
/* Obtain __builtins__.module_paths.__list so we can do lookups directly */
2021-01-01 14:52:18 +03:00
if ( ! krk_tableGet ( & ( AS_INSTANCE ( modulePaths ) - > fields ) , vm . specialMethodNames [ METHOD_LIST_INT ] , & modulePathsInternal ) | | ! IS_FUNCTION ( modulePathsInternal ) ) {
2020-12-31 09:48:39 +03:00
* moduleOut = NONE_VAL ( ) ;
2021-01-02 06:21:11 +03:00
krk_runtimeError ( vm . exceptions . baseException ,
" Internal error: __builtins__.module_paths is corrupted or incorrectly set. " ) ;
2020-12-31 09:48:39 +03:00
return 0 ;
}
/*
* So maybe storing lists magically as functions to reuse their constants
* tables isn ' t the _best_ approach , but it works , and until I do something
* else it ' s what we have , so let ' s do the most efficient thing and look
* at the function object directly instead of calling _list_length / _get
*/
int moduleCount = AS_FUNCTION ( modulePathsInternal ) - > chunk . constants . count ;
if ( ! moduleCount ) {
* moduleOut = NONE_VAL ( ) ;
2021-01-02 06:21:11 +03:00
krk_runtimeError ( vm . exceptions . importError ,
" No module search directories are specified, so no modules may be imported. " ) ;
2020-12-31 09:48:39 +03:00
return 0 ;
}
struct stat statbuf ;
/* First search for {name}.krk in the module search paths */
for ( int i = 0 ; i < moduleCount ; + + i , krk_pop ( ) ) {
krk_push ( AS_FUNCTION ( modulePathsInternal ) - > chunk . constants . values [ i ] ) ;
if ( ! IS_STRING ( krk_peek ( 0 ) ) ) {
* moduleOut = NONE_VAL ( ) ;
2021-01-02 06:21:11 +03:00
krk_runtimeError ( vm . exceptions . typeError ,
" Module search paths must be strings; check the search path at index %d " , i ) ;
2020-12-31 09:48:39 +03:00
return 0 ;
}
krk_push ( OBJECT_VAL ( name ) ) ;
addObjects ( ) ; /* Concatenate path... */
krk_push ( OBJECT_VAL ( S ( " .krk " ) ) ) ;
addObjects ( ) ; /* and file extension */
char * fileName = AS_CSTRING ( krk_peek ( 0 ) ) ;
if ( stat ( fileName , & statbuf ) < 0 ) continue ;
/* Compile and run the module in a new context and exit the VM when it
* returns to the current call frame ; modules should return objects . */
int previousExitFrame = vm . exitOnFrame ;
vm . exitOnFrame = vm . frameCount ;
* moduleOut = krk_runfile ( fileName , 1 , name - > chars , fileName ) ;
vm . exitOnFrame = previousExitFrame ;
if ( ! IS_OBJECT ( * moduleOut ) ) {
2021-01-02 06:21:11 +03:00
krk_runtimeError ( vm . exceptions . importError ,
" Failed to load module '%s' from '%s' " , name - > chars , fileName ) ;
2020-12-31 09:48:39 +03:00
return 0 ;
}
krk_pop ( ) ; /* concatenated filename on stack */
krk_push ( * moduleOut ) ;
krk_tableSet ( & vm . modules , OBJECT_VAL ( name ) , * moduleOut ) ;
return 1 ;
}
/* If we didn't find {name}.krk, try {name}.so in the same order */
for ( int i = 0 ; i < moduleCount ; + + i , krk_pop ( ) ) {
/* Assume things haven't changed and all of these are strings. */
krk_push ( AS_FUNCTION ( modulePathsInternal ) - > chunk . constants . values [ i ] ) ;
krk_push ( OBJECT_VAL ( name ) ) ;
addObjects ( ) ; /* this should just be basic concatenation */
krk_push ( OBJECT_VAL ( S ( " .so " ) ) ) ;
addObjects ( ) ;
char * fileName = AS_CSTRING ( krk_peek ( 0 ) ) ;
if ( stat ( fileName , & statbuf ) < 0 ) continue ;
void * dlRef = dlopen ( fileName , RTLD_NOW ) ;
if ( ! dlRef ) {
* moduleOut = NONE_VAL ( ) ;
2021-01-02 06:21:11 +03:00
krk_runtimeError ( vm . exceptions . importError ,
" Failed to load native module '%s' from shared object '%s' " , name - > chars , fileName ) ;
2020-12-31 09:48:39 +03:00
return 0 ;
}
krk_push ( OBJECT_VAL ( S ( " krk_module_onload_ " ) ) ) ;
krk_push ( OBJECT_VAL ( name ) ) ;
addObjects ( ) ;
char * handlerName = AS_CSTRING ( krk_peek ( 0 ) ) ;
KrkValue ( * moduleOnLoad ) ( ) ;
void * out = dlsym ( dlRef , handlerName ) ;
memcpy ( & moduleOnLoad , & out , sizeof ( out ) ) ;
if ( ! moduleOnLoad ) {
* moduleOut = NONE_VAL ( ) ;
2021-01-02 06:21:11 +03:00
krk_runtimeError ( vm . exceptions . importError ,
" Failed to run module initialization method '%s' from shared object '%s' " ,
2020-12-31 09:48:39 +03:00
handlerName , fileName ) ;
return 0 ;
}
krk_pop ( ) ; /* onload function */
* moduleOut = moduleOnLoad ( ) ;
if ( ! IS_OBJECT ( * moduleOut ) ) {
2021-01-02 06:21:11 +03:00
krk_runtimeError ( vm . exceptions . importError ,
" Failed to load module '%s' from '%s' " , name - > chars , fileName ) ;
2020-12-31 09:48:39 +03:00
return 0 ;
}
krk_pop ( ) ; /* filename */
krk_push ( * moduleOut ) ;
krk_tableSet ( & vm . modules , OBJECT_VAL ( name ) , * moduleOut ) ;
return 1 ;
}
/* If we still haven't found anything, fail. */
* moduleOut = NONE_VAL ( ) ;
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . importError , " No module named '%s' " , name - > chars ) ;
2020-12-31 09:48:39 +03:00
return 0 ;
}
2021-01-02 06:21:11 +03:00
/**
* Try to resolve and push [ stack top ] . name .
* If [ stack top ] is an instance , scan fields first .
* Otherwise , scan for methods from [ stack top ] . __class__ .
* Returns 0 if nothing was found , 1 if something was - and that
* " something " will replace [ stack top ] .
*/
2021-01-01 10:01:58 +03:00
static int valueGetProperty ( KrkString * name ) {
KrkClass * objectClass ;
if ( IS_INSTANCE ( krk_peek ( 0 ) ) ) {
KrkInstance * instance = AS_INSTANCE ( krk_peek ( 0 ) ) ;
KrkValue value ;
if ( krk_tableGet ( & instance - > fields , OBJECT_VAL ( name ) , & value ) ) {
krk_pop ( ) ;
krk_push ( value ) ;
return 1 ;
}
objectClass = instance - > _class ;
} else {
objectClass = AS_CLASS ( krk_typeOf ( 1 , ( KrkValue [ ] ) { krk_peek ( 0 ) } ) ) ;
}
/* See if the base class for this non-instance type has a method available */
if ( krk_bindMethod ( objectClass , name ) ) {
return 1 ;
}
return 0 ;
}
2021-01-02 06:21:11 +03:00
# define READ_BYTE() (*frame->ip++)
# define BINARY_OP(op) { KrkValue b = krk_pop(); KrkValue a = krk_pop(); krk_push(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))
/**
* Read bytes after an opcode . Most instructions take 1 , 2 , or 3 bytes as an
* operand referring to a local slot , constant slot , or offset .
*/
static inline size_t readBytes ( CallFrame * frame , int num ) {
size_t out = READ_BYTE ( ) ;
while ( - - num ) {
out < < = 8 ;
out | = ( READ_BYTE ( ) & 0xFF ) ;
}
return out ;
}
/**
* VM main loop .
*/
2020-12-26 10:53:15 +03:00
static KrkValue run ( ) {
2020-12-27 03:33:28 +03:00
CallFrame * frame = & vm . frames [ vm . frameCount - 1 ] ;
2020-12-26 03:32:21 +03:00
2021-01-02 06:21:11 +03:00
while ( 1 ) {
2021-01-01 11:38:09 +03:00
# ifdef ENABLE_TRACING
2020-12-28 14:38:26 +03:00
if ( vm . flags & KRK_ENABLE_TRACING ) {
2020-12-30 15:56:02 +03:00
dumpStack ( frame ) ;
2021-01-05 09:33:33 +03:00
krk_disassembleInstruction ( stderr , frame - > closure - > function ,
2020-12-28 03:08:35 +03:00
( size_t ) ( frame - > ip - frame - > closure - > function - > chunk . code ) ) ;
2020-12-26 03:32:21 +03:00
}
# endif
2021-01-02 06:21:11 +03:00
2020-12-29 00:13:30 +03:00
uint8_t opcode = READ_BYTE ( ) ;
2021-01-02 06:21:11 +03:00
/* We split the instruction opcode table in half and use the top bit
* to mark instructions as " long " as we can quickly determine operand
* widths . The standard opereand width is 1 byte . If operands need
* to use more than 256 possible values , such as when the stack
* is very large or there are a lot of constants in a single chunk of
* bytecode , the long opcodes provide 24 bits of operand space . */
2020-12-29 00:13:30 +03:00
int operandWidth = ( opcode & ( 1 < < 7 ) ) ? 3 : 1 ;
switch ( opcode ) {
2020-12-26 03:32:21 +03:00
case OP_RETURN : {
2020-12-27 03:33:28 +03:00
KrkValue result = krk_pop ( ) ;
2020-12-28 16:02:39 +03:00
closeUpvalues ( frame - > slots ) ;
2020-12-27 03:33:28 +03:00
vm . frameCount - - ;
if ( vm . frameCount = = 0 ) {
2020-12-28 14:38:26 +03:00
krk_pop ( ) ;
2020-12-27 10:45:34 +03:00
return result ;
2020-12-27 03:33:28 +03:00
}
2021-01-03 02:31:28 +03:00
vm . stackTop = & vm . stack [ frame - > outSlots ] ;
2020-12-29 07:19:22 +03:00
if ( vm . frameCount = = ( size_t ) vm . exitOnFrame ) {
2020-12-27 12:55:52 +03:00
return result ;
}
2020-12-27 03:33:28 +03:00
krk_push ( result ) ;
frame = & vm . frames [ vm . frameCount - 1 ] ;
break ;
2020-12-26 03:32:21 +03:00
}
case OP_EQUAL : {
KrkValue b = krk_pop ( ) ;
KrkValue a = krk_pop ( ) ;
krk_push ( BOOLEAN_VAL ( krk_valuesEqual ( a , b ) ) ) ;
break ;
}
2020-12-27 03:33:28 +03:00
case OP_LESS : BINARY_OP ( less ) ;
2020-12-26 03:32:21 +03:00
case OP_GREATER : BINARY_OP ( greater )
case OP_ADD :
2020-12-27 03:33:28 +03:00
if ( IS_OBJECT ( krk_peek ( 0 ) ) | | IS_OBJECT ( krk_peek ( 1 ) ) ) addObjects ( ) ;
2020-12-26 03:32:21 +03:00
else BINARY_OP ( add )
break ;
case OP_SUBTRACT : BINARY_OP ( subtract )
case OP_MULTIPLY : BINARY_OP ( multiply )
case OP_DIVIDE : BINARY_OP ( divide )
2020-12-28 10:32:27 +03:00
case OP_MODULO : BINARY_OP ( modulo )
2021-01-01 02:55:39 +03:00
case OP_BITOR : BINARY_OP ( bitor )
case OP_BITXOR : BINARY_OP ( bitxor )
case OP_BITAND : BINARY_OP ( bitand )
case OP_SHIFTLEFT : BINARY_OP ( shiftleft )
case OP_SHIFTRIGHT : BINARY_OP ( shiftright )
case OP_BITNEGATE : {
KrkValue value = krk_pop ( ) ;
if ( IS_INTEGER ( value ) ) krk_push ( INTEGER_VAL ( ~ AS_INTEGER ( value ) ) ) ;
2021-01-01 06:04:58 +03:00
else { krk_runtimeError ( vm . exceptions . typeError , " Incompatible operand type for bit negation. " ) ; goto _finishException ; }
2021-01-01 02:55:39 +03:00
break ;
}
2020-12-26 03:32:21 +03:00
case OP_NEGATE : {
KrkValue value = krk_pop ( ) ;
if ( IS_INTEGER ( value ) ) krk_push ( INTEGER_VAL ( - AS_INTEGER ( value ) ) ) ;
else if ( IS_FLOATING ( value ) ) krk_push ( FLOATING_VAL ( - AS_FLOATING ( value ) ) ) ;
2021-01-01 06:04:58 +03:00
else { krk_runtimeError ( vm . exceptions . typeError , " Incompatible operand type for prefix negation. " ) ; goto _finishException ; }
2020-12-26 03:32:21 +03:00
break ;
}
2020-12-26 10:53:15 +03:00
case OP_CONSTANT_LONG :
2020-12-26 03:32:21 +03:00
case OP_CONSTANT : {
2020-12-29 00:13:30 +03:00
size_t index = readBytes ( frame , operandWidth ) ;
2020-12-27 07:02:26 +03:00
KrkValue constant = frame - > closure - > function - > chunk . constants . values [ index ] ;
2020-12-26 03:32:21 +03:00
krk_push ( constant ) ;
break ;
}
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 ;
2020-12-26 10:53:15 +03:00
case OP_POP : krk_pop ( ) ; break ;
case OP_DEFINE_GLOBAL_LONG :
case OP_DEFINE_GLOBAL : {
2020-12-29 00:13:30 +03:00
KrkString * name = READ_STRING ( operandWidth ) ;
2020-12-27 03:33:28 +03:00
krk_tableSet ( & vm . globals , OBJECT_VAL ( name ) , krk_peek ( 0 ) ) ;
2020-12-26 10:53:15 +03:00
krk_pop ( ) ;
break ;
}
case OP_GET_GLOBAL_LONG :
case OP_GET_GLOBAL : {
2020-12-29 00:13:30 +03:00
KrkString * name = READ_STRING ( operandWidth ) ;
2020-12-26 10:53:15 +03:00
KrkValue value ;
if ( ! krk_tableGet ( & vm . globals , OBJECT_VAL ( name ) , & value ) ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . nameError , " Undefined variable '%s'. " , name - > chars ) ;
2020-12-29 05:00:12 +03:00
goto _finishException ;
2020-12-26 10:53:15 +03:00
}
krk_push ( value ) ;
break ;
}
case OP_SET_GLOBAL_LONG :
case OP_SET_GLOBAL : {
2020-12-29 00:13:30 +03:00
KrkString * name = READ_STRING ( operandWidth ) ;
2020-12-27 03:33:28 +03:00
if ( krk_tableSet ( & vm . globals , OBJECT_VAL ( name ) , krk_peek ( 0 ) ) ) {
2020-12-26 10:53:15 +03:00
krk_tableDelete ( & vm . globals , OBJECT_VAL ( name ) ) ;
/* TODO: This should probably just work as an assignment? */
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . nameError , " Undefined variable '%s'. " , name - > chars ) ;
2020-12-29 05:00:12 +03:00
goto _finishException ;
2020-12-26 10:53:15 +03:00
}
break ;
}
2020-12-27 12:55:52 +03:00
case OP_IMPORT_LONG :
case OP_IMPORT : {
2020-12-29 00:13:30 +03:00
KrkString * name = READ_STRING ( operandWidth ) ;
2020-12-27 12:55:52 +03:00
KrkValue module ;
2020-12-31 09:48:39 +03:00
if ( ! krk_loadModule ( name , & module ) ) {
goto _finishException ;
2020-12-27 12:55:52 +03:00
}
break ;
}
2020-12-26 12:39:29 +03:00
case OP_GET_LOCAL_LONG :
case OP_GET_LOCAL : {
2020-12-29 00:13:30 +03:00
uint32_t slot = readBytes ( frame , operandWidth ) ;
2020-12-27 16:40:35 +03:00
krk_push ( vm . stack [ frame - > slots + slot ] ) ;
2020-12-26 12:39:29 +03:00
break ;
}
case OP_SET_LOCAL_LONG :
case OP_SET_LOCAL : {
2020-12-29 00:13:30 +03:00
uint32_t slot = readBytes ( frame , operandWidth ) ;
2020-12-27 16:40:35 +03:00
vm . stack [ frame - > slots + slot ] = krk_peek ( 0 ) ;
2020-12-26 12:39:29 +03:00
break ;
}
2020-12-26 14:39:47 +03:00
case OP_JUMP_IF_FALSE : {
2020-12-27 03:33:28 +03:00
uint16_t offset = readBytes ( frame , 2 ) ;
if ( isFalsey ( krk_peek ( 0 ) ) ) frame - > ip + = offset ;
2020-12-26 14:39:47 +03:00
break ;
}
case OP_JUMP_IF_TRUE : {
2020-12-27 03:33:28 +03:00
uint16_t offset = readBytes ( frame , 2 ) ;
if ( ! isFalsey ( krk_peek ( 0 ) ) ) frame - > ip + = offset ;
2020-12-26 14:39:47 +03:00
break ;
}
case OP_JUMP : {
2020-12-27 03:33:28 +03:00
frame - > ip + = readBytes ( frame , 2 ) ;
2020-12-26 14:39:47 +03:00
break ;
}
case OP_LOOP : {
2020-12-27 03:33:28 +03:00
uint16_t offset = readBytes ( frame , 2 ) ;
frame - > ip - = offset ;
break ;
}
2020-12-29 05:00:12 +03:00
case OP_PUSH_TRY : {
uint16_t tryTarget = readBytes ( frame , 2 ) + ( frame - > ip - frame - > closure - > function - > chunk . code ) ;
KrkValue handler = HANDLER_VAL ( tryTarget ) ;
krk_push ( handler ) ;
break ;
}
case OP_RAISE : {
vm . currentException = krk_pop ( ) ;
vm . flags | = KRK_HAS_EXCEPTION ;
goto _finishException ;
}
2020-12-30 15:56:02 +03:00
/* Sometimes you just want to increment a stack-local integer quickly. */
case OP_INC_LONG :
case OP_INC : {
uint32_t slot = readBytes ( frame , operandWidth ) ;
vm . stack [ frame - > slots + slot ] = INTEGER_VAL ( AS_INTEGER ( vm . stack [ frame - > slots + slot ] ) + 1 ) ;
break ;
}
2020-12-29 00:13:30 +03:00
case OP_CALL_LONG :
2020-12-27 03:33:28 +03:00
case OP_CALL : {
2020-12-29 00:13:30 +03:00
int argCount = readBytes ( frame , operandWidth ) ;
2021-01-03 02:31:28 +03:00
if ( ! krk_callValue ( krk_peek ( argCount ) , argCount , 1 ) ) {
2020-12-29 05:00:12 +03:00
if ( vm . flags & KRK_HAS_EXCEPTION ) goto _finishException ;
2020-12-27 03:33:28 +03:00
return NONE_VAL ( ) ;
}
frame = & vm . frames [ vm . frameCount - 1 ] ;
2020-12-26 14:39:47 +03:00
break ;
}
2020-12-30 15:56:02 +03:00
/* This version of the call instruction takes its arity from the
2021-01-02 06:21:11 +03:00
* top of the stack , so we don ' t have to calculate arity at compile time . */
2020-12-30 15:56:02 +03:00
case OP_CALL_STACK : {
int argCount = AS_INTEGER ( krk_pop ( ) ) ;
2021-01-03 02:31:28 +03:00
if ( ! krk_callValue ( krk_peek ( argCount ) , argCount , 1 ) ) {
2020-12-30 15:56:02 +03:00
if ( vm . flags & KRK_HAS_EXCEPTION ) goto _finishException ;
return NONE_VAL ( ) ;
}
frame = & vm . frames [ vm . frameCount - 1 ] ;
break ;
}
2021-01-03 11:59:50 +03:00
case OP_EXPAND_ARGS : {
int type = READ_BYTE ( ) ;
krk_push ( KWARGS_VAL ( LONG_MAX - type ) ) ;
break ;
}
2020-12-27 07:02:26 +03:00
case OP_CLOSURE_LONG :
case OP_CLOSURE : {
2020-12-29 00:13:30 +03:00
KrkFunction * function = AS_FUNCTION ( READ_CONSTANT ( operandWidth ) ) ;
2020-12-28 05:11:50 +03:00
KrkClosure * closure = krk_newClosure ( function ) ;
2020-12-27 07:02:26 +03:00
krk_push ( OBJECT_VAL ( closure ) ) ;
for ( size_t i = 0 ; i < closure - > upvalueCount ; + + i ) {
int isLocal = READ_BYTE ( ) ;
2020-12-29 00:13:30 +03:00
int index = readBytes ( frame , ( i > 255 ) ? 3 : 1 ) ;
2020-12-27 07:02:26 +03:00
if ( isLocal ) {
2020-12-28 16:02:39 +03:00
closure - > upvalues [ i ] = captureUpvalue ( frame - > slots + index ) ;
2020-12-27 07:02:26 +03:00
} else {
closure - > upvalues [ i ] = frame - > closure - > upvalues [ index ] ;
}
}
break ;
}
case OP_GET_UPVALUE_LONG :
case OP_GET_UPVALUE : {
2020-12-29 00:13:30 +03:00
int slot = readBytes ( frame , operandWidth ) ;
2020-12-28 16:02:39 +03:00
krk_push ( * UPVALUE_LOCATION ( frame - > closure - > upvalues [ slot ] ) ) ;
2020-12-27 07:02:26 +03:00
break ;
}
case OP_SET_UPVALUE_LONG :
case OP_SET_UPVALUE : {
2020-12-29 00:13:30 +03:00
int slot = readBytes ( frame , operandWidth ) ;
2020-12-28 16:02:39 +03:00
* UPVALUE_LOCATION ( frame - > closure - > upvalues [ slot ] ) = krk_peek ( 0 ) ;
2020-12-27 07:02:26 +03:00
break ;
}
case OP_CLOSE_UPVALUE :
2020-12-28 16:02:39 +03:00
closeUpvalues ( ( vm . stackTop - vm . stack ) - 1 ) ;
2020-12-27 07:02:26 +03:00
krk_pop ( ) ;
break ;
2020-12-27 10:45:34 +03:00
case OP_CLASS_LONG :
case OP_CLASS : {
2020-12-29 00:13:30 +03:00
KrkString * name = READ_STRING ( operandWidth ) ;
2020-12-28 05:11:50 +03:00
KrkClass * _class = krk_newClass ( name ) ;
2020-12-30 15:56:02 +03:00
krk_push ( OBJECT_VAL ( _class ) ) ;
2020-12-28 06:16:44 +03:00
_class - > filename = frame - > closure - > function - > chunk . filename ;
2021-01-01 06:04:58 +03:00
_class - > base = vm . objectClass ;
krk_tableAddAll ( & vm . objectClass - > methods , & _class - > methods ) ;
2020-12-27 10:45:34 +03:00
break ;
}
case OP_GET_PROPERTY_LONG :
case OP_GET_PROPERTY : {
2020-12-29 00:13:30 +03:00
KrkString * name = READ_STRING ( operandWidth ) ;
2021-01-01 10:01:58 +03:00
if ( ! valueGetProperty ( name ) ) {
krk_runtimeError ( vm . exceptions . attributeError , " '%s' object has no attribute '%s' " , krk_typeName ( krk_peek ( 0 ) ) , name - > chars ) ;
goto _finishException ;
2020-12-27 11:53:46 +03:00
}
break ;
2020-12-27 10:45:34 +03:00
}
2021-01-01 14:52:18 +03:00
case OP_INVOKE_GETTER : {
2021-01-05 03:30:23 +03:00
KrkClass * type = AS_CLASS ( krk_typeOf ( 1 , ( KrkValue [ ] ) { krk_peek ( 1 ) } ) ) ;
if ( type - > _getter ) {
krk_push ( krk_callSimple ( OBJECT_VAL ( type - > _getter ) , 2 , 0 ) ) ;
2021-01-04 11:29:06 +03:00
} else {
2021-01-04 13:07:39 +03:00
krk_runtimeError ( vm . exceptions . attributeError , " '%s' object is not subscriptable " , krk_typeName ( krk_peek ( 1 ) ) ) ;
2021-01-01 14:52:18 +03:00
}
break ;
}
case OP_INVOKE_SETTER : {
2021-01-05 03:30:23 +03:00
KrkClass * type = AS_CLASS ( krk_typeOf ( 1 , ( KrkValue [ ] ) { krk_peek ( 2 ) } ) ) ;
if ( type - > _setter ) {
krk_push ( krk_callSimple ( OBJECT_VAL ( type - > _setter ) , 3 , 0 ) ) ;
2021-01-04 11:29:06 +03:00
} else {
2021-01-05 05:38:11 +03:00
if ( type - > _getter ) {
krk_runtimeError ( vm . exceptions . attributeError , " '%s' object is not mutable " , krk_typeName ( krk_peek ( 2 ) ) ) ;
} else {
krk_runtimeError ( vm . exceptions . attributeError , " '%s' object is not subscriptable " , krk_typeName ( krk_peek ( 2 ) ) ) ;
}
2021-01-01 14:52:18 +03:00
}
break ;
}
case OP_INVOKE_GETSLICE : {
2021-01-05 03:30:23 +03:00
KrkClass * type = AS_CLASS ( krk_typeOf ( 1 , ( KrkValue [ ] ) { krk_peek ( 2 ) } ) ) ;
if ( type - > _slicer ) {
krk_push ( krk_callSimple ( OBJECT_VAL ( type - > _slicer ) , 3 , 0 ) ) ;
2021-01-04 12:10:55 +03:00
} else {
2021-01-04 13:07:39 +03:00
krk_runtimeError ( vm . exceptions . attributeError , " '%s' object is not sliceable " , krk_typeName ( krk_peek ( 2 ) ) ) ;
2021-01-01 14:52:18 +03:00
}
break ;
}
2020-12-27 10:45:34 +03:00
case OP_SET_PROPERTY_LONG :
case OP_SET_PROPERTY : {
2021-01-01 06:04:58 +03:00
KrkString * name = READ_STRING ( operandWidth ) ;
2020-12-27 10:45:34 +03:00
if ( ! IS_INSTANCE ( krk_peek ( 1 ) ) ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . attributeError , " '%s' object has no attribute '%s' " , krk_typeName ( krk_peek ( 0 ) ) , name - > chars ) ;
2020-12-29 05:00:12 +03:00
goto _finishException ;
2020-12-27 10:45:34 +03:00
}
KrkInstance * instance = AS_INSTANCE ( krk_peek ( 1 ) ) ;
krk_tableSet ( & instance - > fields , OBJECT_VAL ( name ) , krk_peek ( 0 ) ) ;
KrkValue value = krk_pop ( ) ;
krk_pop ( ) ; /* instance */
krk_push ( value ) ; /* Moves value in */
break ;
}
2020-12-27 11:53:46 +03:00
case OP_METHOD_LONG :
case OP_METHOD : {
2021-01-02 06:21:11 +03:00
KrkValue method = krk_peek ( 0 ) ;
KrkClass * _class = AS_CLASS ( krk_peek ( 1 ) ) ;
2021-01-05 03:30:23 +03:00
KrkValue name = OBJECT_VAL ( READ_STRING ( operandWidth ) ) ;
krk_tableSet ( & _class - > methods , name , method ) ;
2021-01-02 06:21:11 +03:00
krk_pop ( ) ;
2020-12-27 11:53:46 +03:00
break ;
}
2021-01-05 03:30:23 +03:00
case OP_FINALIZE : {
KrkClass * _class = AS_CLASS ( krk_peek ( 0 ) ) ;
/* Store special methods for quick access */
krk_finalizeClass ( _class ) ;
krk_pop ( ) ; /* Pop the class as we're done attaching methods */
break ;
}
2020-12-27 16:40:35 +03:00
case OP_INHERIT : {
KrkValue superclass = krk_peek ( 1 ) ;
if ( ! IS_CLASS ( superclass ) ) {
2021-01-01 06:04:58 +03:00
krk_runtimeError ( vm . exceptions . typeError , " Superclass must be a class. " ) ;
2020-12-27 16:40:35 +03:00
return NONE_VAL ( ) ;
}
KrkClass * subclass = AS_CLASS ( krk_peek ( 0 ) ) ;
2020-12-31 03:15:53 +03:00
subclass - > base = AS_CLASS ( superclass ) ;
2020-12-27 16:40:35 +03:00
krk_tableAddAll ( & AS_CLASS ( superclass ) - > methods , & subclass - > methods ) ;
krk_pop ( ) ;
break ;
}
2020-12-30 10:59:21 +03:00
case OP_DOCSTRING : {
KrkClass * me = AS_CLASS ( krk_peek ( 1 ) ) ;
me - > docstring = AS_STRING ( krk_pop ( ) ) ;
break ;
}
2020-12-27 16:40:35 +03:00
case OP_GET_SUPER_LONG :
case OP_GET_SUPER : {
2020-12-29 00:13:30 +03:00
KrkString * name = READ_STRING ( operandWidth ) ;
2020-12-27 16:40:35 +03:00
KrkClass * superclass = AS_CLASS ( krk_pop ( ) ) ;
2021-01-01 10:01:58 +03:00
if ( ! krk_bindMethod ( superclass , name ) ) {
2020-12-27 16:40:35 +03:00
return NONE_VAL ( ) ;
}
break ;
}
2021-01-01 04:42:16 +03:00
case OP_DUP :
krk_push ( krk_peek ( READ_BYTE ( ) ) ) ;
break ;
case OP_SWAP :
2021-01-02 13:41:51 +03:00
krk_swap ( 1 ) ;
2021-01-01 04:42:16 +03:00
break ;
2021-01-03 06:09:41 +03:00
case OP_KWARGS_LONG :
case OP_KWARGS : {
krk_push ( KWARGS_VAL ( readBytes ( frame , operandWidth ) ) ) ;
break ;
}
2021-01-05 06:03:21 +03:00
case OP_TUPLE_LONG :
case OP_TUPLE : {
size_t count = readBytes ( frame , operandWidth ) ;
KrkValue tuple = _tuple_of ( count , & vm . stackTop [ - count ] ) ;
if ( count ) {
vm . stackTop [ - count ] = tuple ;
while ( count > 1 ) {
krk_pop ( ) ;
count - - ;
}
} else {
krk_push ( tuple ) ;
}
break ;
}
2020-12-26 03:32:21 +03:00
}
2020-12-29 05:00:12 +03:00
if ( ! ( vm . flags & KRK_HAS_EXCEPTION ) ) continue ;
_finishException :
2020-12-29 07:19:22 +03:00
if ( ! handleException ( ) ) {
2020-12-29 05:00:12 +03:00
frame = & vm . frames [ vm . frameCount - 1 ] ;
frame - > ip = frame - > closure - > function - > chunk . code + AS_HANDLER ( krk_peek ( 0 ) ) ;
/* Replace the exception handler with the exception */
krk_pop ( ) ;
krk_push ( vm . currentException ) ;
vm . currentException = NONE_VAL ( ) ;
} else {
return NONE_VAL ( ) ;
}
2020-12-26 03:32:21 +03:00
}
2020-12-29 05:00:12 +03:00
2020-12-26 03:32:21 +03:00
# undef BINARY_OP
# undef READ_BYTE
}
2020-12-28 04:54:25 +03:00
KrkValue krk_interpret ( const char * src , int newScope , char * fromName , char * fromFile ) {
KrkFunction * function = krk_compile ( src , newScope , fromFile ) ;
2020-12-28 06:16:44 +03:00
if ( ! function ) return NONE_VAL ( ) ;
2020-12-28 07:51:14 +03:00
krk_push ( OBJECT_VAL ( function ) ) ;
2020-12-28 05:11:50 +03:00
function - > name = krk_copyString ( fromName , strlen ( fromName ) ) ;
2020-12-27 03:33:28 +03:00
2020-12-28 05:11:50 +03:00
KrkClosure * closure = krk_newClosure ( function ) ;
2020-12-27 07:02:26 +03:00
krk_pop ( ) ;
krk_push ( OBJECT_VAL ( closure ) ) ;
2021-01-03 02:31:28 +03:00
krk_callValue ( OBJECT_VAL ( closure ) , 0 , 1 ) ;
2020-12-26 03:32:21 +03:00
2020-12-27 12:55:52 +03:00
return run ( ) ;
}
2020-12-28 04:54:25 +03:00
KrkValue krk_runfile ( const char * fileName , int newScope , char * fromName , char * fromFile ) {
2020-12-27 12:55:52 +03:00
FILE * f = fopen ( fileName , " r " ) ;
2021-01-02 01:47:00 +03:00
if ( ! f ) {
if ( ! newScope ) {
fprintf ( stderr , " kuroko: could not read file '%s': %s \n " , fileName , strerror ( errno ) ) ;
}
return INTEGER_VAL ( errno ) ;
}
2020-12-27 12:55:52 +03:00
fseek ( f , 0 , SEEK_END ) ;
size_t size = ftell ( f ) ;
fseek ( f , 0 , SEEK_SET ) ;
char * buf = malloc ( size + 1 ) ;
2020-12-28 05:25:42 +03:00
if ( fread ( buf , 1 , size , f ) ! = size ) {
fprintf ( stderr , " Warning: Failed to read file. \n " ) ;
}
2020-12-27 12:55:52 +03:00
fclose ( f ) ;
buf [ size ] = ' \0 ' ;
2020-12-28 04:54:25 +03:00
KrkValue result = krk_interpret ( buf , newScope , fromName , fromFile ) ;
2020-12-27 12:55:52 +03:00
free ( buf ) ;
return result ;
2020-12-26 03:32:21 +03:00
}
2020-12-27 12:55:52 +03:00