diff --git a/doc/langref.html.in b/doc/langref.html.in index 959afdbf374e4eec44c150afa97616c564622dcc..df9af26b30173ad8dbf4aae164918fd14ee2781b 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1894,20 +1894,77 @@ const B = error{Two}; {#header_close#} {#header_open|Precedence#} +

+ Zig features partial operator precedence: not every operator has a defined precedence with + every other operator. Mixing operators with ambiguous precedence is prohibited and parentheses + are mandatory. Consequently, valid code is typically auto-descriptive; when reading and + writing Zig code, there are very few precedence rules you'll need to remember. +

+ + {#code|operator_precedence.zig#} + + {#code|ambiguous_precedence_orelse.zig#} + +

+ Simplified operator precedence tables are provided below, each tailored to a general group of + operators. For all of the groups shown below, the operators that have higher precedence are + the same, and themselves have the following precedence table: +

+
{#syntax#}x() x[] x.y x.* x.?
 a!b
 x{}
-!x -x -%x ~x &x ?x try
-* / % *% *| ||
-+ - ++ +% -% +| -|
-<< >> <<|
-& ^ | orelse catch
-== != < > <= >=
-and
-or
-= *= *%= *|= /= %= += +%= +|= -= -%= -|= <<= <<|= >>= &= ^= |={#endsyntax#}
+!x -x -%x ~x &x ?x try{#endsyntax#} + +

+ For the tables below, the operators that have ambiguous precedence or semantic errors with the + given operators have been omitted, as have operators with higher precedence. + The operators colored red and + blue have ambiguous precedence with the other operators + on the same line. Those colored red also have ambiguous + precedence with themselves (they cannot be chained). +

+ +

+ For arithmetic operators: +

+ +
* / *% *| %
++ - +% -% +| -|
+== != < > <= >=
+and or
+ +

+ For bitwise operators: +

+ +
<< <<| >> & ^ |
+== != < > <= >=
+and or
+ +

+ For null and error recovery operators: +

+ +
catch orelse
+ +

+ For error set operators: +

+ +
{#syntax#}||{#endsyntax#}
+ +

+ For array concatenation operators: +

+ +
{#syntax#}++{#endsyntax#}
+ +

+ The full precedence rules can be found in the {#link|Grammar#}, which also includes an {#link|Operator Precedence Flowchart#}. +

+ {#header_close#} - {#header_close#} {#header_open|Arrays#} diff --git a/doc/langref/ambiguous_precedence_mul_bitand.zig b/doc/langref/ambiguous_precedence_mul_bitand.zig deleted file mode 100644 index af3c28cc6c04729e2fd1cec8084c2a1b9afb8ae3..0000000000000000000000000000000000000000 --- a/doc/langref/ambiguous_precedence_mul_bitand.zig +++ /dev/null @@ -1,11 +0,0 @@ -test "ambiguous precedence" { - const x: u8 = 1; - const y: u8 = 2; - const z: u8 = 3; - - // 1. Could be multiplication by a masked value: (x&y) *% z - // 2. Or masking the result of multiplication: x & (y*%z) - _ = x & y *% z; -} - -// obj=ambiguous operator precedence with operators ... & ... *% ...; use parentheses to disambiguate \ No newline at end of file diff --git a/doc/langref/ambiguous_precedence_orelse.zig b/doc/langref/ambiguous_precedence_orelse.zig new file mode 100644 index 0000000000000000000000000000000000000000..dcb9d4cab12f0992400e88c3eccf73711543947d --- /dev/null +++ b/doc/langref/ambiguous_precedence_orelse.zig @@ -0,0 +1,12 @@ +test "ambiguous precedence" { + const x: ?bool = true; + const y: bool = false; + const z: bool = false; + + // The following expression is always considered ambiguous, + // and, in this particular case, `(x orelse y) == z` would + // produce a different result than `x orelse (y == z)`. + _ = x orelse y == z; +} + +// obj=ambiguous operator precedence with operators ... orelse ... == ...; use parentheses to disambiguate diff --git a/doc/langref/ambiguous_precedence_orelse_ne.zig b/doc/langref/ambiguous_precedence_orelse_ne.zig deleted file mode 100644 index b7be5d0c9bdff108e37183365fbcaa59705bb61a..0000000000000000000000000000000000000000 --- a/doc/langref/ambiguous_precedence_orelse_ne.zig +++ /dev/null @@ -1,18 +0,0 @@ -fn makeBool(x: u32) ?bool { - if (x == 0) return false; - if (x == 1) return true; - return null; -} - -test "ambiguous precedence" { - const x: ?u32 = 0; - const y: u8 = 0; - const z: u8 = 0; - - // Without parentheses the compiler cannot distinguish between two conflicting interpretations: - // 1. Compare on the value which may be recovered: (makeBool(x) orelse y) != z - // 2. Recover to the result of the comparison: makeBool(x) orelse (y != z) - _ = makeBool(x) orelse y != z; -} - -// obj=ambiguous operator precedence with operators ... orelse ... != ...; use parentheses to disambiguate \ No newline at end of file diff --git a/doc/langref/ambiguous_precedence_shift_sub.zig b/doc/langref/ambiguous_precedence_shift_sub.zig deleted file mode 100644 index 2410aa950cfcc25060f6d8d341129983f8fd51e3..0000000000000000000000000000000000000000 --- a/doc/langref/ambiguous_precedence_shift_sub.zig +++ /dev/null @@ -1,10 +0,0 @@ -test "ambiguous precedence" { - const x: u64 = 1; - const y: u6 = 63; - - // 1. Could mean shift then subtract: (x<> cannot be repeated without parenthesis nor mixed with other bitwise operators - // The following lines would be compile errors - // _ = x >> 1 >> 2; - // _ = 1 << 16 | 1 << 8 | 1; - // Instead parentheses should be used - try expectEqual(63, (x >> 1) >> 2); - try expectEqual(0x10101, (1 << 16) | (1 << 8) | 1); - - // Comparisons - // < > <= >= == != cannot be chained - // Binds lower than either bitwise operators or arithmetic operators - // which allows one on one side and one on the other - try expect(1 | 4 <= 1 + 4); - // equivilent to this code - try expect((1 | 4) <= (1 + 4)); - - // Boolean Operators - // `and` and `or` can be freely chained with themselves, shortcircuiting left to right - // they may not be mixed with each other without parenthesis - // bind looser than comparisons - const false_expr: bool = 7 < 2; - const true_expr: bool = 1 + 1 == 2; - - try expect(false_expr or false_expr or true_expr); - try expect(7 < 2 or 7 < 2 or 1 + 1 == 2); - try expect(((7 < 2) or (7 < 2)) or (1 + 1 == 2)); - - try expect(true_expr and true_expr and true_expr); - try expect(1 + 1 == 2 and 1 + 1 == 2 and 1 + 1 == 2); - try expect(((1 + 1 == 2) and (1 + 1 == 2)) and (1 + 1 == 2)); - - // The following line would be a compile error - // _ = false_expr or true_expr and true_expr - // Instead parentheses should be used - try expect(false_expr or (true_expr and true_expr)); - - // Orelse and Catch - // Like Boolean Operators, may be chained with themselves, but not each other - // bind looser than prefix expressions - const some_null: ?u8 = null; - try expectEqual(2, some_null orelse some_null orelse 2); - - const some_error: error{invalid}!u8 = error.invalid; - try expectEqual(3, some_error catch some_error catch 3); - - // The following line would be a compile error - // _ = some_error catch some_null orelse 1; - // Instead parentheses should be used - try expectEqual(1, (some_error catch some_null) orelse 1); -} - -// obj \ No newline at end of file diff --git a/doc/langref/nonchaining_sibling_operators.zig b/doc/langref/nonchaining_sibling_operators.zig deleted file mode 100644 index 9873620d783801578cdb5560a0541992be28ab4b..0000000000000000000000000000000000000000 --- a/doc/langref/nonchaining_sibling_operators.zig +++ /dev/null @@ -1,5 +0,0 @@ -fn foo(x: anytype, y: anytype, z: anytype) void { - _ = x << y << z; -} - -// obj=repetition of non-chainable operators ... << ... << ...; use parentheses to disambiguate \ No newline at end of file diff --git a/doc/langref/operator_precedence.zig b/doc/langref/operator_precedence.zig index 9b5a2ed53323e20c44b01d5370de5c811b6ac10a..78d09e2441b8b78654893ff0d3dc2704d49d979b 100644 --- a/doc/langref/operator_precedence.zig +++ b/doc/langref/operator_precedence.zig @@ -1,23 +1,86 @@ -const assert = @import("std").debug.assert; +const std = @import("std"); test "operator precedence" { - const x = 1; - const y = 2; - const z = 3; - - assert(x + y * z == x + (y*z)); - assert(x * y + z == (x*y) + z); - - // Ambiguously related operators may exist in the same expression if they are not adjacent - assert(x*y + x%y == (x*y) + (x%y)); - // Thus includes comparisons - assert(x & y != x + y); - - // Or's and and's may be freely chained, but not with each other - assert(false or x == y or x + y == z); - assert(true and x != y and y != z); - // The following line would be a compile error - // assert(false or x != y and y != z); + // Arithmetic operators follow the conventional order of operations. + { + const a = 1 + 2 * 3 - 4 / 2; + const b = 1 + (2 * 3) - (4 / 2); + try std.testing.expectEqual(a, b); + } + + // Remainder division is a special case. It still has a higher precedence than + // addition and subtraction, but it has ambiguous precedence with itself and + // other multiplication/division operators. + { + const a = 1 + 2 % 3; + const b = 1 + (2 % 3); + try std.testing.expectEqual(a, b); + + // `1 % 2 % 3`, `1 % 2 / 3`, etc. would be a compile error, + // and parentheses would be required to disambiguate. + } + + // Bitwise operators have ambiguous precedence with other bitwise operators. + // However, non-shifting bitwise operators (&, |, ^) can be chained with themselves. + { + const a = 1 ^ 2 ^ 3; + const b = (1 ^ 2) ^ 3; + try std.testing.expectEqual(a, b); + + // `1 ^ 2 | 3`, `1 << 2 << 3`, etc. would be a compile error, + // and parentheses would be required to disambiguate. + + // To mix bitwise operators, parentheses must be used. + try std.testing.expectEqual(0x10101, (1 << 16) | (1 << 8) | 1); + } + + // Comparison operators have lower precedence than both arithmetic and bitwise + // operators. Comparisons cannot be chained. + { + const a = 1 | 4 <= 1 + 4; + const b = (1 | 4) <= (1 + 4); + try std.testing.expectEqual(a, b); + + // `1 == 2 >= 3`, etc. would be a compile error, + // and parentheses would be required to disambiguate. + } + + // Logical operators have lower precedence than comparison operators, + // and may only be chained homogeneously (`and` with `and`, but not + // with `or`, and vice versa). + { + const false_expr: bool = 7 < 2; + const true_expr: bool = 1 + 1 == 2; + + const a = false_expr or false_expr or true_expr; + const b = 7 < 2 or 7 < 2 or 1 + 1 == 2; + const c = ((7 < 2) or (7 < 2)) or (1 + 1 == 2); + try std.testing.expect(a and b and c); + + // `a or b and c`, etc. would be a compile error, + // and parentheses would be required to disambiguate. + } + + // `orelse` and `catch` may be chained with themselves, but not each other. + { + const some_null: ?u8 = null; + + const a = some_null orelse some_null orelse 2; + const b = (some_null orelse some_null) orelse 2; + try std.testing.expectEqual(a, b); + + // `a catch b orelse c`, etc. would be a compile error, + // and parentheses would be required to disambiguate. + + // Prefix operators like `try` have higher precedence than `orelse`/`catch`. + const c = try maybe() orelse 1; + const d = (try maybe()) orelse 1; + try std.testing.expectEqual(c, d); + } +} + +fn maybe() error{Fail}!?u8 { + return null; } -// test \ No newline at end of file +// test