calculator/ast.c

172 lines
5.8 KiB
C

/* vim: set filetype=c: */
#include "ast.h"
#include "ptr_stack.h"
#include <malloc.h>
#include <assert.h>
#include <math.h>
void AST_init(AST* obj) {
obj->root = NULL;
}
static Token* new_default_ExprToken_() {
Token* tkn = malloc(sizeof(Token));
tkn->type = TokenTypeExpr;
tkn->data.expr.lhs = NULL; /* Left operand */
tkn->data.expr.op = NULL; /* Operator */
tkn->data.expr.rhs = NULL; /* Right operand */
return tkn;
}
static Token* new_NumToken_with_num_(NumToken val) {
Token* tkn = malloc(sizeof(Token));
tkn->type = TokenTypeNum;
tkn->data.num = val;
return tkn;
}
static Token* new_OpToken_with_sym_(OpToken val) {
Token* tkn = malloc(sizeof(Token));
tkn->type = TokenTypeOp;
tkn->data.op = val;
return tkn;
}
Result AST_parse_from_TokenList(AST* obj, const TokenList* tokens) {
/* If root weren't NULL, the AST was either not initialized, or
* it was already populated */
assert(obj->root == NULL);
obj->root = new_default_ExprToken_();
PtrStack node_stack; /* Always has curr_node at the top, followed by curr_node's parent, etc.. */
PtrStack_init(&node_stack);
Token* curr_node = obj->root; /* Node means ExprToken in this case */
PtrStack_push(&node_stack, curr_node);
/* Linearly iterate through every token the lexer generated */
const TokenListItem* curr;
for(curr = tokens->front; curr != NULL; curr = curr->next) {
switch(curr->val.type) {
default:
return Result_err("Found invalid token type while building AST");
break;
case TokenTypeSep: {
if(curr->val.data.sep.sym == '(') {
/* Create a new sub expression as a child of the current expression */
Token* new_tkn = new_default_ExprToken_();
/* The left and right hand operands get filled from left to right;
* insert the new sub expression into the next free operand slot,
* free meaning unassigned or NULL */
if(curr_node->data.expr.lhs == NULL)
curr_node->data.expr.lhs = new_tkn;
else if (curr_node->data.expr.rhs == NULL)
curr_node->data.expr.rhs = new_tkn;
else {
free(new_tkn); /* Free new_tkn, as it is unused due to an error */
return Result_err("Found more than 2 operands for 1 operator while building AST");
break;
}
/* Push the new curr_node onto the pointer stack to allow to
* go a layer back, as needed in case of an rparen */
curr_node = new_tkn;
PtrStack_push(&node_stack, curr_node);
} else /* if(curr->val.data.sep.sym == ')') */ {
/* Go back a layer, effectively changing curr_node to its parent */
PtrStack_pop(&node_stack);
curr_node = node_stack.top->ptr;
}
break;
}
case TokenTypeNum: {
assert(curr_node->type == TokenTypeExpr);
Token* num_tkn = new_NumToken_with_num_(curr->val.data.num);
/* Fill the curr_node expression operands from left to right */
if(curr_node->data.expr.lhs == NULL)
curr_node->data.expr.lhs = num_tkn;
else if (curr_node->data.expr.rhs == NULL)
curr_node->data.expr.rhs = num_tkn;
else {
free(num_tkn); /* Free num_tkn, as it is unused due to an error */
return Result_err("Found more than 2 operands for 1 operator while building AST");
break;
}
break;
}
case TokenTypeOp: {
if(curr_node->data.expr.op == NULL) {
/* Fill the expression token's operator field with exactly the same
* data as the current token */
Token* op_tkn = new_OpToken_with_sym_(curr->val.data.op);
curr_node->data.expr.op = op_tkn;
} else {
return Result_err("Found more than 1 operator in a single expression while building the AST");
break;
}
break;
}
}
}
PtrStack_uninit(&node_stack);
return Result_noerr();
}
static long double AST_eval_node_(Token* node) {
assert(node->type == TokenTypeExpr);
long double a, b;
if(node->data.expr.lhs == NULL)
/* TODO: Error handling */
return 0;
if(node->data.expr.lhs->type == TokenTypeExpr)
a = AST_eval_node_(node->data.expr.lhs);
else /* if(node->data.expr.lhs->type == TokenTypeNum) */
a = node->data.expr.lhs->data.num;
if(node->data.expr.rhs == NULL)
/* If there is no right hand side expression, just return the left hand one */
return a;
if(node->data.expr.rhs->type == TokenTypeExpr)
b = AST_eval_node_(node->data.expr.rhs);
else /* if(node->data.expr.rhs->type == TokenTypeNum) */
b = node->data.expr.rhs->data.num;
switch(node->data.expr.op->data.op) {
case '+':
return a + b;
break;
case '-':
return a - b;
break;
case '*':
return a * b;
break;
case '/':
return a / b;
break;
case '^':
return pow(a, b);
break;
default:
/* TODO: Error handling */
break;
}
return 0;
}
long double AST_evaluate(AST* obj) {
return AST_eval_node_(obj->root);
}
void AST_uninit(AST* obj) {
if(obj->root)
/* uninit and deallocate the root token and its children */
ExprToken_uninit_recursive(obj->root);
}