help-bison
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Building a parse tree...


From: Steve Fernandez
Subject: Re: Building a parse tree...
Date: Sun, 19 May 2002 20:56:56 +0530

Hans Aberg wrote:

        Thank you very much for the quick reply. Well,  thought about
what you
said, but I figured out a problem. How will you know which branch to
attach
the resulting new branch, and whether it will be the left/right child of
the
parent? I don't know if the above question is valid or not, but still I
couldn't figure out an answer. Well, I did create a parse tree
successfully,
and I did it using the postfix expression generated by the parser built
by
bison.

        I found that if i return the number itself from the rule
specifying
the identifier in expr, as well as all the operations ( +,- etc ) when
two
numbers are reduced using them, this results in the postfix expression
of the
given expression. Now using this, I was able to build a parse tree and
traverse it successfully using in, pre and post orders. I have attached
the
code herewith.

        BTW, I don't know if this is the right place to say this, but
I'm in
the process of building a C compiler for myself.....a Herculerian task,
no
doubt, but I'm halfway through the lexical analyser part, and it is
faster
than the lex generated by lex.........and I can make several
optimizations to
it. Anyone interested in working together? Please do reply.

       Thanks once more for all the help.

Regards,
Steve Fernandez.


> Reply-to: address@hidden
>
> At 09:10 +0530 2002/05/18, Steve Fernandez wrote:
> >        I'm really sorry if this isn't the place to ask my question, but
> >I found no other. I am a novice as far as bison is concerned. I wrote a
> >desktop calculator, a common exercise, to compute the value of an
> >arithmetic expression. The result was correct. But I need to construct a
> >parse tree for the expression. Now I discovered that I could get the
> >postfix form of the input expression from the parser, and is there some
> >way to build a parse tree using this? Or is there an easier way?
>
> You need to build the parse tree by putting into the grammar suitable
> actions that constructs the parse tree during runtime.
>
> I have not needed to do this in practise myself, but here is a suggestion:
>
> If your original calculator grammar has say:
>   expr: expr '+' expr { $$.value = $1.value + $3.value }
>     | expr '-' expr { $$.value = $1.value - $3.value }
>     | expr '*' expr { $$.value = $1.value * $3.value }
>     | expr '/' expr { $$.value = $1.value / $3.value }
>     | expr '^' expr { $$.value = pow($1.value, $3.value) }
>     | '(' expr ')'  { $$.value = $2.value }
>     | '-' expr  %prec UMINUS { $$.value = -$2.value }
>     | IDENTIFIER expr { $$.value = sqrt($2.value) }
>     | ELEMENTARY_FUNCTION expr  { $$.value = (*$1.f)($2.value) }
>     | NUMBER
>     ;
>
> Then you may for example add (and it is easier to do this under C++ than
> under C) to the YYSTYPE a field "branch" that allows you to add nodes say
> by:
>
> expr: expr '+' expr { $$.branch = node("expr", "expr", "+", "expr"); }
>   | expr '-' expr { $$.branch = node("expr", "expr", "-", "expr"); }
>   ...
>
> Here the function node() would create a node of the parse tree with first
> argument the grammar rule LHS. This way you need to write it out all for
> every rule, so one may try to make some kind of workaround.
>
> The best way would probably be if one defined a macro like
>   #define rule_3(x, x1, x2, x3) x: x1 x2 x3 { $$.branch = node(x, x1, x2,
> x3); }
> and used that to pass the grammar part through the C preprocessor before
> passing  the output to Bison. Then your rule would look like:
>   rule_3(expr, expr, '+', expr);
>   rule_3(expr, expr, '-', expr);
>   ...
>
> Unfortunately, Bison is not hooked up like that; the C preprocessor is only
> invoked by the (C/C++) compiler processing the Bison output. Therefore one
> might try a dynamic (but somewhat runtime slower) solution: Make sure
> YYSTYPE is given field "name", with the idea that the code should look like
>   expr: expr '+' expr { $$.name = "expr";
>      $$.branch = node($$.name, $1.name, $2.name, $3.name); }
>   | expr '-' expr { $$.name = "expr";
>     $$.branch = node($$.name, $1.name, $2.name, $3.name) }
>   ...
>
> As you see, all the rule actions now become the same, except for the rule
> length and the LHS name. But the rule length is dynamically available
> during parsing, in bison.simple, it is called yylen. And the end of the
> stack (probably corresponding to $n) is called yyvsp. So you should be able
> to write a function
>   node($$.name, yyvsp)
> that constructs the parser tree node. (Be aware, though that when you
> change skeleton file, names like yyvsp and yylen may change.)
>
> If you the define a macro
>   #define NODE(x)  $$.name = #x; node($$.name, yyvsp)
> your grammar code might look something like:
>   expr: expr '+' expr          { NODE(expr); }
>     | expr '-' expr            { NODE(expr); }
>     ...
>     | '(' expr ')'             { NODE(expr); }
>     | '-' expr  %prec UMINUS   { NODE(expr); }
>     | IDENTIFIER expr          { NODE(expr); }
>     | ELEMENTARY_FUNCTION expr { NODE(expr); }
>     | NUMBER                   { NODE(expr); }
>     ;
>
> That is, all actions now look the same, which should be fairly easy, even
> if they must be written in by hand.
>
>   Hans Aberg
>
> _______________________________________________
> address@hidden http://mail.gnu.org/mailman/listinfo/help-bison
%{ 
        
        #include <ctype.h>
        #include <stdio.h>      
        #include <stdlib.h>
        #include <math.h>
        #define YYSTYPE double
        #define YYDEBUG 1

        #define OPERAND 0
        #define OPERATOR 1
        
        double power ( double, double );
        void yyerror ( char * );
        int yylex ( void );
        void add_to_parse_tree ( int, double );
        void print_parse_tree ( void );
        
                
        struct node
        {
                char value[25];
                struct node *left;
                struct node *right;
        };
        
        void do_preorder_traversal ( struct node * );
        void do_inorder_traversal ( struct node * );
        void do_postorder_traversal ( struct node * );
                
        struct node *term1 = NULL, *term2 = NULL;
        struct node **stack;
        int stack_pointer = -1;
    
%}

%token NUMBER
%left '+' '-'
%left '*' '/'
%left UMINUS
%right '^'

%%

lines:          lines expr '\n' {       
                                        printf ( "Result : %.7f\n\nParse tree 
traversals...\n", $2 ); 
                                        print_parse_tree ( ); 
                                        stack_pointer = -1; 
                                        printf ( "\nExpression : ");
                                }
        |       lines '\n'      { printf ( "Expression : " ); }
        |                       { printf ( "Expression : " ); }
;

expr:           NUMBER { add_to_parse_tree ( OPERAND, $1); }
        |       expr '+' expr { $$ = $1 + $3; add_to_parse_tree ( OPERATOR, 1 
); }
        |       expr '-' expr { $$ = $1 - $3; add_to_parse_tree ( OPERATOR, 2 
); }
        |       expr '*' expr { $$ = $1 * $3; add_to_parse_tree ( OPERATOR, 3 
); }
        |       expr '/' expr { $$ = $1 / $3; add_to_parse_tree ( OPERATOR, 4 
); }
        |       '-' expr %prec UMINUS { $$ = -$2; add_to_parse_tree ( OPERATOR, 
-1 ); }
        |       expr '^' expr { $$ = power ( $1, $3); add_to_parse_tree ( 
OPERATOR, 5 ); }
        |       '(' expr ')' { $$ = $2; }
;

%%

void yyerror ( char *error )
{

        char dummy[1000], temp;
        
        printf ( "\n!!!%s.\n", error );
        printf ( "Attempting to recover..." );
        
        
        scanf ( "%s", dummy );
        temp = getchar ( );
        
        fflush ( stdin );
        fflush ( stdout );
        
        printf ( "done.\n\n" );
        yyparse ( );    
        
}

int yylex ( void )
{

        int c;          

        while ( ( c = getchar ( ) ) == ' ' )
                ;

        if ( ( c == '.' ) || ( isdigit ( c ) ) )
        {
                ungetc(c,stdin);
                scanf("%lf",&yylval);
                return NUMBER;
        }
    
        if ( c == EOF )
        {
                printf ( "\n\nBye!\n\n" );
                return ( 0 );
        }
        
        return ( c );
    
}       

int main ( void )
{

        stack = malloc ( sizeof ( struct node ) * 100 );
        
        if ( stack == NULL )
        {
                printf ( "!!! Unable to allocate memory for stack.\n" );
                exit ( 1 );
        }
                
        printf ( "\nDesktop calculator.\n\( Press Control-D to exit. )\n\n" );
        
        yyparse ( );

        return ( 0 );
        
}

double power ( double x, double y )
{

        double i=y;
        y = 1;
        
        for ( ; i>0 ; i-- )
                y *= x;    
                
        return ( y );
        
}       

void add_to_parse_tree ( int type, double value )
{

        struct node *tempnode, *newnode;
        double operand1, operand2, result;
        
        switch ( type )
        {
                case OPERAND:
                        
                        tempnode = malloc ( sizeof ( struct node ) );
                        
                        if ( tempnode == NULL )
                        {
                                printf ( "\n!!! Out of memory.\n" );
                                exit ( 1 );
                        }
                        
                        stack_pointer++;
                        
                        if ( stack_pointer == 100 )
                        {
                                printf ( "\n!!! Stack overflow.\n" );
                                exit ( 1 );
                        }
                        
                        sprintf ( tempnode->value, "%.2f", value );
                        stack[stack_pointer] = tempnode;
                        
                        if ( term1 == NULL )
                        {
                                term1 = tempnode;
                        }
                        else if ( term2 == NULL )
                        {
                                term2 = tempnode;
                        }
                        else
                        {
                                term1 = term2;
                                term2 = tempnode;
                        }
                        
                        break;

                case OPERATOR:
                        
                        if ( value == -1 )
                        {
                                if ( stack_pointer < 0 )
                                {
                                        stack_pointer = -1;
                                        yyerror ( "Invalid expression" );
                                }
                                
                                sscanf ( stack[stack_pointer]->value, "%lf", 
&operand1 );
                                operand1 *= -1;
                                sprintf ( stack[stack_pointer]->value, "%.2f", 
operand1 );
                                
                                break;
                        }
                        
                        if ( stack_pointer < 1 )
                        {
                                
                                stack_pointer = -1;
                                yyerror ( "Invalid expression." );
                                
                        }
                        
                        tempnode = malloc ( sizeof ( struct node ) );
                        newnode = malloc ( sizeof ( struct node ) );
                        
                        if ( tempnode == NULL || newnode == NULL )
                        {
                                printf ( "\n!!!Out of memory.\n" );
                                exit ( 1 );
                        }
                        
                        sscanf ( term1->value, "%lf", &operand1 );
                        sscanf ( term2->value, "%lf", &operand2 );
                        
                        if ( term1->left == NULL )
                                tempnode->left = term1;
                        else
                                tempnode->left = term1->left;
                                
                        if ( term2->left == NULL )
                                tempnode->right = term2;
                        else
                                tempnode->right = term2->left;
                                                        
                        stack_pointer -= 2;
                        
                        switch ( (int)value )
                        {
                                case 1:
                                        sprintf ( tempnode->value, "+" );
                                        result = operand1 + operand2;
                                        break;
                                        
                                case 2:
                                        sprintf ( tempnode->value, "-" );
                                        result = operand1 - operand2;
                                        break;
                                        
                                case 3:
                                        sprintf ( tempnode->value, "*" );
                                        result = operand1 * operand2;
                                        break;
                                
                                case 4:
                                        sprintf ( tempnode->value, "/" );
                                        result = operand1 / operand2;
                                        break;
                                
                                case 5:
                                        sprintf ( tempnode->value, "^" );
                                        result = power ( operand1, operand2 );
                                        break;
                                
                        }
                        
                        sprintf ( newnode->value, "%.2f", result );
                        newnode->left = tempnode;
                        
                        stack_pointer++;
                        
                        if ( stack_pointer == 100 )
                        {
                                printf ( "\n!!! Stack overflow.\n" );
                                exit ( 1 );
                        }
                        
                        stack[stack_pointer] = newnode;
                        
                        term2 = newnode;
                        
                        if ( ( stack_pointer-1) > -2 )
                                term1 = stack[stack_pointer-1];
                        
                        break;
                        
        }
        
}

void print_parse_tree ( void )
{
        struct node *traversenode;
        
        traversenode = stack[0];
        
        printf ( "\tPreorder : ");
        do_preorder_traversal ( traversenode );
        
        printf ( "\n\tInorder : " );
        do_inorder_traversal ( traversenode );
        
        printf ( "\n\tPostorder : " );
        do_postorder_traversal ( traversenode );
        
        printf ( "\n" );
}

void do_preorder_traversal ( struct node *start )
{

        struct node *traversenode = start;
        
        if ( traversenode == NULL )
        {
                printf ( "Empty expression.\n" );
                return;
        }
        
        if ( traversenode->left != NULL )
                do_preorder_traversal ( traversenode->left );
        
        if ( traversenode->right != NULL )
                do_preorder_traversal ( traversenode->right );
        
        if ( traversenode != stack[0] ) 
                printf ( "%s ", traversenode->value );
        
}

void do_inorder_traversal ( struct node *start )
{

        struct node *traversenode = start;
        
        if ( traversenode == NULL )
        {
                printf ( "Empty expression.\n" );
                return;
        }
        
        if ( traversenode->left != NULL )
                do_inorder_traversal ( traversenode->left );
        
        if ( traversenode != stack[0] )
                printf ( "%s ", traversenode->value );
        
        if ( traversenode->right != NULL )
                do_inorder_traversal ( traversenode->right );
                
}

void do_postorder_traversal ( struct node *start )
{

        struct node *traversenode = start;
        
        if ( traversenode == NULL )
        {
                printf ( "Empty expression.\n" );
                return;
        }
        
        if ( traversenode != stack[0] )
                printf ( "%s ", traversenode->value );
        
        if ( traversenode->left != NULL )
                do_postorder_traversal ( traversenode->left );
        
        if ( traversenode->right != NULL )
                do_postorder_traversal ( traversenode->right );
        
}

Attachment: franline.vcf
Description: Card for Steve Fernandez

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]