Class: Parsby::BackedIO

Inherits:
Object
  • Object
show all
Defined in:
lib/parsby.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io) ⇒ BackedIO

Initializes a BackedIO out of the provided IO object or String. The String will be turned into an IO using StringIO.



426
427
428
429
430
# File 'lib/parsby.rb', line 426

def initialize(io)
  io = StringIO.new io if io.is_a? String
  @io = io
  @backup = Backup.new
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(m, *args, &b) ⇒ Object

This is to provide transparent delegation to methods of underlying IO.



550
551
552
# File 'lib/parsby.rb', line 550

def method_missing(m, *args, &b)
  @io.send(m, *args, &b)
end

Class Method Details

.for(io, &b) ⇒ Object

Makes a new BackedIO out of the provided IO, calls the provided blocked and restores the IO on an exception.



434
435
436
437
438
439
440
441
442
# File 'lib/parsby.rb', line 434

def self.for(io, &b)
  bio = new io
  begin
    b.call bio
  rescue
    bio.restore
    raise
  end
end

.peek(io, &b) ⇒ Object

Similar to BackedIO.for, but it always restores the IO, even when there’s no exception.



446
447
448
449
450
451
452
453
454
# File 'lib/parsby.rb', line 446

def self.peek(io, &b)
  self.for io do |bio|
    begin
      b.call bio
    ensure
      bio.restore
    end
  end
end

Instance Method Details

#colObject



508
509
510
# File 'lib/parsby.rb', line 508

def col
  backup.col
end

#current_lineObject

Returns current line, including what’s to come from #read, without consuming input.



528
529
530
531
# File 'lib/parsby.rb', line 528

def current_line
  load_rest_of_line
  backup.current_line
end

#current_line_posObject

pos == current_line_pos + col. This is needed to convert a pos to a col.



504
505
506
# File 'lib/parsby.rb', line 504

def current_line_pos
  pos - col
end

#current_line_rangeObject



512
513
514
515
# File 'lib/parsby.rb', line 512

def current_line_range
  start = current_line_pos
  PosRange.new start, start + current_line.length
end

#line_numberObject

Returns line number of current line. This is 1-indexed.



479
480
481
# File 'lib/parsby.rb', line 479

def line_number
  lines_read.length
end

#lines_readObject



521
522
523
524
# File 'lib/parsby.rb', line 521

def lines_read
  load_rest_of_line
  backup.back_lines.map(&:chomp)
end

#load_rest_of_lineObject



517
518
519
# File 'lib/parsby.rb', line 517

def load_rest_of_line
  with_saved_pos { readline }
end

#peek(*args) ⇒ Object

Like #read, but without consuming.



466
467
468
# File 'lib/parsby.rb', line 466

def peek(*args)
  with_saved_pos { read(*args) }
end

#posObject

Delegates pos to inner io, and works around pipes’ inability to return pos by getting the length of the innermost BackedIO.



472
473
474
475
476
# File 'lib/parsby.rb', line 472

def pos
  @io.pos
rescue Errno::ESPIPE
  backup.pos
end

#read(*args) ⇒ Object

Reads from underlying IO and backs it up.



559
560
561
# File 'lib/parsby.rb', line 559

def read(*args)
  @io.read(*args).tap {|r| backup.write r unless r.nil? }
end

#readline(*args) ⇒ Object



554
555
556
# File 'lib/parsby.rb', line 554

def readline(*args)
  @io.readline(*args).tap {|r| backup.write r unless r.nil? }
end

#restore(n = backup.back_size) ⇒ Object

Restore n chars from the backup.



534
535
536
537
538
539
540
541
542
# File 'lib/parsby.rb', line 534

def restore(n = backup.back_size)
  # Handle negatives in consideration of #with_saved_pos.
  if n < 0
    read(-n)
  else
    backup.back(n).chars.reverse.each {|c| ungetc c}
  end
  nil
end

#restore_to(prev_pos) ⇒ Object



544
545
546
# File 'lib/parsby.rb', line 544

def restore_to(prev_pos)
  restore(pos - prev_pos)
end

#seek(amount, whence = IO::SEEK_SET) ⇒ Object



483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
# File 'lib/parsby.rb', line 483

def seek(amount, whence = IO::SEEK_SET)
  if whence == IO::SEEK_END
    read
    restore(-amount)
    return
  end
  new_pos = case whence
  when IO::SEEK_SET
    amount
  when IO::SEEK_CUR
    pos + amount
  end
  if new_pos > pos
    read new_pos - pos
  else
    restore_to new_pos
  end
end

#ungetc(c) ⇒ Object

Pass to underlying IO’s ungetc and discard a part of the same length from the backup. As specified with different IO classes, the argument should be a single character. To restore from the backup, use #restore.



567
568
569
570
571
572
573
# File 'lib/parsby.rb', line 567

def ungetc(c)
  # Though c is supposed to be a single character, as specified by the
  # ungetc of different IO objects, let's not assume that when
  # adjusting the backup.
  backup.seek(-c.length, IO::SEEK_CUR)
  @io.ungetc(c)
end

#with_saved_pos(&b) ⇒ Object



456
457
458
459
460
461
462
463
# File 'lib/parsby.rb', line 456

def with_saved_pos(&b)
  saved = pos
  begin
    b.call saved
  ensure
    restore_to saved
  end
end