Commit e318320

mokha <mokha@cisco.com>
2019-05-15 03:12:37
start to implement a small transaction
1 parent 7f7bdb2
lib/mpeg/base.rb
@@ -1,8 +1,7 @@
 module Mpeg
   class Base
-    def parse(string)
-      input = Input.new(string)
-      call(input) && input.end?
+    def parse(string, input: Input.new(string))
+      call(input) && input.end_of_string?
     end
 
     def repeat(min = 0, max = nil)
lib/mpeg/input.rb
@@ -14,8 +14,22 @@ module Mpeg
       Slice.new(position, slice)
     end
 
-    def end?
+    def rewind
+      transaction do |_|
+        yield
+      end
+    end
+
+    def transaction(&block)
+      Transaction.new(@scanner).run(&block)
+    end
+
+    def end_of_string?
       @scanner.eos?
     end
+
+    def position
+      @scanner.pos
+    end
   end
 end
lib/mpeg/lookahead.rb
@@ -4,5 +4,12 @@ module Mpeg
       @parser = parser
       @positive = positive
     end
+
+    def call(input)
+      input.rewind do
+        result = @parser.call(input)
+        @positive ? result : (result ? false : true)
+      end
+    end
   end
 end
lib/mpeg/sequence.rb
@@ -6,7 +6,11 @@ module Mpeg
     end
 
     def call(input)
-      @left.call(input) && @right.call(input)
+      input.transaction do |x|
+        result = @left.call(input) && @right.call(input)
+        x.commit! if result
+        result
+      end
     end
   end
 end
lib/mpeg/transaction.rb
@@ -0,0 +1,19 @@
+module Mpeg
+  class Transaction
+    def initialize(scanner)
+      @scanner = scanner
+      @rewind_position = @scanner.pos
+      @commit = false
+    end
+
+    def commit!
+      @commit = true
+    end
+
+    def run(&block)
+      block.call(self)
+    ensure
+      @scanner.pos = @rewind_position unless @commit
+    end
+  end
+end
lib/mpeg.rb
@@ -11,6 +11,7 @@ require "mpeg/repitition"
 require "mpeg/sequence"
 require "mpeg/slice"
 require "mpeg/str"
+require "mpeg/transaction"
 
 module Mpeg
   class Error < StandardError; end
spec/lookahead_spec.rb
@@ -0,0 +1,14 @@
+RSpec.describe Mpeg::Lookahead do
+  def str(string)
+    Mpeg::Str.new(string)
+  end
+
+  def any
+    Mpeg::Re.new('.')
+  end
+
+  subject { str('"').absent? >> any.repeat }
+
+  specify { expect(subject.parse('"Hello')).to be_falsey }
+  specify { expect(subject.parse('Hello')).to be_truthy }
+end
spec/sequence_spec.rb
@@ -0,0 +1,19 @@
+RSpec.describe Mpeg::Sequence do
+  subject { Mpeg::Str.new('h') >> Mpeg::Str.new('i') }
+
+  context "when the sequence does match" do
+    let(:input) { Mpeg::Input.new('hi')  }
+    let!(:result) { subject.parse('hi', input: input) }
+
+    specify { expect(result).to be_truthy }
+    specify { expect(input).to be_end_of_string }
+  end
+
+  context "when the sequence does not match" do
+    let(:input) { Mpeg::Input.new('hello')  }
+    let!(:result) { subject.parse('hello', input: input) }
+
+    specify { expect(result).to be_falsey }
+    specify { expect(input.position).to be_zero }
+  end
+end