wongi-engine 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|