Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

improved property nodes for more sharing

  • Loading branch information...
commit f0be3c736a0c86990d59062b1468cdd826bc3152 1 parent f5fe64b
Joe Kutner jkutner authored
47 lib/core/atoms.rb
View
@@ -16,11 +16,11 @@ module Core
class Atom
attr_reader :tag, :proc, :slot, :template
- def initialize(tag, slot, template, &block)
+ def initialize(tag, slot, template, block)
@tag = tag
@slot = slot
@template = template
- @proc = Proc.new(&block) if block_given?
+ @proc = block
end
def to_s
@@ -34,7 +34,15 @@ def to_s
# a.person{ |p| p.is_a? Person }
#
# So there are no references to other atoms.
- class PropertyAtom < Atom
+ class PropertyAtom < Atom
+
+ attr_reader :value
+
+ def initialize(tag, slot, template, value, block)
+ super(tag,slot,template, block)
+ @value = value
+ end
+
def ==(atom)
shareable?(atom) && @tag == atom.tag
end
@@ -43,7 +51,8 @@ def shareable?(atom)
PropertyAtom === atom &&
@slot == atom.slot &&
@template == atom.template &&
- @proc == atom.proc
+ @proc == atom.proc &&
+ @value == atom.value
end
end
@@ -70,13 +79,6 @@ def to_s
"#{self.class},#{@template},#{@arguments.inspect}"
end
end
-
- # TODO use this
- class BlockAtom < PropertyAtom
- def shareable?(atom)
- super && BlockAtom === atom && @proc == atom.proc
- end
- end
# This kind of atom is used to match just a single, hard coded value.
# For example:
@@ -85,12 +87,12 @@ def shareable?(atom)
#
# So there are no references to other atoms.
class EqualsAtom < PropertyAtom
- attr_reader :value
+ EQUAL_PROC = lambda {|x, y| x == y}
+
def initialize(tag, slot, template, value)
- super(tag,slot,template)
- @value = value
+ super(tag,slot,template, value, EQUAL_PROC)
end
-
+
def shareable?(atom)
EqualsAtom === atom &&
@slot == atom.slot &&
@@ -103,12 +105,15 @@ def shareable?(atom)
# 'For each Person as :p'
#
# It is only used at the start of a pattern.
- class HeadAtom < Atom
+ class HeadAtom < PropertyAtom
+ HEAD_EQUAL_PROC = lambda {|t, c| t == c}
+ HEAD_INHERITS_PROC = lambda {|t, c| t === c}
+
def initialize(tag, template)
if template.mode == :equals
- super tag, :class, template do |t| t == template.clazz end
+ super tag, :class, template, template.clazz, HEAD_EQUAL_PROC
elsif template.mode == :inherits
- super tag, :class, template do |t| t === template.clazz end
+ super tag, :class, template, template.clazz, HEAD_INHERITS_PROC
end
end
@@ -126,8 +131,8 @@ def shareable?(atom)
class ReferenceAtom < Atom
attr_reader :vars
- def initialize(tag, slot, vars, template, &block)
- super(tag, slot, template, &block)
+ def initialize(tag, slot, vars, template, block)
+ super(tag, slot, template, block)
@vars = vars # list of referenced variable names
end
@@ -177,4 +182,4 @@ class AtomFactory
end
end
-end
+end
28 lib/core/nodes.rb
View
@@ -222,27 +222,23 @@ def create_property_node(atom)
end
@atom_nodes.each {|n| return n if n.shareable? node}
@atom_nodes.push node
- return node
+ node
end
def create_self_reference_node(atom)
node = SelfReferenceNode.new(@bucket, atom)
@atom_nodes.push node
- return node
+ node
end
def create_reference_node(atom)
node = ReferenceNode.new(@bucket, atom)
@atom_nodes.push node
- return node
+ node
end
def create_adapter_node(side)
- if side == :left
- return LeftAdapterNode.new(@bucket)
- else
- return RightAdapterNode.new(@bucket)
- end
+ side == :left ? LeftAdapterNode.new(@bucket) : RightAdapterNode.new(@bucket)
end
# This method is used to update each TypeNode based on the facts in
@@ -294,7 +290,7 @@ def resolve(mr1, mr2)
end
end
end
- return true
+ true
end
end
@@ -468,8 +464,8 @@ def assert(fact)
class PropertyNode < AtomNode
def assert(assertable)
begin
- val = assertable.fact.object.send(@atom.slot)
- assertable.add_tag(@atom.tag, val)
+ v = assertable.fact.object.send(@atom.slot)
+ assertable.add_tag(@atom.tag, v)
rescue NoMethodError => e
@bucket.add_error Error.new(:no_method, :warn, {
:object => assertable.fact.object.to_s,
@@ -479,12 +475,16 @@ def assert(assertable)
return
end
begin
- super if @atom.proc.call(val)
+ if @atom.proc.arity == 1
+ super if @atom.proc.call(v)
+ else
+ super if @atom.proc.call(v, @atom.value)
+ end
rescue Exception => e
@bucket.add_error Error.new(:proc_call, :error, {
:object => assertable.fact.object.to_s,
:method => @atom.slot,
- :value => val.to_s,
+ :value => v.to_s,
:message => e.message
})
end
@@ -1002,4 +1002,4 @@ def tags
end
end
-end
+end
54 lib/dsl/ferrari.rb
View
@@ -316,7 +316,7 @@ def method_missing(method_id, *args, &block)
puts args.class.to_s + ' --- ' + args.to_s
raise 'Arguments not supported for short-hand conditions'
end
- return ab
+ ab
end
end
@@ -361,46 +361,59 @@ def to_s
class AtomBuilder
attr_accessor :tag, :name, :bindings, :deftemplate, :block
-
+
+ EQ_PROC = lambda {|x,y| x == y}
+ GT_PROC = lambda {|x,y| x > y}
+ LT_PROC = lambda {|x,y| x < y}
+ MATCH_PROC = lambda {|x,y| x =~ y}
+ LTE_PROC = lambda {|x,y| x <= y}
+ GTE_PROC = lambda {|x,y| x >= y}
+ TRUE_PROC = lambda {|x| true}
+
def initialize(method_id)
@name = method_id
@deftemplate = nil
@tag = GeneratedTag.new
@bindings = []
- @block = lambda {|x| true}
+ @block = TRUE_PROC
@child_atom_builders = []
end
def method_missing(method_id, *args, &block)
if method_id == :not
- return NotOperatorBuilder.new(@name)
+ NotOperatorBuilder.new(@name)
end
end
def ==(value)
@atom_type = :equals
- @value = value
- create_block value, lambda {|x,y| x == y}, lambda {|x| x == value}; self
+ create_block value, EQ_PROC
+ self
end
def >(value)
- create_block value, lambda {|x,y| x > y}, lambda {|x| x > value}; self
+ create_block value, GT_PROC
+ self
end
def <(value)
- create_block value, lambda {|x,y| x < y}, lambda {|x| x < value}; self
+ create_block value, LT_PROC
+ self
end
def =~(value)
- create_block value, lambda {|x,y| x =~ y}, lambda {|x| x =~ value}; self
+ create_block value, MATCH_PROC
+ self
end
def <=(value)
- create_block value, lambda {|x,y| x <= y}, lambda {|x| x <= value}; self
+ create_block value, LTE_PROC
+ self
end
def >=(value)
- create_block value, lambda {|x,y| x >= y}, lambda {|x| x >= value}; self
+ create_block value, GTE_PROC
+ self
end
def build_atoms(tags,methods,when_id)
@@ -414,16 +427,16 @@ def build_atoms(tags,methods,when_id)
if @atom_type == :equals
return atoms << Core::EqualsAtom.new(@tag, @name, @deftemplate, @value)
else
- return atoms << Core::PropertyAtom.new(@tag, @name, @deftemplate, &@block)
+ return atoms << Core::PropertyAtom.new(@tag, @name, @deftemplate, @value, @block)
end
end
if references_self?(tags,when_id)
bind_methods = @bindings.collect{ |bb| methods[bb.tag] }
- atoms << Core::SelfReferenceAtom.new(@tag,@name,bind_methods,@deftemplate,&@block)
+ atoms << Core::SelfReferenceAtom.new(@tag,@name,bind_methods,@deftemplate,@block)
else
bind_tags = @bindings.collect{ |bb| bb.tag }
- atoms << Core::ReferenceAtom.new(@tag,@name,bind_tags,@deftemplate,&@block)
+ atoms << Core::ReferenceAtom.new(@tag,@name,bind_tags,@deftemplate,@block)
end
end
@@ -440,26 +453,27 @@ def references_self?(tags,when_id)
raise 'Binding to self and another pattern in the same condition is not yet supported.'
end
- return ref_self > 0
+ ref_self > 0
end
- def create_block(value, ref_block, basic_block)
+ def create_block(value, block)
+ @block = block
if value && value.kind_of?(BindingBuilder)
@bindings = [value]
- @block = ref_block
elsif value && value.kind_of?(AtomBuilder)
@child_atom_builders << value
@bindings = [BindingBuilder.new(value.tag)]
- @block = ref_block
else
- @block = basic_block
+ @value = value
end
end
end
class NotOperatorBuilder < AtomBuilder
+ NOT_PROC = lambda {|x,y| x != y}
def ==(value)
- create_block value, lambda {|x,y| x != y}, lambda {|x| x != value}; self
+ create_block value, NOT_PROC
+ self
end
end
69 spec/property_spec.rb
View
@@ -0,0 +1,69 @@
+require 'spec_helper'
+
+class PropFact
+ attr :value, true
+ def initialize(v=nil); @value = v; end
+end
+
+class PropCtx
+
+end
+
+
+include Ruleby
+
+class PropRulebook < Rulebook
+ def gt_rules
+ rule [PropFact, :p, m.value > 0] do
+ assert Success.new
+ end
+ end
+
+ def lte_rules
+ rule [PropFact, :p, m.value > 42], [PropCtx, :pc] do
+ # do nothing, just being here helps reproduce a bug
+ end
+ rule [PropFact, :p, m.value <= 42], [PropCtx, :pc] do
+ assert Success.new
+ end
+ end
+end
+
+describe Ruleby::Core::Engine do
+ describe "property gt_rules" do
+ subject do
+ engine :engine do |e|
+ PropRulebook.new(e).gt_rules
+ end
+ end
+
+ before do
+ subject.assert PropFact.new(1)
+ subject.match
+ end
+
+ it "should have matched" do
+ subject.errors.should == []
+ subject.retrieve(Success).size.should == 1
+ end
+ end
+ describe "property lte_rules" do
+ subject do
+ engine :engine do |e|
+ PropRulebook.new(e).lte_rules
+ end
+ end
+
+ before do
+ subject.assert PropCtx.new
+ subject.assert PropFact.new(42)
+ subject.assert PropFact.new(41)
+ subject.match
+ end
+
+ it "should have matched" do
+ subject.errors.should == []
+ subject.retrieve(Success).size.should == 2
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.