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,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
|