Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

+ Predicates as proposed by Clifford Heath.

  • Loading branch information...
commit 07caba01d7af1b3a18257158e2811569c1d8d830 1 parent a6938e6
@kschiess authored
View
5 HISTORY.txt
@@ -1,3 +1,8 @@
+= 1.2.0 / ???
+
+ + EXPERIMENTAL Positive predicates that can decide if the parse continues
+ based on the output of a parse subtree.
+
= 1.1.0 / 2Feb2011
+ Uses return (fail/success), cached line counts, memoizing of parse results
View
1  lib/parslet/atoms.rb
@@ -25,5 +25,6 @@ module Precedence # :nodoc:
require 'parslet/atoms/re'
require 'parslet/atoms/str'
require 'parslet/atoms/entity'
+ require 'parslet/atoms/predicate'
end
View
47 lib/parslet/atoms/predicate.rb
@@ -0,0 +1,47 @@
+
+# Allows execution of code in line with parsing. The code can use the result
+# of a subexpression to decide if a parse should go ahead or fail at this
+# point.
+#
+# Please note that the block will probably be memoized and not executed
+# more than once for any input position. Predicates are NOT grammar actions,
+# and I you use them as such, you're on your own.
+#
+# Example:
+# (str('bacon') | str('chunky')).pred { |m| m != 'chunky' }
+# # for when you don't really want chunky bacon.
+#
+class Parslet::Atoms::Predicate < Parslet::Atoms::Base
+ attr_reader :parslet, :predicate
+
+ def initialize(parslet, &predicate)
+ @predicate = predicate
+ @parslet = parslet
+ end
+
+ def try(source, context)
+ error_pos = source.pos
+
+ value = parslet.apply(source, context)
+ return value if value.error?
+
+ flat_result = flatten(value.result)
+ if predicate.call(flat_result)
+ # TODO once we have intermediary flattening, make this return the
+ # flat result.
+ return success(value)
+ else
+ return error(source, "Predicate failure", error_pos)
+ end
+
+ fail 'BUG'
+ end
+
+ def to_s_inner(prec) # :nodoc:
+ parslet.to_s(prec) + " &{ .. }"
+ end
+
+ def error_tree # :nodoc:
+ parslet.error_tree
+ end
+end
View
8 lib/parslet/atoms/visitor.rb
@@ -72,4 +72,12 @@ def accept(visitor)
visitor.re(match)
end
end
+
+ class Predicate
+ # Call back visitors #re method. See parslet/export for an example.
+ #
+ def accept(visitor)
+ visitor.predicate(parslet, predicate)
+ end
+ end
end
View
52 spec/parslet/atoms/predicate_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+describe Parslet::Atoms::Predicate do
+ include Parslet
+
+ context "str('foo').pred { ... }" do
+ before(:each) { flexmock(self).should_receive(:pred => true).by_default }
+ let(:predicate) { described_class.new(str('foo')) { |m| pred(m) } }
+
+ describe "#to_s" do
+ subject { predicate.to_s }
+ it { should == "'foo' &{ .. }" }
+ end
+ describe "#error_tree" do
+ before(:each) { predicate.parse('bar') rescue nil }
+ it "should return a valid error tree" do
+ predicate.error_tree
+ end
+ end
+
+ context "when the predicate returns false" do
+ before(:each) { flexmock(self).should_receive(:pred => false) }
+ it "should not match 'foo'" do
+ predicate.should_not parse('foo')
+ end
+ end
+ context "when the predicate returns true" do
+ before(:each) { flexmock(self).should_receive(:pred => true) }
+ it "should not match 'foo'" do
+ predicate.should parse('foo')
+ end
+ end
+ end
+ context "str('a').as(:b).repeat.pred { ... }" do
+ # before(:each) { flexmock(self).should_receive(:pred => true).by_default }
+ let(:parslet) { str('a').as(:b).repeat }
+ let(:predicate) { described_class.new(parslet) { |m| pred(m) } }
+
+ context "when fed 'aa', the block" do
+ def pred(m)
+ p m
+ end
+ it "should receive :b => 'aa' as argument" do
+ flexmock(self).
+ should_receive(:pred).with([{:b=>"a"}, {:b=>"a"}]).
+ and_return(true).once
+
+ predicate.parse('aa')
+ end
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.