class Sequence
(.… need to steal more features from Array
, String
, File
, StringScanner, Enumerable
?, like:)
pack/unpack
match/matchback
Copyright (C) 2006, 2011 Caleb Clausen Distributed under the terms of Ruby's license.
require 'sequence/split'
Copyright (C) 2006,2008, 2011 Caleb Clausen Distributed under the terms of Ruby's license.
Copyright (C) 2006, 2011 Caleb Clausen Distributed under the terms of Ruby's license.
require 'sequence'
Copyright (C) 2006,2008, 2011 Caleb Clausen Distributed under the terms of Ruby's license.
WeakRefSet
implements an unordered collection of weak references to objects. These references don't prevent garbage collection on these objects. As these objects are thrown away so does their entry in a WeakRefSet
. Immmediate objects are not handled by this class (and wouldn't be useful).
Constants
- SubSequence
- VERSION
Attributes
Public Class Methods
# File lib/sequence.rb, line 61 def [](*x) new(*x) end
# File lib/sequence.rb, line 41 def initialize; abstract end
# File lib/sequence.rb, line 48 def new(seq) case seq when File,IO,Array,String,Enumerable seq.to_sequence else if seq.respond_to? :to_str seq.to_str.to_sequence else raise ArgumentError end end end
Public Instance Methods
Returns a new position
increased by len
(positive or negative).
# File lib/sequence.rb, line 464 def +(other) if ::Sequence===other List[self, other] else position(pos+other) end end
if passed an integer arg, return a new position decreased by len. if passed
a position, return the distance (number or elements) from +other+ (a #position) to +self+. This can be +, -, or 0.
# File lib/sequence.rb, line 456 def -(other) if position?(other) pos-other.pos else position(pos-other) end end
# File lib/sequence.rb, line 639 def <<(x) push x; return self end
# File lib/sequence.rb, line 598 def [](*a) slice(*a) end
# File lib/sequence.rb, line 616 def []=(*a) modify(*a) end
# File lib/sequence.rb, line 679 def _adjust_pos_on_change pos,first,oldsize,newsize # assert newsize != oldsize if pos>=first+oldsize oldsize.zero? and pos==first and return pos pos+newsize-oldsize elsif pos>first first else pos end end
# File lib/sequence.rb, line 243 def _default_maxmatchlen; 1024 end
# File lib/sequence.rb, line 548 def _normalize_pos(pos,size=self.size) if pos<0 pos+=size pos<0 and pos=0 elsif pos>size pos=size end assert((0..size)===pos) pos end
# File lib/sequence.rb, line 560 def _parse_slice_args(*args) asize=args.size assert !closed? size=self.size case r=args.first when Range asize==1 or raise ArgumentError first,last=r.first,r.last first=_normalize_pos(first,size) last=_normalize_pos(last,size) len=last-first r.exclude_end? or len+=1 when Integer asize<=2 or raise ArgumentError first=_normalize_pos(r,size) len=args[1] || (only1=1) when nil asize==0 or raise ArgumentError first=nil len=only1=1 else raise ArgumentError end return first,len,only1 end
# File lib/sequence.rb, line 344 def _pos=(p) abstract end
# File lib/sequence.rb, line 123 def all_data holding_position{|posi| posi.begin! posi.read! } end
push/unshift in stringlike/arraylike
# File lib/sequence.rb, line 643 def append stuff insert(size, stuff) self end
Return a new position
for the beginning.
# File lib/sequence.rb, line 481 def begin position(0) end
go to beginning
# File lib/sequence.rb, line 490 def begin! self._pos=0 end
# File lib/sequence.rb, line 207 def check(pat) holding{scan(pat)} end
# File lib/sequence.rb, line 211 def check_until(pat) holding{scan_until(pat)} end
# File lib/sequence.rb, line 215 def checkback(pat) holding{scanback(pat)} end
# File lib/sequence.rb, line 219 def checkback_until(pat) holding{scanback_until(pat)} end
Close the sequence. This will also close/invalidate every child position or derived sequence.
# File lib/sequence.rb, line 429 def close defined? @change_listeners and @change_listeners.each { |p| Sequence===p and p.close } # this should make just about any operation fail instance_variables.each { |v| remove_instance_variable(v) } nil end
Is this sequence closed?
# File lib/sequence.rb, line 438 def closed? instance_variables.empty? end
# File lib/sequence.rb, line 618 def delete(*args) #index|range, len modify( *args<<new_data) nil end
Performs each just to make this class Enumerable
. self is returned (or the break value if the code does a break).
# File lib/sequence.rb, line 712 def each # :yield: value holding { begin! until eof? yield read1 end or self } end
is there any data in the sequence?
# File lib/sequence.rb, line 534 def empty? size==0 end
Return a new position
for the end.
# File lib/sequence.rb, line 485 def end position(size) end
go to end
# File lib/sequence.rb, line 494 def end! self._pos=size end
are we at past the end of the sequence data, with no more data ever to arrive?
# File lib/sequence.rb, line 529 def eof? abstract end
# File lib/sequence.rb, line 212 def exist?(pat) holding{skip_until(pat)} end
# File lib/sequence.rb, line 220 def existback?(pat) holding{skipback_until(pat) }end
return first element of data
# File lib/sequence.rb, line 539 def first slice 0 end
go to an absolute position; identical to pos=
# File lib/sequence.rb, line 340 def goto p self.pos= p end
hold current position while executing a block. The block is passed the current sequence as its parameter. you can move the position around or call methods like read that do it, but after the block returns, the position is reset to the original location. The return value is the result of the block.
# File lib/sequence.rb, line 259 def holding oldpos=pos begin yield self ensure self.pos=oldpos end end
like holding
, but block is instance_eval'd in the sequence.
# File lib/sequence.rb, line 280 def holding! &block oldpos=pos begin instance_eval self, &block ensure self.pos=oldpos end end
like holding
, but position is reset only if block returns false or nil (or raises an exception).
# File lib/sequence.rb, line 270 def holding? oldpos=pos begin result=yield self ensure (self.pos=oldpos) unless result end end
# File lib/sequence.rb, line 290 def holding_position pos=position begin result=yield self ensure self.position=pos pos.close end end
# File lib/sequence.rb, line 310 def holding_position! &block pos=position begin result=instance_eval self,&block ensure self.position=pos pos.close end end
# File lib/sequence.rb, line 300 def holding_position? pos=position begin result=yield self ensure self.position=pos unless result pos.close end end
# File lib/sequence.rb, line 623 def insert index, replacedata modify index,0, replacedata end
return last element of data
# File lib/sequence.rb, line 544 def last slice( -1) end
# File lib/sequence.rb, line 526 def length; size end
# File lib/sequence.rb, line 208 def match?(pat) holding{skip(pat)} end
# File lib/sequence.rb, line 216 def matchback?(pat) holding{skipback(pat)} end
# File lib/sequence.rb, line 245 def maxmatchlen(backwards) size=self.size list=[ _default_maxmatchlen, backwards ? pos : size-pos%size ] list.push @maxmatchlen if defined? @maxmatchlen list.min end
Similar to slice
except data is written. index
and len
have the same meaning as they do in slice
. len
elements are deleted and replacedata
is inserted. replacedata
is a single item if len is ommitted and 1st param is Fixnum
# File lib/sequence.rb, line 613 def modify(*args) #index|range, len, replacedata abstract end
is there any more data after the position?
# File lib/sequence.rb, line 511 def more_data? #!eof? (size-pos).nonzero? end
move position len
elements, relative to the current position. A negative len
will go in reverse. The (positive) amount actually moved is returned (<len
if reached beginning/end).
# File lib/sequence.rb, line 350 def move(len) oldpos=pos newpos=oldpos+len newpos<0 and newpos=0 goto newpos return (pos-oldpos).abs end
move to end of the remaining elements. reverse=true to move to beginning instead of end The amount moved is returned.
# File lib/sequence.rb, line 360 def move!(reverse=false) reverse ? begin! : end! end
# File lib/sequence.rb, line 500 def nearbegin(len,at=pos) at<=len end
# File lib/sequence.rb, line 506 def nearend(len,at=pos) at+len>=size end
# File lib/sequence.rb, line 697 def notify_change *args #seq, first, oldsize, newsize args[0]=self defined? @change_listeners and @change_listeners.each{|obj| obj.change_notification(*args) } end
# File lib/sequence.rb, line 690 def on_change_notify obj Symbol===obj and raise ArgumentError obj.respond_to? :change_notification or raise ArgumentError @change_listeners||=WeakRefSet[] @change_listeners<<obj end
# File lib/sequence.rb, line 627 def overwrite index, replacedata modify index,replacedata.size, replacedata end
# File lib/sequence.rb, line 631 def pop count=nil slice!(count ? -count...size : -1) end
number of elements from the beginning (0 is at the beginning).
# File lib/sequence.rb, line 322 def pos() abstract end
Set pos
to be p
. When p
is negative, it is set from the end.
# File lib/sequence.rb, line 334 def pos=(p) position?(p) and p=p.pos unless Integer===p self._pos=_normalize_pos p end
this checks to see if p is a valid numeric position.
# File lib/sequence.rb, line 328 def pos?(p) sz=size (-sz..sz)===p end
position
returns a Sequence::Position
to represent a location within this sequence. The argument allows you to specify a numeric location for the position; default is currrent position. If the element that a Position
is anchored to is deleted, that Position
may become invalid or have an unknown behavior.
# File lib/sequence.rb, line 392 def position(_pos=pos) Position.new(self,_pos) end
this queries whether a particular position
p
is valid (is a child or self). numeric positions and also be tested
# File lib/sequence.rb, line 404 def position?(p) case p when Integer; (-size..size)===p when Position; equal? p.data else equal? p end end
Return a new position
for previous location or nil
if we are at the beginning.
# File lib/sequence.rb, line 477 def pred self-1 unless pos.zero? end
# File lib/sequence.rb, line 648 def prepend stuff insert(0, stuff) self end
Get (if no value
) and set properties. Normally, name
should be a symbol. If name
is nil
, it wil get/set using a hash representing all of the properties.
# File lib/sequence.rb, line 367 def prop(name=nil,*value) # :args: (name[,value]) if name.nil? if value.size.zero? defined?(@prop) &&@prop&&@prop.clone else if (value = value[0]).nil? defined?(@prop) &&@prop&&remove_instance_variable(:@prop) else (@prop||={}).replace(value) end end else if value.size.zero? defined?(@prop) &&@prop&&@prop[name] else (@prop||={})[name] = value[0] end end end
attempt to read up to +len+ elements. the position is left just after the data read. #read may return less than the whole requested amount if less data than requested in
len
is available. This can happen at end of file or if more data is simply unavailable currently (ie with a Sequence::IO
). Don't assume that getting less than you requested means you're at end of file; use eof?
to test for that instead.
# File lib/sequence.rb, line 92 def read(len) abstract end
read the remaining elements. if reverse, read everything behind position
# File lib/sequence.rb, line 115 def read!(reverse=false) if reverse readback pos else read rest_size end end
read next element or nil if eof and advance position
# File lib/sequence.rb, line 67 def read1 (read 1)[0] end
like read, but position is left alone.
# File lib/sequence.rb, line 97 def readahead(len) holding{read(len)} end
read element after the pos or nil if eof, leaving position alone
# File lib/sequence.rb, line 77 def readahead1 slice pos end
read data behind the current position, leaving position just before the data read.
# File lib/sequence.rb, line 108 def readback(len) len>pos and len=pos readahead move( -len ) end
read previous element or nil if start of input and move position back
# File lib/sequence.rb, line 72 def readback1 (readback 1)[0] end
read data behind the current position, leaving position unchanged
# File lib/sequence.rb, line 102 def readbehind(len) len>pos and len=pos read move( -len) end
read element before the pos or nil if start of input, leaving position alone
# File lib/sequence.rb, line 82 def readbehind1 slice pos-1 unless pos.zero? end
# File lib/sequence.rb, line 325 def rest_size; size - pos end
make a new sequence that reverses the order of data. reversed and parent sequence share data.
# File lib/sequence.rb, line 423 def reversed Reversed.new self end
# File lib/sequence.rb, line 635 def shift count=nil slice!(count ? 0...count : 0 ) end
Returns the number of elements.
# File lib/sequence.rb, line 523 def size abstract end
def scan(pat)
abstract
end
def scan_until(pat)
abstract
end def scanback(pat)
abstract
end
def scanback_until(pat)
abstract
end
def match(pat)
abstract
end
def matchback(pat)
abstract
end
# File lib/sequence.rb, line 206 def skip(pat) match= scan(pat) and match.length end
# File lib/sequence.rb, line 222 def skip_literal(lits) sz=lits.size lits==readahead(sz) and move sz end
# File lib/sequence.rb, line 210 def skip_until(pat) match= scan_until(pat) and match.length end
# File lib/sequence.rb, line 228 def skip_until_literal(lits) sz=lits.size first=lits[0] holding?{ until eof? skip_until(first) lits==readahead(sz) and break pos end } end
# File lib/sequence.rb, line 214 def skipback(pat) match= scanback(pat) and match.length end
# File lib/sequence.rb, line 218 def skipback_until(pat) match= scanback_until(pat) and match.length end
Provides random access for the sequence like what is in Array/String. index
can be nil
(start at the current location) or a numeric (for pos=
) or a range. len
can be nil
(get a single element) or the number of elements to read
(positive or negative). The sequence's position is left alone.
# File lib/sequence.rb, line 590 def slice(*args) #index|range=nil,len=nil first,len,only1=_parse_slice_args( *args) pos==first and first=nil holding { self.pos = first if first only1 ? read1 : read(len) } end
Like slice
except the element(s) are deleted.
# File lib/sequence.rb, line 602 def slice!(*args) #index|range, len first,len,only1=_parse_slice_args( *args) result=slice(first,len) delete(first,len) only1 ? result.first : result end
# File lib/sequence.rb, line 599 def slice1(idx) slice(idx) end
# File lib/sequence.rb, line 608 def slice1!(idx) slice!(idx) end
make a new sequence out of a subrange of current sequence data. the subseq and parent seq share data, so changes in one will be reflected in the other.
# File lib/sequence.rb, line 415 def subseq(*args) assert !closed? first,len,only1=_parse_slice_args(*args) SubSeq.new(self,first,len) end
Return a new position
for next location or nil
if we are at the end.
# File lib/sequence.rb, line 473 def succ self+1 unless eof? end
# File lib/sequence.rb, line 42 def to_sequence; self end
has any data been seen so far, or are we still at the beginning?
# File lib/sequence.rb, line 517 def was_data? pos.nonzero? end
# File lib/sequence.rb, line 653 def write(data) assert oldpos=pos writeahead(data) assert oldpos==pos move data.size end
# File lib/sequence.rb, line 667 def writeahead(data) raise ArgumentError, "attempted overwrite at end of #{self}" if data.size>rest_size overwrite(pos,data) data.size end
# File lib/sequence.rb, line 660 def writeback(data) assert oldpos=pos writebehind(data) assert oldpos==pos move( -data.size) end
# File lib/sequence.rb, line 673 def writebehind(data) raise ArgumentError, "attempted overwrite at begin of #{self}" if data.size>pos overwrite(pos-data.size,data) data.size end