Commits

Kaspar Schiess committed b2268cd

Alternation and lazyness

Comments (0)

Files changed (8)

-# A sample Guardfile
-# More info at https://github.com/guard/guard#readme
+notification :tmux, display_message: true, timeout: 20
 
 guard 'rspec' do
   watch(%r{^spec/.+_spec\.rb$})

examples/alternation.rb

 include Par
 
 def integer s
-  s.cr {
-    s.pattern(/\+|-/).maybe >>
-      s.pattern(/\d+/)
-  }
+  s.pattern(/\+|-/).maybe >>
+    s.pattern(/\d+/)
 end
 def float s
-  s.cr {
-    s.pattern(/\+|-/).maybe >>
-      s.pattern(/\d+/) >> 
-      s.string('.') >> s.pattern(/\d+/)
-  }
+  s.pattern(/\+|-/).maybe >>
+    s.pattern(/\d+/) >> 
+    s.string('.') >> s.pattern(/\d+/)
 end
 def element s
-  float(s) | integer(s)
+  s.either { float(s) }.or { integer(s) }
 end
 
 %w(
 
 require 'par/result'
 require 'par/source'
+require 'par/lazy'
 
 module Par
 
+module Par
+  class Lazy
+    def initialize source, &block
+      @source = source
+      @block = block
+    end
+
+    def or &block
+      other = Lazy.new(@source, &block)
+
+      left = self.value 
+
+      return left if left != Par.bottom
+      return other.value
+    end
+
+    def value 
+      @source.conditional_rewind {
+        @block.call
+      }
+    end
+  end
+end

lib/par/result.rb

       # Wild concatenation with any other object
       return concat(obj, other)
     end
-    def | other
-      self
-    end
     def maybe
       self
     end
       def >> other
         self
       end
-      def | other
-        other
-      end
       def maybe
         Par.top
       end
       def >> other
         other
       end
-      def | other
-        self
-      end
       def maybe
         self
       end

lib/par/source.rb

   # * #n reads n characters
   class Source
     attr_reader :str 
+    attr_reader :pos
 
     def initialize str
       @str = str
     end
     alias :cr :conditional_rewind
 
+    def either &block
+      Lazy.new self, &block
+    end
+
     def head n=nil
       n && str[@pos, n] || str[@pos..-1]
     end

spec/lib/par/lazy_spec.rb

+require 'spec_helper'
+
+describe Par::Lazy do
+  include Par
+
+  let(:source) { Par::Source.new('holler') }
+
+  describe '#either' do
+    it "creates a lazy block" do
+      source.either { raise }
+    end 
+  end
+  describe 'behaviour with bottom and top' do
+    let(:any) { result 'any' }
+
+    it "bottom or any" do
+      source.either { bottom }.or { any }.should == any
+    end
+    it "any or bottom" do
+      source.either { any }.or { bottom }.should == any
+    end
+    it "top or any" do
+      source.either { top }.or { any }.should == top
+    end
+    it "any or top" do
+      source.either { any }.or { top }.should == any
+    end
+  end
+  describe 'rewinding' do
+    it 'rewinds if the block returns bottom' do
+      lazy = source.either { 
+        source.n 4
+        bottom
+      }
+
+      lazy.value.should == bottom
+      source.pos.should == 0
+    end
+  end
+end

spec/lib/par/result_spec.rb

     it "should allow concatenation" do
       (a >> b).to_s.should == 'foobar'
     end
-    it "should allow alternation" do
-      (a | b).should == a
-    end
     it "allow calling maybe" do
       a.maybe.should == a
     end
     it "cannot be concatenated right" do
       (any >> bottom).should == bottom 
     end
-    it "can be alternated left" do
-      (bottom | any).should == any
-    end
-    it "can be alternated right" do
-      (any | bottom).should == any
-    end
     it "can be tested for bottom?" do 
       bottom.should be_bottom
     end
     it 'allows concatenation right' do
       (any >> top).should == any
     end
-    it 'allows alternation' do
-      (any | top).should == any
-      (top | any).should == top
-    end
     it 'can be maybeed' do
       top.maybe.should == top
     end