2020-10-28 05:16:30 +00:00
# include "cstate.h"
# include "ctable.h"
# include "cobj.h"
# include "cmem.h"
2020-11-17 01:58:16 +00:00
# include "cvm.h"
Added CObjError, cosmoV_throw(), pcall(), and cosmoV_printError()
Errors are now handled very differently, parser errors and VM errors are now treated the same.
When cosmoV_error is called, cosmoV_throw is also called, which formats the error object and sets the panic state.
state->error now points to the latest CObjError when state->panic is true. To get a nice formatted Objection message, use
cosmoV_printError() and pass the state->error. pcall() was added to the standard base library. When called, the first argument
passed is called with the subsequent arguments given. If the call completed successfully, `true`,`nil` is returned. However
when an error occurs during the call, `false`,`<error>` is returned. Simply print the `<error>` to retrieve the error string.
2021-01-06 04:27:59 +00:00
# include "clex.h"
2020-10-28 05:16:30 +00:00
# include <string.h>
// we don't actually hash the whole string :eyes:
uint32_t hashString ( const char * str , size_t sz ) {
uint32_t hash = sz ;
size_t step = ( sz > > 5 ) + 1 ;
2021-01-02 05:06:24 +00:00
for ( size_t i = sz ; i > = step ; i - = step )
2020-10-28 05:16:30 +00:00
hash = ( ( hash < < 5 ) + ( hash > > 2 ) ) + str [ i - 1 ] ;
return hash ;
}
2020-11-06 01:53:55 +00:00
CObj * cosmoO_allocateBase ( CState * state , size_t sz , CObjType type ) {
2020-10-28 05:16:30 +00:00
CObj * obj = ( CObj * ) cosmoM_xmalloc ( state , sz ) ;
obj - > type = type ;
obj - > isMarked = false ;
2021-01-08 01:50:36 +00:00
obj - > proto = state - > protoObjects [ type ] ;
2020-10-28 05:16:30 +00:00
obj - > next = state - > objects ;
state - > objects = obj ;
2020-12-07 21:53:23 +00:00
obj - > nextRoot = NULL ;
2020-10-28 23:29:50 +00:00
# ifdef GC_DEBUG
printf ( " allocated %p with OBJ_TYPE %d \n " , obj , type ) ;
# endif
2020-10-28 05:16:30 +00:00
return obj ;
}
2020-11-06 01:53:55 +00:00
void cosmoO_free ( CState * state , CObj * obj ) {
2020-10-28 05:16:30 +00:00
# ifdef GC_DEBUG
printf ( " freeing %p [ " , obj ) ;
printObject ( obj ) ;
printf ( " ] \n " ) ;
# endif
switch ( obj - > type ) {
case COBJ_STRING : {
CObjString * objStr = ( CObjString * ) obj ;
2020-11-17 09:10:55 +00:00
cosmoM_freearray ( state , char , objStr - > str , objStr - > length + 1 ) ;
2020-10-28 05:16:30 +00:00
cosmoM_free ( state , CObjString , objStr ) ;
break ;
}
2020-11-04 04:10:51 +00:00
case COBJ_OBJECT : {
CObjObject * objTbl = ( CObjObject * ) obj ;
2020-11-03 04:32:39 +00:00
cosmoT_clearTable ( state , & objTbl - > tbl ) ;
2020-11-04 04:10:51 +00:00
cosmoM_free ( state , CObjObject , objTbl ) ;
2020-11-03 04:32:39 +00:00
break ;
}
2021-01-08 20:37:36 +00:00
case COBJ_TABLE : {
CObjTable * tbl = ( CObjTable * ) obj ;
cosmoT_clearTable ( state , & tbl - > tbl ) ;
cosmoM_free ( state , CObjTable , tbl ) ;
2020-12-10 02:32:42 +00:00
break ;
}
2020-10-28 05:16:30 +00:00
case COBJ_UPVALUE : {
cosmoM_free ( state , CObjUpval , obj ) ;
break ;
}
case COBJ_FUNCTION : {
CObjFunction * objFunc = ( CObjFunction * ) obj ;
cleanChunk ( state , & objFunc - > chunk ) ;
cosmoM_free ( state , CObjFunction , objFunc ) ;
break ;
}
case COBJ_CFUNCTION : {
cosmoM_free ( state , CObjCFunction , obj ) ;
break ;
}
2020-11-10 01:44:12 +00:00
case COBJ_METHOD : {
cosmoM_free ( state , CObjMethod , obj ) ; // we don't own the closure or the object so /shrug
break ;
}
Added CObjError, cosmoV_throw(), pcall(), and cosmoV_printError()
Errors are now handled very differently, parser errors and VM errors are now treated the same.
When cosmoV_error is called, cosmoV_throw is also called, which formats the error object and sets the panic state.
state->error now points to the latest CObjError when state->panic is true. To get a nice formatted Objection message, use
cosmoV_printError() and pass the state->error. pcall() was added to the standard base library. When called, the first argument
passed is called with the subsequent arguments given. If the call completed successfully, `true`,`nil` is returned. However
when an error occurs during the call, `false`,`<error>` is returned. Simply print the `<error>` to retrieve the error string.
2021-01-06 04:27:59 +00:00
case COBJ_ERROR : {
CObjError * err = ( CObjError * ) obj ;
cosmoM_freearray ( state , CCallFrame , err - > frames , err - > frameCount ) ;
cosmoM_free ( state , CObjError , obj ) ;
break ;
}
2020-10-28 05:16:30 +00:00
case COBJ_CLOSURE : {
CObjClosure * closure = ( CObjClosure * ) obj ;
cosmoM_freearray ( state , CObjUpval * , closure - > upvalues , closure - > upvalueCount ) ;
cosmoM_free ( state , CObjClosure , closure ) ;
break ;
}
2021-01-08 01:50:36 +00:00
case COBJ_MAX : { /* stubbed, should never happen */ }
2020-10-28 05:16:30 +00:00
}
}
2020-11-06 01:53:55 +00:00
bool cosmoO_equal ( CObj * obj1 , CObj * obj2 ) {
2020-10-28 05:16:30 +00:00
if ( obj1 - > type ! = obj2 - > type )
return false ;
switch ( obj1 - > type ) {
case COBJ_STRING :
return obj1 = = obj2 ; // compare pointers because we already intern all strings :)
2020-11-03 04:32:39 +00:00
case COBJ_CFUNCTION : {
CObjCFunction * cfunc1 = ( CObjCFunction * ) obj1 ;
CObjCFunction * cfunc2 = ( CObjCFunction * ) obj2 ;
return cfunc1 - > cfunc = = cfunc2 - > cfunc ;
}
2020-10-28 05:16:30 +00:00
default :
2020-11-03 04:32:39 +00:00
return false ;
2020-10-28 05:16:30 +00:00
}
}
2020-11-10 01:44:12 +00:00
CObjObject * cosmoO_newObject ( CState * state ) {
CObjObject * obj = ( CObjObject * ) cosmoO_allocateBase ( state , sizeof ( CObjObject ) , COBJ_OBJECT ) ;
2020-11-17 01:58:16 +00:00
obj - > istringFlags = 0 ;
2020-12-18 01:44:04 +00:00
obj - > userP = NULL ; // reserved for C API
2021-01-25 21:04:16 +00:00
cosmoV_pushObj ( state , ( CObj * ) obj ) ; // so our GC can keep track of it
2020-11-10 01:44:12 +00:00
cosmoT_initTable ( state , & obj - > tbl , ARRAY_START ) ;
cosmoV_pop ( state ) ;
2020-11-03 04:32:39 +00:00
2020-11-10 01:44:12 +00:00
return obj ;
2020-11-03 04:32:39 +00:00
}
2021-01-08 20:37:36 +00:00
CObjTable * cosmoO_newTable ( CState * state ) {
CObjTable * obj = ( CObjTable * ) cosmoO_allocateBase ( state , sizeof ( CObjTable ) , COBJ_TABLE ) ;
2020-12-10 02:32:42 +00:00
// init the table (might cause a GC event)
2021-01-25 21:04:16 +00:00
cosmoV_pushObj ( state , ( CObj * ) obj ) ; // so our GC can keep track of obj
2020-12-10 02:32:42 +00:00
cosmoT_initTable ( state , & obj - > tbl , ARRAY_START ) ;
cosmoV_pop ( state ) ;
return obj ;
}
2020-10-28 05:16:30 +00:00
CObjFunction * cosmoO_newFunction ( CState * state ) {
2020-11-06 01:53:55 +00:00
CObjFunction * func = ( CObjFunction * ) cosmoO_allocateBase ( state , sizeof ( CObjFunction ) , COBJ_FUNCTION ) ;
2020-10-28 05:16:30 +00:00
func - > args = 0 ;
func - > upvals = 0 ;
2020-12-27 04:01:22 +00:00
func - > variadic = false ;
2020-10-28 05:16:30 +00:00
func - > name = NULL ;
2020-12-09 18:23:16 +00:00
func - > module = NULL ;
2020-10-28 05:16:30 +00:00
initChunk ( state , & func - > chunk , ARRAY_START ) ;
return func ;
}
CObjCFunction * cosmoO_newCFunction ( CState * state , CosmoCFunction func ) {
2020-11-06 01:53:55 +00:00
CObjCFunction * cfunc = ( CObjCFunction * ) cosmoO_allocateBase ( state , sizeof ( CObjCFunction ) , COBJ_CFUNCTION ) ;
2020-10-28 05:16:30 +00:00
cfunc - > cfunc = func ;
return cfunc ;
}
Added CObjError, cosmoV_throw(), pcall(), and cosmoV_printError()
Errors are now handled very differently, parser errors and VM errors are now treated the same.
When cosmoV_error is called, cosmoV_throw is also called, which formats the error object and sets the panic state.
state->error now points to the latest CObjError when state->panic is true. To get a nice formatted Objection message, use
cosmoV_printError() and pass the state->error. pcall() was added to the standard base library. When called, the first argument
passed is called with the subsequent arguments given. If the call completed successfully, `true`,`nil` is returned. However
when an error occurs during the call, `false`,`<error>` is returned. Simply print the `<error>` to retrieve the error string.
2021-01-06 04:27:59 +00:00
CObjError * cosmoO_newError ( CState * state , CValue err ) {
CObjError * cerror = ( CObjError * ) cosmoO_allocateBase ( state , sizeof ( CObjError ) , COBJ_ERROR ) ;
cerror - > err = err ;
cerror - > frameCount = state - > frameCount ;
2021-01-12 23:46:22 +00:00
cerror - > parserError = false ;
Added CObjError, cosmoV_throw(), pcall(), and cosmoV_printError()
Errors are now handled very differently, parser errors and VM errors are now treated the same.
When cosmoV_error is called, cosmoV_throw is also called, which formats the error object and sets the panic state.
state->error now points to the latest CObjError when state->panic is true. To get a nice formatted Objection message, use
cosmoV_printError() and pass the state->error. pcall() was added to the standard base library. When called, the first argument
passed is called with the subsequent arguments given. If the call completed successfully, `true`,`nil` is returned. However
when an error occurs during the call, `false`,`<error>` is returned. Simply print the `<error>` to retrieve the error string.
2021-01-06 04:27:59 +00:00
// allocate the callframe
cerror - > frames = cosmoM_xmalloc ( state , sizeof ( CCallFrame ) * cerror - > frameCount ) ;
// clone the call frame
for ( int i = 0 ; i < state - > frameCount ; i + + )
cerror - > frames [ i ] = state - > callFrame [ i ] ;
return cerror ;
}
2021-01-08 01:50:36 +00:00
CObjMethod * cosmoO_newMethod ( CState * state , CValue func , CObj * obj ) {
2020-11-13 05:04:09 +00:00
CObjMethod * method = ( CObjMethod * ) cosmoO_allocateBase ( state , sizeof ( CObjMethod ) , COBJ_METHOD ) ;
2021-01-08 01:50:36 +00:00
method - > func = func ;
2020-11-10 01:44:12 +00:00
method - > obj = obj ;
return method ;
}
2020-10-28 05:16:30 +00:00
CObjClosure * cosmoO_newClosure ( CState * state , CObjFunction * func ) {
2020-12-19 19:32:43 +00:00
// initialize array of pointers
2020-10-28 05:16:30 +00:00
CObjUpval * * upvalues = cosmoM_xmalloc ( state , sizeof ( CObjUpval * ) * func - > upvals ) ;
for ( int i = 0 ; i < func - > upvals ; i + + ) {
upvalues [ i ] = NULL ;
}
2020-11-06 01:53:55 +00:00
CObjClosure * closure = ( CObjClosure * ) cosmoO_allocateBase ( state , sizeof ( CObjClosure ) , COBJ_CLOSURE ) ;
2020-10-28 05:16:30 +00:00
closure - > function = func ;
closure - > upvalues = upvalues ;
closure - > upvalueCount = func - > upvals ;
return closure ;
}
CObjUpval * cosmoO_newUpvalue ( CState * state , CValue * val ) {
2020-11-06 01:53:55 +00:00
CObjUpval * upval = ( CObjUpval * ) cosmoO_allocateBase ( state , sizeof ( CObjUpval ) , COBJ_UPVALUE ) ;
2020-10-28 05:16:30 +00:00
upval - > val = val ;
upval - > closed = cosmoV_newNil ( ) ;
upval - > next = NULL ;
return upval ;
}
2021-01-03 23:33:10 +00:00
CObjString * cosmoO_copyString ( CState * state , const char * str , size_t length ) {
uint32_t hash = hashString ( str , length ) ;
CObjString * lookup = cosmoT_lookupString ( & state - > strings , str , length , hash ) ;
2020-10-28 05:16:30 +00:00
// have we already interned this string?
if ( lookup ! = NULL )
return lookup ;
2021-01-03 23:33:10 +00:00
char * buf = cosmoM_xmalloc ( state , sizeof ( char ) * ( length + 1 ) ) ; // +1 for null terminator
memcpy ( buf , str , length ) ; // copy string to heap
buf [ length ] = ' \0 ' ; // don't forget our null terminator
2020-10-28 05:16:30 +00:00
2021-01-03 23:33:10 +00:00
return cosmoO_allocateString ( state , buf , length , hash ) ;
2020-10-28 05:16:30 +00:00
}
2021-02-02 03:07:43 +00:00
// length shouldn't include the null terminator! str should be a null terminated string! (char array should also have been allocated using cosmoM_xmalloc!)
2021-01-03 23:33:10 +00:00
CObjString * cosmoO_takeString ( CState * state , char * str , size_t length ) {
uint32_t hash = hashString ( str , length ) ;
2020-10-28 05:16:30 +00:00
2021-01-03 23:33:10 +00:00
CObjString * lookup = cosmoT_lookupString ( & state - > strings , str , length , hash ) ;
2020-10-28 05:16:30 +00:00
// have we already interned this string?
if ( lookup ! = NULL ) {
2021-01-03 23:33:10 +00:00
cosmoM_freearray ( state , char , str , length + 1 ) ; // free our passed character array, it's unneeded!
2020-10-28 05:16:30 +00:00
return lookup ;
}
2021-01-03 23:33:10 +00:00
return cosmoO_allocateString ( state , str , length , hash ) ;
2020-10-28 05:16:30 +00:00
}
CObjString * cosmoO_allocateString ( CState * state , const char * str , size_t sz , uint32_t hash ) {
2020-11-06 01:53:55 +00:00
CObjString * strObj = ( CObjString * ) cosmoO_allocateBase ( state , sizeof ( CObjString ) , COBJ_STRING ) ;
2020-12-10 02:46:20 +00:00
strObj - > isIString = false ;
2020-10-28 05:16:30 +00:00
strObj - > str = ( char * ) str ;
strObj - > length = sz ;
strObj - > hash = hash ;
// we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we *want* a GC event to happen)
2021-01-25 21:04:16 +00:00
cosmoV_pushObj ( state , ( CObj * ) strObj ) ;
2020-10-28 05:16:30 +00:00
cosmoT_insert ( state , & state - > strings , cosmoV_newObj ( ( CObj * ) strObj ) ) ;
cosmoV_pop ( state ) ;
return strObj ;
}
2020-12-22 21:13:11 +00:00
CObjString * cosmoO_pushVFString ( CState * state , const char * format , va_list args ) {
StkPtr start = state - > top ;
while ( true ) {
const char * end = strchr ( format , ' % ' ) ; // grab the next occurrence of '%'
if ( end = = NULL ) // the end, no '%' found
break ;
// push the string before '%'
cosmoV_pushLString ( state , format , ( end - format ) ) ;
char c = * ( end + 1 ) ; // the character right after '%'
switch ( c ) {
case ' d ' : { // int
cosmoV_pushNumber ( state , va_arg ( args , int ) ) ;
break ;
}
case ' f ' : { // double
cosmoV_pushNumber ( state , va_arg ( args , double ) ) ;
break ;
}
case ' s ' : { // const char *
cosmoV_pushString ( state , va_arg ( args , char * ) ) ;
break ;
}
Added CObjError, cosmoV_throw(), pcall(), and cosmoV_printError()
Errors are now handled very differently, parser errors and VM errors are now treated the same.
When cosmoV_error is called, cosmoV_throw is also called, which formats the error object and sets the panic state.
state->error now points to the latest CObjError when state->panic is true. To get a nice formatted Objection message, use
cosmoV_printError() and pass the state->error. pcall() was added to the standard base library. When called, the first argument
passed is called with the subsequent arguments given. If the call completed successfully, `true`,`nil` is returned. However
when an error occurs during the call, `false`,`<error>` is returned. Simply print the `<error>` to retrieve the error string.
2021-01-06 04:27:59 +00:00
case ' t ' : { // CToken *
CToken * token = va_arg ( args , CToken * ) ;
cosmoV_pushLString ( state , token - > start , token - > length ) ;
break ;
}
2020-12-22 21:13:11 +00:00
default : {
char temp [ 2 ] ;
temp [ 0 ] = ' % ' ;
temp [ 1 ] = c ;
cosmoV_pushLString ( state , temp , 2 ) ;
}
}
format = end + 2 ; // + 2 because of % and the following character
}
cosmoV_pushString ( state , format ) ; // push the rest of the string
cosmoV_concat ( state , state - > top - start ) ; // use cosmoV_concat to concat all the strings on the stack
return cosmoV_readString ( * start ) ; // start should be state->top - 1
}
2021-02-03 19:43:26 +00:00
// walks the protos of obj and checks for proto
bool cosmoO_isDescendant ( CObj * obj , CObjObject * proto ) {
CObjObject * curr = obj - > proto ;
while ( curr ! = NULL ) {
if ( curr = = proto )
return true ; // found proto! return true
curr = ( ( CObj * ) curr ) - > proto ;
}
// we didn't find the proto
return false ;
}
2021-01-13 00:27:29 +00:00
// returns false if error thrown
bool cosmoO_getRawObject ( CState * state , CObjObject * proto , CValue key , CValue * val , CObj * obj ) {
if ( ! cosmoT_get ( & proto - > tbl , key , val ) ) { // if the field doesn't exist in the object, check the proto
if ( cosmoO_getIString ( state , proto , ISTRING_GETTER , val ) & & IS_TABLE ( * val ) & & cosmoT_get ( & cosmoV_readTable ( * val ) - > tbl , key , val ) ) {
2020-12-10 02:32:42 +00:00
cosmoV_pushValue ( state , * val ) ; // push function
2021-01-25 21:04:16 +00:00
cosmoV_pushObj ( state , ( CObj * ) obj ) ; // push object
2021-01-03 23:33:10 +00:00
if ( cosmoV_call ( state , 1 , 1 ) ! = COSMOVM_OK ) // call the function with the 1 argument
return false ;
2020-12-10 02:32:42 +00:00
* val = * cosmoV_pop ( state ) ; // set value to the return value of __index
return true ;
2020-11-17 01:58:16 +00:00
}
2021-01-13 00:27:29 +00:00
if ( proto - > _obj . proto ! = NULL & & cosmoO_getRawObject ( state , proto - > _obj . proto , key , val , obj ) )
2020-12-10 02:32:42 +00:00
return true ;
2021-01-08 01:50:36 +00:00
* val = cosmoV_newNil ( ) ;
2021-01-13 00:27:29 +00:00
return true ; // no protoobject to check against / key not found
2020-11-10 01:44:12 +00:00
}
return true ;
2020-11-06 01:53:55 +00:00
}
2021-01-13 00:27:29 +00:00
void cosmoO_setRawObject ( CState * state , CObjObject * proto , CValue key , CValue val , CObj * obj ) {
2020-11-24 21:16:37 +00:00
CValue ret ;
2020-12-06 20:11:33 +00:00
2020-12-10 02:32:42 +00:00
// first check for __setters
2021-01-13 00:27:29 +00:00
if ( cosmoO_getIString ( state , proto , ISTRING_SETTER , & ret ) & & IS_TABLE ( ret ) & & cosmoT_get ( & cosmoV_readTable ( ret ) - > tbl , key , & ret ) ) {
2020-12-10 02:32:42 +00:00
cosmoV_pushValue ( state , ret ) ; // push function
2021-01-25 21:04:16 +00:00
cosmoV_pushObj ( state , ( CObj * ) obj ) ; // push object
2020-12-10 02:32:42 +00:00
cosmoV_pushValue ( state , val ) ; // push new value
2020-12-13 03:53:12 +00:00
cosmoV_call ( state , 2 , 0 ) ;
2020-12-10 02:32:42 +00:00
return ;
2020-11-24 21:16:37 +00:00
}
2020-12-10 02:46:20 +00:00
// if the key is an IString, we need to reset the cache
2021-01-08 01:50:36 +00:00
if ( IS_STRING ( key ) & & cosmoV_readString ( key ) - > isIString )
2021-01-13 00:27:29 +00:00
proto - > istringFlags = 0 ; // reset cache
2020-12-10 02:46:20 +00:00
2020-11-24 21:16:37 +00:00
if ( IS_NIL ( val ) ) { // if we're setting an index to nil, we can safely mark that as a tombstone
2021-01-13 00:27:29 +00:00
cosmoT_remove ( state , & proto - > tbl , key ) ;
2020-11-24 21:16:37 +00:00
} else {
2021-01-13 00:27:29 +00:00
CValue * newVal = cosmoT_insert ( state , & proto - > tbl , key ) ;
2020-11-24 21:16:37 +00:00
* newVal = val ;
}
2020-11-06 01:53:55 +00:00
}
2020-12-18 01:44:04 +00:00
void cosmoO_setUserP ( CState * state , CObjObject * object , void * p ) {
object - > userP = p ;
2020-12-06 19:38:05 +00:00
}
2020-12-18 01:44:04 +00:00
void * cosmoO_getUserP ( CState * state , CObjObject * object ) {
return object - > userP ;
}
void cosmoO_setUserI ( CState * state , CObjObject * object , int i ) {
object - > userI = i ;
}
int cosmoO_getUserI ( CState * state , CObjObject * object ) {
return object - > userI ;
2020-12-06 19:38:05 +00:00
}
2020-12-10 02:32:42 +00:00
bool rawgetIString ( CState * state , CObjObject * object , int flag , CValue * val ) {
2020-11-17 01:58:16 +00:00
if ( readFlag ( object - > istringFlags , flag ) )
return false ; // it's been cached as bad
2020-12-10 02:32:42 +00:00
if ( ! cosmoT_get ( & object - > tbl , cosmoV_newObj ( state - > iStrings [ flag ] ) , val ) ) {
2020-11-17 01:58:16 +00:00
// mark it bad!
setFlagOn ( object - > istringFlags , flag ) ;
return false ;
}
return true ; // :)
}
2020-12-10 02:32:42 +00:00
bool cosmoO_getIString ( CState * state , CObjObject * object , int flag , CValue * val ) {
CObjObject * obj = object ;
do {
if ( rawgetIString ( state , obj , flag , val ) )
return true ;
2021-01-08 01:50:36 +00:00
} while ( ( obj = obj - > _obj . proto ) ! = NULL ) ; // sets obj to it's proto and compares it to NULL
2020-12-10 02:32:42 +00:00
return false ; // obj->proto was false, the istring doesn't exist in this object chain
}
bool cosmoO_indexObject ( CState * state , CObjObject * object , CValue key , CValue * val ) {
if ( cosmoO_getIString ( state , object , ISTRING_INDEX , val ) ) {
cosmoV_pushValue ( state , * val ) ; // push function
2021-01-25 21:04:16 +00:00
cosmoV_pushObj ( state , ( CObj * ) object ) ; // push object
2020-12-10 02:32:42 +00:00
cosmoV_pushValue ( state , key ) ; // push key
2021-01-03 23:33:10 +00:00
if ( cosmoV_call ( state , 2 , 1 ) ! = COSMOVM_OK ) // call the function with the 2 arguments
return false ;
2020-12-10 02:32:42 +00:00
* val = * cosmoV_pop ( state ) ; // set value to the return value of __index
return true ;
} else { // there's no __index function defined!
cosmoV_error ( state , " Couldn't index object without __index function! " ) ;
}
return false ;
}
bool cosmoO_newIndexObject ( CState * state , CObjObject * object , CValue key , CValue val ) {
CValue ret ; // return value for cosmoO_getIString
if ( cosmoO_getIString ( state , object , ISTRING_NEWINDEX , & ret ) ) {
cosmoV_pushValue ( state , ret ) ; // push function
2021-01-25 21:04:16 +00:00
cosmoV_pushObj ( state , ( CObj * ) object ) ; // push object
2020-12-10 02:32:42 +00:00
cosmoV_pushValue ( state , key ) ; // push key & value pair
cosmoV_pushValue ( state , val ) ;
2021-01-03 23:33:10 +00:00
return cosmoV_call ( state , 3 , 0 ) = = COSMOVM_OK ;
2020-12-10 02:32:42 +00:00
} else { // there's no __newindex function defined
cosmoV_error ( state , " Couldn't set index on object without __newindex function! " ) ;
}
return false ;
}
2020-11-10 01:44:12 +00:00
CObjString * cosmoO_toString ( CState * state , CObj * obj ) {
2021-01-08 01:50:36 +00:00
CObjObject * protoObject = cosmoO_grabProto ( obj ) ;
CValue res ;
// use user-defined __tostring
if ( protoObject ! = NULL & & cosmoO_getIString ( state , protoObject , ISTRING_TOSTRING , & res ) ) {
cosmoV_pushValue ( state , res ) ;
2021-01-25 21:04:16 +00:00
cosmoV_pushObj ( state , ( CObj * ) obj ) ;
2021-01-08 01:50:36 +00:00
if ( cosmoV_call ( state , 1 , 1 ) ! = COSMOVM_OK )
return cosmoO_copyString ( state , " <err> " , 5 ) ;
// make sure the __tostring function returned a string
StkPtr ret = cosmoV_getTop ( state , 0 ) ;
if ( ! IS_STRING ( * ret ) ) {
cosmoV_error ( state , " __tostring expected to return <string>, got %s! " , cosmoV_typeStr ( * ret ) ) ;
return cosmoO_copyString ( state , " <err> " , 5 ) ;
}
// return string
cosmoV_pop ( state ) ;
return ( CObjString * ) cosmoV_readObj ( * ret ) ;
}
switch ( obj - > type ) {
2020-10-28 05:16:30 +00:00
case COBJ_STRING : {
2020-11-10 01:44:12 +00:00
return ( CObjString * ) obj ;
2020-10-28 05:16:30 +00:00
}
2020-12-10 02:32:42 +00:00
case COBJ_CLOSURE : { // should be transparent to the user imo
CObjClosure * closure = ( CObjClosure * ) obj ;
return cosmoO_toString ( state , ( CObj * ) closure - > function ) ;
}
2020-10-28 05:16:30 +00:00
case COBJ_FUNCTION : {
2020-11-10 01:44:12 +00:00
CObjFunction * func = ( CObjFunction * ) obj ;
2020-10-28 05:16:30 +00:00
return func - > name ! = NULL ? func - > name : cosmoO_copyString ( state , UNNAMEDCHUNK , strlen ( UNNAMEDCHUNK ) ) ;
}
2021-01-25 22:14:51 +00:00
case COBJ_CFUNCTION : {
CObjCFunction * cfunc = ( CObjCFunction * ) obj ;
char buf [ 64 ] ;
int sz = sprintf ( buf , " <c function> %p " , ( void * ) cfunc - > cfunc ) + 1 ; // +1 for the null character
return cosmoO_copyString ( state , buf , sz ) ;
}
2021-01-03 23:33:10 +00:00
case COBJ_OBJECT : {
2021-01-08 01:50:36 +00:00
char buf [ 64 ] ;
int sz = sprintf ( buf , " <obj> %p " , ( void * ) obj ) + 1 ; // +1 for the null character
return cosmoO_copyString ( state , buf , sz ) ;
2020-11-03 04:32:39 +00:00
}
Added CObjError, cosmoV_throw(), pcall(), and cosmoV_printError()
Errors are now handled very differently, parser errors and VM errors are now treated the same.
When cosmoV_error is called, cosmoV_throw is also called, which formats the error object and sets the panic state.
state->error now points to the latest CObjError when state->panic is true. To get a nice formatted Objection message, use
cosmoV_printError() and pass the state->error. pcall() was added to the standard base library. When called, the first argument
passed is called with the subsequent arguments given. If the call completed successfully, `true`,`nil` is returned. However
when an error occurs during the call, `false`,`<error>` is returned. Simply print the `<error>` to retrieve the error string.
2021-01-06 04:27:59 +00:00
case COBJ_ERROR : {
CObjError * err = ( CObjError * ) obj ;
return cosmoV_toString ( state , err - > err ) ;
}
2021-01-08 20:37:36 +00:00
case COBJ_TABLE : {
2020-12-10 02:32:42 +00:00
char buf [ 64 ] ;
2021-01-08 20:37:36 +00:00
int sz = sprintf ( buf , " <tbl> %p " , ( void * ) obj ) + 1 ; // +1 for the null character
2020-12-10 02:32:42 +00:00
return cosmoO_copyString ( state , buf , sz ) ;
}
2021-01-25 22:14:51 +00:00
default : {
char buf [ 64 ] ;
int sz = sprintf ( buf , " <unkn obj> %p " , ( void * ) obj ) + 1 ; // +1 for the null character
return cosmoO_copyString ( state , buf , sz ) ;
}
2020-10-28 05:16:30 +00:00
}
}
2021-01-13 20:13:55 +00:00
cosmo_Number cosmoO_toNumber ( CState * state , CObj * obj ) {
2021-01-23 21:30:30 +00:00
CObjObject * proto = cosmoO_grabProto ( obj ) ;
CValue res ;
if ( proto ! = NULL & & cosmoO_getIString ( state , proto , ISTRING_TONUMBER , & res ) ) {
cosmoV_pushValue ( state , res ) ;
2021-01-25 21:04:16 +00:00
cosmoV_pushObj ( state , ( CObj * ) obj ) ;
2021-01-23 21:30:30 +00:00
if ( cosmoV_call ( state , 1 , 1 ) ! = COSMOVM_OK ) // call res, expect 1 return val of <number>
return 0 ;
StkPtr temp = cosmoV_getTop ( state , 0 ) ;
if ( ! IS_NUMBER ( * temp ) ) {
cosmoV_error ( state , " __tonumber expected to return <number>, got %s! " , cosmoV_typeStr ( * temp ) ) ;
return 0 ;
}
// return number
cosmoV_pop ( state ) ;
return cosmoV_readNumber ( * temp ) ;
}
2021-01-13 20:13:55 +00:00
switch ( obj - > type ) {
case COBJ_STRING : {
CObjString * str = ( CObjString * ) obj ;
return strtod ( str - > str , NULL ) ;
}
default : // maybe in the future throw an error?
return 0 ;
}
}
2021-01-22 21:22:30 +00:00
int cosmoO_count ( CState * state , CObj * obj ) {
CObjObject * proto = cosmoO_grabProto ( obj ) ;
CValue res ;
if ( proto ! = NULL & & cosmoO_getIString ( state , proto , ISTRING_COUNT , & res ) ) {
cosmoV_pushValue ( state , res ) ;
2021-01-25 21:04:16 +00:00
cosmoV_pushObj ( state , ( CObj * ) obj ) ;
2021-01-23 21:30:30 +00:00
if ( cosmoV_call ( state , 1 , 1 ) ! = COSMOVM_OK ) // call res, we expect 1 return value of type <number>
2021-01-22 21:22:30 +00:00
return 0 ;
StkPtr ret = cosmoV_getTop ( state , 0 ) ;
if ( ! IS_NUMBER ( * ret ) ) {
cosmoV_error ( state , " __count expected to return <number>, got %s! " , cosmoV_typeStr ( * ret ) ) ;
return 0 ;
}
// return number
cosmoV_pop ( state ) ;
return ( int ) cosmoV_readNumber ( * ret ) ;
}
switch ( obj - > type ) {
case COBJ_TABLE : { // returns the # of entries in the hash table
CObjTable * tbl = ( CObjTable * ) obj ;
return cosmoT_count ( & tbl - > tbl ) ;
}
case COBJ_STRING : { // returns the length of the string
CObjString * str = ( CObjString * ) obj ;
return str - > length ;
}
default :
cosmoV_error ( state , " Couldn't get # (count) of %s! " , cosmoO_typeStr ( obj ) ) ;
return 0 ;
}
}
2020-10-28 05:16:30 +00:00
void printObject ( CObj * o ) {
switch ( o - > type ) {
case COBJ_STRING : {
CObjString * objStr = ( CObjString * ) o ;
2021-01-12 23:46:22 +00:00
printf ( " %.*s " , objStr - > length , objStr - > str ) ;
2020-10-28 05:16:30 +00:00
break ;
}
2020-11-04 04:10:51 +00:00
case COBJ_OBJECT : {
2021-01-02 05:06:24 +00:00
printf ( " <obj> %p " , ( void * ) o ) ;
2020-12-10 02:32:42 +00:00
break ;
}
2021-01-08 20:37:36 +00:00
case COBJ_TABLE : {
CObjTable * tbl = ( CObjTable * ) o ;
printf ( " <tbl> %p " , ( void * ) tbl ) ;
2020-12-10 02:32:42 +00:00
break ;
2020-11-03 04:32:39 +00:00
}
2020-10-28 05:16:30 +00:00
case COBJ_FUNCTION : {
CObjFunction * objFunc = ( CObjFunction * ) o ;
if ( objFunc - > name ! = NULL )
printf ( " <function> %.*s " , objFunc - > name - > length , objFunc - > name - > str ) ;
else
2020-11-10 01:44:12 +00:00
printf ( " <function> %s " , UNNAMEDCHUNK ) ;
2020-10-28 05:16:30 +00:00
break ;
}
case COBJ_CFUNCTION : {
CObjCFunction * objCFunc = ( CObjCFunction * ) o ;
2021-01-02 05:06:24 +00:00
printf ( " <c function> %p " , ( void * ) objCFunc - > cfunc ) ;
2020-10-28 05:16:30 +00:00
break ;
}
2021-01-12 23:46:22 +00:00
case COBJ_ERROR : {
CObjError * err = ( CObjError * ) o ;
printf ( " <error> %p -> " , ( void * ) o ) ;
printValue ( err - > err ) ;
break ;
}
2020-11-10 01:44:12 +00:00
case COBJ_METHOD : {
CObjMethod * method = ( CObjMethod * ) o ;
2021-01-12 23:46:22 +00:00
printf ( " <method> %p -> " , ( void * ) method ) ;
2020-11-17 21:07:56 +00:00
printValue ( method - > func ) ;
2020-11-10 01:44:12 +00:00
break ;
}
2021-01-12 23:46:22 +00:00
case COBJ_CLOSURE : {
CObjClosure * closure = ( CObjClosure * ) o ;
printf ( " <closure> %p -> " , ( void * ) closure ) ;
printObject ( ( CObj * ) closure - > function ) ; // just print the function
break ;
}
case COBJ_UPVALUE : {
CObjUpval * upval = ( CObjUpval * ) o ;
printf ( " <upvalue> %p -> " , ( void * ) upval - > val ) ;
printValue ( * upval - > val ) ;
break ;
}
2020-10-28 05:16:30 +00:00
default :
2021-01-02 05:06:24 +00:00
printf ( " <unkn obj %p> " , ( void * ) o ) ;
2020-10-28 05:16:30 +00:00
}
2020-11-30 18:32:04 +00:00
}
const char * cosmoO_typeStr ( CObj * obj ) {
switch ( obj - > type ) {
case COBJ_STRING : return " <string> " ;
case COBJ_OBJECT : return " <object> " ;
2021-01-08 20:37:36 +00:00
case COBJ_TABLE : return " <table> " ;
2020-11-30 18:32:04 +00:00
case COBJ_FUNCTION : return " <function> " ;
case COBJ_CFUNCTION : return " <c function> " ;
case COBJ_METHOD : return " <method> " ;
case COBJ_CLOSURE : return " <closure> " ;
case COBJ_UPVALUE : return " <upvalue> " ;
default :
return " <unkn obj> " ; // TODO: maybe panic? could be a malformed object :eyes:
}
2021-01-02 05:06:24 +00:00
}