TL;DR
<
has higher precedence than &&
, which affects how Ripper tokenizes your expressions. Evaluation happens after tokenizing.
Booleans and S-Expressions
In your example, foo && foo < 5
is tokenized into two expressions:
require 'ripper'
Ripper.sexp 'foo && foo < 5'
#=> [:program,
# [[:binary,
# [:vcall, [:@ident, "foo", [1, 0]]],
# :"&&",
# [:binary, [:vcall, [:@ident, "foo", [1, 7]]], :<, [:@int, "5", [1, 13]]]]]]
So, the parser sees this as functionally equivalent to (foo) && (foo < 5)
because <
has higher precedence than &&
. However, since Ruby uses short-circuit evaluation for Boolean operations, it never evaluates the right-hand side of your Boolean expression unless foo
evaluates to true
.