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.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.hgignore +6 -0
  4. data/.hgtags +13 -0
  5. data/.ruby-gemset +1 -0
  6. data/.travis.yml +19 -0
  7. data/CHANGELOG.md +106 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE +22 -0
  10. data/README.md +27 -0
  11. data/Rakefile +9 -0
  12. data/examples/ex01.rb +23 -0
  13. data/examples/ex02.rb +37 -0
  14. data/examples/graphviz.rb +16 -0
  15. data/examples/rdf.n3 +6 -0
  16. data/examples/rdf.rb +14 -0
  17. data/examples/timeline.rb +48 -0
  18. data/lib/wongi-engine.rb +36 -0
  19. data/lib/wongi-engine/alpha_memory.rb +60 -0
  20. data/lib/wongi-engine/beta.rb +11 -0
  21. data/lib/wongi-engine/beta/assignment_node.rb +40 -0
  22. data/lib/wongi-engine/beta/beta_memory.rb +49 -0
  23. data/lib/wongi-engine/beta/beta_node.rb +94 -0
  24. data/lib/wongi-engine/beta/filter_node.rb +48 -0
  25. data/lib/wongi-engine/beta/join_node.rb +140 -0
  26. data/lib/wongi-engine/beta/ncc_node.rb +67 -0
  27. data/lib/wongi-engine/beta/ncc_partner.rb +40 -0
  28. data/lib/wongi-engine/beta/neg_node.rb +115 -0
  29. data/lib/wongi-engine/beta/optional_node.rb +142 -0
  30. data/lib/wongi-engine/beta/or_node.rb +37 -0
  31. data/lib/wongi-engine/beta/production_node.rb +31 -0
  32. data/lib/wongi-engine/compiler.rb +115 -0
  33. data/lib/wongi-engine/core_ext.rb +63 -0
  34. data/lib/wongi-engine/data_overlay.rb +144 -0
  35. data/lib/wongi-engine/dsl.rb +132 -0
  36. data/lib/wongi-engine/dsl/action/base.rb +11 -0
  37. data/lib/wongi-engine/dsl/action/error_generator.rb +31 -0
  38. data/lib/wongi-engine/dsl/action/simple_action.rb +60 -0
  39. data/lib/wongi-engine/dsl/action/simple_collector.rb +52 -0
  40. data/lib/wongi-engine/dsl/action/statement_generator.rb +46 -0
  41. data/lib/wongi-engine/dsl/action/trace_action.rb +49 -0
  42. data/lib/wongi-engine/dsl/any_rule.rb +33 -0
  43. data/lib/wongi-engine/dsl/assuming.rb +31 -0
  44. data/lib/wongi-engine/dsl/builder.rb +44 -0
  45. data/lib/wongi-engine/dsl/clause/assign.rb +15 -0
  46. data/lib/wongi-engine/dsl/clause/fact.rb +71 -0
  47. data/lib/wongi-engine/dsl/clause/gen.rb +17 -0
  48. data/lib/wongi-engine/dsl/clause/generic.rb +38 -0
  49. data/lib/wongi-engine/dsl/generated.rb +43 -0
  50. data/lib/wongi-engine/dsl/ncc_subrule.rb +17 -0
  51. data/lib/wongi-engine/dsl/query.rb +24 -0
  52. data/lib/wongi-engine/dsl/rule.rb +84 -0
  53. data/lib/wongi-engine/enumerators.rb +21 -0
  54. data/lib/wongi-engine/error.rb +22 -0
  55. data/lib/wongi-engine/filter.rb +6 -0
  56. data/lib/wongi-engine/filter/asserting_test.rb +20 -0
  57. data/lib/wongi-engine/filter/equality_test.rb +36 -0
  58. data/lib/wongi-engine/filter/filter_test.rb +18 -0
  59. data/lib/wongi-engine/filter/greater_than_test.rb +36 -0
  60. data/lib/wongi-engine/filter/inequality_test.rb +36 -0
  61. data/lib/wongi-engine/filter/less_than_test.rb +36 -0
  62. data/lib/wongi-engine/graph.rb +73 -0
  63. data/lib/wongi-engine/network.rb +416 -0
  64. data/lib/wongi-engine/network/collectable.rb +42 -0
  65. data/lib/wongi-engine/network/debug.rb +85 -0
  66. data/lib/wongi-engine/ruleset.rb +74 -0
  67. data/lib/wongi-engine/template.rb +78 -0
  68. data/lib/wongi-engine/token.rb +114 -0
  69. data/lib/wongi-engine/version.rb +5 -0
  70. data/lib/wongi-engine/wme.rb +89 -0
  71. data/lib/wongi-engine/wme_match_data.rb +34 -0
  72. data/spec/beta_node_spec.rb +29 -0
  73. data/spec/bug_specs/issue_4_spec.rb +141 -0
  74. data/spec/dataset_spec.rb +27 -0
  75. data/spec/dsl_spec.rb +9 -0
  76. data/spec/filter_specs/assert_test_spec.rb +102 -0
  77. data/spec/filter_specs/less_test_spec.rb +41 -0
  78. data/spec/generation_spec.rb +116 -0
  79. data/spec/high_level_spec.rb +378 -0
  80. data/spec/network_spec.rb +182 -0
  81. data/spec/overlay_spec.rb +61 -0
  82. data/spec/rule_specs/any_rule_spec.rb +75 -0
  83. data/spec/rule_specs/assign_spec.rb +88 -0
  84. data/spec/rule_specs/assuming_spec.rb +66 -0
  85. data/spec/rule_specs/maybe_rule_spec.rb +101 -0
  86. data/spec/rule_specs/ncc_spec.rb +258 -0
  87. data/spec/rule_specs/negative_rule_spec.rb +105 -0
  88. data/spec/ruleset_spec.rb +54 -0
  89. data/spec/simple_action_spec.rb +40 -0
  90. data/spec/spec_helper.rb +3 -0
  91. data/spec/wme_spec.rb +83 -0
  92. data/wongi-engine.gemspec +40 -0
  93. metadata +212 -0
@@ -0,0 +1,46 @@
1
+ module Wongi::Engine
2
+ module DSL::Action
3
+ class StatementGenerator < Base
4
+
5
+ def initialize template
6
+ @template = template
7
+ end
8
+
9
+ def execute token
10
+ subject, predicate, object = @template.resolve!(token)
11
+
12
+ # link to rete here to ensure proper linking with token
13
+ wme = WME.new subject, predicate, object, rete
14
+ wme.manual = false
15
+ wme.overlay = token.overlay
16
+
17
+ production.tracer.trace( action: self, wme: wme ) if production.tracer
18
+ if existing = rete.exists?( wme )
19
+ generated = existing.generating_tokens.size
20
+ if generated > 0 && ! token.generated_wmes.include?( existing )
21
+ token.generated_wmes << existing
22
+ existing.generating_tokens << token
23
+ end
24
+ else
25
+ token.generated_wmes << wme
26
+ wme.generating_tokens << token
27
+ # this MUST be done after we link the wme and the token
28
+ # in order for neg rule invalidation to work
29
+ wme.overlay.assert wme
30
+ end
31
+
32
+ end
33
+
34
+ def deexecute token
35
+ token.generated_wmes.reject( &:manual? ).inject( [] ) do |list, wme|
36
+ list.tap do |l|
37
+ wme.generating_tokens.delete token
38
+ l << wme if wme.generating_tokens.empty?
39
+ end
40
+ end.each do |wme|
41
+ wme.overlay.retract wme, automatic: true
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,49 @@
1
+ module Wongi::Engine
2
+ module DSL::Action
3
+ class TraceAction < Base
4
+
5
+ class DefaultTracer
6
+ attr_accessor :action
7
+ def trace args
8
+ case args[:action]
9
+ when TraceAction
10
+ if args[:token]
11
+ action.io.puts "EXECUTED RULE #{args[:action].rule.name} WITH #{args[:token]}"
12
+ else
13
+ action.io.puts "EXECUTED RULE #{args[:action].rule.name}"
14
+ end
15
+ when StatementGenerator
16
+ action.io.puts "GENERATED #{args[:wme]}" if action.generation?
17
+ end
18
+ end
19
+ end
20
+
21
+ attr_reader :io
22
+ attr_predicate :generation, :values
23
+
24
+ def initialize opts = { }
25
+ [:generation, :values, :tracer, :tracer_class, :io].each do |option|
26
+ if opts.has_key? option
27
+ instance_variable_set "@#{option}", opts[option]
28
+ end
29
+ end
30
+ @io ||= $stdout
31
+ @tracer ||= (@tracer_class || DefaultTracer).new
32
+ @tracer.action = self
33
+ end
34
+
35
+ def trace args
36
+ @tracer.trace args
37
+ end
38
+
39
+ def execute token
40
+ production.tracer = self
41
+ if values?
42
+ trace action: self, token: token
43
+ else
44
+ trace action: self
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,33 @@
1
+ module Wongi::Engine
2
+ module DSL
3
+ class AnyRule
4
+
5
+ attr_reader :variants
6
+
7
+ def initialize &block
8
+ @variants = []
9
+ if block
10
+ instance_eval &block
11
+ end
12
+ end
13
+
14
+ def option &block
15
+ var = VariantRule.new
16
+ var.instance_eval &block
17
+ variants << var
18
+ end
19
+
20
+ def compile context
21
+ context.tap { |c| c.or_node(variants) }
22
+ end
23
+
24
+ end
25
+
26
+ class VariantRule < Rule
27
+ def initialize name = nil
28
+ super
29
+ @current_section = :forall
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ module Wongi::Engine
2
+
3
+ class UndefinedBaseRule < StandardError
4
+ def initialize rule_name
5
+ @rule_name = rule_name
6
+ end
7
+
8
+ def message
9
+ "undefined production #@rule_name"
10
+ end
11
+ end
12
+
13
+ class AssumingClause
14
+
15
+ attr_reader :base_rule_name
16
+
17
+ def initialize base_rule_name
18
+ @base_rule_name = base_rule_name
19
+ end
20
+
21
+ def compile context
22
+ base_production = context.rete.productions[base_rule_name]
23
+ raise UndefinedBaseRule.new(base_rule_name) unless base_production
24
+ raise DefinitionError.new("'assuming' cannot be preceded by other matchers") unless context.node.root?
25
+ raise StandardError.new("missing base context") unless base_production.compilation_context
26
+ base_production.compilation_context.dup
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,44 @@
1
+ module Wongi::Engine::DSL
2
+ class Builder
3
+
4
+ def initialize
5
+ @current_section = nil
6
+ @current_clause = nil
7
+ @clauses = []
8
+ end
9
+
10
+ def build &definition
11
+ instance_eval &definition
12
+ @clauses.each do |c|
13
+ Generated.create_dsl_method c
14
+ end
15
+ end
16
+
17
+ def section s
18
+ @current_section = s
19
+ end
20
+
21
+ def clause *c
22
+ @current_clause = c
23
+ end
24
+
25
+ def action klass = nil, &block
26
+ raise DefinitionError, "Cannot create an action without a clause" if @current_clause.nil?
27
+ @clauses << { section: @current_section, clause: @current_clause, action: klass || block }
28
+ @current_clause = nil
29
+ end
30
+
31
+ def body klass = nil, &block
32
+ raise DefinitionError, "Cannot create a body without a clause" if @current_clause.nil?
33
+ @clauses << { section: @current_section, clause: @current_clause, body: klass || block }
34
+ @current_clause = nil
35
+ end
36
+
37
+ def accept klass
38
+ raise DefinitionError, "Cannot create an acceptor without a clause" if @current_clause.nil?
39
+ @clauses << { section: @current_section, clause: @current_clause, accept: klass }
40
+ @current_clause = nil
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ module Wongi::Engine
2
+ module DSL::Clause
3
+ class Assign
4
+
5
+ def initialize variable, &body
6
+ @variable, @body = variable, body
7
+ raise DefinitionError, "#{variable} is not a variable" unless Template.variable?(variable)
8
+ end
9
+
10
+ def compile context
11
+ context.tap { |c| c.assignment_node(@variable, @body) }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,71 @@
1
+ module Wongi::Engine
2
+ module DSL::Clause
3
+ Has = Struct.new(:subject, :predicate, :object, :time) do
4
+ include CoreExt
5
+ attr_predicate :debug
6
+
7
+ def initialize(s, p, o, options = { })
8
+ time = options[:time] || 0
9
+ @unsafe = options[:unsafe] || false
10
+ debug! if options[:debug]
11
+ raise "Cannot work with continuous time" unless time.integer?
12
+ raise "Cannot look into the future" if time > 0
13
+ super(s, p, o, time)
14
+ end
15
+
16
+ def compile(context)
17
+ tests, assignment = parse_variables(context)
18
+ context.tap { |c| c.join_node(self, tests, assignment) }
19
+ end
20
+
21
+ def inspect
22
+ "<+#{subject.inspect} #{predicate.inspect} #{object.inspect}>"
23
+ end
24
+
25
+ private
26
+
27
+ def parse_variables(context)
28
+ tests = []
29
+ assignment_mapping = [:subject, :predicate, :object].map do |member|
30
+ value = send(member)
31
+ if Template.variable?(value)
32
+ if context.declares_variable?(value)
33
+ tests << BetaTest.new(member, value)
34
+ :_
35
+ else
36
+ value
37
+ end
38
+ else
39
+ :_
40
+ end
41
+ end
42
+ assignment = Template.new(*assignment_mapping)
43
+ assignment.variables.each { |v| context.declare(v) }
44
+ [tests, assignment]
45
+ end
46
+ end
47
+
48
+ class Neg < Has
49
+ attr_reader :unsafe
50
+ def compile context
51
+ tests, assignment = parse_variables(context)
52
+ raise DefinitionError.new("Negative matches may not introduce new variables: #{assignment.variables}") unless assignment.root?
53
+ context.tap { |c| c.neg_node(self, tests, unsafe) }
54
+ end
55
+
56
+ def inspect
57
+ "<-#{subject.inspect} #{predicate.inspect} #{object.inspect}>"
58
+ end
59
+ end
60
+
61
+ class Opt < Has
62
+ def compile context
63
+ tests, assignment = parse_variables(context)
64
+ context.tap { |c| c.opt_node(self, tests, assignment) }
65
+ end
66
+ def inspect
67
+ "<?#{subject.inspect} #{predicate.inspect} #{object.inspect}>"
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,17 @@
1
+ module Wongi::Engine
2
+ module DSL::Clause
3
+ class Gen
4
+
5
+ def initialize s, p, o
6
+ @triple = Template.new( s, p, o )
7
+ end
8
+
9
+ def import_into rete
10
+ generator = DSL::Action::StatementGenerator.new @triple
11
+ generator.rete = rete
12
+ generator
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,38 @@
1
+ module Wongi::Engine
2
+ module DSL::Clause
3
+ class Generic
4
+
5
+ attr_accessor :name, :action, :rule
6
+
7
+ def initialize *args, &block
8
+ @args = args
9
+ @block = block
10
+ end
11
+
12
+ def import_into rete
13
+ if action.respond_to? :call
14
+ self
15
+ else
16
+ action.new( *@args, &@block ).tap do |a|
17
+ a.name = name if a.respond_to? :name=
18
+ a.rule = rule if a.respond_to? :rule=
19
+ a.rete = rete if a.respond_to? :rete=
20
+ end
21
+ end
22
+ rescue StandardError => e
23
+ e1 = StandardError.new "error defining clause #{name} handled by #{action}: #{e}"
24
+ e1.set_backtrace e.backtrace
25
+ raise e1
26
+ end
27
+
28
+ def compile *args
29
+ action.call *args
30
+ end
31
+
32
+ def execute *args
33
+ action.call *args
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,43 @@
1
+ module Wongi::Engine::DSL
2
+ module Generated
3
+
4
+ def self.create_dsl_method extension
5
+
6
+ section = extension[:section]
7
+ clause = extension[:clause]
8
+ action = extension[:action]
9
+ body = extension[:body]
10
+ acceptor = extension[:accept]
11
+
12
+ define_method clause.first do |*args, &block|
13
+
14
+ raise "#{clause.first} can only be invoke in section #{section}, currently in #{@current_section}" if section != @current_section
15
+
16
+ if body
17
+
18
+ instance_exec *args, &body
19
+
20
+ elsif acceptor
21
+
22
+ accept acceptor.new( *args, &block )
23
+
24
+ elsif action
25
+
26
+ c = Clause::Generic.new *args, &block
27
+ c.name = clause.first
28
+ c.action = action
29
+ c.rule = self
30
+ accept c
31
+
32
+ end
33
+
34
+ end
35
+
36
+ clause[1..-1].each do |al|
37
+ alias_method al, clause.first
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,17 @@
1
+ module Wongi::Engine
2
+ module DSL
3
+ class NccSubrule < Rule
4
+
5
+ def initialize name = nil, &block
6
+ super
7
+ if block
8
+ forall &block
9
+ end
10
+ end
11
+
12
+ def compile context
13
+ context.tap { |c| c.ncc_node(self, false) }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ module Wongi::Engine
2
+ module DSL
3
+ class Query < Rule
4
+
5
+ def search_on *terms
6
+ terms.each { |term| parameters << term }
7
+ end
8
+
9
+ def import_into model
10
+ super.tap { |copy| copy.search_on *parameters }
11
+ end
12
+
13
+ def parameters
14
+ @parameters ||= []
15
+ end
16
+
17
+ def install( rete )
18
+ rete.install_query( self )
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,84 @@
1
+ module Wongi::Engine
2
+ module DSL
3
+ class Rule
4
+
5
+ attr_reader :name
6
+
7
+ include Generated
8
+
9
+ class << self
10
+
11
+ def section s, *aliases
12
+ unless sections.include?(s)
13
+ sections << s
14
+ define_method s do |&d|
15
+ @current_section = s
16
+ instance_eval &d
17
+ end
18
+ aliases.each { |a| alias_method a, s }
19
+ end
20
+ end
21
+
22
+ def sections
23
+ @sections ||= []
24
+ end
25
+
26
+ end
27
+
28
+ section :forall, :for_all
29
+ section :make, :do!
30
+
31
+ def initialize name
32
+ @name = name
33
+ @current_section = nil
34
+ Rule.sections.each { |section| acceptors[section] ||= [] }
35
+ end
36
+
37
+ def acceptors
38
+ @acceptors ||= {}
39
+ end
40
+
41
+ def conditions
42
+ acceptors[:forall] ||= []
43
+ end
44
+
45
+ def conditions= c
46
+ acceptors[:forall] = c
47
+ end
48
+
49
+ def actions
50
+ acceptors[:make] ||= []
51
+ end
52
+
53
+ def actions= a
54
+ acceptors[:make] = a
55
+ end
56
+
57
+ def import_into rete
58
+ self.class.new( @name ).tap do |copy|
59
+ copy.conditions = conditions
60
+
61
+ copy.actions = actions.map do |action|
62
+ if action.respond_to? :import_into
63
+ action.import_into(rete)
64
+ else
65
+ action
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ def install( rete )
72
+ rete.install_rule( self )
73
+ end
74
+
75
+ protected
76
+
77
+ def accept stuff
78
+ acceptors[@current_section] << stuff
79
+ end
80
+
81
+
82
+ end
83
+ end
84
+ end