Skip to main content

Complex Expressions

This page explores how the compiler handles complex arithmetic expressions, demonstrating operator precedence, associativity, and the use of parentheses.

Operator Precedence

The compiler correctly implements mathematical operator precedence where multiplication and division have higher precedence than addition and subtraction.
let result = 2 + 3 * 4;
print result;
Expected Result: 14 (not 20)The expression evaluates as 2 + (3 * 4) because * has higher precedence than +.

Precedence Levels

The compiler implements two precedence levels:

High Precedence

Operators: * (multiplication), / (division)These are evaluated first in any expression.Example: 10 + 5 * 210 + 1020

Low Precedence

Operators: + (addition), - (subtraction)These are evaluated after high-precedence operators.Example: 10 - 3 + 27 + 29

Precedence Examples

let x = 10 - 5 * 2;
print x;  // Output: 0 (evaluates as 10 - 10)

Left Associativity

Operators at the same precedence level are evaluated left-to-right (left-associative).
1

Addition/Subtraction

let result = 10 - 3 - 2;
print result;  // Output: 5
Evaluates as (10 - 3) - 2 = 7 - 2 = 5Not as 10 - (3 - 2) = 10 - 1 = 9
2

Multiplication/Division

let result = 20 / 2 / 2;
print result;  // Output: 5
Evaluates as (20 / 2) / 2 = 10 / 2 = 5Not as 20 / (2 / 2) = 20 / 1 = 20
3

Mixed Operations

let result = 100 - 50 + 10 - 5;
print result;  // Output: 55
Evaluates left-to-right:
  • 100 - 50 = 50
  • 50 + 10 = 60
  • 60 - 5 = 55

Parentheses for Grouping

Parentheses () can override operator precedence by forcing evaluation order.

Basic Grouping

let x = 2 + 3 * 4;
print x;  // Output: 14
Default precedence: multiplication first.

AST Comparison

See how parentheses change the AST structure:
ExpresionBinaria
├── operador: '+'
├── izquierda:
│   └── NumeroLiteral(2)
└── derecha:
    └── ExpresionBinaria
        ├── operador: '*'
        ├── izquierda:
        │   └── NumeroLiteral(3)
        └── derecha:
            └── NumeroLiteral(4)
Notice the ExpresionAgrupada node that wraps the addition, forcing it to be evaluated first.

Nested Parentheses

Parentheses can be nested to create complex expressions.
1

Simple Nesting

let x = (10 + (5 * 2));
print x;  // Output: 20
Inner parentheses evaluate first: 5 * 2 = 10, then 10 + 10 = 20
2

Multiple Nesting

let x = ((10 + 5) * (3 - 1));
print x;  // Output: 30
Each parenthesized expression evaluates independently:
  • (10 + 5) = 15
  • (3 - 1) = 2
  • 15 * 2 = 30
3

Deep Nesting

let x = (((20 - 10) / 2) + 5) * 2;
print x;  // Output: 20
Evaluation from innermost to outermost:
  1. 20 - 10 = 10
  2. 10 / 2 = 5
  3. 5 + 5 = 10
  4. 10 * 2 = 20

Complex Expression Examples

Putting it all together with real-world scenarios.

Mathematical Formula

// Calculate: (a + b) * (c - d) / e
let a = 10;
let b = 5;
let c = 8;
let d = 3;
let e = 3;
let result = (a + b) * (c - d) / e;
print result;  // Output: 25
Step-by-step evaluation:
  1. (a + b)(10 + 5) = 15
  2. (c - d)(8 - 3) = 5
  3. 15 * 5 = 75
  4. 75 / 3 = 25

Intermediate Code Generated

a = 10
b = 5
c = 8
d = 3
e = 3
t0 = a + b      // t0 = 15
t1 = c - d      // t1 = 5
t2 = t0 * t1    // t2 = 75
t3 = t2 / e     // t3 = 25
result = t3
print result

Expression Parsing Details

Understanding how the parser works internally.
The compiler uses recursive descent parsing with these grammar rules:
expresion    → suma_resta
suma_resta   → mult_div (('+' | '-') mult_div)*
mult_div     → primario (('*' | '/') primario)*
primario     → NUMERO | IDENTIFICADOR | '(' expresion ')'
  • expresion: Entry point for all expressions
  • suma_resta: Handles addition and subtraction (lower precedence)
  • mult_div: Handles multiplication and division (higher precedence)
  • primario: Base cases (numbers, variables, grouped expressions)
The recursive structure automatically handles precedence and associativity correctly.
  1. expresion() calls suma_resta()
  2. suma_resta() calls mult_div()
  3. mult_div() calls primario() → gets 2
  4. Back in suma_resta(), sees + operator
  5. Calls mult_div() again for the right side
  6. mult_div() gets 3, sees *, gets 4
  7. Returns 3 * 4 as a subtree
  8. suma_resta() combines: 2 + (3 * 4)
Result: AST with * deeper than +

Practice Problems

Test your understanding of operator precedence:

Problem 1

What does this output?
let x = 5 + 3 * 2 - 4;
print x;

Problem 2

What does this output?
let x = (5 + 3) * (2 - 4);
print x;

Problem 3

What does this output?
let x = 20 / 4 / 2;
print x;

Problem 4

What does this output?
let x = 10 + 5 * 2 - 3 * 2;
print x;
The compiler uses integer division (// in Python). This means 7 / 2 equals 3, not 3.5.