Project

General

Profile

Actions

Bug #9931

closed

irb: Really weird behavior for x = "#{x\"}" (ex: irb(main:009:-4))

Added by jesserayadkins (Jesse Adkins) almost 10 years ago. Updated about 3 years ago.

Status:
Closed
Target version:
-
ruby -v:
ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-linux]
[ruby-core:63098]
Tags:

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.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0