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.



451
452
453
454
455
# File 'lib/parsby.rb', line 451

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.



575
576
577
# File 'lib/parsby.rb', line 575

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.



459
460
461
462
463
464
465
466
467
# File 'lib/parsby.rb', line 459

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.



471
472
473
474
475
476
477
478
479
# File 'lib/parsby.rb', line 471

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

Instance Method Details

#colObject



533
534
535
# File 'lib/parsby.rb', line 533

def col
  backup.col
end

#current_lineObject

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



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

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.



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

def current_line_pos
  pos - col
end

#current_line_rangeObject



537
538
539
540
# File 'lib/parsby.rb', line 537

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.



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

def line_number
  lines_read.length
end

#lines_readObject



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

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

#load_rest_of_lineObject



542
543
544
# File 'lib/parsby.rb', line 542

def load_rest_of_line
  with_saved_pos { readline }
end

#peek(*args) ⇒ Object

Like #read, but without consuming.



491
492
493
# File 'lib/parsby.rb', line 491

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.



497
498
499
500
501
# File 'lib/parsby.rb', line 497

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

#read(*args) ⇒ Object

Reads from underlying IO and backs it up.



584
585
586
# File 'lib/parsby.rb', line 584

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

#readline(*args) ⇒ Object



579
580
581
# File 'lib/parsby.rb', line 579

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.



559
560
561
562
563
564
565
566
567
# File 'lib/parsby.rb', line 559

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



569
570
571
# File 'lib/parsby.rb', line 569

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

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



508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
# File 'lib/parsby.rb', line 508

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.



592
593
594
595
596
597
598
# File 'lib/parsby.rb', line 592

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



481
482
483
484
485
486
487
488
# File 'lib/parsby.rb', line 481

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