<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[2089] MacRuby/branches/experimental</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.macosforge.org/projects/ruby/changeset/2089">2089</a></dd>
<dt>Author</dt> <dd>mattaimonetti@gmail.com</dd>
<dt>Date</dt> <dd>2009-07-26 19:34:08 -0700 (Sun, 26 Jul 2009)</dd>
</dl>

<h3>Log Message</h3>
<pre>ported the strscan C extension to Ruby and updated the specs to Ruby1.9</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#MacRubybranchesexperimentalspecfrozenlibrarystringscannercheck_specrb">MacRuby/branches/experimental/spec/frozen/library/stringscanner/check_spec.rb</a></li>
<li><a href="#MacRubybranchesexperimentalspecfrozenlibrarystringscannergetch_specrb">MacRuby/branches/experimental/spec/frozen/library/stringscanner/getch_spec.rb</a></li>
<li><a href="#MacRubybranchesexperimentalspecfrozenlibrarystringscannerinitialize_copy_specrb">MacRuby/branches/experimental/spec/frozen/library/stringscanner/initialize_copy_spec.rb</a></li>
<li><a href="#MacRubybranchesexperimentalspecfrozenlibrarystringscannerinitialize_specrb">MacRuby/branches/experimental/spec/frozen/library/stringscanner/initialize_spec.rb</a></li>
<li><a href="#MacRubybranchesexperimentalspecfrozenlibrarystringscannermatchedsize_specrb">MacRuby/branches/experimental/spec/frozen/library/stringscanner/matchedsize_spec.rb</a></li>
<li><a href="#MacRubybranchesexperimentalspecfrozenlibrarystringscannersharedget_byterb">MacRuby/branches/experimental/spec/frozen/library/stringscanner/shared/get_byte.rb</a></li>
<li><a href="#MacRubybranchesexperimentalspecfrozenlibrarystringscannersharedpeekrb">MacRuby/branches/experimental/spec/frozen/library/stringscanner/shared/peek.rb</a></li>
<li><a href="#MacRubybranchesexperimentalspecfrozenlibrarystringscannerskip_specrb">MacRuby/branches/experimental/spec/frozen/library/stringscanner/skip_spec.rb</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#MacRubybranchesexperimentallibstrscanrb">MacRuby/branches/experimental/lib/strscan.rb</a></li>
<li><a href="#MacRubybranchesexperimentalspecfrozenlibrarystringscannersharedeucjprb">MacRuby/branches/experimental/spec/frozen/library/stringscanner/shared/eucjp.rb</a></li>
</ul>

<h3>Removed Paths</h3>
<ul>
<li>MacRuby/branches/experimental/ext/strscan/</li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="MacRubybranchesexperimentallibstrscanrb"></a>
<div class="addfile"><h4>Added: MacRuby/branches/experimental/lib/strscan.rb (0 => 2089)</h4>
<pre class="diff"><span>
<span class="info">--- MacRuby/branches/experimental/lib/strscan.rb                                (rev 0)
+++ MacRuby/branches/experimental/lib/strscan.rb        2009-07-27 02:34:08 UTC (rev 2089)
</span><span class="lines">@@ -0,0 +1,641 @@
</span><ins>+class ScanError &lt; StandardError; end
+
+# StringScanner provides for lexical scanning operations on a String.  Here is
+# an example of its usage:
+#
+#   s = StringScanner.new('This is an example string')
+#   s.eos?               # -&gt; false
+#   
+#   p s.scan(/\w+/)      # -&gt; &quot;This&quot;
+#   p s.scan(/\w+/)      # -&gt; nil
+#   p s.scan(/\s+/)      # -&gt; &quot; &quot;
+#   p s.scan(/\s+/)      # -&gt; nil
+#   p s.scan(/\w+/)      # -&gt; &quot;is&quot;
+#   s.eos?               # -&gt; false
+#   
+#   p s.scan(/\s+/)      # -&gt; &quot; &quot;
+#   p s.scan(/\w+/)      # -&gt; &quot;an&quot;
+#   p s.scan(/\s+/)      # -&gt; &quot; &quot;
+#   p s.scan(/\w+/)      # -&gt; &quot;example&quot;
+#   p s.scan(/\s+/)      # -&gt; &quot; &quot;
+#   p s.scan(/\w+/)      # -&gt; &quot;string&quot;
+#   s.eos?               # -&gt; true
+#   
+#   p s.scan(/\s+/)      # -&gt; nil
+#   p s.scan(/\w+/)      # -&gt; nil
+#
+# Scanning a string means remembering the position of a &lt;i&gt;scan pointer&lt;/i&gt;,
+# which is just an index.  The point of scanning is to move forward a bit at
+# a time, so matches are sought after the scan pointer; usually immediately
+# after it.
+#
+# Given the string &quot;test string&quot;, here are the pertinent scan pointer
+# positions:
+#
+#     t e s t   s t r i n g
+#   0 1 2 ...             1
+#                         0
+#
+# When you #scan for a pattern (a regular expression), the match must occur
+# at the character after the scan pointer.  If you use #scan_until, then the
+# match can occur anywhere after the scan pointer.  In both cases, the scan
+# pointer moves &lt;i&gt;just beyond&lt;/i&gt; the last character of the match, ready to
+# scan again from the next character onwards.  This is demonstrated by the
+# example above.
+#
+# == Method Categories
+#
+# There are other methods besides the plain scanners.  You can look ahead in
+# the string without actually scanning.  You can access the most recent match.
+# You can modify the string being scanned, reset or terminate the scanner,
+# find out or change the position of the scan pointer, skip ahead, and so on.
+# 
+# === Advancing the Scan Pointer
+#
+# - #getch
+# - #get_byte
+# - #scan
+# - #scan_until
+# - #skip
+# - #skip_until
+#
+# === Looking Ahead
+#
+# - #check
+# - #check_until
+# - #exist?
+# - #match?
+# - #peek
+#
+# === Finding Where we Are
+#
+# - #beginning_of_line? (#bol?)
+# - #eos?
+# - #rest?
+# - #rest_size
+# - #pos
+#
+# === Setting Where we Are
+#
+# - #reset
+# - #terminate
+# - #pos=
+# 
+# === Match Data
+#
+# - #matched
+# - #matched?
+# - #matched_size
+# - []
+# - #pre_match
+# - #post_match
+#
+# === Miscellaneous
+#
+# - &lt;&lt;
+# - #concat
+# - #string
+# - #string=
+# - #unscan
+#
+# There are aliases to several of the methods.
+#
+class StringScanner
+  
+  # string &lt;String&gt;::  The string to scan
+  # pos &lt;Integer&gt;:: The position of the scan pointer.  In the 'reset' position, this
+  #     value is zero.  In the 'terminated' position (i.e. the string is exhausted),
+  #     this value is the length of the string.
+  #     
+  #     In short, it's a 0-based index into the string.
+  #     
+  #       s = StringScanner.new('test string')
+  #       s.pos               # -&gt; 0
+  #       s.scan_until /str/  # -&gt; &quot;test str&quot;
+  #       s.pos               # -&gt; 8
+  #       s.terminate         # -&gt; #&lt;StringScanner fin&gt;
+  #       s.pos               # -&gt; 11
+  #
+  # match &lt;String&gt;:: Matched string
+  #
+  attr_reader :string, :pos, :match
+  
+  # This method is defined for backward compatibility.
+  #
+  def self.must_C_version
+    self
+  end  
+  
+  # StringScanner.new(string, dup = false)
+  #
+  # Creates a new StringScanner object to scan over the given +string+.
+  # +dup+ argument is obsolete and not used now.
+  #
+  def initialize(string, dup = false)
+    @string = string
+    @pos = 0
+  end
+  
+  # Duplicates a StringScanner object when dup or clone are called on the
+  # object.
+  #
+  def initialize_copy(orig)
+    @string = orig.string
+    @pos = orig.pos
+    @match = orig.match
+  end 
+  
+  # Reset the scan pointer (index 0) and clear matching data.
+  #
+  def reset
+    @previous_position = self.pos = 0
+    @match = nil
+    self 
+  end
+  
+  # Set the scan pointer to the end of the string and clear matching data.
+  #
+  def terminate
+    @match = nil
+    self.pos = string.size
+    self
+  end
+  
+  # Equivalent to #terminate.
+  # This method is obsolete; use #terminate instead.
+  #
+  def clear
+    # warn &quot;StringScanner#clear is obsolete; use #terminate instead&quot; if $VERBOSE
+    terminate
+  end
+  
+  # Changes the string being scanned to +str+ and resets the scanner.
+  # Returns +str+.
+  #
+  def string=(str)
+    reset
+    @string = str 
+  end
+  
+  # Appends +str+ to the string being scanned.
+  # This method does not affect scan pointer.
+  #
+  #   s = StringScanner.new(&quot;Fri Dec 12 1975 14:39&quot;)
+  #   s.scan(/Fri /)
+  #   s &lt;&lt; &quot; +1000 GMT&quot;
+  #   s.string            # -&gt; &quot;Fri Dec 12 1975 14:39 +1000 GMT&quot;
+  #   s.scan(/Dec/)       # -&gt; &quot;Dec&quot;
+  #
+  def concat(str)
+    raise TypeError, &quot;can't convert #{str.class.name} into String&quot; unless str.class == String
+    self.string &lt;&lt; str
+    self
+  end
+  alias :&lt;&lt; :concat
+  
+  # Modify the scan pointer.
+  #
+  #   s = StringScanner.new('test string')
+  #   s.pos = 7            # -&gt; 7
+  #   s.rest               # -&gt; &quot;ring&quot;
+  #
+  def pos=(n)
+    n = (n + string.size) if (n &lt; 0)
+    raise RangeError, &quot;index out of range&quot; if (n &lt; 0 || (string &amp;&amp; n &gt; string.size))
+    @pos = n
+  end
+  
+  alias :pointer :pos
+  alias :pointer= :pos=
+  
+  # Scans one byte and returns it.
+  # This method is not multibyte sensitive.
+  # See also: #getch.
+  #
+  #   s = StringScanner.new('ab')
+  #   s.get_byte         # =&gt; &quot;a&quot;
+  #   s.get_byte         # =&gt; &quot;b&quot;
+  #   s.get_byte         # =&gt; nil
+  #   
+  #   # encoding: EUC-JP
+  #   s = StringScanner.new(&quot;\244\242&quot;)
+  #   s.get_byte         # =&gt; &quot;244&quot;
+  #   s.get_byte         # =&gt; &quot;242&quot;
+  #   s.get_byte         # =&gt; nil
+  #
+  def get_byte
+    scan(/./mn)
+  end
+  
+  # Equivalent to #get_byte.
+  # This method is obsolete; use #get_byte instead.
+  #
+  def getbyte
+    # warn &quot;StringScanner#getbyte is obsolete; use #get_byte instead&quot; if $VERBOSE
+    get_byte
+  end
+  
+  # Tries to match with +pattern+ at the current position. If there's a match,
+  # the scanner advances the &quot;scan pointer&quot; and returns the matched string.
+  # Otherwise, the scanner returns +nil+.
+  #
+  #   s = StringScanner.new('test string')
+  #   p s.scan(/\w+/)   # -&gt; &quot;test&quot;
+  #   p s.scan(/\w+/)   # -&gt; nil
+  #   p s.scan(/\s+/)   # -&gt; &quot; &quot;
+  #   p s.scan(/\w+/)   # -&gt; &quot;string&quot;
+  #   p s.scan(/./)     # -&gt; nil
+  #
+  def scan(pattern)
+    _scan(pattern, true, true, true)
+  end
+  
+  # Scans the string _until_ the +pattern+ is matched.  Returns the substring up
+  # to and including the end of the match, advancing the scan pointer to that
+  # location. If there is no match, +nil+ is returned.
+  #
+  #   s = StringScanner.new(&quot;Fri Dec 12 1975 14:39&quot;)
+  #   s.scan_until(/1/)        # -&gt; &quot;Fri Dec 1&quot;
+  #   s.pre_match              # -&gt; &quot;Fri Dec &quot;
+  #   s.scan_until(/XYZ/)      # -&gt; nil
+  #
+  def scan_until(pattern)
+    _scan(pattern, true, true, false)
+  end
+  
+  # Tests whether the given +pattern+ is matched from the current scan pointer.
+  # Returns the matched string if +return_string_p+ is true.
+  # Advances the scan pointer if +advance_pointer_p+ is true.
+  # The match register is affected.
+  #
+  # &quot;full&quot; means &quot;#scan with full parameters&quot;.
+  #
+  def scan_full(pattern, succptr, getstr)
+    _scan(pattern, succptr, getstr, true)
+  end
+  
+  # Scans the string _until_ the +pattern+ is matched.
+  # Returns the matched string if +return_string_p+ is true, otherwise
+  # returns the number of bytes advanced.
+  # Advances the scan pointer if +advance_pointer_p+, otherwise not.
+  # This method does affect the match register.
+  #
+  def search_full(pattern, succptr, getstr)
+    _scan(pattern, succptr, getstr, false)
+  end
+  
+  # Scans one character and returns it.
+  # This method is multibyte character sensitive.
+  #
+  #   s = StringScanner.new(&quot;ab&quot;)
+  #   s.getch           # =&gt; &quot;a&quot;
+  #   s.getch           # =&gt; &quot;b&quot;
+  #   s.getch           # =&gt; nil
+  #    
+  #   # encoding: EUC-JP
+  #   s = StringScanner.new(&quot;\244\242&quot;)
+  #   s.getch           # =&gt; &quot;あ&quot;
+  #   s.getch           # =&gt; nil
+  #
+  def getch
+    scan(/./m)
+  end
+  
+  # Returns +true+ if the scan pointer is at the end of the string.
+  #
+  #   s = StringScanner.new('test string')
+  #   p s.eos?          # =&gt; false
+  #   s.scan(/test/)
+  #   p s.eos?          # =&gt; false
+  #   s.terminate
+  #   p s.eos?          # =&gt; true
+  #
+  def eos?
+    self.pos &gt;= self.string.size
+  end
+  
+  # Equivalent to #eos?.
+  # This method is obsolete, use #eos? instead.
+  #
+  def empty?
+    # warn &quot;StringScanner#empty? is obsolete; use #eos? instead&quot; if $VERBOSE
+    eos?
+  end
+  
+  # Returns true iff there is more data in the string.  See #eos?.
+  # This method is obsolete; use #eos? instead.
+  #
+  #   s = StringScanner.new('test string')
+  #   s.eos?              # These two
+  #   s.rest?             # are opposites.
+  #
+  def rest?
+    !eos?
+  end
+  
+  # Returns the &quot;rest&quot; of the string (i.e. everything after the scan pointer).
+  # If there is no more data (eos? = true), it returns &lt;tt&gt;&quot;&quot;&lt;/tt&gt;.
+  #
+  def rest
+    string[pos..-1]
+  end
+  
+  # &lt;tt&gt;s.rest_size&lt;/tt&gt; is equivalent to &lt;tt&gt;s.rest.size&lt;/tt&gt;.
+  #
+  def rest_size
+    rest.size
+  end
+  
+  # &lt;tt&gt;s.restsize&lt;/tt&gt; is equivalent to &lt;tt&gt;s.rest_size&lt;/tt&gt;.
+  # This method is obsolete; use #rest_size instead.
+  #
+  def restsize
+    # warn &quot;StringScanner#restsize is obsolete; use #rest_size instead&quot; if $VERBOSE
+    rest_size
+  end
+  
+  # Returns a string that represents the StringScanner object, showing:
+  # - the current position
+  # - the size of the string
+  # - the characters surrounding the scan pointer
+  #
+  #   s = StringScanner.new(&quot;Fri Dec 12 1975 14:39&quot;)
+  #   s.inspect            # -&gt; '#&lt;StringScanner 0/21 @ &quot;Fri D...&quot;&gt;'
+  #   s.scan_until /12/    # -&gt; &quot;Fri Dec 12&quot;
+  #   s.inspect            # -&gt; '#&lt;StringScanner 10/21 &quot;...ec 12&quot; @ &quot; 1975...&quot;&gt;'
+  #
+  def inspect
+    if defined?(@string)
+      rest = string.size &gt; 5 ? string[pos..pos+4] + &quot;...&quot; : string
+      to_return =  if eos? then
+                    &quot;#&lt;StringScanner fin&gt;&quot;
+                  elsif pos &gt; 0 then
+                    prev = string[0...pos].inspect
+                    &quot;#&lt;StringScanner #{pos}/#{string.size} #{prev} @ #{rest.inspect}&gt;&quot;
+                  else
+                    &quot;#&lt;StringScanner #{pos}/#{string.size} @ #{rest.inspect}&gt;&quot;
+                  end 
+      to_return.taint if self.string.tainted?
+      to_return
+    else
+      &quot;#&lt;StringScanner (uninitialized)&gt;&quot;
+    end
+  end
+  
+  # Tests whether the given +pattern+ is matched from the current scan pointer.
+  # Returns the length of the match, or +nil+.  The scan pointer is not advanced.
+  #
+  #   s = StringScanner.new('test string')
+  #   p s.match?(/\w+/)   # -&gt; 4
+  #   p s.match?(/\w+/)   # -&gt; 4
+  #   p s.match?(/\s+/)   # -&gt; nil
+  #  
+  def match?(pattern)
+    _scan(pattern, false, false, true)
+  end
+  
+  # Returns the last matched string.
+  # 
+  #   s = StringScanner.new('test string')
+  #   s.match?(/\w+/)     # -&gt; 4
+  #   s.matched           # -&gt; &quot;test&quot;
+  #     
+  def matched
+    match.to_s if matched?
+  end
+  
+  # Returns +true+ iff the last match was successful.
+  #
+  #   s = StringScanner.new('test string')
+  #   s.match?(/\w+/)     # =&gt; 4
+  #   s.matched?          # =&gt; true
+  #   s.match?(/\d+/)     # =&gt; nil
+  #   s.matched?          # =&gt; false
+  #
+  def matched?
+    !match.nil?
+  end 
+  
+  # Returns the last matched string.
+  #
+  #   s = StringScanner.new('test string')
+  #   s.match?(/\w+/)     # -&gt; 4
+  #   s.matched           # -&gt; &quot;test&quot;
+  #
+  def matched_size
+    match.to_s.size if matched?
+  end
+  
+  # Equivalent to #matched_size.
+  # This method is obsolete; use #matched_size instead.
+  #
+  def matchedsize
+    # warn &quot;StringScanner#matchedsize is obsolete; use #matched_size instead&quot; if $VERBOSE
+    matched_size
+  end

+  # Attempts to skip over the given +pattern+ beginning with the scan pointer.
+  # If it matches, the scan pointer is advanced to the end of the match, and the
+  # length of the match is returned.  Otherwise, +nil+ is returned.
+  #
+  # It's similar to #scan, but without returning the matched string.
+  #
+  #   s = StringScanner.new('test string')
+  #   p s.skip(/\w+/)   # -&gt; 4
+  #   p s.skip(/\w+/)   # -&gt; nil
+  #   p s.skip(/\s+/)   # -&gt; 1
+  #   p s.skip(/\w+/)   # -&gt; 6
+  #   p s.skip(/./)     # -&gt; nil
+  #
+  def skip(pattern)
+    _scan(pattern, true, false, true)
+  end
+  
+  # Advances the scan pointer until +pattern+ is matched and consumed.  Returns
+  # the number of bytes advanced, or +nil+ if no match was found.
+  #
+  # Look ahead to match +pattern+, and advance the scan pointer to the _end_
+  # of the match.  Return the number of characters advanced, or +nil+ if the
+  # match was unsuccessful.
+  #
+  # It's similar to #scan_until, but without returning the intervening string.
+  #
+  #   s = StringScanner.new(&quot;Fri Dec 12 1975 14:39&quot;)
+  #   s.skip_until /12/           # -&gt; 10
+  #   s
+  #
+  def skip_until(pattern)
+    _scan(pattern, true, false, false)
+  end
+  
+  # This returns the value that #scan would return, without advancing the scan
+  # pointer.  The match register is affected, though.
+  #
+  #   s = StringScanner.new(&quot;Fri Dec 12 1975 14:39&quot;)
+  #   s.check /Fri/               # -&gt; &quot;Fri&quot;
+  #   s.pos                       # -&gt; 0
+  #   s.matched                   # -&gt; &quot;Fri&quot;
+  #   s.check /12/                # -&gt; nil
+  #   s.matched                   # -&gt; nil
+  #
+  # Mnemonic: it &quot;checks&quot; to see whether a #scan will return a value.
+  #
+  def check(pattern)
+    _scan(pattern, false, true, true)
+  end
+  
+
+  # This returns the value that #scan_until would return, without advancing the
+  # scan pointer.  The match register is affected, though.
+  #
+  #   s = StringScanner.new(&quot;Fri Dec 12 1975 14:39&quot;)
+  #   s.check_until /12/          # -&gt; &quot;Fri Dec 12&quot;
+  #   s.pos                       # -&gt; 0
+  #   s.matched                   # -&gt; 12
+  #
+  # Mnemonic: it &quot;checks&quot; to see whether a #scan_until will return a value.
+  #
+  def check_until(pattern)
+    _scan(pattern, false, true, false)
+  end
+  
+  # Looks _ahead_ to see if the +pattern+ exists _anywhere_ in the string,
+  # without advancing the scan pointer.  This predicates whether a #scan_until
+  # will return a value.
+  #
+  #   s = StringScanner.new('test string')
+  #   s.exist? /s/            # -&gt; 3
+  #   s.scan /test/           # -&gt; &quot;test&quot;
+  #   s.exist? /s/            # -&gt; 6
+  #   s.exist? /e/            # -&gt; nil
+  #
+  def exist?(pattern)
+    _scan(pattern, false, false, false)
+  end
+  
+  # Extracts a string corresponding to &lt;tt&gt;string[pos,len]&lt;/tt&gt;, without
+  # advancing the scan pointer.
+  #
+  #   s = StringScanner.new('test string')
+  #   s.peek(7)          # =&gt; &quot;test st&quot;
+  #   s.peek(7)          # =&gt; &quot;test st&quot;
+  #
+  def peek(length)
+    raise TypeError, &quot;can't convert #{length.class.name} into Integer&quot; unless length.respond_to?(:to_int)
+    raise ArgumentError if length &lt; 0
+    length.zero? ? &quot;&quot; :  string[pos, length]
+  end
+  
+  # Equivalent to #peek.
+  # This method is obsolete; use #peek instead.
+  #
+  def peep(length)
+    # warn &quot;StringScanner#peep is obsolete; use #peek instead&quot; if $VERBOSE
+    peek(length)
+  end
+  
+  # Set the scan pointer to the previous position.  Only one previous position is
+  # remembered, and it changes with each scanning operation.
+  #
+  #   s = StringScanner.new('test string')
+  #   s.scan(/\w+/)        # =&gt; &quot;test&quot;
+  #   s.unscan
+  #   s.scan(/../)         # =&gt; &quot;te&quot;
+  #   s.scan(/\d/)         # =&gt; nil
+  #   s.unscan             # ScanError: unscan failed: previous match record not exist
+  #
+  def unscan
+    raise(ScanError, &quot;unscan failed: previous match record not exist&quot;) if @match.nil?
+    self.pos = @prev_pos
+    @prev_pos = nil
+    @match = nil
+    self    
+  end
+  
+  # Returns +true+ iff the scan pointer is at the beginning of the line.
+  #
+  #   s = StringScanner.new(&quot;test\ntest\n&quot;)
+  #   s.bol?           # =&gt; true
+  #   s.scan(/te/)
+  #   s.bol?           # =&gt; false
+  #   s.scan(/st\n/)
+  #   s.bol?           # =&gt; true
+  #   s.terminate
+  #   s.bol?           # =&gt; true
+  #
+  def bol?
+    (pos == 0) || (string[pos-1] == &quot;\n&quot;)
+  end
+  alias :beginning_of_line? :bol?
+  
+  # Return the n-th subgroup in the most recent match.
+  #
+  #   s = StringScanner.new(&quot;Fri Dec 12 1975 14:39&quot;)
+  #   s.scan(/(\w+) (\w+) (\d+) /)       # -&gt; &quot;Fri Dec 12 &quot;
+  #   s[0]                               # -&gt; &quot;Fri Dec 12 &quot;
+  #   s[1]                               # -&gt; &quot;Fri&quot;
+  #   s[2]                               # -&gt; &quot;Dec&quot;
+  #   s[3]                               # -&gt; &quot;12&quot;
+  #   s.post_match                       # -&gt; &quot;1975 14:39&quot;
+  #   s.pre_match                        # -&gt; &quot;&quot;
+  #
+  def [](n)
+    raise TypeError, &quot;Bad argument #{n.inspect}&quot; unless n.respond_to? :to_int
+    match[n]
+  end
+  
+  # Return the &lt;i&gt;&lt;b&gt;pre&lt;/b&gt;-match&lt;/i&gt; (in the regular expression sense) of the last scan.
+  #
+  #   s = StringScanner.new('test string')
+  #   s.scan(/\w+/)           # -&gt; &quot;test&quot;
+  #   s.scan(/\s+/)           # -&gt; &quot; &quot;
+  #   s.pre_match             # -&gt; &quot;test&quot;
+  #   s.post_match            # -&gt; &quot;string&quot;  
+  #
+  def pre_match
+    string[0...(pos - match.to_s.size)] if matched?
+  end
+  
+  # Return the &lt;i&gt;&lt;b&gt;post&lt;/b&gt;-match&lt;/i&gt; (in the regular expression sense) of the last scan.
+  #
+  #   s = StringScanner.new('test string')
+  #   s.scan(/\w+/)           # -&gt; &quot;test&quot;
+  #   s.scan(/\s+/)           # -&gt; &quot; &quot;
+  #   s.pre_match             # -&gt; &quot;test&quot;
+  #   s.post_match            # -&gt; &quot;string&quot;
+  #
+  def post_match
+    match.post_match if matched?
+  end
+  
+  private
+  
+  def _scan(pattern, succptr, getstr, headonly)
+    raise TypeError, &quot;bad pattern argument: #{pattern.inspect}&quot; unless
+      String === pattern or Regexp === pattern or pattern.respond_to? :to_str

+    @match = nil
+    rest = self.rest
+    
+    return nil if rest_size &lt; 0
+  
+    if headonly
+      headonly_pattern = Regexp.new('^' + pattern.to_s)
+      @match = headonly_pattern.match rest
+    else
+      @match = pattern.match rest
+    end

+    return nil unless @match

+    m = rest[0, @match.end(0)]

+    if succptr
+      @prev_pos = pos
+      self.pos += m.size
+    end
+    
+    getstr ? m : m.size
+  end  
+  
+end
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="MacRubybranchesexperimentalspecfrozenlibrarystringscannercheck_specrb"></a>
<div class="modfile"><h4>Modified: MacRuby/branches/experimental/spec/frozen/library/stringscanner/check_spec.rb (2088 => 2089)</h4>
<pre class="diff"><span>
<span class="info">--- MacRuby/branches/experimental/spec/frozen/library/stringscanner/check_spec.rb        2009-07-26 20:50:01 UTC (rev 2088)
+++ MacRuby/branches/experimental/spec/frozen/library/stringscanner/check_spec.rb        2009-07-27 02:34:08 UTC (rev 2089)
</span><span class="lines">@@ -11,6 +11,6 @@
</span><span class="cx">     @s.matched.should == &quot;This&quot;
</span><span class="cx">     @s.pos.should == 0
</span><span class="cx">     @s.check(/is/).should == nil
</span><del>-    @s.matched.should == nil
</del><ins>+    @s.matched.should == nil   
</ins><span class="cx">   end
</span><span class="cx"> end
</span></span></pre></div>
<a id="MacRubybranchesexperimentalspecfrozenlibrarystringscannergetch_specrb"></a>
<div class="modfile"><h4>Modified: MacRuby/branches/experimental/spec/frozen/library/stringscanner/getch_spec.rb (2088 => 2089)</h4>
<pre class="diff"><span>
<span class="info">--- MacRuby/branches/experimental/spec/frozen/library/stringscanner/getch_spec.rb        2009-07-26 20:50:01 UTC (rev 2088)
+++ MacRuby/branches/experimental/spec/frozen/library/stringscanner/getch_spec.rb        2009-07-27 02:34:08 UTC (rev 2089)
</span><span class="lines">@@ -1,4 +1,5 @@
</span><span class="cx"> require File.dirname(__FILE__) + '/../../spec_helper'
</span><ins>+require File.dirname(__FILE__) + '/shared/eucjp'
</ins><span class="cx"> require 'strscan'
</span><span class="cx"> 
</span><span class="cx"> describe &quot;StringScanner#getch&quot; do
</span><span class="lines">@@ -10,14 +11,15 @@
</span><span class="cx">   end
</span><span class="cx"> 
</span><span class="cx">   it &quot;is multi-byte character sensitive&quot; do
</span><del>-    begin
-        old, $KCODE = $KCODE, 'EUC'
-        s = StringScanner.new(&quot;\244\242&quot;)
-        s.getch.should == &quot;\244\242&quot; # Japanese hira-kana &quot;A&quot; in EUC-JP
-    ensure
-      $KCODE = old
-    end
</del><ins>+    s = StringScanner.new(&quot;あ&quot;) # Japanese hira-kana &quot;A&quot; 
+    s.getch.should == &quot;あ&quot; 
+    s.getch.should be_nil
</ins><span class="cx">   end
</span><ins>+  
+  it &quot;should keep the encoding&quot; do
+    s = StringScanner.new(TestStrings.eucjp)
+    s.getch.encoding.to_s.should == &quot;EUC-JP&quot;
+  end
</ins><span class="cx"> 
</span><span class="cx">   it &quot;returns nil at the end of the string&quot; do
</span><span class="cx">     # empty string case
</span><span class="lines">@@ -30,6 +32,12 @@
</span><span class="cx">     s.getch # skip one
</span><span class="cx">     s.getch.should == nil
</span><span class="cx">   end
</span><ins>+  
+  it &quot;should start from scratch even after a scan was used&quot; do
+    s = StringScanner.new('this is a test')
+    s.scan(/\w+/)
+    s.getch.should == &quot; &quot;
+  end
</ins><span class="cx"> 
</span><span class="cx">   it &quot;does not accept any arguments&quot; do
</span><span class="cx">     s = StringScanner.new('abc')
</span></span></pre></div>
<a id="MacRubybranchesexperimentalspecfrozenlibrarystringscannerinitialize_copy_specrb"></a>
<div class="modfile"><h4>Modified: MacRuby/branches/experimental/spec/frozen/library/stringscanner/initialize_copy_spec.rb (2088 => 2089)</h4>
<pre class="diff"><span>
<span class="info">--- MacRuby/branches/experimental/spec/frozen/library/stringscanner/initialize_copy_spec.rb        2009-07-26 20:50:01 UTC (rev 2088)
+++ MacRuby/branches/experimental/spec/frozen/library/stringscanner/initialize_copy_spec.rb        2009-07-27 02:34:08 UTC (rev 2089)
</span><span class="lines">@@ -8,7 +8,7 @@
</span><span class="cx">   end
</span><span class="cx"> 
</span><span class="cx">   it &quot;is a private method&quot; do
</span><del>-    @s.private_methods.should include(&quot;initialize&quot;)
</del><ins>+    @s.private_methods.should include(:initialize)
</ins><span class="cx">   end
</span><span class="cx"> 
</span><span class="cx">   it &quot;copies the passed StringScanner's content to self&quot; do
</span></span></pre></div>
<a id="MacRubybranchesexperimentalspecfrozenlibrarystringscannerinitialize_specrb"></a>
<div class="modfile"><h4>Modified: MacRuby/branches/experimental/spec/frozen/library/stringscanner/initialize_spec.rb (2088 => 2089)</h4>
<pre class="diff"><span>
<span class="info">--- MacRuby/branches/experimental/spec/frozen/library/stringscanner/initialize_spec.rb        2009-07-26 20:50:01 UTC (rev 2088)
+++ MacRuby/branches/experimental/spec/frozen/library/stringscanner/initialize_spec.rb        2009-07-27 02:34:08 UTC (rev 2089)
</span><span class="lines">@@ -7,7 +7,7 @@
</span><span class="cx">   end
</span><span class="cx"> 
</span><span class="cx">   it &quot;is a private method&quot; do
</span><del>-    @s.private_methods.should include(&quot;initialize&quot;)
</del><ins>+    @s.private_methods.should include(:initialize)
</ins><span class="cx">   end
</span><span class="cx"> 
</span><span class="cx">   it &quot;returns an instance of StringScanner&quot; do
</span></span></pre></div>
<a id="MacRubybranchesexperimentalspecfrozenlibrarystringscannermatchedsize_specrb"></a>
<div class="modfile"><h4>Modified: MacRuby/branches/experimental/spec/frozen/library/stringscanner/matchedsize_spec.rb (2088 => 2089)</h4>
<pre class="diff"><span>
<span class="info">--- MacRuby/branches/experimental/spec/frozen/library/stringscanner/matchedsize_spec.rb        2009-07-26 20:50:01 UTC (rev 2088)
+++ MacRuby/branches/experimental/spec/frozen/library/stringscanner/matchedsize_spec.rb        2009-07-27 02:34:08 UTC (rev 2089)
</span><span class="lines">@@ -6,20 +6,24 @@
</span><span class="cx">   it_behaves_like(:strscan_matched_size, :matchedsize)
</span><span class="cx"> 
</span><span class="cx">   it &quot;warns in verbose mode that the method is obsolete&quot; do
</span><del>-    s = StringScanner.new(&quot;abc&quot;)
-    begin
-      old = $VERBOSE
-      lambda {
-        $VERBOSE = true
-        s.matchedsize
-      }.should complain(/matchedsize.*obsolete.*matched_size/)
</del><ins>+    if Kernel.respond_to?(:warn)
+      s = StringScanner.new(&quot;abc&quot;)
+      begin
+        old = $VERBOSE
+        lambda {
+          $VERBOSE = true
+          s.matchedsize
+        }.should complain(/matchedsize.*obsolete.*matched_size/)
</ins><span class="cx"> 
</span><del>-      lambda {
-        $VERBOSE = false
-        s.matchedsize
-      }.should_not complain
-    ensure
-      $VERBOSE = old
</del><ins>+        lambda {
+          $VERBOSE = false
+          s.matchedsize
+        }.should_not complain
+      ensure
+        $VERBOSE = old
+      end
+    else
+      fail &quot;The tested implementation cannot display warnings.&quot;
</ins><span class="cx">     end
</span><span class="cx">   end
</span><span class="cx"> end
</span></span></pre></div>
<a id="MacRubybranchesexperimentalspecfrozenlibrarystringscannersharedeucjprb"></a>
<div class="addfile"><h4>Added: MacRuby/branches/experimental/spec/frozen/library/stringscanner/shared/eucjp.rb (0 => 2089)</h4>
<pre class="diff"><span>
<span class="info">--- MacRuby/branches/experimental/spec/frozen/library/stringscanner/shared/eucjp.rb                                (rev 0)
+++ MacRuby/branches/experimental/spec/frozen/library/stringscanner/shared/eucjp.rb        2009-07-27 02:34:08 UTC (rev 2089)
</span><span class="lines">@@ -0,0 +1,6 @@
</span><ins>+# encoding: EUC-JP
+module TestStrings
+  def self.eucjp 
+    @eucjp_str = &quot;\244\242&quot;  # Japanese hira-kana &quot;A&quot;
+  end
+end
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="MacRubybranchesexperimentalspecfrozenlibrarystringscannersharedget_byterb"></a>
<div class="modfile"><h4>Modified: MacRuby/branches/experimental/spec/frozen/library/stringscanner/shared/get_byte.rb (2088 => 2089)</h4>
<pre class="diff"><span>
<span class="info">--- MacRuby/branches/experimental/spec/frozen/library/stringscanner/shared/get_byte.rb        2009-07-26 20:50:01 UTC (rev 2088)
+++ MacRuby/branches/experimental/spec/frozen/library/stringscanner/shared/get_byte.rb        2009-07-27 02:34:08 UTC (rev 2089)
</span><span class="lines">@@ -1,3 +1,5 @@
</span><ins>+require 'spec/frozen/library/stringscanner/shared/eucjp'
+
</ins><span class="cx"> describe :strscan_get_byte, :shared =&gt; true do
</span><span class="cx">   it &quot;scans one byte and returns it&quot; do
</span><span class="cx">     s = StringScanner.new('abc5.')
</span><span class="lines">@@ -12,7 +14,15 @@
</span><span class="cx">     s = StringScanner.new(&quot;\244\242&quot;)
</span><span class="cx">     s.send(@method).should == &quot;\244&quot;
</span><span class="cx">     s.send(@method).should == &quot;\242&quot;
</span><ins>+    s.send(@method).should == nil
</ins><span class="cx">   end
</span><ins>+  
+  it &quot;is not multi-byte character sensitive and can handle encodings&quot; do
+    s = StringScanner.new(TestStrings.eucjp)
+    s.send(@method).should == &quot;\244&quot;
+    s.send(@method).should == &quot;\242&quot;
+    s.send(@method).should == nil
+  end
</ins><span class="cx"> 
</span><span class="cx">   it &quot;returns nil at the end of the string&quot; do
</span><span class="cx">     # empty string case
</span></span></pre></div>
<a id="MacRubybranchesexperimentalspecfrozenlibrarystringscannersharedpeekrb"></a>
<div class="modfile"><h4>Modified: MacRuby/branches/experimental/spec/frozen/library/stringscanner/shared/peek.rb (2088 => 2089)</h4>
<pre class="diff"><span>
<span class="info">--- MacRuby/branches/experimental/spec/frozen/library/stringscanner/shared/peek.rb        2009-07-26 20:50:01 UTC (rev 2088)
+++ MacRuby/branches/experimental/spec/frozen/library/stringscanner/shared/peek.rb        2009-07-27 02:34:08 UTC (rev 2089)
</span><span class="lines">@@ -21,5 +21,10 @@
</span><span class="cx"> 
</span><span class="cx">   it &quot;raises a RangeError when the passed argument is a Bignum&quot; do
</span><span class="cx">     lambda { @s.send(@method, bignum_value) }.should raise_error(RangeError)
</span><ins>+  end 
+  
+  it &quot;raises a TypeError when the passed argument is not a Fixnum&quot; do
+    lambda { @s.send(@method, &quot;test&quot;) }.should raise_error(TypeError)
</ins><span class="cx">   end
</span><ins>+    
</ins><span class="cx"> end
</span></span></pre></div>
<a id="MacRubybranchesexperimentalspecfrozenlibrarystringscannerskip_specrb"></a>
<div class="modfile"><h4>Modified: MacRuby/branches/experimental/spec/frozen/library/stringscanner/skip_spec.rb (2088 => 2089)</h4>
<pre class="diff"><span>
<span class="info">--- MacRuby/branches/experimental/spec/frozen/library/stringscanner/skip_spec.rb        2009-07-26 20:50:01 UTC (rev 2088)
+++ MacRuby/branches/experimental/spec/frozen/library/stringscanner/skip_spec.rb        2009-07-27 02:34:08 UTC (rev 2089)
</span><span class="lines">@@ -14,5 +14,5 @@
</span><span class="cx">   it &quot;returns nil if there's no match&quot; do
</span><span class="cx">     @s.skip(/\s+/).should == nil
</span><span class="cx">     @s.skip(/\d+/).should == nil
</span><del>-  end
</del><ins>+  end   
</ins><span class="cx"> end
</span></span></pre>
</div>
</div>

</body>
</html>