Bug #9931
closedirb: Really weird behavior for x = "#{x\"}" (ex: irb(main:009:-4))
Description
Our journey starts off rather simple:¶
irb(main):001:0> x = "#{x}"
=> ""
Okay, makes sense.¶
irb(main):002:0> x = "#{x"}"
irb(main):003:0"
Hmm...Still in a string? That's weird.¶
irb(main):003:0" "
irb(main):004:0>
Things seem back to normal but...¶
irb(main):004:0> x
Nothing? But x is defined!¶
irb(main):005:0>
Nothing? Hmm...¶
irb(main):005:0> end
irb(main):006:-1> end
Uhh...I don't think that's supposed to do that.¶
irb(main):007:-2> end
irb(main):008:-3> end
irb(main):009:-4>
...I'm afraid to go deeper.¶
Get me out of here!¶
irb(main):009:-4> quit
irb(main):010:-4>
O.O¶
irb(main):010:-4> ^C
irb(main):010:0>
...Can I go now?¶
irb(main):010:0> quit
Success!¶
Additionally, if I put this in a file:¶
x = "#{x"}"
I get two syntax errors (ruby 1.9.3):¶
test.rb:1: syntax error, unexpected $undefined, expecting '}'
x = "#{x"}"
^
test.rb:1: syntax error, unexpected $end, expecting '}'
x = "#{x"}"
^
Updated by jaredbeck (Jared Beck) over 9 years ago
Interesting. Let's try to figure it out!
The irb prompt¶
The prompt has two numbers. The first is obviously a line number, but what is the second number?
irb(main):001:0> "#{
irb(main):002:0> end
irb(main):003:-1> end
irb(main):004:-2> do
irb(main):005:-1> do
irb(main):006:0> }"
SyntaxError: (irb):2: syntax error, unexpected keyword_end
(irb):6: unterminated string meets end of file
from /Users/jared/.rbenv/versions/2.1.2/bin/irb:11:in `<main>'
The second number seems to be a simple estimate of block depth. It must be implemented very simply, if it is capable of being negative.
String interpolation is a block¶
irb(main):013:0> "#{
irb(main):014:0> [1, 2, 3].map { |x| x * 2 }
irb(main):015:0" }"
=> "[2, 4, 6]"
It's just a block! We can put whatever we want in there. :)
Note that on the third line, the prompt character changed from >
to "
, indicating (roughly) that irb
thinks we're inside a string.
The syntax error¶
In the given example
"#{x\"}"
the string is closed at col. 5. The contents of the string are:
#{x\
We can see that the interpolation block was not closed. So, the syntax error
expecting '}'
makes sense.
Updated by jaredbeck (Jared Beck) over 9 years ago
PS: Check out the pitchfork book's (2009 edition) chapter on strings, especially the part about interpolation on p. 110. http://pragprog.com/book/ruby/programming-ruby
Updated by jeremyevans0 (Jeremy Evans) over 4 years ago
- Status changed from Open to Assigned
- Assignee set to keiju (Keiju Ishitsuka)
This appears to still be a problem in the master branch, though the details have changed slightly. You still need Ctrl-C to reset the state after the x = "#{x\"}"
line:
irb(main):001:0> x = "#{x\"}"
irb(main):001:0> ;
irb(main):001:0> 1
irb(main):001:0> nil
irb(main):001:0> # Ctrl-D at this point has no effect
^C
irb(main):001:0> # Ctrl-D after Ctrl-C exits
In nested irb, you can end up with deadlock due to this:
irb(main):001:0> irb
irb#1(main):001:0> x = "#{x\"}"
irb#1(main):001:0>
^C
irb#1(main):001:0> exit
Traceback (most recent call last):
16: from /home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:84:in `block in each_top_level_statement'
15: from /home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:84:in `loop'
14: from /home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:99:in `block (2 levels) in each_top_level_statement'
13: from /home/jeremy/tmp/irb/lib/irb.rb:493:in `block in eval_input'
12: from /home/jeremy/tmp/irb/lib/irb.rb:650:in `signal_status'
11: from /home/jeremy/tmp/irb/lib/irb.rb:496:in `block (2 levels) in eval_input'
10: from /home/jeremy/tmp/irb/lib/irb/context.rb:404:in `evaluate'
9: from /home/jeremy/tmp/irb/lib/irb/workspace.rb:85:in `evaluate'
8: from /home/jeremy/tmp/irb/lib/irb/workspace.rb:85:in `eval'
7: from (irb):1:in `irb_binding'
6: from /home/jeremy/tmp/irb/lib/irb/extend-command.rb:154:in `irb'
5: from /home/jeremy/tmp/irb/lib/irb/extend-command.rb:151:in `irb'
4: from /home/jeremy/tmp/irb/lib/irb/cmd/nop.rb:20:in `execute'
3: from /home/jeremy/tmp/irb/lib/irb/cmd/subirb.rb:20:in `execute'
2: from /home/jeremy/tmp/irb/lib/irb/ext/multi-irb.rb:229:in `irb'
1: from /home/jeremy/tmp/irb/lib/irb/ext/multi-irb.rb:229:in `stop'
fatal (No live threads left. Deadlock?)
1 threads, 1 sleeps current:0x000004f3ae7d1e00 main thread:0x000004f3ae7d1e00
* #<Thread:0x000004f39ef17f70 sleep_forever>
rb_thread_t:0x000004f3ae7d1e00 native:0x000004f3d8a08000 int:0
/home/jeremy/tmp/irb/lib/irb/ext/multi-irb.rb:229:in `stop'
/home/jeremy/tmp/irb/lib/irb/ext/multi-irb.rb:229:in `irb'
/home/jeremy/tmp/irb/lib/irb/cmd/subirb.rb:20:in `execute'
/home/jeremy/tmp/irb/lib/irb/cmd/nop.rb:20:in `execute'
/home/jeremy/tmp/irb/lib/irb/extend-command.rb:151:in `irb'
/home/jeremy/tmp/irb/lib/irb/extend-command.rb:154:in `irb'
(irb):1:in `irb_binding'
/home/jeremy/tmp/irb/lib/irb/workspace.rb:85:in `eval'
/home/jeremy/tmp/irb/lib/irb/workspace.rb:85:in `evaluate'
/home/jeremy/tmp/irb/lib/irb/context.rb:404:in `evaluate'
/home/jeremy/tmp/irb/lib/irb.rb:496:in `block (2 levels) in eval_input'
/home/jeremy/tmp/irb/lib/irb.rb:650:in `signal_status'
/home/jeremy/tmp/irb/lib/irb.rb:493:in `block in eval_input'
/home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:99:in `block (2 levels) in each_top_level_statement'
/home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:84:in `loop'
/home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:84:in `block in each_top_level_statement'
/home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:83:in `catch'
/home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:83:in `each_top_level_statement'
/home/jeremy/tmp/irb/lib/irb.rb:492:in `eval_input'
/home/jeremy/tmp/irb/lib/irb.rb:431:in `block in run'
/home/jeremy/tmp/irb/lib/irb.rb:430:in `catch'
/home/jeremy/tmp/irb/lib/irb.rb:430:in `run'
/home/jeremy/tmp/irb/lib/irb.rb:388:in `start'
/home/jeremy/tmp/irb/exe/irb:11:in `<main>'
Maybe IRB bug!
Updated by hsbt (Hiroshi SHIBATA) about 4 years ago
- Assignee changed from keiju (Keiju Ishitsuka) to aycabta (aycabta .)
Updated by jeremyevans0 (Jeremy Evans) about 3 years ago
- Status changed from Assigned to Closed
As of Ruby 3.0, I think this is fixed:
$ irb30
irb(main):001:0" x = "#{x\"}"
irb(main):002:0"
irb(main):003:0" }
irb(main):004:0" "
Traceback (most recent call last):
3: from /usr/local/bin/irb30:23:in `<main>'
2: from /usr/local/bin/irb30:23:in `load'
1: from /usr/local/lib/ruby/gems/3.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>'
SyntaxError ((irb):1: syntax error, unexpected backslash)
x = "#{x\"}"
^
This behavior makes sense. It's invalid syntax, so it is reasonable that irb would expect that after "#{x\"}"
, you are still in an interpolation inside a string, and as soon as it knows the interpolation has ended, it evaluates the string and you get a syntax error.