mort666-wongi-engine 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|