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:
parent
ddb0267e76
commit
29c1e9a0d4
3 changed files with 67 additions and 5 deletions
|
@ -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::
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue