authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-09-09 02:59:27-07:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-10-22 12:21:43+02:00
log723c70fd438a6e9221155ff632a9974e30a7fe14
tree30d71ad27dabc8d2238990d5f8ba2c5831f8a521
parent04e8a5e42b58f1fdeb749caf22a1c998344b3ba8

langref: Attempt to simplify precedence section/examples


9 files changed, 161 insertions(+), 165 deletions(-)

doc/langref.html.in+67-10
......@@ -1894,20 +1894,77 @@ const B = error{Two};
18941894 </div>
18951895 {#header_close#}
18961896 {#header_open|Precedence#}
1897 <p>
1898 Zig features partial operator precedence: not every operator has a defined precedence with
1899 every other operator. Mixing operators with ambiguous precedence is prohibited and parentheses
1900 are mandatory. Consequently, valid code is typically auto-descriptive; when reading and
1901 writing Zig code, there are very few precedence rules you'll need to remember.
1902 </p>
1903
1904 {#code|operator_precedence.zig#}
1905
1906 {#code|ambiguous_precedence_orelse.zig#}
1907
1908 <p>
1909 Simplified operator precedence tables are provided below, each tailored to a general group of
1910 operators. For all of the groups shown below, the operators that have higher precedence are
1911 the same, and themselves have the following precedence table:
1912 </p>
1913
18971914 <pre>{#syntax#}x() x[] x.y x.* x.?
18981915a!b
18991916x{}
1900!x -x -%x ~x &x ?x try
1901* / % *% *| ||
1902+ - ++ +% -% +| -|
1903<< >> <<|
1904& ^ | orelse catch
1905== != < > <= >=
1906and
1907or
1908= *= *%= *|= /= %= += +%= +|= -= -%= -|= <<= <<|= >>= &= ^= |={#endsyntax#}</pre>
1909 {#header_close#}
1917!x -x -%x ~x &x ?x try{#endsyntax#}</pre>
1918
1919 <p>
1920 For the tables below, the operators that have ambiguous precedence or semantic errors with the
1921 given operators have been omitted, as have operators with higher precedence.
1922 The operators colored <span style="color: #C54675;">red</span> and
1923 <span style="color: #419AFF;">blue</span> have ambiguous precedence with the other operators
1924 on the same line. Those colored <span style="color: #C54675;">red</span> also have ambiguous
1925 precedence with themselves (they cannot be chained).
1926 </p>
1927
1928 <p>
1929 For arithmetic operators:
1930 </p>
1931
1932 <pre><code>* / *% *| <span style="color: #C54675;">%</span>
1933+ - +% -% +| -|
1934<span style="color: #C54675;">== != &lt; > &lt;= >=</span>
1935<span style="color: #419AFF;">and or</span></code></pre>
1936
1937 <p>
1938 For bitwise operators:
1939 </p>
1940
1941 <pre><code><span style="color: #C54675;">&lt;&lt; &lt;&lt;| >></span> <span style="color: #419AFF;">& ^ |</span>
1942<span style="color: #C54675;">== != &lt; > &lt;= >=</span>
1943<span style="color: #419AFF;">and or</span></code></pre>
1944
1945 <p>
1946 For null and error recovery operators:
1947 </p>
1948
1949 <pre><code><span style="color: #419AFF;">catch orelse</span></code></pre>
1950
1951 <p>
1952 For error set operators:
1953 </p>
19101954
1955 <pre>{#syntax#}||{#endsyntax#}</pre>
1956
1957 <p>
1958 For array concatenation operators:
1959 </p>
1960
1961 <pre>{#syntax#}++{#endsyntax#}</pre>
1962
1963 <p>
1964 The full precedence rules can be found in the {#link|Grammar#}, which also includes an {#link|Operator Precedence Flowchart#}.
1965 </p>
1966
1967 {#header_close#}
19111968 {#header_close#}
19121969
19131970 {#header_open|Arrays#}
doc/langref/ambiguous_precedence_mul_bitand.zig deleted-11
......@@ -1,11 +0,0 @@
1test "ambiguous precedence" {
2 const x: u8 = 1;
3 const y: u8 = 2;
4 const z: u8 = 3;
5
6 // 1. Could be multiplication by a masked value: (x&y) *% z
7 // 2. Or masking the result of multiplication: x & (y*%z)
8 _ = x & y *% z;
9}
10
11// obj=ambiguous operator precedence with operators ... & ... *% ...; use parentheses to disambiguate
\ No newline at end of file
doc/langref/ambiguous_precedence_orelse.zig created+12
......@@ -0,0 +1,12 @@
1test "ambiguous precedence" {
2 const x: ?bool = true;
3 const y: bool = false;
4 const z: bool = false;
5
6 // The following expression is always considered ambiguous,
7 // and, in this particular case, `(x orelse y) == z` would
8 // produce a different result than `x orelse (y == z)`.
9 _ = x orelse y == z;
10}
11
12// obj=ambiguous operator precedence with operators ... orelse ... == ...; use parentheses to disambiguate
doc/langref/ambiguous_precedence_orelse_ne.zig deleted-18
......@@ -1,18 +0,0 @@
1fn makeBool(x: u32) ?bool {
2 if (x == 0) return false;
3 if (x == 1) return true;
4 return null;
5}
6
7test "ambiguous precedence" {
8 const x: ?u32 = 0;
9 const y: u8 = 0;
10 const z: u8 = 0;
11
12 // Without parentheses the compiler cannot distinguish between two conflicting interpretations:
13 // 1. Compare on the value which may be recovered: (makeBool(x) orelse y) != z
14 // 2. Recover to the result of the comparison: makeBool(x) orelse (y != z)
15 _ = makeBool(x) orelse y != z;
16}
17
18// obj=ambiguous operator precedence with operators ... orelse ... != ...; use parentheses to disambiguate
\ No newline at end of file
doc/langref/ambiguous_precedence_shift_sub.zig deleted-10
......@@ -1,10 +0,0 @@
1test "ambiguous precedence" {
2 const x: u64 = 1;
3 const y: u6 = 63;
4
5 // 1. Could mean shift then subtract: (x<<y) - 1 such as to generate a mask
6 // 2. Or to shift after the subtract: x << (y-1) such as selecting bits
7 _ = x << y - 1;
8}
9
10// obj=ambiguous operator precedence with operators ... << ... - ...; use parentheses to disambiguate
\ No newline at end of file
doc/langref/chained_sibling_operators.zig deleted-5
......@@ -1,5 +0,0 @@
1fn foo(x: anytype, y: anytype, z: anytype) void {
2 _ = x + y - z;
3}
4
5// syntax
\ No newline at end of file
doc/langref/incremental_operator_precedence.zig deleted-87
......@@ -1,87 +0,0 @@
1const expectEqual = @import("std").testing.expectEqual;
2const expect = @import("std").testing.expect;
3
4test "Operator Precedence" {
5 // Arithmetic
6 // + and - can be freely mixed and are left associative
7 try expectEqual((2 + 3) + 4, 2 + 3 + 4);
8 try expectEqual((1 - 2) - 3, 1 - 2 - 3);
9
10 // Saturating and wrapping modifications to arithmetic operators act the same
11 const x: u8 = 255;
12 try expectEqual((x +% 2) -| 3, x +% 2 -| 3);
13 try expectEqual(0, x +% 2 -| 3);
14
15 // Since all associative operators are left associative, non-left-to-right evaluation must be forced with parenthesis
16 try expectEqual(255, x +% (2 -| 3));
17
18 // * and / can be freely mixed and bind tigher than any addition
19 try expectEqual((8 / 2) * 4, 8 / 2 * 4);
20 try expectEqual(1 + (2 * 3), 1 + 2 * 3);
21
22 // The Modulus is special in that it cannot be repeated without parenthesis
23 // The following line would be a compile error
24 // _ = x % 7 % 3;
25 // However it does bind tighter than addition
26 try expectEqual(1 + (x % 3), 1 + x % 3);
27
28 // Bitwise Operators
29 // &, |, and ^ can all be freely repeated, but not mixed without parenthesis
30 try expectEqual((x & 0xF) & 1, x & 0xF & 1);
31 try expectEqual((x | 0xF) | 1, x | 0xF | 1);
32 try expectEqual((x ^ 0xF) ^ 1, x ^ 0xF ^ 1);
33 // The following line would be a compile error
34 // _ = x & 0xF | 0xF0;
35
36 // Bitshifts <<, <<|, and >> cannot be repeated without parenthesis nor mixed with other bitwise operators
37 // The following lines would be compile errors
38 // _ = x >> 1 >> 2;
39 // _ = 1 << 16 | 1 << 8 | 1;
40 // Instead parentheses should be used
41 try expectEqual(63, (x >> 1) >> 2);
42 try expectEqual(0x10101, (1 << 16) | (1 << 8) | 1);
43
44 // Comparisons
45 // < > <= >= == != cannot be chained
46 // Binds lower than either bitwise operators or arithmetic operators
47 // which allows one on one side and one on the other
48 try expect(1 | 4 <= 1 + 4);
49 // equivilent to this code
50 try expect((1 | 4) <= (1 + 4));
51
52 // Boolean Operators
53 // `and` and `or` can be freely chained with themselves, shortcircuiting left to right
54 // they may not be mixed with each other without parenthesis
55 // bind looser than comparisons
56 const false_expr: bool = 7 < 2;
57 const true_expr: bool = 1 + 1 == 2;
58
59 try expect(false_expr or false_expr or true_expr);
60 try expect(7 < 2 or 7 < 2 or 1 + 1 == 2);
61 try expect(((7 < 2) or (7 < 2)) or (1 + 1 == 2));
62
63 try expect(true_expr and true_expr and true_expr);
64 try expect(1 + 1 == 2 and 1 + 1 == 2 and 1 + 1 == 2);
65 try expect(((1 + 1 == 2) and (1 + 1 == 2)) and (1 + 1 == 2));
66
67 // The following line would be a compile error
68 // _ = false_expr or true_expr and true_expr
69 // Instead parentheses should be used
70 try expect(false_expr or (true_expr and true_expr));
71
72 // Orelse and Catch
73 // Like Boolean Operators, may be chained with themselves, but not each other
74 // bind looser than prefix expressions
75 const some_null: ?u8 = null;
76 try expectEqual(2, some_null orelse some_null orelse 2);
77
78 const some_error: error{invalid}!u8 = error.invalid;
79 try expectEqual(3, some_error catch some_error catch 3);
80
81 // The following line would be a compile error
82 // _ = some_error catch some_null orelse 1;
83 // Instead parentheses should be used
84 try expectEqual(1, (some_error catch some_null) orelse 1);
85}
86
87// obj
\ No newline at end of file
doc/langref/nonchaining_sibling_operators.zig deleted-5
......@@ -1,5 +0,0 @@
1fn foo(x: anytype, y: anytype, z: anytype) void {
2 _ = x << y << z;
3}
4
5// obj=repetition of non-chainable operators ... << ... << ...; use parentheses to disambiguate
\ No newline at end of file
doc/langref/operator_precedence.zig+82-19
......@@ -1,23 +1,86 @@
1const assert = @import("std").debug.assert;
1const std = @import("std");
22
33test "operator precedence" {
4 const x = 1;
5 const y = 2;
6 const z = 3;
7
8 assert(x + y * z == x + (y*z));
9 assert(x * y + z == (x*y) + z);
10
11 // Ambiguously related operators may exist in the same expression if they are not adjacent
12 assert(x*y + x%y == (x*y) + (x%y));
13 // Thus includes comparisons
14 assert(x & y != x + y);
15
16 // Or's and and's may be freely chained, but not with each other
17 assert(false or x == y or x + y == z);
18 assert(true and x != y and y != z);
19 // The following line would be a compile error
20 // assert(false or x != y and y != z);
4 // Arithmetic operators follow the conventional order of operations.
5 {
6 const a = 1 + 2 * 3 - 4 / 2;
7 const b = 1 + (2 * 3) - (4 / 2);
8 try std.testing.expectEqual(a, b);
9 }
10
11 // Remainder division is a special case. It still has a higher precedence than
12 // addition and subtraction, but it has ambiguous precedence with itself and
13 // other multiplication/division operators.
14 {
15 const a = 1 + 2 % 3;
16 const b = 1 + (2 % 3);
17 try std.testing.expectEqual(a, b);
18
19 // `1 % 2 % 3`, `1 % 2 / 3`, etc. would be a compile error,
20 // and parentheses would be required to disambiguate.
21 }
22
23 // Bitwise operators have ambiguous precedence with other bitwise operators.
24 // However, non-shifting bitwise operators (&, |, ^) can be chained with themselves.
25 {
26 const a = 1 ^ 2 ^ 3;
27 const b = (1 ^ 2) ^ 3;
28 try std.testing.expectEqual(a, b);
29
30 // `1 ^ 2 | 3`, `1 << 2 << 3`, etc. would be a compile error,
31 // and parentheses would be required to disambiguate.
32
33 // To mix bitwise operators, parentheses must be used.
34 try std.testing.expectEqual(0x10101, (1 << 16) | (1 << 8) | 1);
35 }
36
37 // Comparison operators have lower precedence than both arithmetic and bitwise
38 // operators. Comparisons cannot be chained.
39 {
40 const a = 1 | 4 <= 1 + 4;
41 const b = (1 | 4) <= (1 + 4);
42 try std.testing.expectEqual(a, b);
43
44 // `1 == 2 >= 3`, etc. would be a compile error,
45 // and parentheses would be required to disambiguate.
46 }
47
48 // Logical operators have lower precedence than comparison operators,
49 // and may only be chained homogeneously (`and` with `and`, but not
50 // with `or`, and vice versa).
51 {
52 const false_expr: bool = 7 < 2;
53 const true_expr: bool = 1 + 1 == 2;
54
55 const a = false_expr or false_expr or true_expr;
56 const b = 7 < 2 or 7 < 2 or 1 + 1 == 2;
57 const c = ((7 < 2) or (7 < 2)) or (1 + 1 == 2);
58 try std.testing.expect(a and b and c);
59
60 // `a or b and c`, etc. would be a compile error,
61 // and parentheses would be required to disambiguate.
62 }
63
64 // `orelse` and `catch` may be chained with themselves, but not each other.
65 {
66 const some_null: ?u8 = null;
67
68 const a = some_null orelse some_null orelse 2;
69 const b = (some_null orelse some_null) orelse 2;
70 try std.testing.expectEqual(a, b);
71
72 // `a catch b orelse c`, etc. would be a compile error,
73 // and parentheses would be required to disambiguate.
74
75 // Prefix operators like `try` have higher precedence than `orelse`/`catch`.
76 const c = try maybe() orelse 1;
77 const d = (try maybe()) orelse 1;
78 try std.testing.expectEqual(c, d);
79 }
80}
81
82fn maybe() error{Fail}!?u8 {
83 return null;
2184}
2285
23// test
\ No newline at end of file
86// test