0

Suppose we have the following simplistic YACC grammar:

start:
    list
    {
        if ($1 != NULL) {
            Reverse(&$1); /*correct order*/
        }
        Generate($1);
    }
    ;

list:
    list item
    {
        $$ = Node($2, $1);
    }
    |
    {
        $$ = NULL;
    }
    ;

Is there a way to construct the binary abstract syntax tree of list (still using left-recursion) so that the order of the elements doesn't have to be corrected in start? What is the modus operandi?

August Karlstrom
  • 10,773
  • 7
  • 38
  • 60

1 Answers1

1

Not really.

A left-recursive grammar performs reductions left-to-right. If you want to build a linked list, then you either need to reverse the list at the end, as shown in the OP, or you need to keep a pointer to the end of the list so that you can append to the list in O(1). (And you still need to discard this pointer from the parse stack when the list is fully parsed.)

Here's an example of the second strategy:

start
    : list      { if ($1) {
                    $$ = $1->next;
                    $1->next = NULL;
                  }
                }
list:           { $$ = NULL; }
    | list node { if ($1) {
                    $$ = Node($2, $1->next);
                    $1->next = $$;
                  }
                  else {
                    $$ = Node($2, NULL);
                    $$->next = $$;
                  }
                }

Here, the intermediate list is circular and the semantic value of list is its last element. In effect, we maintain the list rotated one node to the right. At the end, in start, we just need to rotate the list one node circularly to the left and break the circle. (The code is complicated by the possibility that the list is empty. If empty lists are not possible or if we are willing to allocate an extra header element and discard it at the end, the code can be simplified.)

In practice, I wouldn't use the above code because it is not easy to read and has no real performance advantage.

Of course, you could use right recursion to reduce the elements backwards, which effectively uses the parse stack to hold the intermediate list. That uses a potentially unbounded amount of stack space and the reversed processing order can have other consequences (if node processing is not functional); in any case, it is no faster than the left-to-right methods.

rici
  • 234,347
  • 28
  • 237
  • 341