Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weird syntax error with parens

Tags:

Other than hashes as last argument, you can drop parenthesis in Ruby on method invocations and get consistent results (you still have to mind the priority).

However, I encountered an example where this is not the case:

''.split(/ ./) # => []
''.split /./   # => []
''.split / ./  # !> SyntaxError: unexpected '.'

Is this a bug/regression (I tested it with 2.1.2 -> 2.4.1 Rubys)?

Are there other generic cases where dropping the parens doesn't work as expected?


Reported it, lets see.


Update: The ticket was rejected a bit ambiguously. It's not clear if it's a bug or not, but it won't get fixed and using %r{} in these situations was suggested. The cause is indeed that the opening slash is interpreted as division.

like image 382
ndnenkov Avatar asked Mar 27 '17 12:03

ndnenkov


2 Answers

In addition to thesecretmaster's answer, let's take a look inside the Ruby parser:

require 'ripper'
require 'pp'

pp Ripper.lex("''.split /./")
# [[[1,  0], :on_tstring_beg,     "'"    ],
#  [[1,  1], :on_tstring_end,     "'"    ],
#  [[1,  2], :on_period,          "."    ],
#  [[1,  3], :on_ident,           "split"],
#  [[1,  8], :on_sp,              " "    ],
#  [[1,  9], :on_regexp_beg,      "/"    ],
#  [[1, 10], :on_tstring_content, "."    ],
#  [[1, 11], :on_regexp_end,      "/"    ]]

Adding a space makes Ruby recognize the / characters as division operators:

pp Ripper.lex("''.split / ./")
# [[[1,  0], :on_tstring_beg, "'"    ],
#  [[1,  1], :on_tstring_end, "'"    ],
#  [[1,  2], :on_period,      "."    ],
#  [[1,  3], :on_ident,       "split"],
#  [[1,  8], :on_sp,          " "    ],
#  [[1,  9], :on_op,          "/"    ],
#  [[1, 10], :on_sp,          " "    ],
#  [[1, 11], :on_period,      "."    ],
#  [[1, 12], :on_op,          "/"    ]]

Are there other generic cases where dropping the parens doesn't work as expected?

Contrived example:

def foo(i = 1)
  10 * i
end

foo(- 2) #=> -20
foo - 2  #=> 8

Another one:

b = 2
def a(arg)
  arg
end

a *b    #=> 2

a = 5

a *b    #=> 10

The first a *b is interpreted as a(*b), whereas the second becomes a * b. Adding parentheses forces Ruby to invoke the method:

a(*b)   #=> 2
like image 108
Stefan Avatar answered Oct 11 '22 07:10

Stefan


While this certainly seems like an issue, it isn't a very significant one because \s in regex will catch spaces. For example:

''.split(/ ./) # => []
''.split /./   # => []
''.split / ./  # !> SyntaxError: unexpected '.'
''.split /\s./ # => []

Or if you want to match only a space you just have to propperly escape it:

''.split /\ ./ # => []

Ruby should parse the arguments the same inside or outside parens, so I'd file a bug, but it's not very urgent and the ruby people may be happy simply leaving it.

Actually, @Stefan mentioned in comments to the questions that it's probably the parser reading it as devision, which is the most likely explanation, so it's not even a bug. But it's one of those fun little ruby quirks.

@Stefan also added that you can create an unambiguous regex literal with the %r{ .} syntax.

like image 45
thesecretmaster Avatar answered Oct 11 '22 08:10

thesecretmaster