wongi-engine 0.4.0.pre.alpha1 → 0.4.0.pre.alpha3
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 +4 -4
- data/.rubocop.yml +0 -3
- data/README.md +1 -1
- data/lib/wongi-engine/beta/aggregate_node.rb +2 -1
- data/lib/wongi-engine/beta/beta_node.rb +9 -0
- data/lib/wongi-engine/beta/join_node.rb +3 -2
- data/lib/wongi-engine/beta/ncc_partner.rb +1 -1
- data/lib/wongi-engine/beta/neg_node.rb +5 -6
- data/lib/wongi-engine/beta/optional_node.rb +3 -1
- data/lib/wongi-engine/compiler.rb +2 -2
- data/lib/wongi-engine/dsl/action/statement_generator.rb +34 -10
- data/lib/wongi-engine/dsl/clause/fact.rb +1 -4
- data/lib/wongi-engine/version.rb +1 -1
- data/spec/action_class_spec.rb +1 -1
- data/spec/{rule_specs/aggregate_spec.rb → aggregate_spec.rb} +1 -1
- data/spec/{rule_specs/any_rule_spec.rb → any_rule_spec.rb} +4 -4
- data/spec/{filter_specs/assert_test_spec.rb → assert_test_spec.rb} +5 -5
- data/spec/{rule_specs/assign_spec.rb → assign_spec.rb} +6 -6
- data/spec/{rule_specs/assuming_spec.rb → assuming_spec.rb} +3 -3
- data/spec/beta_node_spec.rb +1 -1
- data/spec/bug_specs/issue_4_spec.rb +4 -4
- data/spec/dataset_spec.rb +1 -1
- data/spec/generation_spec.rb +7 -7
- data/spec/{filter_specs/greater_than_equality_test_spec.rb → greater_than_equality_test_spec.rb} +1 -1
- data/spec/high_level_spec.rb +12 -12
- data/spec/{filter_specs/less_test_spec.rb → less_test_spec.rb} +1 -1
- data/spec/{filter_specs/less_than_equality_test_spec.rb → less_than_equality_test_spec.rb} +1 -1
- data/spec/{rule_specs/maybe_rule_spec.rb → maybe_rule_spec.rb} +7 -7
- data/spec/{rule_specs/ncc_spec.rb → ncc_spec.rb} +28 -5
- data/spec/{rule_specs/negative_rule_spec.rb → negative_rule_spec.rb} +3 -25
- data/spec/network_spec.rb +4 -3
- data/spec/overlay_spec.rb +3 -2
- data/spec/ruleset_spec.rb +6 -6
- data/spec/simple_action_spec.rb +1 -1
- data/spec/wme_spec.rb +4 -4
- metadata +14 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3401b29a9b186b8594549d45feddda88e885dae9a03d42f7006ad7e6fecbd0b
|
4
|
+
data.tar.gz: c315eb40f0c2a05d6a5995016fd700b6ecdb7795588fa929b81b68d86722c481
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac9b80adde1174fec2361b6411d500a2c5c3c4c2659bac11d42bc630b088ccede7e3bf7d407560d099206daec5da97b0379d4c949e9891c9981d2f5847a815aa
|
7
|
+
data.tar.gz: 03fa343a219317c31384446debde04894d00841bd6b780a45af31968fc5a1af8fb5ca04f2d6bf17bd445e046ab8de76828c797da47ac89f68715405e128a404a
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -11,7 +11,7 @@ Ruby >= 2.7 and JRuby are supported. Rubinius should work but isn't actively sup
|
|
11
11
|
|
12
12
|
There is no API documentation, as most of the library's interfaces are for internal use only and would not be safe to use directly.
|
13
13
|
|
14
|
-
Instead, follow the [tutorial](
|
14
|
+
Instead, follow the [tutorial](https://ulfurinn.github.io/wongi-engine/) and stick to the constructs described in it.
|
15
15
|
|
16
16
|
## Upgrading
|
17
17
|
|
@@ -62,7 +62,8 @@ module Wongi::Engine
|
|
62
62
|
# # TODO: optimise: only clean up if the value changed
|
63
63
|
beta_deactivate_children(token: token)
|
64
64
|
|
65
|
-
|
65
|
+
template = specialize(alpha.template, tests, token)
|
66
|
+
candidates = select_wmes(template) { |asserted_wme| matches?(token, asserted_wme) }
|
66
67
|
|
67
68
|
return if candidates.empty?
|
68
69
|
|
@@ -61,6 +61,15 @@ module Wongi::Engine
|
|
61
61
|
rete.current_overlay.select(template)
|
62
62
|
end
|
63
63
|
|
64
|
+
private def specialize(template, tests, token)
|
65
|
+
tests.each_with_object(template.dup) do |test, template|
|
66
|
+
var = test.variable
|
67
|
+
if token.has_var?(var)
|
68
|
+
template.public_send("#{test.field}=", token[var])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
64
73
|
def tokens
|
65
74
|
overlay.node_tokens(self)
|
66
75
|
end
|
@@ -65,7 +65,8 @@ module Wongi
|
|
65
65
|
|
66
66
|
overlay.add_token(token)
|
67
67
|
|
68
|
-
|
68
|
+
template = specialize(alpha.template, tests, token)
|
69
|
+
select_wmes(template).each do |wme|
|
69
70
|
next unless matches?(token, wme)
|
70
71
|
|
71
72
|
assignments = collect_assignments(wme)
|
@@ -93,7 +94,7 @@ module Wongi
|
|
93
94
|
protected
|
94
95
|
|
95
96
|
def matches?(token, wme)
|
96
|
-
|
97
|
+
tests.each do |test|
|
97
98
|
return false unless test.matches?(token, wme)
|
98
99
|
end
|
99
100
|
true
|
@@ -5,17 +5,16 @@ module Wongi
|
|
5
5
|
class NegNode < BetaNode
|
6
6
|
attr_reader :alpha, :tests
|
7
7
|
|
8
|
-
def initialize(parent, tests, alpha
|
8
|
+
def initialize(parent, tests, alpha)
|
9
9
|
super(parent)
|
10
10
|
@tests = tests
|
11
11
|
@alpha = alpha
|
12
|
-
@unsafe = unsafe
|
13
12
|
end
|
14
13
|
|
15
14
|
def alpha_activate(wme, children: self.children)
|
16
15
|
# p alpha_activate: {class: self.class, object_id:, wme:}
|
17
16
|
tokens.each do |token|
|
18
|
-
next unless matches?(token, wme)
|
17
|
+
next unless matches?(token, wme)
|
19
18
|
|
20
19
|
# order matters for proper invalidation
|
21
20
|
overlay.add_neg_join_result(NegJoinResult.new(token, wme))
|
@@ -44,7 +43,9 @@ module Wongi
|
|
44
43
|
return if tokens.find { |t| t.duplicate? token }
|
45
44
|
|
46
45
|
overlay.add_token(token)
|
47
|
-
|
46
|
+
|
47
|
+
template = specialize(alpha.template, tests, token)
|
48
|
+
select_wmes(template).each do |wme|
|
48
49
|
overlay.add_neg_join_result(NegJoinResult.new(token, wme)) if matches?(token, wme)
|
49
50
|
end
|
50
51
|
return if overlay.neg_join_results_for(token: token).any?
|
@@ -69,8 +70,6 @@ module Wongi
|
|
69
70
|
end
|
70
71
|
end
|
71
72
|
|
72
|
-
protected
|
73
|
-
|
74
73
|
def matches?(token, wme)
|
75
74
|
puts "matching #{wme} against #{token}" if debug?
|
76
75
|
@tests.each do |test|
|
@@ -55,8 +55,10 @@ module Wongi
|
|
55
55
|
return if tokens.find { |t| t.duplicate? token }
|
56
56
|
|
57
57
|
overlay.add_token(token)
|
58
|
+
|
58
59
|
match = false
|
59
|
-
|
60
|
+
template = specialize(alpha.template, tests, token)
|
61
|
+
select_wmes(template).each do |wme|
|
60
62
|
assignments = collect_assignments(wme)
|
61
63
|
next unless matches? token, wme
|
62
64
|
|
@@ -42,9 +42,9 @@ module Wongi::Engine
|
|
42
42
|
node.tap(&:refresh)
|
43
43
|
end
|
44
44
|
|
45
|
-
def neg_node(condition, tests
|
45
|
+
def neg_node(condition, tests)
|
46
46
|
alpha = rete.compile_alpha(condition)
|
47
|
-
self.node = NegNode.new(node, tests, alpha
|
47
|
+
self.node = NegNode.new(node, tests, alpha).tap do |node|
|
48
48
|
alpha.betas << node unless alpha_deaf
|
49
49
|
node.refresh
|
50
50
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "set"
|
2
|
+
|
1
3
|
module Wongi::Engine
|
2
4
|
module DSL::Action
|
3
5
|
class StatementGenerator < BaseAction
|
@@ -16,23 +18,45 @@ module Wongi::Engine
|
|
16
18
|
subject, predicate, object = template.resolve!(token)
|
17
19
|
|
18
20
|
wme = WME.new(subject, predicate, object)
|
19
|
-
|
20
|
-
origin = GeneratorOrigin.new(token, self)
|
21
|
+
wme = overlay.find(wme) || wme
|
21
22
|
|
22
23
|
production.tracer.trace(action: self, wme: wme) if production.tracer
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
if overlay.generated?(wme)
|
27
|
-
token.generated_wmes << existing
|
28
|
-
overlay.assert(existing, generator: origin)
|
29
|
-
end
|
30
|
-
else
|
24
|
+
|
25
|
+
if should_assert?(wme, token)
|
26
|
+
origin = GeneratorOrigin.new(token, self)
|
31
27
|
token.generated_wmes << wme
|
32
28
|
overlay.assert(wme, generator: origin)
|
33
29
|
end
|
34
30
|
end
|
35
31
|
|
32
|
+
private def should_assert?(wme, token)
|
33
|
+
considered_tokens = Set.new
|
34
|
+
tokens_to_consider = [token]
|
35
|
+
until tokens_to_consider.empty?
|
36
|
+
token = tokens_to_consider.shift
|
37
|
+
considered_tokens.add(token)
|
38
|
+
|
39
|
+
# self-affirming reasoning
|
40
|
+
return false if token.wme == wme
|
41
|
+
|
42
|
+
# asserting this WME would invalidate the match
|
43
|
+
return false if token.node.is_a?(NegNode) && token.node.matches?(token, wme)
|
44
|
+
|
45
|
+
if token.parent && !considered_tokens.include?(token.parent)
|
46
|
+
tokens_to_consider.push(token.parent)
|
47
|
+
end
|
48
|
+
|
49
|
+
if token.wme
|
50
|
+
overlay.generators(token.wme).each do |generator|
|
51
|
+
tokens_to_consider.push(generator.token) unless considered_tokens.include?(generator.token)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# we could not prove that the new WME should not be asserted
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
36
60
|
def deexecute(token)
|
37
61
|
origin = GeneratorOrigin.new(token, self)
|
38
62
|
|
@@ -5,7 +5,6 @@ module Wongi::Engine
|
|
5
5
|
attr_predicate :debug
|
6
6
|
|
7
7
|
def initialize(s, p, o, options = {})
|
8
|
-
@unsafe = options[:unsafe] || false
|
9
8
|
debug! if options[:debug]
|
10
9
|
super(s, p, o)
|
11
10
|
end
|
@@ -43,13 +42,11 @@ module Wongi::Engine
|
|
43
42
|
end
|
44
43
|
|
45
44
|
class Neg < Has
|
46
|
-
attr_reader :unsafe
|
47
|
-
|
48
45
|
def compile(context)
|
49
46
|
tests, assignment = parse_variables(context)
|
50
47
|
raise DefinitionError, "Negative matches may not introduce new variables: #{assignment.variables}" unless assignment.root?
|
51
48
|
|
52
|
-
context.tap { |c| c.neg_node(self, tests
|
49
|
+
context.tap { |c| c.neg_node(self, tests) }
|
53
50
|
end
|
54
51
|
|
55
52
|
def inspect
|
data/lib/wongi-engine/version.rb
CHANGED
data/spec/action_class_spec.rb
CHANGED
@@ -9,7 +9,7 @@ describe 'aggregate' do
|
|
9
9
|
let(:production) { engine.productions[rule_name] }
|
10
10
|
|
11
11
|
context 'generic clause' do
|
12
|
-
it '
|
12
|
+
it 'returns a single token' do
|
13
13
|
engine << rule(rule_name) do
|
14
14
|
forall {
|
15
15
|
aggregate :_, :weight, :_, on: :object, function: :min, assign: :X
|
@@ -6,7 +6,7 @@ describe "ANY rule" do
|
|
6
6
|
let(:engine) { Wongi::Engine.create }
|
7
7
|
|
8
8
|
context "with just one option" do
|
9
|
-
it "
|
9
|
+
it "acts like a positive matcher" do
|
10
10
|
engine << rule('one-option') {
|
11
11
|
forall {
|
12
12
|
any {
|
@@ -43,17 +43,17 @@ describe "ANY rule" do
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
it '
|
46
|
+
it 'fires on the first path' do
|
47
47
|
engine << [:x, :path1, true]
|
48
48
|
expect(production.tokens).to have(1).item
|
49
49
|
end
|
50
50
|
|
51
|
-
it '
|
51
|
+
it 'fires on the second path' do
|
52
52
|
engine << [:x, :path2, true]
|
53
53
|
expect(production.tokens).to have(1).item
|
54
54
|
end
|
55
55
|
|
56
|
-
it '
|
56
|
+
it 'fires twice on both paths at once and be retractable' do
|
57
57
|
engine << [:x, :path1, true]
|
58
58
|
engine << [:x, :path2, true]
|
59
59
|
expect(production.tokens).to have(2).items
|
@@ -10,7 +10,7 @@ describe "ASSERT test" do
|
|
10
10
|
@production = (engine << rule('test-rule', &block))
|
11
11
|
end
|
12
12
|
|
13
|
-
it "
|
13
|
+
it "passes with a constant 'true'" do
|
14
14
|
test_rule {
|
15
15
|
forall {
|
16
16
|
assert { |_token|
|
@@ -22,7 +22,7 @@ describe "ASSERT test" do
|
|
22
22
|
expect(production).to have(1).token
|
23
23
|
end
|
24
24
|
|
25
|
-
it "
|
25
|
+
it "fails with a constant 'false'" do
|
26
26
|
test_rule {
|
27
27
|
forall {
|
28
28
|
assert { |_token|
|
@@ -34,7 +34,7 @@ describe "ASSERT test" do
|
|
34
34
|
expect(production).to have(0).tokens
|
35
35
|
end
|
36
36
|
|
37
|
-
it "
|
37
|
+
it "uses the token with no arguments" do
|
38
38
|
test_rule {
|
39
39
|
forall {
|
40
40
|
has :X, "is", :Y
|
@@ -50,7 +50,7 @@ describe "ASSERT test" do
|
|
50
50
|
expect(production.tokens.first[:X]).to eq("resistance")
|
51
51
|
end
|
52
52
|
|
53
|
-
it "
|
53
|
+
it "is retractable" do
|
54
54
|
test_rule {
|
55
55
|
forall {
|
56
56
|
has :X, "is", :Y
|
@@ -65,7 +65,7 @@ describe "ASSERT test" do
|
|
65
65
|
expect(production).to have(0).tokens
|
66
66
|
end
|
67
67
|
|
68
|
-
it "
|
68
|
+
it "uses individual variables with arguments" do
|
69
69
|
test_rule {
|
70
70
|
forall {
|
71
71
|
has :X, "is", :Y
|
@@ -4,7 +4,7 @@ describe "ASSIGN rule" do
|
|
4
4
|
include Wongi::Engine::DSL
|
5
5
|
let(:engine) { Wongi::Engine.create }
|
6
6
|
|
7
|
-
it "
|
7
|
+
it "assigns simple expressions" do
|
8
8
|
production = engine << rule {
|
9
9
|
forall {
|
10
10
|
assign :X do
|
@@ -16,7 +16,7 @@ describe "ASSIGN rule" do
|
|
16
16
|
expect(production.tokens.first[:X]).to eq(42)
|
17
17
|
end
|
18
18
|
|
19
|
-
it '
|
19
|
+
it 'is available in the make section' do
|
20
20
|
production = engine << rule {
|
21
21
|
forall {
|
22
22
|
has 1, 2, :X
|
@@ -32,7 +32,7 @@ describe "ASSIGN rule" do
|
|
32
32
|
expect(production.tokens.first[:Y]).to be == 42
|
33
33
|
end
|
34
34
|
|
35
|
-
it "
|
35
|
+
it "is able to access previous assignments" do
|
36
36
|
production = engine << rule {
|
37
37
|
forall {
|
38
38
|
has 1, 2, :X
|
@@ -46,7 +46,7 @@ describe "ASSIGN rule" do
|
|
46
46
|
expect(production.tokens.first[:Y]).to eq(10)
|
47
47
|
end
|
48
48
|
|
49
|
-
it '
|
49
|
+
it 'is deactivatable' do
|
50
50
|
prod = engine << rule {
|
51
51
|
forall {
|
52
52
|
has 1, 2, :X
|
@@ -62,7 +62,7 @@ describe "ASSIGN rule" do
|
|
62
62
|
expect(prod).to have(0).tokens
|
63
63
|
end
|
64
64
|
|
65
|
-
it '
|
65
|
+
it 'is evaluated once' do
|
66
66
|
x = 0
|
67
67
|
engine << rule {
|
68
68
|
forall {
|
@@ -80,7 +80,7 @@ describe "ASSIGN rule" do
|
|
80
80
|
expect(x).to be == 1
|
81
81
|
end
|
82
82
|
|
83
|
-
it '
|
83
|
+
it 'handles booleans' do
|
84
84
|
engine << rule do
|
85
85
|
forall {
|
86
86
|
has :a, :b, :c
|
@@ -4,7 +4,7 @@ describe Wongi::Engine::AssumingClause do
|
|
4
4
|
include Wongi::Engine::DSL
|
5
5
|
let(:engine) { Wongi::Engine.create }
|
6
6
|
|
7
|
-
it '
|
7
|
+
it 'includes base rules' do
|
8
8
|
engine << rule(:base) {
|
9
9
|
forall {
|
10
10
|
has :x, :y, :Z
|
@@ -26,7 +26,7 @@ describe Wongi::Engine::AssumingClause do
|
|
26
26
|
expect(result).to eq(1 => :a, 2 => :b)
|
27
27
|
end
|
28
28
|
|
29
|
-
it '
|
29
|
+
it 'checks for base rule''s existence' do
|
30
30
|
f = lambda {
|
31
31
|
engine << rule {
|
32
32
|
forall {
|
@@ -38,7 +38,7 @@ describe Wongi::Engine::AssumingClause do
|
|
38
38
|
expect(&f).to raise_error Wongi::Engine::UndefinedBaseRule
|
39
39
|
end
|
40
40
|
|
41
|
-
it '
|
41
|
+
it 'comes first in a rule' do
|
42
42
|
f = lambda {
|
43
43
|
engine << rule(:base) {
|
44
44
|
forall {
|
data/spec/beta_node_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe "issue 4" do
|
4
|
-
it "
|
4
|
+
it "correctlies retract pre-added items from within a rule" do
|
5
5
|
engine = Wongi::Engine.create
|
6
6
|
|
7
7
|
10.times { |i| engine << [i, :is_number, true] }
|
@@ -26,7 +26,7 @@ describe "issue 4" do
|
|
26
26
|
expect(evens.size).to eq(10)
|
27
27
|
end
|
28
28
|
|
29
|
-
it "
|
29
|
+
it "correctlies retract post-added items from within a rule" do
|
30
30
|
engine = Wongi::Engine.create
|
31
31
|
|
32
32
|
engine.rule 'segregate' do
|
@@ -52,7 +52,7 @@ describe "issue 4" do
|
|
52
52
|
end
|
53
53
|
|
54
54
|
# cascaded processing affects this
|
55
|
-
it "
|
55
|
+
it "does not retract later items from within a rule" do
|
56
56
|
engine = Wongi::Engine.create
|
57
57
|
|
58
58
|
10.times { |i| engine << [i, :is_number, true] }
|
@@ -85,7 +85,7 @@ describe "issue 4" do
|
|
85
85
|
expect(odds.size).to eq(5)
|
86
86
|
end
|
87
87
|
|
88
|
-
it "
|
88
|
+
it "does not lose track when another rule affects a set" do
|
89
89
|
engine = Wongi::Engine.create
|
90
90
|
|
91
91
|
10.times { |i| engine << [i, :is_number, true] }
|
data/spec/dataset_spec.rb
CHANGED
data/spec/generation_spec.rb
CHANGED
@@ -20,7 +20,7 @@ describe Wongi::Engine::DSL::Action::StatementGenerator do
|
|
20
20
|
let(:production) { engine << transitive_rule }
|
21
21
|
|
22
22
|
shared_examples 'generation' do
|
23
|
-
it '
|
23
|
+
it 'generates facts' do
|
24
24
|
engine << %w[Alice relative Bob]
|
25
25
|
engine << %w[Bob relative Dwight]
|
26
26
|
|
@@ -28,7 +28,7 @@ describe Wongi::Engine::DSL::Action::StatementGenerator do
|
|
28
28
|
expect(engine.find(*%w[Alice relative Dwight])).not_to be_nil
|
29
29
|
end
|
30
30
|
|
31
|
-
it '
|
31
|
+
it 'retracts generated facts' do
|
32
32
|
engine << %w[Alice relative Bob]
|
33
33
|
engine << %w[Bob relative Dwight]
|
34
34
|
engine.retract %w[Bob relative Dwight]
|
@@ -45,19 +45,19 @@ describe Wongi::Engine::DSL::Action::StatementGenerator do
|
|
45
45
|
engine << %w[Claire relative Dwight]
|
46
46
|
end
|
47
47
|
|
48
|
-
it '
|
48
|
+
it 'is created' do
|
49
49
|
expect(production).to have(2).tokens
|
50
50
|
expect(engine.find(*%w[Alice relative Dwight])).not_to be_nil
|
51
51
|
end
|
52
52
|
|
53
|
-
it '
|
53
|
+
it 'remains after a single retraction' do
|
54
54
|
engine.retract %w[Claire relative Dwight]
|
55
55
|
|
56
56
|
expect(production).to have(1).token
|
57
57
|
expect(engine.find(*%w[Alice relative Dwight])).not_to be_nil
|
58
58
|
end
|
59
59
|
|
60
|
-
it '
|
60
|
+
it 'is destroyed after both retractions' do
|
61
61
|
engine.retract %w[Claire relative Dwight]
|
62
62
|
engine.retract %w[Alice relative Bob]
|
63
63
|
|
@@ -83,7 +83,7 @@ describe Wongi::Engine::DSL::Action::StatementGenerator do
|
|
83
83
|
|
84
84
|
it_behaves_like 'generation'
|
85
85
|
|
86
|
-
it '
|
86
|
+
it 'does not retract generated facts marked as manual', :wip do
|
87
87
|
engine << %w[Alice relative Bob]
|
88
88
|
engine << %w[Bob relative Dwight]
|
89
89
|
engine << %w[Alice relative Dwight]
|
@@ -93,7 +93,7 @@ describe Wongi::Engine::DSL::Action::StatementGenerator do
|
|
93
93
|
expect(engine.find(*%w[Alice relative Dwight])).not_to be_nil
|
94
94
|
end
|
95
95
|
|
96
|
-
it '
|
96
|
+
it 'retracts generated facts unmarked as manual', :wip do
|
97
97
|
engine << %w[Alice relative Bob]
|
98
98
|
engine << %w[Bob relative Dwight]
|
99
99
|
engine << %w[Alice relative Dwight]
|
data/spec/{filter_specs/greater_than_equality_test_spec.rb → greater_than_equality_test_spec.rb}
RENAMED
@@ -12,7 +12,7 @@ describe 'Greater Than Or Equal test' do
|
|
12
12
|
@production = (engine << rule('test-rule', &block))
|
13
13
|
end
|
14
14
|
|
15
|
-
it '
|
15
|
+
it 'interacts with optional node correctly' do
|
16
16
|
# before the fix, filters would try to piggy-back on optional templates
|
17
17
|
|
18
18
|
test_rule do
|
data/spec/high_level_spec.rb
CHANGED
@@ -15,7 +15,7 @@ describe 'the engine' do
|
|
15
15
|
let(:engine) { Wongi::Engine.create }
|
16
16
|
|
17
17
|
context 'with a simple generative positive rule' do
|
18
|
-
it '
|
18
|
+
it 'generates wmes with an existing rule' do
|
19
19
|
engine << rule('symmetric') {
|
20
20
|
forall {
|
21
21
|
has :P, "symmetric", true
|
@@ -35,7 +35,7 @@ describe 'the engine' do
|
|
35
35
|
expect(generated).to eq([Wongi::Engine::WME.new("Bob", "friend", "Alice")])
|
36
36
|
end
|
37
37
|
|
38
|
-
it '
|
38
|
+
it 'generates wmes with an added rule' do
|
39
39
|
engine << Wongi::Engine::WME.new("friend", "symmetric", true)
|
40
40
|
engine << Wongi::Engine::WME.new("Alice", "friend", "Bob")
|
41
41
|
|
@@ -55,7 +55,7 @@ describe 'the engine' do
|
|
55
55
|
expect(engine.facts.select { engine.current_overlay.manual?(_1) }.size).to eq(2)
|
56
56
|
end
|
57
57
|
|
58
|
-
it '
|
58
|
+
it 'does not get confused by recursive activations' do
|
59
59
|
engine << rule('reflexive') {
|
60
60
|
forall {
|
61
61
|
has :Predicate, "reflexive", true
|
@@ -76,7 +76,7 @@ describe 'the engine' do
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
-
it '
|
79
|
+
it 'checks equality' do
|
80
80
|
node = engine << rule('equality') {
|
81
81
|
forall {
|
82
82
|
fact :A, "same", :B
|
@@ -88,7 +88,7 @@ describe 'the engine' do
|
|
88
88
|
expect(node.size).to eq(1)
|
89
89
|
end
|
90
90
|
|
91
|
-
it '
|
91
|
+
it 'compares things' do
|
92
92
|
engine << rule('less') {
|
93
93
|
forall {
|
94
94
|
has :A, :age, :N1
|
@@ -121,7 +121,7 @@ describe 'the engine' do
|
|
121
121
|
expect(items.size).to eq(1)
|
122
122
|
end
|
123
123
|
|
124
|
-
it '
|
124
|
+
it 'uses collectors' do
|
125
125
|
engine << rule('collector') {
|
126
126
|
forall {
|
127
127
|
has :X, :_, 42
|
@@ -139,7 +139,7 @@ describe 'the engine' do
|
|
139
139
|
expect(collection.first).to eq("answer")
|
140
140
|
end
|
141
141
|
|
142
|
-
it "
|
142
|
+
it "properlies show error messages" do
|
143
143
|
engine << rule("Error rule") {
|
144
144
|
forall {
|
145
145
|
has :_, :_, :TestNumber
|
@@ -156,7 +156,7 @@ describe 'the engine' do
|
|
156
156
|
expect(error_messages).to eq(["An error has occurred"])
|
157
157
|
end
|
158
158
|
|
159
|
-
it '
|
159
|
+
it 'uses generic collectors' do
|
160
160
|
engine << rule('generic-collector') {
|
161
161
|
forall {
|
162
162
|
has :X, :_, 42
|
@@ -174,7 +174,7 @@ describe 'the engine' do
|
|
174
174
|
expect(collection.first).to eq("answer")
|
175
175
|
end
|
176
176
|
|
177
|
-
it '
|
177
|
+
it 'accepts several rules' do
|
178
178
|
expect {
|
179
179
|
engine << rule('generic-collector') {
|
180
180
|
forall {
|
@@ -196,7 +196,7 @@ describe 'the engine' do
|
|
196
196
|
}.not_to raise_error
|
197
197
|
end
|
198
198
|
|
199
|
-
it '
|
199
|
+
it 'processes negative nodes' do
|
200
200
|
production = (engine << rule('negative') {
|
201
201
|
forall {
|
202
202
|
neg :_, :_, 42
|
@@ -220,14 +220,14 @@ describe 'the engine' do
|
|
220
220
|
}
|
221
221
|
end
|
222
222
|
|
223
|
-
it '
|
223
|
+
it 'runs' do
|
224
224
|
engine << ["answer", "is", 42]
|
225
225
|
engine.execute "test-query", { X: "answer" }
|
226
226
|
expect(engine.results["test-query"].size).to eq(1)
|
227
227
|
expect(engine.results["test-query"].tokens.first[:Y]).to eq(42)
|
228
228
|
end
|
229
229
|
|
230
|
-
it '
|
230
|
+
it 'runs several times' do
|
231
231
|
engine << ["answer", "is", 42]
|
232
232
|
engine << %w[question is 6x9]
|
233
233
|
engine.execute "test-query", { X: "answer" }
|
@@ -10,7 +10,7 @@ describe "LESS test" do
|
|
10
10
|
@production = (engine << rule('test-rule', &block))
|
11
11
|
end
|
12
12
|
|
13
|
-
it "
|
13
|
+
it "interacts with optional node correctly" do
|
14
14
|
# before the fix, filters would try to piggy-back on optional templates
|
15
15
|
|
16
16
|
test_rule {
|
@@ -12,7 +12,7 @@ describe 'Less Than Or Equal test' do
|
|
12
12
|
@production = (engine << rule('test-rule', &block))
|
13
13
|
end
|
14
14
|
|
15
|
-
it '
|
15
|
+
it 'interacts with optional node correctly' do
|
16
16
|
# before the fix, filters would try to piggy-back on optional templates
|
17
17
|
|
18
18
|
test_rule do
|
@@ -12,7 +12,7 @@ describe "MAYBE rule" do
|
|
12
12
|
}
|
13
13
|
}
|
14
14
|
|
15
|
-
it "
|
15
|
+
it "passes with existing facts" do
|
16
16
|
production = engine << maybe_rule
|
17
17
|
|
18
18
|
engine << [1, 2, 3]
|
@@ -24,7 +24,7 @@ describe "MAYBE rule" do
|
|
24
24
|
expect(production.tokens.first[:Y]).to eq(5)
|
25
25
|
end
|
26
26
|
|
27
|
-
it "
|
27
|
+
it "passes with missing facts" do
|
28
28
|
production = engine << maybe_rule
|
29
29
|
|
30
30
|
engine << [1, 2, 3]
|
@@ -35,7 +35,7 @@ describe "MAYBE rule" do
|
|
35
35
|
expect(production.tokens.first[:Y]).to be_nil
|
36
36
|
end
|
37
37
|
|
38
|
-
it "
|
38
|
+
it "passes with pre-added missing facts" do
|
39
39
|
engine << [1, 2, 3]
|
40
40
|
|
41
41
|
production = engine << maybe_rule
|
@@ -46,7 +46,7 @@ describe "MAYBE rule" do
|
|
46
46
|
expect(production.tokens.first[:Y]).to be_nil
|
47
47
|
end
|
48
48
|
|
49
|
-
it '
|
49
|
+
it 'passes with retracted facts' do
|
50
50
|
prod = engine << maybe_rule
|
51
51
|
|
52
52
|
engine << [1, 2, 3]
|
@@ -59,7 +59,7 @@ describe "MAYBE rule" do
|
|
59
59
|
expect(prod.tokens.first[:Y]).to be_nil
|
60
60
|
end
|
61
61
|
|
62
|
-
it '
|
62
|
+
it 'works with repeated activations' do
|
63
63
|
prod = engine << maybe_rule
|
64
64
|
|
65
65
|
engine << [1, 2, 3]
|
@@ -77,7 +77,7 @@ describe "MAYBE rule" do
|
|
77
77
|
}
|
78
78
|
end
|
79
79
|
|
80
|
-
it '
|
80
|
+
it 'works with with overlays' do
|
81
81
|
prod = engine << maybe_rule
|
82
82
|
|
83
83
|
engine << [1, 2, 3]
|
@@ -96,7 +96,7 @@ describe "MAYBE rule" do
|
|
96
96
|
expect(prod.tokens.first[:Y]).to be == 5
|
97
97
|
end
|
98
98
|
|
99
|
-
it '
|
99
|
+
it 'handles retracted parent tokens' do
|
100
100
|
prod = engine << maybe_rule
|
101
101
|
|
102
102
|
engine << [1, 2, 3]
|
@@ -29,7 +29,7 @@ describe Wongi::Engine::NccNode do
|
|
29
29
|
}
|
30
30
|
end
|
31
31
|
|
32
|
-
it '
|
32
|
+
it 'passes with a mismatching subchain' do
|
33
33
|
engine << ncc_rule
|
34
34
|
production = engine.productions['ncc']
|
35
35
|
|
@@ -46,7 +46,7 @@ describe Wongi::Engine::NccNode do
|
|
46
46
|
expect(production).to have(0).token
|
47
47
|
end
|
48
48
|
|
49
|
-
it '
|
49
|
+
it 'remains consistent after retraction' do
|
50
50
|
engine << ncc_rule
|
51
51
|
production = engine.productions['ncc']
|
52
52
|
|
@@ -81,7 +81,7 @@ describe Wongi::Engine::NccNode do
|
|
81
81
|
expect(production).to have(0).tokens
|
82
82
|
end
|
83
83
|
|
84
|
-
it '
|
84
|
+
it 'cleans up correctly' do
|
85
85
|
engine.rule :rule1 do
|
86
86
|
forall {
|
87
87
|
has :light_kitchen, :value, :on
|
@@ -135,7 +135,7 @@ describe Wongi::Engine::NccNode do
|
|
135
135
|
expect(engine.select(:light_bathroom, :last_user, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :god)]
|
136
136
|
end
|
137
137
|
|
138
|
-
it '
|
138
|
+
it 'cleans up correctly with a different activation order' do
|
139
139
|
engine.rule :rule1 do
|
140
140
|
forall {
|
141
141
|
has :light_kitchen, :value, :on
|
@@ -191,7 +191,7 @@ describe Wongi::Engine::NccNode do
|
|
191
191
|
expect(engine.select(:light_bathroom, :last_user, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :god)]
|
192
192
|
end
|
193
193
|
|
194
|
-
it '
|
194
|
+
it 'ncc-deactivates without destroying tokens' do
|
195
195
|
engine << rule {
|
196
196
|
forall {
|
197
197
|
has :Student, :is_a, :student
|
@@ -254,6 +254,29 @@ describe Wongi::Engine::NccNode do
|
|
254
254
|
expect(prod).to have(1).tokens
|
255
255
|
end
|
256
256
|
|
257
|
+
# TODO: this is probably pretty hard to fix, but it's good to fixate the behaviour
|
258
|
+
it "causes infinite loops" do
|
259
|
+
counter = 0
|
260
|
+
exception = StandardError.new
|
261
|
+
|
262
|
+
looping_rule = rule {
|
263
|
+
forall {
|
264
|
+
none {
|
265
|
+
has :a, :b, :c
|
266
|
+
}
|
267
|
+
}
|
268
|
+
make {
|
269
|
+
action {
|
270
|
+
counter += 1
|
271
|
+
raise exception if counter > 1
|
272
|
+
}
|
273
|
+
gen :a, :b, :c
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
expect { engine << looping_rule }.to raise_exception(exception)
|
278
|
+
end
|
279
|
+
|
257
280
|
context "with overlays" do
|
258
281
|
context 'should pass with a mismatching subchain' do
|
259
282
|
specify "variation 1" do
|
@@ -20,7 +20,7 @@ describe "negative rule" do
|
|
20
20
|
expect(prod).to have(0).tokens
|
21
21
|
end
|
22
22
|
|
23
|
-
it "
|
23
|
+
it "does not introduce variables" do
|
24
24
|
proc = lambda {
|
25
25
|
engine << rule('one-option') {
|
26
26
|
forall {
|
@@ -68,7 +68,7 @@ describe "negative rule" do
|
|
68
68
|
expect(prod).to have(1).tokens
|
69
69
|
end
|
70
70
|
|
71
|
-
it "
|
71
|
+
it "does not create self-negating facts" do
|
72
72
|
engine << rule('feedback') {
|
73
73
|
forall {
|
74
74
|
neg :a, :b, :_
|
@@ -78,28 +78,6 @@ describe "negative rule" do
|
|
78
78
|
}
|
79
79
|
}
|
80
80
|
|
81
|
-
engine.
|
82
|
-
end
|
83
|
-
|
84
|
-
it "should create infinite feedback loops with unsafe option" do
|
85
|
-
counter = 0
|
86
|
-
exception = Class.new(StandardError)
|
87
|
-
|
88
|
-
proc = lambda {
|
89
|
-
engine << rule('feedback') {
|
90
|
-
forall {
|
91
|
-
neg :a, :b, :_, unsafe: true
|
92
|
-
}
|
93
|
-
make {
|
94
|
-
action {
|
95
|
-
counter += 1
|
96
|
-
raise exception if counter > 5
|
97
|
-
}
|
98
|
-
gen :a, :b, :c
|
99
|
-
}
|
100
|
-
}
|
101
|
-
}
|
102
|
-
|
103
|
-
expect(&proc).to raise_error(exception)
|
81
|
+
expect(engine).to have(0).facts
|
104
82
|
end
|
105
83
|
end
|
data/spec/network_spec.rb
CHANGED
@@ -3,15 +3,16 @@ require 'spec_helper'
|
|
3
3
|
describe Wongi::Engine::Network do
|
4
4
|
include Wongi::Engine::DSL
|
5
5
|
|
6
|
-
let(:engine) { Wongi::Engine.create }
|
7
6
|
subject { engine }
|
8
7
|
|
9
|
-
|
8
|
+
let(:engine) { Wongi::Engine.create }
|
9
|
+
|
10
|
+
it 'asserts facts' do
|
10
11
|
subject << [1, 2, 3]
|
11
12
|
expect(subject.select(:_, 2, :_)).to have(1).item
|
12
13
|
end
|
13
14
|
|
14
|
-
it '
|
15
|
+
it 'retracts facts' do
|
15
16
|
subject << [1, 2, 3]
|
16
17
|
subject.retract [1, 2, 3]
|
17
18
|
expect(subject.select(:_, 2, :_).count).to eq(0)
|
data/spec/overlay_spec.rb
CHANGED
@@ -102,6 +102,7 @@ describe Wongi::Engine::Overlay do
|
|
102
102
|
allow(engine).to receive(:real_assert)
|
103
103
|
allow(engine).to receive(:real_retract)
|
104
104
|
end
|
105
|
+
|
105
106
|
it "maintains visibility on each layer" do
|
106
107
|
child1 = overlay.new_child
|
107
108
|
child2 = child1.new_child
|
@@ -145,7 +146,7 @@ describe Wongi::Engine::Overlay do
|
|
145
146
|
end
|
146
147
|
end
|
147
148
|
|
148
|
-
it '
|
149
|
+
it 'is disposable' do
|
149
150
|
production = engine << rule {
|
150
151
|
forall {
|
151
152
|
has 1, 2, :X
|
@@ -177,7 +178,7 @@ describe Wongi::Engine::Overlay do
|
|
177
178
|
expect(production).to have(1).token
|
178
179
|
end
|
179
180
|
|
180
|
-
it '
|
181
|
+
it 'generates into correct overlays' do
|
181
182
|
production = engine << rule {
|
182
183
|
forall {
|
183
184
|
has 1, 2, :X
|
data/spec/ruleset_spec.rb
CHANGED
@@ -6,37 +6,37 @@ describe Wongi::Engine::Ruleset do
|
|
6
6
|
end
|
7
7
|
|
8
8
|
context 'initially' do
|
9
|
-
it '
|
9
|
+
it 'has no rules' do
|
10
10
|
expect(Wongi::Engine::Ruleset.rulesets).to be_empty
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
14
|
context 'when creating' do
|
15
|
-
it '
|
15
|
+
it 'does not register itself when not given a name' do
|
16
16
|
ruleset = Wongi::Engine::Ruleset.new
|
17
17
|
expect(ruleset.name).to be_nil
|
18
18
|
expect(Wongi::Engine::Ruleset.rulesets).to be_empty
|
19
19
|
end
|
20
20
|
|
21
|
-
it '
|
21
|
+
it 'has a name' do
|
22
22
|
ruleset = Wongi::Engine::Ruleset.new 'testing-ruleset'
|
23
23
|
expect(ruleset.name).to be == 'testing-ruleset'
|
24
24
|
end
|
25
25
|
|
26
|
-
it '
|
26
|
+
it 'registers itself when given a name' do
|
27
27
|
ruleset = Wongi::Engine::Ruleset.new 'testing-ruleset'
|
28
28
|
expect(Wongi::Engine::Ruleset.rulesets).not_to be_empty
|
29
29
|
expect(Wongi::Engine::Ruleset[ruleset.name]).to be == ruleset
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
it '
|
33
|
+
it 'is able to clear registered rulesets' do
|
34
34
|
_ = Wongi::Engine::Ruleset.new 'testing-ruleset'
|
35
35
|
Wongi::Engine::Ruleset.reset
|
36
36
|
expect(Wongi::Engine::Ruleset.rulesets).to be_empty
|
37
37
|
end
|
38
38
|
|
39
|
-
it '
|
39
|
+
it 'installs creating rules into a rete' do
|
40
40
|
rete = double 'rete'
|
41
41
|
|
42
42
|
ruleset = Wongi::Engine::Ruleset.new
|
data/spec/simple_action_spec.rb
CHANGED
data/spec/wme_spec.rb
CHANGED
@@ -14,14 +14,14 @@ describe Wongi::Engine::WME do
|
|
14
14
|
}
|
15
15
|
|
16
16
|
context 'a new WME' do
|
17
|
-
it '
|
17
|
+
it 'initializes and expose members' do
|
18
18
|
expect(subject.subject).to be == "a"
|
19
19
|
expect(subject.predicate).to be == "b"
|
20
20
|
expect(subject.object).to be == "c"
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
it '
|
24
|
+
it 'compares instances' do
|
25
25
|
wme1 = Wongi::Engine::WME.new "a", "b", "c"
|
26
26
|
wme2 = Wongi::Engine::WME.new "a", "b", "c"
|
27
27
|
wme3 = Wongi::Engine::WME.new "a", "b", "d"
|
@@ -30,11 +30,11 @@ describe Wongi::Engine::WME do
|
|
30
30
|
expect(wme1).not_to be == wme3
|
31
31
|
end
|
32
32
|
|
33
|
-
it '
|
33
|
+
it 'does not match against non-templates' do
|
34
34
|
expect { subject =~ [1, 2, 3] }.to raise_error(Wongi::Engine::Error)
|
35
35
|
end
|
36
36
|
|
37
|
-
it '
|
37
|
+
it 'matches against templates' do
|
38
38
|
t1 = Wongi::Engine::Template.new "a", :_, :_
|
39
39
|
t2 = Wongi::Engine::Template.new "b", :_, :_
|
40
40
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wongi-engine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.0.pre.
|
4
|
+
version: 0.4.0.pre.alpha3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Valeri Sokolov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -152,25 +152,25 @@ files:
|
|
152
152
|
- lib/wongi-engine/wme.rb
|
153
153
|
- lib/wongi-engine/wme_match_data.rb
|
154
154
|
- spec/action_class_spec.rb
|
155
|
+
- spec/aggregate_spec.rb
|
155
156
|
- spec/alpha_index_spec.rb
|
157
|
+
- spec/any_rule_spec.rb
|
158
|
+
- spec/assert_test_spec.rb
|
159
|
+
- spec/assign_spec.rb
|
160
|
+
- spec/assuming_spec.rb
|
156
161
|
- spec/beta_node_spec.rb
|
157
162
|
- spec/bug_specs/issue_4_spec.rb
|
158
163
|
- spec/dataset_spec.rb
|
159
|
-
- spec/filter_specs/assert_test_spec.rb
|
160
|
-
- spec/filter_specs/greater_than_equality_test_spec.rb
|
161
|
-
- spec/filter_specs/less_test_spec.rb
|
162
|
-
- spec/filter_specs/less_than_equality_test_spec.rb
|
163
164
|
- spec/generation_spec.rb
|
165
|
+
- spec/greater_than_equality_test_spec.rb
|
164
166
|
- spec/high_level_spec.rb
|
167
|
+
- spec/less_test_spec.rb
|
168
|
+
- spec/less_than_equality_test_spec.rb
|
169
|
+
- spec/maybe_rule_spec.rb
|
170
|
+
- spec/ncc_spec.rb
|
171
|
+
- spec/negative_rule_spec.rb
|
165
172
|
- spec/network_spec.rb
|
166
173
|
- spec/overlay_spec.rb
|
167
|
-
- spec/rule_specs/aggregate_spec.rb
|
168
|
-
- spec/rule_specs/any_rule_spec.rb
|
169
|
-
- spec/rule_specs/assign_spec.rb
|
170
|
-
- spec/rule_specs/assuming_spec.rb
|
171
|
-
- spec/rule_specs/maybe_rule_spec.rb
|
172
|
-
- spec/rule_specs/ncc_spec.rb
|
173
|
-
- spec/rule_specs/negative_rule_spec.rb
|
174
174
|
- spec/ruleset_spec.rb
|
175
175
|
- spec/simple_action_spec.rb
|
176
176
|
- spec/spec_helper.rb
|
@@ -196,7 +196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
196
196
|
- !ruby/object:Gem::Version
|
197
197
|
version: 1.3.1
|
198
198
|
requirements: []
|
199
|
-
rubygems_version: 3.
|
199
|
+
rubygems_version: 3.3.7
|
200
200
|
signing_key:
|
201
201
|
specification_version: 4
|
202
202
|
summary: A forward-chaining rule engine in pure Ruby.
|