# Precedence Two pieces, in this order: the full precedence table, and the parse rule for *named unary operators* — built-in functions whose syntax binds at operator precedence rather than as full list-operator calls. ## The full table Operators tighter at the top, looser at the bottom. Same row = same precedence; combining two same-row operators uses associativity. `none` = non-associative (combining two of them without parens is an error or a parse-time warning). | Row | Associativity | Operators | |-------|-----------------|---------------------------------------| | 1 | left | terms, list operators (leftward) | | 2 | left | `->` | | 3 | none | `++` `--` | | 4 | right | `**` | | 5 | right | `!` `~` `\` unary `+` unary `-` | | 6 | left | `=~` `!~` | | 7 | left | `*` `/` `%` `x` | | 8 | left | `+` `-` `.` | | 9 | left | `<<` `>>` | | 10 | none | named unary operators | | 11 | none | `<` `<=` `>` `>=` `lt` `le` `gt` `ge` | | 12 | none | `==` `!=` `<=>` `eq` `ne` `cmp` | | 13 | left | `&` | | 14 | left | `|` `^` | | 15 | left | `&&` | | 16 | left | `||` `//` | | 17 | none | `..` `...` | | 18 | right | `?:` | | 19 | right | `=` `+=` `-=` `*=` `/=` etc. | | 20 | left | `,` `=>` | | 21 | none | list operators (rightward) | | 22 | right | `not` | | 23 | left | `and` | | 24 | left | `or` `xor` | A few rules of thumb: - **Comparison and logic.** Comparison (rows 11–12) is tighter than `&&`/`||`/`//` (rows 15–16), so `$a < $b && $c > $d` works without inner parens. - **Bitwise vs comparison.** `&`, `|`, `^` (rows 13–14) are *looser* than comparison. `$x & 0xFF == 0` parses as `$x & (0xFF == 0)`. Almost always parenthesise around bitwise. - **Symbolic vs word logic.** `&&`/`||`/`!` are tight (rows 5, 15, 16). `and`/`or`/`not` are very loose (rows 22–24). Same logical meaning; different binding. The [logical](logical.md) chapter shows where each is appropriate. - **`=` is loose.** Row 19 — almost everything binds tighter than assignment. This is why `my $fh = open ... or die` works: `or` (row 24) is even looser than `=`, so the open finishes assigning before `or` decides. - **List operators bookend the table.** Row 1 looking *leftward*, row 21 looking *rightward*. A list operator like `print` or `sort` slurps everything to its right, up to the first operator that binds looser than row 21 — which is just `not`/`and`/`or`/`xor`. The [named unary]() section below contrasts this with the row-10 single-argument case. When in doubt, write parentheses. They cost nothing and document intent. ## Post and pre increment / decrement Row 3 (`++` and `--`) is non-associative. The pre and post forms differ in *when* the side-effect happens: - **`++$x`** — increment first, then yield the new value. - **`$x++`** — yield the current value, then increment. ```perl my $x = 5; my $a = ++$x; # $x is now 6, $a is 6 my $b = $x++; # $a was 6, $x++ yields 6 then increments, # so $b is 6 and $x is now 7 ``` `++` on a string performs Perl’s *magic increment*: ```perl my $s = "aa"; $s++; # "ab" $s++; # "ac" # ... $s = "az"; $s++; # "ba" — carries like base-26 $s = "Aa"; $s++; # "Ab" — case is preserved per-position $s = "Az9"; $s++; # "Ba0" — digits and letters carry independently ``` The magic-increment is what makes `'a'..'zz'` produce all 702 two-letter combinations in [range](range.md). ## Named unary operators Row 10 contains a category called *named unary operators*. These are not symbolic operators — they are **built-in functions** that take exactly one argument and parse with operator-style precedence (between bitshifts and comparison) rather than as full list operators. The practical consequence is one parsing rule: > A named unary operator grabs its **one** argument greedily, > and anything that would need more than one argument is not > part of it. ```perl defined $x + 1 # parses as: ( defined($x) ) + 1 # — NOT defined($x + 1) length $s > 3 # parses as: ( length($s) ) > 3 -e $f && -r _ # both file tests are named-unary; # `&&` combines two boolean results ``` Compare with a list operator (`print`, `sort`, `push`, …), which is at row 1 / 21 and slurps the rest of the expression: ```perl print "n=", $n, "\n" # print's argument list is "n=", $n, "\n" # all three commas belong to print ``` If you want a named-unary built-in to take a more complex argument, parenthesise: ```perl defined($x + 1) # explicit: argument is the sum $x+1 length($s . "x") # length of $s with "x" appended ``` The unambiguous form is always available. When in doubt, parenthesise. ### The list of named unary built-ins These built-ins parse at row-10 precedence. Each links to its [`perlfunc`](../perlfunc.md) detail page — the parsing rule above is the only thing you need to know about them as *operators*; their *function* behaviour is in the perlfunc reference. [`defined`](../perlfunc/defined.md), [`exists`](../perlfunc/exists.md), [`ref`](../perlfunc/ref.md), [`scalar`](../perlfunc/scalar.md), [`length`](../perlfunc/length.md), [`uc`](../perlfunc/uc.md), [`lc`](../perlfunc/lc.md), [`ucfirst`](../perlfunc/ucfirst.md), [`lcfirst`](../perlfunc/lcfirst.md), [`chr`](../perlfunc/chr.md), [`ord`](../perlfunc/ord.md), [`hex`](../perlfunc/hex.md), [`oct`](../perlfunc/oct.md), [`int`](../perlfunc/int.md), [`abs`](../perlfunc/abs.md), [`sqrt`](../perlfunc/sqrt.md), [`sin`](../perlfunc/sin.md), [`cos`](../perlfunc/cos.md), [`log`](../perlfunc/log.md), [`exp`](../perlfunc/exp.md), [`rand`](../perlfunc/rand.md), [`srand`](../perlfunc/srand.md), [`alarm`](../perlfunc/alarm.md), [`sleep`](../perlfunc/sleep.md), [`caller`](../perlfunc/caller.md), [`wantarray`](../perlfunc/wantarray.md), [`chroot`](../perlfunc/chroot.md), [`readlink`](../perlfunc/readlink.md), [`umask`](../perlfunc/umask.md), [`lstat`](../perlfunc/lstat.md), [`stat`](../perlfunc/stat.md) (when given exactly one argument), plus all the **file test operators** (`-e`, `-r`, `-w`, `-x`, `-f`, `-d`, `-l`, `-s`, and the rest — see the *file test operators* section below). The «operator» framing in classical Perl documentation is historical. In modern Perl terms, these are *functions whose argument-parsing places them between bitshift and comparison on the precedence table*. The naming is what it is; the behaviour is what matters. ### File test operators File tests are unary operators (named-unary precedence) that inspect a property of a file or filehandle. They begin with a hyphen and a single letter. All return boolean except `-s` (file size in bytes), `-M`/`-A`/`-C` (age in days, fractional), and the read-time tests `-T`/`-B` (boolean but slightly slower because they read the file). | Op | Test | Returns | |------|--------------------------------------------|------------------------| | `-e` | exists | boolean | | `-r` | readable by effective UID | boolean | | `-w` | writable by effective UID | boolean | | `-x` | executable by effective UID | boolean | | `-o` | owned by effective UID | boolean | | `-R` | readable by real UID | boolean | | `-W` | writable by real UID | boolean | | `-X` | executable by real UID | boolean | | `-O` | owned by real UID | boolean | | `-z` | zero size | boolean | | `-s` | non-zero size | size in bytes or false | | `-f` | regular file | boolean | | `-d` | directory | boolean | | `-l` | symbolic link | boolean | | `-p` | named pipe (FIFO) | boolean | | `-S` | socket | boolean | | `-b` | block special file | boolean | | `-c` | character special file | boolean | | `-t` | TTY (terminal) | boolean | | `-u` | setuid bit | boolean | | `-g` | setgid bit | boolean | | `-k` | sticky bit | boolean | | `-T` | text file (heuristic) | boolean | | `-B` | binary file (heuristic) | boolean | | `-M` | modification time (script-start age, days) | number | | `-A` | access time (days) | number | | `-C` | inode change time (days) | number | ```perl if (-e $file) { ... } # exists if (-f $path && -r $path) { ... } # regular file and readable my $size = -s $file; # size in bytes if (-d $path) { ... } # is a directory ``` **Stacked file tests.** `-f -w -r $file` is shorthand for `(-f $file) && (-w $file) && (-r $file)` — the chain re-uses the same target. The pseudo-handle `_` (single underscore) re-uses the *previous* file test’s `stat` cache to avoid extra `stat()` syscalls: ```perl if (-f $file && -r _) { ... } # one stat() call, two checks ``` The trailing `_` is the standard idiom for any chain of file tests on the same path. ## See also - Every other page in this reference links into the rows of this table from its own *Precedence* section. The cross-reference is intentional — when you need to combine an operator with another, this table is the answer. - [perlfunc index](../perlfunc.md) — every name in the named-unary list is documented there.