From 955927e0219e66db127a6453cedb09319d9b042f Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Sat, 28 Feb 2026 20:27:47 +1100 Subject: [PATCH] Function definitions --- src/codegen/codegen.c | 51 ++++++++++++++- src/parser/parser.c | 146 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+), 1 deletion(-) diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 319aa84..729ebd1 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -738,13 +738,61 @@ ResultType(GroundProgram, charptr) generateLambdaNode(SolsNode* node, SolsScope* return Success(GroundProgram, charptr, gp); } +ResultType(GroundProgram, charptr) generateDefNode(SolsNode* node, SolsScope* scope) { + GroundProgram gp = groundCreateProgram(); + + // Generate function signature + GroundInstruction signature = groundCreateInstruction(FUN); + + node->accessArg = groundCreateReference(VALREF, node->children.at[0].as.idName); + + groundAddReferenceToInstruction(&signature, groundCreateReference(FNREF, node->children.at[0].as.idName)); + ResultType(GroundArg, charptr) arg = createGroundArgFromSolsType(node->as.type.returnType); + if (arg.error) { + return Error(GroundProgram, charptr, arg.as.error); + } + groundAddReferenceToInstruction(&signature, arg.as.success); + for (size_t i = 0; i < node->as.type.children.count; i++) { + // Add type + ResultType(GroundArg, charptr) arg = createGroundArgFromSolsType(&node->as.type.children.at[i].type); + if (arg.error) { + return Error(GroundProgram, charptr, arg.as.error); + } + groundAddReferenceToInstruction(&signature, arg.as.success); + + // Add arg name + groundAddReferenceToInstruction(&signature, groundCreateReference(DIRREF, node->as.type.children.at[i].name)); + } + + groundAddInstructionToProgram(&gp, signature); + + // Create a scope for function arguments + SolsScope functionScope = copySolsScope(scope); + + for (size_t i = 0; i < node->as.type.children.count; i++) { + addVariableToScope(&functionScope, node->as.type.children.at[i].name, node->as.type.children.at[i].type); + } + + // Generate children and add then to this program + ResultType(GroundProgram, charptr) bodyCode = generateCode(&node->children.at[1], &functionScope); + if (bodyCode.error) return bodyCode; + for (size_t i = 0; i < bodyCode.as.success.size; i++) { + groundAddInstructionToProgram(&gp, bodyCode.as.success.instructions[i]); + } + + // End the function + groundAddInstructionToProgram(&gp, groundCreateInstruction(ENDFUN)); + + return Success(GroundProgram, charptr, gp); +} + ResultType(GroundProgram, charptr) generateCode(SolsNode* node, SolsScope* scope) { GroundProgram program = groundCreateProgram(); SolsScope backupScope = {NULL, 0}; - if (node->type != SNT_IF && node->type != SNT_WHILE && node->type != SNT_LAMBDA) { + if (node->type != SNT_IF && node->type != SNT_WHILE && node->type != SNT_LAMBDA && node->type != SNT_DEF) { if (node->type == SNT_CODE_BLOCK) { backupScope = *scope; SolsScope newScope = copySolsScope(scope); @@ -785,6 +833,7 @@ ResultType(GroundProgram, charptr) generateCode(SolsNode* node, SolsScope* scope case SNT_IF: generate(If); case SNT_WHILE: generate(While); case SNT_LAMBDA: generate(Lambda); + case SNT_DEF: generate(Def); } return Success(GroundProgram, charptr, program); } diff --git a/src/parser/parser.c b/src/parser/parser.c index 175e3f1..9119bc1 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -1261,6 +1261,151 @@ static inline ResultType(Nothing, charptr) parseLambda(SolsParser* parser) { return Success(Nothing, charptr, {}); } +static inline ResultType(Nothing, charptr) parseDef(SolsParser* parser) { + // Consume and validate the function name identifier + ResultType(SolsToken, Nothing) nameTok = parserConsume(parser); + if (nameTok.error || nameTok.as.success.type != STT_IDENTIFIER) { + return Error(Nothing, charptr, "Expecting function name after 'def'"); + } + + ResultType(SolsToken, Nothing) openBracket = parserConsume(parser); + if (openBracket.error || openBracket.as.success.type != STT_OPEN_PAREN) { + return Error(Nothing, charptr, "Expecting '(' after function name in 'def'"); + } + + ResultType(SolsType, charptr) type = createSolsType(STT_FUN); + if (type.error) { + return Error(Nothing, charptr, type.as.error); + } + + ResultType(SolsNode, charptr) node = createSolsNode(SNT_DEF); + if (node.error) { + return Error(Nothing, charptr, node.as.error); + } + + // Add function name as the first child node + ResultType(SolsNode, charptr) nameNode = createSolsNode(SNT_IDENTIFIER, nameTok.as.success.as.idName); + if (nameNode.error) { + return Error(Nothing, charptr, nameNode.as.error); + } + nameNode.as.success.line = nameTok.as.success.line; + nameNode.as.success.accessArg = (GroundArg) { + .type = VALREF, + .value.refName = nameTok.as.success.as.idName + }; + addChildToSolsNode(&node.as.success, nameNode.as.success); + + // Parse type signature + for (;;) { + ResultType(SolsToken, Nothing) next = parserPeek(parser, 1); + if (next.error) { + return Error(Nothing, charptr, "Expecting ')' at end of def argument list"); + } + if (next.as.success.type == STT_CLOSE_PAREN) { + parserConsume(parser); + break; + } + + if (type.error) { + return Error(Nothing, charptr, type.as.error); + } + + // Pattern of type, name, comma + + SolsType tmpType; + + if (next.as.success.type == STT_TYPE) { + tmpType = next.as.success.as.type; + } else if (next.as.success.type == STT_IDENTIFIER) { + tmpType.identifierType = next.as.success.as.idName; + } else { + return Error(Nothing, charptr, "Expecting a type or identifier of type in def argument list"); + } + + parserConsume(parser); + + char* argName; + next = parserPeek(parser, 1); + + if (next.as.success.type == STT_IDENTIFIER) { + argName = next.as.success.as.idName; + } else { + return Error(Nothing, charptr, "Expecting identifier after type in def argument list"); + } + + // Add type to constructed SolsType + addChildToSolsType(&type.as.success, tmpType, argName); + parserConsume(parser); + + next = parserPeek(parser, 1); + if (next.error) { + return Error(Nothing, charptr, "Expecting a comma or closing bracket"); + } + if (next.as.success.type == STT_CLOSE_PAREN) { + parserConsume(parser); + break; + } + if (next.as.success.type != STT_COMMA) { + return Error(Nothing, charptr, "Expecting a comma or closing bracket"); + } + parserConsume(parser); + } + + // Parse return type after argument list + ResultType(SolsToken, Nothing) retType = parserPeek(parser, 1); + if (retType.error) { + return Error(Nothing, charptr, "Expecting return type or identifier of type after def argument list"); + } + + if (retType.as.success.type == STT_TYPE) { + type.as.success.returnType = malloc(sizeof(SolsType)); + if (type.as.success.returnType == NULL) { + return Error(Nothing, charptr, "Failed to allocate memory for type"); + } + *type.as.success.returnType = retType.as.success.as.type; + } else if (retType.as.success.type == STT_IDENTIFIER) { + type.as.success.returnType = malloc(sizeof(SolsType)); + if (type.as.success.returnType == NULL) { + return Error(Nothing, charptr, "Failed to allocate memory for type"); + } + type.as.success.returnType->identifierType = retType.as.success.as.idName; + } else { + return Error(Nothing, charptr, "Expecting return type or identifier of type after def argument list"); + } + + // Add type to node + node.as.success.as.type = type.as.success; + + parserConsume(parser); // Consumes return type + + // Skip newlines before the opening curly brace + while (parserPeek(parser, 1).as.success.type == STT_LINE_END) { + parserConsume(parser); + } + + ResultType(SolsToken, Nothing) openCurly = parserConsume(parser); + if (openCurly.error || openCurly.as.success.type != STT_OPEN_CURLY) { + return Error(Nothing, charptr, "Expecting opening curly brace ({) for def body"); + } + + // Add node to parent + addChildToSolsNode(parser->currentParent, node.as.success); + + // Parse code block + ResultType(Nothing, charptr) res = parseCodeBlock(parser); + if (res.error) return res; + + // Last child of parent is code block, we need to move it + SolsNode codeBlock = parser->currentParent->children.at[parser->currentParent->children.count - 1]; + parser->currentParent->children.count--; + + // We need to get the actual node from the parent to modify it + SolsNode* defNode = &parser->currentParent->children.at[parser->currentParent->children.count - 1]; + addChildToSolsNode(defNode, codeBlock); + + return Success(Nothing, charptr, {}); +} + ResultType(Nothing, charptr) parse(SolsParser* parser) { parser->currentParent = &parser->output; for (;;) { @@ -1275,6 +1420,7 @@ ResultType(Nothing, charptr) parse(SolsParser* parser) { case STT_KW_IF: PARSER_HANDLE(If); case STT_KW_WHILE: PARSER_HANDLE(While); case STT_KW_LAMBDA: PARSER_HANDLE(Lambda); + case STT_KW_DEF: PARSER_HANDLE(Def); case STT_OP_SET: PARSER_HANDLE(Set); case STT_OP_ADD: PARSER_HANDLE(Add); case STT_OP_SUB: PARSER_HANDLE(Sub);