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

@@ -213,6 +213,16 @@ void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast**
(*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) {
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_FN_DECLARE,
TOY_AST_FN_INVOKE,
TOY_AST_PASS,
TOY_AST_ERROR,
@@ -200,6 +201,12 @@ typedef struct Toy_AstFnDeclare {
Toy_Ast* body;
} Toy_AstFnDeclare;
typedef struct Toy_AstFnInvoke {
Toy_AstType type;
Toy_Ast* function;
Toy_Ast* args;
} Toy_AstFnInvoke;
typedef struct Toy_AstPass {
Toy_AstType type;
} Toy_AstPass;
@@ -233,6 +240,7 @@ union Toy_Ast { //see 'test_ast.c' for bitness tests
Toy_AstVarAssign varAssign;
Toy_AstVarAccess varAccess;
Toy_AstFnDeclare fnDeclare;
Toy_AstFnInvoke fnInvoke;
Toy_AstPass pass;
Toy_AstError error;
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_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_emitAstError(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_FunctionType;
typedef union Toy_FunctionModule {
typedef struct Toy_FunctionModule {
Toy_FunctionType type;
Toy_Module module;
} Toy_FunctionModule;
typedef union Toy_FunctionNative {
typedef struct Toy_FunctionNative {
Toy_FunctionType type;
void* native; //TODO: replace with the native function pointer
} Toy_FunctionNative;

View File

@@ -215,6 +215,7 @@ static unsigned int emitParameters(Toy_ModuleCompiler* mb, Toy_Ast* ast) {
//emit to the param index
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
return 1;
@@ -1041,6 +1042,32 @@ static unsigned int writeInstructionFnDeclare(Toy_ModuleCompiler** mb, Toy_AstFn
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) {
if (ast == NULL) {
return 0;
@@ -1151,6 +1178,10 @@ static unsigned int writeModuleCompilerCode(Toy_ModuleCompiler** mb, Toy_Ast* as
result += writeInstructionFnDeclare(mb, ast->fnDeclare);
break;
case TOY_AST_FN_INVOKE:
result += writeInstructionFnInvoke(mb, ast->fnInvoke);
break;
case TOY_AST_PASS:
//NO-OP
break;

View File

@@ -10,6 +10,7 @@ typedef enum Toy_OpcodeType {
TOY_OPCODE_ASSIGN,
TOY_OPCODE_ASSIGN_COMPOUND, //assign to a compound's internals
TOY_OPCODE_ACCESS,
TOY_OPCODE_INVOKE, //for calling functions
TOY_OPCODE_DUPLICATE, //duplicate 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 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 invoke(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
//precedence definitions
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_DIVIDE_ASSIGN,
{PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_MODULO_ASSIGN,
{PREC_CALL,unary,unaryPostfix},// TOY_TOKEN_OPERATOR_INCREMENT,
{PREC_CALL,unary,unaryPostfix},// TOY_TOKEN_OPERATOR_DECREMENT,
{PREC_UNARY,unary,unaryPostfix},// TOY_TOKEN_OPERATOR_INCREMENT,
{PREC_UNARY,unary,unaryPostfix},// TOY_TOKEN_OPERATOR_DECREMENT,
{PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_ASSIGN,
//comparator operators
@@ -194,7 +195,7 @@ static ParsingTuple parsingRulesetTable[] = {
{PREC_COMPARISON,NULL,binary},// TOY_TOKEN_OPERATOR_COMPARE_GREATER_EQUAL,
//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_GROUP,compound,aggregate},// TOY_TOKEN_OPERATOR_BRACKET_LEFT,
{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_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, // ...
//unused operators
@@ -557,7 +558,7 @@ static Toy_AstFlag binary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast
}
case TOY_TOKEN_OPERATOR_CONCAT: {
parsePrecedence(bucketHandle, parser, rootHandle, PREC_CALL + 1);
parsePrecedence(bucketHandle, parser, rootHandle, PREC_UNARY + 1);
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
static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle, ParsingPrecedence precRule) {
//'step over' the token to parse
@@ -741,7 +767,7 @@ static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A
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);
//finished

View File

@@ -139,14 +139,6 @@ void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
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);
}

View File

@@ -311,7 +311,7 @@ bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) {
}
case TOY_VALUE_FUNCTION:
return false; //URGENT: check this
return false; //URGENT: test this
case TOY_VALUE_OPAQUE:
case TOY_VALUE_ANY:
@@ -432,10 +432,10 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
switch(value.type) {
case TOY_VALUE_NULL:
return Toy_createString(bucketHandle, "null");
return Toy_createString(bucketHandle, "<null>");
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: {
char buffer[16];
@@ -622,7 +622,9 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
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_ANY:

View File

@@ -373,6 +373,79 @@ static void processAccess(Toy_VM* vm) {
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) {
Toy_Value value = Toy_copyValue(Toy_peekStack(&vm->stack));
Toy_pushStack(&vm->stack, value);
@@ -882,6 +955,10 @@ static void process(Toy_VM* vm) {
processAccess(vm);
break;
case TOY_OPCODE_INVOKE:
processInvoke(vm);
break;
case TOY_OPCODE_DUPLICATE:
processDuplicate(vm);
break;