| author | |
| committer | |
| log | 723c70fd438a6e9221155ff632a9974e30a7fe14 |
| tree | 30d71ad27dabc8d2238990d5f8ba2c5831f8a521 |
| parent | 04e8a5e42b58f1fdeb749caf22a1c998344b3ba8 |
9 files changed, 161 insertions(+), 165 deletions(-)
doc/langref.html.in+67-10| ... | ... | @@ -1894,20 +1894,77 @@ const B = error{Two}; |
| 1894 | 1894 | </div> |
| 1895 | 1895 | {#header_close#} |
| 1896 | 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 | 1914 | <pre>{#syntax#}x() x[] x.y x.* x.? |
| 1898 | 1915 | a!b |
| 1899 | 1916 | x{} |
| 1900 | !x -x -%x ~x &x ?x try | |
| 1901 | * / % *% *| || | |
| 1902 | + - ++ +% -% +| -| | |
| 1903 | << >> <<| | |
| 1904 | & ^ | orelse catch | |
| 1905 | == != < > <= >= | |
| 1906 | and | |
| 1907 | or | |
| 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> | |
| 1910 | 1954 | |
| 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 | 1968 | {#header_close#} |
| 1912 | 1969 | |
| 1913 | 1970 | {#header_open|Arrays#} |
doc/langref/ambiguous_precedence_mul_bitand.zig deleted-11| ... | ... | @@ -1,11 +0,0 @@ |
| 1 | test "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 @@ |
| 1 | test "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 @@ |
| 1 | fn makeBool(x: u32) ?bool { | |
| 2 | if (x == 0) return false; | |
| 3 | if (x == 1) return true; | |
| 4 | return null; | |
| 5 | } | |
| 6 | ||
| 7 | test "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 @@ |
| 1 | test "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 @@ |
| 1 | fn 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 @@ |
| 1 | const expectEqual = @import("std").testing.expectEqual; | |
| 2 | const expect = @import("std").testing.expect; | |
| 3 | ||
| 4 | test "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 @@ |
| 1 | fn 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 | const assert = @import("std").debug.assert; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | 3 | test "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 | ||
| 82 | fn maybe() error{Fail}!?u8 { | |
| 83 | return null; | |
| 21 | 84 | } |
| 22 | 85 | |
| 23 | // test | |
| \ No newline at end of file | ||
| 86 | // test |