1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Document the difference between expressions and statements [ci skip]

In the grammar, all expressions are statements, but not all
statements are expressions.  Some parts of the grammar accept
expressions and not other types of statements, which causes
similar looking code to parse differently due to operator
precedence.

Mostly from Dan0042 (Daniel DeLorme).

Fixes [Bug #16092]
This commit is contained in:
Jeremy Evans 2019-10-10 11:25:54 -07:00
parent ddb0267e76
commit 29c1e9a0d4
3 changed files with 67 additions and 5 deletions

View file

@ -76,7 +76,7 @@ for::
expressions}[rdoc-ref:syntax/control_expressions.rdoc]
if::
Used for +if+ and modifier +if+ expressions. See {control
Used for +if+ and modifier +if+ statements. See {control
expressions}[rdoc-ref:syntax/control_expressions.rdoc]
in::
@ -137,7 +137,7 @@ undef::
See {modules and classes}[rdoc-ref:syntax/modules_and_classes.rdoc]
unless::
Used for +unless+ and modifier +unless+ expressions. See {control
Used for +unless+ and modifier +unless+ statements. See {control
expressions}[rdoc-ref:syntax/control_expressions.rdoc]
until::

View file

@ -144,7 +144,7 @@ expression.
== Modifier +if+ and +unless+
+if+ and +unless+ can also be used to modify an expression. When used as a
modifier the left-hand side is the "then" expression and the right-hand side
modifier the left-hand side is the "then" statement and the right-hand side
is the "test" expression:
a = 0
@ -164,7 +164,7 @@ This will print 1.
This will print 0.
While the modifier and standard versions have both a "test" expression and a
"then" expression, they are not exact transformations of each other due to
"then" statement, they are not exact transformations of each other due to
parse order. Here is an example that shows the difference:
p a if a = 0.zero?
@ -439,6 +439,64 @@ longer true, now you will receive a SyntaxError when you use +retry+ outside
of a +rescue+ block. See {Exceptions}[rdoc-ref:syntax/exceptions.rdoc]
for proper usage of +retry+.
== Modifier Statements
Ruby's grammar differentiates between statements and expressions. All
expressions are statements (an expression is a type of statement), but
not all statements are expressions. Some parts of the grammar accept
expressions and not other types of statements, which causes code that
looks similar to be parsed differently.
For example, when not used as a modifier, +if+, +else+, +while+, +until+,
and +begin+ are expressions (and also statements). However, when
used as a modifier, +if+, +else+, +while+, +until+ and +rescue+
are statements but not expressions.
if true; 1 end # expression (and therefore statement)
1 if true # statement (not expression)
Statements that are not expressions cannot be used in contexts where an
expression is expected, such as method arguments.
puts( 1 if true ) #=> SyntaxError
You can wrap a statement in parentheses to create an expression.
puts((1 if true)) #=> 1
If you put a space between the method name and opening parenthesis, you
do not need two sets of parentheses.
puts (1 if true) #=> 1, because of optional parentheses for method
This is because this is parsed similar to a method call without
parentheses. It is equivalent to the following code, without the creation
of a local variable:
x = (1 if true)
p x
In a modifier statement, the left-hand side must be a statement and the
right-hand side must be an expression.
So in <code>a if b rescue c</code>, because <code>b rescue c</code> is a
statement that is not an expression, and therefore is not allowed as the
right-hand side of the +if+ modifier statement, the code is necessarily
parsed as <code>(a if b) rescue c</code>.
This interacts with operator precedence in such a way that:
stmt if v = expr rescue x
stmt if v = expr unless x
are parsed as:
stmt if v = (expr rescue x)
(stmt if v = expr) unless x
This is because modifier +rescue+ has higher precedence than <code>=</code>,
and modifier +if+ has lower precedence than <code>=</code>.
== Flip-Flop
The flip-flop is a rarely seen conditional expression. It's primary use is

View file

@ -49,10 +49,14 @@ Unary <code>+</code> and unary <code>-</code> are for <code>+1</code>,
<code>-1</code> or <code>-(a + b)</code>.
Modifier-if, modifier-unless, etc. are for the modifier versions of those
keywords. For example, this is a modifier-unless expression:
keywords. For example, this is a modifier-unless statement:
a += 1 unless a.zero?
Note that <code>(a if b rescue c)</code> is parsed as <code>((a if b) rescue
c)</code> due to reasons not related to precedence. See {modifier
statements}[control_expressions_rdoc.html#label-Modifier+Statements].
<code>{ ... }</code> blocks have priority below all listed operations, but
<code>do ... end</code> blocks have lower priority.