Search code examples
bisonoperator-precedencejison

Jison operator precedence


I'm developing a language using Jison, and a problem I'm running into is the operator precedence. I want comparison operators to be the first operators to be evaluated, so for example, 1 + 2 < 3 - 3 * 4 would become (1+2) < (3 - (3 * 4))

The rules I have right now:

expression
  : expression OP_COMPARISON expression
    { $$ = { type: "Comparison", operator: $2, args: [$1, $3], location: locationFromObject(@$) } }
  | literal
    { $$ = $literal; }
  | NAME
    { $$ = { type: "Variable", variable: $1, location: locationFromObject(@$) }; }
  | field
    { $$ = { type: "Field", field: $field, location: locationFromObject(@$) }; }
  | '(' ':' typename ')' expression
    { $$ = { type: "Cast", cast_type: $typename, expression: $expression, location: locationFromObject(@$) }; }
  | function_call
    { $$ = $function_call; $$.type = "FunctionCall"; }
  | method_call
    { $$ = $method_call; $$.type = "FunctionCall"; }
  | '(' expression ')'
    { $$ = { type: "Grouped", expression: $expression, location: locationFromObject(@$) }; }
  | OP_PREPOSTFIX expression
    { $$ = { type: "Prefix", operator: $1, arg: $expression, location: locationFromObject(@$) }; }
  | expression OP_PREPOSTFIX
      { $$ = { type: "Postfix", operator: $2, arg: $expression, location: locationFromObject(@$) }; }
  | expression OP_ARITHMETIC expression
    {
      if($1.type == "Arithmetic"){
        $$ = $1;
        $$.args.push($3);
        $$.operators.push($2);
        $$.location = locationFromObject(@$);
      }else{
        $$ = { type: "Arithmetic", operators: [$2], args: [$1, $3], location: locationFromObject(@$) };
      }
    }
  | expression OP_LOGICAL expression
    {
      if($1.type == "Logical"){
        $$ = $1;
        $$.args.push($3);
        $$.operators.push($2);
        $$.location = locationFromObject(@$);
      }else{
        $$ = { type: "Logical", operators: [$2], args: [$1, $3], location: locationFromObject(@$) };
      }
    }
  | '!' expression
    { $$ = {type: "LogicalNot", arg: $expression, location: locationFromObject(@$) }; }
  ;

Any help wil be greatly appreciated


Solution

  • The best way to get operator precedence into a context free grammar is to use several rules that indicate the levels of precedence. For example, with simple arithmetic:

    expression : expression PLUS term
               | expression MINUS term
               | term
               ;
    
    term : term MULTIPLY factor
         | term DIVIDE factor
         | factor
         ;
    
    factor : IDENTIFIER
           | CONSTANT
           | LEFT expression RIGHT
           ;
    

    Using this hierarchy the BODMAS rules of arithmetic are implemented.

    You can do something similar with your grammar: break into several rules representing the different layers of precedence.

    (See standard compiler writing computer science texts for more details)