I'm coding a Bison-Flex calculator following the Bison documentation example. When I try to assign a value to a variable it works properly when the value doesn't include other variables. For example, the calculator assigns to the variable a the correct value after this operation: a = 2 + 3 + 5
. However, if then I try to assign the value of a to another variable b, like b = a + 1
, the calculator stores the value of a+1 in the variable a. If I try to print the name of $1 in my assignment rule in the Bison code, I get that in last example, the program is getting the variable a as $1, not b. I also tried enabling the debug option and it looks like Bison is applying the rules and the reductions properly.
The Bison file, calculadora_bison.y:
%{
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "TablaSimbolos.h"
int yylex (void);
int yyerror (char *s);
%}
%union {
float valor;
NodoArbol * tptr;
};
%token <valor> NUM
%token <tptr> VAR FNCT
%type <valor> exp
%right '='
%left '-' '+'
%left '*' '/'
%left NEG
%right '^'
%%
input: /* vac´ıo */
| input line
;
line: '\n'
| exp '\n' { printf ("\t%.10g\n", $1); }
| exp ';' '\n' { }
| error '\n' { yyerrok; }
;
exp: NUM { $$ = $1;}
| VAR { $$ = $1->valor.val;}
| VAR '=' exp { $$ = $3; ActualizaNodoFloat($1->nombre, $3);}
| FNCT '(' exp ')' { $$ = (*($1->valor.fnctptr))($3);}
| exp '+' exp { $$ = $1 + $3; }
| exp '-' exp { $$ = $1 - $3; }
| exp '*' exp { $$ = $1 * $3; }
| exp '/' exp { $$ = $1 / $3; }
| '-' exp %prec NEG { $$ = -$2; }
| exp '^' exp { $$ = pow ($1, $3); }
| '(' exp ')' { $$ = $2; }
;
%%
struct init{
char *fname;
double (*fnct)();
};
struct init arith_fncts[] =
{
{ "atan", atan },
{ "cos", cos },
{ "exp", exp },
{ "ln", log },
{ "sin", sin },
{ "sqrt", sqrt },
{ 0, 0 },
};
int init_table(){
int i;
CrearArbol();
for (i = 0; arith_fncts[i].fname != 0; i++){
InsertarNodoFuncion(FNCT, arith_fncts[i].fname, arith_fncts[i].fnct);
}
return 0;
}
int main (){
init_table ();
yyparse ();
return 0;
}
int yyerror (char *s){
printf ("%s\n", s);
return 0;
}
My Flex code, calculadora_flex.l:
%{
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "TablaSimbolos.h"
#include "calculadora_bison.tab.h"
%}
ALNUM [a-zA-Z][a-zA-Z0-9]*
DIGITO [0-9]*
FLOAT {DIGITO}*\.{DIGITO}*
OPERADORES [-+()=*^/\n;]
%%
{ALNUM} {
NodoArbol s;
s = buscaTS(yytext);
yylval.tptr = &s;
return s.tipo;
}
{FLOAT} {
yylval.valor = atof(yytext);
return NUM;
}
{DIGITO} {
yylval.valor = atoi(yytext);
return NUM;
}
{OPERADORES} {
return (int)yytext[0];
}
. { };
%%
For the symbol table, I'm using a BST in C. The header file is this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct NodoArbolType{
int tipo; //Componente léxico
char * nombre; // Lexema
union {
float val;
double (*fnctptr)();
} valor;
struct NodoArbolType *izda; //Nodos hijo
struct NodoArbolType *dcha;
}NodoArbol;
void ActualizaNodoFloat(char * nombrenodo, float valor);
void CrearArbol();
NodoArbol BuscarArbol(char * nombre);
NodoArbol buscaTS(char * nombre);
int InsertarNodoEntero(NodoArbol *newNode);
NodoArbol InsertarNodo(int Key, char * cA);
int InsertarNodoFloat(int Key, char * cA, float val);
int InsertarNodoFuncion(int Key, char * cA, double (*fnct)());
void EliminarArbol();
void ImprimirArbol90grados();
void ImprimirArbolConValores();
int EliminarNodo(char * nombre);
The .c file:
#include "TablaSimbolos.h"
#include "calculadora_bison.tab.h"
//Declaramos el nodo como estático para que solo pueda ser accedido desde aquí
static NodoArbol *raiz;
/**
* Función que limpia recursivamente a partir de un nodo
*/
static void LimpiarArbol(NodoArbol *T){
if(T==NULL) return;
if(T->izda != NULL) LimpiarArbol(T->izda); //Si sus hijos no son NULL, también
//se limpian
if(T->dcha != NULL) LimpiarArbol(T->dcha);
free(T); //Libera la memoria del puntero al nodo
return;
}
/*
* Función que crea un árbol a partir del nodo raíz
*/
void CrearArbol(){
LimpiarArbol(raiz); //Primero libera el nodo raíz por si existía un árbol previo
raiz = NULL;
return;
}
/**
* Función que determina si un árbol no tiene ningún nodo
*/
int esVacio(){
return(raiz==NULL);
}
/*
* Función que busca en un árbol un nodo a partir de un string que correspondería
* al campo nombre de los nodos
*/
NodoArbol BuscarArbol(char * nombre){
NodoArbol *temp;
temp = raiz; //Puntero auxiliar
//Mientras temp no sea NULL (haya llegado al final del árbol) o no se
//encuentre el string nombre
while((temp != NULL) && (strcmp(nombre, temp->nombre)!=0)){
if(strcmp(nombre, temp->nombre)<0) //Si el string es "menor" que el
//nombre del nodo a examinar (por ejemplo
// function<while), sigue examinando por
//el nodo hijo izquierdo
temp = temp->izda;
else if(strcmp(nombre, temp->nombre)>0) //Análogamente al caso anterior
//pero cuando sea mayor
temp = temp->dcha;
}
if(temp == NULL){ //Si el nombre no se ha encontrado, devuelve error
temp = (NodoArbol *)malloc(sizeof(NodoArbol));
temp->tipo = -1;
}
return(*temp);
}
/**
* Función que inserta un NodoArbol en el árbol
*/
int InsertarNodoEntero(NodoArbol *nuevoNodo){
NodoArbol *temp;
NodoArbol *anterior;
temp = raiz;
anterior = NULL;
//Busca la posición en la que se insertará el nodo
while((temp != NULL) && (strcmp(nuevoNodo->nombre, temp->nombre)!=0)){
anterior = temp;
if(strcmp(nuevoNodo->nombre, temp->nombre)<0)
temp = temp->izda;
else if(strcmp(nuevoNodo->nombre, temp->nombre)>0)
temp = temp->dcha;
}
//Añadimos el nodo al árbol
if(anterior == NULL) //Si anterior==NULL, el nodo es el primero en introducirse
raiz = nuevoNodo;
else {
//Si el nombre de nuevoNodo < anterior->nombre, lo introducimos a la izquierda
if(strcmp(nuevoNodo->nombre, anterior->nombre)<0)
anterior->izda = nuevoNodo;
//En el caso contrario, lo introducimos a la izquierda
else if(strcmp(nuevoNodo->nombre, anterior->nombre)>0)
anterior->dcha = nuevoNodo;
}
return(nuevoNodo->tipo);
}
/**
* Función que inserta un nodo en el árbol a partir de su valor y su nombre
*/
NodoArbol InsertarNodo(int valor, char *cA){
NodoArbol *nuevoNodo;
nuevoNodo = (NodoArbol *)malloc(sizeof(NodoArbol));
nuevoNodo->tipo = valor;
nuevoNodo->nombre = (char*)malloc(sizeof(char) * strlen(cA));
strcpy(nuevoNodo->nombre, cA);
nuevoNodo->valor.val=0;
nuevoNodo->izda = nuevoNodo->dcha = NULL;
InsertarNodoEntero(nuevoNodo);
return(*nuevoNodo);
}
int InsertarNodoFloat(int Key, char * cA, float val){
NodoArbol *nuevoNodo;
nuevoNodo = (NodoArbol *)malloc(sizeof(NodoArbol));
nuevoNodo->tipo = Key;
nuevoNodo->valor.val = val;
nuevoNodo->nombre = (char*)malloc(sizeof(char) * strlen(cA));
strcpy(nuevoNodo->nombre, cA);
nuevoNodo->izda = nuevoNodo->dcha = NULL;
return(InsertarNodoEntero(nuevoNodo));
}
int InsertarNodoFuncion(int Key, char * cA, double (*fnct)()){
NodoArbol *nuevoNodo;
nuevoNodo = (NodoArbol *)malloc(sizeof(NodoArbol));
nuevoNodo->tipo = Key;
nuevoNodo->valor.fnctptr = fnct;
nuevoNodo->nombre = (char*)malloc(sizeof(char) * strlen(cA));
strcpy(nuevoNodo->nombre, cA);
nuevoNodo->izda = nuevoNodo->dcha = NULL;
return(InsertarNodoEntero(nuevoNodo));
}
/*
* Función que busca un nodo a partir del nombre y devuelve su valor. Si no lo
* encuentra, inserta el nodo y devuelve el valor del nuevo nodo.
*/
NodoArbol buscaTS(char * nombre){
NodoArbol id;
id=BuscarArbol(nombre);
if(id.tipo==-1){
id=InsertarNodo(VAR, nombre);
}
return id;
}
void ActualizaNodoFloat(char * nombreNodo, float valor){
//printf("en actualizarNodoFloat, nombreNodo = %s \n", nombreNodo);
EliminarNodo(nombreNodo);
InsertarNodoFloat(VAR, nombreNodo, valor);
//NodoArbol nodo = BuscarArbol(nombreNodo);
//printf("en buscar dentro de actualizaNodo%f", nodo.valor.val);
}
/*
* Función que elimina un árbol desde la raíz, liberando la memoria ocupada por él
*/
void EliminarArbol(){
LimpiarArbol(raiz);
}
void ImprimirNodoConValores(NodoArbol * nodo){
if(nodo->izda!=NULL){
ImprimirNodoConValores(nodo->izda);
}
printf("%d - %s", nodo->tipo, nodo->nombre);
if(nodo->tipo==VAR){
printf(" --> %f \n", nodo->valor.val);
}else{
printf("\n");
}
if(nodo->dcha!=NULL){
ImprimirNodoConValores(nodo->dcha);
}
}
void ImprimirArbolConValores(){
ImprimirNodoConValores(raiz);
}
int EliminarNodo(char * nombre){
NodoArbol * anterior;
NodoArbol *temp;
NodoArbol *delParent; // Parent of node to delete
NodoArbol *delNode; // Node to delete
temp = raiz;
anterior = NULL;
//Encontramos el nodo a eliminar
while((temp != NULL) && (strcmp(temp->nombre, nombre)!=0)){
anterior = temp;
if(strcmp(nombre, temp->nombre)<0)
temp = temp->izda;
else if(strcmp(nombre, temp->nombre)>0)
temp = temp->dcha;
}
if(temp == NULL){ // No se encontró el nodo
printf("Key not found. Nothing deleted. El nombre es %s\n", nombre);
return 0;
}else{
if(temp == raiz){ //Eliminamos la raíz
delNode = raiz;
delParent = NULL;
}else{
delNode = temp;
delParent = anterior;
}
}
// Case 1: Deleting node with no children or one child
if(delNode->dcha == NULL){
if(delParent == NULL) // If deleting the root
{
raiz = delNode->izda;
free(delNode);
return 1;
}
else
{
if(delParent->izda == delNode)
delParent->izda = delNode->izda;
else
delParent->dcha = delNode->izda;
free(delNode);
return 1;
}
}
else // There is at least one child
{
if(delNode->izda == NULL) // Only 1 child and it is on the right
{
if(delParent == NULL) // If deleting the root
{
raiz = delNode->dcha;
free(delNode);
return 1;
}
else
{
if(delParent->izda == delNode)
delParent->izda = delNode->dcha;
else
delParent->dcha = delNode->dcha;
free(delNode);
return 1;
}
}
else // Case 2: Deleting node with two children
{
temp = delNode->izda;
anterior = delNode;
while(temp->dcha != NULL)
{
anterior = temp;
temp = temp->dcha;
}
// Copy the replacement values into the node to be deleted
delNode->tipo = temp->tipo;
strcpy(delNode->nombre, temp->nombre);
// Remove the replacement node from the tree
if(anterior == delNode)
anterior->izda = temp->izda;
else
anterior->dcha = temp->izda;
free(temp);
return 1;
}
}
}