Grammars

CSC447

Concepts of Programming Languages

Instructor: James Riely

Grammars

Initial language with three binary operators

expr    → expr    "=" expr                     [Assignment] 
expr    → expr    "-" expr                     [Subtraction]
expr    → expr    "÷" expr                     [Division]   
expr    → NUMBER | IDENTIFIER | "(" expr ")"
x = 1 - 2 ÷ 3

  =            =            ÷          ÷            -   
 / \          / \          / \        / \        /     \
x   -        x   ÷        -   3      =   3      =       ÷
   / \          / \      / \        / \        / \     / \
  1   ÷        -   3    =  2       x   -      x   1   2   3
     / \      / \      / \            / \      
    2   3    1   2    x   1          1   2     

Grammars

Provide names for each level

expr    → assign | term | factor | primary
assign  → expr    "=" expr                     
term    → expr    "-" expr                     
factor  → expr    "÷" expr                     
primary → NUMBER | IDENTIFIER | "(" expr ")"
x = 1 - 2 ÷ 3

  =            =            ÷          ÷            -   
 / \          / \          / \        / \        /     \
x   -        x   ÷        -   3      =   3      =       ÷
   / \          / \      / \        / \        / \     / \
  1   ÷        -   3    =  2       x   -      x   1   2   3
     / \      / \      / \            / \      
    2   3    1   2    x   1          1   2     

Grammars

Precedence solved, but still ambiguous

expr    → assign
assign  → assign  "=" assign  | term           [Lowest Precedence]
term    → term    "-" term    | factor         [Middle Precedence]
factor  → factor  "÷" factor  | primary        [Highest Precedence] 
primary → NUMBER | IDENTIFIER | "(" expr ")"
x = 1 - 2 - 3

  =            =      
 / \          / \     
x   -        x   -    
   / \          / \   
  1   -        -   3  
     / \      / \     
    2   3    1   2    

Grammars

Unambiguous, but at most one occurrence of each operator)

expr    → assign
assign  → term    "=" term    | term           [Lowest Precedence]
term    → factor  "-" factor  | factor         [Middle Precedence]
factor  → primary "÷" primary | primary        [Highest Precedence] 
primary → NUMBER | IDENTIFIER | "(" expr ")"
x = 1 - 2 - 3

  =     
 / \    
x   -   
   / \  
  1  ??? 
     
    

Grammars

Unambiguous, full language

expr    → assign
assign  → term    "=" assign  | term           [Right Associative]
term    → term    "-" factor  | factor         [Left Associative]
factor  → factor  "÷" primary | primary        [Left Associative]
primary → NUMBER | IDENTIFIER | "(" expr ")"
x = y = 1 - 2 - 3

  =         
 / \
x   =
   / \      
  y   -     
     / \    
    -   3
   / \
  1   2

Grammars

Removing left recursion (removing connection between grammar and parse tree for left associative operators)

expr    → assign
assign  → term   ("=" assign)?
term    → factor ("-" factor)*
factor  → primary ("÷" primary)*
primary → NUMBER | IDENTIFIER | "(" expr ")"
x = y = 1 - 2 - 3

  =         
 / \
x   =
   / \      
  y   -     
     / \    
    -   3
   / \
  1   2

Grammars

As code

private Expr expression() {
  return assign();
}

/* lowest precedence, right associative */
private Expr assign() {
  Expr expr = term();

  if (match(EQUAL)) {
    Token operator = previous();
    Expr right = assign();
    expr = new Expr.Binary(expr, operator, right);
  }

  return expr;
}

/* middle precedence, left associative */
private Expr term() {
  Expr expr = factor();

  while (match(DIVIDE)) {
    Token operator = previous();
    Expr right = factor();
    expr = new Expr.Binary(expr, operator, right);
  }

  return expr;
}

/* highest precedence, left associative */
private Expr factor() {
  Expr expr = primary();

  while (match(DIVIDE)) {
    Token operator = previous();
    Expr right = primary();
    expr = new Expr.Binary(expr, operator, right);
  }

  return expr;
}

private Expr primary() {
  if (match(FALSE)) return new Expr.Literal(false);
  if (match(TRUE)) return new Expr.Literal(true);
  if (match(NIL)) return new Expr.Literal(null);

  if (match(NUMBER, STRING)) {
    return new Expr.Literal(previous().literal);
  }

  if (match(LEFT_PAREN)) {
    Expr expr = expression();
    consume(RIGHT_PAREN, "Expect ')' after expression.");
    return new Expr.Grouping(expr);
  }

  throw error(peek(), "Expect expression.");
}