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};...@@ -1894,20 +1894,77 @@ const B = error{Two};
1894 </div>1894 </div>
1895 {#header_close#}1895 {#header_close#}
1896 {#header_open|Precedence#}1896 {#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
1897 <pre>{#syntax#}x() x[] x.y x.* x.?1914 <pre>{#syntax#}x() x[] x.y x.* x.?
1898a!b1915a!b
1899x{}1916x{}
1900!x -x -%x ~x &x ?x try1917!x -x -%x ~x &x ?x try{#endsyntax#}</pre>
1901* / % *% *| ||1918
1902+ - ++ +% -% +| -|1919 <p>
1903<< >> <<|1920 For the tables below, the operators that have ambiguous precedence or semantic errors with the
1904& ^ | orelse catch1921 given operators have been omitted, as have operators with higher precedence.
1905== != < > <= >=1922 The operators colored <span style="color: #C54675;">red</span> and
1906and1923 <span style="color: #419AFF;">blue</span> have ambiguous precedence with the other operators
1907or1924 on the same line. Those colored <span style="color: #C54675;">red</span> also have ambiguous
1908= *= *%= *|= /= %= += +%= +|= -= -%= -|= <<= <<|= >>= &= ^= |={#endsyntax#}</pre>1925 precedence with themselves (they cannot be chained).
1909 {#header_close#}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#}
1911 {#header_close#}1968 {#header_close#}
19121969
1913 {#header_open|Arrays#}1970 {#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 @@...@@ -1,23 +1,86 @@
1const assert = @import("std").debug.assert;1const std = @import("std");
22
3test "operator precedence" {3test "operator precedence" {
4 const x = 1;4 // Arithmetic operators follow the conventional order of operations.
5 const y = 2;5 {
6 const z = 3;6 const a = 1 + 2 * 3 - 4 / 2;
77 const b = 1 + (2 * 3) - (4 / 2);
8 assert(x + y * z == x + (y*z));8 try std.testing.expectEqual(a, b);
9 assert(x * y + z == (x*y) + z);9 }
10 10
11 // Ambiguously related operators may exist in the same expression if they are not adjacent11 // Remainder division is a special case. It still has a higher precedence than
12 assert(x*y + x%y == (x*y) + (x%y));12 // addition and subtraction, but it has ambiguous precedence with itself and
13 // Thus includes comparisons13 // other multiplication/division operators.
14 assert(x & y != x + y);14 {
1515 const a = 1 + 2 % 3;
16 // Or's and and's may be freely chained, but not with each other16 const b = 1 + (2 % 3);
17 assert(false or x == y or x + y == z);17 try std.testing.expectEqual(a, b);
18 assert(true and x != y and y != z);18
19 // The following line would be a compile error19 // `1 % 2 % 3`, `1 % 2 / 3`, etc. would be a compile error,
20 // assert(false or x != y and y != z);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;
21}84}
2285
23// test
\ No newline at end of file
86// test