Functions are successfully called, read more

Return keyword is not yet implemented.

Functions are untested.

See #163
This commit is contained in:
2025-02-17 14:05:07 +11:00
parent 76d89fe0ad
commit 02dfc996b4
12 changed files with 193 additions and 27 deletions

View File

@@ -1,5 +1,23 @@
fn name(param1: int, param2: float, param3: string, param4) {
print param1;
print param2;
print param3;
print param4;
}
name(42, 3.14, "hello world", -1);
fn name(foobar: bool const) { //URGENT: return values are still needed
print foobar;
} fn output(arg) {
print arg;
}
output(null);
output(true);
output(42);
output(3.1415);
output("woot!");
output([1, 23, 3]);
output(["key":1]);
output(name);

View File

@@ -213,6 +213,16 @@ void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast**
(*astHandle) = tmp; (*astHandle) = tmp;
} }
void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* args) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_FN_INVOKE;
tmp->fnInvoke.function = (*astHandle);
tmp->fnInvoke.args = args;
(*astHandle) = tmp;
}
void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) { void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

View File

@@ -31,6 +31,7 @@ typedef enum Toy_AstType {
TOY_AST_VAR_ACCESS, TOY_AST_VAR_ACCESS,
TOY_AST_FN_DECLARE, TOY_AST_FN_DECLARE,
TOY_AST_FN_INVOKE,
TOY_AST_PASS, TOY_AST_PASS,
TOY_AST_ERROR, TOY_AST_ERROR,
@@ -200,6 +201,12 @@ typedef struct Toy_AstFnDeclare {
Toy_Ast* body; Toy_Ast* body;
} Toy_AstFnDeclare; } Toy_AstFnDeclare;
typedef struct Toy_AstFnInvoke {
Toy_AstType type;
Toy_Ast* function;
Toy_Ast* args;
} Toy_AstFnInvoke;
typedef struct Toy_AstPass { typedef struct Toy_AstPass {
Toy_AstType type; Toy_AstType type;
} Toy_AstPass; } Toy_AstPass;
@@ -233,6 +240,7 @@ union Toy_Ast { //see 'test_ast.c' for bitness tests
Toy_AstVarAssign varAssign; Toy_AstVarAssign varAssign;
Toy_AstVarAccess varAccess; Toy_AstVarAccess varAccess;
Toy_AstFnDeclare fnDeclare; Toy_AstFnDeclare fnDeclare;
Toy_AstFnInvoke fnInvoke;
Toy_AstPass pass; Toy_AstPass pass;
Toy_AstError error; Toy_AstError error;
Toy_AstEnd end; Toy_AstEnd end;
@@ -262,8 +270,8 @@ void Toy_private_emitAstVariableAssignment(Toy_Bucket** bucketHandle, Toy_Ast**
void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* params, Toy_Ast* body); void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* params, Toy_Ast* body);
void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* params);
void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstError(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstError(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstEnd(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstEnd(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);

View File

@@ -9,12 +9,12 @@ typedef enum Toy_FunctionType {
TOY_FUNCTION_NATIVE, TOY_FUNCTION_NATIVE,
} Toy_FunctionType; } Toy_FunctionType;
typedef union Toy_FunctionModule { typedef struct Toy_FunctionModule {
Toy_FunctionType type; Toy_FunctionType type;
Toy_Module module; Toy_Module module;
} Toy_FunctionModule; } Toy_FunctionModule;
typedef union Toy_FunctionNative { typedef struct Toy_FunctionNative {
Toy_FunctionType type; Toy_FunctionType type;
void* native; //TODO: replace with the native function pointer void* native; //TODO: replace with the native function pointer
} Toy_FunctionNative; } Toy_FunctionNative;

View File

@@ -215,6 +215,7 @@ static unsigned int emitParameters(Toy_ModuleCompiler* mb, Toy_Ast* ast) {
//emit to the param index //emit to the param index
EMIT_INT(&mb, param, dataAddr); EMIT_INT(&mb, param, dataAddr);
EMIT_INT(&mb, param, (unsigned int)(TOY_VALUE_AS_STRING(ast->value.value)->name.varType)); //don't forget this bit
//this returns the number of written parameters //this returns the number of written parameters
return 1; return 1;
@@ -1041,6 +1042,32 @@ static unsigned int writeInstructionFnDeclare(Toy_ModuleCompiler** mb, Toy_AstFn
return 0; return 0;
} }
static unsigned int writeInstructionFnInvoke(Toy_ModuleCompiler** mb, Toy_AstFnInvoke ast) {
unsigned int argCount = writeModuleCompilerCode(mb, ast.args);
if (argCount > 255) {
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Invalid function invokation with %d functions arguments (maximum 255)\n" TOY_CC_RESET, (int)argCount);
(*mb)->panic = true;
return 0;
}
unsigned int fnCount = writeModuleCompilerCode(mb, ast.function);
if (fnCount != 1) {
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Invalid function invokation with %d function AST nodes (expected 1)\n" TOY_CC_RESET, (int)fnCount);
(*mb)->panic = true;
return 0;
}
//call the function
EMIT_BYTE(mb, code, TOY_OPCODE_INVOKE);
EMIT_BYTE(mb, code, TOY_VALUE_FUNCTION);
EMIT_BYTE(mb, code, (unsigned char)argCount);
EMIT_BYTE(mb, code, 0); //IDK how many returns
return 0;
}
static unsigned int writeModuleCompilerCode(Toy_ModuleCompiler** mb, Toy_Ast* ast) { static unsigned int writeModuleCompilerCode(Toy_ModuleCompiler** mb, Toy_Ast* ast) {
if (ast == NULL) { if (ast == NULL) {
return 0; return 0;
@@ -1151,6 +1178,10 @@ static unsigned int writeModuleCompilerCode(Toy_ModuleCompiler** mb, Toy_Ast* as
result += writeInstructionFnDeclare(mb, ast->fnDeclare); result += writeInstructionFnDeclare(mb, ast->fnDeclare);
break; break;
case TOY_AST_FN_INVOKE:
result += writeInstructionFnInvoke(mb, ast->fnInvoke);
break;
case TOY_AST_PASS: case TOY_AST_PASS:
//NO-OP //NO-OP
break; break;

View File

@@ -10,6 +10,7 @@ typedef enum Toy_OpcodeType {
TOY_OPCODE_ASSIGN, TOY_OPCODE_ASSIGN,
TOY_OPCODE_ASSIGN_COMPOUND, //assign to a compound's internals TOY_OPCODE_ASSIGN_COMPOUND, //assign to a compound's internals
TOY_OPCODE_ACCESS, TOY_OPCODE_ACCESS,
TOY_OPCODE_INVOKE, //for calling functions
TOY_OPCODE_DUPLICATE, //duplicate the top of the stack TOY_OPCODE_DUPLICATE, //duplicate the top of the stack
TOY_OPCODE_ELIMINATE, //remove the top of the stack TOY_OPCODE_ELIMINATE, //remove the top of the stack

View File

@@ -120,6 +120,7 @@ static Toy_AstFlag group(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast*
static Toy_AstFlag compound(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag compound(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
static Toy_AstFlag aggregate(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag aggregate(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
static Toy_AstFlag unaryPostfix(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag unaryPostfix(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
static Toy_AstFlag invoke(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
//precedence definitions //precedence definitions
static ParsingTuple parsingRulesetTable[] = { static ParsingTuple parsingRulesetTable[] = {
@@ -181,8 +182,8 @@ static ParsingTuple parsingRulesetTable[] = {
{PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_MULTIPLY_ASSIGN, {PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_MULTIPLY_ASSIGN,
{PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_DIVIDE_ASSIGN, {PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_DIVIDE_ASSIGN,
{PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_MODULO_ASSIGN, {PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_MODULO_ASSIGN,
{PREC_CALL,unary,unaryPostfix},// TOY_TOKEN_OPERATOR_INCREMENT, {PREC_UNARY,unary,unaryPostfix},// TOY_TOKEN_OPERATOR_INCREMENT,
{PREC_CALL,unary,unaryPostfix},// TOY_TOKEN_OPERATOR_DECREMENT, {PREC_UNARY,unary,unaryPostfix},// TOY_TOKEN_OPERATOR_DECREMENT,
{PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_ASSIGN, {PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_ASSIGN,
//comparator operators //comparator operators
@@ -194,7 +195,7 @@ static ParsingTuple parsingRulesetTable[] = {
{PREC_COMPARISON,NULL,binary},// TOY_TOKEN_OPERATOR_COMPARE_GREATER_EQUAL, {PREC_COMPARISON,NULL,binary},// TOY_TOKEN_OPERATOR_COMPARE_GREATER_EQUAL,
//structural operators //structural operators
{PREC_GROUP,group,NULL},// TOY_TOKEN_OPERATOR_PAREN_LEFT, {PREC_CALL,group,invoke},// TOY_TOKEN_OPERATOR_PAREN_LEFT,
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_PAREN_RIGHT, {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_PAREN_RIGHT,
{PREC_GROUP,compound,aggregate},// TOY_TOKEN_OPERATOR_BRACKET_LEFT, {PREC_GROUP,compound,aggregate},// TOY_TOKEN_OPERATOR_BRACKET_LEFT,
{PREC_NONE,compound,aggregate},// TOY_TOKEN_OPERATOR_BRACKET_RIGHT, {PREC_NONE,compound,aggregate},// TOY_TOKEN_OPERATOR_BRACKET_RIGHT,
@@ -212,7 +213,7 @@ static ParsingTuple parsingRulesetTable[] = {
{PREC_GROUP,NULL,aggregate},// TOY_TOKEN_OPERATOR_COMMA, // , {PREC_GROUP,NULL,aggregate},// TOY_TOKEN_OPERATOR_COMMA, // ,
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_DOT, // . {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_DOT, // .
{PREC_CALL,NULL,binary},// TOY_TOKEN_OPERATOR_CONCAT, // .. {PREC_UNARY,NULL,binary},// TOY_TOKEN_OPERATOR_CONCAT, // ..
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_REST, // ... {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_REST, // ...
//unused operators //unused operators
@@ -557,7 +558,7 @@ static Toy_AstFlag binary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast
} }
case TOY_TOKEN_OPERATOR_CONCAT: { case TOY_TOKEN_OPERATOR_CONCAT: {
parsePrecedence(bucketHandle, parser, rootHandle, PREC_CALL + 1); parsePrecedence(bucketHandle, parser, rootHandle, PREC_UNARY + 1);
return TOY_AST_FLAG_CONCAT; return TOY_AST_FLAG_CONCAT;
} }
@@ -708,6 +709,31 @@ static Toy_AstFlag unaryPostfix(Toy_Bucket** bucketHandle, Toy_Parser* parser, T
} }
} }
static Toy_AstFlag invoke(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
//infix must advance
advance(parser);
//read in args
Toy_Ast* args = NULL;
unsigned int paramIterations = 0;
while (parser->current.type != TOY_TOKEN_OPERATOR_PAREN_RIGHT && (paramIterations++ == 0 || match(parser, TOY_TOKEN_OPERATOR_COMMA))) {
//get the next arg
Toy_Ast* ast = NULL;
parsePrecedence(bucketHandle, parser, &ast, PREC_GROUP);
//add to the args aggregate (is added backwards, because weird)
Toy_private_emitAstAggregate(bucketHandle, &args, TOY_AST_FLAG_COLLECTION, ast);
}
consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' at the end of argument list");
//finally, emit the call as an Ast
Toy_private_emitAstFunctionInvokation(bucketHandle, rootHandle, args);
return TOY_AST_FLAG_NONE;
}
//grammar rules //grammar rules
static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle, ParsingPrecedence precRule) { static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle, ParsingPrecedence precRule) {
//'step over' the token to parse //'step over' the token to parse
@@ -741,7 +767,7 @@ static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A
return; return;
} }
Toy_Ast* ptr = NULL; Toy_Ast* ptr = (*rootHandle); //NOTE: infix functions will need to be careful not to damage the pre-existing tree, if they can avoid it
Toy_AstFlag flag = infix(bucketHandle, parser, &ptr); Toy_AstFlag flag = infix(bucketHandle, parser, &ptr);
//finished //finished

View File

@@ -139,14 +139,6 @@ void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
return; return;
} }
//constness check
if (Toy_getNameStringVarConstant(key) && value.type == TOY_VALUE_NULL) {
char buffer[key->info.length + 256];
sprintf(buffer, "Can't declare %s as const with value 'null'", key->name.data);
Toy_error(buffer);
return;
}
Toy_insertTable(&scope->table, TOY_VALUE_FROM_STRING(Toy_copyString(key)), value); Toy_insertTable(&scope->table, TOY_VALUE_FROM_STRING(Toy_copyString(key)), value);
} }

View File

@@ -311,7 +311,7 @@ bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) {
} }
case TOY_VALUE_FUNCTION: case TOY_VALUE_FUNCTION:
return false; //URGENT: check this return false; //URGENT: test this
case TOY_VALUE_OPAQUE: case TOY_VALUE_OPAQUE:
case TOY_VALUE_ANY: case TOY_VALUE_ANY:
@@ -432,10 +432,10 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
switch(value.type) { switch(value.type) {
case TOY_VALUE_NULL: case TOY_VALUE_NULL:
return Toy_createString(bucketHandle, "null"); return Toy_createString(bucketHandle, "<null>");
case TOY_VALUE_BOOLEAN: case TOY_VALUE_BOOLEAN:
return Toy_createString(bucketHandle, value.as.boolean ? "true" : "false"); return Toy_createString(bucketHandle, value.as.boolean ? "<true>" : "<false>");
case TOY_VALUE_INTEGER: { case TOY_VALUE_INTEGER: {
char buffer[16]; char buffer[16];
@@ -622,7 +622,9 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
return string; return string;
} }
case TOY_VALUE_FUNCTION: //URGENT: check this case TOY_VALUE_FUNCTION:
//dummy
return Toy_createString(bucketHandle, "<fn>");
case TOY_VALUE_OPAQUE: case TOY_VALUE_OPAQUE:
case TOY_VALUE_ANY: case TOY_VALUE_ANY:

View File

@@ -373,6 +373,79 @@ static void processAccess(Toy_VM* vm) {
Toy_freeValue(name); Toy_freeValue(name);
} }
static void processInvoke(Toy_VM* vm) {
Toy_ValueType valueType = READ_BYTE(vm); //unused for now
unsigned int argCount = (unsigned int)READ_BYTE(vm);
fixAlignment(vm);
//check for invoking bad values
if (valueType != TOY_VALUE_FUNCTION) {
Toy_error("Unrecognized invoke on a non-function value");
return;
}
//function to call
Toy_Value value = Toy_popStack(&vm->stack);
if (TOY_VALUE_IS_FUNCTION(value) != true) {
Toy_error("Can't call a non-function value");
return;
}
//process based on the function type
Toy_Function* fn = TOY_VALUE_AS_FUNCTION(value);
switch(fn->type) {
case TOY_FUNCTION_MODULE: {
Toy_Module module = fn->module.module;
//NOTE: counts within the modules actually specify size in memory, so the argCount is multiplied by 8 for the 8 bytes used in the params table
//check args count
if (argCount * 8 != module.paramCount) {
Toy_error("Incorrect number of parameters specified for function call");
break;
}
if (argCount > vm->stack->count) {
Toy_error("Incorrect number of parameters on the stack for function call");
break;
}
//spin up a new sub-vm
Toy_VM subVM;
Toy_initVM(&subVM);
Toy_bindVM(&subVM, &module, false);
//inject params, backwards from the stack
for (unsigned int i = argCount; i > 0; i--) {
Toy_Value argValue = Toy_popStack(&vm->stack);
//paramAddr is relative to the data section, and is followed by the param type
unsigned int paramAddr = ((unsigned int*)(module.code + module.paramAddr))[(i-1)*2];
Toy_ValueType paramType = (Toy_ValueType)(((unsigned int*)(module.code + module.paramAddr))[(i-1)*2 + 1]);
//c-string of the param's name
const char* cstr = ((char*)(module.code + module.dataAddr)) + paramAddr;
//as a name string
Toy_String* name = Toy_createNameStringLength(&subVM.literalBucket, cstr, strlen(cstr), paramType, true);
Toy_declareScope(subVM.scope, name, argValue);
}
//run and cleanup
Toy_runVM(&subVM);
Toy_freeVM(&subVM);
}
break;
case TOY_FUNCTION_NATIVE:
default:
Toy_error("Can't call an unknown function type");
break;
}
}
static void processDuplicate(Toy_VM* vm) { static void processDuplicate(Toy_VM* vm) {
Toy_Value value = Toy_copyValue(Toy_peekStack(&vm->stack)); Toy_Value value = Toy_copyValue(Toy_peekStack(&vm->stack));
Toy_pushStack(&vm->stack, value); Toy_pushStack(&vm->stack, value);
@@ -882,6 +955,10 @@ static void process(Toy_VM* vm) {
processAccess(vm); processAccess(vm);
break; break;
case TOY_OPCODE_INVOKE:
processInvoke(vm);
break;
case TOY_OPCODE_DUPLICATE: case TOY_OPCODE_DUPLICATE:
processDuplicate(vm); processDuplicate(vm);
break; break;

View File

@@ -51,6 +51,7 @@ int test_sizeof_ast(void) {
TEST_SIZEOF(Toy_AstVarAssign, 16 , 24); TEST_SIZEOF(Toy_AstVarAssign, 16 , 24);
TEST_SIZEOF(Toy_AstVarAccess, 8 , 16); TEST_SIZEOF(Toy_AstVarAccess, 8 , 16);
TEST_SIZEOF(Toy_AstFnDeclare, 16 , 32); TEST_SIZEOF(Toy_AstFnDeclare, 16 , 32);
TEST_SIZEOF(Toy_AstFnInvoke, 12 , 24);
TEST_SIZEOF(Toy_AstPass, 4 , 4); TEST_SIZEOF(Toy_AstPass, 4 , 4);
TEST_SIZEOF(Toy_AstError, 4 , 4); TEST_SIZEOF(Toy_AstError, 4 , 4);
TEST_SIZEOF(Toy_AstEnd, 4 , 4); TEST_SIZEOF(Toy_AstEnd, 4 , 4);

View File

@@ -369,7 +369,7 @@ int test_value_stringify(void) {
//check //check
if (string->info.type != TOY_STRING_LEAF || if (string->info.type != TOY_STRING_LEAF ||
strcmp(string->leaf.data, "null") != 0) strcmp(string->leaf.data, "<null>") != 0)
{ {
fprintf(stderr, TOY_CC_ERROR "ERROR: stringify 'null' failed\n" TOY_CC_RESET); fprintf(stderr, TOY_CC_ERROR "ERROR: stringify 'null' failed\n" TOY_CC_RESET);
Toy_freeString(string); Toy_freeString(string);
@@ -395,7 +395,7 @@ int test_value_stringify(void) {
//check //check
if (string->info.type != TOY_STRING_LEAF || if (string->info.type != TOY_STRING_LEAF ||
strcmp(string->leaf.data, "true") != 0) strcmp(string->leaf.data, "<true>") != 0)
{ {
fprintf(stderr, TOY_CC_ERROR "ERROR: stringify boolean 'true' failed\n" TOY_CC_RESET); fprintf(stderr, TOY_CC_ERROR "ERROR: stringify boolean 'true' failed\n" TOY_CC_RESET);
Toy_freeString(string); Toy_freeString(string);
@@ -421,7 +421,7 @@ int test_value_stringify(void) {
//check //check
if (string->info.type != TOY_STRING_LEAF || if (string->info.type != TOY_STRING_LEAF ||
strcmp(string->leaf.data, "false") != 0) strcmp(string->leaf.data, "<false>") != 0)
{ {
fprintf(stderr, TOY_CC_ERROR "ERROR: stringify boolean 'false' failed\n" TOY_CC_RESET); fprintf(stderr, TOY_CC_ERROR "ERROR: stringify boolean 'false' failed\n" TOY_CC_RESET);
Toy_freeString(string); Toy_freeString(string);