Here's an example approach:
expr -> addExpr;
addExpr -> multExpr (('+'|'-') multExpr)*;
multExpr -> terminalExpr (('*'|'/') terminalExpr)*;
terminalExpr -> integer | variable | '(' expr ')';
But the associativity is ambiguous. Here's a more explicit way in BNF:
expr -> addExpr;
addExpr -> addExpr '+' multExpr | addExpr '-' multExpr | multExpr;
multExpr -> multExpr '*' terminalExpr | multExpr '/' terminalExpr | terminalExpr;
terminalExpr -> integer | variable | '(' expr ')';
These grammars define the operators *
and /
as having more precedence as +
and -
. You declare the operation with higher precedence deeper in the parse tree, so they will be tried first by the parser.