Compare commits

...

18 Commits

Author SHA1 Message Date
e3d64596ab Merge pull request 'refactor-lexer' (#11) from refactor-lexer into main
Reviewed-on: #11
2026-05-12 20:08:39 -06:00
56c80fa071 addition: Managing of parenthesis
Its a fucking mess, i was writting straight bullshit but it conceptually
should work, just need to refactor the shit out of it to make it way
more clean than it actually is and also later fix the fucking evaluator
like damn it sucks ASSS now (not that much really is nice but obviously
doesn't work, i like my code a lot :)
2026-05-12 20:04:41 -06:00
7f390a8c6b addition: postfix operator capability, may work 2026-05-12 19:40:42 -06:00
e30b3d7175 addition: proccessing of prefix op 2026-05-12 18:33:52 -06:00
59f99059bb refactor: changes and additions ot parser 2026-05-12 18:15:36 -06:00
c41847e120 refactor: rewrote tokenize and modified ohter funcs
Well i wanted to wildly change a lot of things about the lexer thinking
i could do something better but really all i found was automatic lexers
that at least for me don't really fit the project so a manual one it is,
i guess technically is a automata. Whatever, is good enough.
2026-04-30 21:34:27 -06:00
f2c906c6aa initial-commit 2026-04-30 10:40:17 -06:00
fee33ff1f0 Merge pull request 'refactor-generic-array' (#9) from refactor-generic-array into main
Reviewed-on: #9
all gut
2026-04-30 10:05:21 -06:00
ac2e783ccc fix: tests and implementation of lexer
Just a few details here and there, nothing wrong, everything else is
going well.
2026-04-30 09:58:27 -06:00
630d9f53e1 test: changed lexer tests 2026-04-24 09:36:03 -06:00
b7e1cdf3a6 refactor: made parser work with arrayslices and new result types 2026-04-24 09:06:47 -06:00
cef046f7db refactor: changed string to int adn tokenize number 2026-04-24 08:09:31 -06:00
19c84c382b refactor: changed funtions definitions, modified tokenize 2026-04-24 07:17:35 -06:00
855d683005 addition: Resul structs for rework 2026-04-24 07:02:00 -06:00
576bcd9504 addition: starting to refactor NodeArray into ArrayList 2026-04-23 15:37:16 -06:00
e6420cb1c9 add/fix: Added arena implementation and cmake rework 2026-04-23 12:39:04 -06:00
f50546bd07 Merge pull request 'feature-AST-using-arena' (#8) from feature-AST-using-arena into main
Reviewed-on: #8
2026-04-23 12:34:30 -06:00
c99f307827 Merge pull request 'feature-AST-using-arena' (#5) from feature-AST-using-arena into main
Reviewed-on: #5
2026-04-13 08:58:47 -06:00
15 changed files with 338 additions and 421 deletions

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@ out/Release/
# Cmake files
CMakeCache.txt
cmake
CMakeFiles/
cmake_install.cmake
CTestTestfile.cmake

View File

@@ -3,16 +3,8 @@ project(calculator VERSION 1.0 LANGUAGES C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
include(FetchContent)
FetchContent_Declare(
arena
GIT_REPOSITORY https://laentropia-homelab.tail7368da.ts.net/laentropia/Arena.git
GIT_TAG main
SOURCE_DIR ${CMAKE_SOURCE_DIR}/external/arena
)
# Export compile_commands.json (para clangd)
# clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_compile_options(
@@ -21,22 +13,24 @@ add_compile_options(
-Wpedantic
)
include(cmake/CPM.cmake)
FetchContent_MakeAvailable(arena)
add_library(arena STATIC
external/arena/src/arena.c
CPMAddPackage(
NAME arena
GIT_REPOSITORY https://laentropia-homelab.tail7368da.ts.net/laentropia/Arena.git
GIT_TAG main
)
target_include_directories(arena
PUBLIC ${CMAKE_SOURCE_DIR}/external/arena/include
CPMAddPackage(
NAME arraylist
GIT_REPOSITORY https://laentropia-homelab.tail7368da.ts.net/laentropia/ArrayList.git
GIT_TAG main
)
add_library(calculator_lib
src/lexer.c
src/parser.c
src/evaluator.c
src/ASTNodeArray.c
)
target_include_directories(calculator_lib
@@ -45,16 +39,11 @@ target_include_directories(calculator_lib
target_link_libraries(calculator_lib
PUBLIC arena
PUBLIC arraylist
)
add_executable(calculator src/main.c)
target_link_libraries(calculator calculator_lib)
# ------------------------
# Testing
# ------------------------
enable_testing()
add_subdirectory(test)

1
external/arena vendored

Submodule external/arena deleted from 3d3b8596cc

View File

@@ -1,6 +1,7 @@
#ifndef LEXER_H
#define LEXER_H
#include "arraylist.h"
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
@@ -9,6 +10,8 @@
typedef enum {
NODE_INTEGER,
NODE_BINARY_OP,
NODE_UNARY_OP,
NODE_PARENTHESIS,
} ASTNodeType;
// For classify operators
@@ -16,26 +19,19 @@ typedef enum {
OP_ADD,
OP_SUB,
OP_MUL,
OP_DIV
OP_DIV,
OP_POW,
OP_FACTORIAL,
OP_START_PAR,
OP_END_PAR,
} Operator;
typedef enum {
ARRAY_OK = 0,
ARRAY_NULL,
ARRAY_EMPTY,
ARRAY_OUT_OF_BOUNDS,
ARRAY_NULL_ARG,
ARRAY_ALLOC,
} ASTNodeArrayErr;
typedef enum {
LEXER_OK = 0,
LEXER_INT_OVERFLOW,
LEXER_FAILED_NUMBER_CONVERSION,
LEXER_NOT_RECOGNIZED_SYMBOL,
LEXER_EMPTY_INPUT,
LEXER_NULL_ARG,
LEXER_WRONG_SYNTAX,
LEXER_BUF_OVERFLOW,
} LexerErr;
@@ -49,28 +45,45 @@ typedef struct ASTNode {
struct ASTNode *right;
Operator op;
} binary;
struct {
struct ASTNode *val;
Operator op;
} unary;
struct {
struct ASTNode *val;
Operator op;
} parenthesis;
} data;
} ASTNode;
// I prefer ot have a dynamic array for storing the "tokens"
typedef struct {
size_t len;
size_t cap;
ASTNode *data;
} ASTNodeArray;
bool is_valid;
union {
LexerErr err;
ArrayList *arr;
};
} TokenizeResult;
ASTNodeArray ASTNodeArray_init(size_t size);
void ASTNodeArray_free(ASTNodeArray *arr);
ASTNodeArrayErr ASTNodeArray_push(ASTNodeArray *arr, ASTNode node);
ASTNodeArrayErr ASTNodeArray_get(const ASTNodeArray *arr, size_t index, ASTNode *out);
// Out in pop can be NULL so it doesn't return anything
ASTNodeArrayErr ASTNodeArray_pop(ASTNodeArray *arr, size_t index, ASTNode *out);
size_t ASTNodeArray_len(ASTNodeArray *arr);
typedef struct {
bool is_valid;
union {
LexerErr err;
ASTNode node;
};
} ASTNodeResult;
typedef struct {
bool is_valid;
union {
LexerErr err;
int64_t number;
};
} LexerI64Result;
// Lexer funtions as well as few functionality
LexerErr tokenize(const char* input, ASTNodeArray *out);
LexerErr tokenize_number(const char* input, size_t *offset, ASTNode *out);
LexerErr string_to_integer(const char buf[], int64_t *number);
TokenizeResult tokenize(const char* input);
ASTNodeResult tokenize_number(const char* input, size_t *offset);
LexerI64Result string_to_integer(const char buf[]);
bool isoperator(int c);
Operator char_to_operator(int c);
char operator_to_char(Operator op);

View File

@@ -3,33 +3,41 @@
#include "lexer.h"
#include "arena.h"
#include "arraylist.h"
#include <stdint.h>
typedef struct {
ASTNode *head;
} AST;
typedef struct {
ASTNodeArray *arr;
size_t pos;
} ASTNodeSlice;
typedef enum {
PARSER_OK = 0,
PARSER_UNEXPECTED_TOKEN,
PARSER_MISSING_OPERAND,
PARSER_UNMATCHED_PAREN,
PARSER_OUT_OF_MEMORY,
} ParserErr;
typedef struct {
bool is_valid;
union {
ParserErr err;
struct {
Arena arena;
ASTNode *tree;
};
};
} ParseResult;
ASTNode ASTNodeSlice_peek(ASTNodeSlice *slice);
ASTNode ASTNodeSlice_next(ASTNodeSlice *slice);
bool ASTNodeSlice_is_valid(ASTNodeSlice *slice);
ASTNode *nud(ArraySlice *slice);
ASTNode *led(ArraySlice *slice, size_t right_precedence);
ASTNode *nud(ASTNodeSlice *slice);
ASTNode *led(ASTNodeSlice *slice, size_t right_precedence);
uint8_t prefix_rbp(ASTNode node);
uint8_t postfix_lbp(ASTNode node);
uint8_t infix_lbp(ASTNode node);
uint8_t infix_rbp(ASTNode node);
uint8_t node_lbp(ASTNode node);
uint8_t node_rbp(ASTNode node);
ParseResult parse(ASTNodeArray *arr);
ASTNode *parse_expr(ASTNodeSlice *slice, Arena *arena, uint8_t min_bp);
ParseResult parse(TokenizeResult tokens);
ASTNode *parse_expr(ArraySlice *slice, Arena *arena, uint8_t min_bp);
#endif // !PARSER_H

View File

@@ -1,108 +0,0 @@
#include "lexer.h"
#include <stdlib.h>
#define NODE_ARRAY_DEFAULT_SIZE 64
// Helps state machine for the lexer :)
typedef enum {
WAIT_FOR_NUMBER,
WAIT_FOR_OPERATOR,
} LexerState;
ASTNodeArray ASTNodeArray_init(size_t size) {
ASTNodeArray new;
new.len = 0; // if 0 then use default
new.cap = size == 0 ? NODE_ARRAY_DEFAULT_SIZE : size;
new.data = malloc(new.cap * sizeof(ASTNode));
return new;
}
void ASTNodeArray_free(ASTNodeArray *arr) {
free(arr->data);
arr->cap = 0;
arr->len = 0;
}
ASTNodeArrayErr ASTNodeArray_get(const ASTNodeArray *arr, size_t index, ASTNode *out) {
if (arr == NULL) {
return ARRAY_NULL;
}
if (out == NULL) {
return ARRAY_NULL_ARG;
}
if (arr->len == 0) {
return ARRAY_EMPTY;
}
if (index >= arr->len) {
return ARRAY_OUT_OF_BOUNDS;
}
*out = arr->data[index];
return ARRAY_OK;
}
ASTNodeArrayErr ASTNodeArray_push(ASTNodeArray *arr, ASTNode node) {
if (arr == NULL) {
return ARRAY_NULL;
}
if (arr->len >= arr->cap) {
size_t new_cap = arr->cap * 2;
ASTNode *tmp = realloc(arr->data, new_cap * sizeof(ASTNode));
if (tmp == NULL) {
return ARRAY_ALLOC;
}
arr->data = tmp;
arr->cap = new_cap;
}
arr->data[arr->len] = node;
arr->len = arr->len + 1;
return ARRAY_OK;
}
ASTNodeArrayErr ASTNodeArray_pop(ASTNodeArray *arr, size_t index, ASTNode *out) {
if (arr == NULL) {
return ARRAY_NULL;
}
if (arr->len == 0) {
return ARRAY_EMPTY;
}
if (index >= arr->len) {
return ARRAY_OUT_OF_BOUNDS;
}
if (arr->cap / 4 > arr->len) {
size_t new_cap = arr->cap / 2;
ASTNode *tmp = realloc(arr->data, new_cap * sizeof(ASTNode));
if (tmp == NULL) {
return ARRAY_ALLOC;
}
arr->data = tmp;
arr->cap = new_cap;
}
if (out != NULL) {
ASTNode node_to_delete = arr->data[index];
*out = node_to_delete;
}
for (size_t i = index; i < arr->len - 1; i++) {
arr->data[index] = arr->data[index + 1];
}
return ARRAY_OK;
}
size_t ASTNodeArray_len(ASTNodeArray *arr) {
if (arr == NULL) {
return 0;
}
return arr->len;
}

View File

@@ -3,6 +3,7 @@
#include "lexer.h"
#include "parser.h"
#include <stdint.h>
#include <math.h>
int64_t evaluate_tree(ASTNode *tree) {
@@ -20,7 +21,8 @@ int64_t evaluate_tree(ASTNode *tree) {
return evaluate_tree(left) * evaluate_tree(right);
case OP_DIV:
return evaluate_tree(left) / evaluate_tree(right);
case OP_POW:
return pow(evaluate_tree(left), evaluate_tree(right));
}
}

View File

@@ -1,7 +1,9 @@
#include "lexer.h"
#include "arraylist.h"
#include <ctype.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <limits.h>
@@ -12,74 +14,68 @@ typedef enum {
} LexerState;
LexerErr tokenize(const char *input, ASTNodeArray *out) {
TokenizeResult tokenize(const char *input) {
ArrayList *arr = arraylist_init(64, sizeof(ASTNode));
size_t offset = 0;
LexerState state = WAIT_FOR_NUMBER;
ASTNodeArray arr = ASTNodeArray_init(0); // 0 defaults to 64
while (input[offset] != '\n' && input[offset] != '\0') {
int current = input[offset];
while (
input[offset] != '\n' ||
input[offset] != EOF ||
input[offset] != '\0') {
if (isdigit(current)) {
if (state != WAIT_FOR_NUMBER) {
ASTNodeArray_free(&arr);
return LEXER_WRONG_SYNTAX;
}
ASTNode new_node;
LexerErr result = tokenize_number(input, &offset, &new_node);
if (isdigit(input[offset])) {
ASTNodeResult result = tokenize_number(input, &offset);
if (result != LEXER_OK) {
ASTNodeArray_free(&arr);
return result;
if (!result.is_valid) {
return (TokenizeResult) {.is_valid = false, .err = result.err};
}
ASTNodeArray_push(&arr, new_node);
state = WAIT_FOR_OPERATOR;
} else if (isoperator(current)) {
if (state != WAIT_FOR_OPERATOR) {
return LEXER_WRONG_SYNTAX;
}
ASTNode new_node = {
arraylist_push_back(arr, &result.node);
} else if (isoperator(input[offset])) {
ASTNode op_node = {
.type = NODE_BINARY_OP,
.data.binary.op = char_to_operator(current),
.data.binary.right = NULL,
.data.binary.op = char_to_operator(input[offset]),
.data.binary.left = NULL,
.data.binary.right = NULL,
};
ASTNodeArray_push(&arr, new_node);
state = WAIT_FOR_NUMBER;
} else if (isspace(current)) {
arraylist_push_back(arr, &op_node);
} else if (isspace(input[offset])) {
// Nothing...
} else {
ASTNodeArray_free(&arr);
return LEXER_NOT_RECOGNIZED_SYMBOL;
return (TokenizeResult) {
.is_valid = false,
.err = LEXER_NOT_RECOGNIZED_SYMBOL};
}
offset++;
}
if (arr.len < 1) {
return LEXER_EMPTY_INPUT;
if (arraylist_size(arr) < 1) {
return (TokenizeResult) {.is_valid = false, .err = LEXER_EMPTY_INPUT};
}
*out = arr;
return LEXER_OK;
return (TokenizeResult) {.is_valid = true, .arr = arr};
}
// CURRENTLY, it only supports ints, not clear how floating
// point is implemented but i'll figure it out
LexerErr tokenize_number(const char *input, size_t *offset, ASTNode *out) {
char buf[128] = { '\0' };
ASTNodeResult tokenize_number(const char *input, size_t *offset) {
char buf[64] = { '\0' };
size_t buf_pos = 0;
bool is_integer = true; // Will later be used to differentiate fractions
// read number
size_t current = *offset;
while (isdigit(input[current])) {
buf[buf_pos] = input[current];
if (buf_pos >= sizeof(buf)) {
return LEXER_BUF_OVERFLOW;
return (ASTNodeResult) {
.is_valid = false,
.err = LEXER_BUF_OVERFLOW};
}
current++;
buf_pos++;
}
@@ -87,35 +83,46 @@ LexerErr tokenize_number(const char *input, size_t *offset, ASTNode *out) {
ASTNode new_node;
if (is_integer) {
new_node.type = NODE_INTEGER;
LexerErr status = string_to_integer(buf, &new_node.data.integer);
if (status == LEXER_OK) {
*out = new_node;
LexerI64Result status = string_to_integer(buf);
if (!status.is_valid) {
return (ASTNodeResult) {.is_valid = false, .err = status.err};
}
new_node.data.integer = status.number;
*offset = current;
return status;
return (ASTNodeResult) {.is_valid = true, .node = new_node};
}
return LEXER_FAILED_NUMBER_CONVERSION;
return (ASTNodeResult) {
.is_valid = false,
.err = LEXER_FAILED_NUMBER_CONVERSION};
}
LexerErr string_to_integer(const char *buf, int64_t *number) {
LexerI64Result string_to_integer(const char *buf) {
int c = 0;
int64_t count = 0;
while (buf[c] != '\0') {
// Extracts number from char
int digit = buf[c] - '0';
if (count > (INT64_MAX - digit) / 10) {
return LEXER_INT_OVERFLOW;
return (LexerI64Result) {
.is_valid = false,
.err = LEXER_INT_OVERFLOW};
}
count = count * 10;
count += digit;
c++;
}
*number = count;
return LEXER_OK;
return (LexerI64Result) {.is_valid = true, .number = count};
}
bool isoperator(int c) {
@@ -124,6 +131,10 @@ bool isoperator(int c) {
case '-':
case '/':
case '*':
case '^':
case '!':
case '(':
case ')':
return true;
default:
return false;
@@ -144,6 +155,18 @@ Operator char_to_operator(int c) {
case '/':
return OP_DIV;
break;
case '^':
return OP_POW;
break;
case '!':
return OP_FACTORIAL;
break;
case '(':
return OP_START_PAR;
break;
case ')':
return OP_END_PAR;
break;
default: // I mean shouldn't be used, we assume
return -1;
}
@@ -159,5 +182,15 @@ char operator_to_char(Operator op) {
return '*';
case OP_DIV:
return '/';
case OP_POW:
return '^';
case OP_FACTORIAL:
return '!';
case OP_START_PAR:
return '(';
case OP_END_PAR:
return ')';
default:
return EOF;
}
}

View File

@@ -1,4 +1,3 @@
#include "arena.h"
#include "evaluator.h"
#include "lexer.h"
#include "parser.h"
@@ -19,10 +18,9 @@ int main(void) {
}
buf[pos] = '\0';
ASTNodeArray context;
tokenize(buf, &context);
TokenizeResult tokens = tokenize(buf);
ParseResult par = parse(&context);
ParseResult par = parse(tokens);
int64_t result = evaluate(par);

View File

@@ -1,13 +1,40 @@
#include "parser.h"
#include "arraylist.h"
#include "lexer.h"
#include "arena.h"
#include <stdalign.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
uint8_t prefix_rbp(ASTNode node) {
if (node.type == NODE_INTEGER) {
return 0;
}
uint8_t node_lbp(ASTNode node) {
switch (node.data.unary.op) {
case OP_SUB:
case OP_ADD:
return 30;
default:
return -1;
}
}
uint8_t postfix_lbp(ASTNode node) {
if (node.type == NODE_INTEGER) {
return 0;
}
switch (node.data.unary.op) {
case OP_FACTORIAL:
return 40;
default:
// needs to be dealt with with resulttypes
return 255;
}
}
uint8_t infix_lbp(ASTNode node) {
if (node.type == NODE_INTEGER) {
return 0;
}
@@ -20,12 +47,14 @@ uint8_t node_lbp(ASTNode node) {
case OP_DIV:
case OP_MUL:
return 20;
case OP_POW:
return 51;
default:
return 0;
}
}
uint8_t node_rbp(ASTNode node) {
uint8_t infix_rbp(ASTNode node) {
if (node.type == NODE_INTEGER) {
return 0;
}
@@ -38,49 +67,32 @@ uint8_t node_rbp(ASTNode node) {
case OP_DIV:
case OP_MUL:
return 21;
case OP_POW:
return 50;
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;
}
ParseResult parse(ASTNodeArray *arr) {
ASTNodeSlice context = {
.arr = arr,
.pos = 0,
};
Arena arena = arena_init(sizeof(ASTNode) * arr->len).arena;
ParseResult parse(TokenizeResult tokens) {
ArraySlice *context = arraylist_slice(tokens.arr, 0, arraylist_size(tokens.arr));
Arena arena = arena_init(sizeof(ASTNode) * arraylist_size(tokens.arr)).arena;
return (ParseResult) {
.is_valid = true,
.arena = arena,
.tree = parse_expr(&context, &arena, 0)};
.tree = parse_expr(context, &arena, 0)};
}
ASTNode *parse_expr(ASTNodeSlice *slice, Arena *arena, uint8_t min_bp) {
ASTNode *parse_expr(ArraySlice *slice, Arena *arena, uint8_t min_bp) {
// First: Consume a first number
arena_ensure_capacity(
arena,
sizeof(ASTNode),
alignof(ASTNode)
);
); // shouldn't fail but if it does then what a shame
// Get pointer in the arena
ASTNode *left_side = arena_unwrap_pointer(
arena_alloc(
arena,
@@ -89,22 +101,90 @@ ASTNode *parse_expr(ASTNodeSlice *slice, Arena *arena, uint8_t min_bp) {
)
);
*left_side = ASTNodeSlice_next(slice);
arrayslice_next(slice, left_side);
if (left_side->type == NODE_PARENTHESIS &&
left_side->data.parenthesis.op == OP_START_PAR) {
left_side = 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;
}
while (true) {
if (!ASTNodeSlice_is_valid(slice)) {
// Second: Get next one and checn bp
if (!arrayslice_is_valid(slice)) {
break;
}
ASTNode operator = ASTNodeSlice_peek(slice);
uint8_t rbp = node_rbp(operator);
uint8_t lbp = node_lbp(operator);
// Here check if not OP error
ASTNode operator;
// Here should chekc if is operator not some bs
// Third, get operator and binding powers
arrayslice_peek(slice, &operator);
// temporary for bad error handling
if (postfix_lbp(operator) != 255) {
if (postfix_lbp(operator) < min_bp) {
break;
}
// allocate operator
arrayslice_next(slice, NULL);
arena_ensure_capacity(
arena,
sizeof(ASTNode),
alignof(ASTNode));
ASTNode *new_node = arena_unwrap_pointer(
arena_alloc(
arena,
sizeof(ASTNode),
alignof(ASTNode)
)
);
*new_node = operator;
new_node->data.unary.val = left_side;
left_side = new_node;
continue;
}
// check if it has infix or not, if not then error
uint8_t rbp = infix_rbp(operator);
uint8_t lbp = infix_lbp(operator);
if (rbp != 255 && lbp != 255) {
// If lbp is LESS then stop recursion,
// we found the next smaller binding power
// or the one with more precedence
if (lbp < min_bp) {
break;
}
ASTNodeSlice_next(slice);
// If NOT, then we continue wtching ahead
// for the next one but taking our current
// concern that is rbp of the current operator
arrayslice_next(slice, NULL);
ASTNode *right_side = parse_expr(slice, arena, rbp);
arena_ensure_capacity(
@@ -124,11 +204,14 @@ ASTNode *parse_expr(ASTNodeSlice *slice, Arena *arena, uint8_t min_bp) {
new_node->data.binary.right = right_side;
left_side = new_node;
continue;
}
break;
}
// Final: return left side
return left_side;
}

View File

@@ -1,15 +1,9 @@
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)
add_executable(test_evaluator test_evaluator.c)
target_link_libraries(test_nodeArray
calculator_lib
cmocka::cmocka
)
target_link_libraries(test_lexer
calculator_lib
cmocka::cmocka
@@ -25,7 +19,6 @@ target_link_libraries(test_evaluator
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)
add_test(NAME evaluator_tests COMMAND test_evaluator)

View File

@@ -1,86 +0,0 @@
#include "lexer.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <setjmp.h>
#include <cmocka.h>
static void test_array_push(void **state) {
(void) state;
// We use 2 to force resize and checking anything wrong with malloc
ASTNodeArray arr = ASTNodeArray_init(2);
ASTNode node1 = {
.type = NODE_INTEGER,
.data = { .integer = 90 }
};
ASTNode node2 = {
.type = NODE_INTEGER,
.data = { .integer = 80 }
};
ASTNode node3 = {
.type = NODE_INTEGER,
.data = { .integer = 70 }
};
assert_int_equal(ASTNodeArray_push(&arr, node1), ARRAY_OK);
assert_int_equal(ASTNodeArray_len(&arr), 1);
assert_int_equal(ASTNodeArray_push(&arr, node2), ARRAY_OK);
assert_int_equal(ASTNodeArray_len(&arr), 2);
assert_int_equal(ASTNodeArray_push(&arr, node3), ARRAY_OK);
assert_int_equal(ASTNodeArray_len(&arr), 3);
ASTNodeArray_free(&arr);
}
static void test_array_pop(void **state) {
(void) state;
// Set to force desize
ASTNodeArray arr = ASTNodeArray_init(16);
ASTNode node1 = {
.type = NODE_INTEGER,
.data = { .integer = 90 }
};
ASTNode node2 = {
.type = NODE_INTEGER,
.data = { .integer = 80 }
};
ASTNode node3 = {
.type = NODE_INTEGER,
.data = { .integer = 70 }
};
assert_int_equal(ASTNodeArray_push(&arr, node1), ARRAY_OK);
assert_int_equal(ASTNodeArray_len(&arr), 1);
assert_int_equal(ASTNodeArray_push(&arr, node2), ARRAY_OK);
assert_int_equal(ASTNodeArray_len(&arr), 2);
assert_int_equal(ASTNodeArray_push(&arr, node3), ARRAY_OK);
assert_int_equal(ASTNodeArray_len(&arr), 3);
ASTNode node4;
assert_int_equal(ASTNodeArray_pop(&arr, 1, &node4), ARRAY_OK);
assert_int_equal(node4.type, NODE_INTEGER);
assert_int_equal(node4.data.integer, 80);
ASTNodeArray_free(&arr);
}
int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_array_push),
cmocka_unit_test(test_array_pop),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

View File

@@ -13,10 +13,8 @@ static void test_basic_evaluation(void** state) {
(void) state;
char expr[256] = "2 + 4 * 40 / 2";
ASTNodeArray context;
tokenize(expr, &context);
ParseResult result = parse(&context);
TokenizeResult tokens = tokenize(expr);
ParseResult result = parse(tokens);
int64_t value = evaluate(result);
assert_int_equal(value, 82);

View File

@@ -1,3 +1,4 @@
#include "arraylist.h"
#include "lexer.h"
#include <stdarg.h>
#include <stdbool.h>
@@ -10,37 +11,37 @@ static void test_tokenize_normal_expresion(void **state) {
(void) state;
char expr[256] = "2 + 3 / 66 * 789";
ASTNodeArray tokens;
ASTNode node;
TokenizeResult tokens = tokenize(expr);
assert_int_equal(tokenize(expr, &tokens), LEXER_OK);
assert_int_equal(tokens.len, 7);
assert_true(tokens.is_valid);
assert_int_equal(arraylist_size(tokens.arr), 7);
ASTNodeArray_get(&tokens, 0, &node);
arraylist_get(tokens.arr, 0, &node);
assert_int_equal(node.type, NODE_INTEGER);
assert_int_equal(node.data.integer, 2);
ASTNodeArray_get(&tokens, 1, &node);
arraylist_get(tokens.arr, 1, &node);
assert_int_equal(node.type, NODE_BINARY_OP);
assert_int_equal(node.data.binary.op, OP_ADD);
ASTNodeArray_get(&tokens, 2, &node);
arraylist_get(tokens.arr, 2, &node);
assert_int_equal(node.type, NODE_INTEGER);
assert_int_equal(node.data.integer, 3);
ASTNodeArray_get(&tokens, 3, &node);
arraylist_get(tokens.arr, 3, &node);
assert_int_equal(node.type, NODE_BINARY_OP);
assert_int_equal(node.data.binary.op, OP_DIV);
ASTNodeArray_get(&tokens, 4, &node);
arraylist_get(tokens.arr, 4, &node);
assert_int_equal(node.type, NODE_INTEGER);
assert_int_equal(node.data.integer, 66);
ASTNodeArray_get(&tokens, 5, &node);
arraylist_get(tokens.arr, 5, &node);
assert_int_equal(node.type, NODE_BINARY_OP);
assert_int_equal(node.data.binary.op, OP_MUL);
ASTNodeArray_get(&tokens, 6, &node);
arraylist_get(tokens.arr, 6, &node);
assert_int_equal(node.type, NODE_INTEGER);
assert_int_equal(node.data.integer, 789);
}
@@ -49,28 +50,20 @@ static void test_tokenize_unrecognized_symbol(void **state) {
(void) state;
char expr[256] = " 2 j 3 / 66 } 789";
ASTNodeArray tokens = {
.len = 0,
.cap = 0,
};
TokenizeResult tokens = tokenize(expr);
assert_int_equal(tokenize(expr, &tokens), LEXER_NOT_RECOGNIZED_SYMBOL);
assert_int_equal(tokens.len, 0);
assert_int_equal(tokens.cap, 0);
assert_false(tokens.is_valid);
assert_uint_equal(tokens.err, LEXER_NOT_RECOGNIZED_SYMBOL);
}
static void test_tokenize_wrong_sintax(void **state) {
(void) state;
char expr[256] = "2 3 / 66 789";
ASTNodeArray tokens = {
.len = 0,
.cap = 0,
};
TokenizeResult tokens = tokenize(expr);
assert_int_equal(tokenize(expr, &tokens), LEXER_WRONG_SYNTAX);
assert_int_equal(tokens.len, 0);
assert_int_equal(tokens.cap, 0);
assert_false(tokens.is_valid);
assert_uint_equal(tokens.err, LEXER_WRONG_SYNTAX);
}
static void test_string_to_number_normal(void **state) {
@@ -78,23 +71,24 @@ static void test_string_to_number_normal(void **state) {
char num[16] = "2333t55";
size_t offset = 0;
ASTNode result;
ASTNodeResult result = tokenize_number(num, &offset);
assert_int_equal(tokenize_number(num, &offset, &result), LEXER_OK);
assert_true(result.is_valid);
assert_int_equal(offset, 4); // equal to t position in string
assert_int_equal(result.type, NODE_INTEGER);
assert_int_equal(result.data.integer, 2333);
assert_int_equal(result.node.type, NODE_INTEGER);
assert_int_equal(result.node.data.integer, 2333);
}
static void test_string_to_number_overflow(void **state) {
(void) state;
// Number is INT64_MAX but with a extra 8 at the end
char num[32] = "92233720368547758078yy7";
// Number is INT64_MAX but with a extra 899 at the end
char num[32] = "92233720368547758079";
size_t offset = 0;
ASTNode result;
assert_int_equal(tokenize_number(num, &offset, &result), LEXER_INT_OVERFLOW);
ASTNodeResult result = tokenize_number(num, &offset);
assert_false(result.is_valid);
assert_uint_equal(result.err, LEXER_INT_OVERFLOW);
// Technically it can trigger a buf overflow error but obvioulsy
// it will trigger int overflow error first
}

View File

@@ -1,4 +1,5 @@
#include "arena.h"
#include "arraylist.h"
#include "lexer.h"
#include "parser.h"
#include <stdarg.h>
@@ -12,13 +13,12 @@ static void test_parsing_basic_expression(void **state) {
(void) state;
char expr[256] = "2 + 3 / 66 * 789";
ASTNodeArray tokens;
ASTNode node;
TokenizeResult tokens = tokenize(expr);
assert_int_equal(tokenize(expr, &tokens), LEXER_OK);
assert_int_equal(tokens.len, 7);
assert_true(tokens.is_valid);
assert_int_equal(arraylist_size(tokens.arr), 7);
ParseResult result = parse(&tokens);
ParseResult result = parse(tokens);
// Assert head is +
assert_int_equal(result.tree->type, NODE_BINARY_OP);
assert_int_equal(result.tree->data.binary.op, OP_ADD);