0

so I'm doing a thesis where I am making my own programming language. I am currently trying to make a simple calculator language that allows you to assign ints to variables and then do operations/print them out.

However I ran across the situation that bison treats everything with the same precedence unless told otherwise (so 1 + 2 * 3 gives 9 instead of 7).

I looked around and I was told doing something like

%left ADD SUB
%left MUL DIV

would give me the expected output, but it doesn't. Can anyone help me figure out what is going wrong?

here's my flex file

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


%%
print       {return PRINT;}
exit        {return EXIT;}

[a-zA-Z]    {yylval.name = yytext[0]; return ID;}
[0-9]+      {yylval.data = atoi(yytext); return NUMBER;}
[;\n]       {return NEWLINE;}
[ \t]       {;}
"-"         {return SUB;}
"+"         {return ADD;}
"*"         {return MUL;}
"/"         {return DIV;}
.           {return UNKNOWN;}
%%

int yywrap(void) { return -1; }

here's my bison file

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

#include "Test.h"

void yyerror(const char* err);

int size = 52;
int vars[52];

extern int yylex();
%}

%define parse.error verbose

%union {
    char name;
    int data;
}

%token EXIT UNKNOWN NEWLINE
%token <data> PRINT

%token <name> ID
%token <data> NUMBER

%type <data> line exp term prog
%type <name> assignment

%start line

%left ADD SUB
%left MUL DIV

%%
term:   NUMBER {
            $$ = $1;
        }
        | ID {
            $$ = GetVar($1, vars);
        }
;

exp:    term
        | exp MUL term {
            $$ = $1 * $3;
        }
        | exp DIV term {
            $$ = $1 / $3;
        }
        | exp ADD term {
            $$ = $1 + $3;
        }
        | exp SUB term {
            $$ = $1 - $3;
        }
;

assignment: ID '=' exp NEWLINE  { 
    UpdateVar($1, $3, vars); 
}
;

prog:   assignment {;}
        | PRINT exp NEWLINE {
            printf("%d\n", $2);
        }
        | EXIT NEWLINE {
            return 0;
        }
        | NEWLINE { ; }
;

line: prog | line prog
%%



void yyerror(const char* err) {
    printf("Error happened: %s\n", err);
}

int main() {
    for(int i = 0; i < size; i++) {
        vars[i] = 0;
    }
    return yyparse();
}
Das
  • 51
  • 6

1 Answers1

0

Precedence rules are only used by yacc/bison to disambiguate. If the grammar is not ambiguous, the precedence declarations are not used. And your grammar is unambiguous.

It's not that bison treats all operators as having the same precedence unless told otherwise. It's that you told bison that all operators have the same precedence (in your grammar). If your grammar is ambiguous and you don't tell bison what the precedence rules are with precedence declarations, bison will report parsing conflicts. [Note 1]

If you take a look at the Bison manual, which is highly recommended, you will find:

  1. Fully explained example calculators

  2. An explanation of the parsing algorithm

  3. which includes an explanation of the precedence rules

The Bison manual is pretty good. But if the language you're writing is worth writing a thesis about, you might want to read a bit more about writing parsers. There are some pretty good textbooks.


Notes

  1. After reporting the problem, it will fall back to a rule ("prefer shift") which treats all operators as being left-associative with the same precedence. In that sense, you could say that bison has a default. But if you get conflict reports you should fix them.
rici
  • 234,347
  • 28
  • 237
  • 341
  • I don't know if this will be useful; it seems to be getting harder to find easy-to-read examples. It's an answer which I wrote a while ago which discusses precedence, and has a side-by-side example of a simple algebraic grammar implemented explicitly and implemented with precedence declarations. https://stackoverflow.com/a/50937673/1566221 – rici Oct 10 '21 at 04:58