wongi-engine 0.0.2 → 0.0.3

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 CHANGED
@@ -3,6 +3,8 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
+ .rspec
7
+ .rvmrc
6
8
  Gemfile.lock
7
9
  InstalledFiles
8
10
  _yardoc
data/README.md CHANGED
@@ -81,7 +81,9 @@ If you don't care about a specific field's value, you can use the all-matcher `:
81
81
 
82
82
  Once a variable is bound, it can be used to match further facts within a rule. Let's add another friendship:
83
83
 
84
- engine << [ "Bob", "friend", "Claire" ]
84
+ ```ruby
85
+ engine << [ "Bob", "friend", "Claire" ]
86
+ ```
85
87
 
86
88
  and another rule:
87
89
 
@@ -141,7 +143,7 @@ engine.rule "self-printer" do
141
143
  end
142
144
  ```
143
145
 
144
- The `make` section (also spelled `do!`, if you find it more agreeable English, because `do` is a keyword in Ruby) lists everything that happens when a rule's conditions are fully matched (we say the production node is activated). Wongi::Engine provides only a small amount of build-in actions, but you can define define your own ones, and the simplest one is just `action` with a block.
146
+ The `make` section (also spelled `do!`, if you find it more agreeable English, because `do` is a keyword in Ruby) lists everything that happens when a rule's conditions are fully matched (we say "the production node is **activated**"). Wongi::Engine provides only a small amount of built-in actions, but you can define your own ones, and the simplest one is just `action` with a block.
145
147
 
146
148
  ### More facts!
147
149
 
@@ -163,7 +165,7 @@ engine << ["friend", "symmetric", true]
163
165
 
164
166
  If you still have the "self-printer" rule installed, you will see some new friendships pop up immediately!
165
167
 
166
- The built-in `gen` action creates new facts, taking either fixed values or variables as arguments. (It will complain if use provide a variable that isn't bound by the time it's activated.) Here, it takes all relations we've defined to be [symmetric](http://en.wikipedia.org/wiki/Symmetric_relation), finds all couples in those sorts of relations and turns them around.
168
+ The built-in `gen` action creates new facts, taking either fixed values or variables as arguments. (It will complain if you provide a variable that isn't bound by the time it's activated.) Here, it takes all relations we've defined to be [symmetric](http://en.wikipedia.org/wiki/Symmetric_relation), finds all couples in those sorts of relations and turns them around.
167
169
 
168
170
  ### Matchers
169
171
 
@@ -175,7 +177,7 @@ Passes if the specified template does *not* match anything in the dataset. Alias
175
177
 
176
178
  #### `maybe subject, predicate, object`
177
179
 
178
- Passes whether or not the template matches anything. It's only useful if it introduces a new variable. Alias: `optional`.
180
+ Passes whether or not the template matches anything. It's only useful if it introduces a new variable; you can think of `LEFT JOIN`. Alias: `optional`.
179
181
 
180
182
  #### `none { ... }`
181
183
 
@@ -193,13 +195,23 @@ Passes if the arguments are equal. Alias: `eq`, `equal`.
193
195
 
194
196
  Passes if the arguments are not equal. Alias: `ne`.
195
197
 
198
+ #### `assert { |token| ... }`, `assert var1, var2, ... do |val1, val2, ... | ... end`
199
+
200
+ Passes if the block evaluates to `true`. Having no arguments passes the entire token as an argument, listing some variables passes only their values.
201
+
202
+ #### `assign variable do |token| ... end`
203
+
204
+ Not a *matcher*, strictly speaking, because it always passes. What it does instead is introduce a new variable bound to the block's return value.
205
+
196
206
  ### Timeline
197
207
 
198
208
  Wongi::Engine has a limited concept of timed facts: time is discrete and only extends into the past. Matchers that accept a triple specification (`has`, `neg` and `maybe`) can also accept a fourth parameter, an integer <= 0, which will make them look at a past state of the system. "0" means the current state and is the default value, "-1" means the one just before the current, and so on.
199
209
 
200
210
  To create past states, say:
201
211
 
202
- engine.snapshot!
212
+ ```ruby
213
+ engine.snapshot!
214
+ ```
203
215
 
204
216
  This will shift all facts one step into the past. The new current state will be a copy of the last one. You can only insert new facts into the current state, "retroactive" facts are not allowed.
205
217
 
@@ -14,7 +14,7 @@ module Wongi::Engine
14
14
 
15
15
  def activate wme
16
16
  betas.each do |beta|
17
- beta.right_activate wme
17
+ beta.alpha_activate wme
18
18
  end
19
19
  @wmes << wme
20
20
  wme.alphas << self
@@ -0,0 +1,39 @@
1
+ module Wongi::Engine
2
+
3
+ class Assignment
4
+
5
+ def initialize variable, &body
6
+ @variable, @body = variable, body
7
+ end
8
+
9
+ def compile context
10
+ context.node = context.node.beta_memory.assignment_node( @variable, @body )
11
+ context.earlier << self
12
+ context
13
+ end
14
+
15
+ end
16
+
17
+ class AssignmentNode < BetaNode
18
+
19
+ def initialize parent, variable, body
20
+ super parent
21
+ @variable, @body = variable, body
22
+ end
23
+
24
+ def beta_activate token, wme = nil, assignments = { }
25
+ propagate_activation token, nil, { @variable => @body.call(token) }
26
+ end
27
+
28
+ def refresh_child child
29
+ tmp = children
30
+ self.children = [ child ]
31
+ parent.tokens.each do |token|
32
+ beta_activate token
33
+ end
34
+ self.children = tmp
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -1,7 +1,7 @@
1
1
  module Wongi::Engine
2
2
 
3
3
  class BetaMemory < BetaNode
4
- attr_reader :tokens, :last_token
4
+ attr_reader :tokens
5
5
 
6
6
  def initialize parent
7
7
  super
@@ -9,35 +9,49 @@ module Wongi::Engine
9
9
  end
10
10
 
11
11
  def seed assignments = {}
12
+ @seed = assignments
12
13
  t = Token.new( nil, nil, assignments )
13
14
  t.node = self
14
15
  @tokens << t
15
16
  end
16
17
 
17
18
  def subst valuations
18
- token = @tokens.first
19
- token.delete true
19
+ @tokens.first.delete
20
+
21
+ token = Token.new( nil, nil, @seed )
22
+ token.node = self
23
+ @tokens << token
24
+
20
25
  valuations.each { |variable, value| token.subst variable, value }
21
26
  self.children.each do |child|
22
- child.left_activate token
27
+ child.beta_activate token
23
28
  end
24
29
  end
25
30
 
26
- def left_activate token, wme, assignments
31
+ def beta_activate token, wme, assignments
27
32
  # puts "MEMORY #{@id} left-activated with #{wme}"
28
33
  t = Token.new( token, wme, assignments)
29
34
  t.node = self
30
- @last_token = t
31
35
  @tokens << t
32
36
  self.children.each do |child|
33
37
  if child.kind_of? BetaMemory
34
- child.left_activate t, nil, {}
38
+ child.beta_activate t, nil, {}
35
39
  else
36
- child.left_activate t
40
+ child.beta_activate t
37
41
  end
38
42
  end
39
43
  end
40
44
 
45
+ def refresh_child child
46
+ tokens.each do |token|
47
+ child.beta_activate token, nil, {}
48
+ end
49
+ end
50
+
51
+ def delete_token token
52
+ tokens.delete token
53
+ end
54
+
41
55
  # => TODO: investigate if we really need this
42
56
  #def beta_memory
43
57
  # self
@@ -27,7 +27,7 @@ module Wongi::Engine
27
27
 
28
28
 
29
29
  def rete
30
- if parent
30
+ @rete ||= if parent
31
31
  parent.rete
32
32
  else
33
33
  @rete
@@ -39,7 +39,7 @@ module Wongi::Engine
39
39
  beta = children.find { |node| BetaMemory === node }
40
40
  if beta.nil?
41
41
  beta = BetaMemory.new self
42
- beta.update_above
42
+ beta.refresh
43
43
  end
44
44
  beta
45
45
  end
@@ -67,14 +67,20 @@ module Wongi::Engine
67
67
  return existing if existing
68
68
 
69
69
  node = FilterNode.new self, test
70
- node.update_above
70
+ node.refresh
71
+ node
72
+ end
73
+
74
+ def assignment_node variable, body
75
+ node = AssignmentNode.new self, variable, body
76
+ node.refresh
71
77
  node
72
78
  end
73
79
 
74
80
  def neg_node alpha, tests, alpha_deaf
75
81
  node = NegNode.new self, tests, alpha
76
82
  alpha.betas << node unless alpha_deaf
77
- node.update_above
83
+ node.refresh
78
84
  node
79
85
  end
80
86
 
@@ -91,8 +97,8 @@ module Wongi::Engine
91
97
  partner.ncc = ncc
92
98
  partner.divergent = self
93
99
  # partner.conjuncts = condition.children.size
94
- ncc.update_above
95
- partner.update_above
100
+ ncc.refresh
101
+ partner.refresh
96
102
  ncc
97
103
  end
98
104
 
@@ -104,61 +110,26 @@ module Wongi::Engine
104
110
  end.node
105
111
  end
106
112
 
107
- def update_above
108
- update_from self.parent
113
+ def refresh
114
+ parent.refresh_child self
115
+ end
116
+
117
+ def refresh_child node
118
+ raise "#{self.class} must implement refresh_child"
119
+ end
120
+
121
+ def delete_token token
122
+ # => noop
109
123
  end
110
124
 
111
125
  private
112
126
 
113
127
  def propagate_activation token, wme, assignments
114
128
  self.children.each do |child|
115
- child.left_activate token, wme, assignments
129
+ child.beta_activate token, wme, assignments
116
130
  end
117
131
  end
118
132
 
119
- def update_from parent
120
- case parent
121
-
122
- when BetaMemory
123
- parent.tokens.each do |token|
124
- self.left_activate token, nil, {}
125
- end
126
-
127
- when JoinNode, OptionalNode
128
- tmp = parent.children
129
- parent.children = [ self ]
130
- parent.alpha.wmes.each do |wme|
131
- parent.right_activate wme
132
- end
133
- parent.children = tmp
134
-
135
- when FilterNode
136
- tmp = parent.children
137
- parent.children = [ self ]
138
- parent.parent.tokens.each do |token|
139
- parent.left_activate token
140
- end
141
- parent.children = tmp
142
-
143
- when NegNode
144
- parent.tokens.each do |token|
145
- if token.neg_join_results.empty?
146
- left_activate token, nil, {}
147
- end
148
- end
149
- parent.alpha.wmes.each do |wme|
150
- parent.right_activate wme
151
- end
152
-
153
- when NccNode
154
- parent.tokens.each do |token|
155
- if token.ncc_results.empty?
156
- left_activate token, nil, {}
157
- end
158
- end
159
-
160
- end # => case
161
- end
162
133
  end
163
134
 
164
135
  end
@@ -1,91 +1,6 @@
1
1
  module Wongi
2
2
  module Engine
3
3
 
4
-
5
- class FilterTest
6
-
7
- def passes? token
8
- true
9
- end
10
-
11
- def compile context
12
- context.node = context.node.beta_memory.filter_node( self )
13
- context.earlier << self
14
- context
15
- end
16
-
17
- def == other
18
- self.class == other.class
19
- end
20
-
21
- end
22
-
23
- class EqualityTest < FilterTest
24
-
25
- attr_reader :x, :y
26
-
27
- def initialize x, y
28
- @x, @y = x, y
29
- end
30
-
31
- def passes? token
32
-
33
- x = if Template.variable? @x
34
- token[@x]
35
- else
36
- @x
37
- end
38
-
39
- y = if Template.variable? @y
40
- token[@y]
41
- else
42
- @y
43
- end
44
-
45
- return false if x == :_ || y == :_
46
- return x == y
47
-
48
- end
49
-
50
- def == other
51
- super && x == other.x && y == other.y
52
- end
53
-
54
- end
55
-
56
- class InequalityTest < FilterTest
57
-
58
- attr_reader :x, :y
59
-
60
- def initialize x, y
61
- @x, @y = x, y
62
- end
63
-
64
- def passes? token
65
-
66
- x = if Template.variable? @x
67
- token[@x]
68
- else
69
- @x
70
- end
71
-
72
- y = if Template.variable? @y
73
- token[@y]
74
- else
75
- @y
76
- end
77
-
78
- return false if x == :_ || y == :_
79
- return x != y
80
-
81
- end
82
-
83
- def == other
84
- super && x == other.x && y == other.y
85
- end
86
-
87
- end
88
-
89
4
  class FilterNode < BetaNode
90
5
 
91
6
  attr_accessor :test
@@ -95,7 +10,7 @@ module Wongi
95
10
  self.test = test
96
11
  end
97
12
 
98
- def left_activate token
13
+ def beta_activate token, wme = nil, assignments = { }
99
14
  if test.passes? token
100
15
  propagate_activation token, nil, {}
101
16
  end
@@ -104,6 +19,17 @@ module Wongi
104
19
  def equivalent? test
105
20
  test == self.test
106
21
  end
22
+
23
+ def refresh_child child
24
+ tmp = children
25
+ self.children = [ child ]
26
+ parent.tokens.each do |token|
27
+ beta_activate token
28
+ end
29
+ self.children = tmp
30
+ end
31
+
107
32
  end
33
+
108
34
  end
109
35
  end
@@ -51,7 +51,7 @@ module Wongi
51
51
  # puts "\talpha = #{alpha}"
52
52
  end
53
53
 
54
- def right_activate wme
54
+ def alpha_activate wme
55
55
  ws = ' ' * depth
56
56
  # puts "#{ws}JOIN #{@id} right-activated with #{wme}"
57
57
  collected = collect_assignments( wme )
@@ -65,7 +65,7 @@ module Wongi
65
65
  end
66
66
  end
67
67
 
68
- def left_activate token
68
+ def beta_activate token
69
69
  ws = ' ' * depth
70
70
  self.alpha.wmes.uniq.each do |wme|
71
71
  if matches?( token, wme )
@@ -74,6 +74,15 @@ module Wongi
74
74
  end
75
75
  end
76
76
 
77
+ def refresh_child child
78
+ tmp = children
79
+ self.children = [ child ]
80
+ alpha.wmes.each do |wme|
81
+ alpha_activate wme
82
+ end
83
+ self.children = tmp
84
+ end
85
+
77
86
  def self.compile condition, earlier, parameters
78
87
  tests = []
79
88
  assignment = Template.new
@@ -114,7 +123,7 @@ module Wongi
114
123
  return assignments if self.assignment_pattern.nil?
115
124
  # puts "more assignments"
116
125
  [:subject, :predicate, :object].each do |field|
117
- if self.assignment_pattern.send(field)
126
+ if self.assignment_pattern.send(field) != :_
118
127
  #puts "#{self.assignment_pattern.send(field)} = #{wme.send(field)}"
119
128
  assignments[ self.assignment_pattern.send(field) ] = wme.send(field)
120
129
  end
@@ -25,11 +25,12 @@ module Wongi
25
25
  @tokens = []
26
26
  end
27
27
 
28
- # def left_activate token, wme, assignments
28
+ # def beta_activate token, wme, assignments
29
29
  # t = Token.new token, wme, assignments
30
30
  # t.node = self
31
- def left_activate token, wme = nil, assignments = { } # => FIXME: left_activate has different signatures for storing and non-storing nodes...
31
+ def beta_activate token, wme = nil, assignments = { } # => FIXME: beta_activate has different signatures for storing and non-storing nodes...
32
32
  t = Token.new token, nil, {}
33
+ t.node = self
33
34
  tokens << t
34
35
  partner.tokens.each do |ncc_token|
35
36
  t.ncc_results << ncc_token
@@ -37,10 +38,28 @@ module Wongi
37
38
  end
38
39
  if t.ncc_results.empty?
39
40
  children.each do |child|
40
- child.left_activate t, nil, {}
41
+ child.beta_activate t, nil, {}
41
42
  end
42
43
  end
43
44
  end
45
+
46
+ def refresh_child child
47
+ tokens.each do |token|
48
+ if token.ncc_results.empty?
49
+ child.beta_activate token, nil, {}
50
+ end
51
+ end
52
+ end
53
+
54
+ def delete_token token
55
+ tokens.delete token
56
+ token.ncc_results.each do |nccr|
57
+ nccr.wme.tokens.delete nccr
58
+ nccr.parent.children.delete nccr
59
+ end
60
+ token.ncc_results.clear
61
+ end
62
+
44
63
  end
45
64
  end
46
65
  end
@@ -13,22 +13,11 @@ module Wongi
13
13
  @tokens = []
14
14
  end
15
15
 
16
- def left_activate token
16
+ def beta_activate token
17
17
  t = Token.new token, nil, {}
18
18
  t.node = self
19
- # owners_t = t
20
- # # owners_w = t.wme
21
- # conjuncts.times do
22
- # # owners_w = owners_t.wme
23
- # owners_t = owners_t.parent
24
- # end
25
- owner = nil
26
- ncc.tokens.each do |ncc_token|
27
- # if ncc_token.parent == owners_t
28
- if ncc_token.parent.node == divergent
29
- owner = ncc_token
30
- break
31
- end
19
+ owner = ncc.tokens.find do |ncc_token|
20
+ ncc_token.parent.node == divergent
32
21
  end
33
22
  if owner
34
23
  owner.ncc_results << t
@@ -38,6 +27,16 @@ module Wongi
38
27
  tokens << t
39
28
  end
40
29
  end
30
+
31
+ def delete_token token
32
+ token.owner.ncc_results.delete token
33
+ if token.owner.ncc_results.empty?
34
+ ncc.children.each do |node|
35
+ node.beta_activate token.owner, nil, {}
36
+ end
37
+ end
38
+
39
+ end
41
40
  end
42
41
  end
43
42
  end
@@ -13,7 +13,7 @@ module Wongi
13
13
  @tokens = []
14
14
  end
15
15
 
16
- def right_activate wme
16
+ def alpha_activate wme
17
17
  self.tokens.each do |token|
18
18
  if matches?( token, wme )
19
19
  token.delete_children if token.neg_join_results.empty?
@@ -22,7 +22,7 @@ module Wongi
22
22
  end
23
23
  end
24
24
 
25
- def left_activate token, newwme, assignments
25
+ def beta_activate token, newwme, assignments
26
26
  t = Token.new token, newwme, assignments
27
27
  t.node = self
28
28
  self.tokens << t
@@ -33,11 +33,30 @@ module Wongi
33
33
  end
34
34
  if t.neg_join_results.empty?
35
35
  self.children.each do |child|
36
- child.left_activate t, nil, {}
36
+ child.beta_activate t, nil, {}
37
37
  end
38
38
  end
39
39
  end
40
40
 
41
+ def refresh_child child
42
+ tokens.each do |token|
43
+ if token.neg_join_results.empty?
44
+ child.beta_activate token, nil, {}
45
+ end
46
+ end
47
+ alpha.wmes.each do |wme|
48
+ alpha_activate wme
49
+ end
50
+ end
51
+
52
+ def delete_token token
53
+ tokens.delete token
54
+ token.neg_join_results.each do |njr|
55
+ njr.wme.neg_join_results.delete njr if njr.wme
56
+ end
57
+ token.neg_join_results.clear
58
+ end
59
+
41
60
  protected
42
61
 
43
62
  def matches? token, wme
@@ -5,7 +5,7 @@ module Wongi
5
5
 
6
6
  class OptionalNode < JoinNode
7
7
 
8
- def right_activate wme
8
+ def alpha_activate wme
9
9
  parent.tokens.each do |token|
10
10
  if matches? token, wme
11
11
  if token.has_optional?
@@ -20,7 +20,7 @@ module Wongi
20
20
  end
21
21
  end
22
22
 
23
- def left_activate token
23
+ def beta_activate token
24
24
  match = false
25
25
  alpha.wmes.each do |wme|
26
26
  assignments = collect_assignments(wme)
@@ -38,6 +38,23 @@ module Wongi
38
38
  end
39
39
  end
40
40
 
41
+ def refresh_child child
42
+ tmp = children
43
+ self.children = [ child ]
44
+ alpha.wmes.each do |wme|
45
+ alpha_activate wme
46
+ end
47
+ self.children = tmp
48
+ end
49
+
50
+ def delete_token token
51
+ tokens.delete token
52
+ token.opt_join_results.each do |ojr|
53
+ ojr.wme.opt_join_results.delete ojr
54
+ end
55
+ token.opt_join_results.clear
56
+ end
57
+
41
58
  end
42
59
  end
43
60
  end
@@ -18,7 +18,7 @@ module Wongi
18
18
  end
19
19
  context.earlier += added
20
20
  context.node = OrNode.new( branches )
21
- context.node.update_above
21
+ context.node.refresh
22
22
  context
23
23
  end
24
24
 
@@ -65,9 +65,9 @@ module Wongi
65
65
  parents.map( &:depth ).max + 1
66
66
  end
67
67
 
68
- def update_above
68
+ def refresh
69
69
  parents.each do |parent|
70
- update_from parent
70
+ parent.refresh_child self
71
71
  end
72
72
  end
73
73
 
@@ -11,20 +11,16 @@ module Wongi
11
11
  @actions.each { |action| action.production = self }
12
12
  end
13
13
 
14
- def left_activate token, wme, assignments
14
+ def beta_activate token, wme, assignments
15
15
  super
16
16
  @actions.each do |action|
17
17
  # @tokens.each do |t|
18
18
  # action.execute t
19
19
  # end
20
- action.execute last_token if action.respond_to? :execute
20
+ action.execute tokens.last if action.respond_to? :execute
21
21
  end
22
22
  end
23
23
 
24
- # => TODO: investigate
25
- def deexecute token
26
-
27
- end
28
24
  end
29
25
 
30
26
  end