From 00ef8a7d5690ea6d1d8ae53e64c60b0290c17471 Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Thu, 5 Mar 2026 19:32:31 +1100 Subject: [PATCH] Type parser --- Makefile | 4 +- src/main.c | 10 +- src/parser/SolsNode.c | 11 +- src/parser/SolsNode.h | 3 + src/parser/parser.c | 19 ++- src/typeparser/typeparser.c | 230 ++++++++++++++++++++++++++++++++++++ src/typeparser/typeparser.h | 11 ++ tests/closure.sols | 9 ++ 8 files changed, 290 insertions(+), 7 deletions(-) create mode 100644 src/typeparser/typeparser.c create mode 100644 src/typeparser/typeparser.h create mode 100644 tests/closure.sols diff --git a/Makefile b/Makefile index 7491300..fbfbb18 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ PREFIX ?= /usr/local BINDIR = $(PREFIX)/bin LIBDIR = /usr/lib -SRCS = $(SRC_DIR)/main.c $(SRC_DIR)/codegen/SolsScope.c $(SRC_DIR)/codegen/codegen.c $(SRC_DIR)/lexer/SolsLiteral.c $(SRC_DIR)/lexer/SolsToken.c $(SRC_DIR)/lexer/SolsType.c $(SRC_DIR)/lexer/lexer.c $(SRC_DIR)/parser/SolsNode.c $(SRC_DIR)/parser/parser.c +SRCS = $(SRC_DIR)/main.c $(SRC_DIR)/codegen/SolsScope.c $(SRC_DIR)/codegen/codegen.c $(SRC_DIR)/lexer/SolsLiteral.c $(SRC_DIR)/lexer/SolsToken.c $(SRC_DIR)/lexer/SolsType.c $(SRC_DIR)/lexer/lexer.c $(SRC_DIR)/parser/SolsNode.c $(SRC_DIR)/parser/parser.c $(SRC_DIR)/typeparser/typeparser.c OBJS = $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(SRCS)) TARGET = solstice @@ -38,7 +38,7 @@ $(BUILD_DIR)/solstice.tar.gz: $(TARGET) $(LIBS_DIR) package: $(BUILD_DIR)/solstice.tar.gz $(BUILD_DIR): - mkdir -p $(BUILD_DIR) $(BUILD_DIR)/codegen $(BUILD_DIR)/lexer $(BUILD_DIR)/parser + mkdir -p $(BUILD_DIR) $(BUILD_DIR)/codegen $(BUILD_DIR)/lexer $(BUILD_DIR)/parser $(BUILD_DIR)/typeparser clean: rm -rf $(BUILD_DIR) $(TARGET) diff --git a/src/main.c b/src/main.c index c96f525..2f0e8ff 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,6 @@ #include "lexer/SolsType.h" #include "lexer/lexer.h" +#include "typeparser/typeparser.h" #include "parser/parser.h" #include "codegen/codegen.h" #include "include/error.h" @@ -139,8 +140,15 @@ int main(int argc, char** argv) { exit(1); } + // Detect and parse types + ResultType(SolsTokens, charptr) typed = addTypeInfo(&lexer.as.success.output); + if (typed.error) { + printf("%s\n", typed.as.error); + exit(1); + } + // Parse file - ResultType(SolsParser, charptr) parser = createSolsParser(&lexer.as.success.output); + ResultType(SolsParser, charptr) parser = createSolsParser(&typed.as.success); if (parser.error) { printf("Error while creating parser: %s\n", parser.as.error); exit(1); diff --git a/src/parser/SolsNode.c b/src/parser/SolsNode.c index 76b7a11..8777c22 100644 --- a/src/parser/SolsNode.c +++ b/src/parser/SolsNode.c @@ -55,7 +55,16 @@ ResultType(Nothing, charptr) addChildToSolsNode(SolsNode* parent, SolsNode child } parent->children.at = tmp; } - parent->children.at[parent->children.count] = child; + 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 index b0f8961..cbc4075 100644 --- a/src/parser/SolsNode.h +++ b/src/parser/SolsNode.h @@ -59,4 +59,7 @@ ResultType(SolsNode, charptr) createSolsNode(SolsNodeType type, ...); // 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 index f58210a..76023f7 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -6,6 +6,21 @@ #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; @@ -1399,9 +1414,7 @@ static inline ResultType(Nothing, charptr) parseDef(SolsParser* parser) { 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); + addChildToSolsNode(&parser->currentParent->children.at[0], parser->currentParent->children.at[1]); return Success(Nothing, charptr, {}); } diff --git a/src/typeparser/typeparser.c b/src/typeparser/typeparser.c new file mode 100644 index 0000000..dfd69b1 --- /dev/null +++ b/src/typeparser/typeparser.c @@ -0,0 +1,230 @@ +#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 new file mode 100644 index 0000000..7ab4891 --- /dev/null +++ b/src/typeparser/typeparser.h @@ -0,0 +1,11 @@ +#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/tests/closure.sols b/tests/closure.sols new file mode 100644 index 0000000..42a23f1 --- /dev/null +++ b/tests/closure.sols @@ -0,0 +1,9 @@ +def createClosure(int x) fun(int) int { + return lambda(int y) int { + x + y + } +} + +myVar = createClosure(5) + +puts myVar(3) \ No newline at end of file