Commit e318320
Changed files (8)
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