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.



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

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.



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

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.



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

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.



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

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

Instance Method Details

#colObject



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

def col
  backup.col
end

#current_lineObject

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



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

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.



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

def current_line_pos
  pos - col
end

#current_line_rangeObject



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

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.



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

def line_number
  lines_read.length
end

#lines_readObject



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

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

#load_rest_of_lineObject



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

def load_rest_of_line
  with_saved_pos { readline }
end

#peek(*args) ⇒ Object

Like #read, but without consuming.



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

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.



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

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

#read(*args) ⇒ Object

Reads from underlying IO and backs it up.



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

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

#readline(*args) ⇒ Object



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

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.



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

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



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

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

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



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

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.



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

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



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

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