David A. Black:
p local_variables #=> [“a”]
p a #=> undefined local variable or method `a’
We see that the local exists, but because the parser has not seen
the “a = …” syntax, we can’t access it.
In 1.9 you get:
[]
-:3:in <main>': undefined local variable or methoda’ for
main:Object (NameError)
so I guess that’s been changed.
Yes, in 1.9 the local table is inside the parser, and a new parser is
created for each eval. In 1.8 the local table is static (as well as
the parser).
The point about rb_method_missing (as the source of that error
message) is interesting, though since it’s be handled by a handler
for non-existent methods, and within that handler it determines that
it also isn’t a local variable, I think it’s reasonable to say that
it can’t tell – or, perhaps more accurately, it can tell that it
is neither a (viable) method call nor a variable, but it can’t tell
which you meant (hence the wording of the error message).
No, I don’t see a handler which determines that it isn’t a local
variable. That was already decided by the parser which created a
method-call node because no “a = 1” syntax was found. The possibility
of a local variable was long since gone.
p local_variables #=> [:a]
a = nil
Is Ruby prescient? No, it’s just the parser that determines locals.
Take the 1.9 example again:
eval(“a = 3”)
p local_variables #=> []
p a #=> undefined local variable or method `a’
But
eval(“a = 3”)
p local_variables #=> [:a]
a = a
p a #=> 3
The parser looks for local assignment syntax and builds nodes
accordingly, then the interpreter runs.
There’s also this interesting difference. In 1.8, given this code:
eval(“a=3”)
p a rescue p “undef”
eval(“p a”)
you get this output:
“undef”
3
whereas in 1.9 the output for the same code is:
“undef”
-:3:in eval': undefined local variable or methoda’
So the business of having eval always return to the same scope seems
to have been done away with. (I always thought of it as similar to the
phenomenon of having to be drunk again to remember what you did when
drunk 
It’s not really a scope issue; it’s a parser issue. When we use the
same scope it fails in the same manner,
eval(“a=3”, binding)
p a rescue p “undef” #=> “undef”
eval(“p a”, binding) #=> undefined local variable or method `a’
I don’t know if there is an official terminology–I would say these
two calls to ‘binding’ refer to the same scope while technically being
different bindings.
Curiously, we can get the 1.8 behavior in 1.9 by using the same
binding,
bind = binding
eval(“a=3”, bind)
p a rescue p “undef” #=> “undef”
eval(“p a”, bind) #=> 3