Created
May 1, 2013 12:43
-
-
Save haldun/5495089 to your computer and use it in GitHub Desktop.
experiments with a possible rule engine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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