diff --git a/src/codegen/SolsScope.c b/src/codegen/SolsScope.c deleted file mode 100644 index 5865c97..0000000 --- a/src/codegen/SolsScope.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "SolsScope.h" - -#include "../include/uthash.h" -#include "../lexer/SolsType.h" - -void addVariableToScope(SolsScope* scope, const char* name, SolsType type) { - SolsVariable* s = malloc(sizeof(SolsVariable)); - - strncpy(s->id, name, sizeof(s->id) - 1); - s->id[sizeof(s->id) - 1] = '\0'; - - s->typeinfo = ({ - ResultType(SolsType, charptr) _result = copySolsType(&type); - if (_result.error) { - return; - } - _result.as.success; - }); - - HASH_ADD_STR(scope->variables, id, s); -} - -SolsVariable* findSolsVariable(SolsScope* scope, const char* name) { - if (scope == NULL || scope->variables == NULL || name == NULL) { - return NULL; - } - SolsVariable* s; - HASH_FIND_STR(scope->variables, name, s); - return s; -} - -SolsScope copySolsScope(SolsScope* scope) { - SolsScope newScope = { - .variables = NULL, - .tmpCounter = scope->tmpCounter, - .returnType = scope->returnType - }; - - SolsVariable *var, *tmp; - - HASH_ITER(hh, scope->variables, var, tmp) { - addVariableToScope(&newScope, var->id, var->typeinfo); - } - - return newScope; -} - -void destroySolsScope(SolsScope* scope) { - SolsVariable *var, *tmp; - - HASH_ITER(hh, scope->variables, var, tmp) { - HASH_DEL(scope->variables, var); - free(var); - } -} diff --git a/src/codegen/SolsScope.h b/src/codegen/SolsScope.h deleted file mode 100644 index 1f4d12e..0000000 --- a/src/codegen/SolsScope.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef SOLSSCOPE_H -#define SOLSSCOPE_H - -#include "../include/uthash.h" -#include "../lexer/SolsType.h" - -// Stores type information for variables in a UTHash table. -typedef struct SolsVariable { - char id[256]; - UT_hash_handle hh; - SolsType typeinfo; -} SolsVariable; - -typedef struct SolsScope { - SolsVariable* variables; - size_t tmpCounter; - SolsType returnType; -} SolsScope; - -// Adds a variable to the SolsScope. -void addVariableToScope(SolsScope* scope, const char* name, SolsType type); - -// Finds a variable in the SolsScope. -SolsVariable* findSolsVariable(SolsScope* scope, const char* name); - -// Deep copies a SolsScope, usually for being inside a code block -SolsScope copySolsScope(SolsScope* scope); - -// Destroys everything in the SolsScope -void destroySolsScope(SolsScope* scope); - -#endif diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c deleted file mode 100644 index f074cc6..0000000 --- a/src/codegen/codegen.c +++ /dev/null @@ -1,1459 +0,0 @@ -#include "codegen.h" - -#include "SolsScope.h" - -#include -#include -#include -#include - -#include "../lexer/lexer.h" -#include "../lexer/SolsType.h" -#include "../parser/parser.h" -#include "../parser/SolsNode.h" -#include "../typeparser/typeparser.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; -} - -static ResultType(SolsType, charptr) resolveValueType(SolsType* type, SolsScope* scope) { - if (type == NULL) { - return Error(SolsType, charptr, "Type is null"); - } - if (type->identifierType == NULL) { - return Success(SolsType, charptr, *type); - } - - if (type->type != STT_UNKNOWN && - !(type->type == STT_OBJECT && type->children.count == 0)) { - return Success(SolsType, charptr, *type); - } - - SolsVariable* var = findSolsVariable(scope, type->identifierType); - if (var == NULL) { - Estr estr = CREATE_ESTR("Unable to find type "); - APPEND_ESTR(estr, type->identifierType); - return Error(SolsType, charptr, estr.str); - } - - SolsType resolved = var->typeinfo; - if (resolved.type == STT_TEMPLATE) { - resolved.type = STT_OBJECT; - } - - return Success(SolsType, charptr, resolved); -} - -ResultType(SolsType, charptr) getNodeType(SolsNode* node, SolsScope* scope) { - switch (node->type) { - case SNT_PUTS: - case SNT_IF: - case SNT_WHILE: - case SNT_CODE_BLOCK: - case SNT_STRUCT: - case SNT_RETURN: { - 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 resolveValueType(&var->typeinfo, scope); - } - case SNT_LAMBDA: - case SNT_DEF: { - return Success(SolsType, charptr, node->as.type); - } - case SNT_FUNCTION_CALL: { - ResultType(SolsType, charptr) type = getNodeType(&node->children.at[0], scope); - //SolsVariable* var = findSolsVariable(scope, node->as.idName); - if (type.error) { - Estr estr = CREATE_ESTR("Unable to find function "); - if (node->children.at[0].type == SNT_IDENTIFIER) { - APPEND_ESTR(estr, node->children.at[0].as.idName); - } - return Error(SolsType, charptr, estr.str); - } - if (type.as.success.type != STT_FUN) { - if (type.as.success.type == STT_TEMPLATE) { - SolsType* constructor = NULL; - for (size_t i = 0; i < type.as.success.children.count; i++) { - if (strcmp(type.as.success.children.at[i].name, "constructor") == 0) { - constructor = &type.as.success.children.at[i].type; - break; - } - } - if (constructor != NULL) { - return Success(SolsType, charptr, *constructor->returnType); - } - } - Estr estr = CREATE_ESTR(node->as.idName); - APPEND_ESTR(estr, " is not a callable function"); - return Error(SolsType, charptr, estr.str); - } - return resolveValueType(type.as.success.returnType, scope); - } - case SNT_EXPR_IN_PAREN: { - return getNodeType(&node->children.at[node->children.count - 1], scope); - } - case SNT_NEW: { - SolsType type; - if (node->as.type.typeIsKnown) { - type = node->as.type; - } else { - SolsVariable* var = findSolsVariable(scope, node->as.type.identifierType); - if (var == NULL) { - Estr estr = CREATE_ESTR("Unable to find variable "); - if (node->as.type.identifierType != NULL) { - APPEND_ESTR(estr, node->as.type.identifierType); - } - return Error(SolsType, charptr, estr.str); - } - type = var->typeinfo; - } - if (type.type == STT_OBJECT) { - return Error(SolsType, charptr, "Cannot use initialized type on new"); - } - type.type = STT_OBJECT; - return Success(SolsType, charptr, type); - } - case SNT_DOT: { - ResultType(SolsType, charptr) type = getNodeType(&node->children.at[0], scope); - if (type.error) { - return Error(SolsType, charptr, type.as.error); - } - if (type.as.success.type != STT_OBJECT) { - return Error(SolsType, charptr, "Cannot use dot operator on non-object"); - } - ResultType(SolsType, charptr) child = findStructMemberType(&type.as.success, node->children.at[1].as.idName); - if (child.error) { - return child; - } - if (child.as.success.metadata.isPrivate) { - return Error(SolsType, charptr, "Member is private, and cannot be accessed outside of object methods"); - } - return child; - } - default: break; - } - 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) { - 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); - } else if (node->children.at[0].type == SNT_DOT) { - ResultType(SolsType, charptr) ltype = getNodeType(&node->children.at[0].children.at[0], scope); - if (ltype.error) { - return Error(GroundProgram, charptr, ltype.as.error); - } - ResultType(SolsType, charptr) rtype = getNodeType(&node->children.at[1], scope); - if (rtype.error) { - return Error(GroundProgram, charptr, rtype.as.error); - } - ResultType(SolsType, charptr) type = findStructMemberType(<ype.as.success, node->children.at[0].children.at[1].as.idName); - if (type.error) { - return Error(GroundProgram, charptr, type.as.error); - } - if (type.as.success.metadata.isPrivate) { - return Error(GroundProgram, charptr, "Member is private, and cannot be accessed outside of object methods"); - } - if (type.as.success.metadata.isProtected) { - return Error(GroundProgram, charptr, "Member is protected, and cannot be modified outside of object methods"); - } - if (compareTypes(&type.as.success, &rtype.as.success) == false) { - return Error(GroundProgram, charptr, "Type of member cannot be changed"); - } - - // TODO codegen for nested types - - GroundProgram gp = groundCreateProgram(); - GroundInstruction gi = groundCreateInstruction(SETFIELD); - groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, node->children.at[0].children.at[0].accessArg.value.refName)); - groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, node->children.at[0].children.at[1].as.idName)); - groundAddReferenceToInstruction(&gi, node->children.at[1].accessArg); - groundAddInstructionToProgram(&gp, gi); - return Success(GroundProgram, charptr, gp); - } else { - return Error(GroundProgram, charptr, "set requires an identifier before '='"); - } -} - -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()); -} - -ResultType(GroundProgram, charptr) generateExprInParenNode(SolsNode* node, SolsScope* scope) { - // All that needs to be done is to return the child's return id - node->accessArg = node->children.at[node->children.count - 1].accessArg; - (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, scope); - 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, scope); - 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 - SolsScope lambdaScope = copySolsScope(scope); - // Set the scope's return type - lambdaScope.returnType = *node->as.type.returnType; - - for (size_t i = 0; i < node->as.type.children.count; i++) { - ResultType(SolsType, charptr) resolvedType = resolveValueType(&node->as.type.children.at[i].type, scope); - if (resolvedType.error) { - return Error(GroundProgram, charptr, resolvedType.as.error); - } - addVariableToScope(&lambdaScope, node->as.type.children.at[i].name, resolvedType.as.success); - } - - // 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, scope); - 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, scope); - 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); - - // Set the scope's return type - ResultType(SolsType, charptr) resolvedReturnType = resolveValueType(node->as.type.returnType, scope); - if (resolvedReturnType.error) { - return Error(GroundProgram, charptr, resolvedReturnType.as.error); - } - functionScope.returnType = resolvedReturnType.as.success; - - for (size_t i = 0; i < node->as.type.children.count; i++) { - ResultType(SolsType, charptr) resolvedType = resolveValueType(&node->as.type.children.at[i].type, scope); - if (resolvedType.error) { - return Error(GroundProgram, charptr, resolvedType.as.error); - } - addVariableToScope(&functionScope, node->as.type.children.at[i].name, resolvedType.as.success); - } - - // 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->children.at[0], scope); - if (type.error) { - return Error(GroundProgram, charptr, type.as.error); - } - - char* oldId = NULL; - - if (type.as.success.type == STT_TEMPLATE) { - SolsType* constructor = NULL; - for (size_t i = 0; i < type.as.success.children.count; i++) { - if (strcmp(type.as.success.children.at[i].name, "constructor") == 0) { - constructor = &type.as.success.children.at[i].type; - break; - } - } - if (constructor == NULL) { - return Error(GroundProgram, charptr, "Template type does not have a constructor"); - } - type.as.success = *constructor; - - node->children.at[0].type = SNT_IDENTIFIER; - oldId = node->children.at[0].as.idName; - Estr idName = CREATE_ESTR(node->children.at[0].as.idName); - APPEND_ESTR(idName, "_SOLS_CONSTRUCTOR"); - node->children.at[0].as.idName = idName.str; - node->children.at[0].accessArg = groundCreateReference(FNREF, idName.str); - } - - size_t argCount = node->children.count - 1; - - // Ensure the argument types match the function types - if (node->children.count - 1 != type.as.success.children.count) { - return Error(GroundProgram, charptr, "Incorrect amount of arguments for function"); - } - - for (size_t i = 1; 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, &type.as.success) == false) { - return Error(GroundProgram, charptr, "Types incorrect for function call"); - } - } - - - // Now that everything seems to be fine, call the function - GroundInstruction gi; - - // If we're calling a method, use the instruction for that - if (node->children.at[0].type == SNT_DOT) { - gi = groundCreateInstruction(CALLMETHOD); - groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, node->children.at[0].children.at[0].accessArg.value.refName)); - groundAddReferenceToInstruction(&gi, groundCreateReference(FNREF, node->children.at[0].children.at[1].as.idName)); - } else { - gi = groundCreateInstruction(CALL); - groundAddReferenceToInstruction(&gi, groundCreateReference(FNREF, node->children.at[0].accessArg.value.refName)); - } - - for (size_t i = 1; 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); - - if (oldId != NULL) { - node->children.at[0].as.idName = oldId; - } - - return Success(GroundProgram, charptr, program); - -} - -ResultType(GroundProgram, charptr) generateReturnNode(SolsNode* node, SolsScope* scope) { - if (node->children.count < 1) { - return Error(GroundProgram, charptr, "Expecting a value in return"); - } - - ResultType(SolsType, charptr) type = getNodeType(&node->children.at[0], scope); - if (type.error) { - return Error(GroundProgram, charptr, type.as.error); - } - - if (compareTypes(&type.as.success, &scope->returnType) == false) { - return Error(GroundProgram, charptr, "Mismatched return type"); - } - - GroundProgram program = groundCreateProgram(); - GroundInstruction inst = groundCreateInstruction(RETURN); - - groundAddReferenceToInstruction(&inst, node->children.at[0].accessArg); - - groundAddInstructionToProgram(&program, inst); - - return Success(GroundProgram, charptr, program); -} - -// For generateUseNode function, implemented in src/main.c -char* getFileContents(const char* filename); - -ResultType(GroundProgram, charptr) generateUseNode(SolsNode* node, SolsScope* scope) { - char* libraryName = node->as.idName; - char* libPath = getenv("SOLSTICE_LIBS"); - if (libPath == NULL) { - libPath = "/usr/lib/solstice"; - } - - Estr filePath = CREATE_ESTR(libPath); - APPEND_ESTR(filePath, "/"); - APPEND_ESTR(filePath, libraryName); - APPEND_ESTR(filePath, ".sols"); - - // Steal the main function for use here - char* file = getFileContents(filePath.str); - if (file == NULL) { - return Error(GroundProgram, charptr, "Couldn't read contents of imported file"); - } - ResultType(SolsLexer, charptr) lexer = createLexer(file); - if (lexer.error) { - printf("While lexing file %s:\n", filePath.str); - printf("Error while creating lexer: %s", lexer.as.error); - return Error(GroundProgram, charptr, "Error with use"); - } - ResultType(Nothing, charptr) lexed = lex(&lexer.as.success); - if (lexed.error) { - printf("While lexing file %s:\n", filePath.str); - printf("%s\n", lexed.as.error); - return Error(GroundProgram, charptr, "Error with use"); - } - - // Detect and parse types - ResultType(SolsTokens, charptr) typed = addTypeInfo(&lexer.as.success.output); - if (typed.error) { - printf("While parsing types in file %s:\n", filePath.str); - printf("%s\n", typed.as.error); - return Error(GroundProgram, charptr, "Error with use"); - } - - // Parse file - ResultType(SolsParser, charptr) parser = createSolsParser(&typed.as.success); - if (parser.error) { - printf("While parsing file %s:\n", filePath.str); - printf("Error while creating parser: %s\n", parser.as.error); - return Error(GroundProgram, charptr, "Error with use"); - } - ResultType(Nothing, charptr) parsed = parse(&parser.as.success); - if (parsed.error) { - printf("While parsing file %s:\n", filePath.str); - printf("%s\n", parsed.as.error); - return Error(GroundProgram, charptr, "Error with use"); - } - - SolsScope newScope = { - .variables = NULL, - .tmpCounter = 0, - .returnType = createSolsType(STT_INT).as.success - }; - - // Do codegen on root node - ResultType(GroundProgram, charptr) codegen = generateCode(&parser.as.success.output, &newScope); - if (codegen.error) { - printf("While generating code for file %s:\n", filePath.str); - printf("%s\n", codegen.as.error); - return Error(GroundProgram, charptr, "Error with use"); - } - - // Insert all the stuff into our scope - SolsVariable *var, *tmp; - HASH_ITER(hh, newScope.variables, var, tmp) { - addVariableToScope(scope, var->id, var->typeinfo); - } - - return Success(GroundProgram, charptr, codegen.as.success); -} - -ResultType(GroundProgram, charptr) generateInlineGroundNode(SolsNode* node, SolsScope* scope) { - return Success(GroundProgram, charptr, groundParseFile(node->as.inlineGround)); -} - -ResultType(GroundProgram, charptr) generateStructNode(SolsNode* node, SolsScope* scope) { - SolsType type = ({ - ResultType(SolsType, charptr) type = createSolsType(STT_TEMPLATE); - if (type.error) { - return Error(GroundProgram, charptr, type.as.error); - } - type.as.success; - }); - SolsNode* constructor = NULL; - GroundProgram constants = groundCreateProgram(); - GroundProgram structDef = groundCreateProgram(); - GroundInstruction structDefInst = groundCreateInstruction(STRUCT); - groundAddReferenceToInstruction(&structDefInst, groundCreateReference(TYPEREF, node->as.idName)); - groundAddInstructionToProgram(&structDef, structDefInst); - for (size_t i = 0; i < node->children.count; i++) { - bool private = false; - bool protected = false; - // Add to type for type system - SolsNode* child = &node->children.at[i]; - - if (child->type == SNT_DEF_PRIVATE) { - private = true; - child->type = SNT_DEF; - } - if (child->type == SNT_SET_PRIVATE) { - private = true; - child->type = SNT_OP_SET; - } - if (child->type == SNT_DEF_PROTECTED) { - protected = true; - child->type = SNT_DEF; - } - if (child->type == SNT_SET_PROTECTED) { - protected = true; - child->type = SNT_OP_SET; - } - - if (child->type == SNT_DEF) { - ResultType(SolsType, charptr) childType = getNodeType(child, scope); - if (childType.error) { - return Error(GroundProgram, charptr, childType.as.error); - } - - childType.as.success.metadata.isPrivate = private; - childType.as.success.metadata.isProtected = protected; - - addChildToSolsType(&type, childType.as.success, child->children.at[0].as.idName); - - // Add struct members to new scope - SolsScope childScope = copySolsScope(scope); - - SolsType selfType = ({ - ResultType(SolsType, charptr) _result = copySolsType(&type); - if (_result.error) { - return Error(GroundProgram, charptr, _result.as.error); - } - _result.as.success; - }); - selfType.type = STT_OBJECT; - - addVariableToScope(&childScope, "self", selfType); - - // Generate the def node and add to struct - ResultType(GroundProgram, charptr) defNode = generateDefNode(child, &childScope); - if (defNode.error) { - return Error(GroundProgram, charptr, defNode.as.error); - } - - for (size_t j = 0; j < defNode.as.success.size; j++) { - groundAddInstructionToProgram(&structDef, defNode.as.success.instructions[j]); - } - - continue; - } else if (child->type == SNT_CONSTRUCTOR) { - constructor = child; - continue; - } else if (child->type == SNT_DESTRUCTOR) { - GroundInstruction destructorInst = groundCreateInstruction(FUN); - groundAddReferenceToInstruction(&destructorInst, groundCreateReference(FNREF, "destructor")); - groundAddReferenceToInstruction(&destructorInst, groundCreateReference(TYPEREF, "any")); - groundAddInstructionToProgram(&structDef, destructorInst); - - SolsScope childScope = copySolsScope(scope); - - SolsType selfType = ({ - ResultType(SolsType, charptr) _result = copySolsType(&type); - if (_result.error) { - return Error(GroundProgram, charptr, _result.as.error); - } - _result.as.success; - }); - selfType.type = STT_OBJECT; - - addVariableToScope(&childScope, "self", selfType); - - ResultType(GroundProgram, charptr) bodyCode = generateCode(child, &childScope); - if (bodyCode.error) return bodyCode; - for (size_t j = 0; j < bodyCode.as.success.size; j++) { - groundAddInstructionToProgram(&structDef, bodyCode.as.success.instructions[j]); - } - groundAddInstructionToProgram(&structDef, groundCreateInstruction(ENDFUN)); - continue; - } - ResultType(SolsType, charptr) childType = getNodeType(&child->children.at[1], scope); - if (childType.error) { - return Error(GroundProgram, charptr, childType.as.error); - } - childType.as.success.metadata.isPrivate = private; - childType.as.success.metadata.isProtected = protected; - addChildToSolsType(&type, childType.as.success, child->children.at[0].as.idName); - - // Generate constant initial value - ResultType(GroundProgram, charptr) childCode = generateCode(&child->children.at[1], scope); - if (childCode.error) { - return Error(GroundProgram, charptr, childCode.as.error); - } - for (size_t j = 0; j < childCode.as.success.size; j++) { - groundAddInstructionToProgram(&constants, childCode.as.success.instructions[j]); - } - - // Add to struct - GroundInstruction structInst = groundCreateInstruction(SET); - groundAddReferenceToInstruction(&structInst, groundCreateReference(DIRREF, child->children.at[0].as.idName)); - groundAddReferenceToInstruction(&structInst, child->children.at[1].accessArg); - groundAddInstructionToProgram(&structDef, structInst); - } - groundAddInstructionToProgram(&structDef, groundCreateInstruction(ENDSTRUCT)); - - // Construct constructor - if (constructor != NULL) { - GroundInstruction constructorInst = groundCreateInstruction(FUN); - Estr constructorName = CREATE_ESTR(node->as.idName); - APPEND_ESTR(constructorName, "_SOLS_CONSTRUCTOR"); - groundAddReferenceToInstruction(&constructorInst, groundCreateReference(FNREF, constructorName.str)); - groundAddReferenceToInstruction(&constructorInst, groundCreateReference(TYPEREF, node->as.idName)); - for (size_t i = 0; i < constructor->as.type.children.count; i++) { - ResultType(GroundArg, charptr) arg = createGroundArgFromSolsType(&constructor->as.type.children.at[i].type, scope); - if (arg.error) { - return Error(GroundProgram, charptr, arg.as.error); - } - groundAddReferenceToInstruction(&constructorInst, arg.as.success); - groundAddReferenceToInstruction(&constructorInst, groundCreateReference(DIRREF, constructor->as.type.children.at[i].name)); - } - groundAddInstructionToProgram(&structDef, constructorInst); - - GroundInstruction constructorSelf = groundCreateInstruction(INIT); - groundAddReferenceToInstruction(&constructorSelf, groundCreateReference(DIRREF, "self")); - groundAddReferenceToInstruction(&constructorSelf, groundCreateReference(TYPEREF, node->as.idName)); - groundAddInstructionToProgram(&structDef, constructorSelf); - - SolsScope childScope = copySolsScope(scope); - addVariableToScope(&childScope, "self", type); - - // Modify self so that all members are not private or protected - SolsVariable* variable = findSolsVariable(&childScope, "self"); - if (variable == NULL) { - return Error(GroundProgram, charptr, "Failed to find self variable (this should never happen)"); - } - for (size_t i = 0; i < variable->typeinfo.children.count; i++) { - variable->typeinfo.children.at[i].type.metadata.isPrivate = false; - variable->typeinfo.children.at[i].type.metadata.isProtected = false; - } - - for (size_t i = 0; i < constructor->as.type.children.count; i++) { - addVariableToScope(&childScope, constructor->as.type.children.at[i].name, constructor->as.type.children.at[i].type); - } - - ResultType(GroundProgram, charptr) constructorCode = generateCode(&constructor->children.at[0], &childScope); - if (constructorCode.error) { - return Error(GroundProgram, charptr, constructorCode.as.error); - } - for (size_t i = 0; i < constructorCode.as.success.size; i++) { - groundAddInstructionToProgram(&structDef, constructorCode.as.success.instructions[i]); - } - GroundInstruction ret = groundCreateInstruction(RETURN); - groundAddReferenceToInstruction(&ret, groundCreateReference(VALREF, "self")); - groundAddInstructionToProgram(&structDef, ret); - groundAddInstructionToProgram(&structDef, groundCreateInstruction(ENDFUN)); - - *constructor->as.type.returnType = type; - constructor->as.type.returnType->type = STT_OBJECT; - - addChildToSolsType(&type, constructor->as.type, "constructor"); - } - - // Add to scope - addVariableToScope(scope, node->as.idName, type); - - // Combine into one program - for (size_t i = 0; i < structDef.size; i++) { - groundAddInstructionToProgram(&constants, structDef.instructions[i]); - } - - - return Success(GroundProgram, charptr, constants); -} - -ResultType(GroundProgram, charptr) generateNewNode(SolsNode* node, SolsScope* scope) { - GroundProgram program = groundCreateProgram(); - GroundInstruction inst = groundCreateInstruction(INIT); - ResultType(GroundArg, charptr) arg = createGroundArgFromSolsType(&node->as.type, scope); - if (arg.error) { - return Error(GroundProgram, charptr, arg.as.error); - } - char* tmpId = malloc(sizeof(char) * 64); - if (tmpId == NULL) { - return Error(GroundProgram, charptr, "Failed to allocate memory for tmp identifier"); - } - snprintf(tmpId, 64, "__SOLS_TMP_NEW_%zu", scope->tmpCounter++); - node->accessArg = groundCreateReference(VALREF, tmpId); - groundAddReferenceToInstruction(&inst, groundCreateReference(DIRREF, tmpId)); - groundAddReferenceToInstruction(&inst, arg.as.success); - groundAddInstructionToProgram(&program, inst); - return Success(GroundProgram, charptr, program); -} - -ResultType(GroundProgram, charptr) generateDotNode(SolsNode* node, SolsScope* scope) { - GroundProgram program = groundCreateProgram(); - GroundInstruction inst = groundCreateInstruction(GETFIELD); - groundAddReferenceToInstruction(&inst, node->children.at[0].accessArg); - groundAddReferenceToInstruction(&inst, groundCreateReference(DIRREF, node->children.at[1].as.idName)); - char* tmpId = malloc(sizeof(char) * 64); - if (tmpId == NULL) { - return Error(GroundProgram, charptr, "Failed to allocate memory for tmp identifier"); - } - snprintf(tmpId, 64, "__SOLS_TMP_DOT_%zu", scope->tmpCounter++); - node->accessArg = groundCreateReference(VALREF, tmpId); - groundAddReferenceToInstruction(&inst, groundCreateReference(DIRREF, tmpId)); - groundAddInstructionToProgram(&program, inst); - 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 && node->type != SNT_STRUCT && node->type != SNT_NEW) { - 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++) { - if (node->type == SNT_OP_SET && i == 0) { - continue; // Don't evaluate the left of set, just the right - } - 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_EXPR_IN_PAREN: generate(ExprInParen); - 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); - case SNT_RETURN: generate(Return); - case SNT_USE: generate(Use); - case SNT_GROUND: generate(InlineGround); - case SNT_STRUCT: generate(Struct); - case SNT_NEW: generate(New); - case SNT_DOT: generate(Dot); - } - return Success(GroundProgram, charptr, program); -} diff --git a/src/codegen/codegen.h b/src/codegen/codegen.h deleted file mode 100644 index fa98498..0000000 --- a/src/codegen/codegen.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef CODEGEN_H -#define CODEGEN_H - -#include - -#include "SolsScope.h" - -#include "../parser/SolsNode.h" - -Result(GroundProgram, charptr); - -// Generates a GroundProgram (from the Ground VM header) from -// a provided SolsNode. -// Returns: -// Success: Generated GroundProgram -// Failure: charptr detailing what happened -ResultType(GroundProgram, charptr) generateCode(SolsNode* node, SolsScope* scope); - -// Gets the type of a node generated by the parser for the type checker. -ResultType(SolsType, charptr) getNodeType(SolsNode* node, SolsScope* scope); - -// Macro to help with code generation (and soon error handling) -#define generate(nodetype) {\ - ResultType(GroundProgram, charptr) __result = generate##nodetype##Node(node, scope);\ - if (__result.error) {\ - return Error(GroundProgram, charptr, __result.as.error);\ - }\ - for (size_t i = 0; i < __result.as.success.size; i++) {\ - groundAddInstructionToProgram(&program, __result.as.success.instructions[i]);\ - }\ - break;\ -} - -#endif diff --git a/src/include/ansii.h b/src/include/ansii.h deleted file mode 100644 index fc40739..0000000 --- a/src/include/ansii.h +++ /dev/null @@ -1,67 +0,0 @@ -// ansii.h - made by SpookyDervish -// version 1.0.0 -// do with this whatever you want -// -// example usage with printf: printf(ESC_BOLD ESC_RED_FG "hi\n"); - -#ifndef ANSII_H -#define ANSII_H - -#define ESC_RESET "\x1b[0m" -#define ESC_BOLD "\x1b[1m" -#define ESC_DIM "\x1b[2m" -#define ESC_ITALIC "\x1b[3m" -#define ESC_UNDERLINE "\x1b[4m" -#define ESC_BLINKING "\x1b[5m" -#define ESC_REVERSE "\x1b[7m" -#define ESC_HIDDEN "\x1b[8m" -#define ESC_STRIKETHROUGH "\x1b[8m" - -#define ESC_TERMINAL_BELL "\a" - -#define ESC_BLACK_FG "\x1b[30m" -#define ESC_RED_FG "\x1b[31m" -#define ESC_GREEN_FG "\x1b[32m" -#define ESC_YELLOW_FG "\x1b[33m" -#define ESC_BLUE_FG "\x1b[34m" -#define ESC_MAGENTA_FG "\x1b[35m" -#define ESC_CYAN_FG "\x1b[36m" -#define ESC_WHITE_FG "\x1b[37m" - -#define ESC_BLACK_FG "\x1b[30m" -#define ESC_RED_FG "\x1b[31m" -#define ESC_GREEN_FG "\x1b[32m" -#define ESC_YELLOW_FG "\x1b[33m" -#define ESC_BLUE_FG "\x1b[34m" -#define ESC_MAGENTA_FG "\x1b[35m" -#define ESC_CYAN_FG "\x1b[36m" -#define ESC_WHITE_FG "\x1b[37m" -#define ESC_BRIGHT_BLACK_FG "\x1b[90m" -#define ESC_BRIGHT_RED_FG "\x1b[91m" -#define ESC_BRIGHT_GREEN_FG "\x1b[92m" -#define ESC_BRIGHT_YELLOW_FG "\x1b[93m" -#define ESC_BRIGHT_BLUE_FG "\x1b[94m" -#define ESC_BRIGHT_MAGENTA_FG "\x1b[95m" -#define ESC_BRIGHT_CYAN_FG "\x1b[96m" -#define ESC_BRIGHT_WHITE_FG "\x1b[97m" - -#define ESC_BLACK_BG "\x1b[40m" -#define ESC_RED_BG "\x1b[41m" -#define ESC_GREEN_BG "\x1b[42m" -#define ESC_YELLOW_BG "\x1b[43m" -#define ESC_BLUE_BG "\x1b[44m" -#define ESC_MAGENTA_BG "\x1b[45m" -#define ESC_CYAN_BG "\x1b[46m" -#define ESC_WHITE_BG "\x1b[47m" -#define ESC_BRIGHT_BLACK_BG "\x1b[100m" -#define ESC_BRIGHT_RED_BG "\x1b[101m" -#define ESC_BRIGHT_GREEN_BG "\x1b[102m" -#define ESC_BRIGHT_YELLOW_BG "\x1b[103m" -#define ESC_BRIGHT_BLUE_BG "\x1b[104m" -#define ESC_BRIGHT_MAGENTA_BG "\x1b[105m" -#define ESC_BRIGHT_CYAN_BG "\x1b[106m" -#define ESC_BRIGHT_WHITE_BG "\x1b[107m" - -#define ESC_DEFAULT_FG "\x1b[39m" - -#endif // !ANSII_H \ No newline at end of file diff --git a/src/include/error.h b/src/include/error.h deleted file mode 100644 index eb78829..0000000 --- a/src/include/error.h +++ /dev/null @@ -1,83 +0,0 @@ -#include -#include - -#ifndef ERROR_H -#define ERROR_H - -/* - * error.h - First class errors for C - * Have you ever wanted to have a Rust-like error experience in C? - * Look no further than this library! Using a couple simple macros, - * we can emulate their complicated enum system, and I'd argue that - * we do it better. Besides, it's in a better programming language. - * - * Enjoy! - * - * Licenced to you under the MIT license - see below. -*/ - -/* - * Example usage: - * - * #include "error.h" - * #include - * - * // You can't write char*, you have to define it with a typedef - * typedef char* charptr; - * - * Result(int, charptr) myFn(int x) { - * if (x > 5) { - * return Error(int, charptr, "Your number is too big"); - * } - * return Success(int, charptr, x); - * } - * - * int main() { - * ResultType(int, charptr) res = myFn(10); - * if (res.error) { - * printf("Uh oh, error is: %s\n", res.as.error); - * } else { - * printf("Got a result! It is %d\n", res.as.success); - * } - * } - * - */ - -/* - * Copyright 2026 Maxwell Jeffress - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the “Software”), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. -*/ - -// Creates a new struct with the a (success) and b (error) types. -// If Result(a, b) has already been called with the same paramaters, please -// use ResultType(a, b) instead. -#define Result(a, b) struct __ResultType_##a##_##b { bool error; union {a success; b error;} as; } - -// Uses an existing Result(a, b) struct. -#define ResultType(a, b) struct __ResultType_##a##_##b - - -// Creates a __ResultType_a_b struct, with .error as false and .as.success as res. -#define Success(a, b, res) (ResultType(a, b)) { .error = false, .as.success = res } - -// Creates a __ResultType_a_b struct, with .error as true and .as.error as res. -#define Error(a, b, res) (ResultType(a, b)) { .error = true, .as.error = res } - -#endif diff --git a/src/include/estr.h b/src/include/estr.h deleted file mode 100644 index df5d172..0000000 --- a/src/include/estr.h +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include -#include - -#ifndef ESTR_H -#define ESTR_H - -/* - - estr.h - Easy string manipulation - This library has macros to allow easier manipulation of strings. No longer shall - you have to malloc and realloc away to keep adding to your strings. - - Usage: - - Estr myString = CREATE_ESTR("my awesome string"); - APPEND_ESTR(myString, " is so cool"); - printf("%s\n", myString.str); - -*/ - -#define CREATE_ESTR(instr) \ - (Estr) { \ - .str = instr,\ - .size = strlen(instr),\ - .shouldBeFreed = 0, \ - .destroyed = 0 \ - } - -#define APPEND_ESTR(estr, instr) { \ - estr.size = estr.size + strlen(instr); \ - char* tmp_ptr = malloc(estr.size + 1); \ - if (tmp_ptr == NULL) printf("WARNING: Could not realloc estr " #estr "\n"); \ - else { \ - snprintf(tmp_ptr, estr.size + 1, "%s%s", estr.str, instr); \ - if (estr.shouldBeFreed > 0) free(estr.str); \ - estr.shouldBeFreed = 1; \ - estr.str = tmp_ptr; \ - } \ -} - -#define DESTROY_ESTR(estr) if (estr.shouldBeFreed > 0 && estr.destroyed < 1) free(estr.str); - -typedef struct Estr { - char* str; - size_t size; - int8_t shouldBeFreed; - int8_t destroyed; -} Estr; - -#endif // ESTR_H diff --git a/src/include/nothing.h b/src/include/nothing.h deleted file mode 100644 index cfa91b7..0000000 --- a/src/include/nothing.h +++ /dev/null @@ -1,10 +0,0 @@ -// nothing.h - ever needed to return nothing (but not void)? -// boy do I have the solution for you - -#ifndef NOTHING_H -#define NOTHING_H - -// Behold, it is nothing! -typedef struct Nothing {} Nothing; - -#endif diff --git a/src/include/uthash.h b/src/include/uthash.h deleted file mode 100644 index 06c2eeb..0000000 --- a/src/include/uthash.h +++ /dev/null @@ -1,1137 +0,0 @@ -/* -Copyright (c) 2003-2025, Troy D. Hanson https://troydhanson.github.io/uthash/ -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef UTHASH_H -#define UTHASH_H - -#define UTHASH_VERSION 2.3.0 - -#include /* memcmp, memset, strlen */ -#include /* ptrdiff_t */ -#include /* exit */ - -#if defined(HASH_NO_STDINT) && HASH_NO_STDINT -/* The user doesn't have , and must figure out their own way - to provide definitions for uint8_t and uint32_t. */ -#else -#include /* uint8_t, uint32_t */ -#endif - -/* These macros use decltype or the earlier __typeof GNU extension. - As decltype is only available in newer compilers (VS2010 or gcc 4.3+ - when compiling c++ source) this code uses whatever method is needed - or, for VS2008 where neither is available, uses casting workarounds. */ -#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) -#if defined(_MSC_VER) /* MS compiler */ -#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ -#define DECLTYPE(x) (decltype(x)) -#else /* VS2008 or older (or VS2010 in C mode) */ -#define NO_DECLTYPE -#endif -#elif defined(__MCST__) /* Elbrus C Compiler */ -#define DECLTYPE(x) (__typeof(x)) -#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) -#define NO_DECLTYPE -#else /* GNU, Sun and other compilers */ -#define DECLTYPE(x) (__typeof(x)) -#endif -#endif - -#ifdef NO_DECLTYPE -#define DECLTYPE(x) -#define DECLTYPE_ASSIGN(dst,src) \ -do { \ - char **_da_dst = (char**)(&(dst)); \ - *_da_dst = (char*)(src); \ -} while (0) -#else -#define DECLTYPE_ASSIGN(dst,src) \ -do { \ - (dst) = DECLTYPE(dst)(src); \ -} while (0) -#endif - -#ifndef uthash_malloc -#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ -#endif -#ifndef uthash_free -#define uthash_free(ptr,sz) free(ptr) /* free fcn */ -#endif -#ifndef uthash_bzero -#define uthash_bzero(a,n) memset(a,'\0',n) -#endif -#ifndef uthash_strlen -#define uthash_strlen(s) strlen(s) -#endif - -#ifndef HASH_FUNCTION -#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv) -#endif - -#ifndef HASH_KEYCMP -#define HASH_KEYCMP(a,b,n) memcmp(a,b,n) -#endif - -#ifndef uthash_noexpand_fyi -#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ -#endif -#ifndef uthash_expand_fyi -#define uthash_expand_fyi(tbl) /* can be defined to log expands */ -#endif - -#ifndef HASH_NONFATAL_OOM -#define HASH_NONFATAL_OOM 0 -#endif - -#if HASH_NONFATAL_OOM -/* malloc failures can be recovered from */ - -#ifndef uthash_nonfatal_oom -#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ -#endif - -#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) -#define IF_HASH_NONFATAL_OOM(x) x - -#else -/* malloc failures result in lost memory, hash tables are unusable */ - -#ifndef uthash_fatal -#define uthash_fatal(msg) exit(-1) /* fatal OOM error */ -#endif - -#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") -#define IF_HASH_NONFATAL_OOM(x) - -#endif - -/* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ -#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ - -/* calculate the element whose hash handle address is hhp */ -#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) -/* calculate the hash handle from element address elp */ -#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho))) - -#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ -do { \ - struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ - unsigned _hd_bkt; \ - HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ - (head)->hh.tbl->buckets[_hd_bkt].count++; \ - _hd_hh_item->hh_next = NULL; \ - _hd_hh_item->hh_prev = NULL; \ -} while (0) - -#define HASH_VALUE(keyptr,keylen,hashv) \ -do { \ - HASH_FUNCTION(keyptr, keylen, hashv); \ -} while (0) - -#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ -do { \ - (out) = NULL; \ - if (head) { \ - unsigned _hf_bkt; \ - HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ - if (HASH_BLOOM_TEST((head)->hh.tbl, hashval)) { \ - HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ - } \ - } \ -} while (0) - -#define HASH_FIND(hh,head,keyptr,keylen,out) \ -do { \ - (out) = NULL; \ - if (head) { \ - unsigned _hf_hashv; \ - HASH_VALUE(keyptr, keylen, _hf_hashv); \ - HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ - } \ -} while (0) - -#ifdef HASH_BLOOM -#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) -#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) -#define HASH_BLOOM_MAKE(tbl,oomed) \ -do { \ - (tbl)->bloom_nbits = HASH_BLOOM; \ - (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ - if (!(tbl)->bloom_bv) { \ - HASH_RECORD_OOM(oomed); \ - } else { \ - uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ - (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ - } \ -} while (0) - -#define HASH_BLOOM_FREE(tbl) \ -do { \ - uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ -} while (0) - -#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) -#define HASH_BLOOM_BITTEST(bv,idx) ((bv[(idx)/8U] & (1U << ((idx)%8U))) != 0) - -#define HASH_BLOOM_ADD(tbl,hashv) \ - HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) - -#define HASH_BLOOM_TEST(tbl,hashv) \ - HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) - -#else -#define HASH_BLOOM_MAKE(tbl,oomed) -#define HASH_BLOOM_FREE(tbl) -#define HASH_BLOOM_ADD(tbl,hashv) -#define HASH_BLOOM_TEST(tbl,hashv) 1 -#define HASH_BLOOM_BYTELEN 0U -#endif - -#define HASH_MAKE_TABLE(hh,head,oomed) \ -do { \ - (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ - if (!(head)->hh.tbl) { \ - HASH_RECORD_OOM(oomed); \ - } else { \ - uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ - (head)->hh.tbl->tail = &((head)->hh); \ - (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ - (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ - (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ - (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ - HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ - (head)->hh.tbl->signature = HASH_SIGNATURE; \ - if (!(head)->hh.tbl->buckets) { \ - HASH_RECORD_OOM(oomed); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - } else { \ - uthash_bzero((head)->hh.tbl->buckets, \ - HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ - HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ - IF_HASH_NONFATAL_OOM( \ - if (oomed) { \ - uthash_free((head)->hh.tbl->buckets, \ - HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - } \ - ) \ - } \ - } \ -} while (0) - -#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ -do { \ - (replaced) = NULL; \ - HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ - if (replaced) { \ - HASH_DELETE(hh, head, replaced); \ - } \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ -} while (0) - -#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ -do { \ - (replaced) = NULL; \ - HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ - if (replaced) { \ - HASH_DELETE(hh, head, replaced); \ - } \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ -} while (0) - -#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ -do { \ - unsigned _hr_hashv; \ - HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ - HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ -} while (0) - -#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ -do { \ - unsigned _hr_hashv; \ - HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ - HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ -} while (0) - -#define HASH_APPEND_LIST(hh, head, add) \ -do { \ - (add)->hh.next = NULL; \ - (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ - (head)->hh.tbl->tail->next = (add); \ - (head)->hh.tbl->tail = &((add)->hh); \ -} while (0) - -#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ -do { \ - do { \ - if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ - break; \ - } \ - } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ -} while (0) - -#ifdef NO_DECLTYPE -#undef HASH_AKBI_INNER_LOOP -#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ -do { \ - char *_hs_saved_head = (char*)(head); \ - do { \ - DECLTYPE_ASSIGN(head, _hs_iter); \ - if (cmpfcn(head, add) > 0) { \ - DECLTYPE_ASSIGN(head, _hs_saved_head); \ - break; \ - } \ - DECLTYPE_ASSIGN(head, _hs_saved_head); \ - } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ -} while (0) -#endif - -#if HASH_NONFATAL_OOM - -#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ -do { \ - if (!(oomed)) { \ - unsigned _ha_bkt; \ - (head)->hh.tbl->num_items++; \ - HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ - HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ - if (oomed) { \ - HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ - HASH_DELETE_HH(hh, head, &(add)->hh); \ - (add)->hh.tbl = NULL; \ - uthash_nonfatal_oom(add); \ - } else { \ - HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ - HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ - } \ - } else { \ - (add)->hh.tbl = NULL; \ - uthash_nonfatal_oom(add); \ - } \ -} while (0) - -#else - -#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ -do { \ - unsigned _ha_bkt; \ - (head)->hh.tbl->num_items++; \ - HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ - HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ - HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ - HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ -} while (0) - -#endif - - -#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ -do { \ - IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ - (add)->hh.hashv = (hashval); \ - (add)->hh.key = (char*) (keyptr); \ - (add)->hh.keylen = (unsigned) (keylen_in); \ - if (!(head)) { \ - (add)->hh.next = NULL; \ - (add)->hh.prev = NULL; \ - HASH_MAKE_TABLE(hh, add, _ha_oomed); \ - IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ - (head) = (add); \ - IF_HASH_NONFATAL_OOM( } ) \ - } else { \ - void *_hs_iter = (head); \ - (add)->hh.tbl = (head)->hh.tbl; \ - HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ - if (_hs_iter) { \ - (add)->hh.next = _hs_iter; \ - if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ - HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ - } else { \ - (head) = (add); \ - } \ - HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ - } else { \ - HASH_APPEND_LIST(hh, head, add); \ - } \ - } \ - HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ - HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ -} while (0) - -#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ -do { \ - unsigned _hs_hashv; \ - HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ -} while (0) - -#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) - -#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ - HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) - -#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ -do { \ - IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ - (add)->hh.hashv = (hashval); \ - (add)->hh.key = (const void*) (keyptr); \ - (add)->hh.keylen = (unsigned) (keylen_in); \ - if (!(head)) { \ - (add)->hh.next = NULL; \ - (add)->hh.prev = NULL; \ - HASH_MAKE_TABLE(hh, add, _ha_oomed); \ - IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ - (head) = (add); \ - IF_HASH_NONFATAL_OOM( } ) \ - } else { \ - (add)->hh.tbl = (head)->hh.tbl; \ - HASH_APPEND_LIST(hh, head, add); \ - } \ - HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ - HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ -} while (0) - -#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ -do { \ - unsigned _ha_hashv; \ - HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ -} while (0) - -#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) - -#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ - HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) - -#define HASH_TO_BKT(hashv,num_bkts,bkt) \ -do { \ - bkt = ((hashv) & ((num_bkts) - 1U)); \ -} while (0) - -/* delete "delptr" from the hash table. - * "the usual" patch-up process for the app-order doubly-linked-list. - * The use of _hd_hh_del below deserves special explanation. - * These used to be expressed using (delptr) but that led to a bug - * if someone used the same symbol for the head and deletee, like - * HASH_DELETE(hh,users,users); - * We want that to work, but by changing the head (users) below - * we were forfeiting our ability to further refer to the deletee (users) - * in the patch-up process. Solution: use scratch space to - * copy the deletee pointer, then the latter references are via that - * scratch pointer rather than through the repointed (users) symbol. - */ -#define HASH_DELETE(hh,head,delptr) \ - HASH_DELETE_HH(hh, head, &(delptr)->hh) - -#define HASH_DELETE_HH(hh,head,delptrhh) \ -do { \ - const struct UT_hash_handle *_hd_hh_del = (delptrhh); \ - if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ - HASH_BLOOM_FREE((head)->hh.tbl); \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - (head) = NULL; \ - } else { \ - unsigned _hd_bkt; \ - if (_hd_hh_del == (head)->hh.tbl->tail) { \ - (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ - } \ - if (_hd_hh_del->prev != NULL) { \ - HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ - } else { \ - DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ - } \ - if (_hd_hh_del->next != NULL) { \ - HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ - } \ - HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ - HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ - (head)->hh.tbl->num_items--; \ - } \ - HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ -} while (0) - -/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ -#define HASH_FIND_STR(head,findstr,out) \ -do { \ - unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ - HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ -} while (0) -#define HASH_ADD_STR(head,strfield,add) \ -do { \ - unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ - HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ -} while (0) -#define HASH_REPLACE_STR(head,strfield,add,replaced) \ -do { \ - unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ - HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ -} while (0) -#define HASH_FIND_INT(head,findint,out) \ - HASH_FIND(hh,head,findint,sizeof(int),out) -#define HASH_ADD_INT(head,intfield,add) \ - HASH_ADD(hh,head,intfield,sizeof(int),add) -#define HASH_REPLACE_INT(head,intfield,add,replaced) \ - HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) -#define HASH_FIND_PTR(head,findptr,out) \ - HASH_FIND(hh,head,findptr,sizeof(void *),out) -#define HASH_ADD_PTR(head,ptrfield,add) \ - HASH_ADD(hh,head,ptrfield,sizeof(void *),add) -#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ - HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) -#define HASH_DEL(head,delptr) \ - HASH_DELETE(hh,head,delptr) - -/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. - * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. - */ -#ifdef HASH_DEBUG -#include /* fprintf, stderr */ -#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0) -#define HASH_FSCK(hh,head,where) \ -do { \ - struct UT_hash_handle *_thh; \ - if (head) { \ - unsigned _bkt_i; \ - unsigned _count = 0; \ - char *_prev; \ - for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ - unsigned _bkt_count = 0; \ - _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ - _prev = NULL; \ - while (_thh) { \ - if (_prev != (char*)(_thh->hh_prev)) { \ - HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ - (where), (void*)_thh->hh_prev, (void*)_prev); \ - } \ - _bkt_count++; \ - _prev = (char*)(_thh); \ - _thh = _thh->hh_next; \ - } \ - _count += _bkt_count; \ - if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ - HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ - (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ - } \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ - (where), (head)->hh.tbl->num_items, _count); \ - } \ - _count = 0; \ - _prev = NULL; \ - _thh = &(head)->hh; \ - while (_thh) { \ - _count++; \ - if (_prev != (char*)_thh->prev) { \ - HASH_OOPS("%s: invalid prev %p, actual %p\n", \ - (where), (void*)_thh->prev, (void*)_prev); \ - } \ - _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ - _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ - (where), (head)->hh.tbl->num_items, _count); \ - } \ - } \ -} while (0) -#else -#define HASH_FSCK(hh,head,where) -#endif - -/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to - * the descriptor to which this macro is defined for tuning the hash function. - * The app can #include to get the prototype for write(2). */ -#ifdef HASH_EMIT_KEYS -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ -do { \ - unsigned _klen = fieldlen; \ - write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ - write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ -} while (0) -#else -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) -#endif - -/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ -#define HASH_BER(key,keylen,hashv) \ -do { \ - unsigned _hb_keylen = (unsigned)keylen; \ - const unsigned char *_hb_key = (const unsigned char*)(key); \ - (hashv) = 0; \ - while (_hb_keylen-- != 0U) { \ - (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ - } \ -} while (0) - - -/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at - * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx - * (archive link: https://archive.is/Ivcan ) - */ -#define HASH_SAX(key,keylen,hashv) \ -do { \ - unsigned _sx_i; \ - const unsigned char *_hs_key = (const unsigned char*)(key); \ - hashv = 0; \ - for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ - hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ - } \ -} while (0) -/* FNV-1a variation */ -#define HASH_FNV(key,keylen,hashv) \ -do { \ - unsigned _fn_i; \ - const unsigned char *_hf_key = (const unsigned char*)(key); \ - (hashv) = 2166136261U; \ - for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ - hashv = hashv ^ _hf_key[_fn_i]; \ - hashv = hashv * 16777619U; \ - } \ -} while (0) - -#define HASH_OAT(key,keylen,hashv) \ -do { \ - unsigned _ho_i; \ - const unsigned char *_ho_key=(const unsigned char*)(key); \ - hashv = 0; \ - for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ - hashv += _ho_key[_ho_i]; \ - hashv += (hashv << 10); \ - hashv ^= (hashv >> 6); \ - } \ - hashv += (hashv << 3); \ - hashv ^= (hashv >> 11); \ - hashv += (hashv << 15); \ -} while (0) - -#define HASH_JEN_MIX(a,b,c) \ -do { \ - a -= b; a -= c; a ^= ( c >> 13 ); \ - b -= c; b -= a; b ^= ( a << 8 ); \ - c -= a; c -= b; c ^= ( b >> 13 ); \ - a -= b; a -= c; a ^= ( c >> 12 ); \ - b -= c; b -= a; b ^= ( a << 16 ); \ - c -= a; c -= b; c ^= ( b >> 5 ); \ - a -= b; a -= c; a ^= ( c >> 3 ); \ - b -= c; b -= a; b ^= ( a << 10 ); \ - c -= a; c -= b; c ^= ( b >> 15 ); \ -} while (0) - -#define HASH_JEN(key,keylen,hashv) \ -do { \ - unsigned _hj_i,_hj_j,_hj_k; \ - unsigned const char *_hj_key=(unsigned const char*)(key); \ - hashv = 0xfeedbeefu; \ - _hj_i = _hj_j = 0x9e3779b9u; \ - _hj_k = (unsigned)(keylen); \ - while (_hj_k >= 12U) { \ - _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ - + ( (unsigned)_hj_key[2] << 16 ) \ - + ( (unsigned)_hj_key[3] << 24 ) ); \ - _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ - + ( (unsigned)_hj_key[6] << 16 ) \ - + ( (unsigned)_hj_key[7] << 24 ) ); \ - hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ - + ( (unsigned)_hj_key[10] << 16 ) \ - + ( (unsigned)_hj_key[11] << 24 ) ); \ - \ - HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ - \ - _hj_key += 12; \ - _hj_k -= 12U; \ - } \ - hashv += (unsigned)(keylen); \ - switch ( _hj_k ) { \ - case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ - case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ - case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ - case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ - case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ - case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ - case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ - case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ - case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ - case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ - case 1: _hj_i += _hj_key[0]; /* FALLTHROUGH */ \ - default: ; \ - } \ - HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ -} while (0) - -/* The Paul Hsieh hash function */ -#undef get16bits -#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ - || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) -#define get16bits(d) (*((const uint16_t *) (d))) -#endif - -#if !defined (get16bits) -#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ - +(uint32_t)(((const uint8_t *)(d))[0]) ) -#endif -#define HASH_SFH(key,keylen,hashv) \ -do { \ - unsigned const char *_sfh_key=(unsigned const char*)(key); \ - uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ - \ - unsigned _sfh_rem = _sfh_len & 3U; \ - _sfh_len >>= 2; \ - hashv = 0xcafebabeu; \ - \ - /* Main loop */ \ - for (;_sfh_len > 0U; _sfh_len--) { \ - hashv += get16bits (_sfh_key); \ - _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ - hashv = (hashv << 16) ^ _sfh_tmp; \ - _sfh_key += 2U*sizeof (uint16_t); \ - hashv += hashv >> 11; \ - } \ - \ - /* Handle end cases */ \ - switch (_sfh_rem) { \ - case 3: hashv += get16bits (_sfh_key); \ - hashv ^= hashv << 16; \ - hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ - hashv += hashv >> 11; \ - break; \ - case 2: hashv += get16bits (_sfh_key); \ - hashv ^= hashv << 11; \ - hashv += hashv >> 17; \ - break; \ - case 1: hashv += *_sfh_key; \ - hashv ^= hashv << 10; \ - hashv += hashv >> 1; \ - break; \ - default: ; \ - } \ - \ - /* Force "avalanching" of final 127 bits */ \ - hashv ^= hashv << 3; \ - hashv += hashv >> 5; \ - hashv ^= hashv << 4; \ - hashv += hashv >> 17; \ - hashv ^= hashv << 25; \ - hashv += hashv >> 6; \ -} while (0) - -/* iterate over items in a known bucket to find desired item */ -#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ -do { \ - if ((head).hh_head != NULL) { \ - DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ - } else { \ - (out) = NULL; \ - } \ - while ((out) != NULL) { \ - if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ - if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ - break; \ - } \ - } \ - if ((out)->hh.hh_next != NULL) { \ - DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ - } else { \ - (out) = NULL; \ - } \ - } \ -} while (0) - -/* add an item to a bucket */ -#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ -do { \ - UT_hash_bucket *_ha_head = &(head); \ - _ha_head->count++; \ - (addhh)->hh_next = _ha_head->hh_head; \ - (addhh)->hh_prev = NULL; \ - if (_ha_head->hh_head != NULL) { \ - _ha_head->hh_head->hh_prev = (addhh); \ - } \ - _ha_head->hh_head = (addhh); \ - if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ - && !(addhh)->tbl->noexpand) { \ - HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ - IF_HASH_NONFATAL_OOM( \ - if (oomed) { \ - HASH_DEL_IN_BKT(head,addhh); \ - } \ - ) \ - } \ -} while (0) - -/* remove an item from a given bucket */ -#define HASH_DEL_IN_BKT(head,delhh) \ -do { \ - UT_hash_bucket *_hd_head = &(head); \ - _hd_head->count--; \ - if (_hd_head->hh_head == (delhh)) { \ - _hd_head->hh_head = (delhh)->hh_next; \ - } \ - if ((delhh)->hh_prev) { \ - (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ - } \ - if ((delhh)->hh_next) { \ - (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ - } \ -} while (0) - -/* Bucket expansion has the effect of doubling the number of buckets - * and redistributing the items into the new buckets. Ideally the - * items will distribute more or less evenly into the new buckets - * (the extent to which this is true is a measure of the quality of - * the hash function as it applies to the key domain). - * - * With the items distributed into more buckets, the chain length - * (item count) in each bucket is reduced. Thus by expanding buckets - * the hash keeps a bound on the chain length. This bounded chain - * length is the essence of how a hash provides constant time lookup. - * - * The calculation of tbl->ideal_chain_maxlen below deserves some - * explanation. First, keep in mind that we're calculating the ideal - * maximum chain length based on the *new* (doubled) bucket count. - * In fractions this is just n/b (n=number of items,b=new num buckets). - * Since the ideal chain length is an integer, we want to calculate - * ceil(n/b). We don't depend on floating point arithmetic in this - * hash, so to calculate ceil(n/b) with integers we could write - * - * ceil(n/b) = (n/b) + ((n%b)?1:0) - * - * and in fact a previous version of this hash did just that. - * But now we have improved things a bit by recognizing that b is - * always a power of two. We keep its base 2 log handy (call it lb), - * so now we can write this with a bit shift and logical AND: - * - * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) - * - */ -#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ -do { \ - unsigned _he_bkt; \ - unsigned _he_bkt_i; \ - struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ - UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ - _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ - sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ - if (!_he_new_buckets) { \ - HASH_RECORD_OOM(oomed); \ - } else { \ - uthash_bzero(_he_new_buckets, \ - sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ - (tbl)->ideal_chain_maxlen = \ - ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ - ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ - (tbl)->nonideal_items = 0; \ - for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ - _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ - while (_he_thh != NULL) { \ - _he_hh_nxt = _he_thh->hh_next; \ - HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ - _he_newbkt = &(_he_new_buckets[_he_bkt]); \ - if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ - (tbl)->nonideal_items++; \ - if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ - _he_newbkt->expand_mult++; \ - } \ - } \ - _he_thh->hh_prev = NULL; \ - _he_thh->hh_next = _he_newbkt->hh_head; \ - if (_he_newbkt->hh_head != NULL) { \ - _he_newbkt->hh_head->hh_prev = _he_thh; \ - } \ - _he_newbkt->hh_head = _he_thh; \ - _he_thh = _he_hh_nxt; \ - } \ - } \ - uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ - (tbl)->num_buckets *= 2U; \ - (tbl)->log2_num_buckets++; \ - (tbl)->buckets = _he_new_buckets; \ - (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ - ((tbl)->ineff_expands+1U) : 0U; \ - if ((tbl)->ineff_expands > 1U) { \ - (tbl)->noexpand = 1; \ - uthash_noexpand_fyi(tbl); \ - } \ - uthash_expand_fyi(tbl); \ - } \ -} while (0) - - -/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ -/* Note that HASH_SORT assumes the hash handle name to be hh. - * HASH_SRT was added to allow the hash handle name to be passed in. */ -#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) -#define HASH_SRT(hh,head,cmpfcn) \ -do { \ - unsigned _hs_i; \ - unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ - struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ - if (head != NULL) { \ - _hs_insize = 1; \ - _hs_looping = 1; \ - _hs_list = &((head)->hh); \ - while (_hs_looping != 0U) { \ - _hs_p = _hs_list; \ - _hs_list = NULL; \ - _hs_tail = NULL; \ - _hs_nmerges = 0; \ - while (_hs_p != NULL) { \ - _hs_nmerges++; \ - _hs_q = _hs_p; \ - _hs_psize = 0; \ - for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ - _hs_psize++; \ - _hs_q = ((_hs_q->next != NULL) ? \ - HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ - if (_hs_q == NULL) { \ - break; \ - } \ - } \ - _hs_qsize = _hs_insize; \ - while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ - if (_hs_psize == 0U) { \ - _hs_e = _hs_q; \ - _hs_q = ((_hs_q->next != NULL) ? \ - HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ - _hs_qsize--; \ - } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ - _hs_e = _hs_p; \ - if (_hs_p != NULL) { \ - _hs_p = ((_hs_p->next != NULL) ? \ - HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ - } \ - _hs_psize--; \ - } else if ((cmpfcn( \ - DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ - DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ - )) <= 0) { \ - _hs_e = _hs_p; \ - if (_hs_p != NULL) { \ - _hs_p = ((_hs_p->next != NULL) ? \ - HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ - } \ - _hs_psize--; \ - } else { \ - _hs_e = _hs_q; \ - _hs_q = ((_hs_q->next != NULL) ? \ - HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ - _hs_qsize--; \ - } \ - if ( _hs_tail != NULL ) { \ - _hs_tail->next = ((_hs_e != NULL) ? \ - ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ - } else { \ - _hs_list = _hs_e; \ - } \ - if (_hs_e != NULL) { \ - _hs_e->prev = ((_hs_tail != NULL) ? \ - ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ - } \ - _hs_tail = _hs_e; \ - } \ - _hs_p = _hs_q; \ - } \ - if (_hs_tail != NULL) { \ - _hs_tail->next = NULL; \ - } \ - if (_hs_nmerges <= 1U) { \ - _hs_looping = 0; \ - (head)->hh.tbl->tail = _hs_tail; \ - DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ - } \ - _hs_insize *= 2U; \ - } \ - HASH_FSCK(hh, head, "HASH_SRT"); \ - } \ -} while (0) - -/* This function selects items from one hash into another hash. - * The end result is that the selected items have dual presence - * in both hashes. There is no copy of the items made; rather - * they are added into the new hash through a secondary hash - * hash handle that must be present in the structure. */ -#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ -do { \ - unsigned _src_bkt, _dst_bkt; \ - void *_last_elt = NULL, *_elt; \ - UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ - ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ - if ((src) != NULL) { \ - for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ - for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ - _src_hh != NULL; \ - _src_hh = _src_hh->hh_next) { \ - _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ - if (cond(_elt)) { \ - IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ - _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho); \ - _dst_hh->key = _src_hh->key; \ - _dst_hh->keylen = _src_hh->keylen; \ - _dst_hh->hashv = _src_hh->hashv; \ - _dst_hh->prev = _last_elt; \ - _dst_hh->next = NULL; \ - if (_last_elt_hh != NULL) { \ - _last_elt_hh->next = _elt; \ - } \ - if ((dst) == NULL) { \ - DECLTYPE_ASSIGN(dst, _elt); \ - HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ - IF_HASH_NONFATAL_OOM( \ - if (_hs_oomed) { \ - uthash_nonfatal_oom(_elt); \ - (dst) = NULL; \ - continue; \ - } \ - ) \ - } else { \ - _dst_hh->tbl = (dst)->hh_dst.tbl; \ - } \ - HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ - HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ - (dst)->hh_dst.tbl->num_items++; \ - IF_HASH_NONFATAL_OOM( \ - if (_hs_oomed) { \ - HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ - HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ - _dst_hh->tbl = NULL; \ - uthash_nonfatal_oom(_elt); \ - continue; \ - } \ - ) \ - HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ - _last_elt = _elt; \ - _last_elt_hh = _dst_hh; \ - } \ - } \ - } \ - } \ - HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ -} while (0) - -#define HASH_CLEAR(hh,head) \ -do { \ - if ((head) != NULL) { \ - HASH_BLOOM_FREE((head)->hh.tbl); \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - (head) = NULL; \ - } \ -} while (0) - -#define HASH_OVERHEAD(hh,head) \ - (((head) != NULL) ? ( \ - (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ - ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ - sizeof(UT_hash_table) + \ - (HASH_BLOOM_BYTELEN))) : 0U) - -#ifdef NO_DECLTYPE -#define HASH_ITER(hh,head,el,tmp) \ -for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ - (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) -#else -#define HASH_ITER(hh,head,el,tmp) \ -for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ - (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) -#endif - -/* obtain a count of items in the hash */ -#define HASH_COUNT(head) HASH_CNT(hh,head) -#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) - -typedef struct UT_hash_bucket { - struct UT_hash_handle *hh_head; - unsigned count; - - /* expand_mult is normally set to 0. In this situation, the max chain length - * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If - * the bucket's chain exceeds this length, bucket expansion is triggered). - * However, setting expand_mult to a non-zero value delays bucket expansion - * (that would be triggered by additions to this particular bucket) - * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. - * (The multiplier is simply expand_mult+1). The whole idea of this - * multiplier is to reduce bucket expansions, since they are expensive, in - * situations where we know that a particular bucket tends to be overused. - * It is better to let its chain length grow to a longer yet-still-bounded - * value, than to do an O(n) bucket expansion too often. - */ - unsigned expand_mult; - -} UT_hash_bucket; - -/* random signature used only to find hash tables in external analysis */ -#define HASH_SIGNATURE 0xa0111fe1u -#define HASH_BLOOM_SIGNATURE 0xb12220f2u - -typedef struct UT_hash_table { - UT_hash_bucket *buckets; - unsigned num_buckets, log2_num_buckets; - unsigned num_items; - struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ - ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ - - /* in an ideal situation (all buckets used equally), no bucket would have - * more than ceil(#items/#buckets) items. that's the ideal chain length. */ - unsigned ideal_chain_maxlen; - - /* nonideal_items is the number of items in the hash whose chain position - * exceeds the ideal chain maxlen. these items pay the penalty for an uneven - * hash distribution; reaching them in a chain traversal takes >ideal steps */ - unsigned nonideal_items; - - /* ineffective expands occur when a bucket doubling was performed, but - * afterward, more than half the items in the hash had nonideal chain - * positions. If this happens on two consecutive expansions we inhibit any - * further expansion, as it's not helping; this happens when the hash - * function isn't a good fit for the key domain. When expansion is inhibited - * the hash will still work, albeit no longer in constant time. */ - unsigned ineff_expands, noexpand; - - uint32_t signature; /* used only to find hash tables in external analysis */ -#ifdef HASH_BLOOM - uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ - uint8_t *bloom_bv; - uint8_t bloom_nbits; -#endif - -} UT_hash_table; - -typedef struct UT_hash_handle { - struct UT_hash_table *tbl; - void *prev; /* prev element in app order */ - void *next; /* next element in app order */ - struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ - struct UT_hash_handle *hh_next; /* next hh in bucket order */ - const void *key; /* ptr to enclosing struct's key */ - unsigned keylen; /* enclosing struct's key len */ - unsigned hashv; /* result of hash-fcn(key) */ -} UT_hash_handle; - -#endif /* UTHASH_H */ diff --git a/src/lexer/.DS_Store b/src/lexer/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/src/lexer/.DS_Store differ diff --git a/src/lexer/SolsLiteral.c b/src/lexer/SolsLiteral.c deleted file mode 100644 index afddf09..0000000 --- a/src/lexer/SolsLiteral.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "SolsLiteral.h" -#include -#include - -ResultType(SolsLiteral, charptr) createSolsLiteral(SolsLiteralType type, ...) { - va_list args; - va_start(args, type); - SolsLiteral literal = { - .type = type - }; - switch (type) { - case SLT_INT: { - literal.as.intv = va_arg(args, int64_t); - break; - } - case SLT_DOUBLE: { - literal.as.doublev = va_arg(args, double); - break; - } - case SLT_BOOL: { - literal.as.boolv = (bool) va_arg(args, int); - break; - } - case SLT_CHAR: { - literal.as.charv = (char) va_arg(args, int); - break; - } - case SLT_STRING: { - char* input = va_arg(args, char*); - if (input == NULL) { - va_end(args); - return Error(SolsLiteral, charptr, "Unexpected NULL value (in createSolsLiteral() function)"); - } - literal.as.stringv = malloc(strlen(input) + 1); - if (literal.as.stringv == NULL) { - va_end(args); - return Error(SolsLiteral, charptr, "Couldn't allocate memory (in createSolsLiteral() function)"); - } - strcpy(literal.as.stringv, input); - break; - } - } - va_end(args); - return Success(SolsLiteral, charptr, literal); -} - -void freeSolsLiteral(SolsLiteral* lit) { - if (lit->type == SLT_STRING && lit->as.stringv != NULL) { - free(lit->as.stringv); - } -} diff --git a/src/lexer/SolsLiteral.h b/src/lexer/SolsLiteral.h deleted file mode 100644 index 7c97ac9..0000000 --- a/src/lexer/SolsLiteral.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef SOLSLITERAL_H -#define SOLSLITERAL_H - -#include -#include - -#include "../include/error.h" -#include "../include/nothing.h" - -typedef char* charptr; - -typedef enum SolsLiteralType { - SLT_INT, SLT_STRING, SLT_DOUBLE, SLT_BOOL, SLT_CHAR -} SolsLiteralType; - -// Stores literal values which will be added to the Ground code. -// Not much explaining needed here. -typedef struct SolsLiteral { - SolsLiteralType type; - union { - int64_t intv; - char* stringv; - double doublev; - bool boolv; - char charv; - } as; -} SolsLiteral; - -Result(SolsLiteral, charptr); - -// Creates a SolsLiteral, based on the type provided. -// SolsLiteralType -> C type: -// SLT_INT -> int64_t -// SLT_STRING -> char* -// SLT_DOUBLE -> double -// SLT_BOOL -> bool -// SL_CHAR -> char -// An error will only be returned if there is an issue copying a provided char*. -// There is no way to detect incorrectly provided types, so ensure that the right type -// is provided!!!! -ResultType(SolsLiteral, charptr) createSolsLiteral(SolsLiteralType type, ...); - -// Frees a SolsLiteral. Primarily concerned with freeing .as.stringv -void freeSolsLiteral(SolsLiteral* lit); - -#endif diff --git a/src/lexer/SolsToken.c b/src/lexer/SolsToken.c deleted file mode 100644 index c667d3f..0000000 --- a/src/lexer/SolsToken.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "SolsToken.h" -#include "SolsLiteral.h" -#include "../include/error.h" -#include -#include - -ResultType(SolsToken, charptr) createSolsToken(SolsTokenType type, ...) { - va_list args; - va_start(args, type); - SolsToken token = { - .type = type - }; - - if (type == STT_IDENTIFIER) { - char* name = va_arg(args, char*); - if (name == NULL) { - va_end(args); - return Error(SolsToken, charptr, "String passed is NULL (in createSolsToken() function)"); - } - token.as.idName = malloc(strlen(name) + 1); - if (token.as.idName == NULL) { - va_end(args); - return Error(SolsToken, charptr, "Couldn't allocate memory (in createSolsToken() function)"); - } - strcpy(token.as.idName, name); - } - - if (type == STT_KW_GROUND) { - char* ground = va_arg(args, char*); - if (ground == NULL) { - va_end(args); - return Error(SolsToken, charptr, "String passed is NULL (in createSolsToken() function)"); - } - token.as.inlineGround = malloc(strlen(ground) + 1); - if (token.as.inlineGround == NULL) { - va_end(args); - return Error(SolsToken, charptr, "Couldn't allocate memory (in createSolsToken() function)"); - } - strcpy(token.as.inlineGround, ground); - } - - if (type == STT_LITERAL) { - token.as.literal = va_arg(args, SolsLiteral); - } - - if (type == STT_TYPE) { - token.as.type = va_arg(args, SolsType); - } - - va_end(args); - return Success(SolsToken, charptr, token); -} - -void freeSolsToken(SolsToken* token) { - if (token->type == STT_IDENTIFIER && token->as.idName != NULL) { - free(token->as.idName); - } - if (token->type == STT_KW_GROUND && token->as.inlineGround != NULL) { - free(token->as.inlineGround); - } - if (token->type == STT_LITERAL) { - freeSolsLiteral(&token->as.literal); - } - if (token->type == STT_TYPE) { - freeSolsType(&token->as.type); - } -} - -ResultType(SolsTokens, charptr) createSolsTokens() { - SolsTokens tokens = { - .at = malloc(sizeof(SolsToken) * 32), - .capacity = 32, - .count = 0 - }; - if (tokens.at == NULL) { - return Error(SolsTokens, charptr, "Failed to allocate memory (in createSolsTokens() function)"); - } - return Success(SolsTokens, charptr, tokens); -} - -ResultType(Nothing, charptr) addTokenToSolsTokens(SolsTokens* tokens, SolsToken token) { - if (tokens->capacity < tokens->count + 1) { - tokens->capacity *= 2; - SolsToken* tmp = realloc(tokens->at, sizeof(SolsToken) * tokens->capacity); - if (tmp == NULL) { - return Error(Nothing, charptr, "Failed to allocate memory (in addTokenToSolsTokens() function)"); - } - tokens->at = tmp; - } - tokens->at[tokens->count] = token; - tokens->count++; - return Success(Nothing, charptr, {}); -} diff --git a/src/lexer/SolsToken.h b/src/lexer/SolsToken.h deleted file mode 100644 index c63389e..0000000 --- a/src/lexer/SolsToken.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef SOLSTOKEN_H -#define SOLSTOKEN_H - -#include - -#include "../include/error.h" -#include "../include/nothing.h" - -#include "SolsType.h" -#include "SolsLiteral.h" - -typedef enum SolsTokenType { - STT_IDENTIFIER, STT_LITERAL, STT_TYPE, STT_DOT, - STT_OPEN_CURLY, STT_CLOSE_CURLY, STT_OPEN_PAREN, STT_CLOSE_PAREN, - STT_OP_ADD, STT_OP_SUB, STT_OP_MUL, STT_OP_DIV, - STT_OP_ADDTO, STT_OP_SUBTO, STT_OP_MULTO, STT_OP_DIVTO, - STT_OP_INCREMENT, STT_OP_DECREMENT, STT_OP_SET, - STT_OP_GREATER, STT_OP_LESSER, STT_OP_EQUAL, STT_OP_INEQUAL, STT_OP_EQGREATER, STT_OP_EQLESSER, - STT_KW_DEF, STT_KW_LAMBDA, STT_KW_RETURN, - STT_KW_USE, STT_KW_STRUCT, STT_KW_CONSTRUCTOR, STT_KW_DESTRUCTOR, - STT_KW_PRIVATE, STT_KW_PROTECTED, - STT_KW_PUTS, STT_KW_IF, STT_KW_WHILE, - STT_KW_NEW, STT_KW_GROUND, STT_LINE_END, STT_COMMA -} SolsTokenType; - -typedef char* charptr; - -// Stores information about the line that the token/node is on, for printing if an error -// occurs. -// .num is the line number, .content is the line's contents. -typedef struct LineInfo { - size_t num; - char* content; -} LineInfo; - -// Represents a token lexed by the lex() function. -// Most token types exclusively use the .type field, however some tokens require storing -// more data, inside the .as union. -// Those tokens are: -// STT_LITERAL: A literal value. Uses field .as.literal -// STT_TYPE: A type descriptor. Uses field .as.type -// STT_IDENTIFIER: An identifier. Uses field .as.idName -// STT_KW_GROUND: Ground code embedded inside Solstice. Uses field .as.inlineGround -typedef struct SolsToken { - SolsTokenType type; - union { - SolsLiteral literal; - SolsType type; - char* idName; - char* inlineGround; - } as; - LineInfo line; -} SolsToken; - -Result(SolsToken, charptr); - -// Creates a SolsToken. If the type passed in is STT_LITERAL, STT_TYPE, STT_IDENTIFIER or -// STT_KW_GROUND, the function expects another argument, corresponding to the data type -// the token holds. See the SolsToken struct for more information. -// Returns: -// Success: The created SolsToken -// Failure: char* detailing what went wrong (usually memory failure) -ResultType(SolsToken, charptr) createSolsToken(SolsTokenType type, ...); - -// Frees a SolsToken, specifically the .as field elements. -void freeSolsToken(SolsToken* token); - -// Represents a Solstice program, seperated into tokens. -// .at is a pointer to the tokens -// .count is how many tokens are currently being stored -// .capacity is how many tokens worth of memory is allocated -typedef struct SolsTokens { - SolsToken* at; - size_t count; - size_t capacity; -} SolsTokens; - -Result(SolsTokens, charptr); - -// Creates a SolsTokens holder. -// Returns: -// Success: Constructed SolsTokens -// Failure: char* detailing what went wrong (usually memory failure) -ResultType(SolsTokens, charptr) createSolsTokens(); - -// Adds a token to SolsTokens. Used by the lex() function. -// Returns: -// Success: Nothing -// Failure: char* detailing what went wrong (usually memory failure) -ResultType(Nothing, charptr) addTokenToSolsTokens(SolsTokens* tokens, SolsToken token); - - -#endif diff --git a/src/lexer/SolsType.c b/src/lexer/SolsType.c deleted file mode 100644 index 6fbac10..0000000 --- a/src/lexer/SolsType.c +++ /dev/null @@ -1,298 +0,0 @@ -#include "SolsType.h" -#include "../include/error.h" -#include "../include/estr.h" -#include -#include - -ResultType(SolsType, charptr) createSolsType(SolsTypeType in) { - SolsTypeField* ptr = malloc(sizeof(SolsTypeField) * 32); - if (ptr == NULL) { - return Error(SolsType, charptr, "Couldn't allocate memory (in createSolsType() function)"); - } - SolsType type = { - .type = in, - .identifierType = NULL, - .returnType = NULL, - .children.capacity = 32, - .children.count = 0, - .children.at = ptr, - .typeIsKnown = true, - .needsGroundStruct = false, - .metadata.isPrivate = false, - .metadata.isProtected = false - }; - return Success(SolsType, charptr, type); -} - -ResultType(SolsType, charptr) createIdentifiedSolsType(char* in) { - char* copy = malloc(strlen(in) + 1); - if (copy == NULL) { - return Error(SolsType, charptr, "Couldn't allocate memory (in createIdentifiedSolsType() function)"); - } - strcpy(copy, in); - return Success(SolsType, charptr, ((SolsType) { - .type = STT_UNKNOWN, - .identifierType = copy, - .returnType = NULL, - .children.capacity = 0, - .children.count = 0, - .children.at = NULL, - .typeIsKnown = false, - .needsGroundStruct = false, - .metadata.isPrivate = false, - .metadata.isProtected = false - })); -} - -ResultType(SolsType, charptr) copySolsType(SolsType* type) { - SolsType ret = { - .type = type->type, - .identifierType = NULL, - .typeIsKnown = type->typeIsKnown, - .needsGroundStruct = type->needsGroundStruct, - .returnType = NULL, - .children.count = type->children.count, - .children.capacity = type->children.capacity, - .children.at = NULL, - .metadata = type->metadata - }; - - if (type->identifierType != NULL) { - ret.identifierType = malloc(strlen(type->identifierType) + 1); - if (ret.identifierType == NULL) { - return Error(SolsType, charptr, "Couldn't allocate memory (in copySolsType() function)"); - } - strcpy(ret.identifierType, type->identifierType); - } - - if (type->returnType != NULL) { - ResultType(SolsType, charptr) copiedReturn = copySolsType(type->returnType); - if (copiedReturn.error) { - Estr err = CREATE_ESTR(copiedReturn.as.error); - APPEND_ESTR(err, " (in copySolsType() function)"); - return Error(SolsType, charptr, err.str); - } - - ret.returnType = malloc(sizeof(SolsType)); - if (ret.returnType == NULL) { - return Error(SolsType, charptr, "Couldn't allocate memory (in copySolsType() function)"); - } - *ret.returnType = copiedReturn.as.success; - } - - if (type->children.capacity > 0) { - SolsTypeField* ptr = malloc(sizeof(SolsTypeField) * type->children.capacity); - if (ptr == NULL) { - return Error(SolsType, charptr, "Couldn't allocate memory (in copySolsType() function)"); - } - ret.children.at = ptr; - } - - for (size_t i = 0; i < type->children.count; i++) { - ResultType(SolsType, charptr) copied = copySolsType(&type->children.at[i].type); - if (copied.error) { - Estr err = CREATE_ESTR(copied.as.error); - APPEND_ESTR(err, " (in copySolsType() function)"); - return Error(SolsType, charptr, err.str); - } - ret.children.at[i].type = copied.as.success; - - if (type->children.at[i].name == NULL) { - ret.children.at[i].name = NULL; - } else { - ret.children.at[i].name = malloc(strlen(type->children.at[i].name) + 1); - if (ret.children.at[i].name == NULL) { - return Error(SolsType, charptr, "Couldn't allocate memory (in copySolsType() function)"); - } - strcpy(ret.children.at[i].name, type->children.at[i].name); - } - } - - return Success(SolsType, charptr, ret); -} -/* -ResultType(SolsType, charptr) copySolsType(SolsType* type) { - SolsType ret = { .type = type->type, .children.count = type->children.count, .children.capacity = type->children.capacity}; - - // Allocate memory - SolsTypeField* ptr = malloc(sizeof(SolsTypeField) * type->children.capacity); - if (ptr == NULL) { - return Error(SolsType, charptr, "Couldn't allocate memory (in copySolsType() function)"); - } - ret.children.at = ptr; - - // Deep copy values - for (size_t i = 0; i < type->children.count; i++) { - // Copy the SolsType value - ResultType(SolsType, charptr) copied = copySolsType(&type->children.at[i].type); - if (copied.error) { - Estr err = CREATE_ESTR(copied.as.error); - APPEND_ESTR(err, " (in addChildToSolsType() function)"); - return Error(SolsType, charptr, err.str); - } - ret.children.at[i].type = copied.as.success; - - // Copy the name - if (type->children.at[i].name == NULL) { - ret.children.at[i].name = NULL; - } else { - ret.children.at[i].name = malloc(strlen(type->children.at[i].name) + 1); - if (ret.children.at[i].name == NULL) { - return Error(SolsType, charptr, "Couldn't allocate memory (in copySolsType() function)"); - } - strcpy(ret.children.at[i].name, type->children.at[i].name); - } - } - return Success(SolsType, charptr, ret); -} -*/ - -ResultType(Nothing, charptr) addChildToSolsType(SolsType* type, SolsType child, const char* name) { - if (type->children.capacity < type->children.count + 1) { - type->children.capacity *= 2; - SolsTypeField* ptr = realloc(type->children.at, sizeof(SolsTypeField) * type->children.capacity); - if (ptr == NULL) { - return Error(Nothing, charptr, "Couldn't allocate memory (in addChildToSolsType() function)"); - } - type->children.at = ptr; - } - ResultType(SolsType, charptr) copied = copySolsType(&child); - if (copied.error) { - Estr err = CREATE_ESTR(copied.as.error); - APPEND_ESTR(err, " (in addChildToSolsType() function)"); - return Error(Nothing, charptr, err.str); - } - type->children.at[type->children.count].type = copied.as.success; - if (name == NULL) { - type->children.at[type->children.count].name = NULL; - } else { - type->children.at[type->children.count].name = malloc(strlen(name) + 1); - strcpy(type->children.at[type->children.count].name, name); - } - type->children.count++; - - return Success(Nothing, charptr, {}); -} - -void freeSolsType(SolsType* type) { - for (size_t i = 0; i < type->children.count; i++) { - // Free the name - if (type->children.at[i].name != NULL) { - free(type->children.at[i].name); - } - - // Free the child SolsTypes - freeSolsType(&type->children.at[i].type); - } - // Free the field itself - free(type->children.at); - type->children.at = NULL; - - // Set count and capacity to zero - type->children.count = 0; - type->children.capacity = 0; -} - -bool compareTypes(SolsType* left, SolsType* right) { - if (left->type != right->type) { - return false; - } - switch (left->type) { - case STT_OBJECT: { - if (left->children.count != right->children.count) { - return false; - } - for (size_t i = 0; i < left->children.count; i++) { - if (strcmp(left->children.at[i].name, right->children.at[i].name) != 0) { - return false; - } - if (compareTypes(&left->children.at[i].type, &right->children.at[i].type) == false) { - return false; - } - } - return true; - } - case STT_TEMPLATE: { - if (left->children.count != right->children.count) { - return false; - } - for (size_t i = 0; i < left->children.count; i++) { - if (strcmp(left->children.at[i].name, right->children.at[i].name) != 0) { - return false; - } - if (compareTypes(&left->children.at[i].type, &right->children.at[i].type) == false) { - return false; - } - } - return true; - - } - case STT_FUN: { - if (left->children.count != right->children.count) { - return false; - } - for (size_t i = 0; i < left->children.count; i++) { - if (compareTypes(&left->children.at[i].type, &right->children.at[i].type) == false) { - return false; - } - } - return true; - } - default: return true; - } -} - -ResultType(GroundArg, charptr) createGroundArgFromSolsType(SolsType* type, struct SolsScope* scope) { - switch (type->type) { - case STT_INT: { - return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "int")); - } - case STT_DOUBLE: { - return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "double")); - } - case STT_STRING: { - return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "string")); - } - case STT_BOOL: { - return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "bool")); - } - case STT_CHAR: { - return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "char")); - } - case STT_FUN: { - return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "function")); - } - case STT_TEMPLATE: { - return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "struct")); - } - case STT_OBJECT: { - if (!type->needsGroundStruct) { - return Success(GroundArg, charptr, groundCreateReference(TYPEREF, type->identifierType)); - } else { - // FIXME do this later - return Error(GroundArg, charptr, "Anonymous structs are not supported yet"); - } - } - case STT_UNKNOWN: { - if (!type->needsGroundStruct) { - return Success(GroundArg, charptr, groundCreateReference(TYPEREF, type->identifierType)); - } else { - // FIXME do this later - return Error(GroundArg, charptr, "Anonymous structs are not supported yet"); - } - } - case STT_NONE: { - return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "none")); - } - } - return Error(GroundArg, charptr, "How did we get here?"); -} - -ResultType(SolsType, charptr) findStructMemberType(SolsType* type, char* member) { - for (size_t i = 0; i < type->children.count; i++) { - if (strcmp(type->children.at[i].name, member) == 0) { - return Success(SolsType, charptr, type->children.at[i].type); - } - } - return Error(SolsType, charptr, "Could not find member"); -} diff --git a/src/lexer/SolsType.h b/src/lexer/SolsType.h deleted file mode 100644 index 1027275..0000000 --- a/src/lexer/SolsType.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef SOLSTYPE_H -#define SOLSTYPE_H - -#include -#include - -#include "../include/error.h" -#include "../include/nothing.h" - -typedef enum SolsTypeType { - STT_INT, STT_STRING, STT_DOUBLE, STT_BOOL, STT_CHAR, STT_FUN, STT_TEMPLATE, STT_OBJECT, STT_UNKNOWN, STT_NONE -} SolsTypeType; - -// Definition of charptr for Result() and ResultType() macros -typedef char* charptr; - -struct SolsTypeField; - -// Holds type information for a struct, object or function. -// Say, for example, your type signature looks like this: -// object(string x, fun(int) y) -// This is stored like this: -// SolsType { -// type: STT_OBJECT -// children: [ -// { -// type: { -// type: STT_STRING -// } -// name: "x" -// } -// { -// type: { -// type: STT_FUN -// children: [ -// { -// type: { -// type: STT_INT -// } -// } -// ] -// } -// name: "y" -// } -// ] -// } -// -// (Sorry for the long explaination, but it's worth it so you know how the type system works.) -// -typedef struct SolsType { - SolsTypeType type; - - // For use when type is identified with a name - char* identifierType; - - // If type is identified with a name, record whether we have found the actual type - bool typeIsKnown; - - // If using anonymous struct, record whether we need to generate a Ground struct - bool needsGroundStruct; - - // For use in functions - struct SolsType* returnType; - - // For use by fun, template, object - struct { - struct SolsTypeField* at; - size_t count; - size_t capacity; - } children; - - struct { - bool isPrivate; - bool isProtected; - } metadata; -} SolsType; - -// Assists with holding child types in the SolsType struct. -typedef struct SolsTypeField { - SolsType type; - char* name; -} SolsTypeField; - - -Result(SolsType, charptr); - -// Creates a SolsType, with the provided type type. -// Use the "addChildToSolsType()" function to add children, in case this type has children. -// Returns: -// Success: The constructed SolsType -// Failure: char* detailing what went wrong (usually memory failure) -ResultType(SolsType, charptr) createSolsType(SolsTypeType in); - -// Creates a SolsType which is identified by a name. -// The type details are not known yet, so the type is marked as unknown. -ResultType(SolsType, charptr) createIdentifiedSolsType(char* in); - -Result(Nothing, charptr); - -// Adds a child SolsType to a given SolsType. -// Returns: -// Success: Nothing -// Failure: char* detailing what went wrong (usually memory failure) -ResultType(Nothing, charptr) addChildToSolsType(SolsType* type, SolsType child, const char* name); - -// Makes a deep copy of a SolsType. -ResultType(SolsType, charptr) copySolsType(SolsType* type); - -Result(GroundArg, charptr); - -struct SolsScope; - -// Represents a SolsType as a GroundArg (in typeref form) -ResultType(GroundArg, charptr) createGroundArgFromSolsType(SolsType* type, struct SolsScope* scope); - -// Frees a SolsType -void freeSolsType(SolsType* type); - -// Compares two SolsTypes -bool compareTypes(SolsType* left, SolsType* right); - -// Finds the type of a struct member. Errors if the member is not found. -ResultType(SolsType, charptr) findStructMemberType(SolsType* type, char* member); - -#endif diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c deleted file mode 100644 index f0cb4b6..0000000 --- a/src/lexer/lexer.c +++ /dev/null @@ -1,850 +0,0 @@ -#include "lexer.h" -#include "SolsLiteral.h" -#include "SolsToken.h" -#include "../include/error.h" -#include "../include/estr.h" -#include "../include/ansii.h" -#include - -struct _SolsTokenTypeMap SolsTokenTypeMap[] = { - {"puts", STT_KW_PUTS}, - {"if", STT_KW_IF}, - {"while", STT_KW_WHILE}, - {"def", STT_KW_DEF}, - {"lambda", STT_KW_LAMBDA}, - {"return", STT_KW_RETURN}, - {"use", STT_KW_USE}, - {"struct", STT_KW_STRUCT}, - {"constructor", STT_KW_CONSTRUCTOR}, - {"destructor", STT_KW_DESTRUCTOR}, - {"private", STT_KW_PRIVATE}, - {"protected", STT_KW_PROTECTED}, - {"ground", STT_KW_GROUND}, - {"new", STT_KW_NEW}, - {"{", STT_OPEN_CURLY}, - {"}", STT_CLOSE_CURLY}, - {"(", STT_OPEN_PAREN}, - {")", STT_CLOSE_PAREN}, - {"+", STT_OP_ADD}, - {"-", STT_OP_SUB}, - {"*", STT_OP_MUL}, - {"/", STT_OP_DIV}, - {"=", STT_OP_SET}, - {"+=", STT_OP_ADDTO}, - {"-=", STT_OP_SUBTO}, - {"*=", STT_OP_MULTO}, - {"/=", STT_OP_DIVTO}, - {"++", STT_OP_INCREMENT}, - {"--", STT_OP_DECREMENT}, - {"==", STT_OP_EQUAL}, - {"!=", STT_OP_INEQUAL}, - {">", STT_OP_GREATER}, - {"<", STT_OP_LESSER}, - {">=", STT_OP_EQGREATER}, - {"<=", STT_OP_EQLESSER}, - {"\n", STT_LINE_END}, - {";", STT_LINE_END}, - {",", STT_COMMA}, - // Shh, this is our little secret - // Your reward for actually reading the source code - // Enable this by adding -DSUPER_SILLY_MODE to your - // compile flags (not recommended for production) - #ifdef SUPER_SILLY_MODE - {"plus", STT_OP_ADD}, - {"minus", STT_OP_SUB}, - {"times", STT_OP_MUL}, - {"dividedby", STT_OP_DIV}, - {"then", STT_OPEN_CURLY}, - {"do", STT_OPEN_CURLY}, - {"end", STT_CLOSE_CURLY}, - {"is", STT_OP_SET}, - {"equals", STT_OP_EQUAL}, - {"greaterthan", STT_OP_GREATER}, - {"lesserthan", STT_OP_LESSER}, - {"increment", STT_OP_INCREMENT}, - {"decrement", STT_OP_DECREMENT}, - {"adds", STT_OP_ADDTO}, - {"subtracts", STT_OP_SUBTO}, - {"multiplies", STT_OP_MULTO}, - {"divides", STT_OP_DIVTO}, - {"class", STT_KW_STRUCT} - #endif -}; - -ResultType(SolsTokenType, Nothing) getTokenType(const char* input) { - size_t mapsize = sizeof(SolsTokenTypeMap) / sizeof(struct _SolsTokenTypeMap); - for (size_t i = 0; i < mapsize; i++) { - if (strcmp(input, SolsTokenTypeMap[i].str) == 0) { - return Success(SolsTokenType, Nothing, SolsTokenTypeMap[i].type); - } - } - return Error(SolsTokenType, Nothing, {}); -} - -static ResultType(Nothing, charptr) handleGround(SolsLexer* lexer, SolsToken* token, size_t* lineNum, Estr* currentLine, char currentChr, bool* skipDelimiter) { - bool foundBrace = false; - if (currentChr == '{') { - foundBrace = true; - *skipDelimiter = true; - } else { - while (true) { - ResultType(char, Nothing) peek = lexerPeek(lexer, 1); - if (peek.error) break; - if (isspace(peek.as.success)) { - char c = lexerConsume(lexer).as.success; - if (c == '\n') { - (*lineNum)++; - DESTROY_ESTR((*currentLine)); - *currentLine = CREATE_ESTR(""); - size_t lineStart = lexer->current; - for (size_t i = lineStart; i < lexer->inputsize; i++) { - if (lexer->input[i] == '\n') break; - char buf_tmp[] = {lexer->input[i], '\0'}; - APPEND_ESTR((*currentLine), buf_tmp); - } - } - } else if (peek.as.success == '{') { - lexerConsume(lexer); - foundBrace = true; - break; - } else { - break; - } - } - } - - if (!foundBrace) { - return Error(Nothing, charptr, "Expected '{' after 'ground'"); - } - - Estr groundBuf = CREATE_ESTR(""); - int depth = 1; - while (depth > 0) { - ResultType(char, Nothing) next = lexerConsume(lexer); - if (next.error) { - DESTROY_ESTR(groundBuf); - return Error(Nothing, charptr, "Unterminated 'ground' block"); - } - if (next.as.success == '{') depth++; - if (next.as.success == '}') { - depth--; - if (depth == 0) break; - } - - char tmp[] = {next.as.success, '\0'}; - APPEND_ESTR(groundBuf, tmp); - - if (next.as.success == '\n') { - (*lineNum)++; - DESTROY_ESTR((*currentLine)); - *currentLine = CREATE_ESTR(""); - size_t lineStart = lexer->current; - for (size_t i = lineStart; i < lexer->inputsize; i++) { - if (lexer->input[i] == '\n') break; - char buf_tmp[] = {lexer->input[i], '\0'}; - APPEND_ESTR((*currentLine), buf_tmp); - } - } - } - - token->as.inlineGround = malloc(strlen(groundBuf.str) + 1); - if (token->as.inlineGround == NULL) { - DESTROY_ESTR(groundBuf); - return Error(Nothing, charptr, "Memory allocation failed (in handleGround() function)"); - } - strcpy(token->as.inlineGround, groundBuf.str); - DESTROY_ESTR(groundBuf); - return Success(Nothing, charptr, {}); -} - -static ResultType(Nothing, charptr) identifyAndAdd(SolsLexer* lexer, Estr* buf, size_t* lineNum, Estr* currentLine, char currentChr, bool* skipDelimiter) { - if (strcmp(buf->str, "") == 0) return Success(Nothing, charptr, {}); - - ResultType(SolsToken, charptr) result = identifyToken(buf->str); - if (result.error) { - return Error(Nothing, charptr, result.as.error); - } - result.as.success.line.num = *lineNum; - result.as.success.line.content = malloc(strlen(currentLine->str) + 1); - if (result.as.success.line.content == NULL) { - return Error(Nothing, charptr, "Couldn't allocate memory to store line information in token (in identifyAndAdd() function)"); - } - strcpy(result.as.success.line.content, currentLine->str); - - if (result.as.success.type == STT_KW_GROUND) { - ResultType(Nothing, charptr) res = handleGround(lexer, &result.as.success, lineNum, currentLine, currentChr, skipDelimiter); - if (res.error) return res; - } - - addTokenToSolsTokens(&lexer->output, result.as.success); - DESTROY_ESTR((*buf)); - *buf = CREATE_ESTR(""); - return Success(Nothing, charptr, {}); -} - - -ResultType(SolsLexer, charptr) createLexer(char* input) { - - // Copy input into the new lexer struct - char* inputcopy = malloc(strlen(input) + 1); - if (inputcopy == NULL) { - return Error(SolsLexer, charptr, "Couldn't copy string into lexer (in createLexer() function)"); - } - strcpy(inputcopy, input); - - // Create SolsTokens - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - Estr e = CREATE_ESTR(tokens.as.error); - APPEND_ESTR(e, " (in createLexer() function)"); - return Error(SolsLexer, charptr, e.str); - } - - // Construct and return lexer - SolsLexer lexer = { - .input = inputcopy, - .inputsize = strlen(inputcopy), - .output = tokens.as.success, - .current = 0, - }; - return Success(SolsLexer, charptr, lexer); -} - -ResultType(char, Nothing) lexerPeek(SolsLexer* lexer, size_t ahead) { - - // Reduce by 1 so peeking at the next token with 1 works - ahead--; - - // Bounds and null checking - if (lexer->input == NULL) { - return Error(char, Nothing, {}); - } - if (lexer->current + ahead >= lexer->inputsize) { - return Error(char, Nothing, {}); - } - - // Char is within bounds, return it - return Success(char, Nothing, lexer->input[lexer->current + ahead]); -} - - -ResultType(char, Nothing) lexerConsume(SolsLexer* lexer) { - - // Bounds and null checking - if (lexer->input == NULL) { - return Error(char, Nothing, {}); - } - if (lexer->current + 1 > lexer->inputsize) { - return Error(char, Nothing, {}); - } - - // Char is within bounds, return and increment - return Success(char, Nothing, lexer->input[lexer->current++]); -} - -ResultType(SolsToken, charptr) identifyToken(const char* token) { - // Process strings - if (token[0] == '"') { - if (token[strlen(token) - 1] == '"') { - // Cut out the quotes - char* tokencopy = malloc(strlen(token) + 1); - strncpy(tokencopy, token + 1, strlen(token) - 2); - tokencopy[strlen(token) - 2] = '\0'; - - // Create a literal - ResultType(SolsLiteral, charptr) literal = createSolsLiteral(SLT_STRING, tokencopy); - // Free our copy of the string, createSolsLiteral creates a copy - free(tokencopy); - if (literal.error) { - Estr str = CREATE_ESTR(literal.as.error); - APPEND_ESTR(str, " (in identifyToken() function)"); - return Error(SolsToken, charptr, str.str); - } - - // Construct and return the token - SolsToken tok = { - .type = STT_LITERAL, - .as.literal = literal.as.success - }; - return Success(SolsToken, charptr, tok); - } - return Error(SolsToken, charptr, "Unterminated string (in identifyToken() function)"); - } - - // Process characters - if (token[0] == '\'') { - if (strlen(token) != 3) { - return Error(SolsToken, charptr, "Characters can only hold one character at a time (try using \"this\" for strings?)"); - } - if (token[2] == '\'') { - ResultType(SolsLiteral, charptr) literal = createSolsLiteral(SLT_CHAR, token[1]); - if (literal.error) { - Estr str = CREATE_ESTR(literal.as.error); - APPEND_ESTR(str, " (in identifyToken() function)"); - return Error(SolsToken, charptr, str.str); - } - SolsToken tok = { - .type = STT_LITERAL, - .as.literal = literal.as.success - }; - return Success(SolsToken, charptr, tok); - } else { - return Error(SolsToken, charptr, "Unterminated character (in identifyToken() function)"); - } - } - - // Process integers and floats - if (isdigit(token[0]) || (token[0] == '-' && strlen(token) > 1 && (isdigit(token[1]) || token[1] == '.'))) { - size_t len = strlen(token); - bool isInt = true; - bool isDouble = false; - for (size_t i = 1; i < len; i++) { - if (isInt && token[i] == '.') { - isInt = false; - isDouble = true; - continue; - } - if (!isdigit(token[i])) { - isInt = false; - isDouble = false; - } - } - if (isInt) { - int64_t newInt = atoll(token); - ResultType(SolsLiteral, charptr) literal = createSolsLiteral(SLT_INT, newInt); - if (literal.error) { - Estr str = CREATE_ESTR(literal.as.error); - APPEND_ESTR(str, " (in identifyToken() function)"); - return Error(SolsToken, charptr, str.str); - } - SolsToken tok = { - .type = STT_LITERAL, - .as.literal = literal.as.success - }; - return Success(SolsToken, charptr, tok); - } - - if (isDouble) { - double newDouble = atof(token); - ResultType(SolsLiteral, charptr) literal = createSolsLiteral(SLT_DOUBLE, newDouble); - if (literal.error) { - Estr str = CREATE_ESTR(literal.as.error); - APPEND_ESTR(str, " (in identifyToken() function)"); - return Error(SolsToken, charptr, str.str); - } - SolsToken tok = { - .type = STT_LITERAL, - .as.literal = literal.as.success - }; - return Success(SolsToken, charptr, tok); - } - } - - // Handle boolean (true/false) - if (strcmp(token, "true") == 0) { - ResultType(SolsLiteral, charptr) literal = createSolsLiteral(SLT_BOOL, true); - if (literal.error) { - Estr str = CREATE_ESTR(literal.as.error); - APPEND_ESTR(str, " (in identifyToken() function)"); - return Error(SolsToken, charptr, str.str); - } - SolsToken tok = { - .type = STT_LITERAL, - .as.literal = literal.as.success - }; - return Success(SolsToken, charptr, tok); - } - if (strcmp(token, "false") == 0) { - ResultType(SolsLiteral, charptr) literal = createSolsLiteral(SLT_BOOL, false); - if (literal.error) { - Estr str = CREATE_ESTR(literal.as.error); - APPEND_ESTR(str, " (in identifyToken() function)"); - return Error(SolsToken, charptr, str.str); - } - SolsToken tok = { - .type = STT_LITERAL, - .as.literal = literal.as.success - }; - return Success(SolsToken, charptr, tok); - } - - // Process base types - if (strcmp(token, "int") == 0) { - ResultType(SolsType, charptr) type = createSolsType(STT_INT); - if (type.error) { - Estr e = CREATE_ESTR(type.as.error); - APPEND_ESTR(e, " (in identifyToken() function)"); - return Error(SolsToken, charptr, e.str); - } - SolsToken tok = { - .type = STT_TYPE, - .as.type = type.as.success - }; - return Success(SolsToken, charptr, tok); - } - - if (strcmp(token, "double") == 0) { - ResultType(SolsType, charptr) type = createSolsType(STT_DOUBLE); - if (type.error) { - Estr e = CREATE_ESTR(type.as.error); - APPEND_ESTR(e, " (in identifyToken() function)"); - return Error(SolsToken, charptr, e.str); - } - SolsToken tok = { - .type = STT_TYPE, - .as.type = type.as.success - }; - return Success(SolsToken, charptr, tok); - } - - if (strcmp(token, "string") == 0) { - ResultType(SolsType, charptr) type = createSolsType(STT_STRING); - if (type.error) { - Estr e = CREATE_ESTR(type.as.error); - APPEND_ESTR(e, " (in identifyToken() function)"); - return Error(SolsToken, charptr, e.str); - } - SolsToken tok = { - .type = STT_TYPE, - .as.type = type.as.success - }; - return Success(SolsToken, charptr, tok); - } - - if (strcmp(token, "char") == 0) { - ResultType(SolsType, charptr) type = createSolsType(STT_CHAR); - if (type.error) { - Estr e = CREATE_ESTR(type.as.error); - APPEND_ESTR(e, " (in identifyToken() function)"); - return Error(SolsToken, charptr, e.str); - } - SolsToken tok = { - .type = STT_TYPE, - .as.type = type.as.success - }; - return Success(SolsToken, charptr, tok); - } - - if (strcmp(token, "bool") == 0) { - ResultType(SolsType, charptr) type = createSolsType(STT_BOOL); - if (type.error) { - Estr e = CREATE_ESTR(type.as.error); - APPEND_ESTR(e, " (in identifyToken() function)"); - return Error(SolsToken, charptr, e.str); - } - SolsToken tok = { - .type = STT_TYPE, - .as.type = type.as.success - }; - return Success(SolsToken, charptr, tok); - } - - // Find if it's a reserved keyword/operator - ResultType(SolsTokenType, Nothing) result = getTokenType(token); - if (!result.error) { - return Success(SolsToken, charptr, {result.as.success}); - } - - // No appropriate token found, it's an identifier (I hope) - SolsToken id = { - .type = STT_IDENTIFIER, - .as.idName = malloc(strlen(token) + 1) - }; - - if (id.as.idName == NULL) { - return Error(SolsToken, charptr, "Couldn't allocate memory to copy string (in identifyToken() function)"); - } - strcpy(id.as.idName, token); - - return Success(SolsToken, charptr, id); - -} - -char* createLexingError(size_t lineNum, char* line, char* why) { - Estr error = CREATE_ESTR(ESC_RESET ESC_BOLD ESC_RED_FG "Lexing Error " ESC_RESET ESC_YELLOW_FG "on line "); - char buf[256]; - snprintf(buf, sizeof(buf), "%zu", lineNum); - APPEND_ESTR(error, buf); - APPEND_ESTR(error, ":\n\n" ESC_RESET ESC_BLUE_FG " "); - APPEND_ESTR(error, line); - APPEND_ESTR(error, "\n\n"); - APPEND_ESTR(error, ESC_RESET ESC_MAGENTA_FG "-> "); - APPEND_ESTR(error, why); - APPEND_ESTR(error, "\n"); - return error.str; -} - -ResultType(Nothing, charptr) lex(SolsLexer* lexer) { - if (lexer->input == NULL) { - return Error(Nothing, charptr, "Lexer is not initialised"); - } - - Estr buf = CREATE_ESTR(""); - bool inString = false; - - size_t lineNum = 1; - size_t lineStart = 0; - Estr currentLine = CREATE_ESTR(""); - - for (; lineStart < lexer->inputsize; lineStart++) { - if (lexer->input[lineStart] == '\n') { - break; - } - char tmp[] = {lexer->input[lineStart], '\0'}; - APPEND_ESTR(currentLine, tmp); - } - - bool skipDelimiter = false; - for (;;) { - ResultType(char, Nothing) chr = lexerConsume(lexer); - - if (chr.error) { - break; - } - - skipDelimiter = false; - - if (chr.as.success == '/' && !inString) { - ResultType(char, Nothing) peek = lexerPeek(lexer, 1); - if (!peek.error && peek.as.success == '/') { - // Consume characters until \n or EOF - while (true) { - ResultType(char, Nothing) next = lexerPeek(lexer, 1); - if (next.error || next.as.success == '\n') break; - lexerConsume(lexer); - } - continue; - } else if (!peek.error && peek.as.success == '*') { - // Skip the * - lexerConsume(lexer); - // Consume characters until */ or EOF - while (true) { - ResultType(char, Nothing) next = lexerConsume(lexer); - if (next.error) break; - if (next.as.success == '\n') { - lineNum++; - DESTROY_ESTR(currentLine); - currentLine = CREATE_ESTR(""); - lineStart = lexer->current; - for (size_t i = lineStart; i < lexer->inputsize; i++) { - if (lexer->input[i] == '\n') break; - char tmp[] = {lexer->input[i], '\0'}; - APPEND_ESTR(currentLine, tmp); - } - } - if (next.as.success == '*') { - ResultType(char, Nothing) peek2 = lexerPeek(lexer, 1); - if (!peek2.error && peek2.as.success == '/') { - lexerConsume(lexer); // skip / - break; - } - } - } - continue; - } - } - if (chr.as.success == '#' && !inString) { - while (true) { - ResultType(char, Nothing) next = lexerPeek(lexer, 1); - if (next.error || next.as.success == '\n') break; - lexerConsume(lexer); - } - continue; - } - - if (chr.as.success == '\n') { - lineNum++; - DESTROY_ESTR(currentLine); - currentLine = CREATE_ESTR(""); - lineStart = lexer->current; - for (size_t i = lineStart; i < lexer->inputsize; i++) { - if (lexer->input[i] == '\n') { - break; - } - char buf_tmp[] = {lexer->input[i], '\0'}; - APPEND_ESTR(currentLine, buf_tmp); - } - } - - if (inString) { - char str[2] = { chr.as.success, '\0' }; - APPEND_ESTR(buf, str); - if (chr.as.success == '"') { - inString = false; - } - continue; - } - - switch (chr.as.success) { - case '"': { - inString = true; - APPEND_ESTR(buf, "\""); - break; - } - - // These characters require themselves added seperately from the previous token. - case '{': - case '}': - case '(': - case ')': - case ',': - case ':': - case ';': - case '\n': - { - ResultType(Nothing, charptr) res = identifyAndAdd(lexer, &buf, &lineNum, ¤tLine, chr.as.success, &skipDelimiter); - if (res.error) { - char* err = createLexingError(lineNum, currentLine.str, res.as.error); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - - if (skipDelimiter) break; - - char tmp[] = {chr.as.success, '\0'}; - ResultType(SolsToken, charptr) result = identifyToken(tmp); - if (result.error) { - char* err = createLexingError(lineNum, currentLine.str, result.as.error); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - result.as.success.line.num = lineNum; - result.as.success.line.content = malloc(strlen(currentLine.str) + 1); - if (result.as.success.line.content == NULL) { - char* err = createLexingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - strcpy(result.as.success.line.content, currentLine.str); - - addTokenToSolsTokens(&lexer->output, result.as.success); - break; - } - - // These characters may be repeated, or followed by an equals sign. - case '+': - case '-': { - ResultType(Nothing, charptr) res = identifyAndAdd(lexer, &buf, &lineNum, ¤tLine, chr.as.success, &skipDelimiter); - if (res.error) { - char* err = createLexingError(lineNum, currentLine.str, res.as.error); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - // skipDelimiter is unlikely here but handled just in case - if (skipDelimiter) break; - - ResultType(char, Nothing) next = lexerPeek(lexer, 1); - if (next.error || (next.as.success != chr.as.success && next.as.success != '=')) { - char tmp[] = {chr.as.success, '\0'}; - ResultType(SolsToken, charptr) result = identifyToken(tmp); - if (result.error) { - char* err = createLexingError(lineNum, currentLine.str, result.as.error); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - result.as.success.line.num = lineNum; - result.as.success.line.content = malloc(strlen(currentLine.str) + 1); - if (result.as.success.line.content == NULL) { - char* err = createLexingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - strcpy(result.as.success.line.content, currentLine.str); - addTokenToSolsTokens(&lexer->output, result.as.success); - } - if (next.as.success == '=') { - char tmp[] = {chr.as.success, '=', '\0'}; - ResultType(SolsToken, charptr) result = identifyToken(tmp); - if (result.error) { - char* err = createLexingError(lineNum, currentLine.str, result.as.error); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - result.as.success.line.num = lineNum; - result.as.success.line.content = malloc(strlen(currentLine.str) + 1); - if (result.as.success.line.content == NULL) { - char* err = createLexingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - strcpy(result.as.success.line.content, currentLine.str); - addTokenToSolsTokens(&lexer->output, result.as.success); - lexerConsume(lexer); - } - if (next.as.success == chr.as.success) { - char tmp[] = {chr.as.success, chr.as.success, '\0'}; - ResultType(SolsToken, charptr) result = identifyToken(tmp); - if (result.error) { - char* err = createLexingError(lineNum, currentLine.str, result.as.error); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - result.as.success.line.num = lineNum; - result.as.success.line.content = malloc(strlen(currentLine.str) + 1); - if (result.as.success.line.content == NULL) { - char* err = createLexingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - strcpy(result.as.success.line.content, currentLine.str); - addTokenToSolsTokens(&lexer->output, result.as.success); - lexerConsume(lexer); - } - break; - } - - // These characters may be followed by an equals sign, or nothing else. - case '=': - case '!': - case '>': - case '<': - case '*': - case '/': { - ResultType(Nothing, charptr) res = identifyAndAdd(lexer, &buf, &lineNum, ¤tLine, chr.as.success, &skipDelimiter); - if (res.error) { - char* err = createLexingError(lineNum, currentLine.str, res.as.error); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - if (skipDelimiter) break; - - ResultType(char, Nothing) next = lexerPeek(lexer, 1); - if (next.error || next.as.success != '=') { - char tmp[] = {chr.as.success, '\0'}; - ResultType(SolsToken, charptr) result = identifyToken(tmp); - if (result.error) { - char* err = createLexingError(lineNum, currentLine.str, result.as.error); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - result.as.success.line.num = lineNum; - result.as.success.line.content = malloc(strlen(currentLine.str) + 1); - if (result.as.success.line.content == NULL) { - char* err = createLexingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - strcpy(result.as.success.line.content, currentLine.str); - addTokenToSolsTokens(&lexer->output, result.as.success); - } - if (next.as.success == '=') { - char tmp[] = {chr.as.success, '=', '\0'}; - ResultType(SolsToken, charptr) result = identifyToken(tmp); - if (result.error) { - char* err = createLexingError(lineNum, currentLine.str, result.as.error); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - result.as.success.line.num = lineNum; - result.as.success.line.content = malloc(strlen(currentLine.str) + 1); - if (result.as.success.line.content == NULL) { - char* err = createLexingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - strcpy(result.as.success.line.content, currentLine.str); - addTokenToSolsTokens(&lexer->output, result.as.success); - lexerConsume(lexer); - } - break; - } - - - // '.' requires checking whether it's a number or an identifier after - case '.': { - ResultType(char, Nothing) peek = lexerPeek(lexer, 1); - // If the next character is a digit, then this is a literal, not a member access dot. - if (!peek.error && isdigit(peek.as.success)) { - APPEND_ESTR(buf, "."); - } else { - ResultType(Nothing, charptr) res = identifyAndAdd(lexer, &buf, &lineNum, ¤tLine, chr.as.success, &skipDelimiter); - if (res.error) { - char* err = createLexingError(lineNum, currentLine.str, res.as.error); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - if (!skipDelimiter) { - addTokenToSolsTokens(&lexer->output, (SolsToken) {.type = STT_DOT}); - } - } - break; - } - - // This whitespace splits the program and does not get appended as it's own token. - case '\t': - case ' ': { - ResultType(Nothing, charptr) res = identifyAndAdd(lexer, &buf, &lineNum, ¤tLine, chr.as.success, &skipDelimiter); - if (res.error) { - char* err = createLexingError(lineNum, currentLine.str, res.as.error); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - break; - } - default: { - char newchar[] = {chr.as.success, '\0'}; - APPEND_ESTR(buf, newchar); - break; - } - } - - // Check whether we need to parse types - if (strcmp(buf.str, "fun") == 0) { - if (!lexerPeek(lexer, 1).error && lexerPeek(lexer, 1).as.success == '(') { - // do stuff - } - } - - if (strcmp(buf.str, "template") == 0 ) { - if (!lexerPeek(lexer, 1).error && lexerPeek(lexer, 1).as.success == '(') { - - } - } - - if (strcmp(buf.str, "object") == 0 ) { - if (!lexerPeek(lexer, 1).error && lexerPeek(lexer, 1).as.success == '(') { - - } - } - } - - ResultType(Nothing, charptr) res = identifyAndAdd(lexer, &buf, &lineNum, ¤tLine, '\0', &skipDelimiter); - if (res.error) { - char* err = createLexingError(lineNum, currentLine.str, res.as.error); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - - if (inString) { - char* err = createLexingError(lineNum, currentLine.str, "Unterminated string"); - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Error(Nothing, charptr, err); - } - - DESTROY_ESTR(buf); - DESTROY_ESTR(currentLine); - return Success(Nothing, charptr, (Nothing){}); -} - -ResultType(Nothing, charptr) processTypeSignature(SolsLexer* lexer) { - return Error(Nothing, charptr, "WIP (in processTypeSignature() function)"); -} diff --git a/src/lexer/lexer.h b/src/lexer/lexer.h deleted file mode 100644 index 6efa42b..0000000 --- a/src/lexer/lexer.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef LEXER_H -#define LEXER_H - -#include -#include -#include - -#include "../include/error.h" -#include "../include/nothing.h" - -#include "SolsType.h" -#include "SolsToken.h" -#include "SolsLiteral.h" - -// A map containing all corresponding strs and token types. -// Use the getTokenType() function to search this -extern struct _SolsTokenTypeMap {char* str; SolsTokenType type;} SolsTokenTypeMap[]; - -// Represents the current state of the lexer. -// .input is the Solstice program as written by the user. -// .output is the lexed Solstice program, which is constructed by the lex() function. -// .current represents the current character from .input being lexed. -typedef struct SolsLexer { - char* input; - size_t inputsize; - SolsTokens output; - size_t current; -} SolsLexer; - -Result(SolsLexer, charptr); - -// Creates a lexer for use by the lex() function. -// Returns: -// Success: Constructed SolsLexer -// Failure: char* detailing what went wrong (usually memory failure) -ResultType(SolsLexer, charptr) createLexer(char* input); - -// Uses the provided lexer to scan the code, and create tokens. -// Returne: -// Success: Nothing -// Failure: char* detailing what went wrong (usually user failure or memory failure) -ResultType(Nothing, charptr) lex(SolsLexer* lexer); - -Result(char, Nothing); - -// Peeks at the next token in the lexer. -// Returns: -// Success: The token with offset ahead -// Failure: Nothing (requested character is out of bounds) -ResultType(char, Nothing) lexerPeek(SolsLexer* lexer, size_t ahead); - -// Consumes the next token in the lexer. -// Success: The token that has just been consumed -// Failure: Nothing (requested character is out of bounds) -ResultType(char, Nothing) lexerConsume(SolsLexer* lexer); - -// Helper function to classify tokens -// Returns: -// Success: A SolsToken which has all information needed from the token. -// Failure: char* detailing what went wrong (usually memory failure) -ResultType(SolsToken, charptr) identifyToken(const char* token); - -Result(SolsTokenType, Nothing); - -// Helper function to convert a char* into a SolsTokenType using the SolsTokenTypeMap. -// Returns: -// Success: The corresponding SolsTokenType -// Failure: Nothing (meaning the token is likely an identifier) -ResultType(SolsTokenType, Nothing) getTokenType(const char* input); - -// Helper function to lex type signatures into tokens -// FIXME this function is a work in progress -ResultType(Nothing, charptr) processTypeSignature(SolsLexer* lexer); - - -#endif diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 49c0d84..0000000 --- a/src/main.c +++ /dev/null @@ -1,270 +0,0 @@ -#include "lexer/SolsType.h" -#include "lexer/lexer.h" -#include "typeparser/typeparser.h" -#include "parser/parser.h" -#include "codegen/codegen.h" -#include "include/error.h" -#include "include/estr.h" - -#include -#ifdef _WIN32 -#include -#else -#include -#endif -#include - -extern bool groundDisableTypeChecking; - -typedef enum SolsAction { - SA_PRINT, SA_EXEC, SA_BYTECODE, SA_COMPILE, SA_EXIT, SA_EXITOK -} SolsAction; - -typedef struct Args { - SolsAction action; - char* inputFile; - char* outputFile; -} Args; - -Args parseArgs(int argc, char** argv) { - Args args = { - .action = SA_EXEC, - .inputFile = NULL, - .outputFile = NULL - }; - for (int i = 0; i < argc; i++) { - if (strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) { - printf("Solstice programming language\n"); - printf("Usage: %s [-h] [--help] [-p] [--print] [-b ] [--bytecode ] [-c ] [--compile ]\n", argv[0]); - printf("Args:\n"); - printf(" : Solstice source file\n"); - printf(" -h or --help: Prints this help message and exits\n"); - printf(" -p or --print: Prints textual version of Ground bytecode to console\n"); - printf(" -b or --bytecode : Generates Ground bytecode (.grbc) and saves it to the provided filename\n"); - printf(" -c or --compile : Compiles Ground to Linux x86_64 assembly, outputs a binary to the provided filename (experimental)\n"); - printf(" If no extra arguments are provided, the generated Ground bytecode will be executed.\n"); - args.action = SA_EXIT; - return args; - } - else if (strcmp("-p", argv[i]) == 0 || strcmp("--print", argv[i]) == 0) { - args.action = SA_PRINT; - } - else if (strcmp("-b", argv[i]) == 0 || strcmp("--bytecode", argv[i]) == 0) { - if (args.action != SA_EXEC) { - printf("Expecting only one action\n"); - args.action = SA_EXIT; - return args; - } - args.action = SA_BYTECODE; - if (i + 1 >= argc) { - printf("Expecting file name after %s\n", argv[i]); - args.action = SA_EXIT; - return args; - } - i++; - args.outputFile = argv[i]; - } - else if (strcmp("-c", argv[i]) == 0 || strcmp("--compile", argv[i]) == 0) { - if (args.action != SA_EXEC) { - printf("Expecting only one action\n"); - args.action = SA_EXIT; - return args; - } - args.action = SA_COMPILE; - if (i + 1 >= argc) { - printf("Expecting file name after %s\n", argv[i]); - args.action = SA_EXIT; - return args; - } - i++; - args.outputFile = argv[i]; - } - else { - args.inputFile = argv[i]; - } - } - - if (args.inputFile == NULL) { - printf("Usage: %s [-h] [--help] [-p] [--print] [-b ] [--bytecode ] [-c ] [--compile ]\n", argv[0]); - args.action = SA_EXIT; - return args; - } - - return args; -} - -char* getFileContents(const char* filename) { - // https://stackoverflow.com/questions/3747086/reading-the-whole-text-file-into-a-char-array-in-c - FILE* fp; - long lSize; - char* file; - - fp = fopen(filename, "rb"); - if (!fp) { - perror(filename); - return NULL; - } - - fseek(fp, 0L, SEEK_END); - lSize = ftell(fp); - rewind(fp); - - file = calloc(1, lSize + 1); - if (!file) { - fclose(fp); - return NULL; - } - - if (1!=fread(file, lSize, 1, fp)) { - fclose(fp); - free(file); - return NULL; - } - - // we done - fclose(fp); - - return file; -} - -int main(int argc, char** argv) { - groundDisableTypeChecking = true; - - Args args = parseArgs(argc, argv); - - if (args.action == SA_EXIT) { - return 1; - } - if (args.action == SA_EXITOK) { - return 0; - } - - char* fileContents = getFileContents(args.inputFile); - - if (fileContents == NULL) { - printf("Couldn't read that file :(\n"); - return 1; - } - - // Lex file - ResultType(SolsLexer, charptr) lexer = createLexer(fileContents); - if (lexer.error) { - printf("Error while creating lexer: %s", lexer.as.error); - return 1; - } - ResultType(Nothing, charptr) lexed = lex(&lexer.as.success); - if (lexed.error) { - printf("%s\n", lexed.as.error); - return 1; - } - - // Detect and parse types - ResultType(SolsTokens, charptr) typed = addTypeInfo(&lexer.as.success.output); - if (typed.error) { - printf("%s\n", typed.as.error); - return 1; - } - - // Parse file - ResultType(SolsParser, charptr) parser = createSolsParser(&typed.as.success); - if (parser.error) { - printf("Error while creating parser: %s\n", parser.as.error); - return 1; - } - ResultType(Nothing, charptr) parsed = parse(&parser.as.success); - if (parsed.error) { - printf("%s\n", parsed.as.error); - return 1; - } - - SolsScope scope = { - .variables = NULL, - .tmpCounter = 0, - .returnType = createSolsType(STT_INT).as.success - }; - - // Do codegen on root node - ResultType(GroundProgram, charptr) codegen = generateCode(&parser.as.success.output, &scope); - if (codegen.error) { - printf("%s\n", codegen.as.error); - return 1; - } - - switch (args.action) { - case SA_PRINT: { - groundPrintProgram(&codegen.as.success); - break; - } - case SA_EXEC: { - GroundValue retval = groundRunProgram(&codegen.as.success); - if (retval.type == INT) { - return retval.data.intVal; - } else { - return 0; - } - break; - } - case SA_BYTECODE: { - serializeProgramToFile(args.outputFile, &codegen.as.success); - break; - } - case SA_COMPILE: { - char* compiled = groundCompileProgram(&codegen.as.success); - - // Make work directory - Estr dir = CREATE_ESTR(".solsbuild_"); - APPEND_ESTR(dir, args.outputFile); - - #ifdef _WIN32 - int dirstatus = _mkdir(estr.str); - #else - int dirstatus = mkdir(dir.str, 0755); - #endif - if (dirstatus != 0) { - printf("Couldn't create temporary work directory\n"); - return 1; - } - - // Write generated asm to .s - Estr tmpasm = CREATE_ESTR(dir.str); - APPEND_ESTR(tmpasm, "/generated.s"); - - FILE* file = fopen(tmpasm.str, "w"); - if (file == NULL) { - printf("Couldn't write to temporary assembly file"); - } - fprintf(file, "%s", compiled); - fclose(file); - - // Assemble with nasm - Estr nasmcmd = CREATE_ESTR("nasm -f elf64 -o "); - APPEND_ESTR(nasmcmd, dir.str); - APPEND_ESTR(nasmcmd, "/generated.o "); - APPEND_ESTR(nasmcmd, tmpasm.str); - - int nasmstatus = system(nasmcmd.str); - if (nasmstatus != 0) { - printf("command \"%s\" exited with code %d\n", nasmcmd.str, nasmstatus); - return 1; - } - - // Link with ld - Estr ldcmd = CREATE_ESTR("ld -o "); - APPEND_ESTR(ldcmd, args.outputFile); - APPEND_ESTR(ldcmd, " "); - APPEND_ESTR(ldcmd, dir.str); - APPEND_ESTR(ldcmd, "/generated.o"); - - int ldstatus = system(ldcmd.str); - if (ldstatus != 0) { - printf("command \"%s\" exited with code %d\n", ldcmd.str, ldstatus); - return 1; - } - - // Yay we compiled it - break; - } - } - - return 0; -} diff --git a/src/main.sols b/src/main.sols new file mode 100644 index 0000000..e69de29 diff --git a/src/parser/SolsNode.c b/src/parser/SolsNode.c deleted file mode 100644 index 8777c22..0000000 --- a/src/parser/SolsNode.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "SolsNode.h" - -#include -#include - -#include "../include/error.h" -#include "../lexer/SolsLiteral.h" -#include "../lexer/SolsType.h" - -ResultType(SolsNode, charptr) createSolsNode(SolsNodeType type, ...) { - va_list args; - va_start(args, type); - - SolsNode node = { - .type = type, - .children.capacity = 32, - .children.count = 0, - .children.at = malloc(sizeof(SolsNode) * 32) - }; - - if (node.children.at == NULL) { - return Error(SolsNode, charptr, "Failed to allocate memory for children (in createSolsNode() function)"); - } - - switch (type) { - case SNT_LITERAL: { - node.as.literal = va_arg(args, SolsLiteral); - break; - } - case SNT_TYPE: { - node.as.type = va_arg(args, SolsType); - break; - } - case SNT_IDENTIFIER: { - node.as.idName = va_arg(args, char*); - break; - } - case SNT_GROUND: { - node.as.inlineGround = va_arg(args, char*); - break; - } - default: break; - } - - va_end(args); - return Success(SolsNode, charptr, node); -} - -ResultType(Nothing, charptr) addChildToSolsNode(SolsNode* parent, SolsNode child) { - if (parent->children.count + 1 >= parent->children.capacity) { - parent->children.capacity *= 2; - SolsNode* tmp = realloc(parent->children.at, sizeof(SolsNode) * parent->children.capacity); - if (tmp == NULL) { - return Error(Nothing, charptr, "Failed to increase memory for children (in addChildToSolsNode() function)"); - } - parent->children.at = tmp; - } - parent->children.at[parent->children.count] = deepCopySolsNode(child); - parent->children.count++; - return Success(Nothing, charptr, {}); -} - -SolsNode deepCopySolsNode(SolsNode node) { - SolsNode copy = node; - copy.children.at = malloc(sizeof(SolsNode) * node.children.capacity); - for (size_t i = 0; i < node.children.count; i++) { - copy.children.at[i] = deepCopySolsNode(node.children.at[i]); - } - return copy; -} diff --git a/src/parser/SolsNode.h b/src/parser/SolsNode.h deleted file mode 100644 index 639ff58..0000000 --- a/src/parser/SolsNode.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef SOLSNODE_H -#define SOLSNODE_H - -#include -#include - -#include "../include/error.h" - -#include "../lexer/SolsType.h" -#include "../lexer/SolsLiteral.h" -#include "../lexer/SolsToken.h" - -typedef enum SolsNodeType { - SNT_IDENTIFIER, SNT_LITERAL, SNT_TYPE, - SNT_CODE_BLOCK, - SNT_OP_ADD, SNT_OP_SUB, SNT_OP_MUL, SNT_OP_DIV, - SNT_OP_ADDTO, SNT_OP_SUBTO, SNT_OP_MULTO, SNT_OP_DIVTO, - SNT_OP_INCREMENT, SNT_OP_DECREMENT, - SNT_OP_SET, - SNT_OP_GREATER, SNT_OP_LESSER, SNT_OP_EQUAL, SNT_OP_INEQUAL, SNT_OP_EQGREATER, SNT_OP_EQLESSER, - SNT_DEF, SNT_LAMBDA, SNT_FUNCTION_CALL, SNT_RETURN, - SNT_SET_PRIVATE, SNT_SET_PROTECTED, SNT_DEF_PRIVATE, SNT_DEF_PROTECTED, - SNT_USE, SNT_STRUCT, SNT_CONSTRUCTOR, SNT_DESTRUCTOR, - SNT_PUTS, SNT_IF, SNT_WHILE, SNT_NEW, - SNT_GROUND, SNT_ROOT, SNT_EXPR_IN_PAREN, SNT_DOT, -} SolsNodeType; - -struct SolsNode; - -// Represents an AST node. -// Most node types use the .type and .children fields, however some nodes require storing -// more data, inside the .as union. -// Those tokens are: -// SNT_LITERAL: A literal value. Uses field .as.literal -// SNT_TYPE: A type descriptor. Uses field .as.type -// SNT_IDENTIFIER: An identifier. Uses field .as.idName -// SNT_GROUND: Ground code embedded inside Solstice. Uses field .as.inlineGround -typedef struct SolsNode { - SolsNodeType type; - union { - SolsLiteral literal; - SolsType type; - char* idName; - char* inlineGround; - } as; - struct { - size_t count; - size_t capacity; - struct SolsNode* at; - } children; - LineInfo line; - - // Used by the code generator, unless value is a literal do not use in parser - GroundArg accessArg; -} SolsNode; - -Result(SolsNode, charptr); - -// Creates a SolsNode. If the type passed in is SNT_LITERAL, SNT_TYPE, SNT_IDENTIFIER or -// SNT_KW_GROUND, the function expects another argument, corresponding to the data type -// the token holds. See the SolsNode struct for more information. -// Returns: -// Success: The created SolsNode -// Failure: char* detailing what went wrong (usually memory failure) -ResultType(SolsNode, charptr) createSolsNode(SolsNodeType type, ...); - -// Adds a child to a SolsNode. -// Returns: -// Success: Nothing -// Failure: char* detailing what went wrong (usually memory failure) -ResultType(Nothing, charptr) addChildToSolsNode(SolsNode* parent, SolsNode child); - -// Deep copies a SolsNode -SolsNode deepCopySolsNode(SolsNode node); - -#endif diff --git a/src/parser/parser.c b/src/parser/parser.c deleted file mode 100644 index 2d530fd..0000000 --- a/src/parser/parser.c +++ /dev/null @@ -1,2165 +0,0 @@ -#include "parser.h" -#include "SolsNode.h" - -#include "../include/estr.h" -#include "../include/ansii.h" -#include - -SolsTokenPrecedence getPrecedence(SolsToken *token) { - static size_t braceCount = 0; - switch (token->type) { - case STT_OPEN_CURLY: { - braceCount++; - return STP_OTHER; - } - case STT_CLOSE_CURLY: { - braceCount--; - return STP_OTHER; - } - default: break; - } - if (braceCount > 0) { - return STP_OTHER; - } - switch (token->type) { - case STT_LINE_END: return STP_NEWLINE; - case STT_KW_PUTS: return STP_PUTS; - case STT_KW_IF: return STP_IF; - case STT_KW_WHILE: return STP_WHILE; - case STT_OP_ADDTO: - case STT_OP_SUBTO: - case STT_OP_MULTO: - case STT_OP_DIVTO: - case STT_OP_INCREMENT: - case STT_OP_DECREMENT: - case STT_OP_SET: return STP_SET; - // FIXME figure out how to do functions - case STT_OP_MUL: case STT_OP_DIV: return STP_MUL; - case STT_OP_ADD: case STT_OP_SUB: return STP_ADD; - case STT_OP_EQUAL: - case STT_OP_EQGREATER: - case STT_OP_EQLESSER: - case STT_OP_INEQUAL: - case STT_OP_GREATER: - case STT_OP_LESSER: return STP_COMPARE; - default: return STP_OTHER; - } -} - -ResultType(SolsParser, charptr) createSolsParser(SolsTokens* input) { - ResultType(SolsNode, charptr) node = createSolsNode(SNT_ROOT); - if (node.error) { - Estr str = CREATE_ESTR(node.as.error); - APPEND_ESTR(str, " (in createSolsParser() function)"); - return Error(SolsParser, charptr, str.str); - } - SolsParser parser = { - .input = input, - .current = 0, - .output = node.as.success, - .errors.capacity = 32, - .errors.count = 0, - .errors.at = malloc(sizeof(char*) * 32) - }; - if (parser.errors.at == NULL) { - return Error(SolsParser, charptr, "Couldn't allocate memory to store errors (in createSolsParser() function)"); - } - return Success(SolsParser, charptr, parser); -} - -void addToParserErrors(SolsParser* parser, char* errors) { - if (parser->errors.count + 1 >= parser->errors.capacity) { - parser->errors.capacity *= 2; - char** tmp = realloc(parser->errors.at, sizeof(char*) * parser->errors.capacity); - if (tmp == NULL) { - parser->errors.at[parser->errors.count - 1] = "Failed to allocate more memory for addToParserErrors function"; - return; - } - parser->errors.at = tmp; - } - parser->errors.at[parser->errors.count] = errors; - parser->errors.count++; -} - -void createParserError(SolsParser* parser, char* what) { - - ResultType(SolsToken, Nothing) prevToken = Error(SolsToken, Nothing, {}); - SolsToken token = parserPeek(parser, 0).as.success; - ResultType(SolsToken, Nothing) nextToken = Error(SolsToken, Nothing, {}); - - // Find tokens for previous line - if (parser->current > 1) { - for (size_t i = parser->current - 1; i > 0; i--) { - ResultType(SolsToken, Nothing) check = parserLookAt(parser, i - 1); - if (check.error) break; - if (check.as.success.line.num != token.line.num) { - prevToken = check; - break; - } - } - } - - // Find tokens for next line - for (size_t i = parser->current; i < parser->input->count; i++) { - ResultType(SolsToken, Nothing) check = parserLookAt(parser, i); - if (check.error) break; - if (check.as.success.line.num != token.line.num) { - nextToken = check; - break; - } - } - - if (parser->errors.count + 1 >= parser->errors.capacity) { - parser->errors.capacity *= 2; - char** tmp = realloc(parser->errors.at, sizeof(char*) * parser->errors.capacity); - if (tmp == NULL) { - parser->errors.at[parser->errors.count - 1] = "Failed to allocate more memory for createParserError function"; - return; - } - parser->errors.at = tmp; - } - Estr err = CREATE_ESTR(ESC_BOLD ESC_RED_FG "error: " ESC_RESET ESC_BOLD); - - // Append the main error message and a newline - APPEND_ESTR(err, what); - APPEND_ESTR(err, "\n" ESC_RESET); - - APPEND_ESTR(err, ESC_CYAN_FG "-> " ESC_RESET); - APPEND_ESTR(err, "on line "); - - // Format the line number - char line_buf[16]; - snprintf(line_buf, sizeof(line_buf), "%zu", token.line.num); - APPEND_ESTR(err, line_buf); - - APPEND_ESTR(err, "\n\n"); - // Append line before - if (!prevToken.error) { - if (prevToken.as.success.line.content != NULL) { - APPEND_ESTR(err, prevToken.as.success.line.content); - APPEND_ESTR(err, "\n"); - } - } - // Append the actual content of the line that failed - APPEND_ESTR(err, token.line.content); - APPEND_ESTR(err, "\n"); - if (!nextToken.error) { - APPEND_ESTR(err, nextToken.as.success.line.content); - APPEND_ESTR(err, "\n"); - } - - // Output the final constructed error - parser->errors.at[parser->errors.count] = err.str; - parser->errors.count++; -} - -static inline ResultType(Nothing, charptr) parseIdentifier(SolsParser* parser) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) { - return Error(Nothing, charptr, "ruh roh"); - } - ResultType(SolsNode, charptr) node = createSolsNode(SNT_IDENTIFIER, peek.as.success.as.idName); - if (node.error) { - Estr err = CREATE_ESTR(node.as.error); - APPEND_ESTR(err, " (in parseLiteral() function)"); - return Error(Nothing, charptr, err.str); - } - node.as.success.line = peek.as.success.line; - node.as.success.accessArg = (GroundArg) { - .type = VALREF, - .value.refName = peek.as.success.as.idName - }; - addChildToSolsNode(parser->currentParent, node.as.success); - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseSet(SolsParser* parser) { - if (parser->currentParent->children.count < 1) { - return Error(Nothing, charptr, "Expecting identifier before '='"); - } - if (parser->currentParent->children.at[parser->output.children.count - 1].type != SNT_IDENTIFIER && - parser->currentParent->children.at[parser->output.children.count - 1].type != SNT_DOT) { - return Error(Nothing, charptr, "Expecting identifier before '='"); - } - - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - // Get the identifier (previous node) - SolsNode idNode = parser->currentParent->children.at[parser->currentParent->children.count - 1]; - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - size_t bracketCount = 0; - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - - if (peek.as.success.type == STT_OPEN_CURLY) bracketCount++; - else if (peek.as.success.type == STT_CLOSE_CURLY && bracketCount > 0) bracketCount--; - - if (bracketCount == 0 && getPrecedence(&peek.as.success) <= STP_SET) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_OP_SET); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) putsParser = createSolsParser(&tokens.as.success); - if (putsParser.error) return Error(Nothing, charptr, putsParser.as.error); - putsParser.as.success.currentParent = &putsParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&putsParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - if (putsParser.as.success.output.children.count < 1) { - return Error(Nothing, charptr, "Expecting token after '='"); - } - - // Copy idnode into set node - addChildToSolsNode(&node.as.success, idNode); - - // Copy nodes into the set node - for (size_t i = 0; i < putsParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, putsParser.as.success.output.children.at[i]); - } - - // Put the set node where the idNode was - parser->currentParent->children.count--; - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseAdd(SolsParser* parser) { - if (parser->currentParent->children.count < 1) { - return Error(Nothing, charptr, "Expecting something before '+'"); - } - - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - // Get the previous node - SolsNode idNode = parser->currentParent->children.at[parser->currentParent->children.count - 1]; - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - if (getPrecedence(&peek.as.success) <= STP_ADD) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_OP_ADD); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) putsParser = createSolsParser(&tokens.as.success); - if (putsParser.error) return Error(Nothing, charptr, putsParser.as.error); - putsParser.as.success.currentParent = &putsParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&putsParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - if (putsParser.as.success.output.children.count < 1) { - return Error(Nothing, charptr, "Expecting token after '+'"); - } - - // Copy idnode into set node - addChildToSolsNode(&node.as.success, idNode); - - // Copy nodes into the set node - for (size_t i = 0; i < putsParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, putsParser.as.success.output.children.at[i]); - } - - // Put the set node where the idNode was - parser->currentParent->children.count--; - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseSub(SolsParser* parser) { - if (parser->currentParent->children.count < 1) { - return Error(Nothing, charptr, "Expecting something before '-'"); - } - - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - // Get the previous node - SolsNode idNode = parser->currentParent->children.at[parser->currentParent->children.count - 1]; - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - if (getPrecedence(&peek.as.success) <= STP_ADD) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_OP_SUB); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) putsParser = createSolsParser(&tokens.as.success); - if (putsParser.error) return Error(Nothing, charptr, putsParser.as.error); - putsParser.as.success.currentParent = &putsParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&putsParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - if (putsParser.as.success.output.children.count < 1) { - return Error(Nothing, charptr, "Expecting token after '-'"); - } - - // Copy idnode into set node - addChildToSolsNode(&node.as.success, idNode); - - // Copy nodes into the set node - for (size_t i = 0; i < putsParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, putsParser.as.success.output.children.at[i]); - } - - // Put the set node where the idNode was - parser->currentParent->children.count--; - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseMul(SolsParser* parser) { - if (parser->currentParent->children.count < 1) { - return Error(Nothing, charptr, "Expecting something before '*'"); - } - - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - // Get the previous node - SolsNode idNode = parser->currentParent->children.at[parser->currentParent->children.count - 1]; - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - if (getPrecedence(&peek.as.success) <= STP_MUL) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_OP_MUL); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) putsParser = createSolsParser(&tokens.as.success); - if (putsParser.error) return Error(Nothing, charptr, putsParser.as.error); - putsParser.as.success.currentParent = &putsParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&putsParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - if (putsParser.as.success.output.children.count < 1) { - return Error(Nothing, charptr, "Expecting token after '*'"); - } - - // Copy idnode into set node - addChildToSolsNode(&node.as.success, idNode); - - // Copy nodes into the set node - for (size_t i = 0; i < putsParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, putsParser.as.success.output.children.at[i]); - } - - // Put the set node where the idNode was - parser->currentParent->children.count--; - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseDiv(SolsParser* parser) { - if (parser->currentParent->children.count < 1) { - return Error(Nothing, charptr, "Expecting something before '/'"); - } - - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - // Get the previous node - SolsNode idNode = parser->currentParent->children.at[parser->currentParent->children.count - 1]; - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - if (getPrecedence(&peek.as.success) <= STP_MUL) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_OP_DIV); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) putsParser = createSolsParser(&tokens.as.success); - if (putsParser.error) return Error(Nothing, charptr, putsParser.as.error); - putsParser.as.success.currentParent = &putsParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&putsParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - if (putsParser.as.success.output.children.count < 1) { - return Error(Nothing, charptr, "Expecting token after '/'"); - } - - // Copy idnode into set node - addChildToSolsNode(&node.as.success, idNode); - - // Copy nodes into the set node - for (size_t i = 0; i < putsParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, putsParser.as.success.output.children.at[i]); - } - - // Put the set node where the idNode was - parser->currentParent->children.count--; - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseEqual(SolsParser* parser) { - if (parser->currentParent->children.count < 1) { - return Error(Nothing, charptr, "Expecting something before '=='"); - } - - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - // Get the previous node - SolsNode idNode = parser->currentParent->children.at[parser->currentParent->children.count - 1]; - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - if (getPrecedence(&peek.as.success) <= STP_COMPARE) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_OP_EQUAL); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) putsParser = createSolsParser(&tokens.as.success); - if (putsParser.error) return Error(Nothing, charptr, putsParser.as.error); - putsParser.as.success.currentParent = &putsParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&putsParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - if (putsParser.as.success.output.children.count < 1) { - return Error(Nothing, charptr, "Expecting token after '=='"); - } - - // Copy idnode into set node - addChildToSolsNode(&node.as.success, idNode); - - // Copy nodes into the set node - for (size_t i = 0; i < putsParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, putsParser.as.success.output.children.at[i]); - } - - // Put the equal node where the idNode was - parser->currentParent->children.count--; - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseInequal(SolsParser* parser) { - if (parser->currentParent->children.count < 1) { - return Error(Nothing, charptr, "Expecting something before '!='"); - } - - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - // Get the previous node - SolsNode idNode = parser->currentParent->children.at[parser->currentParent->children.count - 1]; - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - if (getPrecedence(&peek.as.success) <= STP_COMPARE) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_OP_INEQUAL); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) putsParser = createSolsParser(&tokens.as.success); - if (putsParser.error) return Error(Nothing, charptr, putsParser.as.error); - putsParser.as.success.currentParent = &putsParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&putsParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - if (putsParser.as.success.output.children.count < 1) { - return Error(Nothing, charptr, "Expecting token after '!='"); - } - - // Copy idnode into set node - addChildToSolsNode(&node.as.success, idNode); - - // Copy nodes into the set node - for (size_t i = 0; i < putsParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, putsParser.as.success.output.children.at[i]); - } - - // Put the equal node where the idNode was - parser->currentParent->children.count--; - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseGreater(SolsParser* parser) { - if (parser->currentParent->children.count < 1) { - return Error(Nothing, charptr, "Expecting something before '>'"); - } - - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - // Get the previous node - SolsNode idNode = parser->currentParent->children.at[parser->currentParent->children.count - 1]; - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - if (getPrecedence(&peek.as.success) <= STP_COMPARE) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_OP_GREATER); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) putsParser = createSolsParser(&tokens.as.success); - if (putsParser.error) return Error(Nothing, charptr, putsParser.as.error); - putsParser.as.success.currentParent = &putsParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&putsParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - if (putsParser.as.success.output.children.count < 1) { - return Error(Nothing, charptr, "Expecting token after '>'"); - } - - // Copy idnode into set node - addChildToSolsNode(&node.as.success, idNode); - - // Copy nodes into the set node - for (size_t i = 0; i < putsParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, putsParser.as.success.output.children.at[i]); - } - - // Put the equal node where the idNode was - parser->currentParent->children.count--; - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseLesser(SolsParser* parser) { - if (parser->currentParent->children.count < 1) { - return Error(Nothing, charptr, "Expecting something before '<'"); - } - - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - // Get the previous node - SolsNode idNode = parser->currentParent->children.at[parser->currentParent->children.count - 1]; - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - if (getPrecedence(&peek.as.success) <= STP_COMPARE) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_OP_LESSER); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) putsParser = createSolsParser(&tokens.as.success); - if (putsParser.error) return Error(Nothing, charptr, putsParser.as.error); - putsParser.as.success.currentParent = &putsParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&putsParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - if (putsParser.as.success.output.children.count < 1) { - return Error(Nothing, charptr, "Expecting token after '<'"); - } - - // Copy idnode into set node - addChildToSolsNode(&node.as.success, idNode); - - // Copy nodes into the set node - for (size_t i = 0; i < putsParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, putsParser.as.success.output.children.at[i]); - } - - // Put the equal node where the idNode was - parser->currentParent->children.count--; - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseEqGreater(SolsParser* parser) { - if (parser->currentParent->children.count < 1) { - return Error(Nothing, charptr, "Expecting something before '>='"); - } - - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - // Get the previous node - SolsNode idNode = parser->currentParent->children.at[parser->currentParent->children.count - 1]; - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - if (getPrecedence(&peek.as.success) <= STP_COMPARE) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_OP_EQGREATER); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) putsParser = createSolsParser(&tokens.as.success); - if (putsParser.error) return Error(Nothing, charptr, putsParser.as.error); - putsParser.as.success.currentParent = &putsParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&putsParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - if (putsParser.as.success.output.children.count < 1) { - return Error(Nothing, charptr, "Expecting token after '>='"); - } - - // Copy idnode into set node - addChildToSolsNode(&node.as.success, idNode); - - // Copy nodes into the set node - for (size_t i = 0; i < putsParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, putsParser.as.success.output.children.at[i]); - } - - // Put the equal node where the idNode was - parser->currentParent->children.count--; - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseEqLesser(SolsParser* parser) { - if (parser->currentParent->children.count < 1) { - return Error(Nothing, charptr, "Expecting something before '<='"); - } - - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - // Get the previous node - SolsNode idNode = parser->currentParent->children.at[parser->currentParent->children.count - 1]; - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - if (getPrecedence(&peek.as.success) <= STP_COMPARE) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_OP_EQLESSER); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) putsParser = createSolsParser(&tokens.as.success); - if (putsParser.error) return Error(Nothing, charptr, putsParser.as.error); - putsParser.as.success.currentParent = &putsParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&putsParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - if (putsParser.as.success.output.children.count < 1) { - return Error(Nothing, charptr, "Expecting token after '<='"); - } - - // Copy idnode into set node - addChildToSolsNode(&node.as.success, idNode); - - // Copy nodes into the set node - for (size_t i = 0; i < putsParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, putsParser.as.success.output.children.at[i]); - } - - // Put the equal node where the idNode was - parser->currentParent->children.count--; - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseLiteral(SolsParser* parser) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) { - return Error(Nothing, charptr, "ruh roh"); - } - ResultType(SolsNode, charptr) node = createSolsNode(SNT_LITERAL, peek.as.success.as.literal); - if (node.error) { - Estr err = CREATE_ESTR(node.as.error); - APPEND_ESTR(err, " (in parseLiteral() function)"); - return Error(Nothing, charptr, err.str); - } - node.as.success.line = peek.as.success.line; - GroundValue value; - switch (peek.as.success.as.literal.type) { - case SLT_INT: { - value = groundCreateValue(INT, peek.as.success.as.literal.as.intv); - break; - } - case SLT_DOUBLE: { - value = groundCreateValue(DOUBLE, peek.as.success.as.literal.as.doublev); - break; - } - case SLT_STRING: { - value = groundCreateValue(STRING, peek.as.success.as.literal.as.stringv); - break; - } - case SLT_BOOL: { - value = groundCreateValue(BOOL, peek.as.success.as.literal.as.boolv); - break; - } - case SLT_CHAR: { - value = groundCreateValue(CHAR, peek.as.success.as.literal.as.charv); - break; - } - } - node.as.success.accessArg = (GroundArg) { - .type = VALUE, - .value.value = value - }; - addChildToSolsNode(parser->currentParent, node.as.success); - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parsePuts(SolsParser* parser) { - - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - if (getPrecedence(&peek.as.success) <= STP_PUTS) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_PUTS); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) putsParser = createSolsParser(&tokens.as.success); - if (putsParser.error) return Error(Nothing, charptr, putsParser.as.error); - putsParser.as.success.currentParent = &putsParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&putsParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - // Copy nodes into the sols node - for (size_t i = 0; i < putsParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, putsParser.as.success.output.children.at[i]); - } - - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseCodeBlock(SolsParser* parser) { - - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - size_t bracketCount = 1; - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) return Error(Nothing, charptr, "Expecting closing curly brace (})"); - if (peek.as.success.type == STT_OPEN_CURLY) { - bracketCount++; - } - if (peek.as.success.type == STT_CLOSE_CURLY) { - bracketCount--; - if (bracketCount < 1) { - parserConsume(parser); - break; - } - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_CODE_BLOCK); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) codeBlockParser = createSolsParser(&tokens.as.success); - if (codeBlockParser.error) { - return Error(Nothing, charptr, codeBlockParser.as.error); - } - codeBlockParser.as.success.currentParent = &codeBlockParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&codeBlockParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - // Copy nodes into the sols node - for (size_t i = 0; i < codeBlockParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, codeBlockParser.as.success.output.children.at[i]); - } - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseWhile(SolsParser* parser) { - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - if (peek.as.success.type == STT_OPEN_CURLY) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_WHILE); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) whileParser = createSolsParser(&tokens.as.success); - if (whileParser.error) return Error(Nothing, charptr, whileParser.as.error); - whileParser.as.success.currentParent = &whileParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&whileParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - // Copy nodes into the sols node - for (size_t i = 0; i < whileParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, whileParser.as.success.output.children.at[i]); - } - - addChildToSolsNode(parser->currentParent, node.as.success); - - if(parserPeek(parser, 1).as.success.type != STT_OPEN_CURLY) { - return Error(Nothing, charptr, "Expecting opening curly brace for while loop body"); - } - - parserConsume(parser); - - 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* whileNode = &parser->currentParent->children.at[parser->currentParent->children.count - 1]; - addChildToSolsNode(whileNode, codeBlock); - - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseIf(SolsParser* parser) { - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - if (peek.as.success.type == STT_OPEN_CURLY) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_IF); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) ifParser = createSolsParser(&tokens.as.success); - if (ifParser.error) return Error(Nothing, charptr, ifParser.as.error); - ifParser.as.success.currentParent = &ifParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&ifParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - // Copy nodes into the sols node - for (size_t i = 0; i < ifParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, ifParser.as.success.output.children.at[i]); - } - - addChildToSolsNode(parser->currentParent, node.as.success); - - if(parserPeek(parser, 1).as.success.type != STT_OPEN_CURLY) { - return Error(Nothing, charptr, "Expecting opening curly brace for if statement body"); - } - - parserConsume(parser); - - 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* ifNode = &parser->currentParent->children.at[parser->currentParent->children.count - 1]; - addChildToSolsNode(ifNode, codeBlock); - - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseCloseCurly(SolsParser* parser) { - (void)parser; - return Error(Nothing, charptr, "Extra closing curly brace"); -} - -static inline ResultType(Nothing, charptr) parseLambda(SolsParser* parser) { - ResultType(SolsToken, Nothing) openBracket = parserConsume(parser); - if (openBracket.error || openBracket.as.success.type != STT_OPEN_PAREN) { - return Error(Nothing, charptr, "Expecting '(' after 'lambda'"); - } - - ResultType(SolsType, charptr) type = createSolsType(STT_FUN); - if (type.error) { - return Error(Nothing, charptr, type.as.error); - } - - ResultType(SolsNode, charptr) node = createSolsNode(SNT_LAMBDA); - if (node.error) { - return Error(Nothing, charptr, node.as.error); - } - - // Parse type signature - for (;;) { - ResultType(SolsToken, Nothing) next = parserPeek(parser, 1); - if (next.error) { - return Error(Nothing, charptr, "Expecting ')' at end of lambda 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 = createIdentifiedSolsType(next.as.success.as.idName).as.success; - } else { - return Error(Nothing, charptr, "Expecting a type or identifier of type in lambda 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 lambda 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 type at the end - ResultType(SolsToken, Nothing) retType = parserPeek(parser, 1); - if (retType.error) { - return Error(Nothing, charptr, "Expecting return type or identifier of type after lambda 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 = createIdentifiedSolsType(retType.as.success.as.idName).as.success; - } else { - return Error(Nothing, charptr, "Expecting return type or identifier of type after lambda 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 lambda 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* lambdaNode = &parser->currentParent->children.at[parser->currentParent->children.count - 1]; - addChildToSolsNode(lambdaNode, codeBlock); - - 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 = createIdentifiedSolsType(next.as.success.as.idName).as.success; - } 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 = createIdentifiedSolsType(retType.as.success.as.idName).as.success; - } 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, {}); -} - -static inline ResultType(Nothing, charptr) parseFunctionCall(SolsParser* parser) { - - ResultType(SolsNode, charptr) node = createSolsNode(SNT_FUNCTION_CALL); - if (node.error) { - return Error(Nothing, charptr, node.as.error); - } - - // The identifier (function name) was already parsed as a node right before '(' - if (parser->currentParent->children.count < 1) { - return Error(Nothing, charptr, "Expecting identifier before '(' for function call"); - } - - SolsNode callee = parser->currentParent->children.at[parser->currentParent->children.count - 1]; - parser->currentParent->children.count--; // remove callee identifier node; we'll replace it with the call node - - addChildToSolsNode(&node.as.success, callee); - node.as.success.line = callee.line; - - // Empty argument list: foo() - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) { - return Error(Nothing, charptr, "Expecting ')' or a value"); - } - if (peek.as.success.type == STT_CLOSE_PAREN) { - parserConsume(parser); // consume ')' - addChildToSolsNode(parser->currentParent, node.as.success); - return Success(Nothing, charptr, {}); - } - - // Parse each argument expression separated by commas until ')' - for (;;) { - ResultType(SolsTokens, charptr) resultTokens = createSolsTokens(); - if (resultTokens.error) { - return Error(Nothing, charptr, resultTokens.as.error); - } - SolsTokens tokens = resultTokens.as.success; - - size_t curlys = 0; - size_t parens = 0; - - for (;;) { - peek = parserPeek(parser, 1); - if (peek.error) { - return Error(Nothing, charptr, "Expecting ')'"); - } - - if (peek.as.success.type == STT_OPEN_PAREN) parens++; - if (peek.as.success.type == STT_CLOSE_PAREN) { - if (parens == 0) break; - parens--; - } - - if (peek.as.success.type == STT_OPEN_CURLY) curlys++; - if (peek.as.success.type == STT_CLOSE_CURLY) curlys--; - - if (curlys == 0 && parens == 0 && peek.as.success.type == STT_COMMA) { - break; - } - - addTokenToSolsTokens(&tokens, peek.as.success); - parserConsume(parser); - } - - ResultType(SolsParser, charptr) newParser = createSolsParser(&tokens); - if (newParser.error) { - return Error(Nothing, charptr, newParser.as.error); - } - newParser.as.success.currentParent = &newParser.as.success.output; - - ResultType(Nothing, charptr) parsed = parse(&newParser.as.success); - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - if (newParser.as.success.output.children.count < 1) { - return Error(Nothing, charptr, "Expecting a value before ',' or ')'"); - } - - // One argument expression -> one AST node - addChildToSolsNode(&node.as.success, newParser.as.success.output.children.at[0]); - - // Consume ',' or ')' - peek = parserConsume(parser); - if (peek.error) { - return Error(Nothing, charptr, "Expecting ')' or ','"); - } - if (peek.as.success.type == STT_CLOSE_PAREN) { - break; - } - if (peek.as.success.type != STT_COMMA) { - return Error(Nothing, charptr, "Expecting ',' or ')'"); - } - } - - // Add the fully-built call node once - addChildToSolsNode(parser->currentParent, node.as.success); - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseReturn(SolsParser* parser) { - - // Collect tokens for node - ResultType(SolsTokens, charptr) tokens = createSolsTokens(); - if (tokens.error) { - return Error(Nothing, charptr, tokens.as.error); - } - - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); - if (peek.error) return Error(Nothing, charptr, "ruh roh"); - - for (;;) { - ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); - if (peek.error) break; - // Return and puts share the same high precedence - if (getPrecedence(&peek.as.success) <= STP_PUTS) { - break; - } - parserConsume(parser); - addTokenToSolsTokens(&tokens.as.success, peek.as.success); - } - - // Create node - ResultType(SolsNode, charptr) node = createSolsNode(SNT_RETURN); - if (node.error) return Error(Nothing, charptr, node.as.error); - node.as.success.line = peek.as.success.line; - - // Parse selected tokens - ResultType(SolsParser, charptr) returnParser = createSolsParser(&tokens.as.success); - if (returnParser.error) return Error(Nothing, charptr, returnParser.as.error); - returnParser.as.success.currentParent = &returnParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&returnParser.as.success); - - // Add any error messages from parsing - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - return Success(Nothing, charptr, {}); - } - - // Copy nodes into the sols node - for (size_t i = 0; i < returnParser.as.success.output.children.count; i++) { - addChildToSolsNode(&node.as.success, returnParser.as.success.output.children.at[i]); - } - - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseUse(SolsParser* parser) { - ResultType(SolsToken, Nothing) name = parserConsume(parser); - if (name.error || name.as.success.type != STT_IDENTIFIER) { - return Error(Nothing, charptr, "Expecting identifier after 'use'"); - } - ResultType(SolsNode, charptr) node = createSolsNode(SNT_USE); - if (node.error) { - return Error(Nothing, charptr, node.as.error); - } - node.as.success.as.idName = name.as.success.as.idName; - addChildToSolsNode(parser->currentParent, node.as.success); - - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseInlineGround(SolsParser* parser) { - ResultType(SolsToken, Nothing) token = parserPeek(parser, 0); - if (token.error) { - return Error(Nothing, charptr, "ruh roh"); - } - ResultType(SolsNode, charptr) newNode = createSolsNode(SNT_GROUND, token.as.success.as.inlineGround); - if (newNode.error) { - return Error(Nothing, charptr, newNode.as.error); - } - - addChildToSolsNode(parser->currentParent, newNode.as.success); - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseOpenParen(SolsParser* parser) { - size_t parenCount = 1; - size_t curlyCount = 0; - SolsTokens tokens = ({ - ResultType(SolsTokens, charptr) _result = createSolsTokens(); - if (_result.error) { - return Error(Nothing, charptr, _result.as.error); - } - _result.as.success; - }); - // Collect tokens - for (;;) { - ResultType(SolsToken, Nothing) token = parserConsume(parser); - if (token.error) { - return Error(Nothing, charptr, "Expecting ')'"); - } - bool done = false; - switch (token.as.success.type) { - case STT_OPEN_PAREN: - parenCount++; - break; - case STT_CLOSE_PAREN: - parenCount--; - if (parenCount == 0) { - if (curlyCount == 0) { - done = true; - } else { - return Error(Nothing, charptr, "Expecting '}' before ')'"); - } - } - break; - case STT_OPEN_CURLY: - curlyCount++; - break; - case STT_CLOSE_CURLY: - if (curlyCount == 0) { - return Error(Nothing, charptr, "Extra closing curly brace inside ()"); - } - curlyCount--; - break; - default: - break; - } - if (done) break; - addTokenToSolsTokens(&tokens, token.as.success); - } - // Parse tokens - ResultType(SolsParser, charptr) newParser = createSolsParser(&tokens); - if (newParser.error) { - return Error(Nothing, charptr, newParser.as.error); - } - newParser.as.success.currentParent = &newParser.as.success.output; - ResultType(Nothing, charptr) parsed = parse(&newParser.as.success); - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - } - // Add node to parent - newParser.as.success.output.type = SNT_EXPR_IN_PAREN; - addChildToSolsNode(parser->currentParent, newParser.as.success.output); - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseCloseParen(SolsParser* parser) { - (void)parser; - return Error(Nothing, charptr, "Extra closing parentheses"); -} - -static inline ResultType(Nothing, charptr) parseConstructor(SolsParser* parser) { - ResultType(SolsNode, charptr) node = createSolsNode(SNT_CONSTRUCTOR); - ResultType(SolsType, charptr) type = createSolsType(STT_FUN); - if (type.error) { - return Error(Nothing, charptr, type.as.error); - } - type.as.success.returnType = malloc(sizeof(SolsType)); - if (type.as.success.returnType == NULL) { - return Error(Nothing, charptr, "Failed to allocate memory for return type"); - } - type.as.success.returnType->type = STT_NONE; - if (node.error) { - return Error(Nothing, charptr, node.as.error); - } - ResultType(SolsToken, Nothing) openParen = parserConsume(parser); - if (openParen.error || openParen.as.success.type != STT_OPEN_PAREN) { - return Error(Nothing, charptr, "Expecting '('"); - } - // Parse type signature - for (;;) { - ResultType(SolsToken, Nothing) next = parserPeek(parser, 1); - if (next.error) { - return Error(Nothing, charptr, "Expecting ')' at end of constructor argument list"); - } - if (next.as.success.type == STT_CLOSE_PAREN) { - parserConsume(parser); - break; - } - - // 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 = createIdentifiedSolsType(next.as.success.as.idName).as.success; - } else { - return Error(Nothing, charptr, "Expecting a type or identifier of type in constructor 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 constructor 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); - } - - // Add type to node - node.as.success.as.type = type.as.success; - - // 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 constructor 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* constructorNode = &parser->currentParent->children.at[parser->currentParent->children.count - 1]; - addChildToSolsNode(constructorNode, codeBlock); - return Success(Nothing, charptr, {}); -} - -static inline ResultType(Nothing, charptr) parseDestructor(SolsParser* parser) { - ResultType(SolsNode, charptr) node = createSolsNode(SNT_DESTRUCTOR); - ResultType(SolsType, charptr) type = createSolsType(STT_FUN); - if (type.error) { - return Error(Nothing, charptr, type.as.error); - } - type.as.success.returnType = malloc(sizeof(SolsType)); - if (type.as.success.returnType == NULL) { - return Error(Nothing, charptr, "Failed to allocate memory for return type"); - } - type.as.success.returnType->type = STT_NONE; - - // 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 destructor 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* constructorNode = &parser->currentParent->children.at[parser->currentParent->children.count - 1]; - addChildToSolsNode(constructorNode, codeBlock); - return Success(Nothing, charptr, {}); - -} - -static inline ResultType(Nothing, charptr) parseStruct(SolsParser* parser) { - // Get struct name - SolsNode structNode = ({ - ResultType(SolsNode, charptr) _result = createSolsNode(SNT_STRUCT); - if (_result.error) { - return Error(Nothing, charptr, _result.as.error); - } - _result.as.success; - }); - ResultType(SolsToken, Nothing) nameTok = parserConsume(parser); - if (nameTok.error || nameTok.as.success.type != STT_IDENTIFIER) { - return Error(Nothing, charptr, "Expecting identifier after 'struct'"); - } - structNode.as.idName = nameTok.as.success.as.idName; - ResultType(SolsToken, Nothing) openCurly = parserConsume(parser); - if (openCurly.error || openCurly.as.success.type != STT_OPEN_CURLY) { - return Error(Nothing, charptr, "Expecting '{' after struct identifier"); - } - // Ignore new lines between struct and opening curly brace - for (;;) { - ResultType(SolsToken, Nothing) token = parserPeek(parser, 1); - if (token.error) { - return Error(Nothing, charptr, "Expecting '{' after 'struct'"); - } - if (token.as.success.type == STT_LINE_END) { - parserConsume(parser); - } else { - break; - } - } - for (;;) { - bool done = false; - // Skip newlines between struct values - for (;;) { - ResultType(SolsToken, Nothing) token = parserPeek(parser, 1); - if (token.error) { - return Error(Nothing, charptr, "Expecting '}' to end struct"); - } - if (token.as.success.type == STT_LINE_END) { - parserConsume(parser); - } - else if (token.as.success.type == STT_CLOSE_CURLY) { - done = true; - parserConsume(parser); - break; - } else { - break; - } - } - if (done) break; - - bool private = false; - bool protected = false; - - // key = value\n - SolsToken keyTok = ({ - ResultType(SolsToken, Nothing) _result = parserConsume(parser); - if (_result.error) { - return Error(Nothing, charptr, "Expecting struct key"); - } - _result.as.success; - }); - if (keyTok.type == STT_KW_PRIVATE) { - // private key = value - private = true; - - ResultType(SolsToken, Nothing) result = parserConsume(parser); - if (result.error) { - return Error(Nothing, charptr, "Expecting identifier after 'private'"); - } - keyTok = result.as.success; - } - if (keyTok.type == STT_KW_PROTECTED) { - // protected key = value - protected = true; - - ResultType(SolsToken, Nothing) result = parserConsume(parser); - if (result.error) { - return Error(Nothing, charptr, "Expecting identifier after 'protected'"); - } - keyTok = result.as.success; - } - if (keyTok.type == STT_KW_DEF) { - // def fnName(Arg arg1, Arg arg2, ...) ReturnType {} - ResultType(Nothing, charptr) defNode = parseDef(parser); - if (defNode.error) { - return Error(Nothing, charptr, defNode.as.error); - } - if (protected) parser->currentParent->children.at[parser->currentParent->children.count - 1].type = SNT_DEF_PROTECTED; - if (private) parser->currentParent->children.at[parser->currentParent->children.count - 1].type = SNT_DEF_PRIVATE; - addChildToSolsNode(&structNode, parser->currentParent->children.at[parser->currentParent->children.count - 1]); - parser->currentParent->children.count--; - continue; - } - if (keyTok.type == STT_KW_CONSTRUCTOR) { - // constructor(Arg arg1, Arg arg2, ...) {} - if (private) { - return Error(Nothing, charptr, "Constructor cannot be private"); - } - if (protected) { - return Error(Nothing, charptr, "Constructor cannot be protected"); - } - ResultType(Nothing, charptr) constructorNode = parseConstructor(parser); - if (constructorNode.error) { - return Error(Nothing, charptr, constructorNode.as.error); - } - addChildToSolsNode(&structNode, parser->currentParent->children.at[parser->currentParent->children.count - 1]); - parser->currentParent->children.count--; - continue; - } - if (keyTok.type == STT_KW_DESTRUCTOR) { - // destructor {} - if (private) { - return Error(Nothing, charptr, "Destructor cannot be private"); - } - if (protected) { - return Error(Nothing, charptr, "Destructor cannot be protected"); - } - ResultType(Nothing, charptr) destructorNode = parseDestructor(parser); - if (destructorNode.error) { - return Error(Nothing, charptr, destructorNode.as.error); - } - addChildToSolsNode(&structNode, parser->currentParent->children.at[parser->currentParent->children.count - 1]); - parser->currentParent->children.count--; - continue; - } - if (keyTok.type != STT_IDENTIFIER) { - return Error(Nothing, charptr, "Expecting struct key"); - } - SolsToken setTok = ({ - ResultType(SolsToken, Nothing) _result = parserConsume(parser); - if (_result.error) { - return Error(Nothing, charptr, "Expecting '=' after struct key"); - } - _result.as.success; - }); - if (setTok.type != STT_OP_SET) { - return Error(Nothing, charptr, "Expecting '=' after struct key"); - } - SolsTokens tokens = ({ - ResultType(SolsTokens, charptr) _result = createSolsTokens(); - if (_result.error) { - return Error(Nothing, charptr, _result.as.error); - } - _result.as.success; - }); - // Collect tokens for value - if (parserPeek(parser, 1).error) { - return Error(Nothing, charptr, "Expecting value after '='"); - } - for (;;) { - ResultType(SolsToken, Nothing) token = parserPeek(parser, 1); - if (token.error) { - break; - } - if (getPrecedence(&token.as.success) <= STP_NEWLINE) { - break; - } - addTokenToSolsTokens(&tokens, token.as.success); - parserConsume(parser); - } - // Parse value - SolsParser newParser = ({ - ResultType(SolsParser, charptr) _result = createSolsParser(&tokens); - if (_result.error) { - return Error(Nothing, charptr, _result.as.error); - } - _result.as.success; - }); - newParser.currentParent = &newParser.output; - ResultType(Nothing, charptr) parsed = parse(&newParser); - if (parsed.error) { - addToParserErrors(parser, parsed.as.error); - } - // Construct set node - SolsNode setNode = ({ - ResultType(SolsNode, charptr) _result = createSolsNode(SNT_OP_SET); - if (_result.error) { - return Error(Nothing, charptr, _result.as.error); - } - _result.as.success; - }); - if (protected) setNode.type = SNT_SET_PROTECTED; - if (private) setNode.type = SNT_SET_PRIVATE; - addChildToSolsNode(&setNode, ({ - ResultType(SolsNode, charptr) _result = createSolsNode(SNT_IDENTIFIER, keyTok.as.idName); - if (_result.error) { - return Error(Nothing, charptr, _result.as.error); - } - _result.as.success; - })); - addChildToSolsNode(&setNode, newParser.output.children.at[0]); - - // Add set node to struct node - addChildToSolsNode(&structNode, setNode); - } - // Add struct node to parent - addChildToSolsNode(parser->currentParent, structNode); - return Success(Nothing, charptr, {}); -} - -ResultType(Nothing, charptr) parseNew(SolsParser* parser) { - ResultType(SolsNode, charptr) newNode = createSolsNode(SNT_NEW); - if (newNode.error) { - return Error(Nothing, charptr, newNode.as.error); - } - ResultType(SolsToken, Nothing) nameTok = parserConsume(parser); - if (nameTok.error || !(nameTok.as.success.type == STT_IDENTIFIER || nameTok.as.success.type == STT_TYPE)) { - return Error(Nothing, charptr, "Expecting identifier after 'new'"); - } - if (nameTok.as.success.type == STT_IDENTIFIER) { - newNode.as.success.as.type = createIdentifiedSolsType(nameTok.as.success.as.idName).as.success; - } else if (nameTok.as.success.type == STT_TYPE) { - newNode.as.success.as.type = nameTok.as.success.as.type; - } - addChildToSolsNode(parser->currentParent, newNode.as.success); - return Success(Nothing, charptr, {}); -} - -ResultType(Nothing, charptr) parseDot(SolsParser* parser) { - ResultType(SolsNode, charptr) newNode = createSolsNode(SNT_DOT); - if (newNode.error) { - return Error(Nothing, charptr, newNode.as.error); - } - if (parser->currentParent->children.count < 1) { - return Error(Nothing, charptr, "Expecting value before '.'"); - } - SolsNode left = parser->currentParent->children.at[parser->currentParent->children.count - 1]; - ResultType(SolsToken, Nothing) right = parserConsume(parser); - if (right.error || right.as.success.type != STT_IDENTIFIER) { - return Error(Nothing, charptr, "Expecting identifier after '.'"); - } - addChildToSolsNode(&newNode.as.success, left); - parser->currentParent->children.count--; - addChildToSolsNode(&newNode.as.success, ({ - ResultType(SolsNode, charptr) _result = createSolsNode(SNT_IDENTIFIER, right.as.success.as.idName); - if (_result.error) { - return Error(Nothing, charptr, _result.as.error); - } - _result.as.success; - })); - - addChildToSolsNode(parser->currentParent, newNode.as.success); - return Success(Nothing, charptr, {}); -} - -ResultType(Nothing, charptr) parse(SolsParser* parser) { - parser->currentParent = &parser->output; - for (;;) { - ResultType(SolsToken, Nothing) token = parserConsume(parser); - if (token.error) { - break; - } - switch (token.as.success.type) { - case STT_IDENTIFIER: PARSER_HANDLE(Identifier); - case STT_LITERAL: PARSER_HANDLE(Literal); - case STT_KW_PUTS: PARSER_HANDLE(Puts); - case STT_KW_IF: PARSER_HANDLE(If); - case STT_KW_WHILE: PARSER_HANDLE(While); - case STT_KW_GROUND: PARSER_HANDLE(InlineGround); - case STT_KW_LAMBDA: PARSER_HANDLE(Lambda); - case STT_KW_RETURN: PARSER_HANDLE(Return); - case STT_KW_STRUCT: PARSER_HANDLE(Struct); - case STT_KW_NEW: PARSER_HANDLE(New); - case STT_KW_USE: PARSER_HANDLE(Use); - 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); - case STT_OP_MUL: PARSER_HANDLE(Mul); - case STT_OP_DIV: PARSER_HANDLE(Div); - case STT_OP_EQUAL: PARSER_HANDLE(Equal); - case STT_OP_INEQUAL: PARSER_HANDLE(Inequal); - case STT_OP_GREATER: PARSER_HANDLE(Greater); - case STT_OP_LESSER: PARSER_HANDLE(Lesser); - case STT_OP_EQGREATER: PARSER_HANDLE(EqGreater); - case STT_OP_EQLESSER: PARSER_HANDLE(EqLesser); - case STT_OPEN_CURLY: PARSER_HANDLE(CodeBlock); - case STT_CLOSE_CURLY: PARSER_HANDLE(CloseCurly); - case STT_OPEN_PAREN: { - if (parser->output.children.count > 0 && - (parser->output.children.at[parser->output.children.count - 1].type == SNT_IDENTIFIER - || parser->output.children.at[parser->output.children.count - 1].type == SNT_DOT)) { - PARSER_HANDLE(FunctionCall); - } else { - PARSER_HANDLE(OpenParen); - } - } - case STT_CLOSE_PAREN: PARSER_HANDLE(CloseParen); - case STT_DOT: PARSER_HANDLE(Dot); - case STT_KW_CONSTRUCTOR: { - createParserError(parser, "'constructor' must be used inside a struct definition"); - break; - } - case STT_KW_DESTRUCTOR: { - createParserError(parser, "'destructor' must be used inside a struct definition"); - break; - } - case STT_KW_PRIVATE: { - createParserError(parser, "'private' must be used inside a struct definition"); - break; - } - case STT_KW_PROTECTED: { - createParserError(parser, "'protected' must be used inside a struct definition"); - break; - } - } - } - if (parser->errors.count > 0) { - Estr estr = CREATE_ESTR(""); - for (size_t i = 0; i < parser->errors.count; i++) { - APPEND_ESTR(estr, parser->errors.at[i]); - APPEND_ESTR(estr, "\n"); - } - return Error(Nothing, charptr, estr.str); - } - return Success(Nothing, charptr, {}); -} - -ResultType(SolsToken, Nothing) parserLookAt(SolsParser* parser, size_t where) { - if (parser->input == NULL) { - return Error(SolsToken, Nothing, {}); - } - if (where >= parser->input->count) { - return Error(SolsToken, Nothing, {}); - } - return Success(SolsToken, Nothing, parser->input->at[where]); -} - -ResultType(SolsToken, Nothing) parserPeek(SolsParser* parser, int64_t ahead) { - if (parser->input == NULL) { - return Error(SolsToken, Nothing, {}); - } - if (parser->current + ahead - 1 >= parser->input->count) { - return Error(SolsToken, Nothing, {}); - } - return Success(SolsToken, Nothing, parser->input->at[parser->current + ahead - 1]); -} - -ResultType(SolsToken, Nothing) parserConsume(SolsParser* parser) { - if (parser->input == NULL) { - return Error(SolsToken, Nothing, {}); - } - if (parser->current >= parser->input->count) { - return Error(SolsToken, Nothing, {}); - } - return Success(SolsToken, Nothing, parser->input->at[parser->current ++]); -} diff --git a/src/parser/parser.h b/src/parser/parser.h deleted file mode 100644 index 4968e4e..0000000 --- a/src/parser/parser.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef PARSER_H -#define PARSER_H - -#include "SolsNode.h" -#include "../lexer/SolsToken.h" -#include "../include/error.h" -#include "../include/nothing.h" - -// Defines precedence for different tokens. -// Lower number means higher priority. -// Any operation involving same or higher precedence than -// the current token's precedence should be processed first -// (i.e. placed further down the tree) -typedef enum SolsTokenPrecedence { - STP_NEWLINE = 0, - STP_PUTS = 1, - STP_IF = 1, - STP_WHILE = 1, - STP_COMPARE = 2, - STP_SET = 3, - STP_FUNCTION = 4, - STP_ADD = 5, - STP_MUL = 6, - STP_OTHER = 7, -} SolsTokenPrecedence; - -// Gets the precedence of the provided token. -SolsTokenPrecedence getPrecedence(SolsToken* token); - -// Holds information about the parser. -// .input is lexed tokens, produced by a lexer. -// .current is the token currently being parsed. -// .output is the final product of the parser. -// .currentParent points to the current node being processed -// .errors holds any errors generated during parsing -typedef struct SolsParser { - SolsTokens* input; - size_t current; - SolsNode output; - SolsNode* currentParent; - struct { - size_t count; - size_t capacity; - char** at; - } errors; -} SolsParser; - -Result(SolsParser, charptr); - -// Creates a SolsParser. -// Returns: -// Success: Constructed SolsParser -// Failure: char* detailing what went wrong (usually memory failure) -ResultType(SolsParser, charptr) createSolsParser(SolsTokens* input); - -// Parses the tokens in the SolsParser into an AST. -// Returns: -// Success: Nothing (output is stored in the passed SolsLexer) -// Failure: char* detailing what went wrong (usually user error) -ResultType(Nothing, charptr) parse(SolsParser* parser); - -Result(SolsNode, Nothing); - -// Parses one singular node and returns it. -// Returns: -// Success: The parsed SolsNode -// Failure: Nothing (out of bounds, or an error) - -Result(SolsToken, Nothing); - -// Peeks at a token at a specific index in the lexer, 0 being the first token. -// Returns: -// Success: The requested token -// Failure: Nothing (token is out of bounds) -ResultType(SolsToken, Nothing) parserLookAt(SolsParser* parser, size_t where); - -// Peeks at future tokens in the parser, 0 meaning current token, 1 the next. -// Returns: -// Success: The requested token -// Failure: Nothing (token is out of bounds) -ResultType(SolsToken, Nothing) parserPeek(SolsParser* parser, int64_t ahead); - -// Consumes the next token in the parser. -// Returns: -// Success: The requested token -// Failure: Nothing (we have reached the end of the tokens passed) -ResultType(SolsToken, Nothing) parserConsume(SolsParser* parser); - -// Macro for cleaner handling of each token type in the parser. -// Calls functions and returns errors for you! Such amazing -#define PARSER_HANDLE(tokentype) {\ - ResultType(Nothing, charptr) __result = parse##tokentype(parser);\ - if (__result.error) {\ - createParserError(parser, __result.as.error);\ - }\ - break;\ -} - -#endif diff --git a/src/typeparser/typeparser.c b/src/typeparser/typeparser.c deleted file mode 100644 index dfd69b1..0000000 --- a/src/typeparser/typeparser.c +++ /dev/null @@ -1,230 +0,0 @@ -#include "typeparser.h" -#include "../lexer/SolsToken.h" -#include "../include/error.h" -#include - -ResultType(SolsType, charptr) parseType(SolsTokens* in, size_t* index) { - if (*index >= in->count) { - return Error(SolsType, charptr, "Unexpected end of tokens while parsing type"); - } - - SolsToken* token = &in->at[*index]; - - if (token->type == STT_TYPE) { - // It's already a basic type token - ResultType(SolsType, charptr) res = copySolsType(&token->as.type); - (*index)++; - return res; - } - - if (token->type == STT_IDENTIFIER) { - if (strcmp(token->as.idName, "fun") == 0) { - (*index)++; // skip fun - if (*index >= in->count || in->at[*index].type != STT_OPEN_PAREN) { - return Error(SolsType, charptr, "Expected '(' after 'fun' in type signature"); - } - (*index)++; // skip ( - - ResultType(SolsType, charptr) funTypeRes = createSolsType(STT_FUN); - if (funTypeRes.error) return funTypeRes; - SolsType funType = funTypeRes.as.success; - - if (*index < in->count && in->at[*index].type != STT_CLOSE_PAREN) { - for (;;) { - ResultType(SolsType, charptr) argType = parseType(in, index); - if (argType.error) { - freeSolsType(&funType); - return argType; - } - addChildToSolsType(&funType, argType.as.success, NULL); - freeSolsType(&argType.as.success); // addChildToSolsType copies it - - if (*index < in->count && in->at[*index].type == STT_COMMA) { - (*index)++; // skip comma - } else { - break; - } - } - } - - if (*index >= in->count || in->at[*index].type != STT_CLOSE_PAREN) { - freeSolsType(&funType); - return Error(SolsType, charptr, "Expected ')' after function arguments in type signature"); - } - (*index)++; // skip ) - - // Return type - ResultType(SolsType, charptr) retType = parseType(in, index); - if (retType.error) { - freeSolsType(&funType); - return retType; - } - - // Allocate memory for returnType pointer - funType.returnType = malloc(sizeof(SolsType)); - if (funType.returnType == NULL) { - freeSolsType(&funType); - freeSolsType(&retType.as.success); - return Error(SolsType, charptr, "Couldn't allocate memory (in parseType() function)"); - } - *funType.returnType = retType.as.success; - - return Success(SolsType, charptr, funType); - } - - if (strcmp(token->as.idName, "object") == 0 || strcmp(token->as.idName, "template") == 0) { - bool isTemplate = strcmp(token->as.idName, "template") == 0; - (*index)++; // skip object/template - if (*index >= in->count || in->at[*index].type != STT_OPEN_PAREN) { - return Error(SolsType, charptr, "Expected '(' after object/template in type signature"); - } - (*index)++; // skip ( - - ResultType(SolsType, charptr) complexTypeRes = createSolsType(isTemplate ? STT_TEMPLATE : STT_OBJECT); - if (complexTypeRes.error) return complexTypeRes; - SolsType complexType = complexTypeRes.as.success; - - if (*index < in->count && in->at[*index].type != STT_CLOSE_PAREN) { - for (;;) { - ResultType(SolsType, charptr) fieldType = parseType(in, index); - if (fieldType.error) { - freeSolsType(&complexType); - return fieldType; - } - - char* fieldName = NULL; - if (*index < in->count && in->at[*index].type == STT_IDENTIFIER) { - fieldName = in->at[*index].as.idName; - (*index)++; - } - - addChildToSolsType(&complexType, fieldType.as.success, fieldName); - freeSolsType(&fieldType.as.success); - - if (*index < in->count && in->at[*index].type == STT_COMMA) { - (*index)++; // skip comma - } else { - break; - } - } - } - - if (*index >= in->count || in->at[*index].type != STT_CLOSE_PAREN) { - freeSolsType(&complexType); - return Error(SolsType, charptr, "Expected ')' in type signature"); - } - (*index)++; // skip ) - return Success(SolsType, charptr, complexType); - } - - // Handle user-defined types (as identifiers) - // For now, let's just create an OBJECT type with identifierType set - ResultType(SolsType, charptr) userTypeRes = createSolsType(STT_OBJECT); - if (userTypeRes.error) return userTypeRes; - SolsType userType = userTypeRes.as.success; - userType.identifierType = malloc(strlen(token->as.idName) + 1); - if (userType.identifierType == NULL) { - freeSolsType(&userType); - return Error(SolsType, charptr, "Couldn't allocate memory (in parseType() function)"); - } - strcpy(userType.identifierType, token->as.idName); - (*index)++; - return Success(SolsType, charptr, userType); - } - - return Error(SolsType, charptr, "Unexpected token while parsing type"); -} - -ResultType(SolsTokens, charptr) addTypeInfo(SolsTokens* in) { - ResultType(SolsTokens, charptr) tokensres = createSolsTokens(); - if (tokensres.error) { - return tokensres; - } - SolsTokens tokens = tokensres.as.success; - - for (size_t i = 0; i < in->count; ) { - if (in->at[i].type == STT_IDENTIFIER && - (strcmp(in->at[i].as.idName, "fun") == 0 || - strcmp(in->at[i].as.idName, "object") == 0 || - strcmp(in->at[i].as.idName, "template") == 0)) { - size_t startIndex = i; - ResultType(SolsType, charptr) typeRes = parseType(in, &i); - if (typeRes.error) { - // For now, return error - return Error(SolsTokens, charptr, typeRes.as.error); - } - - ResultType(SolsToken, charptr) tokenRes = createSolsToken(STT_TYPE, typeRes.as.success); - if (tokenRes.error) { - freeSolsType(&typeRes.as.success); - return Error(SolsTokens, charptr, tokenRes.as.error); - } - - // Add line info from original token - tokenRes.as.success.line.num = in->at[startIndex].line.num; - if (in->at[startIndex].line.content) { - tokenRes.as.success.line.content = malloc(strlen(in->at[startIndex].line.content) + 1); - if (tokenRes.as.success.line.content) { - strcpy(tokenRes.as.success.line.content, in->at[startIndex].line.content); - } - } else { - tokenRes.as.success.line.content = NULL; - } - - addTokenToSolsTokens(&tokens, tokenRes.as.success); - // SolsToken now owns typeRes.as.success and its buffers. - } else { - // Need to deep copy the token because addTokenToSolsTokens just copies the struct - // and freeSolsToken will free buffers. - // Wait, if we use the same tokens, it's fine. - // But 'in' tokens might be freed later. - // Actually, we are creating a *new* SolsTokens. - // So we should probably copy the token properly. - - SolsToken original = in->at[i]; - SolsToken copy = {0}; - copy.type = original.type; - copy.line.num = original.line.num; - if (original.line.content) { - copy.line.content = malloc(strlen(original.line.content) + 1); - if (copy.line.content) strcpy(copy.line.content, original.line.content); - } - - switch (original.type) { - case STT_IDENTIFIER: - copy.as.idName = malloc(strlen(original.as.idName) + 1); - if (copy.as.idName) strcpy(copy.as.idName, original.as.idName); - break; - case STT_KW_GROUND: - copy.as.inlineGround = malloc(strlen(original.as.inlineGround) + 1); - if (copy.as.inlineGround) strcpy(copy.as.inlineGround, original.as.inlineGround); - break; - case STT_LITERAL: - // Literals also need deep copy? - // SolsLiteral has stringv. - if (original.as.literal.type == SLT_STRING) { - copy.as.literal.type = SLT_STRING; - copy.as.literal.as.stringv = malloc(strlen(original.as.literal.as.stringv) + 1); - if (copy.as.literal.as.stringv) strcpy(copy.as.literal.as.stringv, original.as.literal.as.stringv); - } else { - copy.as.literal = original.as.literal; - } - break; - case STT_TYPE: { - ResultType(SolsType, charptr) copiedType = copySolsType(&original.as.type); - if (!copiedType.error) { - copy.as.type = copiedType.as.success; - } - break; - } - default: - copy.as = original.as; - break; - } - addTokenToSolsTokens(&tokens, copy); - i++; - } - } - - return Success(SolsTokens, charptr, tokens); -} diff --git a/src/typeparser/typeparser.h b/src/typeparser/typeparser.h deleted file mode 100644 index 7ab4891..0000000 --- a/src/typeparser/typeparser.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef TYPEPARSER_H -#define TYPEPARSER_H - -#include "../lexer/SolsToken.h" -#include "../include/error.h" - -// Scans a SolsTokens and identifies type signatures, like fun(int) string. -// These are then converted into a single STT_TYPE token. -ResultType(SolsTokens, charptr) addTypeInfo(SolsTokens* in); - -#endif diff --git a/src/wasm_main.c b/src/wasm_main.c deleted file mode 100644 index 67ab689..0000000 --- a/src/wasm_main.c +++ /dev/null @@ -1,81 +0,0 @@ -#include -#include -#include -#include - -#include "lexer/lexer.h" -#include "typeparser/typeparser.h" -#include "parser/parser.h" -#include "codegen/codegen.h" - -#include - - -static char out_buf[65536]; -static int out_pos = 0; - -// Defined by Ground -void wasm_print(const char* str); - -EMSCRIPTEN_KEEPALIVE -const char* solstice_run(char* source) { - out_pos = 0; - out_buf[0] = '\0'; - - // 1. Lex - ResultType(SolsLexer, charptr) lexer = createLexer(source); - if (lexer.error) { - snprintf(out_buf, sizeof(out_buf), - "[lex error] %s", lexer.as.error); - return out_buf; - } - ResultType(Nothing, charptr) lexed = lex(&lexer.as.success); - if (lexed.error) { - snprintf(out_buf, sizeof(out_buf), "%s", lexed.as.error); - return out_buf; - } - - // 2. Type parse - ResultType(SolsTokens, charptr) typed = addTypeInfo(&lexer.as.success.output); - if (typed.error) { - snprintf(out_buf, sizeof(out_buf), "%s", typed.as.error); - return out_buf; - } - - // 3. Parse - ResultType(SolsParser, charptr) parser = createSolsParser(&typed.as.success); - if (parser.error) { - snprintf(out_buf, sizeof(out_buf), - "[parse error] %s", parser.as.error); - return out_buf; - } - ResultType(Nothing, charptr) parsed = parse(&parser.as.success); - if (parsed.error) { - snprintf(out_buf, sizeof(out_buf), "%s", parsed.as.error); - return out_buf; - } - - // 4. Codegen - SolsScope scope = { - .variables = NULL, - .tmpCounter = 0, - .returnType = createSolsType(STT_INT).as.success - }; - ResultType(GroundProgram, charptr) codegen = - generateCode(&parser.as.success.output, &scope); - if (codegen.error) { - snprintf(out_buf, sizeof(out_buf), "%s", codegen.as.error); - return out_buf; - } - - // 5. Run - GroundValue retval = groundRunProgram(&codegen.as.success); - if (out_pos == 0) { - // Program produced no output — report exit code - snprintf(out_buf, sizeof(out_buf), - "[exited with code %d]", - retval.type == INT ? retval.data.intVal : 0); - } - - return out_buf; -}