简体   繁体   中英

Printing Abstract Syntax Tree, infinite recursion issue

For the final project in my C programming class, we are implementing a reverse polish notation calculator which can either evaluate an expression for correctness, return the corresponding infix expression, or print out mock assembly code. To do so, we are to implement both a stack and a Abstract Syntax Tree.

struct snode /*stack data structure */
{
  int datum;
  struct snode* bottom;
};

struct tnode /*tree data structure */
{
  int datum;
  struct tnode* left;
  struct tnode* right;
};

Currently, I've designed my program to read from stdin and put the elements onto a stack, then to use that stack to create a Abstract Syntax Tree which can later be used to evaluate the expression. For right now, I haven't put in any checks yet, I'm just trying to build a AST first.

Below is the majority of my main function. For right now, there are no checks to ensure that the given equation is correct.

int i = 1;


struct snode *stack;
stack = push(stack, -999); /* Here I set what will be the "empty" value of my stack. There's better ways to do it, but this is how I did it for a previous lab and it tended to cause less issues for me when I check if my stack is empty at the end */

struct tnode *AST;
AST = (struct tnode*)malloc(sizeof(struct tnode));
AST->datum = -7062; /*same thing as with stack, this is just a place holder that will be over written. */
AST->right = NULL;
AST -> left = NULL;

struct tnode* tmp;
tmp = (struct tnode*)malloc(sizeof(struct tnode));
tmp -> datum = -7062;
tmp -> right = NULL;
tmp -> left = NULL;

/*Operators are given as letters instead of +,-, etc. */
char *addition = "A"
char *subtraction = "S";
char *multiplication = "X";
char *division = "D"
char *mod = "M";

while(argv[i] !=NULL){
    if(isdigit(unsignedchar)argv[i][0])){
      stack = push(stack, atol(argv[i]));
   }else{ /* In the final code, a strcmp will check to see if argv[i] is one of the above A,S,X,D, or M arguments. For right now, it just fires if it's not a digit for my testing. */
       if(AST->datum == -7062){ /*if this is the first time we're running it*/
         AST->datum = atol(argv[i]);
         AST->right = create_node(stack->datum);
         stack = pop(stack);
         AST -> left = create_node(stack->datum);
         stack = pop(stack); /* I pop the top off the stack twice to get the 2 integers it stores. I know it will for the current testing, checks will be in place later */
       }else{ /* if AST already has elements in it. */
         tmp = AST;
         tmp -> left = tmp-> right = NULL;
         AST->datum = atol(argv[i]);
         AST->right = create_node(stack->datum);
         stack = pop(stack);
         AST->left = tmp; /*This puts the new operator as the root of the tree, the old tree as the left child of that root, and the right child as the integer stored on stack.*/
         }
     }
  i++;
  }

print_table(AST); /*Should print the AST */
}

create_node allocates space and stores what's given to it.

struct tnode*
create_node(int x){
  struct tnode* tmp;
  tmp = (struct tnode*)malloc(sizeof(struct tnode))
  tmp->datum = x;
  tmp->right = NULL;
  tmp->left = NULL;
  return tmp;
}

print_table recursively prints the Abstract Syntax Tree.

void
print_table(struct tnode *AST){
    if(AST !=NULL){
        print_table(AST->left);
        printf("%d ", AST->datum);
        print_table(AST->right);
      }
  }

Now currently, if the following is given: /rpcalc 5 4 A Then the program will return 5 0 4. I understand why the A is returning as a 0, so this part is working as intended. However, when I try to give the program /rpcalc 5 4 A 3 X, which is (5+4)*3, it freezes for a moment and then returns a Segmentation Fault.

Using several printf statements, I have isolated the issue down to the recursion in the print_table function. For some reason, the AST->left does not appear to be reaching the NULL pointer to terminate the program, causing it to run infinitely until the program crashes. I'm not sure what's causing this, and unfortunately until I fix it I can't really continue any farther with my project..

Let's start right in the middle:

       }else{ /* if AST already has elements in it. */
         tmp = AST;
         tmp -> left = tmp-> right = NULL;
         AST->datum = atol(argv[i]);
         AST->right = create_node(stack->datum);
         stack = pop(stack);
         AST->left = tmp; /*This puts the new operator as the root of the tree, the old tree as the left child of that root, and the right child as the integer stored on stack.*/
         }
     }
  i++;
  }

This is bad. You set tmp = AST , then tmp->left and tmp->right to NULL , but that also sets AST->left and AST->right to NULL , since they point to the same thing! Later on, you set AST->left = tmp , which creates a cycle, since again, AST and tmp point to the same thing, so it is equivalent to AST->left = AST . Recursing into AST->left is therefore infinite descent, AKA stack overflow .

While the reason for the infinite recursion of your code is already given by EOF's answer, taking a look at the code I couldn't resist to tell you, that at least to me it seems, maybe you should rethink your algorithm too.

Most probably it is not numbers ( int ) you want to keep on the stack, but AST-fragments. Imagine expressions like 1 2 + 3 4 + * or even deeper nested ones. Changing struct snode to:

struct snode /*stack data structure */
{
    struct tnode* datum; /* we want to store AST nodes here !! */
    struct snode* bottom;
};

Your code might look something like:

int main(int argc, char* argv[]) {
    int i = 1;

    struct snode *stack = NULL;
    struct tnode *AST = NULL;

    while(argv[i] !=NULL){

        /* isdigit takes argument of type char! don't typecast here */
        if(isdigit(argv[i][0])){ 
            stack = push(stack, create_node(atol(argv[i])));
        } 
        else {
            /* original code using atol(argv[i]), always returning 0 here */
            AST = create_node(0); 
            AST->left = stack->datum;
            stack = pop(stack);
            AST->right = stack->datum;
            stack = pop (stack);
            stack = push(stack, AST);
        }
        i++;
    }

    if (stack != NULL) {
        AST = stack->datum;
        print_table(AST); /*Should print the AST */
    }

    return 0;
}

Input 5 4 A 3 X will produce 3 0 4 0 5 as expected, so there should still be enough for you to work upon.

Good luck on that mission.

To be sure, we are talking about the same facts here. For push() and pop() , not given in your code fragments, I used the following code.

struct snode* push(struct snode* stack, struct tnode* datum) {
    struct snode *S = (struct snode*)malloc(sizeof(struct snode));
    S->datum = datum;
    S->bottom = stack;
    return S;
}

struct snode* pop(struct snode* stack) {
    struct snode *S;
    if (stack == NULL)
        return NULL;
    S = stack->bottom;
    free(stack);
    return S;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM