Wado

Operator Precedence Research

Research on operator precedence across C, Java, Rust, Go, JavaScript, and Python for Wado language design.

Executive Summary

Key Findings:

  1. C's Design Mistake: Bitwise operators (&, |, ^) have lower precedence than comparison operators (==, <, etc.), which is counterintuitive since they conceptually behave like arithmetic operators
  2. Rust Fixed This: Rust gives bitwise operators higher precedence than comparison operators, aligning with intuition
  3. Go Also Fixed This: Go groups bitwise AND with multiplication (highest), bitwise OR/XOR with addition (middle), and comparisons last (lowest)
  4. Python's Power Operator Quirk: ** binds tighter than unary on left, looser than unary on right, so -1**2 = -1
  5. Increment/Decrement Problems: ++/-- cause undefined behavior, side effect issues, and confusion - languages like Rust, Go, and Python omit them

The C Design Mistake Explained

The Problem

In C (and languages that inherited this design like Java and JavaScript), the expression:

if (flags & MASK == EXPECTED)  // Bug! Parsed as: flags & (MASK == EXPECTED)

is parsed as flags & (MASK == EXPECTED), not (flags & MASK) == EXPECTED.

This happens because comparison operators have higher precedence than bitwise operators in C.

Why This Is Wrong

Conceptually, & and | are arithmetic-like operations:

This violates the principle of least surprise and requires excessive parentheses.

Historical Reason

According to the Wikipedia article on C operators and foonathan.net's analysis:

Historically, there was no syntactic distinction between the bitwise and logical operators. In BCPL, B and early C, the operators && || didn't exist. Instead & | had different meaning depending on whether they are used in a 'truth-value context'.

When && and || were added, the precedence of & and | was left as-is for backward compatibility, creating this permanent design flaw.

Real-World Impact

From Microsoft's documentation:

The comparison operator has a higher precedence than the bitwise operator. The comparison operator will be evaluated first. The result will be implicitly cast to an integer and used as an operand in the bitwise operation.

This causes bugs that are caught by static analyzers with warnings like "comparison operator has higher precedence than bitwise operator."

Comprehensive Precedence Comparison

1. Bitwise vs Comparison Operators

Language Bitwise Precedence Comparison Precedence Which is Higher? Source
C & (8), ^ (9), \| (10) ==, != (7) ❌ Comparison > Bitwise cppreference
Java & (8), ^ (9), \| (10) ==, != (7) ❌ Comparison > Bitwise Programiz Java
Rust & (8), ^ (9), \| (10) ==, != (11) ✅ Bitwise > Comparison Rust Reference
Go & (5), ^/\| (4) ==, != (3) ✅ Bitwise > Comparison Go 101
JS & (9), ^ (10), \| (11) ==, != (10) ❌ Mixed (& > ==, | < =) MDN
Python & (9), ^ (10), \| (11) ==, != (7) ❌ Comparison > Bitwise Python Docs

Note: Lower precedence number = higher priority (evaluated first). Table uses common numbering where 1 is highest.

2. Unary Operators

Language Unary -, + Bitwise NOT Notes
C Level 2 ~ (Level 2) All unary at same level, right-to-left
Java Level 2 ~ (Level 2) Same as C
Rust Not applicable ! (Level 1) Rust uses ! for bitwise NOT, highest precedence
Go Level 2 ^ (Level 2) Go uses ^ for bitwise NOT (XOR with 1s)
JS Level 3 ~ (Level 3) All unary at same level
Python Level 3 ~ (Level 3) All unary at same level

Important: Wado uses ~ for bitwise NOT (like C/Java/JS/Python), not ! (Rust) or ^ (Go).

3. Increment/Decrement Operators (++, --)

Language Has ++/--? Prefix/Postfix? Notes
C ✅ Yes Both Side effects, undefined behavior risks
Java ✅ Yes Both Inherits C semantics
Rust ❌ No N/A Removed to avoid side effect issues
Go ✅ Yes Postfix only Statements only, not expressions
JS ✅ Yes Both Inherits C semantics
Python ❌ No N/A Never had them, uses += 1 instead

Problems with ++/-- (Learn C++):

  1. Undefined Behavior: x + ++x has different results in different compilers
  2. Side Effects: Modifying a variable multiple times in same expression is UB
  3. Confusion: Prefix vs postfix semantics are error-prone
  4. Performance Myth: Postfix must copy original value (though optimizers fix this)

Wado Decision: ❌ No ++/-- operators (following Rust/Python). Use x += 1 and x -= 1 instead.

4. Power Operator (**)

Language Has **? Precedence Notes
C ❌ No N/A Use pow() function
Java ❌ No N/A Use Math.pow()
Rust ❌ No N/A Use .pow() method
Go ❌ No N/A Use math.Pow()
JS ✅ Yes Level 3 (very high) Right-associative: 2**3**2 = 2**(3**2) = 512
Python ✅ Yes Between unary -1**2 = -1 (unary binds looser on left)

Python's Quirk (Python Docs):

The power operator binds more tightly than unary operators on its left; it binds less tightly than unary operators on its right.

This means:

Wado Decision: ❌ **No ** operator**. Reasons:

  1. Wasm has no native power instruction (would compile to function call anyway)
  2. Ambiguous precedence (Python's approach is confusing)
  3. Use explicit pow(x, y) function instead
  4. Consistent with Rust/Go

Detailed Precedence Tables

Rust Precedence (Wado's Baseline)

From highest to lowest (Rust Reference):

Level Operators Associativity Description
1 Paths, method calls, field access Left-to-right ::, ., ()
2 ? N/A Error propagation
3 Unary: !, -, *, &, &mut Right-to-left Prefix operators
4 as Left-to-right Type cast
5 *, /, % Left-to-right Multiplicative
6 +, - Left-to-right Additive
7 <<, >> Left-to-right Bitwise shift
8 & Left-to-right Bitwise AND
9 ^ Left-to-right Bitwise XOR
10 \| Left-to-right Bitwise OR
11 ==, != (left-assoc), <, >, <=, >= (non-assoc) Mixed Comparison
12 && Left-to-right Logical AND
13 \|\| Left-to-right Logical OR
14 .., ..= N/A Range operators
15 =, +=, etc. Right-to-left Assignment

Key Points:

Go Precedence (Alternative Approach)

From highest to lowest (Go 101):

Level Operators Associativity Description
5 *, /, %, <<, >>, &, &^ Left-to-right Multiplicative + bit ops
4 +, -, \|, ^ Left-to-right Additive + bit ops
3 ==, !=, <, <=, >, >= Left-to-right Comparison
2 && Left-to-right Logical AND
1 \|\| Left-to-right Logical OR

Key Points:

C Precedence (The Problematic One)

From highest to lowest (cppreference):

Level Operators Associativity Description
2 ++, --, !, ~, +, -, *, & Right-to-left Unary
3 *, /, % Left-to-right Multiplicative
4 +, - Left-to-right Additive
5 <<, >> Left-to-right Bitwise shift
6 <, <=, >, >= Left-to-right Relational
7 ==, != Left-to-right Equality
8 & Left-to-right Bitwise AND
9 ^ Left-to-right Bitwise XOR
10 \| Left-to-right Bitwise OR
11 && Left-to-right Logical AND
12 \|\| Left-to-right Logical OR
13 ?: Right-to-left Ternary conditional
14 =, +=, etc. Right-to-left Assignment

Problems:

Associativity Comparison

Language Comparison Associativity Notes
C Left-to-right a < b < c = (a < b) < c (probably wrong)
Java Left-to-right Same as C
Rust Non-associative a < b < c is compile error
Go Left-to-right But rarely chained in practice
JS Left-to-right 1 < 2 < 3 = true (confusing!)
Python Chained comparisons a < b < c = (a < b) and (b < c)

Analysis:

Wado's Decision (2026-01-11)

Wado supports mathematical comparison chaining similar to Python, with stricter validation rules.

Valid chains (same direction):

Invalid chains (semantic error):

Chaining rules:

  1. Same-direction inequality: </<= can only chain with </<=, and >/>= can only chain with >/>=
  2. Equality chaining: == can only chain with ==
  3. No != chaining: != cannot be chained (ambiguous meaning)
  4. No mixing: Cannot mix equality operators with inequality operators

Rationale:

Concrete Examples of Issues

Example 1: C's Bitwise Precedence Bug

// C code - Bug lurking!
#define READ_FLAG  0x01
#define WRITE_FLAG 0x02

// Intended: check if BOTH flags are set
if (permissions & READ_FLAG == READ_FLAG) {  // ❌ WRONG!
    // This checks: permissions & (READ_FLAG == READ_FLAG)
    //            = permissions & 1
    //            = permissions & true
    // Never what you want!
}

// Correct version (requires parentheses)
if ((permissions & READ_FLAG) == READ_FLAG) {  // ✅ Correct
    // Do something
}

Example 2: Python's Power Operator Confusion

# Python - Confusing precedence
print(-1**2)   # Output: -1 (not 1!)
# Parsed as: -(1**2) = -(1) = -1

print((-1)**2) # Output: 1
# Explicitly grouped

# With variables, even more confusing
x = 2
print(-x**2)   # Output: -4 (parsed as -(x**2))

Example 3: C's Increment Operator Undefined Behavior

// C code - Undefined behavior!
int x = 1;
int y = x + ++x;  // UB! Different compilers give different results
// GCC/Visual Studio: y = 2 + 2 = 4
// Clang: y = 1 + 2 = 3

// Also problematic
int arr[3] = {1, 2, 3};
int i = 0;
arr[i] = i++;  // UB! Order of evaluation not specified

Example 4: JavaScript's Chained Comparison Confusion

// JavaScript - Logically wrong but syntactically valid
console.log(1 < 2 < 3); // true (seems right?)
console.log(3 > 2 > 1); // false (wait, what?)

// What's actually happening:
// 1 < 2 < 3
// = (1 < 2) < 3
// = true < 3
// = 1 < 3
// = true

// 3 > 2 > 1
// = (3 > 2) > 1
// = true > 1
// = 1 > 1
// = false

Recommendations for Wado

✅ Use Rust's Precedence as Baseline

Reasons:

  1. Fixes C's bitwise mistake: Bitwise operators > Comparison operators
  2. No increment/decrement: Avoids undefined behavior and side effect issues
  3. Non-associative comparisons: Prevents a < b < c bugs at compile time
  4. No power operator: Avoids precedence ambiguity
  5. Well-designed: Rust's precedence has been carefully thought through

✅ Wado-Specific Decisions Align Well

From the spec, Wado has already decided:

⚠️ Potential Pitfall: Bitwise NOT Symbol

The Issue: Wado uses ~ for bitwise NOT, while Rust uses !.

Operation Rust Wado Issue
Bitwise NOT !x ~x Different symbol
Logical NOT !x !x Same
Precedence Same Same Both are unary level 3

Analysis:

Based on Rust, with Wado-specific operators:

Level Operators Associativity Description
1 Paths, method calls, field access Left-to-right ::, ., ()
2 ? N/A Error propagation
3 Unary: !, ~, -, *, &, &mut Right-to-left Prefix operators
4 as Left-to-right Type cast
5 *, /, % Left-to-right Multiplicative
6 +, - Left-to-right Additive
7 <<, >> Left-to-right Bitwise shift
8 & Left-to-right Bitwise AND
9 ^ Left-to-right Bitwise XOR
10 \| Left-to-right Bitwise OR
11 ==, != (left-assoc), <, >, <=, >= (non-assoc) Mixed Comparison
12 && Left-to-right Logical AND
13 \|\| Left-to-right Logical OR
14 .., ..= N/A Range operators
15 =, +=, etc. Right-to-left Assignment

Key differences from Rust:

🎯 Verification Examples

To ensure Wado's precedence is correct:

// Example 1: Bitwise vs Comparison (Rust-style, correct)
let flags = 0b1010;
let mask = 0b0010;

if flags & mask == mask {  // ✅ Parses as: (flags & mask) == mask
    println("Flag is set");
}

// Example 2: Bitwise NOT
let x = 0b1010;
let y = ~x;  // ✅ Bitwise NOT (Wado uses ~, not !)

// Example 3: Comparison chaining rules
if a < b < c {  // ✅ OK (mathematical chaining: a < b AND b < c)
    println("a < b < c");
}

if 0 <= x <= 100 {  // ✅ OK (range check)
    println("x is in range [0, 100]");
}

if a < b > c {  // ❌ Semantic error (mixed directions)
}

if a != b != c {  // ❌ Semantic error (!= chaining not allowed)
}

if a == b == c {  // ✅ OK (equality chaining)
    println("All three are equal");
}

// Example 4: No increment operators
let mut count = 0;
count++;  // ❌ Compile error (no ++ operator)
count += 1;  // ✅ Correct way

// Example 5: No power operator
let result = x ** 2;  // ❌ Compile error (no ** operator)
let result = pow(x, 2);  // ✅ Use function instead

Summary of Design Decisions

Feature C/Java Rust Wado Rationale
Bitwise > Comparison Fixes C's counterintuitive precedence
Bitwise NOT symbol ~ ! ~ Familiar to most developers
Increment/decrement (++) Avoids undefined behavior & side effects
Power operator (**) No native Wasm instruction, use pow()
Chained comparisons Left Non Math Same-direction chains OK, mixed/!= rejected
Unary precedence High High High Consistent across languages

Sources

  1. Operator precedence is broken - foonathan.net
  2. Microsoft Learn - Precedence and order of evaluation
  3. Operators in C and C++ - Wikipedia
  4. Microsoft Learn - lnt-comparison-bitwise-precedence
  5. C Operator Precedence - cppreference.com
  6. Rust Reference - Expressions
  7. Operator expressions - The Rust Reference
  8. Operator Precedence: We can do better - Adamant Programming Language Blog
  9. Python 3 Expressions Documentation
  10. Go 101 - Common Operators
  11. Operator precedence in Go - Programming.Guide
  12. MDN - JavaScript Operator Precedence
  13. Learn C++ - Increment/decrement operators and side effects
  14. Programiz - Increment and Decrement Operators
  15. Increment and decrement operators - Wikipedia

Date: 2026-01-11 Status: Research Complete Decision: See docs/wep-2026-01-11-operator-precedence.md for the final decision

Summary: ✅ Use Rust's precedence as baseline with the following modifications: