mort666-wongi-engine 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
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