0
votes

I'm trying to understand how compilers and programming languages are made. And to do so I thought about creating a simple calculator which does just addition and subtraction. Below are the Lex and Yacc files which I wrote.

calc.yacc file:

%{
  #include <stdio.h>
  #include <stdlib.h>

  extern int yylex();
  void yyerror(char *);
%}

%union { int number; }
%start line
%token <number> NUM
%type <number> expression

%%

line: expression  { printf("%d\n", $1); };

expression: expression '+' NUM  { $$ = $1 + $3; };
expression: expression '-' NUM  { $$ = $1 - $3; };
expression: NUM  { $$ = $1; };

%%

void yyerror(char *s) {
  fprintf(stderr, "%s", s);
  exit(1);
}

int main() {
  yyparse();
  return 0;
}

calc.lex file:

%{
  #include <stdio.h>
  #include <stdlib.h>
  #include "y.tab.h"
%}

%%

[0-9]+ {
  yylval.number = atoi(yytext);
  return NUM;
}

[-+] { return yytext[0]; }

[ \t\f\v\n] { ; }

%%

int yywrap() {
  return 1;
}

It compiles nicely but when I run it and type something like 2 + 4 then it gets stuck and doesn't print the answer. Can somebody explain why? My guess is that my grammar is not correct (but I don't know how).

1
Does it print output when you enter ctrl/d or ctrl/z, whichever is an EOF on your operating system's terminal?user207421
It prints out the answer on ctrl + d. But now after printing, it gets out of the program as soon as I press ctrl + d. I want it to take more inputs after printing out the answer. How can I do that?Arno Lorentz
If you want your calculator to respond to newlines, you will need to incorporate newlines into your grammar. Iirc there is an example in the bison manual.rici

1 Answers

0
votes

I came to the same idea like rici and changed your samples appropriately:

file calc.l:

%{
  #include <stdio.h>
  #include <stdlib.h>
  #include "calc.y.h"
%}

%%

[0-9]+ {
  yylval.number = atoi(yytext);
  return NUM;
}

[-+] { return yytext[0]; }

"\n" { return EOL; }

[ \t\f\v\n] { ; }

%%

int yywrap() {
  return 1;
}

file calc.y:

%{
  #include <stdio.h>
  #include <stdlib.h>

  extern int yylex();
  void yyerror(char *);
%}

%union { int number; }
%start input
%token EOL
%token <number> NUM
%type <number> expression

%%

input: line input | line

line: expression EOL { printf("%d\n", $1); };

expression: expression '+' NUM  { $$ = $1 + $3; };
expression: expression '-' NUM  { $$ = $1 - $3; };
expression: NUM  { $$ = $1; };

%%

void yyerror(char *s) {
  fprintf(stderr, "%s", s);
  exit(1);
}

int main() {
  yyparse();
  return 0;
}

Compiled & tested in cygwin on Windows 10 (64 bit):

$ flex -o calc.l.c calc.l

$ bison -o calc.y.c -d calc.y

$ gcc -o calc calc.l.c calc.y.c

$ ./calc
2 + 4
6
2 - 4
-2
234 + 432
666

Notes:

  1. Minor issue: According to the build commands, I had to change the #include for the generated token table. (A matter of taste.)

  2. I introduced the EOL token in the lex source as well as in the line rule of the parser.

  3. While testing I recognized that the 2nd input ended everytimes in a syntax error. I needed a while until I recognized that the grammer was actually limited now to accept precisely one line. Thus, I inserted the recursive input rule in the parser source.