Friendship ended with C++, C is my new best friend

This commit is contained in:
2026-03-01 16:00:03 +11:00
parent 139be30e2d
commit 38473f0e01
32 changed files with 5996 additions and 2518 deletions

61
src/parser/SolsNode.c Normal file
View File

@@ -0,0 +1,61 @@
#include "SolsNode.h"
#include <stdarg.h>
#include <stdio.h>
#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] = child;
parent->children.count++;
return Success(Nothing, charptr, {});
}

62
src/parser/SolsNode.h Normal file
View File

@@ -0,0 +1,62 @@
#ifndef SOLSNODE_H
#define SOLSNODE_H
#include <stdarg.h>
#include <groundvm.h>
#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_USE, SNT_STRUCT, SNT_PUTS, SNT_IF, SNT_WHILE, SNT_NEW, SNT_GROUND, SNT_ROOT
} 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);
#endif

1677
src/parser/parser.c Normal file

File diff suppressed because it is too large Load Diff

99
src/parser/parser.h Normal file
View File

@@ -0,0 +1,99 @@
#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