2025-11-23 18:34:30 +11:00
# include "interpreter.h"
# include "parser.h"
# include "types.h"
# include "include/uthash.h"
2025-12-13 09:50:49 +11:00
# include <inttypes.h>
2025-11-23 18:34:30 +11:00
# include <stdlib.h>
2025-11-23 21:23:04 +11:00
# include <string.h>
2026-01-25 14:37:50 +11:00
# ifndef _WIN32
2026-01-02 20:29:27 +11:00
# include <dlfcn.h>
2026-01-25 14:37:50 +11:00
# endif
2026-01-02 20:29:27 +11:00
# include <stdarg.h>
# include <unistd.h>
2025-11-23 18:34:30 +11:00
int currentInstruction = 0 ;
2026-01-18 20:49:50 +11:00
bool isMainScopeGlobal = true ;
2026-04-11 09:44:48 +10:00
bool listDeprecationWarning = true ;
2026-03-17 18:35:37 +11:00
char * getFileContents ( const char * filename ) ;
2026-01-19 21:14:48 +11:00
[ [ noreturn ] ] void customError ( GroundArg type , GroundArg what , GroundInstruction * where , int whereLine , int exitCode ) {
printf ( " Ground runtime error: \n ErrorType: " ) ;
printGroundArg ( & type ) ;
printf ( " \n ErrorContext: " ) ;
printGroundArg ( & what ) ;
printf ( " \n " ) ;
if ( where ! = NULL ) {
printf ( " ErrorInstruction: " ) ;
printGroundInstruction ( where ) ;
printf ( " \n " ) ;
}
if ( whereLine > - 1 ) {
printf ( " ErrorLine: %d \n " , whereLine + 1 ) ;
}
exit ( exitCode ) ;
}
2026-01-17 12:21:43 +11:00
[ [ noreturn ] ] void runtimeError ( GroundRuntimeError error , char * what , GroundInstruction * where , int whereLine ) {
2025-11-23 18:34:30 +11:00
printf ( " Ground runtime error: \n ErrorType: " ) ;
switch ( error ) {
case ARG_TYPE_MISMATCH : {
printf ( " ArgTypeMismatch " ) ;
break ;
}
case TOO_FEW_ARGS : {
printf ( " TooFewArgs " ) ;
break ;
}
case TOO_MANY_ARGS : {
printf ( " TooManyArgs " ) ;
break ;
}
2025-11-23 21:23:04 +11:00
case UNKNOWN_LABEL : {
printf ( " UnknownLabel " ) ;
break ;
}
case UNKNOWN_VARIABLE : {
printf ( " UnknownVariable " ) ;
break ;
}
2025-12-01 10:15:37 +11:00
case LIST_ERROR : {
printf ( " ListError " ) ;
break ;
}
2025-12-02 09:00:21 +11:00
case STRING_ERROR : {
printf ( " StringError " ) ;
break ;
}
case MATH_ERROR : {
printf ( " MathError " ) ;
break ;
}
2026-01-17 12:21:43 +11:00
case RETURN_TYPE_MISMATCH : {
printf ( " ReturnTypeMismatch " ) ;
break ;
}
case PREMATURE_EOF : {
printf ( " PrematureEof " ) ;
break ;
}
case INVALID_INSTRUCTION : {
printf ( " InvalidInstruction " ) ;
break ;
}
2025-11-23 18:34:30 +11:00
default :
case FIXME : {
printf ( " FIXME (please report issue to https://chsp.au/ground/cground) " ) ;
}
}
printf ( " \n " ) ;
if ( what ! = NULL ) {
printf ( " ErrorContext: %s \n " , what ) ;
}
if ( where ! = NULL ) {
printf ( " ErrorInstruction: " ) ;
printGroundInstruction ( where ) ;
printf ( " \n " ) ;
}
if ( whereLine > - 1 ) {
printf ( " ErrorLine: %d \n " , whereLine + 1 ) ;
}
exit ( 1 ) ;
}
2026-01-18 20:49:50 +11:00
[ [ noreturn ] ] void throwError ( GroundError * error ) {
printf ( " Uncaught Ground runtime error: \n ErrorType: %s \n " , error - > type ) ;
if ( error - > what ! = NULL ) {
printf ( " ErrorContext: %s \n " , error - > what ) ;
}
if ( error - > where ! = NULL ) {
printf ( " ErrorInstruction: " ) ;
printGroundInstruction ( error - > where ) ;
printf ( " \n " ) ;
}
2026-01-18 21:37:46 +11:00
if ( error - > hasLine ) {
2026-01-18 20:49:50 +11:00
printf ( " ErrorLine: %zu \n " , error - > line + 1 ) ;
}
exit ( 1 ) ;
}
2025-11-23 19:18:10 +11:00
GroundLabel * findLabel ( GroundLabel * head , const char * id ) {
GroundLabel * item ;
HASH_FIND_STR ( head , id , item ) ;
return item ;
}
2026-04-14 10:50:27 +10:00
GroundCatch * findCatch ( GroundCatch * head , const char * id ) {
GroundCatch * item ;
HASH_FIND_STR ( head , id , item ) ;
return item ;
}
2025-11-23 19:18:10 +11:00
void deleteLabel ( GroundLabel * * head , GroundLabel * item ) {
HASH_DEL ( * head , item ) ;
free ( item ) ;
}
2025-11-24 10:15:53 +11:00
void addLabel ( GroundLabel * * head , const char * id , int data ) {
GroundLabel * label = findLabel ( * head , id ) ;
if ( label ) {
deleteLabel ( head , label ) ;
}
GroundLabel * item = malloc ( sizeof ( GroundLabel ) ) ;
2025-11-23 19:59:22 +11:00
snprintf ( item - > id , MAX_ID_LEN , " %s " , id ) ;
2025-11-24 10:15:53 +11:00
item - > lineNum = data ;
2025-11-23 19:59:22 +11:00
HASH_ADD_STR ( * head , id , item ) ;
}
GroundVariable * findVariable ( GroundVariable * head , const char * id ) {
GroundVariable * item ;
HASH_FIND_STR ( head , id , item ) ;
return item ;
}
void deleteVariable ( GroundVariable * * head , GroundVariable * item ) {
2026-04-11 20:41:30 +10:00
if ( item = = NULL ) {
return ;
}
2026-04-11 18:53:17 +10:00
if ( strcmp ( item - > id , " self " ) = = 0 ) {
return ;
}
2026-04-11 20:41:30 +10:00
if ( item - > freed ) {
return ;
}
item - > freed = true ;
2026-04-11 18:53:17 +10:00
if ( item - > value . type = = CUSTOM ) {
GroundObjectField * destructor = findField ( * item - > value . data . customVal , " destructor " ) ;
if ( destructor ! = NULL ) {
if ( destructor - > value . type = = FUNCTION ) {
GroundScope scope = {
. labels = NULL ,
. variables = malloc ( sizeof ( GroundVariable * ) ) ,
2026-04-14 10:50:27 +10:00
. catches = malloc ( sizeof ( GroundCatch * ) ) ,
2026-04-11 18:53:17 +10:00
. isMainScope = false
} ;
if ( scope . variables = = NULL ) {
runtimeError ( FIXME , " Failed to allocate memory for scope variables " , NULL , - 1 ) ;
}
* scope . variables = NULL ;
addVariable ( scope . variables , " self " , item - > value ) ;
GroundInstruction instruction = createGroundInstruction ( CALLMETHOD ) ;
addArgToInstruction ( & instruction , createRefGroundArg ( DIRREF , " self " ) ) ;
addArgToInstruction ( & instruction , createRefGroundArg ( FNREF , " destructor " ) ) ;
addArgToInstruction ( & instruction , createRefGroundArg ( DIRREF , " " ) ) ;
interpretGroundInstruction ( instruction , & scope ) ;
} else {
runtimeError ( ARG_TYPE_MISMATCH , " Custom type destructor must be a function " , NULL , - 1 ) ;
}
}
}
2026-04-11 20:41:30 +10:00
freeGroundValue ( & item - > value ) ;
2025-11-23 19:59:22 +11:00
HASH_DEL ( * head , item ) ;
free ( item ) ;
2025-11-23 19:18:10 +11:00
}
2025-11-24 10:15:53 +11:00
void addVariable ( GroundVariable * * head , const char * id , GroundValue data ) {
GroundVariable * variable = findVariable ( * head , id ) ;
if ( variable ) {
deleteVariable ( head , variable ) ;
}
GroundVariable * item = malloc ( sizeof ( GroundVariable ) ) ;
snprintf ( item - > id , MAX_ID_LEN , " %s " , id ) ;
2025-12-01 10:36:09 +11:00
item - > value = copyGroundValue ( & data ) ;
2026-04-11 20:41:30 +10:00
item - > freed = false ;
2025-11-24 10:15:53 +11:00
HASH_ADD_STR ( * head , id , item ) ;
}
2025-12-06 11:50:42 +11:00
GroundFunction * createGroundFunction ( ) {
GroundFunction * gf = malloc ( sizeof ( GroundFunction ) ) ;
gf - > argSize = 0 ;
gf - > args = malloc ( sizeof ( GroundFunctionArgs ) ) ;
gf - > program = createGroundProgram ( ) ;
2026-01-02 20:29:27 +11:00
gf - > isNative = false ;
gf - > nativeFn = NULL ;
2025-12-02 09:00:21 +11:00
return gf ;
}
void addArgsToGroundFunction ( GroundFunction * function , GroundValueType type , char * name ) {
GroundFunctionArgs arg ;
arg . type = type ;
arg . name = name ;
function - > argSize + + ;
GroundFunctionArgs * ptr = realloc ( function - > args , function - > argSize * sizeof ( GroundFunctionArgs ) ) ;
if ( ptr = = NULL ) {
printf ( " Failed to allocate memory for arg of GroundFunction " ) ;
function - > argSize - - ;
return ;
}
function - > args = ptr ;
function - > args [ function - > argSize - 1 ] = arg ;
} ;
GroundValueType stringToValueType ( char * in ) {
2026-04-13 13:37:42 +10:00
if ( in = = NULL ) {
return ANY ;
}
2025-12-02 09:00:21 +11:00
if ( strcmp ( in , " int " ) = = 0 ) {
return INT ;
}
if ( strcmp ( in , " double " ) = = 0 ) {
return DOUBLE ;
}
if ( strcmp ( in , " bool " ) = = 0 ) {
return BOOL ;
}
if ( strcmp ( in , " string " ) = = 0 ) {
return STRING ;
}
if ( strcmp ( in , " char " ) = = 0 ) {
return CHAR ;
}
if ( strcmp ( in , " list " ) = = 0 ) {
return LIST ;
}
2025-12-08 11:08:08 +11:00
if ( strcmp ( in , " function " ) = = 0 ) {
return FUNCTION ;
}
2026-04-11 13:19:04 +10:00
if ( strcmp ( in , " struct " ) = = 0 ) {
return STRUCTVAL ;
}
if ( strcmp ( in , " any " ) = = 0 ) {
return ANY ;
}
2025-12-02 09:00:21 +11:00
return CUSTOM ;
}
2025-12-08 11:08:08 +11:00
GroundDebugInstruction parseDebugInstruction ( char * in ) {
GroundDebugInstruction gdi ;
size_t insize = strlen ( in ) ;
size_t spacepos = - 1 ;
for ( size_t i = 0 ; i < insize ; i + + ) {
if ( in [ i ] = = ' ' ) {
spacepos = i ;
break ;
}
}
2026-01-17 12:21:43 +11:00
if ( spacepos = = ( size_t ) - 1 ) {
2025-12-08 11:08:08 +11:00
spacepos = insize ;
}
char * instruction = malloc ( spacepos + 1 ) ;
if ( ! instruction ) {
gdi . type = UNKNOWN ;
gdi . arg = NULL ;
return gdi ;
}
strncpy ( instruction , in , spacepos ) ;
instruction [ spacepos ] = ' \0 ' ;
if ( strcmp ( instruction , " dump " ) = = 0 ) {
gdi . type = DUMP ;
} else if ( strcmp ( instruction , " inspect " ) = = 0 ) {
gdi . type = INSPECT ;
} else if ( strcmp ( instruction , " eval " ) = = 0 ) {
gdi . type = EVAL ;
} else if ( strcmp ( instruction , " continue " ) = = 0 ) {
gdi . type = CONTINUE ;
} else if ( strcmp ( instruction , " exit " ) = = 0 ) {
gdi . type = EXIT ;
2025-12-08 15:06:29 +11:00
} else if ( strcmp ( instruction , " step " ) = = 0 ) {
gdi . type = STEP ;
} else if ( strcmp ( instruction , " view " ) = = 0 ) {
gdi . type = VIEW ;
2025-12-08 11:08:08 +11:00
} else if ( strcmp ( instruction , " help " ) = = 0 ) {
gdi . type = HELP ;
} else {
gdi . type = UNKNOWN ;
}
size_t arglen = insize - spacepos ;
gdi . arg = malloc ( arglen + 1 ) ;
if ( gdi . arg ) {
strcpy ( gdi . arg , in + spacepos + 1 ) ;
}
free ( instruction ) ;
return gdi ;
}
2026-01-02 20:29:27 +11:00
void groundAddNativeFunction ( GroundScope * scope , char * name , NativeGroundFunction fn , GroundValueType returnType , int argCount , . . . ) {
GroundFunction * gf = createGroundFunction ( ) ;
gf - > isNative = true ;
gf - > nativeFn = fn ;
gf - > returnType = returnType ;
va_list args ;
va_start ( args , argCount ) ;
for ( int i = 0 ; i < argCount ; i + + ) {
GroundValueType type = va_arg ( args , GroundValueType ) ;
char * argName = va_arg ( args , char * ) ;
addArgsToGroundFunction ( gf , type , argName ) ;
}
va_end ( args ) ;
addVariable ( scope - > variables , name , createFunctionGroundValue ( gf ) ) ;
}
2026-01-17 20:30:14 +11:00
GroundFunction * parseFunction ( GroundProgram * in , size_t errorOffset ) {
GroundFunction * function = createGroundFunction ( ) ;
for ( size_t i = 0 ; i < in - > size ; i + + ) {
if ( in - > instructions [ i ] . args . length < 2 ) {
function - > returnType = NONE ;
} else {
if ( in - > instructions [ i ] . args . args [ 1 ] . type ! = TYPEREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a TypeRef for arg 2 " , & in - > instructions [ i ] , errorOffset + i ) ;
}
GroundArg * args = in - > instructions [ i ] . args . args ;
function - > returnType = stringToValueType ( args [ 1 ] . value . refName ) ;
size_t length = in - > instructions [ i ] . args . length ;
for ( size_t j = 2 ; j < length ; j + = 2 ) {
if ( args [ j ] . type ! = TYPEREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a TypeRef " , & in - > instructions [ i ] , errorOffset + i ) ;
}
if ( j + 1 > = length ) {
runtimeError ( TOO_FEW_ARGS , " Expecting a DirectRef after a TypeRef " , & in - > instructions [ i ] , errorOffset + i ) ;
}
if ( args [ j + 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef after a TypeRef " , & in - > instructions [ i ] , errorOffset + i ) ;
}
addArgsToGroundFunction ( function , stringToValueType ( args [ j ] . value . refName ) , args [ j + 1 ] . value . refName ) ;
}
}
i + + ;
2026-01-17 20:37:16 +11:00
while ( i < in - > size ) {
2026-01-17 20:30:14 +11:00
addInstructionToProgram ( & function - > program , in - > instructions [ i ] ) ;
i + + ;
}
break ;
}
return function ;
}
2026-01-17 20:04:18 +11:00
GroundStruct parseStruct ( GroundProgram * in , GroundScope * scope , size_t errorOffset ) {
2026-01-16 17:45:48 +11:00
GroundStruct gstruct = createStruct ( ) ;
2026-01-17 12:21:43 +11:00
for ( size_t i = 0 ; i < in - > size ; i + + ) {
switch ( in - > instructions [ i ] . type ) {
case SET : {
2026-01-17 19:58:21 +11:00
if ( in - > instructions [ i ] . args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , & in - > instructions [ i ] , i + errorOffset ) ;
}
if ( in - > instructions [ i ] . args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , & in - > instructions [ i ] , i + errorOffset ) ;
}
if ( in - > instructions [ i ] . args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 1 " , & in - > instructions [ i ] , i + errorOffset ) ;
}
if ( in - > instructions [ i ] . args . args [ 1 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 2 " , & in - > instructions [ i ] , i + errorOffset ) ;
}
addFieldToStruct ( & gstruct , in - > instructions [ i ] . args . args [ 0 ] . value . refName , in - > instructions [ i ] . args . args [ 1 ] . value . value ) ;
2026-01-17 12:21:43 +11:00
break ;
}
case INIT : {
2026-01-17 20:04:18 +11:00
if ( in - > instructions [ i ] . args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , & in - > instructions [ i ] , currentInstruction ) ;
}
if ( in - > instructions [ i ] . args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , & in - > instructions [ i ] , currentInstruction ) ;
}
if ( in - > instructions [ i ] . args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 1 " , & in - > instructions [ i ] , currentInstruction ) ;
}
if ( in - > instructions [ i ] . args . args [ 1 ] . type ! = TYPEREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a TypeRef for arg 2 " , & in - > instructions [ i ] , currentInstruction ) ;
}
GroundValue gv ;
2026-01-21 11:17:19 +11:00
switch ( stringToValueType ( in - > instructions [ i ] . args . args [ 1 ] . value . refName ) ) {
2026-01-17 20:04:18 +11:00
case INT : {
gv = createIntGroundValue ( 0 ) ;
break ;
}
case DOUBLE : {
gv = createDoubleGroundValue ( 0 ) ;
break ;
}
case STRING : {
gv = createStringGroundValue ( " " ) ;
break ;
}
case CHAR : {
gv = createCharGroundValue ( ' \0 ' ) ;
break ;
}
case BOOL : {
gv = createBoolGroundValue ( false ) ;
break ;
}
case LIST : {
gv = createListGroundValue ( createList ( ) ) ;
break ;
}
case FUNCTION : {
gv = createFunctionGroundValue ( createGroundFunction ( ) ) ;
break ;
}
case STRUCTVAL : {
gv . type = STRUCTVAL ;
gv . data . structVal = malloc ( sizeof ( GroundStruct ) ) ;
* gv . data . structVal = createStruct ( ) ;
break ;
}
case CUSTOM : {
GroundVariable * var = findVariable ( * scope - > variables , in - > instructions [ i ] . args . args [ 1 ] . value . refName ) ;
if ( var = = NULL ) {
runtimeError ( UNKNOWN_VARIABLE , " Couldn't find the specified type " , & in - > instructions [ i ] , currentInstruction ) ;
}
if ( var - > value . type ! = STRUCTVAL ) {
runtimeError ( ARG_TYPE_MISMATCH , " TypeRef does not reference a struct " , & in - > instructions [ i ] , currentInstruction ) ;
}
GroundStruct * gstruct = var - > value . data . structVal ;
gv . type = CUSTOM ;
gv . data . customVal = malloc ( sizeof ( GroundObject ) ) ;
* gv . data . customVal = createObject ( * gstruct ) ;
2026-01-20 19:57:49 +11:00
gv . customType = gstruct ;
2026-01-17 20:04:18 +11:00
break ;
}
case NONE : {
gv . type = NONE ;
break ;
}
default : {
runtimeError ( FIXME , " Reached should-be-impossible state " , & in - > instructions [ i ] , currentInstruction ) ;
break ;
}
}
addFieldToStruct ( & gstruct , in - > instructions [ i ] . args . args [ 0 ] . value . refName , gv ) ;
2026-01-17 12:21:43 +11:00
break ;
}
case FUN : {
2026-01-18 13:44:26 +11:00
if ( in - > instructions [ i ] . args . length < 1 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 1 or more args " , & in - > instructions [ i ] , i ) ;
}
if ( in - > instructions [ i ] . args . args [ 0 ] . type ! = FNREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a FunctionRef for arg 1 " , & in - > instructions [ i ] , i ) ;
}
char * name = malloc ( strlen ( in - > instructions [ i ] . args . args [ 0 ] . value . refName ) + 1 ) ;
strcpy ( name , in - > instructions [ i ] . args . args [ 0 ] . value . refName ) ;
size_t counter = 1 ;
GroundProgram gp = createGroundProgram ( ) ;
addInstructionToProgram ( & gp , in - > instructions [ i ] ) ;
size_t errorOffset = i ;
i + + ;
while ( counter > 0 ) {
if ( i > = in - > size ) {
runtimeError ( PREMATURE_EOF , " Reached end of scope before function definition ended " , & in - > instructions [ i - 1 ] , i - 1 ) ;
}
if ( in - > instructions [ i ] . type = = FUN ) {
counter + + ;
}
if ( in - > instructions [ i ] . type = = ENDFUN ) {
counter - - ;
}
addInstructionToProgram ( & gp , in - > instructions [ i ] ) ;
i + + ;
}
2026-01-24 15:46:09 +11:00
i - - ;
2026-01-18 13:44:26 +11:00
GroundFunction * function = parseFunction ( & gp , errorOffset ) ;
function - > startLine = i ;
2026-03-02 10:09:10 +11:00
function - > closure = copyGroundScope ( scope ) ;
2026-01-18 13:44:26 +11:00
GroundValue gv = createFunctionGroundValue ( function ) ;
addFieldToStruct ( & gstruct , name , gv ) ;
2026-01-17 12:21:43 +11:00
break ;
}
2026-01-17 19:58:21 +11:00
case ENDSTRUCT : {
break ;
}
2026-01-17 12:21:43 +11:00
default : {
runtimeError ( INVALID_INSTRUCTION , " Unsupported instruction while inside struct " , & in - > instructions [ i ] , errorOffset + i ) ;
}
}
}
2026-01-16 17:45:48 +11:00
return gstruct ;
}
2025-12-02 09:00:21 +11:00
GroundValue interpretGroundProgram ( GroundProgram * in , GroundScope * inScope ) {
2025-11-23 19:59:22 +11:00
GroundLabel * labels = NULL ;
GroundVariable * variables = NULL ;
2026-04-14 10:50:27 +10:00
GroundCatch * catches = NULL ;
2025-12-08 15:06:29 +11:00
int instructionsToPause = - 1 ;
2025-11-23 19:59:22 +11:00
GroundScope scope ;
2025-12-02 09:00:21 +11:00
if ( inScope ! = NULL ) {
scope = * inScope ;
} else {
scope . labels = & labels ;
scope . variables = & variables ;
2026-04-14 10:50:27 +10:00
scope . catches = & catches ;
2025-12-02 09:00:21 +11:00
}
2026-01-18 20:49:50 +11:00
scope . isMainScope = isMainScopeGlobal ;
isMainScopeGlobal = false ;
2026-01-16 17:45:48 +11:00
// Preprocess all labels, structs and functions
2026-01-17 12:21:43 +11:00
for ( size_t i = 0 ; i < in - > size ; i + + ) {
2025-11-23 18:34:30 +11:00
if ( in - > instructions [ i ] . type = = CREATELABEL ) {
2025-11-23 19:59:22 +11:00
addLabel ( scope . labels , in - > instructions [ i ] . args . args [ 0 ] . value . refName , i ) ;
2025-11-23 18:34:30 +11:00
}
2026-03-02 10:09:10 +11:00
if ( in - > instructions [ i ] . type = = STRUCT ) {
2025-12-02 09:00:21 +11:00
if ( in - > instructions [ i ] . args . length < 1 ) {
2026-03-02 10:09:10 +11:00
runtimeError ( TOO_FEW_ARGS , " Expecting 1 arg " , & in - > instructions [ i ] , i ) ;
2025-12-02 09:00:21 +11:00
}
2026-03-02 10:09:10 +11:00
if ( in - > instructions [ i ] . args . length > 1 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 1 arg " , & in - > instructions [ i ] , i ) ;
}
if ( in - > instructions [ i ] . args . args [ 0 ] . type ! = TYPEREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expected arg 1 to be a typeref " , & in - > instructions [ i ] , i ) ;
2025-12-02 09:00:21 +11:00
}
2026-01-17 20:30:14 +11:00
char * name = malloc ( strlen ( in - > instructions [ i ] . args . args [ 0 ] . value . refName ) + 1 ) ;
strcpy ( name , in - > instructions [ i ] . args . args [ 0 ] . value . refName ) ;
2026-03-02 10:09:10 +11:00
i + + ;
2026-01-17 20:30:14 +11:00
size_t counter = 1 ;
GroundProgram gp = createGroundProgram ( ) ;
size_t errorOffset = i ;
while ( counter > 0 ) {
if ( i > = in - > size ) {
2026-03-02 10:09:10 +11:00
runtimeError ( PREMATURE_EOF , " Reached end of scope before struct definition ended " , & in - > instructions [ i - 1 ] , i - 1 ) ;
2026-01-17 20:30:14 +11:00
}
2026-03-02 10:09:10 +11:00
if ( in - > instructions [ i ] . type = = STRUCT ) {
2026-01-17 20:30:14 +11:00
counter + + ;
}
2026-03-02 10:09:10 +11:00
if ( in - > instructions [ i ] . type = = ENDSTRUCT ) {
2026-01-17 20:30:14 +11:00
counter - - ;
}
addInstructionToProgram ( & gp , in - > instructions [ i ] ) ;
i + + ;
}
2026-01-24 15:46:09 +11:00
i - - ;
2026-01-17 20:30:14 +11:00
2026-03-02 10:09:10 +11:00
GroundValue gv = {
. type = STRUCTVAL ,
. data . structVal = malloc ( sizeof ( GroundStruct ) )
} ;
* gv . data . structVal = parseStruct ( & gp , & scope , errorOffset ) ;
2026-01-17 20:30:14 +11:00
addVariable ( scope . variables , name , gv ) ;
2025-12-02 09:00:21 +11:00
}
2026-03-02 10:09:10 +11:00
}
for ( size_t i = 0 ; i < in - > size ; i + + ) {
if ( in - > instructions [ i ] . type = = FUN ) {
2026-01-16 17:45:48 +11:00
if ( in - > instructions [ i ] . args . length < 1 ) {
2026-03-02 10:09:10 +11:00
runtimeError ( TOO_FEW_ARGS , " Expecting 1 or more args " , & in - > instructions [ i ] , i ) ;
2026-01-16 17:45:48 +11:00
}
2026-03-02 10:09:10 +11:00
if ( in - > instructions [ i ] . args . args [ 0 ] . type ! = FNREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a FunctionRef for arg 1 " , & in - > instructions [ i ] , i ) ;
2026-01-16 17:45:48 +11:00
}
2026-01-17 19:58:21 +11:00
char * name = malloc ( strlen ( in - > instructions [ i ] . args . args [ 0 ] . value . refName ) + 1 ) ;
strcpy ( name , in - > instructions [ i ] . args . args [ 0 ] . value . refName ) ;
2026-01-16 17:45:48 +11:00
size_t counter = 1 ;
2026-01-17 12:21:43 +11:00
GroundProgram gp = createGroundProgram ( ) ;
2026-03-02 10:09:10 +11:00
addInstructionToProgram ( & gp , in - > instructions [ i ] ) ;
2026-01-17 12:21:43 +11:00
size_t errorOffset = i ;
2026-03-02 10:09:10 +11:00
i + + ;
2026-01-16 17:45:48 +11:00
while ( counter > 0 ) {
if ( i > = in - > size ) {
2026-03-02 10:09:10 +11:00
runtimeError ( PREMATURE_EOF , " Reached end of scope before function definition ended " , & in - > instructions [ i - 1 ] , i - 1 ) ;
2026-01-16 17:45:48 +11:00
}
2026-03-02 10:09:10 +11:00
if ( in - > instructions [ i ] . type = = FUN ) {
2026-01-16 17:45:48 +11:00
counter + + ;
}
2026-03-02 10:09:10 +11:00
if ( in - > instructions [ i ] . type = = ENDFUN ) {
2026-01-16 17:45:48 +11:00
counter - - ;
}
addInstructionToProgram ( & gp , in - > instructions [ i ] ) ;
2026-01-17 12:21:43 +11:00
i + + ;
2026-01-16 17:45:48 +11:00
}
2026-01-24 15:46:09 +11:00
i - - ;
2026-01-16 17:45:48 +11:00
2026-03-02 10:09:10 +11:00
GroundFunction * function = parseFunction ( & gp , errorOffset ) ;
function - > startLine = i ;
function - > closure = copyGroundScope ( inScope ) ;
GroundValue gv = createFunctionGroundValue ( function ) ;
2026-01-17 19:58:21 +11:00
addVariable ( scope . variables , name , gv ) ;
2026-01-16 17:45:48 +11:00
}
2026-03-02 10:09:10 +11:00
/*
2025-12-06 14:35:13 +11:00
if ( in - > instructions [ i ] . type = = FUN ) {
int count = 1 ;
while ( count > 0 ) {
i + + ;
if ( i > = in - > size ) {
return createNoneGroundValue ( ) ;
}
if ( in - > instructions [ i ] . type = = FUN ) {
count + + ;
}
if ( in - > instructions [ i ] . type = = ENDFUN ) {
count - - ;
}
}
}
2026-03-02 10:09:10 +11:00
*/
2026-01-18 13:44:26 +11:00
if ( in - > instructions [ i ] . type = = STRUCT ) {
int count = 1 ;
while ( count > 0 ) {
i + + ;
if ( i > = in - > size ) {
return createNoneGroundValue ( ) ;
}
if ( in - > instructions [ i ] . type = = STRUCT ) {
count + + ;
}
if ( in - > instructions [ i ] . type = = ENDSTRUCT ) {
count - - ;
}
}
}
2025-12-08 15:06:29 +11:00
if ( in - > instructions [ i ] . type = = PAUSE | | instructionsToPause = = 0 ) {
2025-12-08 11:08:08 +11:00
printf ( " Paused execution \n " ) ;
printf ( " Previous instruction: " ) ;
if ( i > 0 ) {
printGroundInstruction ( & in - > instructions [ i - 1 ] ) ;
}
while ( true ) {
printf ( " \n prompt> " ) ;
char buffer [ 256 ] ;
if ( fgets ( buffer , sizeof ( buffer ) , stdin ) ! = NULL ) {
buffer [ strcspn ( buffer , " \n " ) ] = ' \0 ' ;
} else {
runtimeError ( FIXME , " Failed to read input from console with fgets " , NULL , - 1 ) ;
}
GroundDebugInstruction gdi = parseDebugInstruction ( buffer ) ;
bool shouldBreak = false ;
switch ( gdi . type ) {
case CONTINUE : {
shouldBreak = true ;
break ;
}
case EXIT : {
exit ( 0 ) ;
break ;
}
case DUMP : {
if ( scope . variables = = NULL ) {
printf ( " Can't access variables " ) ;
break ;
}
if ( * scope . variables = = NULL ) {
printf ( " Can't access variables " ) ;
break ;
}
GroundVariable * entry ;
GroundVariable * tmp ;
GroundVariable * head = * scope . variables ;
HASH_ITER ( hh , head , entry , tmp ) {
if ( entry ! = NULL ) {
printf ( " %s: " , entry - > id ) ;
printGroundValue ( & entry - > value ) ;
printf ( " \n " ) ;
}
}
break ;
}
case INSPECT : {
printf ( " %s: " , gdi . arg ) ;
GroundVariable * var = findVariable ( * scope . variables , gdi . arg ) ;
if ( var = = NULL ) {
printf ( " (nothing) " ) ;
} else {
printGroundValue ( & var - > value ) ;
}
break ;
}
case EVAL : {
GroundProgram program = parseFile ( gdi . arg ) ;
interpretGroundProgram ( & program , & scope ) ;
freeGroundProgram ( & program ) ;
break ;
}
2025-12-08 15:06:29 +11:00
case STEP : {
if ( gdi . arg ! = NULL & & strlen ( gdi . arg ) > 0 ) {
instructionsToPause = atoi ( gdi . arg ) ;
} else {
instructionsToPause = 1 ;
}
instructionsToPause + + ;
shouldBreak = true ;
break ;
}
case VIEW : {
break ;
}
2025-12-08 11:08:08 +11:00
case HELP : {
printf ( " Ground Debugger Help \n " ) ;
printf ( " Didn't mean to end up here? Type \" continue \" to escape this prompt. \n " ) ;
printf ( " Commands: \n " ) ;
printf ( " continue: Continue execution of the program \n " ) ;
printf ( " exit: Stop execution early \n " ) ;
printf ( " dump: Shows all variables and their contents \n " ) ;
printf ( " inspect (variable): Shows the contents of a variable \n " ) ;
printf ( " eval (code): Runs Ground code in the current scope \n " ) ;
printf ( " help: Shows this help message " ) ;
2025-12-08 15:06:29 +11:00
break ;
2025-12-08 11:08:08 +11:00
}
case UNKNOWN : {
printf ( " Unknown instruction (type \" help \" for help) " ) ;
break ;
}
}
if ( shouldBreak ) {
break ;
}
}
2025-12-08 15:06:29 +11:00
if ( in - > instructions [ i ] . type = = PAUSE ) {
continue ;
}
2025-12-08 11:08:08 +11:00
}
2025-12-08 15:06:29 +11:00
instructionsToPause - - ;
2025-12-06 14:35:13 +11:00
int ci = currentInstruction ;
GroundValue gv = interpretGroundInstruction ( in - > instructions [ i ] , & scope ) ;
2026-04-15 10:37:14 +10:00
if ( gv . type = = ERROR ) {
GroundCatch * catch = findCatch ( * scope . catches , gv . data . errorVal . type ) ;
if ( catch ! = NULL ) {
currentInstruction = catch - > label - > lineNum ;
} else {
if ( scope . isMainScope ) {
throwError ( & gv . data . errorVal ) ;
} else {
return gv ;
}
}
} else if ( gv . type ! = NONE ) {
2025-12-06 14:35:13 +11:00
return gv ;
}
if ( ci ! = currentInstruction ) {
i = currentInstruction ;
}
2025-11-23 18:34:30 +11:00
currentInstruction + + ;
}
2025-12-06 14:35:13 +11:00
return createNoneGroundValue ( ) ;
2025-11-23 18:34:30 +11:00
}
2025-12-02 09:00:21 +11:00
GroundValue interpretGroundInstruction ( GroundInstruction inst , GroundScope * scope ) {
2025-11-24 10:15:53 +11:00
GroundInstruction copied_inst = copyGroundInstruction ( & inst ) ;
GroundInstruction * in = & copied_inst ;
2025-11-23 21:23:04 +11:00
// Insert variables prefixed with $
2026-01-17 12:21:43 +11:00
for ( size_t i = 0 ; i < in - > args . length ; i + + ) {
2025-11-23 21:23:04 +11:00
if ( in - > args . args [ i ] . type = = VALREF ) {
2025-11-24 10:15:53 +11:00
GroundVariable * variable = findVariable ( * scope - > variables , in - > args . args [ i ] . value . refName ) ;
2025-11-23 21:23:04 +11:00
if ( variable ) {
2026-04-13 18:27:42 +10:00
// If there is a duplicator, call it (except when returning and getting fields)
if ( variable - > value . type = = CUSTOM & & in - > type ! = RETURN & & in - > type ! = GETFIELD ) {
GroundObject * obj = variable - > value . data . customVal ;
GroundObjectField * duplicator = findField ( * obj , " duplicator " ) ;
if ( duplicator ! = NULL ) {
if ( duplicator - > value . type ! = FUNCTION ) {
runtimeError ( ARG_TYPE_MISMATCH , " duplicator is not a function " , in , currentInstruction ) ;
}
GroundInstruction gi = createGroundInstruction ( CALLMETHOD ) ;
addArgToInstruction ( & gi , createRefGroundArg ( DIRREF , in - > args . args [ i ] . value . refName ) ) ;
addArgToInstruction ( & gi , createRefGroundArg ( FNREF , " duplicator " ) ) ;
addArgToInstruction ( & gi , createValueGroundArg ( variable - > value ) ) ;
addArgToInstruction ( & gi , createRefGroundArg ( DIRREF , " __ground_tmp_duplicate " ) ) ;
GroundValue gv = interpretGroundInstruction ( gi , scope ) ;
if ( gv . type = = ERROR ) {
return gv ;
}
GroundVariable * tmp = findVariable ( * scope - > variables , " __ground_tmp_duplicate " ) ;
if ( tmp = = NULL ) {
runtimeError ( FIXME , " Couldn't find temporary variable (this should never happen) " , in , currentInstruction ) ;
}
in - > args . args [ i ] . value . value = copyGroundValue ( & tmp - > value ) ;
in - > args . args [ i ] . type = VALUE ;
deleteVariable ( scope - > variables , tmp ) ;
continue ;
}
}
2025-11-24 10:15:53 +11:00
free ( in - > args . args [ i ] . value . refName ) ; // Free the strdup'd refName
2025-12-01 10:36:09 +11:00
in - > args . args [ i ] . value . value = copyGroundValue ( & variable - > value ) ;
2025-11-23 21:23:04 +11:00
in - > args . args [ i ] . type = VALUE ;
} else {
runtimeError ( UNKNOWN_VARIABLE , NULL , in , currentInstruction ) ;
}
}
}
2025-11-23 18:34:30 +11:00
switch ( in - > type ) {
2026-01-17 12:21:43 +11:00
// We can safely ignore these instructions, as they have been preprocessed
2025-12-02 09:00:21 +11:00
case FUN :
case ENDFUN :
2026-01-17 12:21:43 +11:00
case STRUCT :
case ENDSTRUCT :
2025-11-23 18:34:30 +11:00
case CREATELABEL : {
break ;
}
2025-11-28 09:23:43 +11:00
/*
* CONTROL FLOW
* These instructions are for controlling how the program is executed .
* Instructions :
* if , jump , end
*/
2025-11-23 19:18:10 +11:00
case IF : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 arguments " , in , currentInstruction ) ;
}
if ( in - > args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 arguments " , in , currentInstruction ) ;
}
2025-11-23 19:59:22 +11:00
if ( in - > args . args [ 0 ] . type ! = VALUE | | in - > args . args [ 0 ] . value . value . type ! = BOOL ) {
2025-11-23 19:18:10 +11:00
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a bool for arg 0 " , in , currentInstruction ) ;
}
2025-11-23 19:59:22 +11:00
if ( in - > args . args [ 1 ] . type ! = LINEREF ) {
2025-11-23 19:18:10 +11:00
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a LineRef for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . value . value . data . boolVal ) {
2025-11-23 19:59:22 +11:00
GroundLabel * label = findLabel ( * scope - > labels , in - > args . args [ 1 ] . value . refName ) ;
2025-11-23 19:18:10 +11:00
if ( label ) {
currentInstruction = label - > lineNum ;
} else {
runtimeError ( UNKNOWN_LABEL , NULL , in , currentInstruction ) ;
}
}
2025-11-23 19:59:22 +11:00
break ;
2025-11-23 19:18:10 +11:00
}
case JUMP : {
if ( in - > args . length < 1 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 1 argument " , in , currentInstruction ) ;
}
if ( in - > args . length > 1 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 1 argument " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = LINEREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a LineRef for arg 1 " , in , currentInstruction ) ;
}
2025-11-23 19:59:22 +11:00
GroundLabel * label = findLabel ( * scope - > labels , in - > args . args [ 0 ] . value . refName ) ;
2025-11-23 19:18:10 +11:00
if ( label ) {
currentInstruction = label - > lineNum ;
} else {
runtimeError ( UNKNOWN_LABEL , NULL , in , currentInstruction ) ;
}
2025-11-23 19:59:22 +11:00
break ;
2025-11-23 19:18:10 +11:00
}
case END : {
2025-11-23 19:59:22 +11:00
if ( in - > args . length < 1 | | in - > args . args [ 0 ] . type ! = VALUE | | in - > args . args [ 0 ] . value . value . type ! = INT ) {
2025-11-23 19:18:10 +11:00
exit ( 0 ) ;
} else {
2025-11-23 19:59:22 +11:00
exit ( in - > args . args [ 0 ] . value . value . data . intVal ) ;
2025-11-23 19:18:10 +11:00
}
break ;
}
2025-11-28 09:23:43 +11:00
/*
* I / O
* These instructions take information from the user , and print it out .
* Instructions :
* print , println , input
*/
2025-11-23 18:34:30 +11:00
case PRINT : {
if ( in - > args . length < 1 ) {
2025-11-23 21:23:04 +11:00
runtimeError ( TOO_FEW_ARGS , " Expecting 1 or more args " , in , currentInstruction ) ;
2025-11-23 18:34:30 +11:00
}
2026-01-17 12:21:43 +11:00
for ( size_t i = 0 ; i < in - > args . length ; i + + ) {
2025-11-24 11:37:16 +11:00
if ( i ! = 0 ) printf ( " " ) ;
2025-11-23 18:34:30 +11:00
printGroundArg ( & in - > args . args [ i ] ) ;
}
break ;
}
case PRINTLN : {
if ( in - > args . length < 1 ) {
2025-11-23 21:23:04 +11:00
runtimeError ( TOO_FEW_ARGS , " Expecting 1 or more args " , in , currentInstruction ) ;
2025-11-23 18:34:30 +11:00
}
2026-01-17 12:21:43 +11:00
for ( size_t i = 0 ; i < in - > args . length ; i + + ) {
2025-11-24 11:37:16 +11:00
if ( i ! = 0 ) printf ( " " ) ;
2025-11-23 18:34:30 +11:00
printGroundArg ( & in - > args . args [ i ] ) ;
}
printf ( " \n " ) ;
break ;
}
2025-11-24 11:37:16 +11:00
case INPUT : {
if ( in - > args . length < 1 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 1 arg " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 1 " , in , currentInstruction ) ;
}
char buffer [ 256 ] ;
if ( fgets ( buffer , sizeof ( buffer ) , stdin ) ! = NULL ) {
buffer [ strcspn ( buffer , " \n " ) ] = ' \0 ' ;
addVariable ( scope - > variables , in - > args . args [ 0 ] . value . refName , createStringGroundValue ( buffer ) ) ;
} else {
runtimeError ( FIXME , " Failed to read input from console with fgets " , in , currentInstruction ) ;
}
break ;
}
2026-01-19 21:14:48 +11:00
case ERRORCMD : {
if ( in - > args . length < 3 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
} else if ( in - > args . length > 3 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 2 ] . value . value . type ! = INT ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an int for arg 2 " , in , currentInstruction ) ;
}
customError ( in - > args . args [ 0 ] , in - > args . args [ 1 ] , in , currentInstruction , in - > args . args [ 2 ] . value . value . data . intVal ) ;
break ;
}
2025-11-28 09:23:43 +11:00
/*
* VARIABLES AND LISTS
* These instructions are for initializing variables and lists .
* Instructions :
2025-12-01 10:15:37 +11:00
* set , gettype , exists , setlist , setlistat , getlistat , getlistsize , listappend
2025-11-28 09:23:43 +11:00
*/
2025-11-23 21:23:04 +11:00
case SET : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 2 " , in , currentInstruction ) ;
}
addVariable ( scope - > variables , in - > args . args [ 0 ] . value . refName , in - > args . args [ 1 ] . value . value ) ;
break ;
}
2025-11-28 09:23:43 +11:00
case GETTYPE : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 2 " , in , currentInstruction ) ;
}
switch ( in - > args . args [ 0 ] . value . value . type ) {
case INT : {
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createStringGroundValue ( " int " ) ) ;
break ;
}
case DOUBLE : {
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createStringGroundValue ( " double " ) ) ;
break ;
}
case STRING : {
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createStringGroundValue ( " string " ) ) ;
break ;
}
case CHAR : {
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createStringGroundValue ( " char " ) ) ;
break ;
}
case BOOL : {
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createStringGroundValue ( " bool " ) ) ;
break ;
}
case LIST : {
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createStringGroundValue ( " list " ) ) ;
break ;
}
case CUSTOM : {
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createStringGroundValue ( " custom " ) ) ;
break ;
}
2025-12-11 13:07:37 +11:00
case FUNCTION : {
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createStringGroundValue ( " function " ) ) ;
2026-01-17 12:21:43 +11:00
break ;
}
case STRUCTVAL : {
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createStringGroundValue ( " struct " ) ) ;
break ;
2025-12-11 13:07:37 +11:00
}
2026-04-11 13:19:04 +10:00
case ANY : {
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createStringGroundValue ( " any " ) ) ;
break ;
}
2025-12-02 09:00:21 +11:00
case NONE : {
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createStringGroundValue ( " none " ) ) ;
2026-01-17 12:21:43 +11:00
break ;
2025-12-02 09:00:21 +11:00
}
2026-01-18 20:49:50 +11:00
case ERROR : {
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createStringGroundValue ( " error " ) ) ;
break ;
}
2025-11-28 09:23:43 +11:00
}
break ;
}
case EXISTS : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 2 " , in , currentInstruction ) ;
}
if ( findVariable ( * scope - > variables , in - > args . args [ 0 ] . value . refName ) ) {
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createBoolGroundValue ( true ) ) ;
} else {
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createBoolGroundValue ( false ) ) ;
}
break ;
}
2025-12-01 10:15:37 +11:00
case SETLIST : {
2026-04-11 09:44:48 +10:00
if ( listDeprecationWarning ) {
printf ( " Lists are deprecated, please migrate to using the \" collections \" library soon. \n " ) ;
listDeprecationWarning = false ;
}
2025-12-01 10:15:37 +11:00
if ( in - > args . length < 1 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 1 arg " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 1 " , in , currentInstruction ) ;
}
List newList = createList ( ) ;
2026-01-17 12:21:43 +11:00
for ( size_t i = 1 ; i < in - > args . length ; i + + ) {
2025-12-01 10:15:37 +11:00
if ( in - > args . args [ i ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for all args after arg 1 " , in , currentInstruction ) ;
}
appendToList ( & newList , in - > args . args [ i ] . value . value ) ;
}
addVariable ( scope - > variables , in - > args . args [ 0 ] . value . refName , createListGroundValue ( newList ) ) ;
break ;
}
case SETLISTAT : {
2026-04-11 09:44:48 +10:00
if ( listDeprecationWarning ) {
printf ( " Lists are deprecated, please migrate to using the \" collections \" library soon. \n " ) ;
listDeprecationWarning = false ;
}
2025-12-01 10:15:37 +11:00
if ( in - > args . length < 3 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef to a List for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = VALUE & & in - > args . args [ 1 ] . value . value . type ! = INT ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int value for arg 2 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 2 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 3 " , in , currentInstruction ) ;
}
GroundVariable * var = findVariable ( * scope - > variables , in - > args . args [ 0 ] . value . refName ) ;
if ( var = = NULL ) {
runtimeError ( UNKNOWN_VARIABLE , NULL , in , currentInstruction ) ;
}
GroundValue * value = & var - > value ;
if ( value = = NULL ) {
runtimeError ( FIXME , NULL , in , currentInstruction ) ;
}
if ( value - > type ! = LIST ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef to a List for arg 1 " , in , currentInstruction ) ;
}
ListAccessStatus status = setListAt ( & value - > data . listVal , in - > args . args [ 1 ] . value . value . data . intVal , in - > args . args [ 2 ] . value . value ) ;
switch ( status ) {
case LIST_OKAY :
2025-12-02 09:00:21 +11:00
break ;
2025-12-01 10:15:37 +11:00
case LIST_OUT_OF_BOUNDS :
runtimeError ( LIST_ERROR , " Out of bounds index " , in , currentInstruction ) ;
case LIST_FIXME :
default :
runtimeError ( FIXME , " List related error " , in , currentInstruction ) ;
}
break ;
}
case GETLISTAT : {
2026-04-11 09:44:48 +10:00
if ( listDeprecationWarning ) {
printf ( " Lists are deprecated, please migrate to using the \" collections \" library soon. \n " ) ;
listDeprecationWarning = false ;
}
2025-12-01 10:15:37 +11:00
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef to a List for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = VALUE & & in - > args . args [ 1 ] . value . value . type ! = INT ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an integer value for arg 2 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 2 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef to a List for arg 3 " , in , currentInstruction ) ;
}
GroundVariable * var = findVariable ( * scope - > variables , in - > args . args [ 0 ] . value . refName ) ;
if ( var = = NULL ) {
runtimeError ( UNKNOWN_VARIABLE , NULL , in , currentInstruction ) ;
}
GroundValue * value = & var - > value ;
if ( value = = NULL ) {
runtimeError ( FIXME , NULL , in , currentInstruction ) ;
}
if ( value - > type ! = LIST ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef to a List for arg 1 " , in , currentInstruction ) ;
}
ListAccess status = getListAt ( & value - > data . listVal , in - > args . args [ 1 ] . value . value . data . intVal ) ;
switch ( status . status ) {
case LIST_OKAY : {
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , * status . value ) ;
break ;
}
case LIST_OUT_OF_BOUNDS :
runtimeError ( LIST_ERROR , " Out of bounds index " , in , currentInstruction ) ;
case LIST_FIXME :
default :
runtimeError ( FIXME , " List related error " , in , currentInstruction ) ;
}
break ;
}
case GETLISTSIZE : {
2026-04-11 09:44:48 +10:00
if ( listDeprecationWarning ) {
printf ( " Lists are deprecated, please migrate to using the \" collections \" library soon. \n " ) ;
listDeprecationWarning = false ;
}
2025-12-01 10:15:37 +11:00
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef to a List for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 2 " , in , currentInstruction ) ;
}
GroundVariable * var = findVariable ( * scope - > variables , in - > args . args [ 0 ] . value . refName ) ;
if ( var = = NULL ) {
runtimeError ( UNKNOWN_VARIABLE , NULL , in , currentInstruction ) ;
}
GroundValue * value = & var - > value ;
if ( value = = NULL ) {
runtimeError ( FIXME , NULL , in , currentInstruction ) ;
}
if ( value - > type ! = LIST ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef to a List for arg 1 " , in , currentInstruction ) ;
}
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createIntGroundValue ( value - > data . listVal . size ) ) ;
break ;
}
case LISTAPPEND : {
2026-04-11 09:44:48 +10:00
if ( listDeprecationWarning ) {
printf ( " Lists are deprecated, please migrate to using the \" collections \" library soon. \n " ) ;
listDeprecationWarning = false ;
}
2025-12-01 10:15:37 +11:00
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef to a List for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 2 " , in , currentInstruction ) ;
}
GroundVariable * var = findVariable ( * scope - > variables , in - > args . args [ 0 ] . value . refName ) ;
if ( var = = NULL ) {
runtimeError ( UNKNOWN_VARIABLE , NULL , in , currentInstruction ) ;
}
GroundValue * value = & var - > value ;
if ( value = = NULL ) {
runtimeError ( FIXME , NULL , in , currentInstruction ) ;
}
if ( value - > type ! = LIST ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef to a List for arg 1 " , in , currentInstruction ) ;
}
appendToList ( & value - > data . listVal , in - > args . args [ 1 ] . value . value ) ;
break ;
}
2025-11-23 21:23:04 +11:00
2025-12-13 09:50:49 +11:00
case STOI : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE | | in - > args . args [ 0 ] . value . value . type ! = STRING ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a String for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 2 " , in , currentInstruction ) ;
}
2026-01-24 16:36:37 +11:00
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createIntGroundValue ( atoll ( in - > args . args [ 0 ] . value . value . data . stringVal ) ) ) ;
2025-12-13 09:50:49 +11:00
break ;
}
case STOD : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE | | in - > args . args [ 0 ] . value . value . type ! = STRING ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a String for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 2 " , in , currentInstruction ) ;
}
2026-01-24 16:36:37 +11:00
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createDoubleGroundValue ( atof ( in - > args . args [ 0 ] . value . value . data . stringVal ) ) ) ;
2025-12-13 09:50:49 +11:00
break ;
}
2026-01-24 18:18:52 +11:00
case ITOC : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE | | in - > args . args [ 0 ] . value . value . type ! = INT ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 2 " , in , currentInstruction ) ;
}
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createCharGroundValue ( ( char ) in - > args . args [ 0 ] . value . value . data . intVal ) ) ;
break ;
}
case CTOI : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE | | in - > args . args [ 0 ] . value . value . type ! = CHAR ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Char for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 2 " , in , currentInstruction ) ;
}
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createIntGroundValue ( ( int ) in - > args . args [ 0 ] . value . value . data . charVal ) ) ;
break ;
}
2025-12-13 09:50:49 +11:00
case TOSTRING : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 2 " , in , currentInstruction ) ;
}
GroundValue * value = & in - > args . args [ 0 ] . value . value ;
2026-01-24 16:36:37 +11:00
char buf [ 256 ] ;
2025-12-13 09:50:49 +11:00
switch ( value - > type ) {
case INT : {
2026-03-17 18:45:16 +11:00
snprintf ( buf , sizeof ( buf ) , " % " PRId64 , value - > data . intVal ) ;
2025-12-13 09:50:49 +11:00
break ;
}
case DOUBLE : {
2026-03-17 18:45:16 +11:00
snprintf ( buf , sizeof ( buf ) , " %f " , value - > data . doubleVal ) ;
2025-12-13 09:50:49 +11:00
break ;
}
case STRING : {
2026-01-24 16:36:37 +11:00
snprintf ( buf , sizeof ( buf ) , " %s " , value - > data . stringVal ) ;
2025-12-13 09:50:49 +11:00
break ;
}
case CHAR : {
buf [ 0 ] = value - > data . charVal ;
buf [ 1 ] = ' \0 ' ;
break ;
}
case BOOL : {
if ( value - > data . boolVal ) {
2026-01-24 16:36:37 +11:00
snprintf ( buf , sizeof ( buf ) , " true " ) ;
2025-12-13 09:50:49 +11:00
} else {
2026-01-24 16:36:37 +11:00
snprintf ( buf , sizeof ( buf ) , " false " ) ;
2025-12-13 09:50:49 +11:00
}
break ;
}
case LIST : {
2026-01-24 16:36:37 +11:00
snprintf ( buf , sizeof ( buf ) , " <list> " ) ;
2025-12-13 09:50:49 +11:00
break ;
}
case FUNCTION : {
2026-01-24 16:36:37 +11:00
snprintf ( buf , sizeof ( buf ) , " <function> " ) ;
2025-12-13 09:50:49 +11:00
break ;
}
case CUSTOM : {
2026-01-24 16:36:37 +11:00
snprintf ( buf , sizeof ( buf ) , " <custom> " ) ;
2025-12-13 09:50:49 +11:00
break ;
}
case NONE :
default : {
2026-01-24 16:36:37 +11:00
snprintf ( buf , sizeof ( buf ) , " <default> " ) ;
2025-12-13 09:50:49 +11:00
break ;
}
}
2026-01-24 16:36:37 +11:00
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createStringGroundValue ( buf ) ) ;
2025-12-13 09:50:49 +11:00
break ;
}
2025-11-28 09:23:43 +11:00
/*
* MATHS
* These instructions allow running mathematical operations on values .
* Instructions :
* add , subtract , multiply , divide
*/
2025-11-23 21:23:04 +11:00
case ADD : {
if ( in - > args . length < 3 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 3 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 2 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 2 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 3 " , in , currentInstruction ) ;
}
GroundValue * left = & in - > args . args [ 0 ] . value . value ;
GroundValue * right = & in - > args . args [ 1 ] . value . value ;
if ( left - > type = = STRING ) {
if ( right - > type ! = STRING ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a String for arg 2 " , in , currentInstruction ) ;
}
2025-11-24 10:15:53 +11:00
char * newString = malloc ( strlen ( left - > data . stringVal ) + strlen ( right - > data . stringVal ) ) ;
2025-11-23 21:23:04 +11:00
strcpy ( newString , left - > data . stringVal ) ;
strcat ( newString , right - > data . stringVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createStringGroundValue ( newString ) ) ;
}
else if ( left - > type = = INT | | left - > type = = DOUBLE ) {
if ( right - > type ! = INT & & right - > type ! = DOUBLE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int or Double for arg 2 " , in , currentInstruction ) ;
}
if ( left - > type = = DOUBLE | | right - > type = = DOUBLE ) {
double result = 0 ;
if ( left - > type = = INT ) {
result + = left - > data . intVal ;
} else if ( left - > type = = DOUBLE ) {
result + = left - > data . doubleVal ;
}
if ( right - > type = = INT ) {
result + = right - > data . intVal ;
2025-11-24 10:15:53 +11:00
} else if ( right - > type = = DOUBLE ) {
2025-11-23 21:23:04 +11:00
result + = right - > data . doubleVal ;
}
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createDoubleGroundValue ( result ) ) ;
} else {
int64_t result = left - > data . intVal + right - > data . intVal ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createIntGroundValue ( result ) ) ;
}
} else {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int, Double, or String for arg 1 " , in , currentInstruction ) ;
2025-11-24 11:37:16 +11:00
}
break ;
}
case SUBTRACT : {
if ( in - > args . length < 3 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 3 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 2 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 2 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 3 " , in , currentInstruction ) ;
}
GroundValue * left = & in - > args . args [ 0 ] . value . value ;
GroundValue * right = & in - > args . args [ 1 ] . value . value ;
if ( left - > type = = INT | | left - > type = = DOUBLE ) {
if ( right - > type ! = INT & & right - > type ! = DOUBLE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int or Double for arg 2 " , in , currentInstruction ) ;
}
if ( left - > type = = DOUBLE | | right - > type = = DOUBLE ) {
double result = 0 ;
if ( left - > type = = INT ) {
2025-12-02 09:00:21 +11:00
result = left - > data . intVal ;
2025-11-24 11:37:16 +11:00
} else if ( left - > type = = DOUBLE ) {
2025-12-02 09:00:21 +11:00
result = left - > data . doubleVal ;
2025-11-24 11:37:16 +11:00
}
if ( right - > type = = INT ) {
result + = right - > data . intVal ;
} else if ( right - > type = = DOUBLE ) {
result + = right - > data . doubleVal ;
}
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createDoubleGroundValue ( result ) ) ;
} else {
int64_t result = left - > data . intVal - right - > data . intVal ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createIntGroundValue ( result ) ) ;
}
} else {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int or Double for arg 1 " , in , currentInstruction ) ;
}
break ;
}
case MULTIPLY : {
if ( in - > args . length < 3 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 3 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 2 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 2 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 3 " , in , currentInstruction ) ;
}
GroundValue * left = & in - > args . args [ 0 ] . value . value ;
GroundValue * right = & in - > args . args [ 1 ] . value . value ;
if ( left - > type = = INT | | left - > type = = DOUBLE ) {
if ( right - > type ! = INT & & right - > type ! = DOUBLE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int or Double for arg 2 " , in , currentInstruction ) ;
}
if ( left - > type = = DOUBLE | | right - > type = = DOUBLE ) {
2025-12-02 09:00:21 +11:00
double result = 1.0 ;
2025-11-24 11:37:16 +11:00
if ( left - > type = = INT ) {
result * = left - > data . intVal ;
} else if ( left - > type = = DOUBLE ) {
result + = left - > data . doubleVal ;
}
if ( right - > type = = INT ) {
result * = right - > data . intVal ;
} else if ( right - > type = = DOUBLE ) {
result + = right - > data . doubleVal ;
}
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createDoubleGroundValue ( result ) ) ;
} else {
int64_t result = left - > data . intVal * right - > data . intVal ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createIntGroundValue ( result ) ) ;
}
} else {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int or Double for arg 1 " , in , currentInstruction ) ;
}
break ;
}
case DIVIDE : {
if ( in - > args . length < 3 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 3 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 2 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 2 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 3 " , in , currentInstruction ) ;
}
GroundValue * left = & in - > args . args [ 0 ] . value . value ;
GroundValue * right = & in - > args . args [ 1 ] . value . value ;
if ( left - > type = = INT | | left - > type = = DOUBLE ) {
if ( right - > type ! = INT & & right - > type ! = DOUBLE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int or Double for arg 2 " , in , currentInstruction ) ;
}
2025-11-28 09:23:43 +11:00
if ( right - > type = = INT & & right - > data . intVal = = 0 ) {
2025-12-02 09:00:21 +11:00
runtimeError ( MATH_ERROR , " Division by zero " , in , currentInstruction ) ;
2025-11-28 09:23:43 +11:00
}
2025-11-24 11:37:16 +11:00
if ( left - > type = = DOUBLE | | right - > type = = DOUBLE ) {
double result = 0 ;
if ( left - > type = = INT ) {
2025-12-02 09:00:21 +11:00
result = left - > data . intVal ;
2025-11-24 11:37:16 +11:00
} else if ( left - > type = = DOUBLE ) {
2025-12-02 09:00:21 +11:00
result = left - > data . doubleVal ;
2025-11-24 11:37:16 +11:00
}
if ( right - > type = = INT ) {
result / = right - > data . intVal ;
} else if ( right - > type = = DOUBLE ) {
result / = right - > data . doubleVal ;
}
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createDoubleGroundValue ( result ) ) ;
} else {
int64_t result = left - > data . intVal / right - > data . intVal ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createIntGroundValue ( result ) ) ;
}
} else {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int or Double for arg 1 " , in , currentInstruction ) ;
2025-11-23 21:23:04 +11:00
}
break ;
}
2026-03-25 21:04:44 +11:00
case OR : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting at least 2 args " , in , currentInstruction ) ;
}
for ( size_t i = 0 ; i < in - > args . length - 1 ; i + + ) {
if ( in - > args . args [ i ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a value for all args except last " , in , currentInstruction ) ;
}
if ( in - > args . args [ i ] . value . value . type ! = INT ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an int for all args except last " , in , currentInstruction ) ;
}
}
if ( in - > args . args [ in - > args . length - 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a direct reference for last argument " , in , currentInstruction ) ;
}
int64_t result = 0 ;
for ( size_t i = 0 ; i < in - > args . length - 1 ; i + + ) {
result | = in - > args . args [ i ] . value . value . data . intVal ;
}
addVariable ( scope - > variables , in - > args . args [ in - > args . length - 1 ] . value . refName , createIntGroundValue ( result ) ) ;
break ;
}
2025-12-01 12:28:15 +11:00
/*
* STRING OPERATIONS
* Allows easier manipulation of strings .
* Instructions :
* getstrcharat , getstrsize
*/
case GETSTRCHARAT : {
if ( in - > args . length < 3 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 3 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE & & in - > args . args [ 0 ] . value . value . type ! = STRING ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a String for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = VALUE & & in - > args . args [ 1 ] . value . value . type ! = INT ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int for arg 2 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 2 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 3 " , in , currentInstruction ) ;
}
char * str = in - > args . args [ 0 ] . value . value . data . stringVal ;
2026-01-17 12:21:43 +11:00
size_t idx = in - > args . args [ 1 ] . value . value . data . intVal ;
2025-12-01 12:28:15 +11:00
if ( idx < strlen ( str ) ) {
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createCharGroundValue ( str [ idx ] ) ) ;
} else {
runtimeError ( STRING_ERROR , " Out of bounds index " , in , currentInstruction ) ;
}
break ;
}
case GETSTRSIZE : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE & & in - > args . args [ 0 ] . value . value . type ! = STRING ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a String for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 2 " , in , currentInstruction ) ;
}
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createIntGroundValue ( strlen ( in - > args . args [ 0 ] . value . value . data . stringVal ) ) ) ;
break ;
}
2025-11-28 09:23:43 +11:00
/*
* COMPARISONS
* Allows comparing values .
* Instructions :
* equal , inequal , not , greater , lesser
*/
2025-11-24 10:15:53 +11:00
case EQUAL : {
if ( in - > args . length < 3 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 3 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 2 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 2 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 3 " , in , currentInstruction ) ;
}
GroundValue * left = & in - > args . args [ 0 ] . value . value ;
GroundValue * right = & in - > args . args [ 1 ] . value . value ;
if ( left - > type ! = right - > type ) {
2025-11-24 13:19:37 +11:00
if ( left - > type = = INT & & right - > type = = DOUBLE ) {
bool cond = ( left - > data . intVal = = right - > data . doubleVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
} else if ( left - > type = = DOUBLE & & right - > type = = INT ) {
bool cond = ( left - > data . doubleVal = = right - > data . intVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
} else {
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( false ) ) ;
}
2025-11-24 10:15:53 +11:00
} else {
switch ( left - > type ) {
case INT : {
bool cond = ( left - > data . intVal = = right - > data . intVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
break ;
}
case DOUBLE : {
bool cond = ( left - > data . doubleVal = = right - > data . doubleVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
break ;
}
case STRING : {
bool cond = ( strcmp ( left - > data . stringVal , right - > data . stringVal ) = = 0 ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
break ;
}
case CHAR : {
bool cond = ( left - > data . charVal = = right - > data . charVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
break ;
}
case BOOL : {
bool cond = ( left - > data . boolVal = = right - > data . boolVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
break ;
}
default : {
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( false ) ) ;
break ;
}
}
}
break ;
}
2025-11-24 13:19:37 +11:00
case INEQUAL : {
if ( in - > args . length < 3 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 3 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 2 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 2 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 3 " , in , currentInstruction ) ;
}
GroundValue * left = & in - > args . args [ 0 ] . value . value ;
GroundValue * right = & in - > args . args [ 1 ] . value . value ;
if ( left - > type ! = right - > type ) {
if ( left - > type = = INT & & right - > type = = DOUBLE ) {
bool cond = ( left - > data . intVal ! = right - > data . doubleVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
} else if ( left - > type = = DOUBLE & & right - > type = = INT ) {
bool cond = ( left - > data . doubleVal ! = right - > data . intVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
} else {
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( false ) ) ;
}
} else {
switch ( left - > type ) {
case INT : {
bool cond = ( left - > data . intVal ! = right - > data . intVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
break ;
}
case DOUBLE : {
bool cond = ( left - > data . doubleVal ! = right - > data . doubleVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
break ;
}
case STRING : {
bool cond = ( strcmp ( left - > data . stringVal , right - > data . stringVal ) ! = 0 ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
break ;
}
case CHAR : {
bool cond = ( left - > data . charVal ! = right - > data . charVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
break ;
}
case BOOL : {
bool cond = ( left - > data . boolVal ! = right - > data . boolVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
break ;
}
default : {
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( true ) ) ;
break ;
}
}
}
break ;
}
2025-11-28 09:23:43 +11:00
case NOT : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE & & in - > args . args [ 0 ] . value . value . type ! = BOOL ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Bool for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 2 " , in , currentInstruction ) ;
}
2025-12-02 09:00:21 +11:00
bool condition = ! in - > args . args [ 0 ] . value . value . data . boolVal ;
2025-11-28 09:23:43 +11:00
addVariable ( scope - > variables , in - > args . args [ 1 ] . value . refName , createBoolGroundValue ( condition ) ) ;
2025-12-15 11:34:56 +11:00
break ;
2025-11-28 09:23:43 +11:00
}
2025-11-24 13:19:37 +11:00
case GREATER : {
if ( in - > args . length < 3 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 3 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 2 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 2 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 3 " , in , currentInstruction ) ;
}
GroundValue * left = & in - > args . args [ 0 ] . value . value ;
GroundValue * right = & in - > args . args [ 1 ] . value . value ;
if ( left - > type ! = INT & & left - > type ! = DOUBLE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int or Double for arg 1 " , in , currentInstruction ) ;
} else if ( right - > type ! = INT & & right - > type ! = DOUBLE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int or Double for arg 2 " , in , currentInstruction ) ;
}
if ( left - > type ! = right - > type ) {
if ( left - > type = = INT & & right - > type = = DOUBLE ) {
bool cond = ( left - > data . intVal > right - > data . doubleVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
} else if ( left - > type = = DOUBLE & & right - > type = = INT ) {
bool cond = ( left - > data . doubleVal > right - > data . intVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
} else {
runtimeError ( FIXME , " Uncaught invalid type " , in , currentInstruction ) ;
}
} else {
switch ( left - > type ) {
case INT : {
bool cond = ( left - > data . intVal > right - > data . intVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
break ;
}
case DOUBLE : {
bool cond = ( left - > data . doubleVal > right - > data . doubleVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
break ;
}
default : {
runtimeError ( FIXME , " Uncaught invalid type " , in , currentInstruction ) ;
break ;
}
}
}
break ;
}
case LESSER : {
if ( in - > args . length < 3 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 3 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 2 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 2 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 3 " , in , currentInstruction ) ;
}
GroundValue * left = & in - > args . args [ 0 ] . value . value ;
GroundValue * right = & in - > args . args [ 1 ] . value . value ;
if ( left - > type ! = INT & & left - > type ! = DOUBLE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int or Double for arg 1 " , in , currentInstruction ) ;
} else if ( right - > type ! = INT & & right - > type ! = DOUBLE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Int or Double for arg 2 " , in , currentInstruction ) ;
}
if ( left - > type ! = right - > type ) {
if ( left - > type = = INT & & right - > type = = DOUBLE ) {
bool cond = ( left - > data . intVal < right - > data . doubleVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
} else if ( left - > type = = DOUBLE & & right - > type = = INT ) {
bool cond = ( left - > data . doubleVal < right - > data . intVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
} else {
runtimeError ( FIXME , " Uncaught invalid type " , in , currentInstruction ) ;
}
} else {
switch ( left - > type ) {
case INT : {
bool cond = ( left - > data . intVal < right - > data . intVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
break ;
}
case DOUBLE : {
bool cond = ( left - > data . doubleVal < right - > data . doubleVal ) ;
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , createBoolGroundValue ( cond ) ) ;
break ;
}
default : {
runtimeError ( FIXME , " Uncaught invalid type " , in , currentInstruction ) ;
break ;
}
}
}
break ;
}
2025-12-06 11:50:42 +11:00
/*
* FUNCTIONS
* Allows execution of functions in Ground .
* Instructions :
* fun , return , endfun , pusharg , call
*/
case RETURN : {
2025-12-06 14:35:13 +11:00
if ( in - > args . length < 1 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 1 arg " , in , currentInstruction ) ;
}
if ( in - > args . length > 1 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 1 arg " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 1 " , in , currentInstruction ) ;
}
return in - > args . args [ 0 ] . value . value ;
2025-12-06 11:50:42 +11:00
}
case CALL : {
if ( in - > args . length < 2 ) {
2025-12-11 13:07:37 +11:00
runtimeError ( TOO_FEW_ARGS , " Expecting 2 or more args " , in , currentInstruction ) ;
2025-12-06 11:50:42 +11:00
}
if ( in - > args . args [ 0 ] . type ! = FNREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a FunctionRef for arg 1 " , in , currentInstruction ) ;
}
2025-12-11 13:07:37 +11:00
if ( in - > args . args [ in - > args . length - 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef as the last arg " , in , currentInstruction ) ;
2025-12-06 11:50:42 +11:00
}
GroundVariable * var = findVariable ( * scope - > variables , in - > args . args [ 0 ] . value . refName ) ;
if ( var = = NULL ) {
runtimeError ( UNKNOWN_VARIABLE , " Function not found " , in , currentInstruction ) ;
}
GroundValue * value = & var - > value ;
if ( value - > type ! = FUNCTION ) {
runtimeError ( UNKNOWN_VARIABLE , " Provided reference does not reference a function " , in , currentInstruction ) ;
}
GroundFunction * function = value - > data . fnVal ;
2025-12-11 13:07:37 +11:00
if ( function - > argSize < in - > args . length - 2 ) {
runtimeError ( TOO_FEW_ARGS , " Incorrect amount of arguments provided for function " , in , currentInstruction ) ;
}
if ( function - > argSize > in - > args . length - 2 ) {
runtimeError ( TOO_MANY_ARGS , " Incorrect amount of arguments provided for function " , in , currentInstruction ) ;
}
2026-01-02 20:29:27 +11:00
if ( function - > isNative ) {
List argsList = createList ( ) ;
for ( size_t i = 0 ; i < function - > argSize ; i + + ) {
if ( in - > args . args [ i + 1 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value " , in , currentInstruction ) ;
}
2026-03-14 13:52:42 +11:00
if ( function - > nativeFn ) {
2026-04-11 19:52:24 +10:00
if ( function - > args [ i ] . type ! = ANY & & in - > args . args [ i + 1 ] . value . value . type ! = function - > args [ i ] . type ) {
2026-03-14 13:52:42 +11:00
runtimeError ( ARG_TYPE_MISMATCH , " Mismatched function argument types " , in , currentInstruction ) ;
}
} else {
2026-04-11 20:41:30 +10:00
if ( function - > args [ i ] . type ! = ANY & & ! checkFnTypes ( & in - > args . args [ i + 1 ] . value . value , & function - > args [ i ] ) ) {
2026-03-14 13:52:42 +11:00
runtimeError ( ARG_TYPE_MISMATCH , " Mismatched function argument types " , in , currentInstruction ) ;
}
2026-01-02 20:29:27 +11:00
}
appendToList ( & argsList , in - > args . args [ i + 1 ] . value . value ) ;
}
if ( function - > nativeFn ) {
GroundValue returnValue = function - > nativeFn ( scope , argsList ) ;
2026-01-18 20:49:50 +11:00
if ( returnValue . type = = ERROR ) {
2026-01-18 21:37:46 +11:00
returnValue . data . errorVal . line = currentInstruction ;
returnValue . data . errorVal . hasLine = true ;
returnValue . data . errorVal . where = in ;
2026-01-18 20:49:50 +11:00
return returnValue ;
}
2026-04-11 19:33:58 +10:00
if ( returnValue . type ! = ANY & & returnValue . type ! = function - > returnType ) {
2026-01-02 20:29:27 +11:00
runtimeError ( RETURN_TYPE_MISMATCH , " Unexpected return value type from native function " , in , currentInstruction ) ;
}
addVariable ( scope - > variables , in - > args . args [ in - > args . length - 1 ] . value . refName , returnValue ) ;
}
free ( argsList . values ) ;
} else {
2026-03-02 10:09:10 +11:00
GroundScope newScope = copyGroundScope ( & function - > closure ) ;
2026-01-02 20:29:27 +11:00
for ( size_t i = 0 ; i < function - > argSize ; i + + ) {
2026-04-11 19:26:55 +10:00
if ( in - > args . args [ i + 1 ] . type ! = VALUE ) {
2026-01-02 20:29:27 +11:00
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value " , in , currentInstruction ) ;
}
2026-04-11 20:41:30 +10:00
if ( function - > args [ i ] . type ! = ANY & & ! checkFnTypes ( & in - > args . args [ i + 1 ] . value . value , & function - > args [ i ] ) ) {
2026-01-02 20:29:27 +11:00
runtimeError ( ARG_TYPE_MISMATCH , " Mismatched function argument types " , in , currentInstruction ) ;
}
addVariable ( newScope . variables , function - > args [ i ] . name , in - > args . args [ i + 1 ] . value . value ) ;
2025-12-11 13:07:37 +11:00
}
2026-01-02 20:29:27 +11:00
size_t currentCurrentInstruction = currentInstruction ;
currentInstruction = function - > startLine ;
GroundValue returnValue = interpretGroundProgram ( & function - > program , & newScope ) ;
2026-01-18 20:49:50 +11:00
if ( returnValue . type = = ERROR ) {
return returnValue ;
}
2026-04-11 19:33:58 +10:00
if ( function - > returnType ! = ANY & & returnValue . type ! = function - > returnType ) {
2026-01-02 20:29:27 +11:00
runtimeError ( RETURN_TYPE_MISMATCH , " Unexpected return value type from function " , in , currentInstruction ) ;
2025-12-11 13:07:37 +11:00
}
2026-04-11 18:53:17 +10:00
GroundVariable * var , * tmp ;
HASH_ITER ( hh , * newScope . variables , var , tmp ) {
deleteVariable ( newScope . variables , var ) ;
}
2026-01-02 20:29:27 +11:00
addVariable ( scope - > variables , in - > args . args [ in - > args . length - 1 ] . value . refName , returnValue ) ;
currentInstruction = currentCurrentInstruction ;
2025-12-11 13:07:37 +11:00
}
2026-01-02 20:29:27 +11:00
break ;
}
2026-04-11 10:42:53 +10:00
case CALLMETHOD : {
if ( in - > args . length < 3 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 3 or more args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef to an object for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = FNREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a FunctionRef for arg 2 " , in , currentInstruction ) ;
}
if ( in - > args . args [ in - > args . length - 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef as the last arg " , in , currentInstruction ) ;
}
GroundVariable * objvar = findVariable ( * scope - > variables , in - > args . args [ 0 ] . value . refName ) ;
if ( objvar = = NULL ) {
runtimeError ( UNKNOWN_VARIABLE , " Object not found " , in , currentInstruction ) ;
}
GroundValue * obj = & objvar - > value ;
if ( obj - > type ! = CUSTOM ) {
runtimeError ( UNKNOWN_VARIABLE , " Provided reference does not reference an object " , in , currentInstruction ) ;
}
GroundObjectField * fnvar = findField ( * obj - > data . customVal , in - > args . args [ 1 ] . value . refName ) ;
if ( fnvar = = NULL ) {
runtimeError ( UNKNOWN_VARIABLE , " Method not found inside specified object " , in , currentInstruction ) ;
}
if ( fnvar - > value . type ! = FUNCTION ) {
runtimeError ( UNKNOWN_VARIABLE , " Provided reference does not reference a method " , in , currentInstruction ) ;
}
GroundFunction * function = fnvar - > value . data . fnVal ;
if ( function - > argSize < in - > args . length - 3 ) {
runtimeError ( TOO_FEW_ARGS , " Incorrect amount of arguments provided for function " , in , currentInstruction ) ;
}
if ( function - > argSize > in - > args . length - 3 ) {
runtimeError ( TOO_MANY_ARGS , " Incorrect amount of arguments provided for function " , in , currentInstruction ) ;
}
if ( function - > isNative ) {
List argsList = createList ( ) ;
for ( size_t i = 0 ; i < function - > argSize ; i + + ) {
if ( in - > args . args [ i + 2 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value " , in , currentInstruction ) ;
}
if ( function - > nativeFn ) {
2026-04-11 19:52:24 +10:00
if ( function - > args [ i ] . type ! = ANY & & in - > args . args [ i + 2 ] . value . value . type ! = function - > args [ i ] . type ) {
2026-04-11 10:42:53 +10:00
runtimeError ( ARG_TYPE_MISMATCH , " Mismatched function argument types " , in , currentInstruction ) ;
}
} else {
2026-04-11 20:41:30 +10:00
if ( function - > args [ i ] . type ! = ANY & & ! checkFnTypes ( & in - > args . args [ i + 2 ] . value . value , & function - > args [ i ] ) ) {
2026-04-11 10:42:53 +10:00
runtimeError ( ARG_TYPE_MISMATCH , " Mismatched function argument types " , in , currentInstruction ) ;
}
}
appendToList ( & argsList , in - > args . args [ i + 2 ] . value . value ) ;
}
GroundScope newscope = {
. labels = NULL ,
. variables = malloc ( sizeof ( GroundVariable * ) ) ,
. isMainScope = false
} ;
if ( newscope . variables = = NULL ) {
runtimeError ( FIXME , " Failed to allocate memory " , in , currentInstruction ) ;
}
* newscope . variables = NULL ;
GroundObjectField * tmp ;
GroundObjectField * el ;
HASH_ITER ( hh , obj - > data . customVal - > fields , el , tmp ) {
addVariable ( newscope . variables , el - > id , el - > value ) ;
}
if ( function - > nativeFn ) {
GroundValue returnValue = function - > nativeFn ( & newscope , argsList ) ;
if ( returnValue . type = = ERROR ) {
returnValue . data . errorVal . line = currentInstruction ;
returnValue . data . errorVal . hasLine = true ;
returnValue . data . errorVal . where = in ;
if ( scope - > isMainScope ) {
throwError ( & returnValue . data . errorVal ) ;
}
return returnValue ;
}
2026-04-11 19:33:58 +10:00
if ( function - > returnType ! = ANY & & returnValue . type ! = function - > returnType ) {
runtimeError ( RETURN_TYPE_MISMATCH , " Unexpected return value type from native function " , in , currentInstruction ) ;
2026-04-11 10:42:53 +10:00
}
addVariable ( scope - > variables , in - > args . args [ in - > args . length - 1 ] . value . refName , returnValue ) ;
// Copy back modified variables
HASH_ITER ( hh , obj - > data . customVal - > fields , el , tmp ) {
el - > value = findVariable ( * newscope . variables , el - > id ) - > value ;
}
}
free ( argsList . values ) ;
} else {
GroundScope newScope = copyGroundScope ( & function - > closure ) ;
for ( size_t i = 0 ; i < function - > argSize ; i + + ) {
if ( in - > args . args [ i + 2 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value " , in , currentInstruction ) ;
}
//if (in->args.args[i + 1].value.value.type != function->args[i].type) {
2026-04-11 20:41:30 +10:00
if ( function - > args [ i ] . type ! = ANY & & ! checkFnTypes ( & in - > args . args [ i + 2 ] . value . value , & function - > args [ i ] ) ) {
2026-04-11 10:42:53 +10:00
runtimeError ( ARG_TYPE_MISMATCH , " Mismatched function argument types " , in , currentInstruction ) ;
}
addVariable ( newScope . variables , function - > args [ i ] . name , in - > args . args [ i + 2 ] . value . value ) ;
}
// Add the object to the scope
addVariable ( newScope . variables , " self " , * obj ) ;
size_t currentCurrentInstruction = currentInstruction ;
currentInstruction = function - > startLine ;
GroundValue returnValue = interpretGroundProgram ( & function - > program , & newScope ) ;
if ( returnValue . type = = ERROR ) {
return returnValue ;
}
2026-04-11 19:33:58 +10:00
if ( function - > returnType ! = ANY & & returnValue . type ! = function - > returnType ) {
2026-04-11 10:42:53 +10:00
runtimeError ( RETURN_TYPE_MISMATCH , " Unexpected return value type from function " , in , currentInstruction ) ;
}
addVariable ( scope - > variables , in - > args . args [ in - > args . length - 1 ] . value . refName , returnValue ) ;
currentInstruction = currentCurrentInstruction ;
// Copy out the object
GroundVariable * objvar = findVariable ( * newScope . variables , " self " ) ;
if ( objvar = = NULL ) {
runtimeError ( UNKNOWN_VARIABLE , " self not found in scope, did you drop the object? " , in , currentInstruction ) ;
}
addVariable ( scope - > variables , in - > args . args [ 0 ] . value . refName , objvar - > value ) ;
}
break ;
}
2026-01-02 20:29:27 +11:00
case USE : {
if ( in - > args . length < 1 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 1 arg " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE | | in - > args . args [ 0 ] . value . value . type ! = STRING ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a String for arg 1 " , in , currentInstruction ) ;
}
char * libName = in - > args . args [ 0 ] . value . value . data . stringVal ;
char path [ 1024 ] ;
int found = 0 ;
char * envPath = getenv ( " GROUND_LIBS " ) ;
if ( envPath ) {
2026-03-17 18:35:37 +11:00
snprintf ( path , sizeof ( path ) , " %s/%s.grnd " , envPath , libName ) ;
2026-01-02 20:29:27 +11:00
if ( access ( path , F_OK ) = = 0 ) found = 1 ;
}
if ( ! found ) {
2026-03-17 18:35:37 +11:00
snprintf ( path , sizeof ( path ) , " /usr/lib/ground/%s.grnd " , libName ) ;
2026-01-02 20:29:27 +11:00
if ( access ( path , F_OK ) = = 0 ) found = 1 ;
}
if ( ! found ) {
2026-03-17 18:35:37 +11:00
snprintf ( path , sizeof ( path ) , " %s.grnd " , libName ) ;
2026-01-02 20:29:27 +11:00
if ( access ( path , F_OK ) = = 0 ) found = 1 ;
}
if ( ! found ) {
char errorMsg [ 1100 ] ;
snprintf ( errorMsg , sizeof ( errorMsg ) , " Could not find library: %s " , libName ) ;
runtimeError ( FIXME , errorMsg , in , currentInstruction ) ;
}
2026-03-17 18:35:37 +11:00
char * fileContents = getFileContents ( path ) ;
GroundProgram program = parseFile ( fileContents ) ;
free ( fileContents ) ;
2026-03-17 19:45:16 +11:00
GroundScope newScope = {
. variables = scope - > variables ,
. labels = malloc ( sizeof ( GroundLabel * ) ) ,
. isMainScope = false
} ;
* newScope . labels = NULL ;
2026-03-17 18:35:37 +11:00
2026-03-17 19:45:16 +11:00
int ci = currentInstruction ;
2026-01-02 20:29:27 +11:00
2026-03-17 18:35:37 +11:00
interpretGroundProgram ( & program , & newScope ) ;
2026-03-17 19:45:16 +11:00
currentInstruction = ci ;
2026-01-02 20:29:27 +11:00
break ;
}
case EXTERN : {
2026-01-25 14:37:50 +11:00
# ifdef _WIN32
runtimeError ( FIXME , " No Windows support for extern, sucker " , in , currentInstruction ) ;
# endif
# ifndef _WIN32
2026-01-02 20:29:27 +11:00
if ( in - > args . length < 1 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 1 arg " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE | | in - > args . args [ 0 ] . value . value . type ! = STRING ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a String for arg 1 " , in , currentInstruction ) ;
2025-12-06 11:50:42 +11:00
}
2026-01-02 20:29:27 +11:00
char * libName = in - > args . args [ 0 ] . value . value . data . stringVal ;
char path [ 1024 ] ;
int found = 0 ;
2026-01-02 21:04:42 +00:00
// care about mac and windows later
char * extension = " so " ;
2026-01-02 20:29:27 +11:00
char * envPath = getenv ( " GROUND_LIBS " ) ;
if ( envPath ) {
2026-01-02 21:04:42 +00:00
snprintf ( path , sizeof ( path ) , " %s/%s.%s " , envPath , libName , extension ) ;
2026-01-02 20:29:27 +11:00
if ( access ( path , F_OK ) = = 0 ) found = 1 ;
}
if ( ! found ) {
2026-01-02 21:04:42 +00:00
snprintf ( path , sizeof ( path ) , " /usr/lib/ground/%s.%s " , libName , extension ) ;
2026-01-02 20:29:27 +11:00
if ( access ( path , F_OK ) = = 0 ) found = 1 ;
}
if ( ! found ) {
2026-01-02 21:04:42 +00:00
snprintf ( path , sizeof ( path ) , " ./%s.%s " , libName , extension ) ;
2026-01-02 20:29:27 +11:00
if ( access ( path , F_OK ) = = 0 ) found = 1 ;
}
if ( ! found ) {
char errorMsg [ 1100 ] ;
2026-01-02 21:04:42 +00:00
snprintf ( errorMsg , sizeof ( errorMsg ) , " Could not find external library: %s, in path: %s " , libName , path ) ;
2026-01-02 20:29:27 +11:00
runtimeError ( FIXME , errorMsg , in , currentInstruction ) ;
}
void * handle = dlopen ( path , RTLD_LAZY ) ;
if ( ! handle ) {
runtimeError ( FIXME , dlerror ( ) , in , currentInstruction ) ;
}
typedef void ( * GroundInitFn ) ( GroundScope * ) ;
GroundInitFn initFn = ( GroundInitFn ) dlsym ( handle , " ground_init " ) ;
if ( ! initFn ) {
runtimeError ( FIXME , " Could not find 'ground_init' symbol in library " , in , currentInstruction ) ;
}
initFn ( scope ) ;
2026-01-25 14:37:50 +11:00
# endif
2025-12-06 14:35:13 +11:00
break ;
2025-12-06 11:50:42 +11:00
}
2025-12-22 14:05:40 +11:00
2026-01-17 19:58:21 +11:00
case INIT : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = TYPEREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a TypeRef for arg 2 " , in , currentInstruction ) ;
}
GroundValue gv ;
2026-01-17 20:30:14 +11:00
switch ( stringToValueType ( in - > args . args [ 1 ] . value . refName ) ) {
2026-01-17 19:58:21 +11:00
case INT : {
gv = createIntGroundValue ( 0 ) ;
break ;
}
case DOUBLE : {
gv = createDoubleGroundValue ( 0 ) ;
break ;
}
case STRING : {
gv = createStringGroundValue ( " " ) ;
break ;
}
case CHAR : {
gv = createCharGroundValue ( ' \0 ' ) ;
break ;
}
case BOOL : {
gv = createBoolGroundValue ( false ) ;
break ;
}
case LIST : {
gv = createListGroundValue ( createList ( ) ) ;
break ;
}
case FUNCTION : {
2026-01-18 13:44:26 +11:00
GroundFunction * gf = createGroundFunction ( ) ;
gf - > returnType = NONE ;
gv = createFunctionGroundValue ( gf ) ;
2026-01-17 19:58:21 +11:00
break ;
}
case STRUCTVAL : {
gv . type = STRUCTVAL ;
gv . data . structVal = malloc ( sizeof ( GroundStruct ) ) ;
* gv . data . structVal = createStruct ( ) ;
break ;
}
case CUSTOM : {
GroundVariable * var = findVariable ( * scope - > variables , in - > args . args [ 1 ] . value . refName ) ;
if ( var = = NULL ) {
runtimeError ( UNKNOWN_VARIABLE , " Couldn't find the specified type " , in , currentInstruction ) ;
}
if ( var - > value . type ! = STRUCTVAL ) {
runtimeError ( ARG_TYPE_MISMATCH , " TypeRef does not reference a struct " , in , currentInstruction ) ;
}
GroundStruct * gstruct = var - > value . data . structVal ;
gv . type = CUSTOM ;
gv . data . customVal = malloc ( sizeof ( GroundObject ) ) ;
* gv . data . customVal = createObject ( * gstruct ) ;
2026-01-20 19:57:49 +11:00
gv . customType = gstruct ;
2026-01-17 19:58:21 +11:00
break ;
}
case NONE : {
gv . type = NONE ;
break ;
}
default : {
runtimeError ( FIXME , " Reached should-be-impossible state " , in , currentInstruction ) ;
break ;
}
}
2026-01-17 20:04:18 +11:00
addVariable ( scope - > variables , in - > args . args [ 0 ] . value . refName , gv ) ;
2026-01-17 19:58:21 +11:00
break ;
}
2026-01-21 18:41:24 +11:00
/*
* OBJECTS
* Allows manipulation of Ground objects .
*/
// getfield $obj &field &result
case GETFIELD : {
if ( in - > args . length < 3 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 3 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Object for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . value . value . type ! = CUSTOM ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting an Object for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 2 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 2 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 3 " , in , currentInstruction ) ;
}
GroundObjectField * field = findField ( * in - > args . args [ 0 ] . value . value . data . customVal , in - > args . args [ 1 ] . value . refName ) ;
if ( field = = NULL ) {
runtimeError ( UNKNOWN_VARIABLE , " Struct does not contain that field " , in , currentInstruction ) ;
}
addVariable ( scope - > variables , in - > args . args [ 2 ] . value . refName , field - > value ) ;
break ;
}
// setfield &obj &field $value
case SETFIELD : {
if ( in - > args . length < 3 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 3 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 3 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef to an Object for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 2 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 2 ] . type ! = VALUE ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a Value for arg 3 " , in , currentInstruction ) ;
}
GroundVariable * var = findVariable ( * scope - > variables , in - > args . args [ 0 ] . value . refName ) ;
if ( var = = NULL ) {
runtimeError ( UNKNOWN_VARIABLE , " Could not find that object " , in , currentInstruction ) ;
}
if ( var - > value . type ! = CUSTOM ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef to an Object for arg 1 " , in , currentInstruction ) ;
}
GroundObjectField * field = findField ( * var - > value . data . customVal , in - > args . args [ 1 ] . value . refName ) ;
if ( field = = NULL ) {
runtimeError ( UNKNOWN_VARIABLE , " Struct does not contain that field " , in , currentInstruction ) ;
}
if ( field - > value . type ! = in - > args . args [ 2 ] . value . value . type ) {
runtimeError ( ARG_TYPE_MISMATCH , " Field type and provided type do not match " , in , currentInstruction ) ;
}
field - > value = copyGroundValue ( & in - > args . args [ 2 ] . value . value ) ;
break ;
}
2025-12-22 14:05:40 +11:00
case DROP : {
if ( in - > args . length < 1 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 1 arg " , in , currentInstruction ) ;
}
if ( in - > args . length > 1 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 1 arg " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = DIRREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a DirectRef for arg 1 " , in , currentInstruction ) ;
}
GroundVariable * var = findVariable ( * scope - > variables , in - > args . args [ 0 ] . value . refName ) ;
if ( ! var ) {
runtimeError ( UNKNOWN_VARIABLE , NULL , in , currentInstruction ) ;
}
deleteVariable ( scope - > variables , var ) ;
break ;
}
2026-04-14 10:50:27 +10:00
case THROW : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE | | in - > args . args [ 0 ] . value . value . type ! = STRING ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a String value for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = VALUE | | in - > args . args [ 1 ] . value . value . type ! = STRING ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a String value for arg 2 " , in , currentInstruction ) ;
}
return createErrorGroundValue (
createGroundError (
in - > args . args [ 1 ] . value . value . data . stringVal ,
2026-04-15 10:37:14 +10:00
in - > args . args [ 0 ] . value . value . data . stringVal ,
2026-04-14 10:50:27 +10:00
in ,
( size_t * ) & currentInstruction
)
) ;
}
case CATCH : {
if ( in - > args . length < 2 ) {
runtimeError ( TOO_FEW_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . length > 2 ) {
runtimeError ( TOO_MANY_ARGS , " Expecting 2 args " , in , currentInstruction ) ;
}
if ( in - > args . args [ 0 ] . type ! = VALUE | | in - > args . args [ 0 ] . value . value . type ! = STRING ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a String value for arg 1 " , in , currentInstruction ) ;
}
if ( in - > args . args [ 1 ] . type ! = LINEREF ) {
runtimeError ( ARG_TYPE_MISMATCH , " Expecting a LineRef for arg 2 " , in , currentInstruction ) ;
}
GroundLabel * label = findLabel ( * scope - > labels , in - > args . args [ 1 ] . value . refName ) ;
if ( label = = NULL ) {
runtimeError ( UNKNOWN_LABEL , NULL , in , currentInstruction ) ;
}
GroundCatch catch = {
2026-04-15 10:37:14 +10:00
. label = label ,
2026-04-14 10:50:27 +10:00
} ;
2026-04-15 10:37:14 +10:00
snprintf ( catch . id , sizeof ( catch . id ) , " %s " , in - > args . args [ 0 ] . value . value . data . stringVal ) ;
2026-04-14 10:50:27 +10:00
HASH_ADD_STR ( * scope - > catches , id , & catch ) ;
break ;
}
2026-03-19 16:00:46 +11:00
/*
* License for REPL
*/
case LICENSE : {
2026-04-14 10:50:27 +10:00
printf ( " GNU GENERAL PUBLIC LICENSE \n Version 3, 29 June 2007 \n \n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> \n Everyone is permitted to copy and distribute verbatim copies \n of this license document, but changing it is not allowed. \n \n Preamble \n \n The GNU General Public License is a free, copyleft license for \n software and other kinds of works. \n \n The licenses for most software and other practical works are designed \n to take away your freedom to share and change the works. By contrast, \n the GNU General Public License is intended to guarantee your freedom to \n share and change all versions of a program--to make sure it remains free \n software for all its users. We, the Free Software Foundation, use the \n GNU General Public License for most of our software; it applies also to \n any other work released this way by its authors. You can apply it to \n your programs, too. \n \n When we speak of free software, we are referring to freedom, not \n price. Our General Public Licenses are designed to make sure that you \n have the freedom to distribute copies of free software (and charge for \n them if you wish), that you receive source code or can get it if you \n want it, that you can change the software or use pieces of it in new \n free programs, and that you know you can do these things. \n \n To protect your rights, we need to prevent others from denying you \n these rights or asking you to surrender the rights. Therefore, you have \n certain responsibilities if you distribute copies of the software, or if \n you modify it: responsibilities to respect the freedom of others. \n \n For example, if you distribute copies of such a program, whether \n gratis or for a fee, you must pass on to the recipients the same \n freedoms that you received. You must make sure that they, too, receive \n or can get the source code. And you must show them these terms so they \n know their rights. \n \n Developers that use the GNU GPL protect your rights with two steps: \n (1) assert copyright on the software, and (2) offer you this License \n giving you legal permission to copy, distribute and/or modify it. \n \n For the developers' and authors' protection, the GPL clearly explains \n that there is no warranty for this free software. For both users' and \n authors' sake, the GPL requires that modified versions be marked as \n changed, so that their problems will not be attributed erroneously to \n authors of previous versions. \n \n Some devices are designed to deny users access to install or run \n modified versions of the software inside them, although the manufacturer \n can do so. This is fundamentally incompatible with the aim of \n protecting users' freedom to change the software. The systematic \n pattern of such abuse occurs in the area of products for individuals to \n use, which is precisely where it is most unacceptable. Therefore, we \n have designed this version of the GPL to prohibit the practice for those \n products. If such problems arise substantially in other domains, we \n stand ready to extend this provision to those domains in future versions \n of the GPL, as needed to protect the freedom of users. \n \n Finally, every program is threatened constantly by software patents. \n States should not allow patents to restrict development and use of \n software on general-purpose computers, but in those that do, we wish to \n avoid the special danger that patents applied to a free program could \n make it effectively proprietary. To prevent this, the GPL assures that \n patents cannot be used to render the program non-free. \n \n The precise terms and conditions for copying, distribution and \n modification follow. \n \n TERMS AND CONDITIONS \n \n 0. Definitions. \n \n \" This License \" refers to version 3 of the GNU General Public License. \n \n \" Copyright \" also means copyright-like laws that apply to other kinds of \n works, such as semiconductor masks. \n \n \" The Program \" refers to any copyrightable work licensed under this \n License. Each licensee is addressed as \" you
break ;
2026-03-19 16:00:46 +11:00
}
2026-04-14 10:50:27 +10:00
2026-03-19 16:00:46 +11:00
2025-12-13 09:50:49 +11:00
default : {
runtimeError ( FIXME , " Currently unimplemented instruction " , in , currentInstruction ) ;
}
2025-11-23 18:34:30 +11:00
}
2025-11-24 10:15:53 +11:00
freeGroundInstruction ( in ) ;
2025-12-06 14:35:13 +11:00
return createNoneGroundValue ( ) ;
2025-11-23 18:34:30 +11:00
}