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,34 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
|
3
|
+
class WMEMatchData
|
4
|
+
|
5
|
+
attr_reader :assignments
|
6
|
+
|
7
|
+
def initialize assignments = { }, match = false
|
8
|
+
@assignments = assignments
|
9
|
+
@match = match
|
10
|
+
end
|
11
|
+
|
12
|
+
def [] key
|
13
|
+
assignments[key]
|
14
|
+
end
|
15
|
+
|
16
|
+
def []= key, value
|
17
|
+
assignments[key] = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def match?
|
21
|
+
@match
|
22
|
+
end
|
23
|
+
|
24
|
+
def match!
|
25
|
+
@match = true
|
26
|
+
end
|
27
|
+
|
28
|
+
def & other
|
29
|
+
WMEMatchData.new( assignments.merge( other.assignments ), match? && other.match? )
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Wongi::Engine::BetaNode do
|
4
|
+
|
5
|
+
include Wongi::Engine::DSL
|
6
|
+
|
7
|
+
let( :engine ) { Wongi::Engine.create }
|
8
|
+
|
9
|
+
describe '#tokens' do
|
10
|
+
|
11
|
+
it 'should be enumerable' do
|
12
|
+
|
13
|
+
production = engine << rule {
|
14
|
+
forall {
|
15
|
+
has :x, :y, :Z
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
engine << [:x, :y, 1]
|
20
|
+
engine << [:x, :y, 2]
|
21
|
+
engine << [:x, :y, 3]
|
22
|
+
zs = production.tokens.map { |token| token[:Z] }
|
23
|
+
expect( zs ).to be == [1, 2, 3]
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "issue 4" do
|
4
|
+
|
5
|
+
it "should correctly retract pre-added items from within a rule" do
|
6
|
+
|
7
|
+
engine = Wongi::Engine.create
|
8
|
+
|
9
|
+
10.times{ |i| engine << [i, :is_number, true] }
|
10
|
+
|
11
|
+
engine.rule 'segregate' do
|
12
|
+
forall {
|
13
|
+
has :Number, :is_number, true
|
14
|
+
}
|
15
|
+
make {
|
16
|
+
action { |token|
|
17
|
+
number = token[:Number]
|
18
|
+
engine << [number, :is_even, true]
|
19
|
+
engine.retract [number, :is_number, true]
|
20
|
+
}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
numbers = engine.select :_, :is_number, true
|
25
|
+
evens = engine.select :_, :is_even, true
|
26
|
+
|
27
|
+
expect(numbers).to be_empty
|
28
|
+
expect(evens.length).to eq(10)
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should correctly retract post-added items from within a rule" do
|
33
|
+
|
34
|
+
engine = Wongi::Engine.create
|
35
|
+
|
36
|
+
engine.rule 'segregate' do
|
37
|
+
forall {
|
38
|
+
has :Number, :is_number, true
|
39
|
+
}
|
40
|
+
make {
|
41
|
+
action { |token|
|
42
|
+
number = token[:Number]
|
43
|
+
engine << [number, :is_even, true]
|
44
|
+
engine.retract [number, :is_number, true]
|
45
|
+
}
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
10.times{ |i| engine << [i, :is_number, true] }
|
50
|
+
|
51
|
+
numbers = engine.select :_, :is_number, true
|
52
|
+
evens = engine.select :_, :is_even, true
|
53
|
+
|
54
|
+
expect(numbers).to be_empty
|
55
|
+
expect(evens.length).to eq(10)
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
# cascaded processing affects this
|
60
|
+
it "should not retract later items from within a rule" do
|
61
|
+
|
62
|
+
engine = Wongi::Engine.create
|
63
|
+
|
64
|
+
10.times{ |i| engine << [i, :is_number, true] }
|
65
|
+
|
66
|
+
engine.rule 'segregate' do
|
67
|
+
forall {
|
68
|
+
has :Number, :is_number, true
|
69
|
+
}
|
70
|
+
make {
|
71
|
+
action { |token|
|
72
|
+
number = token[:Number]
|
73
|
+
if number % 2 == 0
|
74
|
+
engine << [number, :is_even, true]
|
75
|
+
engine.retract [number, :is_number, true]
|
76
|
+
engine.retract [number + 1, :is_number, true]
|
77
|
+
else
|
78
|
+
# this is not reached without cascades
|
79
|
+
engine << [number, :is_odd, true]
|
80
|
+
end
|
81
|
+
}
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
numbers = engine.select :_, :is_number, true
|
86
|
+
evens = engine.select :_, :is_even, true
|
87
|
+
odds = engine.select :_, :is_odd, true
|
88
|
+
|
89
|
+
expect(numbers).to be_empty
|
90
|
+
expect(evens).to have(5).items
|
91
|
+
expect(odds).to have(5).items
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
it "should not lose track when another rule affects a set" do
|
97
|
+
engine = Wongi::Engine.create
|
98
|
+
|
99
|
+
10.times{ |i| engine << [i, :is_number, true] }
|
100
|
+
|
101
|
+
engine.rule 'find odds' do
|
102
|
+
forall {
|
103
|
+
has :Number, :is_number, true
|
104
|
+
has :Number, :probably_odd, true
|
105
|
+
}
|
106
|
+
make {
|
107
|
+
action { |token|
|
108
|
+
number = token[:Number]
|
109
|
+
if number % 2 != 0
|
110
|
+
engine << [number, :is_odd, true]
|
111
|
+
engine.retract [number, :is_number, true]
|
112
|
+
end
|
113
|
+
}
|
114
|
+
}
|
115
|
+
end
|
116
|
+
engine.rule 'find evens' do
|
117
|
+
forall {
|
118
|
+
has :Number, :is_number, true
|
119
|
+
neg :Number, :is_odd, true
|
120
|
+
}
|
121
|
+
make {
|
122
|
+
action { |token|
|
123
|
+
number = token[:Number]
|
124
|
+
if number % 2 == 0
|
125
|
+
engine << [number, :is_even, true]
|
126
|
+
else
|
127
|
+
engine << [number, :probably_odd, true]
|
128
|
+
end
|
129
|
+
}
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
numbers = engine.select :_, :is_number, true
|
134
|
+
evens = engine.select :_, :is_even, true
|
135
|
+
odds = engine.select :_, :is_odd, true
|
136
|
+
|
137
|
+
# numbers.should be_empty
|
138
|
+
expect(evens.length).to eq(5)
|
139
|
+
expect(odds.length).to eq(5)
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Wongi::Engine::Network do
|
4
|
+
|
5
|
+
it 'should expose compiled productions' do
|
6
|
+
|
7
|
+
ds = Wongi::Engine::Network.new
|
8
|
+
|
9
|
+
ds << rule('test-rule') {
|
10
|
+
forall {
|
11
|
+
has 1, 2, 3
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
production = ds.productions['test-rule']
|
16
|
+
expect(production).not_to be_nil
|
17
|
+
|
18
|
+
expect(production).to be_empty
|
19
|
+
|
20
|
+
ds << [1, 2, 3]
|
21
|
+
|
22
|
+
expect(production.size).to eq(1)
|
23
|
+
expect( production.size ).to be == 1
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/spec/dsl_spec.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "ASSERT test" do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@engine = Wongi::Engine.create
|
7
|
+
end
|
8
|
+
|
9
|
+
def engine
|
10
|
+
@engine
|
11
|
+
end
|
12
|
+
|
13
|
+
def production
|
14
|
+
@production
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_rule &block
|
18
|
+
@production = ( engine << rule( 'test-rule', &block ) )
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should pass with a constant 'true'" do
|
22
|
+
|
23
|
+
test_rule {
|
24
|
+
forall {
|
25
|
+
assert { |token|
|
26
|
+
true
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
expect(production).to have(1).token
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should fail with a constant 'false'" do
|
36
|
+
|
37
|
+
test_rule {
|
38
|
+
forall {
|
39
|
+
assert { |token|
|
40
|
+
false
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
expect(production).to have(0).tokens
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should use the token with no arguments" do
|
50
|
+
|
51
|
+
test_rule {
|
52
|
+
forall {
|
53
|
+
has :X, "is", :Y
|
54
|
+
assert { |token|
|
55
|
+
token[:X] == "resistance"
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
engine << ["resistance", "is", "futile"]
|
61
|
+
|
62
|
+
expect(production).to have(1).token
|
63
|
+
expect(production.tokens.first[:X]).to eq("resistance")
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should be retractable" do
|
68
|
+
|
69
|
+
test_rule {
|
70
|
+
forall {
|
71
|
+
has :X, "is", :Y
|
72
|
+
assert { |token|
|
73
|
+
token[:X] == "resistance"
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
engine << ["resistance", "is", "futile"]
|
79
|
+
engine.retract ["resistance", "is", "futile"]
|
80
|
+
expect(production).to have(0).tokens
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should use individual variables with arguments" do
|
85
|
+
|
86
|
+
test_rule {
|
87
|
+
forall {
|
88
|
+
has :X, "is", :Y
|
89
|
+
assert :X, :Y do |x, y|
|
90
|
+
y == "futile"
|
91
|
+
end
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
engine << ["resistance", "is", "futile"]
|
96
|
+
|
97
|
+
expect(production).to have(1).token
|
98
|
+
expect(production.tokens.first[:X]).to eq("resistance")
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "LESS test" do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@engine = Wongi::Engine.create
|
7
|
+
end
|
8
|
+
|
9
|
+
def engine
|
10
|
+
@engine
|
11
|
+
end
|
12
|
+
|
13
|
+
def production
|
14
|
+
@production
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_rule &block
|
18
|
+
@production = ( engine << rule( 'test-rule', &block ) )
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should interact with optional node correctly" do
|
22
|
+
|
23
|
+
# before the fix, filters would try to piggy-back on optional templates
|
24
|
+
|
25
|
+
test_rule {
|
26
|
+
forall {
|
27
|
+
maybe "Z", "Z", "Z"
|
28
|
+
less 6,4 # this should fail
|
29
|
+
}
|
30
|
+
|
31
|
+
make {
|
32
|
+
gen ".", ".", "."
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
engine << ["A", "B", "C"]
|
37
|
+
|
38
|
+
expect(@production.size).to eq(0)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Wongi::Engine::DSL::Action::StatementGenerator do
|
4
|
+
|
5
|
+
let( :engine ) { Wongi::Engine.create }
|
6
|
+
|
7
|
+
let( :transitive_rule ) {
|
8
|
+
rule {
|
9
|
+
forall {
|
10
|
+
has :P, :transitive, true
|
11
|
+
has :X, :P, :Y
|
12
|
+
has :Y, :P, :Z
|
13
|
+
}
|
14
|
+
make {
|
15
|
+
gen :X, :P, :Z
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
let( :production ) { engine << transitive_rule }
|
21
|
+
|
22
|
+
shared_examples 'generation' do
|
23
|
+
|
24
|
+
it 'should generate facts' do
|
25
|
+
engine << %w( Alice relative Bob )
|
26
|
+
engine << %w( Bob relative Dwight )
|
27
|
+
|
28
|
+
expect( production ).to have(1).token
|
29
|
+
expect( engine.find *%w( Alice relative Dwight ) ).not_to be_nil
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should retrct generated facts' do
|
33
|
+
engine << %w( Alice relative Bob )
|
34
|
+
engine << %w( Bob relative Dwight )
|
35
|
+
engine.retract %w( Bob relative Dwight )
|
36
|
+
|
37
|
+
expect( production ).to have(0).tokens
|
38
|
+
expect( engine.find *%w( Alice relative Dwight ) ).to be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'transitive diamond' do
|
42
|
+
|
43
|
+
before :each do
|
44
|
+
engine << %w( Alice relative Bob )
|
45
|
+
engine << %w( Bob relative Dwight )
|
46
|
+
engine << %w( Alice relative Claire )
|
47
|
+
engine << %w( Claire relative Dwight )
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should be created' do
|
51
|
+
expect( production ).to have(2).tokens
|
52
|
+
expect( engine.find *%w( Alice relative Dwight ) ).not_to be_nil
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should remain after a single retraction' do
|
56
|
+
engine.retract %w( Claire relative Dwight )
|
57
|
+
|
58
|
+
expect( production ).to have(1).token
|
59
|
+
expect( engine.find *%w( Alice relative Dwight ) ).not_to be_nil
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should be destroyed after both retractions' do
|
63
|
+
engine.retract %w( Claire relative Dwight )
|
64
|
+
engine.retract %w( Alice relative Bob )
|
65
|
+
|
66
|
+
expect( production ).to have(0).tokens
|
67
|
+
expect( engine.find *%w( Alice relative Dwight ) ).to be_nil
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
context "pre-asserted", :pre do
|
75
|
+
|
76
|
+
before :each do
|
77
|
+
engine << [ "relative", :transitive, true ]
|
78
|
+
end
|
79
|
+
|
80
|
+
it_behaves_like 'generation'
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
context "post-asserted", :post do
|
85
|
+
|
86
|
+
before :each do
|
87
|
+
production
|
88
|
+
engine << [ "relative", :transitive, true ]
|
89
|
+
end
|
90
|
+
|
91
|
+
it_behaves_like 'generation'
|
92
|
+
|
93
|
+
it 'should not retract generated facts marked as manual', :wip do
|
94
|
+
engine << %w( Alice relative Bob )
|
95
|
+
engine << %w( Bob relative Dwight )
|
96
|
+
engine << %w( Alice relative Dwight )
|
97
|
+
engine.retract %w( Alice relative Bob )
|
98
|
+
|
99
|
+
expect( production ).to have(0).tokens
|
100
|
+
expect( engine.find *%w( Alice relative Dwight ) ).not_to be_nil
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should retract generated facts unmarked as manual', :wip do
|
104
|
+
engine << %w( Alice relative Bob )
|
105
|
+
engine << %w( Bob relative Dwight )
|
106
|
+
engine << %w( Alice relative Dwight )
|
107
|
+
engine.retract %w( Alice relative Dwight )
|
108
|
+
engine.retract %w( Alice relative Bob )
|
109
|
+
|
110
|
+
expect( production ).to have(0).tokens
|
111
|
+
expect( engine.find *%w( Alice relative Dwight ) ).to be_nil
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|