diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c index e7be6ac..3be6c0b 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -346,7 +346,7 @@ ResultType(SolsToken, charptr) identifyToken(const char* token) { } -char* createParsingError(size_t lineNum, char* line, char* why) { +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); @@ -476,7 +476,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { if (strcmp(buf.str, "") != 0) { ResultType(SolsToken, charptr) result = identifyToken(buf.str); if (result.error) { - char* err = createParsingError(lineNum, currentLine.str, result.as.error); + char* err = createLexingError(lineNum, currentLine.str, result.as.error); DESTROY_ESTR(buf); DESTROY_ESTR(currentLine); return Error(Nothing, charptr, err); @@ -484,7 +484,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { 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 = createParsingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); + 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); @@ -497,7 +497,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { char tmp[] = {chr.as.success, '\0'}; ResultType(SolsToken, charptr) result = identifyToken(tmp); if (result.error) { - char* err = createParsingError(lineNum, currentLine.str, result.as.error); + char* err = createLexingError(lineNum, currentLine.str, result.as.error); DESTROY_ESTR(buf); DESTROY_ESTR(currentLine); return Error(Nothing, charptr, err); @@ -505,7 +505,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { 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 = createParsingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); + 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); @@ -522,7 +522,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { if (strcmp(buf.str, "") != 0) { ResultType(SolsToken, charptr) result = identifyToken(buf.str); if (result.error) { - char* err = createParsingError(lineNum, currentLine.str, result.as.error); + char* err = createLexingError(lineNum, currentLine.str, result.as.error); DESTROY_ESTR(buf); DESTROY_ESTR(currentLine); return Error(Nothing, charptr, err); @@ -530,7 +530,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { 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 = createParsingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); + 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); @@ -545,7 +545,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { char tmp[] = {chr.as.success, '\0'}; ResultType(SolsToken, charptr) result = identifyToken(tmp); if (result.error) { - char* err = createParsingError(lineNum, currentLine.str, result.as.error); + char* err = createLexingError(lineNum, currentLine.str, result.as.error); DESTROY_ESTR(buf); DESTROY_ESTR(currentLine); return Error(Nothing, charptr, err); @@ -553,7 +553,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { 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 = createParsingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); + 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); @@ -565,7 +565,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { char tmp[] = {chr.as.success, '=', '\0'}; ResultType(SolsToken, charptr) result = identifyToken(tmp); if (result.error) { - char* err = createParsingError(lineNum, currentLine.str, result.as.error); + char* err = createLexingError(lineNum, currentLine.str, result.as.error); DESTROY_ESTR(buf); DESTROY_ESTR(currentLine); return Error(Nothing, charptr, err); @@ -573,7 +573,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { 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 = createParsingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); + 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); @@ -586,7 +586,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { char tmp[] = {chr.as.success, chr.as.success, '\0'}; ResultType(SolsToken, charptr) result = identifyToken(tmp); if (result.error) { - char* err = createParsingError(lineNum, currentLine.str, result.as.error); + char* err = createLexingError(lineNum, currentLine.str, result.as.error); DESTROY_ESTR(buf); DESTROY_ESTR(currentLine); return Error(Nothing, charptr, err); @@ -594,7 +594,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { 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 = createParsingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); + 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); @@ -616,7 +616,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { if (strcmp(buf.str, "") != 0) { ResultType(SolsToken, charptr) result = identifyToken(buf.str); if (result.error) { - char* err = createParsingError(lineNum, currentLine.str, result.as.error); + char* err = createLexingError(lineNum, currentLine.str, result.as.error); DESTROY_ESTR(buf); DESTROY_ESTR(currentLine); return Error(Nothing, charptr, err); @@ -624,7 +624,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { 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 = createParsingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); + 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); @@ -639,7 +639,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { char tmp[] = {chr.as.success, '\0'}; ResultType(SolsToken, charptr) result = identifyToken(tmp); if (result.error) { - char* err = createParsingError(lineNum, currentLine.str, result.as.error); + char* err = createLexingError(lineNum, currentLine.str, result.as.error); DESTROY_ESTR(buf); DESTROY_ESTR(currentLine); return Error(Nothing, charptr, err); @@ -647,7 +647,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { 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 = createParsingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); + 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); @@ -659,7 +659,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { char tmp[] = {chr.as.success, '=', '\0'}; ResultType(SolsToken, charptr) result = identifyToken(tmp); if (result.error) { - char* err = createParsingError(lineNum, currentLine.str, result.as.error); + char* err = createLexingError(lineNum, currentLine.str, result.as.error); DESTROY_ESTR(buf); DESTROY_ESTR(currentLine); return Error(Nothing, charptr, err); @@ -667,7 +667,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { 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 = createParsingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); + 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); @@ -690,7 +690,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { if (strcmp(buf.str, "") != 0) { ResultType(SolsToken, charptr) result = identifyToken(buf.str); if (result.error) { - char* err = createParsingError(lineNum, currentLine.str, result.as.error); + char* err = createLexingError(lineNum, currentLine.str, result.as.error); DESTROY_ESTR(buf); DESTROY_ESTR(currentLine); return Error(Nothing, charptr, err); @@ -698,7 +698,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { 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 = createParsingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); + 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); @@ -719,7 +719,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { if (strcmp(buf.str, "") != 0) { ResultType(SolsToken, charptr) result = identifyToken(buf.str); if (result.error) { - char* err = createParsingError(lineNum, currentLine.str, result.as.error); + char* err = createLexingError(lineNum, currentLine.str, result.as.error); DESTROY_ESTR(buf); DESTROY_ESTR(currentLine); return Error(Nothing, charptr, err); @@ -727,7 +727,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { 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 = createParsingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); + 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); @@ -769,7 +769,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { if (strcmp(buf.str, "") != 0) { ResultType(SolsToken, charptr) result = identifyToken(buf.str); if (result.error) { - char* err = createParsingError(lineNum, currentLine.str, result.as.error); + char* err = createLexingError(lineNum, currentLine.str, result.as.error); DESTROY_ESTR(buf); DESTROY_ESTR(currentLine); return Error(Nothing, charptr, err); @@ -777,7 +777,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { 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 = createParsingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)"); + 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); @@ -787,7 +787,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) { } if (inString) { - char* err = createParsingError(lineNum, currentLine.str, "Unterminated string"); + char* err = createLexingError(lineNum, currentLine.str, "Unterminated string"); DESTROY_ESTR(buf); DESTROY_ESTR(currentLine); return Error(Nothing, charptr, err); diff --git a/src/main.c b/src/main.c index 4d85232..0e12e59 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,5 @@ #include "lexer/lexer.h" +#include "parser/parser.h" #include char* getFileContents(const char* filename) { @@ -43,6 +44,8 @@ int main(int argc, char** argv) { exit(1); } char* fileContents = getFileContents(argv[1]); + + // Lex file ResultType(SolsLexer, charptr) lexer = createLexer(fileContents); if (lexer.error) { printf("Error while creating lexer: %s", lexer.as.error); @@ -50,7 +53,19 @@ int main(int argc, char** argv) { } ResultType(Nothing, charptr) lexed = lex(&lexer.as.success); if (lexed.error) { - printf("%s", lexed.as.error); + printf("%s\n", lexed.as.error); + exit(1); + } + + // Parse file + ResultType(SolsParser, charptr) parser = createSolsParser(&lexer.as.success.output); + if (parser.error) { + printf("Error while creating parser: %s\n", parser.as.error); + exit(1); + } + ResultType(Nothing, charptr) parsed = parse(&parser.as.success); + if (parsed.error) { + printf("%s\n", parsed.as.error); exit(1); } diff --git a/src/parser/parser.c b/src/parser/parser.c index 68d9ba6..9bdec32 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -2,6 +2,7 @@ #include "SolsNode.h" #include "../include/estr.h" +#include "../include/ansii.h" ResultType(SolsParser, charptr) createSolsParser(SolsTokens* input) { ResultType(SolsNode, charptr) node = createSolsNode(SNT_ROOT); @@ -12,14 +13,134 @@ ResultType(SolsParser, charptr) createSolsParser(SolsTokens* input) { SolsParser parser = { .input = input, .current = 0, - .output = node.as.success + .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)"); + } parser.currentParent = &parser.output; return Success(SolsParser, charptr, parser); } +void createParserError(SolsParser* parser, char* what) { + + ResultType(SolsToken, Nothing) prevToken; + SolsToken token = parserPeek(parser, 0).as.success; + ResultType(SolsToken, Nothing) nextToken; + + // Find tokens for previous line and next line + for (size_t i = parser->current; i > 0; i--) { + prevToken = parserLookAt(parser, i); + if (prevToken.error || prevToken.as.success.line.num != token.line.num) { + break; + } + } + if (prevToken.error || prevToken.as.success.line.num == token.line.num) { + prevToken = Error(SolsToken, Nothing, {}); + } + + for (size_t i = parser->current; i <= parser->input->count; i++) { + nextToken = parserLookAt(parser, i); + if (nextToken.error || nextToken.as.success.line.num != token.line.num) { + break; + } + } + if (nextToken.error || nextToken.as.success.line.num == token.line.num) { + nextToken = Error(SolsToken, Nothing, {}); + } + + 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; + } + } + 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) { + 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); + return Error(Nothing, charptr, "Not an error, just curious what errors look like"); + + return Success(Nothing, charptr, {}); +} +static inline ResultType(Nothing, charptr) parseLiteral(SolsParser* parser) { + return Error(Nothing, charptr, "Not an error, just curious what errors look like"); + + return Success(Nothing, charptr, {}); +} + +static inline ResultType(Nothing, charptr) parsePuts(SolsParser* parser) { + return Error(Nothing, charptr, "Not an error, just curious what errors look like"); + return Success(Nothing, charptr, {}); +} + ResultType(Nothing, charptr) parse(SolsParser* parser) { - return Error(Nothing, charptr, "Work in progress"); + 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); + } + } + 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, size_t ahead) { diff --git a/src/parser/parser.h b/src/parser/parser.h index c81e74d..1f8a377 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -11,11 +11,17 @@ // .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); @@ -34,6 +40,12 @@ ResultType(Nothing, charptr) parse(SolsParser* parser); 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 @@ -46,4 +58,15 @@ ResultType(SolsToken, Nothing) parserPeek(SolsParser* parser, size_t ahead); // 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) {\ + printf("Currently parsing " #tokentype "\n");\ + ResultType(Nothing, charptr) __result = parse##tokentype(parser);\ + if (__result.error) {\ + createParserError(parser, __result.as.error);\ + }\ + break;\ +} + #endif