This repository has been archived on 2026-03-01. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
v2/src/codegen/codegen.c

928 lines
38 KiB
C

#include "codegen.h"
#include "SolsScope.h"
#include <groundvm.h>
#include <stdbool.h>
#include <stdio.h>
#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(&not, groundCreateReference(VALREF, tmpId));
groundAddReferenceToInstruction(&not, 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(&not, groundCreateReference(VALREF, tmpId));
groundAddReferenceToInstruction(&not, 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(&not_inst, node->children.at[0].accessArg);
groundAddReferenceToInstruction(&not_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(&not_inst, node->children.at[0].accessArg);
groundAddReferenceToInstruction(&not_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);
}