Nikul Vyas
Nikul Vyas

Reputation: 363

Yacc not parsing second expression in txt file

I am trying to parse the set of arithmetic expressions from a .txt file. (one in each line.) I am able to get the proper logic for first line but the parser gives 0 for second expression. Moreover, I want to print the whole string in the output but getting confused from where to start.

Lex

%{
#include <stdio.h>
#include "y.tab.h"  
int yylval; /*declared extern by yacc code. used to pass info to yacc*/  
%}

letter  [A-Za-z]
digit   ([0-9])*
op      "+"|"*"|"("|")"|"/"|"-"
ws      [ \t\n\r]+$
other   .

%%

{ws}    {  /*Nothing*/ }
{digit} {  yylval = atoi(yytext); return NUM;}
{op}    {  return yytext[0];}
{other} {  printf("bad %c bad %d \n",*yytext,*yytext); return  '?'; }

%%

Yacc

%{
#include <stdio.h>
#include <string.h>
#define YYSTYPE int    /* the attribute type for Yacc's stack */ 
extern int yylval;     /* defined by lex, holds attrib of cur token */
extern char yytext[]; /* defined by lex and holds most recent token */
extern FILE * yyin;    /* defined by lex; lex reads from this file   */
%}

%token  NUM 

%%
Calc  : Expr           {printf(" = %d\n",$1);}
  | Calc Expr          {printf(" = %d\n",$1);}
  ;
Expr  : Expr '+' Expr  { $$ = $1 + $3;    }
  | Expr '-' Expr      { $$ = $1 - $3;    }
  | Expr '*' Expr      { $$ = $1 * $3;    }
  | Expr '/' Expr      { if($3==0) 
                          yyerror("Divide by Zero Encountered.");
                         else
                          $$ = $1 / $3;    
                       }
  | '-' Expr           { $$ = -$2;       }
  | Fact               { $$=$1;          }
  ;      
Fact  : '(' Expr ')'   { $$ = $2;        }
  | Id                 { $$ = $1;        }
  ;
Id    : NUM            { $$ = yylval;    }
  ;

%%

void yyerror(char *mesg); /* this one is required by YACC */

main(int argc, char* *argv){
char ch,c;
FILE *f;    
if(argc != 2) {printf("useage:  calc filename \n"); exit(1);}
if( !(yyin = fopen(argv[1],"r")) ){ 
       printf("cannot open file\n");exit(1);
 }
yyparse();
}

void yyerror(char *mesg){
printf("Bad Expression : %s\n", mesg);
}

Text File

4+3-2*(-7)
65*+/abc
9/3-2*(-5)

Output

=21
Bad Expression : syntax error

Expected Output

4+3-2*(-7)=21
65*+/abc=Bad Expression : syntax error
9/3-2*(-5)=13

Even if I remove the bad expression from 2nd line int the text file the parser is giving result

=21 
=0 

instead of

=21    
=13

I tried to read data and store it in variable & display arithmetic expression using file handling option in while loop and using yyparse() in the loop to scan line by line. I am not able to track down the problem as the source code is bit complex and I have started studying this thing from just 20 days.

Running Code By using Commands

yacc -v -t -d calc.yacc (I am getting 22 shift/reduce conflicts.)
lex calc.lex
gcc y.tab.c lex.yy.c -lm -ll -o calc
./calc calc.txt

Upvotes: 0

Views: 1359

Answers (3)

Chris Dodd
Chris Dodd

Reputation: 126488

The problem is that if you don't specify precedence with precedence rules (or by rewriting the grammar, as others have suggested), yacc resolves shift-reduce conflicts by shift, which means it makes such mutually recursive rules right-associative. So the expression

9/3-2*(-5)

is parsed as

9/(3-(2*(-5)))

with integer arithmetic, that final divide ends up being 9/13, which is 0.

Upvotes: 1

Nikul Vyas
Nikul Vyas

Reputation: 363

The code works fine with improvements only YACC was needed to be edited.

%{
#include <stdio.h>
#include <string.h>
#define YYSTYPE int    /* the attribute type for Yacc's stack */ 
extern int yylval;     /* defined by lex, holds attrib of cur token */
extern char yytext[]; /* defined by lex and holds most recent token */
extern FILE * yyin;    /* defined by lex; lex reads from this file   */
%}

%token  NUM

%%

Calc  : Expr               {printf(" = %d\n",$1);} 
  | Calc Expr          {printf(" = %d\n",$2);}
  | Calc error         {yyerror("\n");}
  ;
Expr  : Term               { $$ = $1;         }
  | Expr '+' Term      { $$ = $1 + $3;    }
  | Expr '-' Term      { $$ = $1 - $3;    }
  ;
Term  : Fact               { $$ = $1;         }
  | Term '*' Fact      { $$ = $1 * $3;    }
  | Term '/' Fact      { if($3==0){ 
                yyerror("Divide by Zero Encountered.");
                            break;}
               else
                $$ = $1 / $3;    
                   }
  ;
Fact  : Prim               { $$ = $1;        }
  | '-' Prim           { $$ = -$2;       }
  ;      
Prim  : '(' Expr ')'       { $$ = $2;        }
  | Id                 { $$ = $1;        }
  ;
Id    :NUM                 { $$ = yylval;    }
  ;
%%

void yyerror(char *mesg); /* this one is required by YACC */

main(int argc, char* *argv){
char ch,c;
FILE *f;    
if(argc != 2) {printf("useage:  calc filename \n"); exit(1);}
if( !(yyin = fopen(argv[1],"r")) ){ 
       printf("cannot open file\n");exit(1);
 }
/*  
 f=fopen(argv[1],"r");
 if(f!=NULL){
char line[1000];
while(fgets(line,sizeof(line),f)!=NULL)
    {
            fprintf(stdout,"%s",line);
        yyparse();
    }

}
*/
yyparse();
}

void yyerror(char *mesg){
printf("\n%s", mesg);
}

I tried to print the expression but it is not possible with reading file line by line & calling yyparse() in the while loop. But the calculations & grammar is working fine.

Upvotes: 0

user207421
user207421

Reputation: 311039

Yacc not parsing second expression

Yes it is. That's where the syntax error comes from. If it wasn't parsing it, it couldn't give a syntax error for it.

Expected output

There is nothing here that prints the input expression, so there is no reason for this expectation.

There is also no error recovery, so no possibility of performing the reduction that prints = if there is a syntax error.

The rule for Calc : Calc Expr should print $2, not $1.

The rule

| '-' Expr           { $$ = -$2;       }

should read

| '-' Fact           { $$ = -$2;       }

And finally you need to do something about operator precedence. I'm wondering where you got this bizarre expression grammar from. There are plenty of correct examples. Something like this:

expression
    : term
    | expression '+' term
    | expression '-' term
    ;
term
    : factor
    | term '*' factor
    | term '/' factor
    ;
factor
    : primary
    | '-' primary
    ;
primary
    : ID
    | NUM
    | '(' expression ')'
    ;

Errors and omissions excepted.

Upvotes: 1

Related Questions