0

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
Nikul Vyas
  • 363
  • 3
  • 7
  • 30

3 Answers3

1

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.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • but still after putting $2 parser is giving output of next valid expression =0 – Nikul Vyas Feb 13 '17 at 21:51
  • 1
    @NikulVyas you don't check errors, you don't handle operator precedence and I think your handling of the unary minus is one of the causes for the 22 shift/reduce conflicts. (this comment should have enough keywords for Google to find solutions) – deamentiaemundi Feb 13 '17 at 21:58
  • @deamentiaemundi my idea bottom to top approach. That's why I am passing + & - before * & / – Nikul Vyas Feb 13 '17 at 22:16
  • That's lucky because *yacc* generates a bottom-up parser, but it doesn't relax the requirement for operator precedence, or make your `- Expr` rule correct. The order of the productions for the same terminal symbol doesn't define the precedence. You have to do that yourself, either with `%prec` directives or the common form of the expression grammar, with separate rules for `term`, `factor`, and `prime`. – user207421 Feb 13 '17 at 22:18
  • @EJP your grammar is helping but can you explain why parser stops scanning when error occcurs, I haven't used exit statement in yyerror function. – Nikul Vyas Feb 13 '17 at 23:05
  • Because you don't have any `error` productions, so it shifts `error` forever. Try `| error` as an extra rule at the top for `Calc`. – user207421 Feb 13 '17 at 23:27
  • 1
    @EJP Thank you so much for the solution. I am grateful. – Nikul Vyas Feb 13 '17 at 23:38
1

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.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • Can you suggest me any way to display the arithmetic expression of text file before the output result. Like : 9/3-2*(-5)=13 – Nikul Vyas Feb 14 '17 at 02:11
0

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.

Nikul Vyas
  • 363
  • 3
  • 7
  • 30