mort666-wongi-engine 0.2.9
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.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.hgignore +6 -0
- data/.hgtags +13 -0
- data/.ruby-gemset +1 -0
- data/.travis.yml +19 -0
- data/CHANGELOG.md +106 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +27 -0
- data/Rakefile +9 -0
- data/examples/ex01.rb +23 -0
- data/examples/ex02.rb +37 -0
- data/examples/graphviz.rb +16 -0
- data/examples/rdf.n3 +6 -0
- data/examples/rdf.rb +14 -0
- data/examples/timeline.rb +48 -0
- data/lib/wongi-engine.rb +36 -0
- data/lib/wongi-engine/alpha_memory.rb +60 -0
- data/lib/wongi-engine/beta.rb +11 -0
- data/lib/wongi-engine/beta/assignment_node.rb +40 -0
- data/lib/wongi-engine/beta/beta_memory.rb +49 -0
- data/lib/wongi-engine/beta/beta_node.rb +94 -0
- data/lib/wongi-engine/beta/filter_node.rb +48 -0
- data/lib/wongi-engine/beta/join_node.rb +140 -0
- data/lib/wongi-engine/beta/ncc_node.rb +67 -0
- data/lib/wongi-engine/beta/ncc_partner.rb +40 -0
- data/lib/wongi-engine/beta/neg_node.rb +115 -0
- data/lib/wongi-engine/beta/optional_node.rb +142 -0
- data/lib/wongi-engine/beta/or_node.rb +37 -0
- data/lib/wongi-engine/beta/production_node.rb +31 -0
- data/lib/wongi-engine/compiler.rb +115 -0
- data/lib/wongi-engine/core_ext.rb +63 -0
- data/lib/wongi-engine/data_overlay.rb +144 -0
- data/lib/wongi-engine/dsl.rb +132 -0
- data/lib/wongi-engine/dsl/action/base.rb +11 -0
- data/lib/wongi-engine/dsl/action/error_generator.rb +31 -0
- data/lib/wongi-engine/dsl/action/simple_action.rb +60 -0
- data/lib/wongi-engine/dsl/action/simple_collector.rb +52 -0
- data/lib/wongi-engine/dsl/action/statement_generator.rb +46 -0
- data/lib/wongi-engine/dsl/action/trace_action.rb +49 -0
- data/lib/wongi-engine/dsl/any_rule.rb +33 -0
- data/lib/wongi-engine/dsl/assuming.rb +31 -0
- data/lib/wongi-engine/dsl/builder.rb +44 -0
- data/lib/wongi-engine/dsl/clause/assign.rb +15 -0
- data/lib/wongi-engine/dsl/clause/fact.rb +71 -0
- data/lib/wongi-engine/dsl/clause/gen.rb +17 -0
- data/lib/wongi-engine/dsl/clause/generic.rb +38 -0
- data/lib/wongi-engine/dsl/generated.rb +43 -0
- data/lib/wongi-engine/dsl/ncc_subrule.rb +17 -0
- data/lib/wongi-engine/dsl/query.rb +24 -0
- data/lib/wongi-engine/dsl/rule.rb +84 -0
- data/lib/wongi-engine/enumerators.rb +21 -0
- data/lib/wongi-engine/error.rb +22 -0
- data/lib/wongi-engine/filter.rb +6 -0
- data/lib/wongi-engine/filter/asserting_test.rb +20 -0
- data/lib/wongi-engine/filter/equality_test.rb +36 -0
- data/lib/wongi-engine/filter/filter_test.rb +18 -0
- data/lib/wongi-engine/filter/greater_than_test.rb +36 -0
- data/lib/wongi-engine/filter/inequality_test.rb +36 -0
- data/lib/wongi-engine/filter/less_than_test.rb +36 -0
- data/lib/wongi-engine/graph.rb +73 -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 +85 -0
- data/lib/wongi-engine/ruleset.rb +74 -0
- data/lib/wongi-engine/template.rb +78 -0
- data/lib/wongi-engine/token.rb +114 -0
- data/lib/wongi-engine/version.rb +5 -0
- data/lib/wongi-engine/wme.rb +89 -0
- data/lib/wongi-engine/wme_match_data.rb +34 -0
- data/spec/beta_node_spec.rb +29 -0
- data/spec/bug_specs/issue_4_spec.rb +141 -0
- data/spec/dataset_spec.rb +27 -0
- data/spec/dsl_spec.rb +9 -0
- data/spec/filter_specs/assert_test_spec.rb +102 -0
- data/spec/filter_specs/less_test_spec.rb +41 -0
- data/spec/generation_spec.rb +116 -0
- data/spec/high_level_spec.rb +378 -0
- data/spec/network_spec.rb +182 -0
- data/spec/overlay_spec.rb +61 -0
- data/spec/rule_specs/any_rule_spec.rb +75 -0
- data/spec/rule_specs/assign_spec.rb +88 -0
- data/spec/rule_specs/assuming_spec.rb +66 -0
- data/spec/rule_specs/maybe_rule_spec.rb +101 -0
- data/spec/rule_specs/ncc_spec.rb +258 -0
- data/spec/rule_specs/negative_rule_spec.rb +105 -0
- data/spec/ruleset_spec.rb +54 -0
- data/spec/simple_action_spec.rb +40 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/wme_spec.rb +83 -0
- data/wongi-engine.gemspec +40 -0
- metadata +212 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
|
3
|
+
module CoreExt
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def attr_predicate *names
|
8
|
+
|
9
|
+
names_hash = names.inject( {} ) do |hash, element|
|
10
|
+
if Hash === element
|
11
|
+
hash.merge element
|
12
|
+
else
|
13
|
+
hash[element] = false
|
14
|
+
hash
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
names_hash.each do |name, def_value|
|
19
|
+
|
20
|
+
varname = "@#{name}".to_sym
|
21
|
+
predname = "#{name}?".to_sym
|
22
|
+
setname = "#{name}=".to_sym
|
23
|
+
exclname = "#{name}!".to_sym
|
24
|
+
noexclname = "no_#{name}!".to_sym
|
25
|
+
|
26
|
+
define_method predname do
|
27
|
+
if instance_variable_defined?( varname )
|
28
|
+
instance_variable_get( varname )
|
29
|
+
else
|
30
|
+
def_value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
define_method setname do |newvalue|
|
35
|
+
instance_variable_set( varname, newvalue == true )
|
36
|
+
end
|
37
|
+
|
38
|
+
define_method exclname do
|
39
|
+
instance_variable_set( varname, true )
|
40
|
+
end
|
41
|
+
|
42
|
+
define_method noexclname do
|
43
|
+
instance_variable_set( varname, false )
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def abstract name
|
50
|
+
define_method name do |*args|
|
51
|
+
raise NoMethodError.new "#{name} is not implemented for #{self.class.name}", name
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.included mod
|
58
|
+
mod.extend ClassMethods
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
class DataOverlay
|
3
|
+
|
4
|
+
attr_reader :rete
|
5
|
+
attr_reader :parent
|
6
|
+
|
7
|
+
def initialize(rete, parent = nil)
|
8
|
+
@rete = rete
|
9
|
+
@parent = parent
|
10
|
+
@raw_wmes = Hash.new { |h, k| h[k] = [] }
|
11
|
+
@raw_tokens = Hash.new { |h, k| h[k] = [] }
|
12
|
+
rete.add_overlay(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
def new_child
|
16
|
+
DataOverlay.new(rete, self)
|
17
|
+
end
|
18
|
+
|
19
|
+
def with_child
|
20
|
+
return unless block_given?
|
21
|
+
new_child.tap do |overlay|
|
22
|
+
begin
|
23
|
+
result = yield overlay
|
24
|
+
ensure
|
25
|
+
overlay.dispose
|
26
|
+
end
|
27
|
+
result
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def ancestor?(other)
|
32
|
+
return false if parent.nil?
|
33
|
+
return true if parent == other
|
34
|
+
parent.ancestor?(other)
|
35
|
+
end
|
36
|
+
|
37
|
+
def dispose
|
38
|
+
return if self == rete.default_overlay
|
39
|
+
rete.remove_overlay(self)
|
40
|
+
@raw_tokens.values.each do |tokens|
|
41
|
+
tokens.each(&:dispose!)
|
42
|
+
end
|
43
|
+
@raw_tokens.clear
|
44
|
+
end
|
45
|
+
|
46
|
+
def <<(thing)
|
47
|
+
case thing
|
48
|
+
when Array
|
49
|
+
assert WME.new(*thing).tap { |wme| wme.overlay = self }
|
50
|
+
when WME
|
51
|
+
assert(thing)
|
52
|
+
else
|
53
|
+
raise Error, "overlays can only accept data"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def assert wme
|
58
|
+
@next_cascade ||= []
|
59
|
+
@next_cascade << [:assert, wme]
|
60
|
+
if @current_cascade.nil?
|
61
|
+
@current_cascade = @next_cascade
|
62
|
+
@next_cascade = nil
|
63
|
+
process_cascade
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def retract wme, options = { }
|
68
|
+
if wme.is_a? Array
|
69
|
+
wme = WME.new(*wme)
|
70
|
+
end
|
71
|
+
@next_cascade ||= []
|
72
|
+
@next_cascade << [:retract, wme, options]
|
73
|
+
if @current_cascade.nil?
|
74
|
+
@current_cascade = @next_cascade
|
75
|
+
@next_cascade = nil
|
76
|
+
process_cascade
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def process_cascade
|
81
|
+
while @current_cascade
|
82
|
+
@current_cascade.each do |(operation, wme, options)|
|
83
|
+
case operation
|
84
|
+
when :assert
|
85
|
+
wme.overlay = self
|
86
|
+
rete.real_assert(wme)
|
87
|
+
when :retract
|
88
|
+
rete.real_retract(wme, options)
|
89
|
+
wme.overlay = nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
@current_cascade = @next_cascade
|
93
|
+
@next_cascade = nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def highest(other)
|
98
|
+
return self if self == other
|
99
|
+
return self if other.nil?
|
100
|
+
return self if ancestor?(other)
|
101
|
+
return other if other.ancestor?(self)
|
102
|
+
nil # unrelated lineages
|
103
|
+
end
|
104
|
+
|
105
|
+
# TODO: this is inconsistent.
|
106
|
+
# A WME retracted in-flight will be visible in active enumerators
|
107
|
+
# but a token will not.
|
108
|
+
# But this is how it works.
|
109
|
+
|
110
|
+
def wmes(alpha)
|
111
|
+
DuplicatingEnumerator.new(raw_wmes(alpha))
|
112
|
+
end
|
113
|
+
|
114
|
+
def tokens(beta)
|
115
|
+
DeleteSafeEnumerator.new(raw_tokens(beta))
|
116
|
+
end
|
117
|
+
|
118
|
+
def add_wme(wme, alpha)
|
119
|
+
wmes = raw_wmes(alpha)
|
120
|
+
wmes << wme unless wmes.include?(wme)
|
121
|
+
end
|
122
|
+
|
123
|
+
def remove_wme(wme, alpha)
|
124
|
+
raw_wmes(alpha).delete(wme)
|
125
|
+
end
|
126
|
+
|
127
|
+
def add_token(token, beta)
|
128
|
+
tokens = raw_tokens(beta)
|
129
|
+
tokens << token unless tokens.include?(token)
|
130
|
+
end
|
131
|
+
|
132
|
+
def remove_token(token, beta)
|
133
|
+
raw_tokens(beta).delete(token)
|
134
|
+
end
|
135
|
+
|
136
|
+
def raw_wmes(alpha)
|
137
|
+
@raw_wmes[alpha.object_id]
|
138
|
+
end
|
139
|
+
|
140
|
+
def raw_tokens(beta)
|
141
|
+
@raw_tokens[beta.object_id]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
module DSL
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def ruleset name = nil, &definition
|
6
|
+
rs = Ruleset.new
|
7
|
+
if ! name.nil?
|
8
|
+
rs.name name
|
9
|
+
end
|
10
|
+
rs.instance_eval &definition if block_given?
|
11
|
+
rs
|
12
|
+
end
|
13
|
+
|
14
|
+
def rule name = nil, &definition
|
15
|
+
r = Rule.new name
|
16
|
+
r.instance_eval &definition
|
17
|
+
r
|
18
|
+
end
|
19
|
+
|
20
|
+
def query name, &definition
|
21
|
+
q = Query.new name
|
22
|
+
q.instance_eval &definition
|
23
|
+
q
|
24
|
+
end
|
25
|
+
|
26
|
+
def dsl &definition
|
27
|
+
Builder.new.build &definition
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
require 'wongi-engine/dsl/generated'
|
34
|
+
require 'wongi-engine/dsl/builder'
|
35
|
+
require 'wongi-engine/dsl/clause/generic'
|
36
|
+
require 'wongi-engine/dsl/clause/fact'
|
37
|
+
require 'wongi-engine/dsl/clause/assign'
|
38
|
+
require 'wongi-engine/dsl/clause/gen'
|
39
|
+
require 'wongi-engine/dsl/action/base'
|
40
|
+
require 'wongi-engine/dsl/rule'
|
41
|
+
require 'wongi-engine/dsl/ncc_subrule'
|
42
|
+
require 'wongi-engine/dsl/any_rule'
|
43
|
+
require 'wongi-engine/dsl/query'
|
44
|
+
require 'wongi-engine/dsl/assuming'
|
45
|
+
require 'wongi-engine/dsl/action/simple_action'
|
46
|
+
require 'wongi-engine/dsl/action/statement_generator'
|
47
|
+
require 'wongi-engine/dsl/action/simple_collector'
|
48
|
+
require 'wongi-engine/dsl/action/trace_action'
|
49
|
+
require 'wongi-engine/dsl/action/error_generator'
|
50
|
+
|
51
|
+
module Wongi::Engine::DSL
|
52
|
+
dsl {
|
53
|
+
section :forall
|
54
|
+
|
55
|
+
clause :has, :fact
|
56
|
+
accept Clause::Has
|
57
|
+
|
58
|
+
clause :missing, :neg
|
59
|
+
accept Clause::Neg
|
60
|
+
|
61
|
+
clause :none, :ncc
|
62
|
+
accept NccSubrule
|
63
|
+
|
64
|
+
clause :any
|
65
|
+
accept AnyRule
|
66
|
+
|
67
|
+
clause :maybe, :optional
|
68
|
+
accept Clause::Opt
|
69
|
+
|
70
|
+
clause :same, :eq, :equal
|
71
|
+
accept Wongi::Engine::EqualityTest
|
72
|
+
|
73
|
+
clause :diff, :ne
|
74
|
+
accept Wongi::Engine::InequalityTest
|
75
|
+
|
76
|
+
clause :less
|
77
|
+
accept Wongi::Engine::LessThanTest
|
78
|
+
|
79
|
+
clause :greater
|
80
|
+
accept Wongi::Engine::GreaterThanTest
|
81
|
+
|
82
|
+
clause :assert, :dynamic
|
83
|
+
accept Wongi::Engine::AssertingTest
|
84
|
+
|
85
|
+
clause :assign, :introduce
|
86
|
+
accept Clause::Assign
|
87
|
+
|
88
|
+
clause :asserted, :added
|
89
|
+
body { |s, p, o|
|
90
|
+
has s, p, o, time: 0
|
91
|
+
missing s, p, o, time: -1
|
92
|
+
}
|
93
|
+
|
94
|
+
clause :retracted, :removed
|
95
|
+
body { |s, p, o|
|
96
|
+
has s, p, o, time: -1
|
97
|
+
missing s, p, o, time: 0
|
98
|
+
}
|
99
|
+
|
100
|
+
clause :kept, :still_has
|
101
|
+
body { |s, p, o|
|
102
|
+
has s, p, o, time: -1
|
103
|
+
has s, p, o, time: 0
|
104
|
+
}
|
105
|
+
|
106
|
+
clause :kept_missing, :still_missing
|
107
|
+
body { |s, p, o|
|
108
|
+
missing s, p, o, time: -1
|
109
|
+
missing s, p, o, time: 0
|
110
|
+
}
|
111
|
+
|
112
|
+
clause :assuming
|
113
|
+
accept Wongi::Engine::AssumingClause
|
114
|
+
|
115
|
+
section :make
|
116
|
+
|
117
|
+
clause :gen
|
118
|
+
accept Clause::Gen
|
119
|
+
|
120
|
+
clause :trace
|
121
|
+
action Action::TraceAction
|
122
|
+
|
123
|
+
clause :error
|
124
|
+
action Action::ErrorGenerator
|
125
|
+
|
126
|
+
clause :collect
|
127
|
+
action Action::SimpleCollector
|
128
|
+
|
129
|
+
clause :action
|
130
|
+
action Action::SimpleAction
|
131
|
+
}
|
132
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
module DSL::Action
|
3
|
+
class ErrorGenerator < Base
|
4
|
+
|
5
|
+
def initialize message = nil, &messenger
|
6
|
+
@message, @messenger = message, messenger
|
7
|
+
end
|
8
|
+
|
9
|
+
def rete=(*)
|
10
|
+
super
|
11
|
+
rete.add_collector self, :error
|
12
|
+
end
|
13
|
+
|
14
|
+
def errors
|
15
|
+
production.tokens.map do |token|
|
16
|
+
message = if @messenger
|
17
|
+
@messenger.call token.assignments
|
18
|
+
else
|
19
|
+
@message
|
20
|
+
end
|
21
|
+
ReteError.new token, message, literate?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def literate?
|
26
|
+
not @messenger.nil?
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
module DSL::Action
|
3
|
+
class SimpleAction < Base
|
4
|
+
def initialize action = nil, *args, &block
|
5
|
+
@args = args
|
6
|
+
case action
|
7
|
+
when Class
|
8
|
+
@action = @deaction = @reaction = action.new *args, &block
|
9
|
+
when Hash
|
10
|
+
@action = instance_or_proc action[:activate]
|
11
|
+
@deaction = instance_or_proc action[:deactivate]
|
12
|
+
@reaction = instance_or_proc action[:reactivate]
|
13
|
+
end
|
14
|
+
@action ||= block
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute token
|
18
|
+
return unless @action
|
19
|
+
if @action.is_a?( Proc ) || @action.respond_to?( :to_proc )
|
20
|
+
rete.instance_exec token, &@action
|
21
|
+
elsif @action.respond_to? :call
|
22
|
+
@action.call token
|
23
|
+
elsif @action.respond_to? :execute
|
24
|
+
@action.execute token
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def deexecute token
|
29
|
+
return unless @deaction
|
30
|
+
if @deaction.is_a?( Proc ) || @deaction.respond_to?( :to_proc )
|
31
|
+
rete.instance_exec token, &@deaction
|
32
|
+
elsif @deaction.respond_to? :call
|
33
|
+
@deaction.call token
|
34
|
+
elsif @deaction.respond_to? :deexecute
|
35
|
+
@deaction.execute token
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def reexecute token, newtoken
|
40
|
+
return unless @reaction
|
41
|
+
if @reaction.is_a?( Proc ) || @reaction.respond_to?( :to_proc )
|
42
|
+
rete.instance_exec token, newtoken, &@reaction
|
43
|
+
elsif @reaction.respond_to? :call
|
44
|
+
@reaction.call token, newtoken
|
45
|
+
elsif @reaction.respond_to? :reexecute
|
46
|
+
@reaction.execute token, newtoken
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def instance_or_proc thing
|
51
|
+
case thing
|
52
|
+
when Class
|
53
|
+
thing.new
|
54
|
+
when Proc
|
55
|
+
thing
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
module DSL::Action
|
3
|
+
class SimpleCollector < Base
|
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
|
52
|
+
end
|