2020-10-28 05:16:30 +00:00
# include "cvm.h"
# include "cstate.h"
# include "cdebug.h"
# include "cmem.h"
# include <stdarg.h>
# include <string.h>
2021-01-01 06:47:15 +00:00
# include <math.h>
2020-12-22 21:13:11 +00:00
COSMO_API void cosmoV_pushFString ( CState * state , const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
cosmoO_pushVFString ( state , format , args ) ;
va_end ( args ) ;
}
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
// inserts val at state->top - indx - 1, moving everything else up
COSMO_API void cosmo_insert ( CState * state , int indx , CValue val ) {
StkPtr tmp = cosmoV_getTop ( state , indx ) ;
// moves everything up
for ( StkPtr i = state - > top ; i > tmp ; i - - )
* i = * ( i - 1 ) ;
* tmp = val ;
state - > top + + ;
}
2020-10-28 05:16:30 +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
void cosmoV_printError ( CState * state , CObjError * err ) {
2020-10-28 05:16:30 +00:00
// print stack trace
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
for ( int i = 0 ; i < err - > frameCount ; i + + ) {
CCallFrame * frame = & err - > frames [ i ] ;
2020-10-28 05:16:30 +00:00
CObjFunction * function = frame - > closure - > function ;
CChunk * chunk = & function - > chunk ;
int line = chunk - > lineInfo [ frame - > pc - chunk - > buf - 1 ] ;
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
if ( i = = err - > frameCount - 1 & & ! err - > parserError ) // it's the last call frame (and not a parser error), prepare for the objection to be printed
2020-12-09 18:23:16 +00:00
fprintf ( stderr , " Objection in %.*s on [line %d] in " , function - > module - > length , function - > module - > str , line ) ;
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
else
2020-10-28 05:16:30 +00:00
fprintf ( stderr , " [line %d] in " , line ) ;
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
if ( function - > name = = NULL ) { // unnamed chunk
fprintf ( stderr , " %s \n " , UNNAMEDCHUNK ) ;
} else {
fprintf ( stderr , " %.*s() \n " , function - > name - > length , function - > name - > str ) ;
2020-10-28 05:16:30 +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
if ( err - > parserError )
fprintf ( stderr , " Objection while parsing on [line %d] \n " , err - > line ) ;
// finally, print the error message
CObjString * errString = cosmoV_toString ( state , err - > err ) ;
printf ( " \t %.*s \n " , errString - > length , errString - > str ) ;
}
/*
takes value on top of the stack and wraps an CObjError around it , state - > error is set to that value
the value on the stack is * expected * to be a string , but not required , so
yes , this means you could throw a nil value if you really wanted too . .
*/
CObjError * cosmoV_throw ( CState * state ) {
StkPtr temp = cosmoV_getTop ( state , 0 ) ;
CObjError * error = cosmoO_newError ( state , * temp ) ;
state - > error = error ;
state - > panic = true ;
cosmoV_pop ( state ) ; // pops thrown value off the stack
return error ;
}
void cosmoV_error ( CState * state , const char * format , . . . ) {
if ( state - > panic )
return ;
// i set panic before calling cosmoO_pushVFString, since that can also call cosmoV_error
state - > panic = true ;
// format the error string and push it onto the stack
2020-10-28 05:16:30 +00:00
va_list args ;
va_start ( args , format ) ;
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
cosmoO_pushVFString ( state , format , args ) ;
2020-10-28 05:16:30 +00:00
va_end ( args ) ;
2020-12-16 03:21:51 +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
// throw the error onto the state
cosmoV_throw ( state ) ;
2020-10-28 05:16:30 +00:00
}
CObjUpval * captureUpvalue ( CState * state , CValue * local ) {
CObjUpval * prev = NULL ;
CObjUpval * upvalue = state - > openUpvalues ;
while ( upvalue ! = NULL & & upvalue - > val > local ) { // while upvalue exists and is higher on the stack than local
prev = upvalue ;
upvalue = upvalue - > next ;
}
if ( upvalue ! = NULL & & upvalue - > val = = local ) { // we found the local we were going to capture
return upvalue ;
}
CObjUpval * newUpval = cosmoO_newUpvalue ( state , local ) ;
newUpval - > next = upvalue ;
// the list is sorted, so insert it at our found upvalue
if ( prev = = NULL ) {
state - > openUpvalues = newUpval ;
} else {
prev - > next = newUpval ;
}
return newUpval ;
}
void closeUpvalues ( CState * state , CValue * local ) {
while ( state - > openUpvalues ! = NULL & & state - > openUpvalues - > val > = local ) { // for every upvalue that points to the local or anything above it
CObjUpval * upvalue = state - > openUpvalues ;
upvalue - > closed = * upvalue - > val ;
upvalue - > val = & upvalue - > closed ; // upvalue now points to itself :P
state - > openUpvalues = upvalue - > next ;
}
}
void pushCallFrame ( CState * state , CObjClosure * closure , int args ) {
2021-01-02 02:02:36 +00:00
# ifdef SAFE_STACK
2021-01-02 01:20:24 +00:00
if ( state - > frameCount > = FRAME_MAX ) {
cosmoV_error ( state , " Callframe overflow! " ) ;
return ;
}
2021-01-02 02:02:36 +00:00
# endif
2021-01-02 01:20:24 +00:00
2020-10-28 05:16:30 +00:00
CCallFrame * frame = & state - > callFrame [ state - > frameCount + + ] ;
frame - > base = state - > top - args - 1 ; // - 1 for the function
frame - > pc = closure - > function - > chunk . buf ;
frame - > closure = closure ;
}
2020-11-13 23:39:47 +00:00
// offset is the offset of the callframe base we set the state->top back too (useful for passing values in the stack as arguments, like methods)
void popCallFrame ( CState * state , int offset ) {
2020-10-28 05:16:30 +00:00
closeUpvalues ( state , state - > callFrame [ state - > frameCount - 1 ] . base ) ; // close any upvalue still open
2020-11-13 23:39:47 +00:00
state - > top = state - > callFrame [ state - > frameCount - 1 ] . base + offset ; // resets the stack
2020-10-28 05:16:30 +00:00
state - > frameCount - - ;
}
2020-12-22 21:13:11 +00:00
void cosmoV_concat ( CState * state , int vals ) {
StkPtr start = state - > top - vals ;
StkPtr end = cosmoV_getTop ( state , 0 ) ;
CObjString * result = cosmoV_toString ( state , * start ) ;
for ( StkPtr current = start + 1 ; current < = end ; current + + ) {
cosmoV_pushValue ( state , cosmoV_newObj ( result ) ) ; // so our GC can find our current result string
CObjString * otherStr = cosmoV_toString ( state , * current ) ;
cosmoV_pushValue ( state , cosmoV_newObj ( otherStr ) ) ; // also so our GC won't free otherStr
// concat the two strings together
size_t sz = result - > length + otherStr - > length ;
char * buf = cosmoM_xmalloc ( state , sz + 1 ) ; // +1 for null terminator
memcpy ( buf , result - > str , result - > length ) ;
memcpy ( buf + result - > length , otherStr - > str , otherStr - > length ) ;
buf [ sz ] = ' \0 ' ;
result = cosmoO_takeString ( state , buf , sz ) ;
cosmoV_setTop ( state , 2 ) ; // pop result & otherStr off the stack
}
2020-10-28 05:16:30 +00:00
2020-12-22 21:13:11 +00:00
state - > top = start ;
cosmoV_pushValue ( state , cosmoV_newObj ( result ) ) ;
2020-10-28 05:16:30 +00:00
}
2020-12-13 03:53:12 +00:00
int cosmoV_execute ( CState * state ) ;
2021-01-04 22:04:38 +00:00
bool invokeMethod ( CState * state , CObjObject * obj , CValue func , int args , int nresults , int offset ) ;
2020-10-28 05:16:30 +00:00
2021-01-04 22:04:38 +00:00
/*
calls a native C Function with # args on the stack , nresults are pushed onto the stack upon return .
returns :
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
false : state paniced during C Function , error is at state - > error
2021-01-04 22:04:38 +00:00
true : state - > top is moved to base + offset + nresults , with nresults pushed onto the stack from base + offset
*/
static bool callCFunction ( CState * state , CosmoCFunction cfunc , int args , int nresults , int offset ) {
2020-11-13 23:39:47 +00:00
StkPtr savedBase = cosmoV_getTop ( state , args ) ;
2021-01-04 22:04:38 +00:00
// we don't want a GC event during c api because we don't actually trust the user to know how to evade the GC
cosmoM_freezeGC ( state ) ;
2020-12-13 03:53:12 +00:00
int nres = cfunc ( state , args , savedBase + 1 ) ;
2020-11-13 23:39:47 +00:00
cosmoM_unfreezeGC ( state ) ;
2020-12-13 03:53:12 +00:00
2021-01-03 23:33:10 +00:00
2021-01-04 22:04:38 +00:00
// caller function wasn't expecting this many return values, cap it
if ( nres > nresults )
2020-12-16 09:58:56 +00:00
nres = nresults ;
// remember where the return values are
2020-12-27 04:01:22 +00:00
StkPtr results = cosmoV_getTop ( state , nres - 1 ) ;
2020-12-13 03:53:12 +00:00
state - > top = savedBase + offset ; // set stack
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
// if the state paniced during the c function, return false
if ( state - > panic )
return false ;
2020-12-13 03:53:12 +00:00
// push the return value back onto the stack
2020-12-18 01:44:04 +00:00
memmove ( state - > top , results , sizeof ( CValue ) * nres ) ; // copies the return values to the top of the stack
2020-12-13 03:53:12 +00:00
state - > top + = nres ; // and make sure to move state->top to match
// now, if the caller function expected more return values, push nils onto the stack
for ( int i = nres ; i < nresults ; i + + )
cosmoV_pushValue ( state , cosmoV_newNil ( ) ) ;
2021-01-03 23:33:10 +00:00
return true ;
2020-11-13 23:39:47 +00:00
}
2021-01-04 22:04:38 +00:00
/*
calls a raw closure object with # args on the stack , nresults are pushed onto the stack upon return .
returns :
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
false : state paniced , error is at state - > error
2021-01-04 22:04:38 +00:00
true : stack - > top is moved to base + offset + nresults , with nresults pushed onto the stack from base + offset
*/
static bool rawCall ( CState * state , CObjClosure * closure , int args , int nresults , int offset ) {
2020-12-27 04:01:22 +00:00
CObjFunction * func = closure - > function ;
// if the function is variadic and theres more args than parameters, push the args into a dictionary
if ( func - > variadic & & args > = func - > args ) {
int extraArgs = args - func - > args ;
StkPtr variStart = cosmoV_getTop ( state , extraArgs - 1 ) ;
// push key & value pairs
for ( int i = 0 ; i < extraArgs ; i + + ) {
cosmoV_pushNumber ( state , i ) ;
cosmoV_pushValue ( state , * ( variStart + i ) ) ;
}
cosmoV_makeDictionary ( state , extraArgs ) ;
* variStart = * cosmoV_getTop ( state , 0 ) ; // move dict on the stack to the vari local
state - > top - = extraArgs ;
pushCallFrame ( state , closure , func - > args + 1 ) ;
2020-12-27 19:36:17 +00:00
} else if ( args ! = func - > args ) { // mismatched args
2020-12-27 04:01:22 +00:00
cosmoV_error ( state , " Expected %d arguments for %s, got %d! " , closure - > function - > args , closure - > function - > name = = NULL ? UNNAMEDCHUNK : closure - > function - > name - > str , args ) ;
2020-11-10 01:44:12 +00:00
return false ;
2020-12-27 04:01:22 +00:00
} else {
// load function into callframe
pushCallFrame ( state , closure , func - > args ) ;
2020-11-10 01:44:12 +00:00
}
// execute
2020-12-13 03:53:12 +00:00
int nres = cosmoV_execute ( state ) ;
2020-11-10 01:44:12 +00:00
2020-12-16 09:51:50 +00:00
if ( nres > nresults ) // caller function wasn't expecting this many return values, cap it
nres = nresults ;
2020-12-13 03:53:12 +00:00
// remember where the return values are
2020-12-27 04:01:22 +00:00
StkPtr results = cosmoV_getTop ( state , nres - 1 ) ;
2020-11-10 01:44:12 +00:00
2020-12-13 03:53:12 +00:00
// pop the callframe and return results :)
2020-11-13 23:39:47 +00:00
popCallFrame ( state , offset ) ;
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
if ( state - > panic ) // panic state
return false ;
2020-11-10 01:44:12 +00:00
2020-12-27 04:01:22 +00:00
// push the return values back onto the stack
2020-12-18 01:44:04 +00:00
memmove ( state - > top , results , sizeof ( CValue ) * nres ) ; // copies the return values to the top of the stack
2020-12-13 03:53:12 +00:00
state - > top + = nres ; // and make sure to move state->top to match
// now, if the caller function expected more return values, push nils onto the stack
for ( int i = nres ; i < nresults ; i + + )
cosmoV_pushValue ( state , cosmoV_newNil ( ) ) ;
2020-11-10 01:44:12 +00:00
return true ;
2020-10-28 05:16:30 +00:00
}
2021-01-04 22:04:38 +00:00
bool callCValue ( CState * state , CValue func , int args , int nresults , int offset ) {
if ( GET_TYPE ( func ) ! = COSMO_TOBJ ) {
cosmoV_error ( state , " Cannot call non-function type %s! " , cosmoV_typeStr ( func ) ) ;
return false ;
2020-10-28 05:16:30 +00:00
}
2021-01-04 22:04:38 +00:00
switch ( cosmoV_readObj ( func ) - > type ) {
case COBJ_CLOSURE :
return rawCall ( state , cosmoV_readClosure ( func ) , args , nresults , offset ) ;
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_CFUNCTION :
return callCFunction ( state , cosmoV_readCFunction ( func ) , args , nresults , offset ) ;
2020-11-10 01:44:12 +00:00
case COBJ_METHOD : {
2021-01-04 22:04:38 +00:00
CObjMethod * method = ( CObjMethod * ) cosmoV_readObj ( func ) ;
return invokeMethod ( state , method - > obj , method - > func , args , nresults , offset + 1 ) ;
2020-11-10 01:44:12 +00:00
}
2020-12-05 23:55:09 +00:00
case COBJ_OBJECT : { // object is being instantiated, making another object
2021-01-04 22:04:38 +00:00
CObjObject * protoObj = ( CObjObject * ) cosmoV_readObj ( func ) ;
2020-11-10 01:44:12 +00:00
CObjObject * newObj = cosmoO_newObject ( state ) ;
2020-11-15 18:22:11 +00:00
newObj - > proto = protoObj ;
2020-11-10 01:44:12 +00:00
CValue ret ;
2021-01-04 22:04:38 +00:00
// check if they defined an initializer (we accept 0 return values)
2020-11-17 21:07:56 +00:00
if ( cosmoO_getIString ( state , protoObj , ISTRING_INIT , & ret ) ) {
2021-01-04 22:04:38 +00:00
if ( ! invokeMethod ( state , newObj , ret , args , 0 , offset + 1 ) )
return false ;
2020-11-10 01:44:12 +00:00
} else {
2020-12-19 19:32:43 +00:00
// no default initializer
2020-12-30 23:51:59 +00:00
cosmoV_error ( state , " Expected __init() in proto, object cannot be instantiated! " ) ;
2021-01-04 22:04:38 +00:00
return false ;
2020-10-28 05:16:30 +00:00
}
2021-01-04 22:04:38 +00:00
if ( nresults > 0 ) {
cosmoV_pushValue ( state , cosmoV_newObj ( newObj ) ) ;
2021-01-03 23:33:10 +00:00
2021-01-04 22:04:38 +00:00
// push the nils to fill up the expected return values
for ( int i = 0 ; i < nresults - 1 ; i + + ) { // -1 since the we already pushed the important value
cosmoV_pushValue ( state , cosmoV_newNil ( ) ) ;
}
}
2020-10-28 05:16:30 +00:00
break ;
}
default :
2021-01-04 22:04:38 +00:00
cosmoV_error ( state , " Cannot call non-function value %s! " , cosmoV_typeStr ( func ) ) ;
return false ;
2020-10-28 05:16:30 +00:00
}
2021-01-04 22:04:38 +00:00
return true ;
}
bool invokeMethod ( CState * state , CObjObject * obj , CValue func , int args , int nresults , int offset ) {
// first, set the first argument to the object
StkPtr temp = cosmoV_getTop ( state , args ) ;
* temp = cosmoV_newObj ( obj ) ;
return callCValue ( state , func , args + 1 , nresults , offset ) ;
}
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
// wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call failed, else return values are passed
2021-01-04 22:04:38 +00:00
COSMOVMRESULT cosmoV_pcall ( CState * state , int args , int nresults ) {
StkPtr base = cosmoV_getTop ( state , args ) ;
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
if ( ! callCValue ( state , * base , args , nresults , 0 ) ) {
2021-01-04 22:04:38 +00:00
// restore panic state
state - > panic = 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
state - > error = NULL ;
2021-01-04 22:04:38 +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
cosmoV_pushValue ( state , cosmoV_newObj ( state - > error ) ) ;
return COSMOVM_RUNTIME_ERR ;
2021-01-04 22:04:38 +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
return COSMOVM_OK ;
2021-01-04 22:04:38 +00:00
}
/*
calls a callable object at stack - > top - args - 1 , passing the # of args to the callable , and ensuring nresults are returned
returns :
COSMOVM_OK : callable object exited normally
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
COSMOVM_RUNTIME_ERR : an error occurred , grab the error from state - > error
2021-01-04 22:04:38 +00:00
*/
COSMOVMRESULT cosmoV_call ( CState * state , int args , int nresults ) {
StkPtr val = cosmoV_getTop ( state , args ) ; // function will always be right above the args
return callCValue ( state , * val , args , nresults , 0 ) ? COSMOVM_OK : COSMOVM_RUNTIME_ERR ;
2020-10-28 05:16:30 +00:00
}
static inline bool isFalsey ( StkPtr val ) {
2020-12-04 06:04:14 +00:00
return IS_NIL ( * val ) | | ( IS_BOOLEAN ( * val ) & & ! cosmoV_readBoolean ( * val ) ) ;
2020-10-28 05:16:30 +00:00
}
2020-12-05 23:55:09 +00:00
COSMO_API void cosmoV_makeObject ( CState * state , int pairs ) {
2020-11-12 23:17:41 +00:00
StkPtr key , val ;
2020-11-30 18:50:55 +00:00
CObjObject * newObj = cosmoO_newObject ( state ) ;
2020-11-12 23:17:41 +00:00
cosmoV_pushValue ( state , cosmoV_newObj ( newObj ) ) ; // so our GC doesn't free our new object
for ( int i = 0 ; i < pairs ; i + + ) {
val = cosmoV_getTop ( state , ( i * 2 ) + 1 ) ;
key = cosmoV_getTop ( state , ( i * 2 ) + 2 ) ;
// set key/value pair
CValue * newVal = cosmoT_insert ( state , & newObj - > tbl , * key ) ;
* newVal = * val ;
}
// once done, pop everything off the stack + push new object
2020-11-30 18:50:55 +00:00
cosmoV_setTop ( state , ( pairs * 2 ) + 1 ) ; // + 1 for our object
2020-11-12 23:17:41 +00:00
cosmoV_pushValue ( state , cosmoV_newObj ( newObj ) ) ;
}
2020-12-10 02:32:42 +00:00
COSMO_API void cosmoV_makeDictionary ( CState * state , int pairs ) {
StkPtr key , val ;
CObjDict * newObj = cosmoO_newDictionary ( state ) ;
cosmoV_pushValue ( state , cosmoV_newObj ( newObj ) ) ; // so our GC doesn't free our new dictionary
for ( int i = 0 ; i < pairs ; i + + ) {
val = cosmoV_getTop ( state , ( i * 2 ) + 1 ) ;
key = cosmoV_getTop ( state , ( i * 2 ) + 2 ) ;
// set key/value pair
CValue * newVal = cosmoT_insert ( state , & newObj - > tbl , * key ) ;
* newVal = * val ;
}
// once done, pop everything off the stack + push new dictionary
cosmoV_setTop ( state , ( pairs * 2 ) + 1 ) ; // + 1 for our dictionary
cosmoV_pushValue ( state , cosmoV_newObj ( newObj ) ) ;
}
2020-11-13 05:04:09 +00:00
COSMO_API bool cosmoV_getObject ( CState * state , CObjObject * object , CValue key , CValue * val ) {
2020-12-18 01:44:04 +00:00
cosmoV_pushValue ( state , cosmoV_newObj ( object ) ) ;
2020-12-30 23:51:59 +00:00
if ( cosmoO_getRawObject ( state , object , key , val ) ) {
2020-12-04 06:04:14 +00:00
if ( IS_OBJ ( * val ) ) {
if ( cosmoV_readObj ( * val ) - > type = = COBJ_CLOSURE ) { // is it a function? if so, make it a method to the current object
CObjMethod * method = cosmoO_newMethod ( state , ( CObjClosure * ) cosmoV_readObj ( * val ) , object ) ;
2020-11-13 05:04:09 +00:00
* val = cosmoV_newObj ( method ) ;
2020-12-04 06:04:14 +00:00
} else if ( cosmoV_readObj ( * val ) - > type = = COBJ_CFUNCTION ) {
CObjMethod * method = cosmoO_newCMethod ( state , ( CObjCFunction * ) cosmoV_readObj ( * val ) , object ) ;
2020-11-13 05:04:09 +00:00
* val = cosmoV_newObj ( method ) ;
2020-12-30 23:51:59 +00:00
} // else, just pass the raw object
2020-11-13 05:04:09 +00:00
}
2020-12-18 01:44:04 +00:00
cosmoV_pop ( state ) ;
2020-11-13 05:04:09 +00:00
return true ;
}
2020-12-18 01:44:04 +00:00
cosmoV_pop ( state ) ;
2020-11-13 05:04:09 +00:00
return false ;
}
2020-12-18 01:44:04 +00:00
int _dict__next ( CState * state , int nargs , CValue * args ) {
if ( nargs ! = 1 ) {
cosmoV_error ( state , " Expected 1 parameter, %d received! " , nargs ) ;
return 0 ;
}
if ( ! IS_OBJECT ( args [ 0 ] ) ) {
cosmoV_error ( state , " Expected iterable object, %s received! " , cosmoV_typeStr ( args [ 0 ] ) ) ;
return 0 ;
}
CObjObject * obj = cosmoV_readObject ( args [ 0 ] ) ;
int index = cosmoO_getUserI ( state , obj ) ; // we store the index in the userdata
CValue val ;
cosmoO_getIString ( state , obj , ISTRING_RESERVED , & val ) ;
if ( ! IS_DICT ( val ) ) {
return 0 ; // someone set the __reserved member to something else. this will exit the iterator loop
}
CObjDict * dict = ( CObjDict * ) cosmoV_readObj ( val ) ;
// while the entry is invalid, go to the next entry
CTableEntry * entry ;
do {
entry = & dict - > tbl . table [ index + + ] ;
} while ( IS_NIL ( entry - > key ) & & index < dict - > tbl . capacity ) ;
cosmoO_setUserI ( state , obj , index ) ; // update the userdata
if ( ! IS_NIL ( entry - > key ) ) { // if the entry is valid, return it's key and value pair
cosmoV_pushValue ( state , entry - > key ) ;
cosmoV_pushValue ( state , entry - > val ) ;
return 2 ; // we pushed 2 values onto the stack for the return values
} else {
return 0 ; // we have nothing to return, this should exit the iterator loop
}
}
2020-11-30 18:50:55 +00:00
# define NUMBEROP(typeConst, op) \
2020-10-28 05:16:30 +00:00
StkPtr valA = cosmoV_getTop ( state , 1 ) ; \
StkPtr valB = cosmoV_getTop ( state , 0 ) ; \
2020-12-04 06:04:14 +00:00
if ( IS_NUMBER ( * valA ) & & IS_NUMBER ( * valB ) ) { \
2020-10-28 05:16:30 +00:00
cosmoV_setTop ( state , 2 ) ; /* pop the 2 values */ \
2020-12-04 06:04:14 +00:00
cosmoV_pushValue ( state , typeConst ( cosmoV_readNumber ( * valA ) op cosmoV_readNumber ( * valB ) ) ) ; \
2020-10-28 05:16:30 +00:00
} else { \
2020-11-30 18:32:04 +00:00
cosmoV_error ( state , " Expected numbers, got %s and %s! " , cosmoV_typeStr ( * valA ) , cosmoV_typeStr ( * valB ) ) ; \
2020-12-30 23:51:59 +00:00
return - 1 ; \
2020-10-28 05:16:30 +00:00
} \
2020-12-30 23:51:59 +00:00
// returns -1 if panic
2020-12-13 03:53:12 +00:00
int cosmoV_execute ( CState * state ) {
2020-10-28 05:16:30 +00:00
CCallFrame * frame = & state - > callFrame [ state - > frameCount - 1 ] ; // grabs the current frame
CValue * constants = frame - > closure - > function - > chunk . constants . values ; // cache the pointer :)
# define READBYTE() *frame->pc++
# define READUINT() (frame->pc += 2, *(uint16_t*)(&frame->pc[-2]))
while ( ! state - > panic ) {
/*disasmInstr(&frame->closure->function->chunk, frame->pc - frame->closure->function->chunk.buf, 0);
printf ( " \n " ) ; */
switch ( READBYTE ( ) ) {
case OP_LOADCONST : { // push const[uint] to stack
uint16_t indx = READUINT ( ) ;
cosmoV_pushValue ( state , constants [ indx ] ) ;
break ;
}
case OP_SETGLOBAL : {
uint16_t indx = READUINT ( ) ;
CValue ident = constants [ indx ] ; // grabs identifier
CValue * val = cosmoT_insert ( state , & state - > globals , ident ) ;
* val = * cosmoV_pop ( state ) ; // sets the value in the hash table
break ;
}
case OP_GETGLOBAL : {
uint16_t indx = READUINT ( ) ;
CValue ident = constants [ indx ] ; // grabs identifier
CValue val ; // to hold our value
cosmoT_get ( & state - > globals , ident , & val ) ;
cosmoV_pushValue ( state , val ) ; // pushes the value to the stack
break ;
}
case OP_SETLOCAL : {
2020-11-17 19:17:23 +00:00
uint8_t indx = READBYTE ( ) ;
frame - > base [ indx ] = * cosmoV_pop ( state ) ;
2020-10-28 05:16:30 +00:00
break ;
}
case OP_GETLOCAL : {
2020-11-17 19:17:23 +00:00
uint8_t indx = READBYTE ( ) ;
cosmoV_pushValue ( state , frame - > base [ indx ] ) ;
2020-10-28 05:16:30 +00:00
break ;
}
case OP_GETUPVAL : {
uint8_t indx = READBYTE ( ) ;
cosmoV_pushValue ( state , * frame - > closure - > upvalues [ indx ] - > val ) ;
break ;
}
case OP_SETUPVAL : {
uint8_t indx = READBYTE ( ) ;
* frame - > closure - > upvalues [ indx ] - > val = * cosmoV_pop ( state ) ;
break ;
}
2020-11-30 18:50:55 +00:00
case OP_PEJMP : { // pop equality jump
2020-10-28 05:16:30 +00:00
uint16_t offset = READUINT ( ) ;
if ( isFalsey ( cosmoV_pop ( state ) ) ) { // pop, if the condition is false, jump!
frame - > pc + = offset ;
}
break ;
}
2020-11-30 18:50:55 +00:00
case OP_EJMP : { // equality jump
2020-10-28 05:16:30 +00:00
uint16_t offset = READUINT ( ) ;
if ( isFalsey ( cosmoV_getTop ( state , 0 ) ) ) { // if the condition is false, jump!
frame - > pc + = offset ;
}
break ;
}
2020-11-30 18:50:55 +00:00
case OP_JMP : { // jump
2020-10-28 05:16:30 +00:00
uint16_t offset = READUINT ( ) ;
frame - > pc + = offset ;
break ;
}
case OP_JMPBACK : {
uint16_t offset = READUINT ( ) ;
frame - > pc - = offset ;
break ;
}
case OP_POP : { // pops value off the stack
cosmoV_setTop ( state , READBYTE ( ) ) ;
break ;
}
case OP_CALL : {
uint8_t args = READBYTE ( ) ;
2020-12-13 03:53:12 +00:00
uint8_t nres = READBYTE ( ) ;
2021-01-03 23:33:10 +00:00
if ( cosmoV_call ( state , args , nres ) ! = COSMOVM_OK ) {
return - 1 ;
2020-10-28 05:16:30 +00:00
}
break ;
}
case OP_CLOSURE : {
uint16_t index = READUINT ( ) ;
CObjFunction * func = cosmoV_readFunction ( constants [ index ] ) ;
CObjClosure * closure = cosmoO_newClosure ( state , func ) ;
cosmoV_pushValue ( state , cosmoV_newObj ( ( CObj * ) closure ) ) ;
for ( int i = 0 ; i < closure - > upvalueCount ; i + + ) {
uint8_t encoding = READBYTE ( ) ;
uint8_t index = READBYTE ( ) ;
if ( encoding = = OP_GETUPVAL ) {
// capture upvalue from current frame's closure
closure - > upvalues [ i ] = frame - > closure - > upvalues [ index ] ;
} else {
// capture local
closure - > upvalues [ i ] = captureUpvalue ( state , frame - > base + index ) ;
}
}
break ;
}
case OP_CLOSE : {
closeUpvalues ( state , state - > top - 1 ) ;
cosmoV_pop ( state ) ;
break ;
}
2020-12-10 02:32:42 +00:00
case OP_NEWDICT : {
uint16_t pairs = READUINT ( ) ;
cosmoV_makeDictionary ( state , pairs ) ;
break ;
}
case OP_INDEX : {
StkPtr key = cosmoV_getTop ( state , 0 ) ; // key should be the top of the stack
StkPtr temp = cosmoV_getTop ( state , 1 ) ; // after that should be the dictionary
// sanity check
if ( IS_OBJ ( * temp ) ) {
CValue val ; // to hold our value
2020-12-30 23:51:59 +00:00
switch ( cosmoV_readObj ( * temp ) - > type ) {
case COBJ_DICT : {
CObjDict * dict = ( CObjDict * ) cosmoV_readObj ( * temp ) ;
2020-12-10 02:32:42 +00:00
2020-12-30 23:51:59 +00:00
cosmoT_get ( & dict - > tbl , * key , & val ) ;
2020-12-10 02:32:42 +00:00
break ;
2020-12-30 23:51:59 +00:00
}
case COBJ_OBJECT : { // check for __index
CObjObject * object = ( CObjObject * ) cosmoV_readObj ( * temp ) ;
if ( ! cosmoO_indexObject ( state , object , * key , & val ) ) // if returns false, cosmoV_error was called
return - 1 ;
break ;
}
default :
cosmoV_error ( state , " Couldn't index type %s! " , cosmoV_typeStr ( * temp ) ) ;
return - 1 ;
2020-12-10 02:32:42 +00:00
}
cosmoV_setTop ( state , 2 ) ; // pops the object & the key
cosmoV_pushValue ( state , val ) ; // pushes the field result
} else {
cosmoV_error ( state , " Couldn't index type %s! " , cosmoV_typeStr ( * temp ) ) ;
}
break ;
}
case OP_NEWINDEX : {
StkPtr value = cosmoV_getTop ( state , 0 ) ; // value is at the top of the stack
StkPtr key = cosmoV_getTop ( state , 1 ) ;
StkPtr temp = cosmoV_getTop ( state , 2 ) ; // object is after the key
// sanity check
if ( IS_OBJ ( * temp ) ) {
2020-12-30 23:51:59 +00:00
switch ( cosmoV_readObj ( * temp ) - > type ) {
case COBJ_DICT : { // index and set table
CObjDict * dict = ( CObjDict * ) cosmoV_readObj ( * temp ) ;
CValue * newVal = cosmoT_insert ( state , & dict - > tbl , * key ) ;
2020-12-10 02:32:42 +00:00
2020-12-30 23:51:59 +00:00
* newVal = * value ; // set the index
break ;
}
case COBJ_OBJECT : { // check for __newindex
CObjObject * object = ( CObjObject * ) cosmoV_readObj ( * temp ) ;
if ( ! cosmoO_newIndexObject ( state , object , * key , * value ) ) // if it returns false, cosmoV_error was called
return - 1 ;
2020-12-10 02:32:42 +00:00
break ;
2020-12-30 23:51:59 +00:00
}
default :
cosmoV_error ( state , " Couldn't set index with type %s! " , cosmoV_typeStr ( * temp ) ) ;
return - 1 ;
2020-12-10 02:32:42 +00:00
}
// pop everything off the stack
cosmoV_setTop ( state , 3 ) ;
} else {
cosmoV_error ( state , " Couldn't set index with type %s! " , cosmoV_typeStr ( * temp ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-12-10 02:32:42 +00:00
}
break ;
}
2020-11-04 04:10:51 +00:00
case OP_NEWOBJECT : {
2020-11-18 20:35:58 +00:00
uint16_t pairs = READUINT ( ) ;
2020-12-05 23:55:09 +00:00
cosmoV_makeObject ( state , pairs ) ;
2020-11-04 04:10:51 +00:00
break ;
}
case OP_GETOBJECT : {
2020-12-30 23:51:59 +00:00
CValue val ; // to hold our value
2020-11-04 04:10:51 +00:00
StkPtr key = cosmoV_getTop ( state , 0 ) ; // key should be the top of the stack
StkPtr temp = cosmoV_getTop ( state , 1 ) ; // after that should be the object
// sanity check
2020-12-30 23:51:59 +00:00
if ( IS_OBJ ( * temp ) ) {
switch ( cosmoV_readObj ( * temp ) - > type ) {
case COBJ_DICT : { // syntax sugar, makes "namespaces" possible
CObjDict * dict = ( CObjDict * ) cosmoV_readObj ( * temp ) ;
cosmoT_get ( & dict - > tbl , * key , & val ) ;
break ;
}
case COBJ_OBJECT : {
CObjObject * object = ( CObjObject * ) cosmoV_readObj ( * temp ) ;
cosmoV_getObject ( state , object , * key , & val ) ;
break ;
}
default :
cosmoV_error ( state , " Couldn't get from type %s! " , cosmoV_typeStr ( * temp ) ) ;
return - 1 ;
}
} else {
2020-11-30 18:32:04 +00:00
cosmoV_error ( state , " Couldn't get from type %s! " , cosmoV_typeStr ( * temp ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-11-04 04:10:51 +00:00
}
cosmoV_setTop ( state , 2 ) ; // pops the object & the key
cosmoV_pushValue ( state , val ) ; // pushes the field result
break ;
}
case OP_SETOBJECT : {
StkPtr value = cosmoV_getTop ( state , 0 ) ; // value is at the top of the stack
StkPtr key = cosmoV_getTop ( state , 1 ) ;
StkPtr temp = cosmoV_getTop ( state , 2 ) ; // object is after the key
// sanity check
2020-12-30 23:51:59 +00:00
if ( IS_OBJ ( * temp ) ) {
switch ( cosmoV_readObj ( * temp ) - > type ) {
case COBJ_DICT : { // index and set the table
CObjDict * dict = ( CObjDict * ) cosmoV_readObj ( * temp ) ;
CValue * newVal = cosmoT_insert ( state , & dict - > tbl , * key ) ;
* newVal = * value ; // set the index
break ;
}
case COBJ_OBJECT : {
CObjObject * object = ( CObjObject * ) cosmoV_readObj ( * temp ) ;
cosmoO_setRawObject ( state , object , * key , * value ) ;
break ;
}
default :
cosmoV_error ( state , " Couldn't set a field on type %s! " , cosmoV_typeStr ( * temp ) ) ;
return - 1 ;
}
} else {
2020-11-30 18:32:04 +00:00
cosmoV_error ( state , " Couldn't set a field on type %s! " , cosmoV_typeStr ( * temp ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-11-04 04:10:51 +00:00
}
// pop everything off the stack
cosmoV_setTop ( state , 3 ) ;
break ;
}
2020-11-13 23:45:14 +00:00
case OP_INVOKE : { // this is an optimization made by the parser, instead of allocating a CObjMethod every time we want to invoke a method, we shrink it down into one minimal instruction!
2020-12-30 23:51:59 +00:00
CValue val ; // to hold our value
2020-11-13 23:39:47 +00:00
uint8_t args = READBYTE ( ) ;
2020-12-13 03:53:12 +00:00
uint8_t nres = READBYTE ( ) ;
2020-11-13 23:39:47 +00:00
StkPtr key = cosmoV_getTop ( state , args ) ; // grabs key from stack
StkPtr temp = cosmoV_getTop ( state , args + 1 ) ; // grabs object from stack
// sanity check
2020-12-30 23:51:59 +00:00
if ( IS_OBJ ( * temp ) ) {
switch ( cosmoV_readObj ( * temp ) - > type ) {
case COBJ_DICT : { // again, syntax sugar ("""""namespaces""""")
CObjDict * dict = ( CObjDict * ) cosmoV_readObj ( * temp ) ;
2020-11-13 23:39:47 +00:00
2020-12-30 23:51:59 +00:00
cosmoT_get ( & dict - > tbl , * key , & val ) ;
2020-11-13 23:39:47 +00:00
2020-12-30 23:51:59 +00:00
// call closure/cfunction
2021-01-04 22:04:38 +00:00
if ( ! callCValue ( state , val , args , nres , - 1 ) )
2020-12-30 23:51:59 +00:00
return - 1 ;
break ;
}
case COBJ_OBJECT : {
CObjObject * object = ( CObjObject * ) cosmoV_readObj ( * temp ) ;
cosmoO_getRawObject ( state , object , * key , & val ) ; // we use cosmoO_getRawObject instead of the cosmoV_getObject wrapper so we get the raw value from the object instead of the CObjMethod wrapper
// now invoke the method!
invokeMethod ( state , object , val , args , nres , 0 ) ;
break ;
}
default :
cosmoV_error ( state , " Couldn't get from type %s! " , cosmoV_typeStr ( * temp ) ) ;
return - 1 ;
}
} else {
cosmoV_error ( state , " Couldn't get from type %s! " , cosmoV_typeStr ( * temp ) ) ;
return - 1 ;
}
2020-11-13 23:39:47 +00:00
break ;
}
2020-12-16 03:21:51 +00:00
case OP_ITER : {
StkPtr temp = cosmoV_getTop ( state , 0 ) ; // should be the object/dictionary
if ( ! IS_OBJ ( * temp ) ) {
cosmoV_error ( state , " Couldn't iterate over non-iterator type %s! " , cosmoV_typeStr ( * temp ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-12-16 03:21:51 +00:00
}
switch ( cosmoV_readObj ( * temp ) - > type ) {
case COBJ_DICT : {
2020-12-18 01:44:04 +00:00
CObjDict * dict = ( CObjDict * ) cosmoV_readObj ( * temp ) ;
cosmoV_pushValue ( state , cosmoV_newObj ( state - > iStrings [ ISTRING_RESERVED ] ) ) ; // key
cosmoV_pushValue ( state , cosmoV_newObj ( dict ) ) ; // value
2020-12-20 03:15:12 +00:00
cosmoV_pushString ( state , " __next " ) ; // key
2020-12-18 01:44:04 +00:00
CObjCFunction * dict_next = cosmoO_newCFunction ( state , _dict__next ) ;
cosmoV_pushValue ( state , cosmoV_newObj ( dict_next ) ) ; // value
cosmoV_makeObject ( state , 2 ) ; // pushes the new object to the stack
CObjObject * obj = cosmoV_readObject ( * ( cosmoV_getTop ( state , 0 ) ) ) ;
cosmoO_setUserI ( state , obj , 0 ) ; // increment for iterator
// make our CObjMethod for OP_NEXT to call
CObjMethod * method = cosmoO_newCMethod ( state , dict_next , obj ) ;
2020-12-16 03:21:51 +00:00
2020-12-18 01:44:04 +00:00
cosmoV_setTop ( state , 2 ) ; // pops the object & the dict
cosmoV_pushValue ( state , cosmoV_newObj ( method ) ) ; // pushes the method for OP_NEXT
2020-12-16 03:21:51 +00:00
break ;
}
case COBJ_OBJECT : {
2020-12-18 01:44:04 +00:00
CObjObject * obj = cosmoV_readObject ( * temp ) ;
2020-12-16 03:21:51 +00:00
CValue val ;
// grab __iter & call it
if ( cosmoO_getIString ( state , obj , ISTRING_ITER , & val ) ) {
cosmoV_pop ( state ) ; // pop the object from the stack
cosmoV_pushValue ( state , val ) ;
cosmoV_pushValue ( state , cosmoV_newObj ( obj ) ) ;
2021-01-03 23:33:10 +00:00
if ( cosmoV_call ( state , 1 , 1 ) ! = COSMOVM_OK ) // we expect 1 return value on the stack, the iterable object
return - 1 ;
2020-12-16 09:51:50 +00:00
2020-12-19 19:32:43 +00:00
StkPtr iObj = cosmoV_getTop ( state , 0 ) ;
2020-12-16 09:51:50 +00:00
2020-12-19 19:32:43 +00:00
if ( ! IS_OBJECT ( * iObj ) ) {
cosmoV_error ( state , " Expected iterable object! '__iter' returned %s, expected object! " , cosmoV_typeStr ( * iObj ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-12-16 09:51:50 +00:00
}
2020-12-19 19:32:43 +00:00
CObjObject * obj = ( CObjObject * ) cosmoV_readObj ( * iObj ) ;
2020-12-16 09:51:50 +00:00
2020-12-19 19:32:43 +00:00
cosmoV_getObject ( state , obj , cosmoV_newObj ( state - > iStrings [ ISTRING_NEXT ] ) , iObj ) ;
2020-12-16 03:21:51 +00:00
} else {
cosmoV_error ( state , " Expected iterable object! '__iter' not defined! " ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-12-16 03:21:51 +00:00
}
break ;
}
2020-12-30 23:51:59 +00:00
default :
2020-12-16 03:21:51 +00:00
cosmoV_error ( state , " Couldn't iterate over non-iterator type %s! " , cosmoV_typeStr ( * temp ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-12-16 03:21:51 +00:00
}
break ;
}
case OP_NEXT : {
uint8_t nresults = READBYTE ( ) ;
uint16_t jump = READUINT ( ) ;
StkPtr temp = cosmoV_getTop ( state , 0 ) ; // we don't actually pop this off the stack
2020-12-16 09:51:50 +00:00
if ( ! IS_METHOD ( * temp ) ) {
cosmoV_error ( state , " Expected '__next' to be a method, got type %s! " , cosmoV_typeStr ( * temp ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-12-16 03:21:51 +00:00
}
2020-12-16 09:51:50 +00:00
cosmoV_pushValue ( state , * temp ) ;
2021-01-03 23:33:10 +00:00
if ( cosmoV_call ( state , 0 , nresults ) ! = COSMOVM_OK )
return - 1 ;
2020-12-16 03:21:51 +00:00
2020-12-16 09:51:50 +00:00
if ( IS_NIL ( * ( cosmoV_getTop ( state , 0 ) ) ) ) { // __next returned a nil, which means to exit the loop
cosmoV_setTop ( state , nresults ) ; // pop the return values
frame - > pc + = jump ;
2020-12-16 03:21:51 +00:00
}
break ;
}
2020-10-28 05:16:30 +00:00
case OP_ADD : { // pop 2 values off the stack & try to add them together
2020-11-30 18:50:55 +00:00
NUMBEROP ( cosmoV_newNumber , + ) ;
2020-10-28 05:16:30 +00:00
break ;
}
case OP_SUB : { // pop 2 values off the stack & try to subtracts them
2020-11-30 18:50:55 +00:00
NUMBEROP ( cosmoV_newNumber , - )
2020-10-28 05:16:30 +00:00
break ;
}
case OP_MULT : { // pop 2 values off the stack & try to multiplies them together
2020-11-30 18:50:55 +00:00
NUMBEROP ( cosmoV_newNumber , * )
2020-10-28 05:16:30 +00:00
break ;
}
case OP_DIV : { // pop 2 values off the stack & try to divides them
2020-11-30 18:50:55 +00:00
NUMBEROP ( cosmoV_newNumber , / )
2020-10-28 05:16:30 +00:00
break ;
}
2021-01-01 06:47:15 +00:00
case OP_MOD : {
StkPtr valA = cosmoV_getTop ( state , 1 ) ;
StkPtr valB = cosmoV_getTop ( state , 0 ) ;
if ( IS_NUMBER ( * valA ) & & IS_NUMBER ( * valB ) ) {
cosmoV_setTop ( state , 2 ) ; /* pop the 2 values */
cosmoV_pushValue ( state , cosmoV_newNumber ( fmod ( cosmoV_readNumber ( * valA ) , cosmoV_readNumber ( * valB ) ) ) ) ;
} else { \
cosmoV_error ( state , " Expected numbers, got %s and %s! " , cosmoV_typeStr ( * valA ) , cosmoV_typeStr ( * valB ) ) ;
return - 1 ; \
} \
break ;
}
2020-10-28 05:16:30 +00:00
case OP_NOT : {
2020-11-30 18:40:36 +00:00
cosmoV_pushBoolean ( state , isFalsey ( cosmoV_pop ( state ) ) ) ;
2020-10-28 05:16:30 +00:00
break ;
}
case OP_NEGATE : { // pop 1 value off the stack & try to negate
StkPtr val = cosmoV_getTop ( state , 0 ) ;
2020-12-04 06:04:14 +00:00
if ( IS_NUMBER ( * val ) ) {
2020-10-28 05:16:30 +00:00
cosmoV_pop ( state ) ;
2020-12-04 06:04:14 +00:00
cosmoV_pushNumber ( state , - ( cosmoV_readNumber ( * val ) ) ) ;
2020-10-28 05:16:30 +00:00
} else {
2020-11-30 18:32:04 +00:00
cosmoV_error ( state , " Expected number, got %s! " , cosmoV_typeStr ( * val ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-10-28 05:16:30 +00:00
}
break ;
}
2020-12-19 19:32:43 +00:00
case OP_COUNT : { // pop 1 value off the stack & if it's a dictionary return the amount of active entries it has
2020-11-30 18:32:04 +00:00
StkPtr temp = cosmoV_getTop ( state , 0 ) ;
2020-12-12 23:34:54 +00:00
if ( ! IS_OBJ ( * temp ) | | cosmoV_readObj ( * temp ) - > type ! = COBJ_DICT ) {
2020-11-30 18:32:04 +00:00
cosmoV_error ( state , " Expected object, got %s! " , cosmoV_typeStr ( * temp ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-11-30 18:32:04 +00:00
}
2020-12-12 23:34:54 +00:00
CObjDict * dict = ( CObjDict * ) cosmoV_readObj ( * temp ) ;
2020-11-30 18:32:04 +00:00
cosmoV_pop ( state ) ;
2020-12-12 23:34:54 +00:00
cosmoV_pushNumber ( state , cosmoT_count ( & dict - > tbl ) ) ; // pushes the count onto the stack
2020-11-30 18:32:04 +00:00
break ;
}
2020-10-28 05:16:30 +00:00
case OP_CONCAT : {
uint8_t vals = READBYTE ( ) ;
2020-12-22 21:13:11 +00:00
cosmoV_concat ( state , vals ) ;
2020-10-28 05:16:30 +00:00
break ;
}
2020-11-19 20:41:21 +00:00
case OP_INCLOCAL : { // this leaves the value on the stack
2020-12-19 19:32:43 +00:00
int8_t inc = READBYTE ( ) - 128 ; // amount we're incrementing by
2020-11-19 20:41:21 +00:00
uint8_t indx = READBYTE ( ) ;
StkPtr val = & frame - > base [ indx ] ;
// check that it's a number value
2020-12-04 06:04:14 +00:00
if ( IS_NUMBER ( * val ) ) {
2020-11-19 20:41:21 +00:00
cosmoV_pushValue ( state , * val ) ; // pushes old value onto the stack :)
2020-12-04 06:04:14 +00:00
* val = cosmoV_newNumber ( cosmoV_readNumber ( * val ) + inc ) ;
2020-11-19 20:41:21 +00:00
} else {
2020-11-30 18:32:04 +00:00
cosmoV_error ( state , " Expected number, got %s! " , cosmoV_typeStr ( * val ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-11-19 20:41:21 +00:00
}
break ;
}
case OP_INCGLOBAL : {
2020-12-19 19:32:43 +00:00
int8_t inc = READBYTE ( ) - 128 ; // amount we're incrementing by
2020-11-19 20:41:21 +00:00
uint16_t indx = READUINT ( ) ;
CValue ident = constants [ indx ] ; // grabs identifier
CValue * val = cosmoT_insert ( state , & state - > globals , ident ) ;
// check that it's a number value
2020-12-04 06:04:14 +00:00
if ( IS_NUMBER ( * val ) ) {
2020-11-19 20:41:21 +00:00
cosmoV_pushValue ( state , * val ) ; // pushes old value onto the stack :)
2020-12-04 06:04:14 +00:00
* val = cosmoV_newNumber ( cosmoV_readNumber ( * val ) + inc ) ;
2020-11-19 20:41:21 +00:00
} else {
2020-11-30 18:32:04 +00:00
cosmoV_error ( state , " Expected number, got %s! " , cosmoV_typeStr ( * val ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-11-19 20:41:21 +00:00
}
break ;
}
case OP_INCUPVAL : {
2020-12-19 19:32:43 +00:00
int8_t inc = READBYTE ( ) - 128 ; // amount we're incrementing by
2020-11-19 20:41:21 +00:00
uint8_t indx = READBYTE ( ) ;
CValue * val = frame - > closure - > upvalues [ indx ] - > val ;
// check that it's a number value
2020-12-04 06:04:14 +00:00
if ( IS_NUMBER ( * val ) ) {
2020-11-19 20:41:21 +00:00
cosmoV_pushValue ( state , * val ) ; // pushes old value onto the stack :)
2020-12-04 06:04:14 +00:00
* val = cosmoV_newNumber ( cosmoV_readNumber ( * val ) + inc ) ;
2020-11-19 20:41:21 +00:00
} else {
2020-11-30 18:32:04 +00:00
cosmoV_error ( state , " Expected number, got %s! " , cosmoV_typeStr ( * val ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-11-19 20:41:21 +00:00
}
break ;
}
2020-12-10 02:32:42 +00:00
case OP_INCINDEX : {
2020-12-19 19:32:43 +00:00
int8_t inc = READBYTE ( ) - 128 ; // amount we're incrementing by
2020-12-10 02:32:42 +00:00
StkPtr temp = cosmoV_getTop ( state , 1 ) ; // object should be above the key
StkPtr key = cosmoV_getTop ( state , 0 ) ; // grabs key
if ( IS_OBJ ( * temp ) ) {
2021-01-02 04:52:54 +00:00
switch ( cosmoV_readObj ( * temp ) - > type ) {
case COBJ_DICT : {
CObjDict * dict = ( CObjDict * ) cosmoV_readObj ( * temp ) ;
CValue * val = cosmoT_insert ( state , & dict - > tbl , * key ) ;
2020-12-10 02:32:42 +00:00
2021-01-02 04:52:54 +00:00
// pops dict & key from stack
cosmoV_setTop ( state , 2 ) ;
2020-12-10 02:32:42 +00:00
2021-01-02 04:52:54 +00:00
if ( IS_NUMBER ( * val ) ) {
cosmoV_pushValue ( state , * val ) ; // pushes old value onto the stack :)
* val = cosmoV_newNumber ( cosmoV_readNumber ( * val ) + inc ) ;
2020-12-10 02:32:42 +00:00
} else {
2021-01-02 04:52:54 +00:00
cosmoV_error ( state , " Expected number, got %s! " , cosmoV_typeStr ( * val ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-12-10 02:32:42 +00:00
}
2021-01-02 04:52:54 +00:00
break ;
}
case COBJ_OBJECT : {
CObjObject * object = ( CObjObject * ) cosmoV_readObj ( * temp ) ;
CValue val ;
// call __index
if ( cosmoO_indexObject ( state , object , * key , & val ) ) {
if ( IS_NUMBER ( val ) ) {
cosmoV_pushValue ( state , val ) ; // pushes old value onto the stack :)
// call __newindex
cosmoO_newIndexObject ( state , object , * key , cosmoV_newNumber ( cosmoV_readNumber ( val ) + inc ) ) ;
} else {
cosmoV_error ( state , " Expected number, got %s! " , cosmoV_typeStr ( val ) ) ;
return - 1 ;
}
}
break ;
2020-12-10 02:32:42 +00:00
}
2021-01-02 04:52:54 +00:00
default :
cosmoV_error ( state , " Couldn't set index with type %s! " , cosmoV_typeStr ( * temp ) ) ;
return - 1 ;
2020-12-10 02:32:42 +00:00
}
} else {
cosmoV_error ( state , " Couldn't set index with type %s! " , cosmoV_typeStr ( * temp ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-12-10 02:32:42 +00:00
}
break ;
}
2020-11-19 20:41:21 +00:00
case OP_INCOBJECT : {
2020-12-19 19:32:43 +00:00
int8_t inc = READBYTE ( ) - 128 ; // amount we're incrementing by
2020-11-19 20:41:21 +00:00
uint16_t indx = READUINT ( ) ;
StkPtr temp = cosmoV_getTop ( state , 0 ) ; // object should be at the top of the stack
CValue ident = constants [ indx ] ; // grabs identifier
// sanity check
2021-01-02 04:52:54 +00:00
if ( IS_OBJ ( * temp ) ) {
switch ( cosmoV_readObj ( * temp ) - > type ) {
case COBJ_OBJECT : {
CObjObject * object = ( CObjObject * ) cosmoV_readObj ( * temp ) ;
CValue val ;
cosmoO_getRawObject ( state , object , ident , & val ) ;
2020-11-19 20:41:21 +00:00
2021-01-02 04:52:54 +00:00
// pop the object off the stack
cosmoV_pop ( state ) ;
2020-11-19 20:41:21 +00:00
2021-01-02 04:52:54 +00:00
// check that it's a number value
if ( IS_NUMBER ( val ) ) {
cosmoV_pushValue ( state , val ) ; // pushes old value onto the stack :)
cosmoO_setRawObject ( state , object , ident , cosmoV_newNumber ( cosmoV_readNumber ( val ) + inc ) ) ;
} else {
cosmoV_error ( state , " Expected number, got %s! " , cosmoV_typeStr ( val ) ) ;
return - 1 ;
}
break ;
}
case COBJ_DICT : {
CObjDict * dict = ( CObjDict * ) cosmoV_readObj ( * temp ) ;
CValue * val = cosmoT_insert ( state , & dict - > tbl , ident ) ;
2020-11-19 20:41:21 +00:00
2021-01-02 04:52:54 +00:00
// pops dict from stack
cosmoV_pop ( state ) ;
if ( IS_NUMBER ( * val ) ) {
cosmoV_pushValue ( state , * val ) ; // pushes old value onto the stack :)
* val = cosmoV_newNumber ( cosmoV_readNumber ( * val ) + inc ) ;
} else {
cosmoV_error ( state , " Expected number, got %s! " , cosmoV_typeStr ( * val ) ) ;
return - 1 ;
}
break ;
}
default :
cosmoV_error ( state , " Couldn't set a field on type %s! " , cosmoV_typeStr ( * temp ) ) ;
return - 1 ;
}
2020-11-19 20:41:21 +00:00
} else {
2021-01-02 04:52:54 +00:00
cosmoV_error ( state , " Couldn't set a field on type %s! " , cosmoV_typeStr ( * temp ) ) ;
2020-12-30 23:51:59 +00:00
return - 1 ;
2020-11-19 20:41:21 +00:00
}
break ;
}
2020-10-28 05:16:30 +00:00
case OP_EQUAL : {
// pop vals
StkPtr valB = cosmoV_pop ( state ) ;
StkPtr valA = cosmoV_pop ( state ) ;
// compare & push
2020-11-30 18:40:36 +00:00
cosmoV_pushBoolean ( state , cosmoV_equal ( * valA , * valB ) ) ;
2020-10-28 05:16:30 +00:00
break ;
}
case OP_GREATER : {
2020-11-30 18:50:55 +00:00
NUMBEROP ( cosmoV_newBoolean , > )
2020-10-28 05:16:30 +00:00
break ;
}
case OP_LESS : {
2020-11-30 18:50:55 +00:00
NUMBEROP ( cosmoV_newBoolean , < )
2020-10-28 05:16:30 +00:00
break ;
}
case OP_GREATER_EQUAL : {
2020-11-30 18:50:55 +00:00
NUMBEROP ( cosmoV_newBoolean , > = )
2020-10-28 05:16:30 +00:00
break ;
}
case OP_LESS_EQUAL : {
2020-11-30 18:50:55 +00:00
NUMBEROP ( cosmoV_newBoolean , < = )
2020-10-28 05:16:30 +00:00
break ;
}
2020-11-30 18:40:36 +00:00
case OP_TRUE : cosmoV_pushBoolean ( state , true ) ; break ;
case OP_FALSE : cosmoV_pushBoolean ( state , false ) ; break ;
2020-10-28 05:16:30 +00:00
case OP_NIL : cosmoV_pushValue ( state , cosmoV_newNil ( ) ) ; break ;
case OP_RETURN : {
2020-12-14 20:38:46 +00:00
uint8_t res = READBYTE ( ) ;
return res ;
2020-10-28 05:16:30 +00:00
}
default :
CERROR ( " unknown opcode! " ) ;
exit ( 0 ) ;
}
//cosmoV_printStack(state);
}
# undef READBYTE
# undef READUINT
// we'll only reach this is state->panic is true
2020-12-13 03:53:12 +00:00
return - 1 ;
2020-10-28 05:16:30 +00:00
}
2021-01-02 05:06:24 +00:00
# undef NUMBEROP