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.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +24 -0
  3. data/.gitignore +3 -0
  4. data/.rubocop.yml +86 -0
  5. data/.travis.yml +8 -15
  6. data/Gemfile +4 -0
  7. data/README.md +15 -15
  8. data/Rakefile +1 -2
  9. data/examples/ex01.rb +1 -1
  10. data/examples/ex02.rb +3 -7
  11. data/examples/graphviz.rb +1 -1
  12. data/examples/rdf.rb +1 -1
  13. data/examples/timeline.rb +6 -6
  14. data/lib/wongi-engine/alpha_index.rb +36 -0
  15. data/lib/wongi-engine/alpha_memory.rb +4 -30
  16. data/lib/wongi-engine/beta/aggregate_node.rb +84 -0
  17. data/lib/wongi-engine/beta/assignment_node.rb +17 -20
  18. data/lib/wongi-engine/beta/beta_node.rb +49 -43
  19. data/lib/wongi-engine/beta/filter_node.rb +18 -26
  20. data/lib/wongi-engine/beta/join_node.rb +58 -84
  21. data/lib/wongi-engine/beta/ncc_node.rb +23 -38
  22. data/lib/wongi-engine/beta/ncc_partner.rb +29 -29
  23. data/lib/wongi-engine/beta/neg_node.rb +43 -76
  24. data/lib/wongi-engine/beta/optional_node.rb +60 -89
  25. data/lib/wongi-engine/beta/or_node.rb +29 -9
  26. data/lib/wongi-engine/beta/production_node.rb +15 -12
  27. data/lib/wongi-engine/beta/root_node.rb +47 -0
  28. data/lib/wongi-engine/beta.rb +2 -1
  29. data/lib/wongi-engine/compiler.rb +24 -39
  30. data/lib/wongi-engine/core_ext.rb +11 -20
  31. data/lib/wongi-engine/dsl/action/assign_action.rb +1 -1
  32. data/lib/wongi-engine/dsl/action/base_action.rb +12 -0
  33. data/lib/wongi-engine/dsl/action/error_generator.rb +10 -10
  34. data/lib/wongi-engine/dsl/action/simple_action.rb +15 -11
  35. data/lib/wongi-engine/dsl/action/simple_collector.rb +11 -26
  36. data/lib/wongi-engine/dsl/action/statement_generator.rb +47 -29
  37. data/lib/wongi-engine/dsl/action/trace_action.rb +10 -10
  38. data/lib/wongi-engine/dsl/any_rule.rb +16 -20
  39. data/lib/wongi-engine/dsl/assuming.rb +8 -15
  40. data/lib/wongi-engine/dsl/builder.rb +13 -12
  41. data/lib/wongi-engine/dsl/clause/aggregate.rb +20 -0
  42. data/lib/wongi-engine/dsl/clause/assign.rb +4 -4
  43. data/lib/wongi-engine/dsl/clause/fact.rb +10 -13
  44. data/lib/wongi-engine/dsl/clause/gen.rb +3 -5
  45. data/lib/wongi-engine/dsl/clause/generic.rb +7 -9
  46. data/lib/wongi-engine/dsl/generated.rb +6 -12
  47. data/lib/wongi-engine/dsl/ncc_subrule.rb +6 -9
  48. data/lib/wongi-engine/dsl/query.rb +12 -15
  49. data/lib/wongi-engine/dsl/rule.rb +45 -50
  50. data/lib/wongi-engine/dsl.rb +42 -36
  51. data/lib/wongi-engine/entity_iterator.rb +60 -0
  52. data/lib/wongi-engine/enumerators.rb +2 -1
  53. data/lib/wongi-engine/error.rb +6 -7
  54. data/lib/wongi-engine/filter/asserting_test.rb +4 -7
  55. data/lib/wongi-engine/filter/equality_test.rb +16 -19
  56. data/lib/wongi-engine/filter/filter_test.rb +3 -6
  57. data/lib/wongi-engine/filter/greater_than_or_equal_test.rb +6 -3
  58. data/lib/wongi-engine/filter/greater_than_test.rb +16 -19
  59. data/lib/wongi-engine/filter/in_list_test.rb +33 -0
  60. data/lib/wongi-engine/filter/inequality_test.rb +16 -19
  61. data/lib/wongi-engine/filter/less_than_or_equal_test.rb +6 -3
  62. data/lib/wongi-engine/filter/less_than_test.rb +16 -19
  63. data/lib/wongi-engine/filter/not_in_list_test.rb +33 -0
  64. data/lib/wongi-engine/filter.rb +3 -1
  65. data/lib/wongi-engine/generator_tracker.rb +32 -0
  66. data/lib/wongi-engine/graph.rb +25 -36
  67. data/lib/wongi-engine/join_results.rb +80 -0
  68. data/lib/wongi-engine/network/collectable.rb +10 -14
  69. data/lib/wongi-engine/network/debug.rb +15 -31
  70. data/lib/wongi-engine/network.rb +113 -206
  71. data/lib/wongi-engine/overlay.rb +489 -0
  72. data/lib/wongi-engine/ruleset.rb +18 -20
  73. data/lib/wongi-engine/template.rb +48 -35
  74. data/lib/wongi-engine/token.rb +51 -85
  75. data/lib/wongi-engine/token_assignment.rb +15 -0
  76. data/lib/wongi-engine/version.rb +1 -1
  77. data/lib/wongi-engine/wme.rb +11 -58
  78. data/lib/wongi-engine/wme_match_data.rb +5 -9
  79. data/lib/wongi-engine.rb +6 -5
  80. data/spec/action_class_spec.rb +44 -50
  81. data/spec/aggregate_spec.rb +213 -0
  82. data/spec/alpha_index_spec.rb +78 -0
  83. data/spec/any_rule_spec.rb +202 -0
  84. data/spec/{filter_specs/assert_test_spec.rb → assert_test_spec.rb} +17 -36
  85. data/spec/{rule_specs/assign_spec.rb → assign_spec.rb} +15 -21
  86. data/spec/{rule_specs/assuming_spec.rb → assuming_spec.rb} +13 -20
  87. data/spec/beta_node_spec.rb +3 -9
  88. data/spec/bug_specs/issue_4_spec.rb +25 -33
  89. data/spec/dataset_spec.rb +5 -8
  90. data/spec/entity_iterator_spec.rb +88 -0
  91. data/spec/generation_spec.rb +51 -60
  92. data/spec/greater_than_equality_test_spec.rb +24 -0
  93. data/spec/high_level_spec.rb +85 -224
  94. data/spec/in_list_spec.rb +115 -0
  95. data/spec/less_test_spec.rb +25 -0
  96. data/spec/{filter_specs/less_than_equality_test_spec.rb → less_than_equality_test_spec.rb} +5 -8
  97. data/spec/maybe_rule_spec.rb +147 -0
  98. data/spec/ncc_spec.rb +339 -0
  99. data/spec/negative_rule_spec.rb +83 -0
  100. data/spec/network_spec.rb +83 -73
  101. data/spec/overlay_spec.rb +179 -8
  102. data/spec/ruleset_spec.rb +23 -29
  103. data/spec/simple_action_spec.rb +5 -15
  104. data/spec/spec_helper.rb +4 -0
  105. data/spec/wme_spec.rb +17 -56
  106. data/wongi-engine.gemspec +28 -26
  107. metadata +46 -57
  108. data/.hgignore +0 -7
  109. data/lib/wongi-engine/beta/beta_memory.rb +0 -49
  110. data/lib/wongi-engine/data_overlay.rb +0 -144
  111. data/lib/wongi-engine/dsl/action/base.rb +0 -11
  112. data/spec/dsl_spec.rb +0 -9
  113. data/spec/filter_specs/greater_than_equality_test_spec.rb +0 -33
  114. data/spec/filter_specs/less_test_spec.rb +0 -41
  115. data/spec/rule_specs/any_rule_spec.rb +0 -109
  116. data/spec/rule_specs/maybe_rule_spec.rb +0 -101
  117. data/spec/rule_specs/ncc_spec.rb +0 -274
  118. data/spec/rule_specs/negative_rule_spec.rb +0 -105
  119. 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: d3f9d2adf82d8e36010a2ea956c308d58c073673cba49013352c420ed69180e5
4
- data.tar.gz: 792ee293fce552b92c516f17749253fe9c6ba6c72f8b8e4c751b32b7f87902a6
3
+ metadata.gz: 461346062a679921c61fc0b35bc2e89ef0d06920745d650b48fedbbc452b780a
4
+ data.tar.gz: df2bf13a3551dc049e08398b11ac42440a34e4cb651f5092b74d3efdd5a3aecf
5
5
  SHA512:
6
- metadata.gz: 319936296084020880b58d775a241eaa575bfce12246fac060e8150a0ff97a30aa7558653c771548616b7a1d0da450fa625dc11ce9ceca490cdf8affd2005231
7
- data.tar.gz: 8f7397d233f1eb4cd714c0c69caa866cd18dd42a3304bc19c263662bafb122382b9be50ffd101bb9dacf50a9c4ce11804b4071dfefefeda6d72a38d60074b8f0
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
@@ -18,3 +18,6 @@ test/tmp
18
18
  test/version_tmp
19
19
  tmp
20
20
  .ruby-version
21
+ .idea
22
+ .hugo_build.lock
23
+ themes
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.1.10
4
- - 2.2.6
5
- - 2.3.3
6
- - 2.4.0
7
- - jruby-9.0
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
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in wongi-engine.gemspec
4
4
  gemspec
5
+
6
+ gem 'rubocop', require: false
7
+ gem 'rubocop-rake', require: false
8
+ gem 'rubocop-rspec', require: false
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://travis-ci.org/ulfurinn/wongi-engine.svg?branch=master)](https://travis-ci.org/ulfurinn/wongi-engine)
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
- Ruby >= 2.1 and JRuby are supported. Rubinius should work but isn't actively supported.
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
- ## [Documentation](http://ulfurinn.github.io/wongi-engine/)
8
+ Ruby >= 2.7 and JRuby are supported. Rubinius should work but isn't actively supported.
9
9
 
10
- This library contains a rule engine written in Ruby. It's based on the [Rete algorithm](http://en.wikipedia.org/wiki/Rete_algorithm) and uses a DSL to express rules in a readable way.
10
+ ## Documentation
11
11
 
12
- **Word of caution**: this is complex and fragile machinery, and there may be subtle bugs that are only revealed with nontrivial usage. Be conservative with upgrades, test your rules extensively, and please report any behaviour that is not consistent with your expectations.
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
- [Feature annoucements](https://github.com/ulfurinn/wongi-engine/issues?q=is%3Aopen+is%3Aissue+label%3Aannoucement)
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
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env rake
2
1
  require "bundler/gem_tasks"
3
2
 
4
3
  require 'rspec/core/rake_task'
@@ -6,4 +5,4 @@ require 'rspec/core/rake_task'
6
5
  RSpec::Core::RakeTask.new('spec')
7
6
 
8
7
  # If you want to make this the default task
9
- task :default => :spec
8
+ task default: :spec
data/examples/ex01.rb CHANGED
@@ -2,7 +2,7 @@ include Wongi::Engine
2
2
 
3
3
  ds = Network.new
4
4
 
5
- ds << WME.new( "Alice", "friend", "Bob" )
5
+ ds << WME.new("Alice", "friend", "Bob")
6
6
 
7
7
  puts "Enumerate all:"
8
8
 
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( "friend", "symmetric", true )
25
- ds << WME.new( "Alice", "friend", "Bob" )
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
@@ -12,5 +12,5 @@ ds << rule('demo') {
12
12
  }
13
13
 
14
14
  File.open "rete.dot", "w" do |io|
15
- Graph.new( ds ).dot( io )
15
+ Graph.new(ds).dot(io)
16
16
  end
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( "rdf.n3", File.dirname(__FILE__) ), engine
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( :_, :_, :_, 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 )
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 template, rete = nil
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 wme
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 wme
24
- wme.overlay.remove_wme(wme, self)
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
- def initialize parent, variable, body
6
- super parent
7
- @variable, @body = variable, body
3
+ def initialize(parent, variable, body)
4
+ super(parent)
5
+ @variable = variable
6
+ @body = body
8
7
  end
9
8
 
10
- def beta_activate token, wme = nil, assignments = { }
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
- child.beta_activate Token.new( child, token, nil, { @variable => @body.respond_to?(:call) ? @body.call(token) : @body } )
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 token
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.parent == token
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 child
28
- tmp = children
29
- self.children = [ child ]
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