#include "codegen.h" #include "SolsScope.h" #include #include #include #include "../parser/SolsNode.h" #include "../include/estr.h" #include "../include/uthash.h" #include "../include/ansii.h" // FIXME show multiple lines for the error char* createCodegenError(SolsNode* node, char* what) { if (node->line.content == NULL) { return what; } Estr err = CREATE_ESTR(ESC_BOLD ESC_RED_FG "error: " ESC_RESET ESC_BOLD); APPEND_ESTR(err, what); APPEND_ESTR(err, "\n" ESC_RESET); APPEND_ESTR(err, ESC_CYAN_FG "-> " ESC_RESET); APPEND_ESTR(err, "on line "); char line_buf[16]; snprintf(line_buf, sizeof(line_buf), "%zu", node->line.num); APPEND_ESTR(err, line_buf); APPEND_ESTR(err, "\n\n"); APPEND_ESTR(err, node->line.content); APPEND_ESTR(err, "\n"); return err.str; } ResultType(SolsType, charptr) getNodeType(SolsNode* node, SolsScope* scope) { switch (node->type) { case SNT_PUTS: case SNT_IF: case SNT_WHILE: case SNT_CODE_BLOCK: { return Error(SolsType, charptr, "Specified node does not return data"); } case SNT_OP_SET: { if (node->children.count < 2) { return Error(SolsType, charptr, "Not enough children to determine type"); } ResultType(SolsType, charptr) type = getNodeType(&node->children.at[0], scope); if (type.error) { return Error(SolsType, charptr, type.as.error); } return Success(SolsType, charptr, type.as.success); } case SNT_OP_ADD: { if (node->children.count < 2) { return Error(SolsType, charptr, "Not enough children to determine type"); } ResultType(SolsType, charptr) leftType = getNodeType(&node->children.at[0], scope); if (leftType.error) { return Error(SolsType, charptr, leftType.as.error); } ResultType(SolsType, charptr) rightType = getNodeType(&node->children.at[1], scope); if (rightType.error) { return Error(SolsType, charptr, rightType.as.error); } if (!(leftType.as.success.type == STT_INT || leftType.as.success.type == STT_DOUBLE || leftType.as.success.type == STT_STRING)) { return Error(SolsType, charptr, "Cannot add left type"); } if (!(rightType.as.success.type == STT_INT || rightType.as.success.type == STT_DOUBLE || rightType.as.success.type == STT_STRING)) { return Error(SolsType, charptr, "Cannot add right type"); } if (leftType.as.success.type == rightType.as.success.type) { return Success(SolsType, charptr, leftType.as.success); } if ((leftType.as.success.type == STT_INT && rightType.as.success.type == STT_DOUBLE) || (leftType.as.success.type == STT_DOUBLE && rightType.as.success.type == STT_INT)) { ResultType(SolsType, charptr) type = createSolsType(STT_DOUBLE); if (type.error) { return Error(SolsType, charptr, type.as.error); } return Success(SolsType, charptr, type.as.success); } return Error(SolsType, charptr, "Cannot add these types together"); } case SNT_OP_MUL: case SNT_OP_DIV: case SNT_OP_SUB: { if (node->children.count < 2) { return Error(SolsType, charptr, "Not enough children to determine type"); } ResultType(SolsType, charptr) leftType = getNodeType(&node->children.at[0], scope); if (leftType.error) { return Error(SolsType, charptr, leftType.as.error); } ResultType(SolsType, charptr) rightType = getNodeType(&node->children.at[1], scope); if (rightType.error) { return Error(SolsType, charptr, rightType.as.error); } if (!(leftType.as.success.type == STT_INT || leftType.as.success.type == STT_DOUBLE)) { return Error(SolsType, charptr, "Cannot operate on left type"); } if (!(rightType.as.success.type == STT_INT || rightType.as.success.type == STT_DOUBLE)) { return Error(SolsType, charptr, "Cannot operate on right type"); } if (leftType.as.success.type == rightType.as.success.type) { return Success(SolsType, charptr, leftType.as.success); } ResultType(SolsType, charptr) type = createSolsType(STT_DOUBLE); if (type.error) { return Error(SolsType, charptr, type.as.error); } return Success(SolsType, charptr, type.as.success); } case SNT_OP_EQUAL: case SNT_OP_INEQUAL: { if (node->children.count < 2) { return Error(SolsType, charptr, "Not enough children to determine type"); } ResultType(SolsType, charptr) leftType = getNodeType(&node->children.at[0], scope); if (leftType.error) { return Error(SolsType, charptr, leftType.as.error); } ResultType(SolsType, charptr) rightType = getNodeType(&node->children.at[1], scope); if (rightType.error) { return Error(SolsType, charptr, rightType.as.error); } if (leftType.as.success.type == STT_FUN || leftType.as.success.type == STT_TEMPLATE || leftType.as.success.type == STT_OBJECT) { return Error(SolsType, charptr, "Cannot compare with left type"); } if (rightType.as.success.type == STT_FUN || rightType.as.success.type == STT_TEMPLATE || rightType.as.success.type == STT_OBJECT) { return Error(SolsType, charptr, "Cannot compare with right type"); } if (leftType.as.success.type == rightType.as.success.type) { return Success(SolsType, charptr, {STT_BOOL}); } if ((leftType.as.success.type == STT_INT && rightType.as.success.type == STT_DOUBLE) || (rightType.as.success.type == STT_INT && leftType.as.success.type == STT_DOUBLE)) { return Success(SolsType, charptr, {STT_BOOL}); } return Error(SolsType, charptr, ""); } case SNT_OP_GREATER: case SNT_OP_LESSER: case SNT_OP_EQGREATER: case SNT_OP_EQLESSER: { if (node->children.count < 2) { return Error(SolsType, charptr, "Not enough children to determine type"); } ResultType(SolsType, charptr) leftType = getNodeType(&node->children.at[0], scope); if (leftType.error) { return Error(SolsType, charptr, leftType.as.error); } ResultType(SolsType, charptr) rightType = getNodeType(&node->children.at[1], scope); if (rightType.error) { return Error(SolsType, charptr, rightType.as.error); } if (!(leftType.as.success.type == STT_INT || leftType.as.success.type == STT_DOUBLE)) { return Error(SolsType, charptr, "Cannot compare with left type"); } if (!(rightType.as.success.type == STT_INT || rightType.as.success.type == STT_DOUBLE)) { return Error(SolsType, charptr, "Cannot compare with right type"); } return Success(SolsType, charptr, STT_BOOL); } case SNT_LITERAL: { switch (node->as.literal.type) { case SLT_INT: { return Success(SolsType, charptr, {STT_INT}); } case SLT_DOUBLE: { return Success(SolsType, charptr, {STT_DOUBLE}); } case SLT_STRING: { return Success(SolsType, charptr, {STT_STRING}); } case SLT_BOOL: { return Success(SolsType, charptr, {STT_BOOL}); } case SLT_CHAR: { return Success(SolsType, charptr, {STT_CHAR}); } } break; } case SNT_IDENTIFIER: { SolsVariable* var = findSolsVariable(scope, node->as.idName); if (var == NULL) { Estr estr = CREATE_ESTR("Unable to find variable "); APPEND_ESTR(estr, node->as.idName); return Error(SolsType, charptr, estr.str); } return Success(SolsType, charptr, var->typeinfo); } case SNT_LAMBDA: { return Success(SolsType, charptr, node->as.type); } case SNT_FUNCTION_CALL: { SolsVariable* var = findSolsVariable(scope, node->as.idName); if (var == NULL) { Estr estr = CREATE_ESTR("Unable to find function "); APPEND_ESTR(estr, node->as.idName); return Error(SolsType, charptr, estr.str); } if (var->typeinfo.type != STT_FUN) { Estr estr = CREATE_ESTR(node->as.idName); APPEND_ESTR(estr, " is not a callable function"); return Error(SolsType, charptr, estr.str); } return Success(SolsType, charptr, *var->typeinfo.returnType); } } return Error(SolsType, charptr, "Not yet implemented"); } static inline ResultType(GroundProgram, charptr) generateLiteralNode(SolsNode* node, SolsScope* scope) { // We don't even need to do anything lmao return Success(GroundProgram, charptr, groundCreateProgram()); } static inline ResultType(GroundProgram, charptr) generatePutsNode(SolsNode* node, SolsScope* scope) { if (node->children.count < 1) { return Error(GroundProgram, charptr, "puts requires arguments"); } GroundInstruction inst = groundCreateInstruction(PRINTLN); for (size_t i = 0; i < node->children.count; i++) { // Validate arg ResultType(SolsType, charptr) type = getNodeType(&node->children.at[i], scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } groundAddReferenceToInstruction(&inst, node->children.at[i].accessArg); } GroundProgram program = groundCreateProgram(); groundAddInstructionToProgram(&program, inst); return Success(GroundProgram, charptr, program); } static inline ResultType(GroundProgram, charptr) generateSetNode(SolsNode* node, SolsScope* scope) { if (node->children.count < 2) { return Error(GroundProgram, charptr, "set requires arguments"); } if (node->children.at[0].type != SNT_IDENTIFIER) { return Error(GroundProgram, charptr, "set requires an identifier before '='"); } SolsVariable* var = findSolsVariable(scope, node->children.at[0].as.idName); ResultType(SolsType, charptr) type = getNodeType(&node->children.at[1], scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } if (var == NULL) { addVariableToScope(scope, node->children.at[0].as.idName, type.as.success); } else { ResultType(SolsType, charptr) type = getNodeType(&node->children.at[0], scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } if (compareTypes(&var->typeinfo, &type.as.success) == false) { return Error(GroundProgram, charptr, "Type of variable cannot be changed"); } } GroundProgram gp = groundCreateProgram(); GroundInstruction gi = groundCreateInstruction(SET); groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, node->children.at[0].as.idName)); groundAddReferenceToInstruction(&gi, node->children.at[1].accessArg); groundAddInstructionToProgram(&gp, gi); return Success(GroundProgram, charptr, gp); } static inline ResultType(GroundProgram, charptr) generateAddNode(SolsNode* node, SolsScope* scope) { if (node->children.count < 2) { return Error(GroundProgram, charptr, "add requires arguments"); } // Use this function for type checking ResultType(SolsType, charptr) type = getNodeType(node, scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } GroundProgram gp = groundCreateProgram(); GroundInstruction add = groundCreateInstruction(ADD); groundAddReferenceToInstruction(&add, node->children.at[0].accessArg); groundAddReferenceToInstruction(&add, node->children.at[1].accessArg); char* tmpId = malloc(sizeof(char) * 64); if (tmpId == NULL) { return Error(GroundProgram, charptr, "Failed to allocate memory for temporary identifier in add"); } snprintf(tmpId, 64, "__SOLS_TMP_ADD_%zu", scope->tmpCounter++); groundAddReferenceToInstruction(&add, groundCreateReference(DIRREF, tmpId)); node->accessArg = groundCreateReference(VALREF, tmpId); groundAddInstructionToProgram(&gp, add); return Success(GroundProgram, charptr, gp); } static inline ResultType(GroundProgram, charptr) generateSubNode(SolsNode* node, SolsScope* scope) { if (node->children.count < 2) { return Error(GroundProgram, charptr, "sub requires arguments"); } // Use this function for type checking ResultType(SolsType, charptr) type = getNodeType(node, scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } GroundProgram gp = groundCreateProgram(); GroundInstruction add = groundCreateInstruction(SUBTRACT); groundAddReferenceToInstruction(&add, node->children.at[0].accessArg); groundAddReferenceToInstruction(&add, node->children.at[1].accessArg); char* tmpId = malloc(sizeof(char) * 64); if (tmpId == NULL) { return Error(GroundProgram, charptr, "Failed to allocate memory for temporary identifier in subtract"); } snprintf(tmpId, 64, "__SOLS_TMP_SUB_%zu", scope->tmpCounter++); groundAddReferenceToInstruction(&add, groundCreateReference(DIRREF, tmpId)); node->accessArg = groundCreateReference(VALREF, tmpId); groundAddInstructionToProgram(&gp, add); return Success(GroundProgram, charptr, gp); } static inline ResultType(GroundProgram, charptr) generateMulNode(SolsNode* node, SolsScope* scope) { if (node->children.count < 2) { return Error(GroundProgram, charptr, "mul requires arguments"); } // Use this function for type checking ResultType(SolsType, charptr) type = getNodeType(node, scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } GroundProgram gp = groundCreateProgram(); GroundInstruction add = groundCreateInstruction(MULTIPLY); groundAddReferenceToInstruction(&add, node->children.at[0].accessArg); groundAddReferenceToInstruction(&add, node->children.at[1].accessArg); char* tmpId = malloc(sizeof(char) * 64); if (tmpId == NULL) { return Error(GroundProgram, charptr, "Failed to allocate memory for temporary identifier in multiply"); } snprintf(tmpId, 64, "__SOLS_TMP_MUL_%zu", scope->tmpCounter++); groundAddReferenceToInstruction(&add, groundCreateReference(DIRREF, tmpId)); node->accessArg = groundCreateReference(VALREF, tmpId); groundAddInstructionToProgram(&gp, add); return Success(GroundProgram, charptr, gp); } static inline ResultType(GroundProgram, charptr) generateDivNode(SolsNode* node, SolsScope* scope) { if (node->children.count < 2) { return Error(GroundProgram, charptr, "div requires arguments"); } // Use this function for type checking ResultType(SolsType, charptr) type = getNodeType(node, scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } GroundProgram gp = groundCreateProgram(); GroundInstruction add = groundCreateInstruction(DIVIDE); groundAddReferenceToInstruction(&add, node->children.at[0].accessArg); groundAddReferenceToInstruction(&add, node->children.at[1].accessArg); char* tmpId = malloc(sizeof(char) * 64); if (tmpId == NULL) { return Error(GroundProgram, charptr, "Failed to allocate memory for temporary identifier in divide"); } snprintf(tmpId, 64, "__SOLS_TMP_DIV_%zu", scope->tmpCounter++); groundAddReferenceToInstruction(&add, groundCreateReference(DIRREF, tmpId)); node->accessArg = groundCreateReference(VALREF, tmpId); groundAddInstructionToProgram(&gp, add); return Success(GroundProgram, charptr, gp); } static inline ResultType(GroundProgram, charptr) generateEqualNode(SolsNode* node, SolsScope* scope) { if (node->children.count < 2) { return Error(GroundProgram, charptr, "equal requires arguments"); } // Use this function for type checking ResultType(SolsType, charptr) type = getNodeType(node, scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } GroundProgram gp = groundCreateProgram(); GroundInstruction add = groundCreateInstruction(EQUAL); groundAddReferenceToInstruction(&add, node->children.at[0].accessArg); groundAddReferenceToInstruction(&add, node->children.at[1].accessArg); char* tmpId = malloc(sizeof(char) * 64); if (tmpId == NULL) { return Error(GroundProgram, charptr, "Failed to allocate memory for temporary identifier in equal"); } snprintf(tmpId, 64, "__SOLS_TMP_EQUAL_%zu", scope->tmpCounter++); groundAddReferenceToInstruction(&add, groundCreateReference(DIRREF, tmpId)); node->accessArg = groundCreateReference(VALREF, tmpId); groundAddInstructionToProgram(&gp, add); return Success(GroundProgram, charptr, gp); } static inline ResultType(GroundProgram, charptr) generateInequalNode(SolsNode* node, SolsScope* scope) { if (node->children.count < 2) { return Error(GroundProgram, charptr, "inequal requires arguments"); } // Use this function for type checking ResultType(SolsType, charptr) type = getNodeType(node, scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } GroundProgram gp = groundCreateProgram(); GroundInstruction add = groundCreateInstruction(INEQUAL); groundAddReferenceToInstruction(&add, node->children.at[0].accessArg); groundAddReferenceToInstruction(&add, node->children.at[1].accessArg); char* tmpId = malloc(sizeof(char) * 64); if (tmpId == NULL) { return Error(GroundProgram, charptr, "Failed to allocate memory for temporary identifier in inequal"); } snprintf(tmpId, 64, "__SOLS_TMP_INEQUAL_%zu", scope->tmpCounter++); groundAddReferenceToInstruction(&add, groundCreateReference(DIRREF, tmpId)); node->accessArg = groundCreateReference(VALREF, tmpId); groundAddInstructionToProgram(&gp, add); return Success(GroundProgram, charptr, gp); } static inline ResultType(GroundProgram, charptr) generateGreaterNode(SolsNode* node, SolsScope* scope) { if (node->children.count < 2) { return Error(GroundProgram, charptr, "greater requires arguments"); } // Use this function for type checking ResultType(SolsType, charptr) type = getNodeType(node, scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } GroundProgram gp = groundCreateProgram(); GroundInstruction add = groundCreateInstruction(GREATER); groundAddReferenceToInstruction(&add, node->children.at[0].accessArg); groundAddReferenceToInstruction(&add, node->children.at[1].accessArg); char* tmpId = malloc(sizeof(char) * 64); if (tmpId == NULL) { return Error(GroundProgram, charptr, "Failed to allocate memory for temporary identifier in greater"); } snprintf(tmpId, 64, "__SOLS_TMP_GREATER_%zu", scope->tmpCounter++); groundAddReferenceToInstruction(&add, groundCreateReference(DIRREF, tmpId)); node->accessArg = groundCreateReference(VALREF, tmpId); groundAddInstructionToProgram(&gp, add); return Success(GroundProgram, charptr, gp); } static inline ResultType(GroundProgram, charptr) generateEqGreaterNode(SolsNode* node, SolsScope* scope) { if (node->children.count < 2) { return Error(GroundProgram, charptr, "eqgreater requires arguments"); } // Use this function for type checking ResultType(SolsType, charptr) type = getNodeType(node, scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } GroundProgram gp = groundCreateProgram(); GroundInstruction add = groundCreateInstruction(LESSER); groundAddReferenceToInstruction(&add, node->children.at[0].accessArg); groundAddReferenceToInstruction(&add, node->children.at[1].accessArg); char* tmpId = malloc(sizeof(char) * 64); if (tmpId == NULL) { return Error(GroundProgram, charptr, "Failed to allocate memory for temporary identifier in eqgreater"); } snprintf(tmpId, 64, "__SOLS_TMP_EQGREATER_%zu", scope->tmpCounter++); groundAddReferenceToInstruction(&add, groundCreateReference(DIRREF, tmpId)); node->accessArg = groundCreateReference(VALREF, tmpId); groundAddInstructionToProgram(&gp, add); GroundInstruction not = groundCreateInstruction(NOT); groundAddReferenceToInstruction(¬, groundCreateReference(VALREF, tmpId)); groundAddReferenceToInstruction(¬, groundCreateReference(DIRREF, tmpId)); groundAddInstructionToProgram(&gp, not); return Success(GroundProgram, charptr, gp); } static inline ResultType(GroundProgram, charptr) generateLesserNode(SolsNode* node, SolsScope* scope) { if (node->children.count < 2) { return Error(GroundProgram, charptr, "lesser requires arguments"); } // Use this function for type checking ResultType(SolsType, charptr) type = getNodeType(node, scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } GroundProgram gp = groundCreateProgram(); GroundInstruction add = groundCreateInstruction(LESSER); groundAddReferenceToInstruction(&add, node->children.at[0].accessArg); groundAddReferenceToInstruction(&add, node->children.at[1].accessArg); char* tmpId = malloc(sizeof(char) * 64); if (tmpId == NULL) { return Error(GroundProgram, charptr, "Failed to allocate memory for temporary identifier in lesser"); } snprintf(tmpId, 64, "__SOLS_TMP_LESSER_%zu", scope->tmpCounter++); groundAddReferenceToInstruction(&add, groundCreateReference(DIRREF, tmpId)); node->accessArg = groundCreateReference(VALREF, tmpId); groundAddInstructionToProgram(&gp, add); return Success(GroundProgram, charptr, gp); } static inline ResultType(GroundProgram, charptr) generateEqLesserNode(SolsNode* node, SolsScope* scope) { if (node->children.count < 2) { return Error(GroundProgram, charptr, "eqlesser requires arguments"); } // Use this function for type checking ResultType(SolsType, charptr) type = getNodeType(node, scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } GroundProgram gp = groundCreateProgram(); GroundInstruction add = groundCreateInstruction(GREATER); groundAddReferenceToInstruction(&add, node->children.at[0].accessArg); groundAddReferenceToInstruction(&add, node->children.at[1].accessArg); char* tmpId = malloc(sizeof(char) * 64); if (tmpId == NULL) { return Error(GroundProgram, charptr, "Failed to allocate memory for temporary identifier in eqlesser"); } snprintf(tmpId, 64, "__SOLS_TMP_LESSER_%zu", scope->tmpCounter++); groundAddReferenceToInstruction(&add, groundCreateReference(DIRREF, tmpId)); node->accessArg = groundCreateReference(VALREF, tmpId); groundAddInstructionToProgram(&gp, add); GroundInstruction not = groundCreateInstruction(NOT); groundAddReferenceToInstruction(¬, groundCreateReference(VALREF, tmpId)); groundAddReferenceToInstruction(¬, groundCreateReference(DIRREF, tmpId)); groundAddInstructionToProgram(&gp, not); return Success(GroundProgram, charptr, gp); } ResultType(GroundProgram, charptr) generateCodeBlockNode(SolsNode* node, SolsScope* scope) { // Nothing needs to be done, as children are handled by the generateCode function (void)node; (void)scope; return Success(GroundProgram, charptr, groundCreateProgram()); } static inline ResultType(GroundProgram, charptr) generateWhileNode(SolsNode* node, SolsScope* scope) { GroundProgram gp = groundCreateProgram(); char* start_label = malloc(64); snprintf(start_label, 64, "__SOLS_WHILE_START_%zu", scope->tmpCounter++); char* end_label = malloc(64); snprintf(end_label, 64, "__SOLS_WHILE_END_%zu", scope->tmpCounter++); GroundInstruction start_label_inst = groundCreateInstruction(CREATELABEL); groundAddReferenceToInstruction(&start_label_inst, groundCreateReference(LABEL, start_label)); groundAddInstructionToProgram(&gp, start_label_inst); ResultType(GroundProgram, charptr) cond_code = generateCode(&node->children.at[0], scope); if (cond_code.error) return cond_code; for (size_t i = 0; i < cond_code.as.success.size; i++) { groundAddInstructionToProgram(&gp, cond_code.as.success.instructions[i]); } ResultType(SolsType, charptr) type = getNodeType(&node->children.at[0], scope); if (type.error) return Error(GroundProgram, charptr, type.as.error); if (type.as.success.type != STT_BOOL) { return Error(GroundProgram, charptr, "While condition must be a boolean"); } char* tmp_inverted_cond = malloc(64); snprintf(tmp_inverted_cond, 64, "__SOLS_IF_COND_NOT_%zu", scope->tmpCounter++); GroundInstruction not_inst = groundCreateInstruction(NOT); groundAddReferenceToInstruction(¬_inst, node->children.at[0].accessArg); groundAddReferenceToInstruction(¬_inst, groundCreateReference(DIRREF, tmp_inverted_cond)); groundAddInstructionToProgram(&gp, not_inst); GroundInstruction if_inst = groundCreateInstruction(IF); groundAddReferenceToInstruction(&if_inst, groundCreateReference(VALREF, tmp_inverted_cond)); groundAddReferenceToInstruction(&if_inst, groundCreateReference(LINEREF, end_label)); groundAddInstructionToProgram(&gp, if_inst); ResultType(GroundProgram, charptr) body_code = generateCode(&node->children.at[1], scope); if (body_code.error) return body_code; for (size_t i = 0; i < body_code.as.success.size; i++) { groundAddInstructionToProgram(&gp, body_code.as.success.instructions[i]); } GroundInstruction jump_inst = groundCreateInstruction(JUMP); groundAddReferenceToInstruction(&jump_inst, groundCreateReference(LINEREF, start_label)); groundAddInstructionToProgram(&gp, jump_inst); GroundInstruction end_label_inst = groundCreateInstruction(CREATELABEL); groundAddReferenceToInstruction(&end_label_inst, groundCreateReference(LABEL, end_label)); groundAddInstructionToProgram(&gp, end_label_inst); return Success(GroundProgram, charptr, gp); } static inline ResultType(GroundProgram, charptr) generateIfNode(SolsNode* node, SolsScope* scope) { GroundProgram gp = groundCreateProgram(); ResultType(GroundProgram, charptr) cond_code = generateCode(&node->children.at[0], scope); if (cond_code.error) return cond_code; for (size_t i = 0; i < cond_code.as.success.size; i++) { groundAddInstructionToProgram(&gp, cond_code.as.success.instructions[i]); } ResultType(SolsType, charptr) type = getNodeType(&node->children.at[0], scope); if (type.error) return Error(GroundProgram, charptr, type.as.error); if (type.as.success.type != STT_BOOL) { return Error(GroundProgram, charptr, "If condition must be a boolean"); } char* end_label = malloc(64); snprintf(end_label, 64, "__SOLS_IF_END_%zu", scope->tmpCounter++); char* tmp_inverted_cond = malloc(64); snprintf(tmp_inverted_cond, 64, "__SOLS_IF_COND_NOT_%zu", scope->tmpCounter++); GroundInstruction not_inst = groundCreateInstruction(NOT); groundAddReferenceToInstruction(¬_inst, node->children.at[0].accessArg); groundAddReferenceToInstruction(¬_inst, groundCreateReference(DIRREF, tmp_inverted_cond)); groundAddInstructionToProgram(&gp, not_inst); GroundInstruction if_inst = groundCreateInstruction(IF); groundAddReferenceToInstruction(&if_inst, groundCreateReference(VALREF, tmp_inverted_cond)); groundAddReferenceToInstruction(&if_inst, groundCreateReference(LINEREF, end_label)); groundAddInstructionToProgram(&gp, if_inst); ResultType(GroundProgram, charptr) body_code = generateCode(&node->children.at[1], scope); if (body_code.error) return body_code; for (size_t i = 0; i < body_code.as.success.size; i++) { groundAddInstructionToProgram(&gp, body_code.as.success.instructions[i]); } GroundInstruction label_inst = groundCreateInstruction(CREATELABEL); groundAddReferenceToInstruction(&label_inst, groundCreateReference(LABEL, end_label)); groundAddInstructionToProgram(&gp, label_inst); return Success(GroundProgram, charptr, gp); } ResultType(GroundProgram, charptr) generateLambdaNode(SolsNode* node, SolsScope* scope) { GroundProgram gp = groundCreateProgram(); // Generate function signature GroundInstruction signature = groundCreateInstruction(FUN); char* lambdaId = malloc(sizeof(char) * 64); snprintf(lambdaId, 64, "__SOLS_LAMBDA_%zu", scope->tmpCounter++); node->accessArg = groundCreateReference(VALREF, lambdaId); groundAddReferenceToInstruction(&signature, groundCreateReference(FNREF, lambdaId)); 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 lambda arguments // Lambdas do NOT have access to external state SolsScope lambdaScope = { .variables = NULL, .tmpCounter = 0 }; for (size_t i = 0; i < node->as.type.children.count; i++) { addVariableToScope(&lambdaScope, 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[0], &lambdaScope); 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) generateDefNode(SolsNode* node, SolsScope* scope) { GroundProgram gp = groundCreateProgram(); // Register the function in the current scope so calls can resolve it // node->children.at[0] is the identifier node for the function name if (node->children.count < 2 || node->children.at[0].type != SNT_IDENTIFIER) { return Error(GroundProgram, charptr, "Invalid def node shape (expected name + body)"); } char* fnName = node->children.at[0].as.idName; SolsVariable* existing = findSolsVariable(scope, fnName); if (existing == NULL) { addVariableToScope(scope, fnName, node->as.type); } else { if (existing->typeinfo.type != STT_FUN) { return Error(GroundProgram, charptr, "A non-function variable already exists with this name"); } if (compareTypes(&existing->typeinfo, &node->as.type) == false) { return Error(GroundProgram, charptr, "Function already exists with a different type signature"); } } // Generate function signature GroundInstruction signature = groundCreateInstruction(FUN); node->accessArg = groundCreateReference(VALREF, fnName); groundAddReferenceToInstruction(&signature, groundCreateReference(FNREF, fnName)); 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) generateFunctionCallNode(SolsNode* node, SolsScope* scope) { // Check whether the function exists and is callable ResultType(SolsType, charptr) type = getNodeType(node, scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } SolsVariable* var = findSolsVariable(scope, node->as.idName); if (var == NULL) { return Error(GroundProgram, charptr, "Could not find variable"); } // Ensure the argument types match the function types if (node->children.count != var->typeinfo.children.count) { return Error(GroundProgram, charptr, "Incorrect amount of arguments for function"); } for (size_t i = 0; i < node->children.count; i++) { ResultType(SolsType, charptr) type = getNodeType(&node->children.at[i], scope); if (type.error) { return Error(GroundProgram, charptr, type.as.error); } if (compareTypes(&type.as.success, &var->typeinfo.children.at[i].type) == false) { return Error(GroundProgram, charptr, "Types incorrect for function call"); } } // Now that everything seems to be fine, call the function GroundInstruction gi = groundCreateInstruction(CALL); groundAddReferenceToInstruction(&gi, groundCreateReference(FNREF, node->as.idName)); for (size_t i = 0; i < node->children.count; i++) { groundAddReferenceToInstruction(&gi, node->children.at[i].accessArg); } char* returnStr = malloc(sizeof(char) * 64); if (returnStr == NULL) { return Error(GroundProgram, charptr, "Failed to allocate memory for tmp identifier"); } snprintf(returnStr, 64, "__SOLS_TMP_CALL_%zu", scope->tmpCounter++); groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, returnStr)); GroundProgram program = groundCreateProgram(); groundAddInstructionToProgram(&program, gi); node->accessArg = groundCreateReference(VALREF, returnStr); return Success(GroundProgram, charptr, program); } 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 && node->type != SNT_DEF) { if (node->type == SNT_CODE_BLOCK) { backupScope = *scope; SolsScope newScope = copySolsScope(scope); *scope = newScope; } // Generate code for all children before generating this node's code for (size_t i = 0; i < node->children.count; i++) { ResultType(GroundProgram, charptr) generated = generateCode(&node->children.at[i], scope); if (generated.error) { return Error(GroundProgram, charptr, createCodegenError(&node->children.at[i], generated.as.error)); } for (size_t j = 0; j < generated.as.success.size; j++) { groundAddInstructionToProgram(&program, generated.as.success.instructions[j]); } } if (node->type == SNT_CODE_BLOCK) { destroySolsScope(scope); *scope = backupScope; } } // Now generate code for this node switch (node->type) { case SNT_PUTS: generate(Puts); case SNT_LITERAL: generate(Literal); case SNT_OP_SET: generate(Set); case SNT_OP_ADD: generate(Add); case SNT_OP_SUB: generate(Sub); case SNT_OP_MUL: generate(Mul); case SNT_OP_DIV: generate(Div); case SNT_OP_EQUAL: generate(Equal); case SNT_OP_INEQUAL: generate(Inequal); case SNT_OP_GREATER: generate(Greater); case SNT_OP_EQGREATER: generate(EqGreater); case SNT_OP_LESSER: generate(Lesser); case SNT_OP_EQLESSER: generate(EqLesser); case SNT_CODE_BLOCK: generate(CodeBlock); case SNT_IF: generate(If); case SNT_WHILE: generate(While); case SNT_LAMBDA: generate(Lambda); case SNT_DEF: generate(Def); case SNT_FUNCTION_CALL: generate(FunctionCall); } return Success(GroundProgram, charptr, program); }