Skip to content

Instantly share code, notes, and snippets.

@haldun
Created May 1, 2013 12:43
Show Gist options
  • Save haldun/5495089 to your computer and use it in GitHub Desktop.
Save haldun/5495089 to your computer and use it in GitHub Desktop.
experiments with a possible rule engine
require 'json'
require 'pp'
class Node
def to_json
to_h.to_json
end
def to_h
raise "Subclass responsibility"
end
def as_list
lst = []
prev = 0
(tree = as_list_inner(self).flatten).each_with_index do |e, index|
if e.is_a?(Class) && e <= Conjunction
lst << tree[prev..index]
prev = index + 1
end
end
lst << tree[prev..(tree.length)]
RuleList.new lst
end
private
def as_list_inner expr, acc = []
case expr
when PropertyOf
expr
when Conjunction
acc << [*as_list_inner(expr.lhs), expr.class]
acc << as_list_inner(expr.rhs)
when Binary
[as_list_inner(expr.lhs), expr.class, as_list_inner(expr.rhs)]
when Symbol
expr
when Value
expr
when Variable
expr
end
end
end
class Operator < Node
def apply env
raise "Subclass responsibility"
end
def to_h
{ class: self.class.name, lhs: lhs.to_h, rhs: rhs.to_h }
end
end
class Operand < Node
end
class Variable < Operand
attr_reader :name
def initialize name
@name = name
end
def to_s
name.to_s
end
end
class Value < Operand
attr_reader :content
def initialize content
@content = content
end
def to_s
content.to_s
end
end
class Unary < Operator
attr_reader :operand
def initialize operand
@operand = operand
end
end
class Binary < Operator
attr_accessor :lhs, :rhs
def initialize lhs=nil, rhs=nil
@lhs = lhs
@rhs = rhs
end
end
class Conjunction < Binary
end
class PropertyOf < Binary
def apply evaluator
evaluator.eval(lhs).__send__(rhs)
end
end
class And < Conjunction
def apply evaluator
evaluator.eval(lhs) && evaluator.eval(rhs)
end
end
class Or < Conjunction
def apply evaluator
evaluator.eval(lhs) || evaluator.eval(rhs)
end
end
class Equals < Binary
def apply evaluator
evaluator.eval(lhs) == evaluator.eval(rhs)
end
def to_s
'='
end
end
class LessThan < Binary
def apply evaluator
evaluator.eval(lhs) < evaluator.eval(rhs)
end
def to_s
'<'
end
end
class GreaterThan < Binary
def apply evaluator
evaluator.eval(lhs) > evaluator.eval(rhs)
end
def to_s
'>'
end
end
class In < Binary
def apply evaluator
evaluator.eval(rhs).include?(evaluator.eval(lhs))
end
end
class Env < Hash
end
class Evaluator
attr_reader :env
def initialize env
@env = env
end
def eval expr
case expr
when Operator
expr.apply(self)
when Symbol
env.fetch(expr)
when Value
expr.content
end
end
end
class RuleList
attr_reader :lines
def initialize lines = []
@lines = lines
end
def << line
lines << line
end
def as_tree
prev = nil
lines.each do |sentence|
lhs, op, rhs, conj = sentence
current = op.new(lhs, rhs)
if conj.nil?
if prev.nil?
prev = current
else
prev.rhs = current
end
else
conj_node = conj.new
if prev.nil?
conj_node.lhs = current
else
prev.rhs = current
conj_node.lhs = prev
end
prev = conj_node
end
end
prev
end
end
e = And.new(
Or.new(
Equals.new(
PropertyOf.new(:programme, :name),
Value.new('Deneme')
),
Equals.new(
PropertyOf.new(:channel, :id),
Value.new(5)
)
),
In.new(
PropertyOf.new(:channel, :id),
Value.new([1, 2, 3, 4])
)
)
e = Equals.new(
PropertyOf.new(:channel, :id),
Value.new(5)
)
puts "tree:\n"
pp e
puts "rule list:\n"
pp e.as_list
puts "tree:\n"
pp e.as_list.as_tree
# e = Or.new(
# GreaterThan.new(
# Variable.new(:a),
# Value.new(5)
# ),
# And.new(
# LessThan.new(
# Variable.new(:b),
# Value.new(2)
# ),
# Equals.new(
# Variable.new(:c),
# Value.new(1)
# )
# )
# )
#
# #
# # pp rule.as_tree
# # e.as_list.each do |sentence|
# # puts sentence.map(&:to_s).join(",")
# # end
# #
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment