wongi-engine 0.3.5 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +24 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +86 -0
- data/.travis.yml +8 -15
- data/Gemfile +4 -0
- data/README.md +15 -15
- data/Rakefile +1 -2
- data/examples/ex01.rb +1 -1
- data/examples/ex02.rb +3 -7
- data/examples/graphviz.rb +1 -1
- data/examples/rdf.rb +1 -1
- data/examples/timeline.rb +6 -6
- data/lib/wongi-engine/alpha_index.rb +36 -0
- data/lib/wongi-engine/alpha_memory.rb +4 -30
- data/lib/wongi-engine/beta/aggregate_node.rb +84 -0
- data/lib/wongi-engine/beta/assignment_node.rb +17 -20
- data/lib/wongi-engine/beta/beta_node.rb +49 -43
- data/lib/wongi-engine/beta/filter_node.rb +18 -26
- data/lib/wongi-engine/beta/join_node.rb +58 -84
- data/lib/wongi-engine/beta/ncc_node.rb +23 -38
- data/lib/wongi-engine/beta/ncc_partner.rb +29 -29
- data/lib/wongi-engine/beta/neg_node.rb +43 -76
- data/lib/wongi-engine/beta/optional_node.rb +60 -89
- data/lib/wongi-engine/beta/or_node.rb +29 -9
- data/lib/wongi-engine/beta/production_node.rb +15 -12
- data/lib/wongi-engine/beta/root_node.rb +47 -0
- data/lib/wongi-engine/beta.rb +2 -1
- data/lib/wongi-engine/compiler.rb +24 -39
- data/lib/wongi-engine/core_ext.rb +11 -20
- data/lib/wongi-engine/dsl/action/assign_action.rb +1 -1
- data/lib/wongi-engine/dsl/action/base_action.rb +12 -0
- data/lib/wongi-engine/dsl/action/error_generator.rb +10 -10
- data/lib/wongi-engine/dsl/action/simple_action.rb +15 -11
- data/lib/wongi-engine/dsl/action/simple_collector.rb +11 -26
- data/lib/wongi-engine/dsl/action/statement_generator.rb +47 -29
- data/lib/wongi-engine/dsl/action/trace_action.rb +10 -10
- data/lib/wongi-engine/dsl/any_rule.rb +16 -20
- data/lib/wongi-engine/dsl/assuming.rb +8 -15
- data/lib/wongi-engine/dsl/builder.rb +13 -12
- data/lib/wongi-engine/dsl/clause/aggregate.rb +20 -0
- data/lib/wongi-engine/dsl/clause/assign.rb +4 -4
- data/lib/wongi-engine/dsl/clause/fact.rb +10 -13
- data/lib/wongi-engine/dsl/clause/gen.rb +3 -5
- data/lib/wongi-engine/dsl/clause/generic.rb +7 -9
- data/lib/wongi-engine/dsl/generated.rb +6 -12
- data/lib/wongi-engine/dsl/ncc_subrule.rb +6 -9
- data/lib/wongi-engine/dsl/query.rb +12 -15
- data/lib/wongi-engine/dsl/rule.rb +45 -50
- data/lib/wongi-engine/dsl.rb +42 -36
- data/lib/wongi-engine/entity_iterator.rb +60 -0
- data/lib/wongi-engine/enumerators.rb +2 -1
- data/lib/wongi-engine/error.rb +6 -7
- data/lib/wongi-engine/filter/asserting_test.rb +4 -7
- data/lib/wongi-engine/filter/equality_test.rb +16 -19
- data/lib/wongi-engine/filter/filter_test.rb +3 -6
- data/lib/wongi-engine/filter/greater_than_or_equal_test.rb +6 -3
- data/lib/wongi-engine/filter/greater_than_test.rb +16 -19
- data/lib/wongi-engine/filter/in_list_test.rb +33 -0
- data/lib/wongi-engine/filter/inequality_test.rb +16 -19
- data/lib/wongi-engine/filter/less_than_or_equal_test.rb +6 -3
- data/lib/wongi-engine/filter/less_than_test.rb +16 -19
- data/lib/wongi-engine/filter/not_in_list_test.rb +33 -0
- data/lib/wongi-engine/filter.rb +3 -1
- data/lib/wongi-engine/generator_tracker.rb +32 -0
- data/lib/wongi-engine/graph.rb +25 -36
- data/lib/wongi-engine/join_results.rb +80 -0
- data/lib/wongi-engine/network/collectable.rb +10 -14
- data/lib/wongi-engine/network/debug.rb +15 -31
- data/lib/wongi-engine/network.rb +113 -206
- data/lib/wongi-engine/overlay.rb +489 -0
- data/lib/wongi-engine/ruleset.rb +18 -20
- data/lib/wongi-engine/template.rb +48 -35
- data/lib/wongi-engine/token.rb +51 -85
- data/lib/wongi-engine/token_assignment.rb +15 -0
- data/lib/wongi-engine/version.rb +1 -1
- data/lib/wongi-engine/wme.rb +11 -58
- data/lib/wongi-engine/wme_match_data.rb +5 -9
- data/lib/wongi-engine.rb +6 -5
- data/spec/action_class_spec.rb +44 -50
- data/spec/aggregate_spec.rb +213 -0
- data/spec/alpha_index_spec.rb +78 -0
- data/spec/any_rule_spec.rb +202 -0
- data/spec/{filter_specs/assert_test_spec.rb → assert_test_spec.rb} +17 -36
- data/spec/{rule_specs/assign_spec.rb → assign_spec.rb} +15 -21
- data/spec/{rule_specs/assuming_spec.rb → assuming_spec.rb} +13 -20
- data/spec/beta_node_spec.rb +3 -9
- data/spec/bug_specs/issue_4_spec.rb +25 -33
- data/spec/dataset_spec.rb +5 -8
- data/spec/entity_iterator_spec.rb +88 -0
- data/spec/generation_spec.rb +51 -60
- data/spec/greater_than_equality_test_spec.rb +24 -0
- data/spec/high_level_spec.rb +85 -224
- data/spec/in_list_spec.rb +115 -0
- data/spec/less_test_spec.rb +25 -0
- data/spec/{filter_specs/less_than_equality_test_spec.rb → less_than_equality_test_spec.rb} +5 -8
- data/spec/maybe_rule_spec.rb +147 -0
- data/spec/ncc_spec.rb +339 -0
- data/spec/negative_rule_spec.rb +83 -0
- data/spec/network_spec.rb +83 -73
- data/spec/overlay_spec.rb +179 -8
- data/spec/ruleset_spec.rb +23 -29
- data/spec/simple_action_spec.rb +5 -15
- data/spec/spec_helper.rb +4 -0
- data/spec/wme_spec.rb +17 -56
- data/wongi-engine.gemspec +28 -26
- metadata +46 -57
- data/.hgignore +0 -7
- data/lib/wongi-engine/beta/beta_memory.rb +0 -49
- data/lib/wongi-engine/data_overlay.rb +0 -144
- data/lib/wongi-engine/dsl/action/base.rb +0 -11
- data/spec/dsl_spec.rb +0 -9
- data/spec/filter_specs/greater_than_equality_test_spec.rb +0 -33
- data/spec/filter_specs/less_test_spec.rb +0 -41
- data/spec/rule_specs/any_rule_spec.rb +0 -109
- data/spec/rule_specs/maybe_rule_spec.rb +0 -101
- data/spec/rule_specs/ncc_spec.rb +0 -274
- data/spec/rule_specs/negative_rule_spec.rb +0 -105
- data/spec/rule_specs/or_rule_spec.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 461346062a679921c61fc0b35bc2e89ef0d06920745d650b48fedbbc452b780a
|
4
|
+
data.tar.gz: df2bf13a3551dc049e08398b11ac42440a34e4cb651f5092b74d3efdd5a3aecf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a3495df610621e35c8cef1a0cbf39aa0b26a8cee6d699df2bf4cdb451f4c06db534d7943bb4f30778d21289406d54ba0264f54ecfe3b839e3b1d50ccd6b63b3
|
7
|
+
data.tar.gz: 8f85d69a8f476eec9a6ee8cf97ade3a4b8e9959616114511ca65007cd225feb0963613750628a450d5ea5f905fcaed65082662db828491a7caa9c533dc70db37
|
@@ -0,0 +1,24 @@
|
|
1
|
+
name: Test
|
2
|
+
|
3
|
+
on:
|
4
|
+
- push
|
5
|
+
- pull_request
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
test:
|
9
|
+
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
|
12
|
+
strategy:
|
13
|
+
matrix:
|
14
|
+
ruby-version: ["3.1", "3.0", "2.7", "jruby-head"]
|
15
|
+
|
16
|
+
steps:
|
17
|
+
- uses: actions/checkout@v3
|
18
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
19
|
+
uses: ruby/setup-ruby@v1
|
20
|
+
with:
|
21
|
+
ruby-version: ${{ matrix.ruby-version }}
|
22
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
23
|
+
- name: Run tests
|
24
|
+
run: bundle exec rake
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-rake
|
3
|
+
- rubocop-rspec
|
4
|
+
|
5
|
+
AllCops:
|
6
|
+
TargetRubyVersion: 2.7
|
7
|
+
NewCops: enable
|
8
|
+
|
9
|
+
Style/StringLiterals:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
Style/BlockDelimiters:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Style/TrailingCommaInArguments:
|
16
|
+
EnforcedStyleForMultiline: consistent_comma
|
17
|
+
|
18
|
+
Style/TrailingCommaInArrayLiteral:
|
19
|
+
EnforcedStyleForMultiline: consistent_comma
|
20
|
+
|
21
|
+
Style/TrailingCommaInHashLiteral:
|
22
|
+
EnforcedStyleForMultiline: consistent_comma
|
23
|
+
|
24
|
+
Style/FrozenStringLiteralComment:
|
25
|
+
Enabled: false # reconsider later
|
26
|
+
|
27
|
+
Metrics:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Style/Documentation:
|
31
|
+
Enabled: false
|
32
|
+
|
33
|
+
Style/ClassAndModuleChildren:
|
34
|
+
Enabled: false # temporary
|
35
|
+
|
36
|
+
Lint/EmptyClass:
|
37
|
+
Enabled: false
|
38
|
+
|
39
|
+
Naming/MethodParameterName:
|
40
|
+
Enabled: false
|
41
|
+
|
42
|
+
Layout/LineLength:
|
43
|
+
Enabled: false
|
44
|
+
|
45
|
+
Style/SafeNavigation:
|
46
|
+
Enabled: false
|
47
|
+
|
48
|
+
Naming:
|
49
|
+
Enabled: false
|
50
|
+
|
51
|
+
Style/MixinUsage:
|
52
|
+
Exclude:
|
53
|
+
- examples/*.rb
|
54
|
+
|
55
|
+
Style/IfUnlessModifier:
|
56
|
+
Enabled: false
|
57
|
+
|
58
|
+
Style/SelectByRegexp:
|
59
|
+
Enabled: false
|
60
|
+
|
61
|
+
Lint/EmptyBlock:
|
62
|
+
Enabled: false
|
63
|
+
|
64
|
+
Security/CompoundHash:
|
65
|
+
Enabled: false # we actually need it to work
|
66
|
+
|
67
|
+
Lint/HashCompareByIdentity:
|
68
|
+
Enabled: false # investigate later
|
69
|
+
|
70
|
+
Style/AccessModifierDeclarations:
|
71
|
+
Enabled: false # maybe for the use of module_function which I think is better off being explicit instead of grouped
|
72
|
+
|
73
|
+
Style/OptionalBooleanParameter:
|
74
|
+
Enabled: false
|
75
|
+
|
76
|
+
Gemspec/RequiredRubyVersion:
|
77
|
+
Enabled: false
|
78
|
+
|
79
|
+
RSpec/MultipleExpectations:
|
80
|
+
Enabled: false
|
81
|
+
|
82
|
+
RSpec/ContextWording:
|
83
|
+
Enabled: false
|
84
|
+
|
85
|
+
RSpec/ExampleLength:
|
86
|
+
Enabled: false
|
data/.travis.yml
CHANGED
@@ -1,19 +1,12 @@
|
|
1
1
|
language: ruby
|
2
|
+
|
2
3
|
rvm:
|
3
|
-
- 2.
|
4
|
-
-
|
5
|
-
-
|
6
|
-
-
|
7
|
-
|
4
|
+
- 2.7
|
5
|
+
- 3.0
|
6
|
+
- 3.1
|
7
|
+
- jruby
|
8
|
+
|
8
9
|
sudo: false
|
9
|
-
notifications:
|
10
|
-
hipchat:
|
11
|
-
rooms:
|
12
|
-
secure: OwpsmGlIxlSbLUWVZZI+xBErDkFc5XO2vmjs+ddQkNiL1E9N5SG35x75113S6EZ3qhqeNMPFBfZYDqem6jkhNOWsmH3EIH+QT1lWxnosrr0LgyJpvFosvjQaryvqJHVhT5tdBqzaWEYB6ObRLOUt7A5YrbtWHyTScB6ThpXCiR4=
|
13
|
-
webhooks:
|
14
|
-
urls:
|
15
|
-
- secure: Xm+R/3O6iG2sDbfrR8D5Y9AYJkB7DDjLBcKZEn+xaTVjuX/XFLF7p+v9n9v7Qs/v0VtU75pVX4Z++OmNJZJzJKB3Owgb4c1rMOGmctza2JPL+1yovmhpG0/S0hjMkO5g4CsAKKcQoaDXNrubUVHpEchLPP/aCx7/F4twCan9bfc=
|
16
|
-
on_success: always
|
17
|
-
on_failure: always
|
18
|
-
on_start: false
|
19
10
|
|
11
|
+
notifications:
|
12
|
+
email: true
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,28 +1,28 @@
|
|
1
1
|
# Wongi::Engine
|
2
2
|
|
3
|
-
[![Gem](https://img.shields.io/gem/v/wongi-engine.svg)](https://rubygems.org/gems/wongi-engine/)
|
4
|
-
[![Build Status](https://
|
3
|
+
[![Gem](https://img.shields.io/gem/v/wongi-engine.svg)](https://rubygems.org/gems/wongi-engine/)
|
4
|
+
[![Build Status](https://github.com/ulfurinn/wongi-engine/actions/workflows/test.yml/badge.svg)](https://github.com/ulfurinn/wongi-engine/actions/workflows/test.yml)
|
5
5
|
|
6
|
-
|
6
|
+
This is a pure-Ruby forward-chaining rule engine based on the classic [Rete algorithm](http://en.wikipedia.org/wiki/Rete_algorithm).
|
7
7
|
|
8
|
-
|
8
|
+
Ruby >= 2.7 and JRuby are supported. Rubinius should work but isn't actively supported.
|
9
9
|
|
10
|
-
|
10
|
+
## Documentation
|
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
|
-
[
|
14
|
+
Instead, follow the [tutorial](https://ulfurinn.github.io/wongi-engine/) and stick to the constructs described in it.
|
15
|
+
|
16
|
+
## Upgrading
|
17
|
+
|
18
|
+
Until there is a 1.0 release, all minor versions should be treated as potentially breaking.
|
19
|
+
|
20
|
+
Always test your rules extensively. There's always a chance of you finding a bug in the engine that is only triggered by a very specific rule configuration.
|
21
|
+
|
22
|
+
[Feature annoucements](https://github.com/ulfurinn/wongi-engine/issues?q=is%3Aissue+label%3Aannoucement)
|
15
23
|
|
16
24
|
[Open discussions](https://github.com/ulfurinn/wongi-engine/issues?q=is%3Aopen+is%3Aissue+label%3Adiscussion)
|
17
25
|
|
18
26
|
## Acknowledgements
|
19
27
|
|
20
28
|
The Rete implementation in this library largely follows the outline presented in [\[Doorenbos, 1995\]](http://reports-archive.adm.cs.cmu.edu/anon/1995/CMU-CS-95-113.pdf).
|
21
|
-
|
22
|
-
## Contributing
|
23
|
-
|
24
|
-
1. Fork it
|
25
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
26
|
-
3. Commit your changes (`git commit -am 'Added some feature'`)
|
27
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
28
|
-
5. Create new Pull Request
|
data/Rakefile
CHANGED
data/examples/ex01.rb
CHANGED
data/examples/ex02.rb
CHANGED
@@ -4,7 +4,6 @@ include Wongi::Engine::DSL
|
|
4
4
|
ds = Network.new
|
5
5
|
|
6
6
|
ds << ruleset {
|
7
|
-
|
8
7
|
name "Example"
|
9
8
|
|
10
9
|
rule "symmetric" do
|
@@ -16,22 +15,19 @@ ds << ruleset {
|
|
16
15
|
gen :B, :P, :A
|
17
16
|
}
|
18
17
|
end
|
19
|
-
|
20
18
|
}
|
21
19
|
|
22
20
|
puts "Installed ruleset"
|
23
21
|
|
24
|
-
ds << WME.new(
|
25
|
-
ds << WME.new(
|
22
|
+
ds << WME.new("friend", "symmetric", true)
|
23
|
+
ds << WME.new("Alice", "friend", "Bob")
|
26
24
|
|
27
25
|
puts "Asserted facts:"
|
28
26
|
|
29
27
|
puts "Should print 3 facts:"
|
30
28
|
puts ds.wmes.to_a
|
31
29
|
|
32
|
-
|
33
|
-
ds.retract WME.new( "Alice", "friend", "Bob" )
|
30
|
+
ds.retract WME.new("Alice", "friend", "Bob")
|
34
31
|
|
35
32
|
puts "Should print 1 fact:"
|
36
33
|
puts ds.wmes.to_a
|
37
|
-
|
data/examples/graphviz.rb
CHANGED
data/examples/rdf.rb
CHANGED
@@ -4,7 +4,7 @@ require 'wongi-engine'
|
|
4
4
|
engine = Wongi::Engine.create
|
5
5
|
engine.rdf!
|
6
6
|
parser = Wongi::RDF::Parser.new
|
7
|
-
parser.parse_file File.expand_path(
|
7
|
+
parser.parse_file File.expand_path("rdf.n3", File.dirname(__FILE__)), engine
|
8
8
|
|
9
9
|
engine.each do |wme|
|
10
10
|
puts wme
|
data/examples/timeline.rb
CHANGED
@@ -8,12 +8,12 @@ engine = Wongi::Engine.create
|
|
8
8
|
|
9
9
|
engine.debug!
|
10
10
|
|
11
|
-
engine.compile_alpha Wongi::Engine::Template.new(
|
12
|
-
engine.compile_alpha Wongi::Engine::Template.new(
|
13
|
-
engine.compile_alpha Wongi::Engine::Template.new(
|
14
|
-
engine.compile_alpha Wongi::Engine::Template.new(
|
15
|
-
engine.compile_alpha Wongi::Engine::Template.new(
|
16
|
-
engine.compile_alpha Wongi::Engine::Template.new(
|
11
|
+
engine.compile_alpha Wongi::Engine::Template.new(:_, :_, :_, 0)
|
12
|
+
engine.compile_alpha Wongi::Engine::Template.new(:_, :_, :_, -1)
|
13
|
+
engine.compile_alpha Wongi::Engine::Template.new(:_, :_, :_, -2)
|
14
|
+
engine.compile_alpha Wongi::Engine::Template.new(:_, :_, :_, -3)
|
15
|
+
engine.compile_alpha Wongi::Engine::Template.new(:_, :_, :_, -4)
|
16
|
+
engine.compile_alpha Wongi::Engine::Template.new(:_, :_, :_, -5)
|
17
17
|
|
18
18
|
engine.full_wme_dump
|
19
19
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
class AlphaIndex
|
3
|
+
attr_reader :pattern, :index
|
4
|
+
private :pattern
|
5
|
+
private :index
|
6
|
+
|
7
|
+
def initialize(pattern)
|
8
|
+
@pattern = pattern
|
9
|
+
@index = Hash.new { |h, k| h[k] = Set.new }
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(wme)
|
13
|
+
collection_for_wme(wme).add(wme)
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove(wme)
|
17
|
+
collection = collection_for_wme(wme)
|
18
|
+
collection.delete(wme)
|
19
|
+
|
20
|
+
# release some memory
|
21
|
+
index.delete(hashed_key(wme)) if collection.empty?
|
22
|
+
end
|
23
|
+
|
24
|
+
def collection_for_wme(wme)
|
25
|
+
index[hashed_key(wme)]
|
26
|
+
end
|
27
|
+
|
28
|
+
private def key(wme)
|
29
|
+
pattern.map { wme.public_send(_1) }
|
30
|
+
end
|
31
|
+
|
32
|
+
private def hashed_key(wme)
|
33
|
+
key(wme).map(&:hash)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,38 +1,28 @@
|
|
1
1
|
module Wongi::Engine
|
2
|
-
|
3
2
|
class AlphaMemory
|
4
|
-
|
5
3
|
attr_reader :betas, :template, :rete
|
6
4
|
|
7
|
-
def initialize
|
5
|
+
def initialize(template, rete)
|
8
6
|
@template = template
|
9
7
|
@rete = rete
|
10
8
|
@betas = []
|
11
|
-
@wmes = []
|
12
9
|
@frozen = false
|
13
10
|
end
|
14
11
|
|
15
|
-
def activate
|
16
|
-
wme.overlay.add_wme(wme, self)
|
12
|
+
def activate(wme)
|
17
13
|
# TODO: it used to activate before adding to the list. mandated by the original thesis. investigate. it appears to create duplicate tokens - needs a remedy in collecting nodes
|
18
14
|
betas.each do |beta|
|
19
15
|
beta.alpha_activate wme
|
20
16
|
end
|
21
17
|
end
|
22
18
|
|
23
|
-
def deactivate
|
24
|
-
wme
|
19
|
+
def deactivate(wme)
|
20
|
+
# p deactivate: {wme:}
|
25
21
|
betas.each do |beta|
|
26
22
|
beta.alpha_deactivate wme
|
27
23
|
end
|
28
24
|
end
|
29
25
|
|
30
|
-
def snapshot! alpha
|
31
|
-
alpha.wmes.map( &:dup ).each do |wme|
|
32
|
-
activate wme
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
26
|
def inspect
|
37
27
|
"<Alpha #{__id__} template=#{template}>"
|
38
28
|
end
|
@@ -40,21 +30,5 @@ module Wongi::Engine
|
|
40
30
|
def to_s
|
41
31
|
inspect
|
42
32
|
end
|
43
|
-
|
44
|
-
def size
|
45
|
-
wmes.count
|
46
|
-
end
|
47
|
-
|
48
|
-
def wmes
|
49
|
-
Enumerator.new do |y|
|
50
|
-
rete.overlays.each do |overlay|
|
51
|
-
overlay.raw_wmes(self).dup.each do |wme|
|
52
|
-
y << wme
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
33
|
end
|
59
|
-
|
60
34
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
class AggregateNode < BetaNode
|
3
|
+
attr_reader :var, :over, :partition, :aggregate, :map
|
4
|
+
|
5
|
+
def initialize(parent, var, over, partition, aggregate, map)
|
6
|
+
super(parent)
|
7
|
+
@var = var
|
8
|
+
@over = over
|
9
|
+
@partition = make_partition_fn(partition)
|
10
|
+
@aggregate = make_aggregate_fn(aggregate)
|
11
|
+
@map = make_map_fn(map)
|
12
|
+
end
|
13
|
+
|
14
|
+
def make_partition_fn(partition)
|
15
|
+
return nil if partition.empty?
|
16
|
+
|
17
|
+
->(token) { token.values_at(*partition) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def make_aggregate_fn(agg)
|
21
|
+
agg
|
22
|
+
end
|
23
|
+
|
24
|
+
def make_map_fn(map)
|
25
|
+
if map.nil?
|
26
|
+
->(token) { token[over] }
|
27
|
+
else
|
28
|
+
map
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def beta_activate(token)
|
33
|
+
return if tokens.find { |t| t.duplicate? token }
|
34
|
+
|
35
|
+
overlay.add_token(token)
|
36
|
+
evaluate
|
37
|
+
end
|
38
|
+
|
39
|
+
def beta_deactivate(token)
|
40
|
+
overlay.remove_token(token)
|
41
|
+
beta_deactivate_children(token: token)
|
42
|
+
evaluate
|
43
|
+
end
|
44
|
+
|
45
|
+
def refresh_child(child)
|
46
|
+
evaluate(child: child)
|
47
|
+
end
|
48
|
+
|
49
|
+
def evaluate(child: nil)
|
50
|
+
return if tokens.empty?
|
51
|
+
|
52
|
+
groups =
|
53
|
+
if partition
|
54
|
+
tokens.group_by(&partition).values
|
55
|
+
else
|
56
|
+
# just a single group of everything
|
57
|
+
[tokens]
|
58
|
+
end
|
59
|
+
|
60
|
+
groups.each do |tokens|
|
61
|
+
aggregated = aggregate.call(tokens.map(&map))
|
62
|
+
assignment = { var => aggregated }
|
63
|
+
children = child ? [child] : self.children
|
64
|
+
children.each do |beta|
|
65
|
+
new_token = Token.new(beta, tokens, nil, assignment)
|
66
|
+
# nothing changed, skip useless traversal
|
67
|
+
next if beta.tokens.find { _1.duplicate?(new_token) }
|
68
|
+
|
69
|
+
beta.tokens.select { |child| tokens.any? { child.child_of?(_1) } }.each { beta.beta_deactivate(_1) }
|
70
|
+
beta.beta_activate(new_token)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
77
|
+
def matches?(token, wme)
|
78
|
+
@tests.each do |test|
|
79
|
+
return false unless test.matches?(token, wme)
|
80
|
+
end
|
81
|
+
true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -1,40 +1,37 @@
|
|
1
1
|
module Wongi::Engine
|
2
|
-
|
3
2
|
class AssignmentNode < BetaNode
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
@
|
3
|
+
def initialize(parent, variable, body)
|
4
|
+
super(parent)
|
5
|
+
@variable = variable
|
6
|
+
@body = body
|
8
7
|
end
|
9
8
|
|
10
|
-
def beta_activate
|
9
|
+
def beta_activate(token, _wme = nil, _assignments = {})
|
10
|
+
return if tokens.find { |t| t.duplicate? token }
|
11
|
+
|
12
|
+
overlay.add_token(token)
|
11
13
|
children.each do |child|
|
12
|
-
|
14
|
+
value = @body.respond_to?(:call) ? @body.call(token) : @body
|
15
|
+
child.beta_activate Token.new(child, token, nil, { @variable => value })
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
16
|
-
def beta_deactivate
|
19
|
+
def beta_deactivate(token)
|
20
|
+
overlay.remove_token(token)
|
17
21
|
children.each do |child|
|
18
22
|
child.tokens.each do |t|
|
19
|
-
if t.
|
23
|
+
if t.child_of?(token)
|
20
24
|
child.beta_deactivate t
|
21
|
-
#token.destroy
|
25
|
+
# token.destroy
|
22
26
|
end
|
23
27
|
end
|
24
28
|
end
|
25
29
|
end
|
26
30
|
|
27
|
-
def refresh_child
|
28
|
-
|
29
|
-
|
30
|
-
parent.tokens.each do |token|
|
31
|
-
children.each do |child|
|
32
|
-
child.beta_activate Token.new( child, token, nil, { @variable => @body.respond_to?(:call) ? @body.call(token) : @body } )
|
33
|
-
end
|
31
|
+
def refresh_child(child)
|
32
|
+
tokens.each do |token|
|
33
|
+
child.beta_activate Token.new(child, token, nil, { @variable => @body.respond_to?(:call) ? @body.call(token) : @body })
|
34
34
|
end
|
35
|
-
self.children = tmp
|
36
35
|
end
|
37
|
-
|
38
36
|
end
|
39
|
-
|
40
37
|
end
|