I was reading Perl Cannot Be Parsed: A Formal Proof on PerlMonks over breakfast this morning (this may in itself cause you to worry about me), which introduced me to a clever, ambiguous snippet of Perl constructed by Randall Schwartz:

whatever  / 25 ; # / ; die "this dies!";

How this is parsed depends on what whatever is: if it’s a function that takes an argument, the slashes delimit a regular expression, and the following statement kills the program.

If, on the other hand, whatever is a function without any arguments, its return value is divided by 25, and the rest of the line is a comment.

Since it’s possible to define whatever dynamically, the snippet can’t be parsed without running the program up to that point. Ergo, Perl cannot be parsed statically.

But can we do the same in Ruby? Initially, it seems possible. The disambiguation of slashes has an additional nicety in Matz’s Ruby interpreter: if there’s a space after the opening slash, it will always be interpreted as a division operator. This works, though:

whatever /25#/; raise 'this dies!'

The meaning of the line depends on whether whatever is a method or a variable:

whatever = 100
whatever /25#/; raise 'this dies!'
# completes
def whatever(re) end
whatever /25#/; raise 'this dies!'
# dies

So it looks like we can perform the Perl trick and use code to define what whatever is, thereby creating Ruby code that can’t be parsed. But, in fact, it doesn’t work, because Ruby’s cleverer than that. And by clever, I mean evil. Ruby looks at everything in the current context—even unreachable code—to determine what the symbol whatever refers to. So this works as you’d expect:

if false
  def whatever() 200 end
else
  whatever = 100
end
whatever # => 100

But reverse the logic, and something strange happens:

if true
  def whatever() 200 end
else
  whatever = 100
end
whatever # => nil

Even though the method is defined, and the variable isn’t, the parser has ‘seen’ the variable, and the meaning of the symbol is changed into a variable. The message :whatever is never sent, because it refers to a variable. But that variable isn’t defined! Instead of an error, though, we get nil.

If you’re thinking of getting round it by setting up the definitions in eval, I’ve bad news: that doesn’t work for variables:

eval "def whatever() 200 end"
whatever # => 200
eval "whatever = 100"
whatever # => NameError: undefined local variable or method `whatever' for main:Object

So there you go. Ruby’s syntax may have some apparent ambiguities, but they can be resolved statically. At least, unless there are any others I don’t know about …

Incidentally, it’s testament to the hard work that the JRuby developers have put in that it has exactly the same behaviour.