#include "parser.h" #include "SolsNode.h" #include "../include/estr.h" #include "../include/ansii.h" SolsTokenPrecedence getPrecedence(SolsToken *token) { switch (token->type) { case STT_LINE_END: return STP_NEWLINE; case STT_KW_PUTS: return STP_PUTS; 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; 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) { 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); } 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); } 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); } 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); // 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); // 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]); } // Add any error messages from parsing if (parsed.error) { addToParserErrors(parser, parsed.as.error); } addChildToSolsNode(parser->currentParent, node.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); } } 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 ++]); }