mini_kraken 0.1.03 → 0.1.08
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.
- checksums.yaml +4 -4
- data/.travis.yml +5 -1
- data/CHANGELOG.md +54 -3
- data/Gemfile +3 -1
- data/README.md +22 -1
- data/Rakefile +5 -3
- data/lib/mini_kraken.rb +3 -1
- data/lib/mini_kraken/core/any_value.rb +9 -7
- data/lib/mini_kraken/core/association.rb +20 -7
- data/lib/mini_kraken/core/association_walker.rb +5 -1
- data/lib/mini_kraken/core/atomic_term.rb +5 -3
- data/lib/mini_kraken/core/binary_relation.rb +8 -6
- data/lib/mini_kraken/core/composite_goal.rb +46 -0
- data/lib/mini_kraken/core/composite_term.rb +7 -20
- data/lib/mini_kraken/core/conj2.rb +77 -0
- data/lib/mini_kraken/core/cons_cell.rb +51 -41
- data/lib/mini_kraken/core/designation.rb +55 -0
- data/lib/mini_kraken/core/disj2.rb +71 -0
- data/lib/mini_kraken/core/duck_fiber.rb +4 -2
- data/lib/mini_kraken/core/environment.rb +25 -11
- data/lib/mini_kraken/core/equals.rb +128 -189
- data/lib/mini_kraken/core/fail.rb +20 -14
- data/lib/mini_kraken/core/freshness.rb +11 -8
- data/lib/mini_kraken/core/goal.rb +8 -4
- data/lib/mini_kraken/core/goal_arg.rb +10 -0
- data/lib/mini_kraken/core/goal_relation.rb +28 -0
- data/lib/mini_kraken/core/k_integer.rb +4 -3
- data/lib/mini_kraken/core/k_symbol.rb +4 -3
- data/lib/mini_kraken/core/nullary_relation.rb +3 -1
- data/lib/mini_kraken/core/outcome.rb +29 -25
- data/lib/mini_kraken/core/relation.rb +4 -18
- data/lib/mini_kraken/core/succeed.rb +20 -14
- data/lib/mini_kraken/core/term.rb +7 -2
- data/lib/mini_kraken/core/variable.rb +11 -25
- data/lib/mini_kraken/core/variable_ref.rb +12 -59
- data/lib/mini_kraken/core/vocabulary.rb +267 -48
- data/lib/mini_kraken/glue/fresh_env.rb +5 -3
- data/lib/mini_kraken/glue/run_star_expression.rb +18 -8
- data/lib/mini_kraken/version.rb +3 -1
- data/mini_kraken.gemspec +15 -13
- data/spec/core/association_spec.rb +4 -4
- data/spec/core/association_walker_spec.rb +25 -24
- data/spec/core/conj2_spec.rb +114 -0
- data/spec/core/cons_cell_spec.rb +12 -3
- data/spec/core/disj2_spec.rb +99 -0
- data/spec/core/duck_fiber_spec.rb +22 -12
- data/spec/core/environment_spec.rb +16 -28
- data/spec/core/equals_spec.rb +7 -7
- data/spec/core/fail_spec.rb +7 -7
- data/spec/core/goal_spec.rb +10 -10
- data/spec/core/k_symbol_spec.rb +5 -6
- data/spec/core/succeed_spec.rb +4 -4
- data/spec/core/variable_ref_spec.rb +0 -4
- data/spec/core/vocabulary_spec.rb +33 -27
- data/spec/glue/fresh_env_spec.rb +1 -1
- data/spec/glue/run_star_expression_spec.rb +213 -60
- data/spec/mini_kraken_spec.rb +4 -0
- data/spec/spec_helper.rb +3 -2
- data/spec/support/factory_methods.rb +20 -2
- metadata +12 -2
@@ -1,44 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'composite_term'
|
2
4
|
|
3
|
-
|
4
|
-
module
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
5
|
+
unless MiniKraken::Core.constants(false).include? :ConsCell
|
6
|
+
module MiniKraken
|
7
|
+
module Core
|
8
|
+
class ConsCell < CompositeTerm
|
9
|
+
attr_reader :car
|
10
|
+
attr_reader :cdr
|
11
|
+
|
12
|
+
def initialize(obj1, obj2 = nil)
|
13
|
+
@car = obj1
|
14
|
+
@cdr = obj2
|
15
|
+
end
|
16
|
+
|
17
|
+
def children
|
18
|
+
[car, cdr]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return true if it is an empty list, otherwise false.
|
22
|
+
# A list is empty, when both car and cdr fields are nil.
|
23
|
+
def null?
|
24
|
+
car.nil? && cdr.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==(other)
|
28
|
+
return false unless other.respond_to?(:car)
|
29
|
+
|
30
|
+
(car == other.car) && (cdr == other.cdr)
|
31
|
+
end
|
32
|
+
|
33
|
+
def eql?(other)
|
34
|
+
(self.class == other.class) && car.eql?(other.car) && cdr.eql?(other.cdr)
|
35
|
+
end
|
36
|
+
|
37
|
+
def quote(anEnv)
|
38
|
+
return self if null?
|
39
|
+
|
40
|
+
new_car = car.nil? ? nil : car.quote(anEnv)
|
41
|
+
new_cdr = cdr.nil? ? nil : cdr.quote(anEnv)
|
42
|
+
ConsCell.new(new_car, new_cdr)
|
43
|
+
end
|
44
|
+
|
45
|
+
def append(another)
|
46
|
+
@cdr = another
|
47
|
+
end
|
48
|
+
end # class
|
49
|
+
|
50
|
+
# Constant representing the null (empty) list.
|
51
|
+
NullList = ConsCell.new(nil, nil).freeze
|
52
|
+
end # module
|
43
53
|
end # module
|
44
|
-
end #
|
54
|
+
end # defined
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniKraken
|
4
|
+
module Core
|
5
|
+
# Wordnet definition: Identifying word or words by which someone or
|
6
|
+
# something is called and classified or distinguished from others.
|
7
|
+
# Mix-in module that contains factored code for managing named entries
|
8
|
+
# in a vocabulary such as variables and variable references.
|
9
|
+
module Designation
|
10
|
+
# @return [String] User-defined name of the variable
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
def init_designation(aName)
|
14
|
+
@name = valid_name(aName)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param voc [Vocabulary]
|
18
|
+
# @return [Freshness]
|
19
|
+
def freshness(voc)
|
20
|
+
voc.freshness_ref(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param voc [Vocabulary]
|
24
|
+
# @return [Boolean]
|
25
|
+
def fresh?(voc)
|
26
|
+
frsh = freshness(voc)
|
27
|
+
frsh.degree == :fresh || frsh.degree == :bound
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param voc [Vocabulary]
|
31
|
+
# @return [Boolean]
|
32
|
+
def bound?(voc)
|
33
|
+
frsh = freshness(voc)
|
34
|
+
frsh.degree == :bound
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param voc [Vocabulary]
|
38
|
+
# @return [Boolean]
|
39
|
+
def ground?(voc)
|
40
|
+
frsh = freshness(voc)
|
41
|
+
frsh.degree == :bound
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def valid_name(aName)
|
47
|
+
if aName.empty?
|
48
|
+
raise StandardError, 'Variable name may not be empty.'
|
49
|
+
end
|
50
|
+
|
51
|
+
aName
|
52
|
+
end
|
53
|
+
end # class
|
54
|
+
end # module
|
55
|
+
end # module
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
require_relative 'duck_fiber'
|
5
|
+
require_relative 'goal'
|
6
|
+
require_relative 'goal_relation'
|
7
|
+
require_relative 'outcome'
|
8
|
+
|
9
|
+
unless MiniKraken::Core.constants(false).include? :Disj2
|
10
|
+
module MiniKraken
|
11
|
+
module Core
|
12
|
+
# The disjunction is a relation that accepts only goal(s) as its two
|
13
|
+
# arguments. It succeeds if at least one of its goal arguments succeeds.
|
14
|
+
class Disj2 < GoalRelation
|
15
|
+
include Singleton
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
super('disj2', nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param actuals [Array<Term>] A two-elements array
|
22
|
+
# @param anEnv [Vocabulary] A vocabulary object
|
23
|
+
# @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
|
24
|
+
def solver_for(actuals, anEnv)
|
25
|
+
g1, g2 = *validated_args(actuals)
|
26
|
+
Fiber.new { disjunction(g1, g2, anEnv) }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Yields [Outcome, NilClass] result of the disjunction
|
30
|
+
# @param g1 [Goal] First goal argument
|
31
|
+
# @param g2 [Goal] Second goal argument
|
32
|
+
# @param voc [Vocabulary] A vocabulary object
|
33
|
+
def disjunction(g1, g2, voc)
|
34
|
+
# require 'debug'
|
35
|
+
outcome1 = nil
|
36
|
+
outcome2 = nil
|
37
|
+
if g1.relation.kind_of?(Fail) && g2.relation.kind_of?(Fail)
|
38
|
+
Fiber.yield Outcome.new(:"#u", voc)
|
39
|
+
else
|
40
|
+
f1 = g1.attain(voc)
|
41
|
+
loop do
|
42
|
+
outcome1 = f1.resume
|
43
|
+
break unless outcome1
|
44
|
+
|
45
|
+
outcome1.parent = voc unless outcome1.parent
|
46
|
+
if outcome1.successful?
|
47
|
+
Fiber.yield outcome1
|
48
|
+
outcome1.clear
|
49
|
+
end
|
50
|
+
end
|
51
|
+
f2 = g2.attain(voc)
|
52
|
+
loop do
|
53
|
+
outcome2 = f2.resume
|
54
|
+
break unless outcome2
|
55
|
+
|
56
|
+
outcome2.parent = voc unless outcome2.parent
|
57
|
+
if outcome2.successful?
|
58
|
+
Fiber.yield outcome2
|
59
|
+
outcome2.clear
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
Fiber.yield nil
|
65
|
+
end
|
66
|
+
end # class
|
67
|
+
|
68
|
+
Disj2.instance.freeze
|
69
|
+
end # module
|
70
|
+
end # module
|
71
|
+
end # unless
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'outcome'
|
2
4
|
|
3
5
|
module MiniKraken
|
@@ -28,13 +30,13 @@ module MiniKraken
|
|
28
30
|
return nil
|
29
31
|
end
|
30
32
|
end
|
31
|
-
|
33
|
+
|
32
34
|
def valid_outcome(outcomeKind)
|
33
35
|
case outcomeKind
|
34
36
|
when :failure
|
35
37
|
Failure
|
36
38
|
when :success
|
37
|
-
|
39
|
+
Outcome.new(:"#s")
|
38
40
|
else
|
39
41
|
raise StandardError, "Unknonw outcome kind #{outcomeKind}"
|
40
42
|
end
|
@@ -1,17 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
1
4
|
require_relative 'vocabulary'
|
2
5
|
|
3
6
|
module MiniKraken
|
4
7
|
module Core
|
5
8
|
class Environment
|
6
|
-
include Vocabulary
|
9
|
+
include Vocabulary # Use mix-in module
|
7
10
|
|
11
|
+
# Mapping from user-defined name to Variable instance
|
8
12
|
# @return [Hash] Pairs of the kind {String => Variable}
|
9
13
|
attr_reader :vars
|
10
14
|
|
15
|
+
# Mapping from internal name to user-defined name(s)
|
16
|
+
# @return [Hash] Pairs of the kind {String => Set<String>}
|
17
|
+
attr_reader :ivars
|
18
|
+
|
11
19
|
# @param aParent [Environment, NilClass] Parent environment to this one.
|
12
20
|
def initialize(aParent = nil)
|
13
21
|
init_vocabulary(aParent)
|
14
22
|
@vars = {}
|
23
|
+
@ivars = {}
|
15
24
|
end
|
16
25
|
|
17
26
|
# @param aVariable [Variable]
|
@@ -22,6 +31,13 @@ module MiniKraken
|
|
22
31
|
raise StandardError, err_msg
|
23
32
|
end
|
24
33
|
vars[name] = aVariable
|
34
|
+
i_name = aVariable.i_name
|
35
|
+
if ivars.include?(i_name)
|
36
|
+
set = ivars[i_name]
|
37
|
+
set.add(name)
|
38
|
+
else
|
39
|
+
ivars[i_name] = Set.new([i_name])
|
40
|
+
end
|
25
41
|
end
|
26
42
|
|
27
43
|
# Handler for the event: an outcome has been produced.
|
@@ -31,23 +47,21 @@ module MiniKraken
|
|
31
47
|
# Rollout associations from hierarchy
|
32
48
|
walker = descendent.ancestor_walker
|
33
49
|
begin
|
34
|
-
env = walker.
|
50
|
+
env = walker.next
|
35
51
|
break if env.nil?
|
36
|
-
|
52
|
+
|
53
|
+
env.do_propagate(descendent) if env.kind_of?(Environment)
|
37
54
|
end until env.equal?(self)
|
38
55
|
end
|
39
56
|
|
40
|
-
#
|
57
|
+
# Roll up associations from descendent outcome object
|
58
|
+
# @param descendent [Outcome]
|
41
59
|
def do_propagate(descendent)
|
42
|
-
return unless descendent.successful?
|
60
|
+
return unless descendent.successful?
|
43
61
|
|
44
62
|
vars.each_key do |var_name|
|
45
63
|
assocs = descendent[var_name]
|
46
|
-
|
47
|
-
own = self[var_name]
|
48
|
-
add_assoc(assoc) unless assoc.equal?(own)
|
49
|
-
end
|
50
|
-
descendent.associations.delete(var_name) unless assocs.empty?
|
64
|
+
move_assocs(var_name, descendent)
|
51
65
|
end
|
52
66
|
end
|
53
67
|
|
@@ -56,4 +70,4 @@ module MiniKraken
|
|
56
70
|
end
|
57
71
|
end # class
|
58
72
|
end # module
|
59
|
-
end # module
|
73
|
+
end # module
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'singleton'
|
2
4
|
require_relative 'binary_relation'
|
3
5
|
# require_relative 'any_value'
|
@@ -5,212 +7,149 @@ require_relative 'duck_fiber'
|
|
5
7
|
require_relative 'variable'
|
6
8
|
require_relative 'variable_ref'
|
7
9
|
|
8
|
-
|
9
|
-
module
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
unless MiniKraken::Core.constants(false).include? :Equals
|
11
|
+
module MiniKraken
|
12
|
+
module Core
|
13
|
+
# equals tries to unify two terms
|
14
|
+
class Equals < BinaryRelation
|
15
|
+
include Singleton
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
+
def initialize
|
18
|
+
super('equals', '==')
|
19
|
+
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
=
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
when [false, false]
|
33
|
-
if arg1.eql?(arg2)
|
34
|
-
result = Outcome.new(:"#s", [])
|
35
|
-
else
|
36
|
-
result = Failure
|
37
|
-
end
|
38
|
-
when [false, true]
|
39
|
-
if arg2.fresh?
|
40
|
-
arg2.bind_to(arg1)
|
41
|
-
result = Outcome.new(:"#s", [arg1])
|
42
|
-
else
|
43
|
-
if arg2.value.eql?(arg1)
|
44
|
-
result = Outcome.new(:"#s", [arg1])
|
45
|
-
else
|
46
|
-
result = Failure
|
47
|
-
end
|
48
|
-
end
|
49
|
-
when [true, false]
|
50
|
-
if arg1.fresh?
|
51
|
-
arg1.bind_to(arg2)
|
52
|
-
result = Outcome.new(:"#s", [arg2])
|
53
|
-
else
|
54
|
-
if arg1.value.eql?(arg2)
|
55
|
-
result = Outcome.new(:"#s", [arg2])
|
56
|
-
else
|
57
|
-
result = Failure
|
58
|
-
end
|
59
|
-
end
|
60
|
-
when [true, true]
|
61
|
-
case [arg1.fresh?, arg2.fresh?]
|
62
|
-
when [false, false]
|
63
|
-
if arg1.value == arg2.value
|
64
|
-
result = Outcome.new(:"#s", [arg1.value])
|
21
|
+
# @param actuals [Array<Term>] A two-elements array
|
22
|
+
# @param anEnv [Vocabulary] A vocabulary object
|
23
|
+
# @return [Fiber<Outcome>] A Fiber(-like) instance that yields Outcomes
|
24
|
+
def solver_for(actuals, anEnv)
|
25
|
+
arg1, arg2 = *actuals
|
26
|
+
DuckFiber.new(:custom) { unification(arg1, arg2, anEnv) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def unification(arg1, arg2, anEnv)
|
30
|
+
arg1_nil = arg1.nil?
|
31
|
+
arg2_nil = arg2.nil?
|
32
|
+
if arg1_nil || arg2_nil
|
33
|
+
if arg1_nil && arg2_nil
|
34
|
+
result = Outcome.new(:"#s", anEnv)
|
65
35
|
else
|
66
36
|
result = Failure
|
67
37
|
end
|
68
|
-
|
69
|
-
arg2.bind_to(arg1)
|
70
|
-
result = Outcome.new(:"#s", arg1.value)
|
71
|
-
when [true, false]
|
72
|
-
arg1.bind_to(arg2)
|
73
|
-
result = Outcome.new(:"#s", arg2.value)
|
74
|
-
when [true, true]
|
75
|
-
if arg1.variable.name == arg2.variable.name
|
76
|
-
result = Outcome.new(:"#s", [])
|
77
|
-
else
|
78
|
-
# TODO: add constraints
|
79
|
-
result = Outcome.new(:"#s", [])
|
80
|
-
end
|
38
|
+
return result
|
81
39
|
end
|
82
|
-
|
40
|
+
new_arg1, new_arg2 = commute_cond(arg1, arg2, anEnv)
|
41
|
+
result = do_unification(new_arg1, new_arg2, anEnv)
|
42
|
+
# anEnv.merge(result) if result.successful? && !result.association.empty?
|
83
43
|
|
84
|
-
|
85
|
-
end
|
86
|
-
=end
|
87
|
-
|
88
|
-
def solver_for(actuals, anEnv)
|
89
|
-
arg1, arg2 = *actuals
|
90
|
-
DuckFiber.new(:custom) { unification(arg1, arg2, anEnv) }
|
91
|
-
end
|
92
|
-
|
93
|
-
def unification(arg1, arg2, anEnv)
|
94
|
-
arg1_nil = arg1.nil?
|
95
|
-
arg2_nil = arg2.nil?
|
96
|
-
if arg1_nil || arg2_nil
|
97
|
-
if arg1_nil && arg2_nil
|
98
|
-
result = Outcome.new(:"#s", anEnv)
|
99
|
-
else
|
100
|
-
result = Failure
|
44
|
+
result
|
101
45
|
end
|
102
|
-
return result
|
103
|
-
end
|
104
|
-
new_arg1, new_arg2 = commute_cond(arg1, arg2, anEnv)
|
105
|
-
result = do_unification(new_arg1, new_arg2, anEnv)
|
106
|
-
# anEnv.merge(result) if result.successful? && !result.association.empty?
|
107
46
|
|
108
|
-
|
109
|
-
end
|
47
|
+
private
|
110
48
|
|
111
|
-
|
49
|
+
# table: Unification
|
50
|
+
# | arg1 | arg2 | Criterion || Unification |
|
51
|
+
# | isa? Atomic | isa? Atomic | arg1.eq? arg2 is true || { "s", [] } |
|
52
|
+
# | isa? Atomic | isa? Atomic | arg1.eq? arg2 is false || { "u", [] } |
|
53
|
+
# | isa? CompositeTerm | isa? Atomic | dont_care || { "u", [] } |
|
54
|
+
# | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.car, arg2.car) => "s" || { "s", [bindings*] } |
|
55
|
+
# | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.cdr, arg2.cdr) => "u" || { "u", [] ) | |
|
56
|
+
# | isa? VariableRef | isa? Atomic | arg1.fresh? is true || { "s", [arg2] } |
|
57
|
+
# | isa? VariableRef | isa? Atomic | arg1.fresh? is false || |
|
58
|
+
# | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
|
59
|
+
# | | unification(arg1.value, arg2) => "u" || { "u", [] } |
|
60
|
+
# | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is true || { "s", [arg2] } | # What if arg1 occurs in arg2?
|
61
|
+
# | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is false || |
|
62
|
+
# | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
|
63
|
+
# | | unification(arg1.value, arg2) => "u" || { "u", [] } |
|
64
|
+
# | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, true] || { "s", [arg1 <=> arg2] } |
|
65
|
+
# | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, false] || |
|
66
|
+
# | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
|
67
|
+
# | | unification(arg1, arg2.value) => "u" || { "u", [] } |
|
68
|
+
# | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [false, false]|| |
|
69
|
+
# | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
|
70
|
+
# | | unification(arg1, arg2.value) => "u" || { "u", [] }
|
71
|
+
def do_unification(arg1, arg2, anEnv)
|
72
|
+
# require 'debug'
|
73
|
+
return Outcome.new(:"#s", anEnv) if arg1.equal?(arg2)
|
112
74
|
|
113
|
-
|
114
|
-
# | arg1 | arg2 | Criterion || Unification |
|
115
|
-
# | isa? Atomic | isa? Atomic | arg1.eq? arg2 is true || { "s", [] } |
|
116
|
-
# | isa? Atomic | isa? Atomic | arg1.eq? arg2 is false || { "u", [] } |
|
117
|
-
# | isa? CompositeTerm | isa? Atomic | dont_care || { "u", [] } |
|
118
|
-
# | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.car, arg2.car) => "s" || { "s", [bindings*] } |
|
119
|
-
# | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.cdr, arg2.cdr) => "u" || { "u", [] ) | |
|
120
|
-
# | isa? VariableRef | isa? Atomic | arg1.fresh? is true || { "s", [arg2] } |
|
121
|
-
# | isa? VariableRef | isa? Atomic | arg1.fresh? is false || |
|
122
|
-
# | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
|
123
|
-
# | | unification(arg1.value, arg2) => "u" || { "u", [] } |
|
124
|
-
# | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is true || { "s", [arg2] } | # What if arg1 occurs in arg2?
|
125
|
-
# | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is false || |
|
126
|
-
# | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
|
127
|
-
# | | unification(arg1.value, arg2) => "u" || { "u", [] } |
|
128
|
-
# | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, true] || { "s", [arg1 <=> arg2] } |
|
129
|
-
# | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, false] || |
|
130
|
-
# | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
|
131
|
-
# | | unification(arg1, arg2.value) => "u" || { "u", [] } |
|
132
|
-
# | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [false, false]|| |
|
133
|
-
# | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
|
134
|
-
# | | unification(arg1, arg2.value) => "u" || { "u", [] }
|
135
|
-
def do_unification(arg1, arg2, anEnv)
|
136
|
-
# require 'debug'
|
137
|
-
return Outcome.new(:"#s", anEnv) if arg1.equal?(arg2)
|
138
|
-
result = Outcome.new(:"#u", anEnv) # default case
|
75
|
+
result = Outcome.new(:"#u", anEnv) # default case
|
139
76
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
77
|
+
if arg1.kind_of?(AtomicTerm)
|
78
|
+
result = BasicSuccess if arg1.eql?(arg2)
|
79
|
+
elsif arg1.kind_of?(CompositeTerm)
|
80
|
+
if arg2.kind_of?(CompositeTerm) # AtomicTerm is default case => fail
|
81
|
+
result = unify_composite_terms(arg1, arg2, anEnv)
|
82
|
+
end
|
83
|
+
elsif arg1.kind_of?(VariableRef)
|
84
|
+
arg1_freshness = arg1.freshness(anEnv)
|
85
|
+
if arg2.kind_of?(AtomicTerm)
|
86
|
+
if arg1_freshness.degree == :fresh
|
87
|
+
result = Outcome.new(:"#s", anEnv)
|
88
|
+
arg1.associate(arg2, result)
|
89
|
+
else
|
90
|
+
result = Outcome.new(:"#s", anEnv) if arg1.value(anEnv).eql?(arg2)
|
91
|
+
end
|
92
|
+
elsif arg2.kind_of?(CompositeTerm)
|
93
|
+
if arg1_freshness.degree == :fresh
|
94
|
+
result = Outcome.new(:"#s", anEnv)
|
95
|
+
arg1.associate(arg2, result)
|
96
|
+
else
|
97
|
+
# Ground case...
|
98
|
+
result = unify_composite_terms(arg1_freshness.associated, arg2, anEnv)
|
99
|
+
end
|
100
|
+
elsif arg2.kind_of?(VariableRef)
|
101
|
+
freshness = [arg1.fresh?(anEnv), arg2.fresh?(anEnv)]
|
102
|
+
case freshness
|
103
|
+
when [false, false] # TODO: confirm this...
|
104
|
+
result = unification(arg1.value(anEnv), arg2.value(anEnv), anEnv)
|
105
|
+
when [true, true]
|
106
|
+
result = Outcome.new(:"#s", anEnv)
|
107
|
+
if arg1.var_name != arg2.var_name
|
108
|
+
arg1.associate(arg2, result)
|
109
|
+
arg2.associate(arg1, result)
|
110
|
+
end
|
111
|
+
else
|
112
|
+
raise StandardError, "Unsupported freshness combination #{freshness}"
|
113
|
+
end
|
114
|
+
else
|
115
|
+
arg_kinds = [arg1.class, arg2.class]
|
116
|
+
raise StandardError, "Unsupported combination #{arg_kinds}"
|
173
117
|
end
|
174
|
-
else
|
175
|
-
raise StandardError, "Unsupported freshness combination #{freshness}"
|
176
118
|
end
|
177
|
-
else
|
178
|
-
arg_kinds = [arg1.class, arg2.class]
|
179
|
-
raise StandardError, "Unsupported combination #{arg_kinds}"
|
180
|
-
end
|
181
|
-
end
|
182
119
|
|
183
|
-
|
184
|
-
|
120
|
+
result
|
121
|
+
end
|
185
122
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
123
|
+
# @return [Freshness]
|
124
|
+
def unify_composite_terms(arg1, arg2, anEnv)
|
125
|
+
# require 'debug'
|
126
|
+
result = Outcome.new(:"#u", anEnv)
|
127
|
+
children1 = arg1.children
|
128
|
+
children2 = arg2.children
|
192
129
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
130
|
+
if children1.size == children2.size
|
131
|
+
i = 0
|
132
|
+
subresults = children1.map do |child1|
|
133
|
+
child2 = children2[i]
|
134
|
+
i += 1
|
135
|
+
unification(child1, child2, anEnv)
|
136
|
+
end
|
137
|
+
total_success = subresults.all?(&:successful?)
|
138
|
+
if total_success
|
139
|
+
memo = Outcome.new(:"#s", anEnv)
|
140
|
+
associations = subresults.reduce(memo) do |sub_total, outcome|
|
141
|
+
sub_total.merge(outcome)
|
142
|
+
sub_total
|
143
|
+
end
|
144
|
+
result = memo
|
145
|
+
end
|
206
146
|
end
|
207
|
-
result = memo
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
result
|
212
|
-
end
|
213
147
|
|
214
|
-
|
148
|
+
result
|
149
|
+
end
|
150
|
+
end # class
|
151
|
+
|
152
|
+
Equals.instance.freeze
|
153
|
+
end # module
|
215
154
|
end # module
|
216
|
-
end #
|
155
|
+
end # unless
|