wongi-engine 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +349 -0
- data/Rakefile +2 -0
- data/examples/ex01.rb +23 -0
- data/examples/ex02.rb +36 -0
- data/examples/graphviz.rb +15 -0
- data/examples/timeline.rb +48 -0
- data/lib/wongi-engine.rb +22 -0
- data/lib/wongi-engine/alpha_memory.rb +46 -0
- data/lib/wongi-engine/beta.rb +10 -0
- data/lib/wongi-engine/beta/beta_memory.rb +48 -0
- data/lib/wongi-engine/beta/beta_node.rb +164 -0
- data/lib/wongi-engine/beta/filter_node.rb +109 -0
- data/lib/wongi-engine/beta/join_node.rb +127 -0
- data/lib/wongi-engine/beta/ncc_node.rb +46 -0
- data/lib/wongi-engine/beta/ncc_partner.rb +43 -0
- data/lib/wongi-engine/beta/neg_node.rb +58 -0
- data/lib/wongi-engine/beta/optional_node.rb +43 -0
- data/lib/wongi-engine/beta/or_node.rb +76 -0
- data/lib/wongi-engine/beta/production_node.rb +31 -0
- data/lib/wongi-engine/core_ext.rb +57 -0
- data/lib/wongi-engine/dsl.rb +112 -0
- data/lib/wongi-engine/dsl/action.rb +12 -0
- data/lib/wongi-engine/dsl/actions/error_generator.rb +42 -0
- data/lib/wongi-engine/dsl/actions/simple_action.rb +23 -0
- data/lib/wongi-engine/dsl/actions/simple_collector.rb +51 -0
- data/lib/wongi-engine/dsl/actions/statement_generator.rb +52 -0
- data/lib/wongi-engine/dsl/actions/trace_action.rb +52 -0
- data/lib/wongi-engine/dsl/any_rule.rb +48 -0
- data/lib/wongi-engine/dsl/dsl_builder.rb +44 -0
- data/lib/wongi-engine/dsl/dsl_extensions.rb +43 -0
- data/lib/wongi-engine/dsl/extension_clause.rb +36 -0
- data/lib/wongi-engine/dsl/generation_clause.rb +15 -0
- data/lib/wongi-engine/dsl/generic_production_rule.rb +78 -0
- data/lib/wongi-engine/dsl/ncc_production_rule.rb +21 -0
- data/lib/wongi-engine/dsl/production_rule.rb +4 -0
- data/lib/wongi-engine/dsl/query.rb +24 -0
- data/lib/wongi-engine/graph.rb +71 -0
- data/lib/wongi-engine/model_context.rb +13 -0
- data/lib/wongi-engine/network.rb +416 -0
- data/lib/wongi-engine/network/collectable.rb +42 -0
- data/lib/wongi-engine/network/debug.rb +25 -0
- data/lib/wongi-engine/ruleset.rb +74 -0
- data/lib/wongi-engine/template.rb +111 -0
- data/lib/wongi-engine/token.rb +137 -0
- data/lib/wongi-engine/version.rb +5 -0
- data/lib/wongi-engine/wme.rb +134 -0
- data/lib/wongi-engine/wme_match_data.rb +34 -0
- data/spec/dataset_spec.rb +26 -0
- data/spec/dsl_spec.rb +9 -0
- data/spec/high_level_spec.rb +341 -0
- data/spec/ruleset_spec.rb +54 -0
- data/spec/simple_action_spec.rb +40 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/wme_spec.rb +83 -0
- data/wongi-engine.gemspec +19 -0
- metadata +110 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
def ruleset name = nil, &definition
|
2
|
+
rs = Wongi::Engine::Ruleset.new
|
3
|
+
if ! name.nil?
|
4
|
+
rs.name name
|
5
|
+
end
|
6
|
+
rs.instance_eval &definition if block_given?
|
7
|
+
rs
|
8
|
+
end
|
9
|
+
|
10
|
+
def rule name, &definition
|
11
|
+
r = Wongi::Engine::ProductionRule.new name
|
12
|
+
r.instance_eval &definition
|
13
|
+
r
|
14
|
+
end
|
15
|
+
|
16
|
+
def query name, &definition
|
17
|
+
q = Wongi::Engine::Query.new name
|
18
|
+
q.instance_eval &definition
|
19
|
+
q
|
20
|
+
end
|
21
|
+
|
22
|
+
def dsl &definition
|
23
|
+
Wongi::Engine::DSLBuilder.new.build &definition
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'wongi-engine/dsl/dsl_extensions'
|
27
|
+
require 'wongi-engine/dsl/dsl_builder'
|
28
|
+
require 'wongi-engine/dsl/action'
|
29
|
+
require 'wongi-engine/dsl/generation_clause'
|
30
|
+
require 'wongi-engine/dsl/extension_clause'
|
31
|
+
require 'wongi-engine/dsl/generic_production_rule'
|
32
|
+
require 'wongi-engine/dsl/production_rule'
|
33
|
+
require 'wongi-engine/dsl/ncc_production_rule'
|
34
|
+
require 'wongi-engine/dsl/any_rule'
|
35
|
+
require 'wongi-engine/dsl/query'
|
36
|
+
require 'wongi-engine/dsl/actions/simple_action'
|
37
|
+
require 'wongi-engine/dsl/actions/statement_generator'
|
38
|
+
require 'wongi-engine/dsl/actions/simple_collector'
|
39
|
+
require 'wongi-engine/dsl/actions/trace_action'
|
40
|
+
require 'wongi-engine/dsl/actions/error_generator'
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
dsl {
|
45
|
+
|
46
|
+
section :forall
|
47
|
+
|
48
|
+
clause :has, :fact
|
49
|
+
action Wongi::Engine::Template
|
50
|
+
|
51
|
+
clause :missing, :neg
|
52
|
+
action Wongi::Engine::NegTemplate
|
53
|
+
|
54
|
+
clause :none, :ncc
|
55
|
+
accept Wongi::Engine::NccProductionRule
|
56
|
+
|
57
|
+
clause :any
|
58
|
+
action Wongi::Engine::AnyRule
|
59
|
+
|
60
|
+
clause :maybe, :optional
|
61
|
+
accept Wongi::Engine::OptionalTemplate
|
62
|
+
|
63
|
+
clause :same, :eq, :equal
|
64
|
+
accept Wongi::Engine::EqualityTest
|
65
|
+
|
66
|
+
clause :diff, :ne
|
67
|
+
accept Wongi::Engine::InequalityTest
|
68
|
+
|
69
|
+
clause :asserted, :added
|
70
|
+
body { |s, p, o|
|
71
|
+
missing s, p, o, -1
|
72
|
+
has s, p, o, 0
|
73
|
+
}
|
74
|
+
|
75
|
+
clause :retracted, :removed
|
76
|
+
body { |s, p, o|
|
77
|
+
has s, p, o, -1
|
78
|
+
missing s, p, o, 0
|
79
|
+
}
|
80
|
+
|
81
|
+
clause :kept, :still_has
|
82
|
+
body { |s, p, o|
|
83
|
+
has s, p, o, -1
|
84
|
+
has s, p, o, 0
|
85
|
+
}
|
86
|
+
|
87
|
+
clause :kept_missing, :still_missing
|
88
|
+
body { |s, p, o|
|
89
|
+
missing s, p, o, -1
|
90
|
+
missing s, p, o, 0
|
91
|
+
}
|
92
|
+
|
93
|
+
|
94
|
+
section :make
|
95
|
+
|
96
|
+
clause :gen
|
97
|
+
accept Wongi::Engine::GenerationClause
|
98
|
+
|
99
|
+
clause :trace
|
100
|
+
action Wongi::Engine::TraceAction
|
101
|
+
|
102
|
+
clause :error
|
103
|
+
action Wongi::Engine::ErrorGenerator
|
104
|
+
|
105
|
+
clause :collect
|
106
|
+
action Wongi::Engine::SimpleCollector
|
107
|
+
|
108
|
+
clause :action
|
109
|
+
action Wongi::Engine::SimpleAction
|
110
|
+
|
111
|
+
}
|
112
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Wongi
|
2
|
+
module Engine
|
3
|
+
class ReteError
|
4
|
+
|
5
|
+
attr_reader :token, :message
|
6
|
+
def initialize token, message, literate
|
7
|
+
@token, @message, @literate = token, message, literate
|
8
|
+
end
|
9
|
+
def literate?
|
10
|
+
@literate
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ErrorGenerator < Wongi::Engine::Action
|
15
|
+
|
16
|
+
def initialize message = nil, &messenger
|
17
|
+
@message, @messenger = message, messenger
|
18
|
+
end
|
19
|
+
|
20
|
+
def rete=
|
21
|
+
super
|
22
|
+
rete.add_collector :error, self
|
23
|
+
end
|
24
|
+
|
25
|
+
def errors
|
26
|
+
production.tokens.map do |token|
|
27
|
+
message = if @messenger
|
28
|
+
@messenger.call token.assignments
|
29
|
+
else
|
30
|
+
@message
|
31
|
+
end
|
32
|
+
ReteError.new token, message, literate?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def literate?
|
37
|
+
not @messenger.nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
|
3
|
+
class SimpleAction < Action
|
4
|
+
|
5
|
+
def initialize action = nil, *args, &block
|
6
|
+
@action = if action.is_a? Class
|
7
|
+
action.new *args, &block
|
8
|
+
else
|
9
|
+
action || block
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute token
|
14
|
+
if @action.respond_to? :call
|
15
|
+
@action.call token
|
16
|
+
elsif @action.respond_to? :execute
|
17
|
+
@action.execute token
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
|
3
|
+
class SimpleCollector < Action
|
4
|
+
|
5
|
+
def self.collector
|
6
|
+
Class.new self
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize variable, name = nil
|
10
|
+
@variable = variable
|
11
|
+
@name = name if name
|
12
|
+
#(class << self; self; end).instance_eval do
|
13
|
+
# define_method method do
|
14
|
+
# collect variable
|
15
|
+
# end
|
16
|
+
# alias_method method, :default_collect
|
17
|
+
# end
|
18
|
+
end
|
19
|
+
|
20
|
+
def default_collect
|
21
|
+
collect @variable
|
22
|
+
end
|
23
|
+
|
24
|
+
def name= n
|
25
|
+
@name = n unless @name
|
26
|
+
end
|
27
|
+
|
28
|
+
def rete= rete
|
29
|
+
rete.add_collector self, name
|
30
|
+
end
|
31
|
+
|
32
|
+
def collect var
|
33
|
+
production.tokens.map { |token| token[var] }
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
class GenericCollectClause
|
39
|
+
|
40
|
+
def initialize name, variable
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def import_into rete
|
45
|
+
collector = SimpleCollector.new @variable
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
class StatementGenerator < Action
|
3
|
+
|
4
|
+
def initialize template
|
5
|
+
@template = template
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute token
|
9
|
+
|
10
|
+
subject = if Template.variable?( @template.subject )
|
11
|
+
v = token[ @template.subject ]
|
12
|
+
raise "Unbound variable #{@template.subject} in token #{token}" if v.nil?
|
13
|
+
v
|
14
|
+
else
|
15
|
+
@template.subject
|
16
|
+
end
|
17
|
+
|
18
|
+
predicate = if Template.variable?( @template.predicate )
|
19
|
+
v = token[ @template.predicate ]
|
20
|
+
raise "Unbound variable #{@template.predicate} in token #{token}" if v.nil?
|
21
|
+
v
|
22
|
+
else
|
23
|
+
@template.predicate
|
24
|
+
end
|
25
|
+
|
26
|
+
object = if Template.variable?( @template.object )
|
27
|
+
v = token[ @template.object ]
|
28
|
+
raise "Unbound variable #{@template.object} in token #{token}" if v.nil?
|
29
|
+
v
|
30
|
+
else
|
31
|
+
@template.object
|
32
|
+
end
|
33
|
+
|
34
|
+
wme = WME.new subject, predicate, object
|
35
|
+
|
36
|
+
production.tracer.trace( action: self, wme: wme ) if production.tracer
|
37
|
+
if existing = rete.exists?( wme )
|
38
|
+
generated = existing.generating_tokens.size
|
39
|
+
if generated > 0 && ! token.generated_wmes.include?( existing )
|
40
|
+
token.generated_wmes << existing
|
41
|
+
existing.generating_tokens << token
|
42
|
+
end
|
43
|
+
else
|
44
|
+
added = rete << wme
|
45
|
+
token.generated_wmes << added
|
46
|
+
added.generating_tokens << token
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
class TraceAction < Action
|
3
|
+
|
4
|
+
class DefaultTracer
|
5
|
+
|
6
|
+
attr_accessor :action
|
7
|
+
|
8
|
+
def trace args
|
9
|
+
case args[:action]
|
10
|
+
when TraceAction
|
11
|
+
if args[:token]
|
12
|
+
action.io.puts "EXECUTED RULE #{args[:action].rule.name} WITH #{args[:token]}"
|
13
|
+
else
|
14
|
+
action.io.puts "EXECUTED RULE #{args[:action].rule.name}"
|
15
|
+
end
|
16
|
+
when StatementGenerator
|
17
|
+
action.io.puts "GENERATED #{args[:wme]}" if action.generation?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :io
|
24
|
+
attr_predicate :generation, :values
|
25
|
+
|
26
|
+
def initialize opts = { }
|
27
|
+
[:generation, :values, :tracer, :tracer_class, :io].each do |option|
|
28
|
+
if opts.has_key? option
|
29
|
+
instance_variable_set "@#{option}", opts[option]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
@io ||= $stdout
|
33
|
+
@tracer ||= (@tracer_class || DefaultTracer).new
|
34
|
+
@tracer.action = self
|
35
|
+
end
|
36
|
+
|
37
|
+
def trace args
|
38
|
+
@tracer.trace args
|
39
|
+
end
|
40
|
+
|
41
|
+
def execute token
|
42
|
+
production.tracer = self
|
43
|
+
if values?
|
44
|
+
trace action: self, token: token
|
45
|
+
else
|
46
|
+
trace action: self
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
class AnyRule
|
3
|
+
|
4
|
+
attr_reader :variants
|
5
|
+
|
6
|
+
def initialize &block
|
7
|
+
@variants = []
|
8
|
+
if block
|
9
|
+
instance_eval &block
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def variant &block
|
14
|
+
var = VariantRule.new
|
15
|
+
var.instance_eval &block
|
16
|
+
variants << var
|
17
|
+
end
|
18
|
+
|
19
|
+
def import_into rete
|
20
|
+
AnySet.new variants.map { |variant|
|
21
|
+
if variant.respond_to? :import_into
|
22
|
+
variant.import_into(rete)
|
23
|
+
else
|
24
|
+
variant
|
25
|
+
end
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
class VariantRule < GenericProductionRule
|
32
|
+
|
33
|
+
def initialize name = nil
|
34
|
+
super
|
35
|
+
@current_section = :forall
|
36
|
+
end
|
37
|
+
|
38
|
+
def import_into rete
|
39
|
+
VariantSet.new @acceptors[:forall].map { |condition|
|
40
|
+
if condition.respond_to? :import_into
|
41
|
+
condition.import_into(rete)
|
42
|
+
else
|
43
|
+
condition
|
44
|
+
end
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
class DSLBuilder
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@current_section = nil
|
6
|
+
@current_clause = nil
|
7
|
+
@clauses = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def build &definition
|
11
|
+
instance_eval &definition
|
12
|
+
@clauses.each do |c|
|
13
|
+
DSLExtensions.create_extension c
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def section s
|
18
|
+
@current_section = s
|
19
|
+
end
|
20
|
+
|
21
|
+
def clause *c
|
22
|
+
@current_clause = c
|
23
|
+
end
|
24
|
+
|
25
|
+
def action klass = nil, &block
|
26
|
+
raise "Cannot create an action without a clause" if @current_clause.nil?
|
27
|
+
@clauses << { :section => @current_section, :clause => @current_clause, :action => klass || block }
|
28
|
+
@current_clause = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def body klass = nil, &block
|
32
|
+
raise "Cannot create a body without a clause" if @current_clause.nil?
|
33
|
+
@clauses << { :section => @current_section, :clause => @current_clause, :body => klass || block }
|
34
|
+
@current_clause = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def accept klass
|
38
|
+
raise "Cannot create an acceptor without a clause" if @current_clause.nil?
|
39
|
+
@clauses << { :section => @current_section, :clause => @current_clause, :accept => klass }
|
40
|
+
@current_clause = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|