Bug #9931
irb: 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 6 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 6 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 1 year ago
- Assignee set to keiju (Keiju Ishitsuka)
- Status changed from Open to Assigned
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 1 year ago
- Assignee changed from keiju (Keiju Ishitsuka) to aycabta (aycabta .)
Updated by jeremyevans0 (Jeremy Evans) about 4 hours 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.