/* parser.c
* geg, a GTK+ Equation Grapher
* David Bryant 1998
*/
/*
* Corrected grammar
* Kris Jurka 27/7/1999
*/
/*
* Corrected more grammar
* Torsten Landschoff 24/11/1999
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */
#include <math.h>
#include "parser.h"
#include "tokeniser.h"
#include "log.h"
/*
*
* EBNF:
* <expr> ::= +|- <term> { +|- <term> }
* <term> ::= <product> { *|/ <product> }
* <product> ::= <factor> { ^ <factor> }
* <factor> ::= <number> | <function> | (<expr>)
* <number> ::= <float> | x
* <function> ::= <function_name>(<expression>)
*/
static parse_tree *expr(void);
static parse_tree *term(void);
static parse_tree *factor(void);
static parse_tree *product(void);
static token_list *list_g; /* global list, makes life easier */
static gint parse_error; /* flag this when a parse error occurs */
/* some convenience functions follow, these stop seg faults and other stuff,
* when the user enters un-parsable equations.
* There is probably a fair bit of redundancy in the NULL pointer checking
* but leave it there for now.
*/
static gint
token_type_is(gint type)
{
if(!list_g)
return(FALSE);
return(list_g->type == type);
}
static gint
token_type(void)
{
if(!list_g)
return(EOE);
return(list_g->type);
}
static gdouble
token_value(void)
{
if(!list_g)
return(0);
return(list_g->value);
}
static void
get_next_token(void)
{
if(list_g)
list_g = list_g->next;
}
static void
skip_token(gint type)
{
if(!list_g) {
parse_error = TRUE;
return;
}
else
if(list_g->type != type)
parse_error = TRUE;
list_g = list_g->next;
}
/* end of convenience functions
*/
/* expr, starting non-terminal for generating parse tree
*/
static parse_tree *
expr(void)
{
parse_tree *tree = NULL, *temp_tree;
if(token_type_is(NUM) ||
token_type_is(X) ||
token_type_is(FUN) ||
token_type_is(SUB) || /* negate, (not minus) */
token_type_is(LB) ||
token_type_is(LCB) ||
token_type_is(LSB)) {
tree = term();
}
else {
parse_error = TRUE;
return(NULL);
}
while(token_type_is(ADD) ||
token_type_is(SUB)) {
temp_tree = tree; /* save the old tree */
tree = g_new(parse_tree, 1);
tree->token.type = token_type();
tree->left = temp_tree; /* push old tree down a 'left branch' */
get_next_token();
tree->right = term(); /* 'right branch' */
}
if((tree == NULL) || (token_type_is(ERROR)))
parse_error = TRUE;
return(tree);
}
/* term, non-terminal
*/
static parse_tree *
term(void)
{
parse_tree *tree = NULL, *temp_tree;
if(token_type_is(NUM) ||
token_type_is(X) ||
token_type_is(FUN) ||
token_type_is(SUB) || /* negate, (not minus) */
token_type_is(LB) ||
token_type_is(LCB) ||
token_type_is(LSB)) {
if(token_type_is(SUB)) { /* negate */
tree = g_new(parse_tree, 1);
tree->token.type = SUB; /* token_type(); */
tree->left = g_new(parse_tree, 1);
tree->left->token.type = NUM;
tree->left->token.value = 0;
tree->left->left = NULL;
tree->left->right = NULL;
get_next_token(); /* skip the minus-sign */
tree->right = product();
}
else {
tree = product();
}
}
else {
parse_error = TRUE;
return(NULL);
}
while(token_type_is(MUL) ||
token_type_is(DIV) ||
token_type_is(X) ||
token_type_is(FUN) ||
token_type_is(LB) ||
token_type_is(LCB) ||
token_type_is(LSB)) {
if(token_type_is(X) || /* assume multiplication */
token_type_is(FUN) ||
token_type_is(LB) ||
token_type_is(LCB) ||
token_type_is(LSB)) {
temp_tree = tree;
tree = g_new(parse_tree, 1);
tree->token.type = MUL;
tree->left = temp_tree;
tree->right = term();
}
else {
temp_tree = tree;
tree = g_new(parse_tree, 1);
tree->token.type = token_type();
tree->left = temp_tree;
get_next_token();
tree->right = term();
}
}
if(tree == NULL)
parse_error = TRUE;
return(tree);
}
/* product, non-terminal
*/
static parse_tree *
product(void)
{
parse_tree *tree = NULL, *temp_tree;
if(token_type_is(NUM) ||
token_type_is(X) ||
token_type_is(FUN) ||
token_type_is(LB) ||
token_type_is(LCB) ||
token_type_is(LSB)) {
tree = factor();
}
else {
parse_error = TRUE;
return(NULL);
}
while(token_type_is(POW)) {
temp_tree = tree;
tree = g_new(parse_tree, 1);
tree->token.type = token_type();
tree->left = temp_tree;
get_next_token();
tree->right = product();
}
if(tree == NULL)
parse_error = TRUE;
return(tree);
}
/* factor, non-terminal
* negation is a bit of a hack, if we get a negator, we create a left tree,
* with number=0, and a negation node, and continue down the right tree....
* ie, we subtract the following factor from 0
*/
static parse_tree *
factor(void)
{
parse_tree *tree;
if(token_type_is(NUM) ||
token_type_is(X)) {
tree = g_new(parse_tree, 1);
tree->left = NULL;
tree->right = NULL;
tree->token.type = token_type();
tree->token.value = token_value();
get_next_token();
return(tree);
}
else if(token_type_is(FUN)) {
tree = g_new(parse_tree, 1);
tree->right = NULL;
tree->token.type = token_type();
tree->token.func = list_g->func;
skip_token(FUN);
if(token_type_is(LB)) {
skip_token(LB);
tree->left = expr();
skip_token(RB);
}
else if(token_type_is(LSB)) {
skip_token(LSB);
tree->left = expr();
skip_token(RSB);
}
else {
skip_token(LCB);
tree->left = expr();
skip_token(RCB);
}
return(tree);
}
else if(token_type_is(LB)) {
skip_token(LB);
tree = expr();
skip_token(RB);
return(tree);
}
else if(token_type_is(LSB)) {
skip_token(LSB);
tree = expr();
skip_token(RSB);
return(tree);
}
else if(token_type_is(LCB)) {
skip_token(LCB);
tree = expr();
skip_token(RCB);
return(tree);
}
else {
parse_error = TRUE;
return(NULL);
}
}
/* make_parse_tree, creates a parse tree from a list of tokens
*/
parse_tree *
make_parse_tree(token_list *list)
{
parse_tree *tree = NULL;
parse_error = FALSE;
list_g = list; /* assign global list */
tree = expr();
if((parse_error)||(!token_type_is(EOE)))
return(NULL);
return(tree);
}
/* free_tree, frees the memory allocated to the tree using a recursive
* algorithm
*/
void
free_tree(parse_tree *tree)
{
if(tree->left)
free_tree(tree->left);
if(tree->right)
free_tree(tree->right);
g_free(tree);
return;
}
/* eval_tree, this function recursively evaluates the parse tree using
* LRN notation. Currently only unary functions are supported, where the
* argument to the function is assumed to be the left child branch of the
* function node.... in the future binary functions might exist, like mod,
* where the arguments are the children branches from left to right
*/
double
eval_tree(parse_tree *tree, double x)
{
switch(tree->token.type) {
case ADD:
return(eval_tree(tree->left, x) + eval_tree(tree->right, x));
case SUB:
return(eval_tree(tree->left, x) - eval_tree(tree->right, x));
case MUL:
return(eval_tree(tree->left, x) * eval_tree(tree->right, x));
case DIV:
return(eval_tree(tree->left, x) / eval_tree(tree->right, x));
case POW:
return(pow(eval_tree(tree->left, x), eval_tree(tree->right, x)));
case FUN:
return(tree->token.func(eval_tree(tree->left, x)));
case NUM:
return(tree->token.value);
case X:
return(x);
default:
write_log(NULL, "Eval Error");
return(0);
}
}
syntax highlighted by Code2HTML, v. 0.9.1