Compare commits

...

10 Commits

Author SHA1 Message Date
1ce64d8e9e Fixed many minor things, tested it on main and is amazing, i think i need to start adding fractions next so further testing can be done with more powerful operators 2026-03-25 12:25:15 -06:00
92d142b9cf It works, basic but works, need to move out logic to places, like ASTNode array shouldn't be on the lexer or future logic for fractions and error handling in the evaluator. Just the things at the top of my head 2026-03-25 11:30:12 -06:00
845673fb0e FUCK YEAH, it works, it was the is_valid thing fo slices that was inverted, did the test by brute force and damn, is the expected AST 2026-03-25 10:22:54 -06:00
f24671bd19 well, nothing works, at least there is something to debug 2026-03-25 07:43:00 -06:00
f11b6f8c12 print funtion added, not tested 2026-03-25 06:59:52 -06:00
17be815ed0 Done with helpers, need some function to actually print the tree and see for myself if it works wit simple symbols 2026-03-24 21:51:28 -06:00
7d28b69790 added parse, just need a few more helper funtions to start testing some things 2026-03-24 21:44:08 -06:00
0d883ae978 WOW, finally making some sense, i think i get it, finally got the skelleton, gonna keep reading for adding more interesting things and doing things right, for now i think i got the parse_expr 2026-03-24 21:36:14 -06:00
acd5e9781e DAMN, i still don't get it, i really don't want to ask some AI to do it but damn, i'm finding it REALLY difficult, is like the 4th article i read, i hope this is the one and that i can get going for fucks sake 2026-03-24 21:04:36 -06:00
27787308f2 Read a pratt parsing article, i think i can do it, got nud and led the right way i think 2026-03-24 11:19:47 -06:00
13 changed files with 295 additions and 19 deletions

View File

@@ -19,7 +19,6 @@ include_directories(include)
add_library(calculator_lib
src/lexer.c
src/parser.c
src/ast.c
src/evaluator.c
)

View File

View File

@@ -0,0 +1,9 @@
#ifndef EVALUATOR_H
#define EVALUATOR_H
#include "lexer.h"
#include <stdint.h>
int64_t evaluate(ASTNode *tree);
#endif // !EVALUATOR_H

View File

@@ -74,5 +74,6 @@ LexerErr tokenize_number(const char* input, size_t *offset, ASTNode *out);
LexerErr string_to_integer(const char buf[], int64_t *number);
bool isoperator(int c);
Operator char_to_operator(int c);
char operator_to_char(Operator op);
#endif // !LEXER_H

View File

@@ -1,14 +1,29 @@
#ifndef PARSER_H
#define PARSER_H
#include "lexer.h"
#include <stdint.h>
typedef struct {
ASTNode *head;
} AST;
typedef enum {
PARSER_NUD, // Null Denotation
PARSER_LED, // Left Denotation
} ParserState;
typedef struct {
ASTNodeArray *arr;
size_t pos;
} ASTNodeSlice;
size_t node_lbp(Operator op);
size_t node_rbp(Operator op);
AST parse(ASTNodeArray arr);
ASTNode ASTNodeSlice_peek(ASTNodeSlice *slice);
ASTNode ASTNodeSlice_next(ASTNodeSlice *slice);
bool ASTNodeSlice_is_valid(ASTNodeSlice *slice);
ASTNode *nud(ASTNodeSlice *slice);
ASTNode *led(ASTNodeSlice *slice, size_t right_precedence);
uint8_t node_lbp(ASTNode node);
uint8_t node_rbp(ASTNode node);
AST parse(ASTNodeArray *arr);
ASTNode *parse_expr(ASTNodeSlice *slice, uint8_t min_bp);
#endif // !PARSER_H

View File

View File

@@ -0,0 +1,22 @@
#include "evaluator.h"
#include "lexer.h"
#include <stdint.h>
int64_t evaluate(ASTNode *tree) {
if (tree->type == NODE_BINARY_OP) {
switch (tree->data.binary.op) {
case OP_ADD:
return evaluate(tree->data.binary.left) + evaluate(tree->data.binary.right);
case OP_SUB:
return evaluate(tree->data.binary.left) - evaluate(tree->data.binary.right);
case OP_MUL:
return evaluate(tree->data.binary.left) * evaluate(tree->data.binary.right);
case OP_DIV:
return evaluate(tree->data.binary.left) / evaluate(tree->data.binary.right);
}
} else {
return tree->data.integer;
}
}

View File

@@ -248,3 +248,16 @@ Operator char_to_operator(int c) {
return -1;
}
}
char operator_to_char(Operator op) {
switch (op) {
case OP_ADD:
return '+';
case OP_SUB:
return '-';
case OP_MUL:
return '*';
case OP_DIV:
return '/';
}
}

View File

@@ -1,7 +1,30 @@
#include "evaluator.h"
#include "lexer.h"
#include "parser.h"
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
puts("Hello");
int main(void) {
char buf[256];
printf("Insert a valid mathematical expression: ");
int c;
int pos = 0;
while ((c = getc(stdin)) != '\n' && c != EOF) {
buf[pos] = c;
pos++;
}
buf[pos] = '\0';
ASTNodeArray context;
tokenize(buf, &context);
AST tree = parse(&context);
int64_t result = evaluate(tree.head);
printf("El resultado es: %" PRIi64 "\n", result);
return EXIT_SUCCESS;
}

View File

@@ -1,9 +1,16 @@
#include "parser.h"
#include "lexer.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
size_t node_lbp(Operator op) {
switch (op) {
uint8_t node_lbp(ASTNode node) {
if (node.type == NODE_INTEGER) {
return 0;
}
switch (node.data.binary.op) {
case OP_ADD:
case OP_SUB:
return 10;
@@ -11,17 +18,91 @@ size_t node_lbp(Operator op) {
case OP_DIV:
case OP_MUL:
return 20;
default:
return 0;
}
}
size_t node_rbp(Operator op) {
switch (op) {
uint8_t node_rbp(ASTNode node) {
if (node.type == NODE_INTEGER) {
return 0;
}
switch (node.data.binary.op) {
case OP_ADD:
case OP_SUB:
return 10;
return 11;
break;
case OP_DIV:
case OP_MUL:
return 20;
return 21;
default:
return 0;
}
}
ASTNode ASTNodeSlice_next(ASTNodeSlice *slice) {
return slice->arr->data[slice->pos++];
}
ASTNode ASTNodeSlice_peek(ASTNodeSlice *slice) {
return slice->arr->data[slice->pos];
}
bool ASTNodeSlice_is_valid(ASTNodeSlice *slice) {
if (slice->arr->len < 1) {
return false;
}
if (slice->pos >= slice->arr->len) {
return false;
}
return true;
}
AST parse(ASTNodeArray *arr) {
AST tree;
ASTNodeSlice context = {
.arr = arr,
.pos = 0,
};
tree.head = parse_expr(&context, 0);
return tree;
}
ASTNode *parse_expr(ASTNodeSlice *slice, uint8_t min_bp) {
ASTNode *left_side = malloc(sizeof(ASTNode));
*left_side = ASTNodeSlice_next(slice);
while (true) {
if (!ASTNodeSlice_is_valid(slice)) {
break;
}
ASTNode operator = ASTNodeSlice_peek(slice);
uint8_t rbp = node_rbp(operator);
uint8_t lbp = node_lbp(operator);
if (lbp < min_bp) {
break;
}
ASTNodeSlice_next(slice);
ASTNode *right_side = parse_expr(slice, rbp);
ASTNode *new_node = malloc(sizeof(ASTNode));
*new_node = operator;
new_node->data.binary.left = left_side;
new_node->data.binary.right = right_side;
left_side = new_node;
}
return left_side;
}

View File

@@ -2,6 +2,7 @@ find_package(cmocka REQUIRED)
add_executable(test_nodeArray test_ASTNodeArray.c)
add_executable(test_lexer test_lexer.c)
add_executable(test_parser test_parser.c)
target_link_libraries(test_nodeArray
calculator_lib
@@ -13,5 +14,11 @@ target_link_libraries(test_lexer
cmocka::cmocka
)
target_link_libraries(test_parser
calculator_lib
cmocka::cmocka
)
add_test(NAME nodeArray_tests COMMAND test_nodeArray)
add_test(NAME lexer_tests COMMAND test_lexer)
add_test(NAME parser_tests COMMAND test_parser)

32
test/test_evaluator.c Normal file
View File

@@ -0,0 +1,32 @@
#include "lexer.h"
#include "parser.h"
#include "evaluator.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <setjmp.h>
#include <cmocka.h>
#include <stdlib.h>
static void test_basic_evaluation(void** state) {
(void) state;
char expr[256] = "2 + 4 * 40 / 2";
ASTNodeArray context;
tokenize(expr, &context);
AST tree = parse(&context);
uint64_t value = evaluate(tree.head);
assert_int_equal(value, 82);
}
int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_basic_evaluation),
};
cmocka_run_group_tests(tests, NULL, NULL);
return EXIT_SUCCESS;
}

View File

@@ -1,5 +1,79 @@
#include <stdlib.h>
#include "lexer.h"
#include "parser.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <setjmp.h>
#include <cmocka.h>
int main() {
return EXIT_SUCCESS;
static void test_parsing_basic_expression(void **state) {
(void) state;
char expr[256] = "2 + 3 / 66 * 789";
ASTNodeArray tokens;
ASTNode node;
assert_int_equal(tokenize(expr, &tokens), LEXER_OK);
assert_int_equal(tokens.len, 7);
AST tree = parse(&tokens);
// Assert head is +
assert_int_equal(tree.head->type, NODE_BINARY_OP);
assert_int_equal(tree.head->data.binary.op, OP_ADD);
assert_int_equal(tree.head->data.binary.left->type, NODE_INTEGER);
assert_int_equal(tree.head->data.binary.left->data.integer, 2);
assert_int_equal(
tree.head->data.binary.right->type,
NODE_BINARY_OP
);
assert_int_equal(
tree.head->data.binary.right->data.binary.op,
OP_MUL
);
assert_int_equal(
tree.head->data.binary.right->data.binary.right->type,
NODE_INTEGER);
assert_int_equal(
tree.head->data.binary.right->data.binary.right->data.integer,
789);
assert_int_equal(
tree.head->data.binary.right->data.binary.left->type,
NODE_BINARY_OP
);
assert_int_equal(
tree.head->data.binary.right->data.binary.left->data.binary.op,
OP_DIV
);
assert_int_equal(
tree.head->data.binary.right->data.binary.left->data.binary.right->type,
NODE_INTEGER
);
assert_int_equal(
tree.head->data.binary.right->data.binary.left->data.binary.right->data.integer,
66
);
assert_int_equal(
tree.head->data.binary.right->data.binary.left->data.binary.left->type,
NODE_INTEGER
);
assert_int_equal(
tree.head->data.binary.right->data.binary.left->data.binary.left->data.integer,
3
);
}
int main(void) {
const struct CMUnitTest tests [] = {
cmocka_unit_test(test_parsing_basic_expression),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}