refactor-error-handling #12

Merged
laentropia merged 11 commits from refactor-error-handling into main 2026-05-13 19:05:55 -06:00
2 changed files with 244 additions and 96 deletions
Showing only changes of commit 542a94ef81 - Show all commits

View File

@@ -11,9 +11,10 @@ typedef enum {
NODE_INT, NODE_INT,
NODE_BINARY_OP, NODE_BINARY_OP,
NODE_UNARY_OP, NODE_UNARY_OP,
NODE_PARENTHESIS,
} NodeType; } NodeType;
typedef struct { typedef struct Node {
NodeType type; NodeType type;
union { union {
int64_t num; int64_t num;
@@ -26,6 +27,7 @@ typedef struct {
Operator op; Operator op;
struct Node *to; struct Node *to;
}unary; }unary;
Operator par;
}; };
} Node; } Node;
@@ -64,6 +66,8 @@ typedef struct {
}; };
} ParserU8Result; } ParserU8Result;
Node token_to_node(Token token);
ParserU8Result prefix_rbp(Node node); ParserU8Result prefix_rbp(Node node);
ParserU8Result postfix_lbp(Node node); ParserU8Result postfix_lbp(Node node);
ParserU8Result infix_lbp(Node node); ParserU8Result infix_lbp(Node node);

View File

@@ -2,146 +2,281 @@
#include "arraylist.h" #include "arraylist.h"
#include "lexer.h" #include "lexer.h"
#include "arena.h" #include "arena.h"
#include <cmocka.h>
#include <stdalign.h> #include <stdalign.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
uint8_t prefix_rbp(ASTNode node) { ParserU8Result prefix_rbp(Node node) {
if (node.type == NODE_INTEGER) { if (node.type != NODE_UNARY_OP) {
return 0; return (ParserU8Result) {
.is_valid = false,
.err = PARSER_UNEXPECTED_TOKEN
};
} }
switch (node.data.unary.op) { switch (node.unary.op) {
case OP_SUB: case OP_SUB:
case OP_ADD: case OP_ADD:
return 30; return (ParserU8Result) {
.is_valid = true,
.num = 30,
};
default: default:
return -1; return (ParserU8Result) {
.is_valid = false,
.err = PARSER_UNEXPECTED_TOKEN,
};
} }
} }
uint8_t postfix_lbp(ASTNode node) { ParserU8Result postfix_lbp(Node node) {
if (node.type == NODE_INTEGER) { if (node.type != NODE_UNARY_OP) {
return 0; return (ParserU8Result) {
.is_valid = false,
.err = PARSER_UNEXPECTED_TOKEN,
};
} }
switch (node.data.unary.op) { switch (node.unary.op) {
case OP_FACTORIAL: case OP_FACTORIAL:
return 40; return (ParserU8Result) {
.is_valid = true,
.num = 40,
};
default: default:
// needs to be dealt with with resulttypes return (ParserU8Result) {
return 255; .is_valid = false,
.err = PARSER_UNEXPECTED_TOKEN,
};
} }
} }
uint8_t infix_lbp(ASTNode node) { ParserU8Result infix_lbp(Node node) {
if (node.type == NODE_INTEGER) { if (node.type != NODE_BINARY_OP) {
return 0; return (ParserU8Result) {
.is_valid = false,
.err = PARSER_UNEXPECTED_TOKEN,
};
} }
switch (node.data.binary.op) { switch (node.binary.op) {
case OP_ADD: case OP_ADD:
case OP_SUB: case OP_SUB:
return 10; return (ParserU8Result) {
break; .is_valid = true,
.num = 10,
};
case OP_DIV: case OP_DIV:
case OP_MUL: case OP_MUL:
return 20; return (ParserU8Result) {
.is_valid = true,
.num = 20,
};
case OP_POW: case OP_POW:
return 51; return (ParserU8Result) {
.is_valid = true,
.num = 51,
};
default: default:
return 0; return (ParserU8Result) {
.is_valid = false,
.err = PARSER_UNEXPECTED_TOKEN,
};
} }
} }
uint8_t infix_rbp(ASTNode node) { ParserU8Result infix_rbp(Node node) {
if (node.type == NODE_INTEGER) { if (node.type != NODE_BINARY_OP) {
return 0; return (ParserU8Result) {
.is_valid = false,
.err = PARSER_UNEXPECTED_TOKEN,
};
} }
switch (node.data.binary.op) { switch (node.binary.op) {
case OP_ADD: case OP_ADD:
case OP_SUB: case OP_SUB:
return 11; return (ParserU8Result) {
break; .is_valid = true,
.num = 11,
};
case OP_DIV: case OP_DIV:
case OP_MUL: case OP_MUL:
return 21; return (ParserU8Result) {
.is_valid = true,
.num = 21,
};
case OP_POW: case OP_POW:
return 50; return (ParserU8Result) {
.is_valid = true,
.num = 50,
};
default: default:
return 0; return (ParserU8Result) {
.is_valid = false,
.err = PARSER_UNEXPECTED_TOKEN,
};
} }
} }
ParseResult parse(TokenizeResult tokens) { Node token_to_node(Token token) {
if (token.type == TOKEN_INTEGER) {
return (Node) {
.type = NODE_INT,
.num = token.num,
};
}
switch (token.op) {
case OP_ADD:
return (Node) {
.type = NODE_BINARY_OP,
.binary.op = token.op,
};
case OP_SUB:
return (Node) {
.type = NODE_BINARY_OP,
.binary.op = token.op,
};
case OP_MUL:
return (Node) {
.type = NODE_BINARY_OP,
.binary.op = token.op,
};
case OP_DIV:
return (Node) {
.type = NODE_BINARY_OP,
.binary.op = token.op,
};
case OP_POW:
return (Node) {
.type = NODE_UNARY_OP,
.binary.op = token.op,
};
case OP_FACTORIAL:
return (Node) {
.type = NODE_UNARY_OP,
.binary.op = token.op,
};
case OP_START_PAR:
return (Node) {
.type = NODE_PARENTHESIS,
.binary.op = token.op,
};
case OP_END_PAR:
return (Node) {
.type = NODE_PARENTHESIS,
.binary.op = token.op,
};
}
}
ParserResult parse(TokenizeResult tokens) {
ArraySlice *context = arraylist_slice(tokens.arr, 0, arraylist_size(tokens.arr)); ArraySlice *context = arraylist_slice(tokens.arr, 0, arraylist_size(tokens.arr));
Arena arena = arena_init(sizeof(ASTNode) * arraylist_size(tokens.arr)).arena; Arena arena = arena_init(sizeof(Node) * arraylist_size(tokens.arr)).arena;
return (ParseResult) { NodeResult result = parse_expr(context, &arena, 0);
if (!result.is_valid) {
return (ParserResult) {
.is_valid = false,
.err = result.err,
};
}
return (ParserResult) {
.is_valid = true, .is_valid = true,
.arena = arena, .arena = arena,
.tree = parse_expr(context, &arena, 0)}; .tree = result.node};
} }
ASTNode *parse_expr(ArraySlice *slice, Arena *arena, uint8_t min_bp) { NodeResult parse_expr(ArraySlice *slice, Arena *arena, uint8_t min_bp) {
// First: Consume a first number
arena_ensure_capacity( arena_ensure_capacity(
arena, arena,
sizeof(ASTNode), sizeof(Node),
alignof(ASTNode) alignof(Node)
); // shouldn't fail but if it does then what a shame ); // shouldn't fail but if it does then what a shame
// Get pointer in the arena // Get pointer in the arena
ASTNode *left_side = arena_unwrap_pointer( Node *left_side = arena_unwrap_pointer(
arena_alloc( arena_alloc(
arena, arena,
sizeof(ASTNode), sizeof(Node),
alignof(ASTNode) alignof(Node)
) )
); );
arrayslice_next(slice, left_side); // convert token to node :)
Token current_token;
arrayslice_next(slice, &current_token);
*left_side = token_to_node(current_token);
if (left_side->type == NODE_PARENTHESIS && if (left_side->type == NODE_PARENTHESIS &&
left_side->data.parenthesis.op == OP_START_PAR) { left_side->par == OP_START_PAR) {
left_side = parse_expr(slice, arena, 0); NodeResult result = parse_expr(slice, arena, 0);
// HERE CHEKC LATER if slice.next != ')'
ASTNode *end_par;
arrayslice_next(slice, &end_par);
if (end_par->type != NODE_PARENTHESIS ||
end_par->data.parenthesis.op != OP_END_PAR) {
// todo
}
return left_side;
}
// if is unary then take prefix bp and continue
// to the right, no need to allocate left side
// because we just did and right side
// WILL return a valid allocated pointer.
if (left_side->type == NODE_UNARY_OP) {
uint8_t rbp = prefix_rbp(*left_side);
ASTNode *righ_side = parse_expr(slice, arena, rbp);
left_side->data.unary.val = righ_side; if (!result.is_valid) {
return result;
}
left_side = result.node;
// We dont really need to convert to node
// parenthesis are there just to change up
// the bp
Token end_par;
arrayslice_next(slice, &end_par);
if (end_par.type != TOKEN_OPERATOR ||
end_par.op != OP_END_PAR) {
return (NodeResult) {
.is_valid = false,
.err = PARSER_UNMATCHED_PAREN,
};
}
return (NodeResult) {
.is_valid = true,
.node = left_side,
};
}
// If prefix
if (left_side->type == NODE_UNARY_OP) {
ParserU8Result rbp_result = prefix_rbp(*left_side);
if (!rbp_result.is_valid) {
return (NodeResult) {
.is_valid = false,
.err = rbp_result.err,
};
}
NodeResult righ_side_result = parse_expr(slice, arena, rbp_result.num);
if (!righ_side_result.is_valid) {
return righ_side_result;
}
left_side->unary.to = righ_side_result.node;
} }
while (true) { while (true) {
// Second: Get next one and checn bp
if (!arrayslice_is_valid(slice)) { if (!arrayslice_is_valid(slice)) {
break; break;
} }
// Here check if not OP error Token operator_token;
arrayslice_peek(slice, &operator_token);
ASTNode operator; if (operator_token.type != TOKEN_OPERATOR) {
// Here should chekc if is operator not some bs return (NodeResult) {
// Third, get operator and binding powers .is_valid = false,
arrayslice_peek(slice, &operator); .err = PARSER_MISSING_OPERAND,
};
}
Node operator_node = token_to_node(operator_token);
// temporary for bad error handling // temporary for bad error handling
if (postfix_lbp(operator) != 255) { ParserU8Result postfix_lbp_result = postfix_lbp(operator_node);
if (postfix_lbp(operator) < min_bp) { if (postfix_lbp_result.is_valid) {
if (postfix_lbp_result.num < min_bp) {
break; break;
} }
@@ -149,59 +284,65 @@ ASTNode *parse_expr(ArraySlice *slice, Arena *arena, uint8_t min_bp) {
arrayslice_next(slice, NULL); arrayslice_next(slice, NULL);
arena_ensure_capacity( arena_ensure_capacity(
arena, arena,
sizeof(ASTNode), sizeof(Node),
alignof(ASTNode)); alignof(Node));
ASTNode *new_node = arena_unwrap_pointer( Node *new_node = arena_unwrap_pointer(
arena_alloc( arena_alloc(
arena, arena,
sizeof(ASTNode), sizeof(Node),
alignof(ASTNode) alignof(Node)
) )
); );
*new_node = operator; *new_node = operator_node;
new_node->data.unary.val = left_side; new_node->unary.to = left_side;
left_side = new_node; left_side = new_node;
continue; continue;
} }
// check if it has infix or not, if not then error ParserU8Result rbp_result = infix_rbp(operator_node);
uint8_t rbp = infix_rbp(operator); ParserU8Result lbp_result = infix_lbp(operator_node);
uint8_t lbp = infix_lbp(operator); if (!rbp_result.is_valid || !lbp_result.is_valid) {
return (NodeResult) {
if (rbp != 255 && lbp != 255) { .is_valid = false,
.err = PARSER_UNEXPECTED_TOKEN,
};
}
if (rbp_result.is_valid && lbp_result.is_valid) {
// If lbp is LESS then stop recursion, // If lbp is LESS then stop recursion,
// we found the next smaller binding power // we found the next smaller binding power
// or the one with more precedence // or the one with more precedence
if (lbp < min_bp) { if (lbp_result.num < min_bp) {
break; break;
} }
// If NOT, then we continue wtching ahead // If NOT, then we continue wtching ahead
// for the next one but taking our current // for the next one but taking our current
// concern that is rbp of the current operator // concern that is rbp of the current operator
arrayslice_next(slice, NULL); arrayslice_next(slice, NULL);
ASTNode *right_side = parse_expr(slice, arena, rbp); NodeResult right_side_result = parse_expr(slice, arena, rbp_result.num);
if (!right_side_result.is_valid) {
return right_side_result;
}
arena_ensure_capacity( arena_ensure_capacity(
arena, arena,
sizeof(ASTNode), sizeof(Node),
alignof(ASTNode)); alignof(Node));
ASTNode *new_node = arena_unwrap_pointer( Node *new_node = arena_unwrap_pointer(
arena_alloc( arena_alloc(
arena, arena,
sizeof(ASTNode), sizeof(Node),
alignof(ASTNode) alignof(Node)
) )
); );
*new_node = operator; *new_node = operator_node;
new_node->data.binary.left = left_side; new_node->binary.left = left_side;
new_node->data.binary.right = right_side; new_node->binary.right = right_side_result.node;
left_side = new_node; left_side = new_node;
@@ -213,5 +354,8 @@ ASTNode *parse_expr(ArraySlice *slice, Arena *arena, uint8_t min_bp) {
// Final: return left side // Final: return left side
return left_side; return (NodeResult){
.is_valid = true,
.node = left_side,
};
} }