Ruby Operator Precedence Table

Ruby operator precedence table

Ruby 2.1.0, 2.0, 1.9, 1.8

An operator is a token that represents an operation (such as addition or comparison) to be performed on one or more operands. The operands are expressions, and operators allow us to combine these operand expressions into larger expressions. (Ref)

N = arity = The number of operands the operator operates on. (Ref)

A = associativity = The order of evaluation when the same operator (or operators with the same precedence) appear sequentially in an expression. The value L means that expressions are evaluated from left to right. The value R means that expressions are evaluated from right to left. And the value N means that the operator is nonassociative and cannot be used multiple times in an expression without parentheses to specify the evaluation order. (Ref)

M = definability = Ruby implements a number of its operators as methods, allowing classes to define new meanings for those operators. Column M of specifies which operators are methods. Operators marked with a Y are implemented with methods and may be redefined, and operators marked with an N may not. (Ref)

The following table is ordered according to descending precedence (highest precedence at the top).

N A M  Operator(s)            Description
- - - ----------- -----------
1 R Y ! ~ + boolean NOT, bitwise complement, unary plus
(unary plus may be redefined from Ruby 1.9 with +@)

2 R Y ** exponentiation
1 R Y - unary minus (redefine with -@)

2 L Y * / % multiplication, division, modulo (remainder)
2 L Y + - addition (or concatenation), subtraction

2 L Y << >> bitwise shift-left (or append), bitwise shift-right
2 L Y & bitwise AND

2 L Y | ^ bitwise OR, bitwise XOR (exclusive OR)
2 L Y < <= >= > ordering

2 N Y == === != =~ !~ <=> equality, pattern matching, comparison
(!= and !~ may not be redefined prior to Ruby 1.9)

2 L N && boolean AND
2 L N || boolean OR

2 N N .. ... range creation (inclusive and exclusive)
and boolean flip-flops

3 R N ? : ternary if-then-else (conditional)
2 L N rescue exception-handling modifier

2 R N = assignment
2 R N **= *= /= %= += -= assignment
2 R N <<= >>= assignment
2 R N &&= &= ||= |= ^= assignment

1 N N defined? test variable definition and type
1 R N not boolean NOT (low precedence)
2 L N and or boolean AND, boolean OR (low precedence)
2 N N if unless while until conditional and loop modifiers

Ruby operator precedence

If x is integer, then x**2.to_s is x.**(2.to_s). Because Fixnum#** method exist. In your case ** is a method, not an operator.

In this case, no operator precedence and associativity comes into existence.

In the expression - x.**(2.to_s)

  • x is Fixnum object.

  • ** is a method called on x.

  • 2.to_s is simply an expression, which will be evaluated first, and passed as an argument to the method **.

My answer is as per your example. But there are several edge cases in Ruby. Just to get the taste of those, give some time to read this thread why does a + f b not parse?.

ruby operator precedence issue with shift operator

<< operator on arrays returns the array. So if party is nil, nil gets pushed to arr and then party[3] is evaluated as part of boolean expression (because left part of the expression is truthy and didn't short-circuit). Since party is nil, an error will be raised here.

Actually, << operator gets resolved to << method of Array class. Which is very much like push. But it, being an operator, has different precedence, yes. If you were to use a method, it'd work more like you expect.

arr.push party && party[3] # => [nil]
arr.<< party && party[3] # => [nil]

Me, I put parentheses everywhere where there's even a shadow of doubt.

arr.push(party && party[3])

Also, not sure about intentions, but there might be a little bug in your code. The line above will always push something to arr. One of false, nil and party[3]. If you want to not do anything with a falsy value, then it's better to do something like this

arr << party[3] if party

What's the precedence of method calls with and without parentheses?

Prelude

This aims to test all possible scenarios.

Note that when saying "operator X has higher precedence than method invocation" what is meant is in arguments. Aka:

invocation foo X bar

as opposed to (call on object)

X invocation

As far as the second case is concerned, method calls always have higher precedence.


Short answer

It doesn't fit:

  • It causes SyntaxError in some cases
  • It has higher precedence than rescue, but lower than assignment

Summary

  • not can't be used after method invocation regardless of brackets
  • Using brackets (()) with method invocations sometimes causes a SyntaxError. These cases are: and, or, if, unless, until, while and rescue
  • In cases when brackets don't cause an error, they don't change the precedence in any way
  • All operators, except for and, or, postfix if, unless, until, while, rescue have higher precedence than method invocation

Lets try it:

class Noone < BasicObject
undef_method :!

def initialize(order)
@order = order
end

def method_missing(name, *args)
@order << name
self
end
end

First unary:

# + and - will become binary
unary_operators = %i(! ~ not defined?)

puts 'No brackets'
unary_operators.each do |operator|
puts operator

order = []
foo = Noone.new order
bar = Noone.new order
begin
eval("foo.meta #{operator} bar")
rescue SyntaxError => e
puts e
end
p order
puts '-----------'
end

puts 'Brackets'
unary_operators.each do |operator|
puts operator

order = []
foo = Noone.new order
bar = Noone.new order
begin
eval("foo.meta(#{operator} bar)")
rescue SyntaxError => e
puts e
end
p order
puts '-----------'
end

Points taken:

  • not after a method invocation is a SyntaxError
  • all unary operators have higher precedence than method invocation regardless of brackets

Now binary:

binary_operators = %i(
**
* / %
+ -
<< >>
&
| ^
> >= < <=
<=> == === =~
.. ...
or and
)

puts 'No brackets'
binary_operators.each do |operator|
order = []
foo = Noone.new order
bar = Noone.new order
baz = Noone.new order
begin
eval("foo.meta bar #{operator} baz")
rescue SyntaxError => e
puts e
end
p order
end

puts 'Brackets'
binary_operators.each do |operator|
order = []
foo = Noone.new order
bar = Noone.new order
baz = Noone.new order
begin
eval("foo.meta( bar #{operator} baz)")
rescue SyntaxError => e
puts e
end
p order
end

Points taken:

  • brackets around method invocation with and or or is a SyntaxError
  • we have to test and and or further without brackets
  • .. and ... call <=>. We have to test this further
  • we couldn't test a few other binary operators this way, namely &&, ||, ==, !=, modifier rescue, if, unless, until, while
  • other than the above mentioned, operators have higher precedence, regardless of brackets

def yes
puts 'yes'
true
end

def no
puts 'no'
false
end

def anything(arg)
puts 'Anything'
arg
end

anything yes and no
anything no or yes
anything yes && no
anything no || yes
anything(yes && no)
anything(no || yes)

anything yes == no
anything(yes == no)
anything yes != no
anything(yes != no)

Points taken:

  • and and or have lower precedence without brackets
  • &&, ||, == and != have higher precedence regardless of brackets

def five(*args)
p args
5
end

five 2..7
five(2..7)
five 2...7
five(2...7)

Points taken:

  • .. and ... have higher precedence regardless of brackets

anything yes if no
anything(yes if no)
anything no unless yes
anything(no unless yes)

anything no until yes
anything(no until yes)
anything yes while no
anything(yes while no)

Points taken:

  • brackets with if, unless, until, while cause a SyntaxError
  • all of the above have lower precedence than method invocation without brackets

def error
puts 'Error'
raise
end

anything error rescue yes
anything(error rescue yes)

Points taken:

  • brackets around rescue cause a SyntaxError
  • rescue has lower precedence if no brackets are present

Ternary:

anything yes ? no : 42
anything(yes ? no : 42)

Points taken:

  • ternary has higher precedence regardless of brackets

Assignment (left for last as it changes yes and no):

anything yes = no
anything(no = five(42))

Points taken:

  • Assignment has higher precedence than invocation

Note that += and the like are just shortcuts for + and = so they exhibit the same behaviour.

Operator precedence of assignment and conditional operators

Higher precedence of assignment means that your expression evaluates to (x = 5) if false, and not to x = (5 if false). Note, that later is a perfectly valid expression too.

Whether each particular clause is executed is determined by language rules. E.g., in a ternary operator a ? b : c, only b or c will be executed, but not both.

edit

About the difference.

In x = (5 if false), assignment is processed first. But to complete it, we need left part of assignment, which is nil, because 5 if false evaluates to nil. So, the expression is equivalent of x = nil.

In (x = 5) if false, conditional operator is processed first. According to its rules, we first have to evaluate condition (false). Since it's false, there's nothing more to do and result of evaluation is nil.

Hope that's clear.



Related Topics



Leave a reply



Submit