Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

+ Reenables the cache - now Context has a spec

  • Loading branch information...
commit 0d67a220f088c9f1da2cfa18912c245c785a5613 1 parent 00c4cb0
@kschiess authored
View
65 lib/parslet/atoms/base.rb
@@ -1,3 +1,6 @@
+
+require 'parslet/atoms/context'
+
# Base class for all parslets, handles orchestration of calls and implements
# a lot of the operator and chaining methods.
#
@@ -10,7 +13,7 @@ class Parslet::Atoms::Base
#
def parse(io)
source = Parslet::Source.new(io)
- context = Context.new
+ context = Parslet::Atoms::Context.new
message = catch(:error) {
result = apply(source, context)
@@ -238,66 +241,6 @@ def cause? # :nodoc:
not @last_cause.nil?
end
private
- # Helper class that implements a transient cache that maps position and
- # parslet object to results.
- #
- class Context
- def initialize
- @cache = Hash.new
- end
-
- # Caches a parse answer for obj at source.pos. Applying the same parslet
- # at one position of input always yields the same result, unless the input
- # has changed.
- #
- # We need the entire source here so we can ask for how many characters
- # were consumed by a successful parse. Imitation of such a parse must
- # advance the input pos by the same amount of bytes.
- #
- def cache(obj, source, &block)
- yield
- end
- # beg = source.pos
- #
- # # Not in cache yet? Return early.
- # unless entry = lookup(obj, beg)
- # result = yield
- #
- # set obj, beg, [result, source.pos-beg]
- # return result
- # end
- #
- # # the condition in unless has returned true, so entry is not nil.
- # result, advance = entry
- #
- # source.read(advance)
- # return result
- # end
-
- class Item
- attr_reader :obj, :pos
- def initialize(obj, pos)
- @obj, @pos = obj, pos
- end
- def hash
- @obj.hash - @pos
- end
- def eql?(o)
- o.obj == self.obj && o.pos == self.pos
- end
- end
-
- private
- def lookup(obj, pos)
- i = Item.new(obj, pos)
- @cache[i]
- end
- def set(obj, pos, val)
- i = Item.new(obj, pos)
- @cache[i] = val
- end
- end
-
# Produces an instance of Success and returns it.
#
def success(result)
View
64 lib/parslet/atoms/context.rb
@@ -0,0 +1,64 @@
+
+# Helper class that implements a transient cache that maps position and
+# parslet object to results.
+#
+class Parslet::Atoms::Context
+ def initialize
+ @cache = Hash.new
+ end
+
+ # Caches a parse answer for obj at source.pos. Applying the same parslet
+ # at one position of input always yields the same result, unless the input
+ # has changed.
+ #
+ # We need the entire source here so we can ask for how many characters
+ # were consumed by a successful parse. Imitation of such a parse must
+ # advance the input pos by the same amount of bytes.
+ #
+ def cache(obj, source, &block)
+ beg = source.pos
+
+ # Not in cache yet? Return early.
+ unless entry = lookup(obj, beg)
+ message = catch(:error) {
+ result = yield
+ set obj, beg, [true, result, source.pos-beg]
+ return result
+ }
+
+ set obj, beg, [false, message, source.pos-beg]
+ throw :error, message
+ end
+
+ # the condition in unless has returned true, so entry is not nil.
+ success, obj, advance = entry
+
+ source.read(advance)
+
+ throw :error, obj unless success
+ return obj
+ end
+
+ class Item
+ attr_reader :obj, :pos
+ def initialize(obj, pos)
+ @obj, @pos = obj, pos
+ end
+ def hash
+ @obj.hash - @pos
+ end
+ def eql?(o)
+ o.obj == self.obj && o.pos == self.pos
+ end
+ end
+
+private
+ def lookup(obj, pos)
+ i = Item.new(obj, pos)
+ @cache[i]
+ end
+ def set(obj, pos, val)
+ i = Item.new(obj, pos)
+ @cache[i] = val
+ end
+end
View
2  spec/parslet/atoms/base_spec.rb
@@ -2,7 +2,7 @@
describe Parslet::Atoms::Base do
let(:parslet) { Parslet::Atoms::Base.new }
- let(:context) { Parslet::Atoms::Base::Context.new }
+ let(:context) { Parslet::Atoms::Context.new }
describe "<- #try(io)" do
it "should raise NotImplementedError" do
View
37 spec/parslet/atoms/context_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+require 'stringio'
+
+describe Parslet::Atoms::Context do
+ let(:source) { StringIO.new('foobar') }
+
+ context "when clean" do
+ it "should return whatever the block returns" do
+ subject.cache(self, source) { 1 }.should == 1
+ end
+ it "should rethrow :error" do
+ lambda {
+ subject.cache(self, source) { throw :error, 'foo' }
+ }.should throw_symbol(:error, 'foo')
+ end
+ end
+ context "when already called for obj and input position" do
+ context "(storing success, 1)" do
+ before(:each) { subject.cache(self, source) { 1 } }
+
+ it "should return cached result" do
+ subject.cache(self, source) { 2 }.should == 1
+ end
+ end
+ context "(storing error, 'foo')" do
+ before(:each) { catch(:error) {
+ subject.cache(self, source) { throw :error, 'foo' }} }
+
+ it "should return cached result" do
+ lambda {
+ subject.cache(self, source) { 2 }
+ }.should throw_symbol(:error, 'foo')
+ end
+ end
+ end
+end
View
2  spec/parslet/atoms_spec.rb
@@ -12,7 +12,7 @@ def not_parse
extend Parslet
def src(str); Parslet::Source.new str; end
- let(:context) { Parslet::Atoms::Base::Context.new }
+ let(:context) { Parslet::Atoms::Context.new }
describe "match('[abc]')" do
attr_reader :parslet
Please sign in to comment.
Something went wrong with that request. Please try again.