mini_kraken 0.2.03 → 0.3.03
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/.rubocop.yml +378 -333
- data/CHANGELOG.md +48 -0
- data/README.md +29 -21
- data/lib/mini_kraken/atomic/all_atomic.rb +5 -0
- data/lib/mini_kraken/atomic/atomic_term.rb +96 -0
- data/lib/mini_kraken/atomic/k_boolean.rb +42 -0
- data/lib/mini_kraken/{core → atomic}/k_integer.rb +2 -5
- data/lib/mini_kraken/atomic/k_string.rb +17 -0
- data/lib/mini_kraken/{core → atomic}/k_symbol.rb +4 -8
- data/lib/mini_kraken/composite/all_composite.rb +4 -0
- data/lib/mini_kraken/composite/composite_term.rb +27 -0
- data/lib/mini_kraken/composite/cons_cell.rb +301 -0
- data/lib/mini_kraken/composite/cons_cell_visitor.rb +50 -0
- data/lib/mini_kraken/composite/list.rb +32 -0
- data/lib/mini_kraken/core/all_core.rb +8 -0
- data/lib/mini_kraken/core/any_value.rb +31 -7
- data/lib/mini_kraken/core/arity.rb +69 -0
- data/lib/mini_kraken/core/association.rb +29 -4
- data/lib/mini_kraken/core/association_copy.rb +50 -0
- data/lib/mini_kraken/core/base_term.rb +13 -0
- data/lib/mini_kraken/core/blackboard.rb +315 -0
- data/lib/mini_kraken/core/bookmark.rb +46 -0
- data/lib/mini_kraken/core/context.rb +492 -0
- data/lib/mini_kraken/core/duck_fiber.rb +21 -19
- data/lib/mini_kraken/core/entry.rb +40 -0
- data/lib/mini_kraken/core/fail.rb +20 -18
- data/lib/mini_kraken/core/fusion.rb +29 -0
- data/lib/mini_kraken/core/goal.rb +20 -29
- data/lib/mini_kraken/core/log_var.rb +22 -0
- data/lib/mini_kraken/core/log_var_ref.rb +108 -0
- data/lib/mini_kraken/core/nullary_relation.rb +2 -9
- data/lib/mini_kraken/core/parametrized_term.rb +68 -0
- data/lib/mini_kraken/core/relation.rb +14 -28
- data/lib/mini_kraken/core/scope.rb +67 -0
- data/lib/mini_kraken/core/solver_adapter.rb +58 -0
- data/lib/mini_kraken/core/specification.rb +48 -0
- data/lib/mini_kraken/core/succeed.rb +21 -17
- data/lib/mini_kraken/core/symbol_table.rb +137 -0
- data/lib/mini_kraken/core/term.rb +15 -4
- data/lib/mini_kraken/glue/dsl.rb +44 -88
- data/lib/mini_kraken/glue/run_star_expression.rb +28 -30
- data/lib/mini_kraken/rela/all_rela.rb +8 -0
- data/lib/mini_kraken/rela/binary_relation.rb +30 -0
- data/lib/mini_kraken/rela/conde.rb +143 -0
- data/lib/mini_kraken/rela/conj2.rb +65 -0
- data/lib/mini_kraken/rela/def_relation.rb +93 -0
- data/lib/mini_kraken/rela/disj2.rb +70 -0
- data/lib/mini_kraken/rela/fresh.rb +98 -0
- data/lib/mini_kraken/{core → rela}/goal_relation.rb +7 -9
- data/lib/mini_kraken/rela/unify.rb +265 -0
- data/lib/mini_kraken/version.rb +1 -1
- data/mini_kraken.gemspec +2 -2
- data/spec/.rubocop.yml +1 -1
- data/spec/atomic/atomic_term_spec.rb +98 -0
- data/spec/{core → atomic}/k_boolean_spec.rb +19 -34
- data/spec/{core → atomic}/k_symbol_spec.rb +3 -16
- data/spec/composite/cons_cell_spec.rb +225 -0
- data/spec/{core → composite}/cons_cell_visitor_spec.rb +36 -20
- data/spec/composite/list_spec.rb +50 -0
- data/spec/core/any_value_spec.rb +52 -0
- data/spec/core/arity_spec.rb +92 -0
- data/spec/core/association_copy_spec.rb +69 -0
- data/spec/core/association_spec.rb +31 -4
- data/spec/core/blackboard_spec.rb +287 -0
- data/spec/core/bookmark_spec.rb +40 -0
- data/spec/core/context_spec.rb +245 -0
- data/spec/core/core_spec.rb +40 -0
- data/spec/core/duck_fiber_spec.rb +16 -46
- data/spec/core/fail_spec.rb +5 -6
- data/spec/core/goal_spec.rb +22 -12
- data/spec/core/log_var_ref_spec.rb +105 -0
- data/spec/core/log_var_spec.rb +64 -0
- data/spec/core/nullary_relation_spec.rb +33 -0
- data/spec/core/parametrized_tem_spec.rb +39 -0
- data/spec/core/relation_spec.rb +33 -0
- data/spec/core/scope_spec.rb +73 -0
- data/spec/core/solver_adapter_spec.rb +70 -0
- data/spec/core/specification_spec.rb +43 -0
- data/spec/core/succeed_spec.rb +5 -5
- data/spec/core/symbol_table_spec.rb +142 -0
- data/spec/glue/dsl_chap1_spec.rb +88 -144
- data/spec/glue/dsl_chap2_spec.rb +454 -19
- data/spec/glue/run_star_expression_spec.rb +81 -906
- data/spec/rela/conde_spec.rb +153 -0
- data/spec/rela/conj2_spec.rb +123 -0
- data/spec/rela/def_relation_spec.rb +119 -0
- data/spec/rela/disj2_spec.rb +117 -0
- data/spec/rela/fresh_spec.rb +147 -0
- data/spec/rela/unify_spec.rb +369 -0
- data/spec/support/factory_atomic.rb +29 -0
- data/spec/support/factory_composite.rb +21 -0
- data/spec/support/factory_methods.rb +11 -26
- metadata +98 -70
- data/lib/mini_kraken/core/association_walker.rb +0 -183
- data/lib/mini_kraken/core/atomic_term.rb +0 -67
- data/lib/mini_kraken/core/base_arg.rb +0 -10
- data/lib/mini_kraken/core/binary_relation.rb +0 -63
- data/lib/mini_kraken/core/composite_goal.rb +0 -46
- data/lib/mini_kraken/core/composite_term.rb +0 -41
- data/lib/mini_kraken/core/conde.rb +0 -143
- data/lib/mini_kraken/core/conj2.rb +0 -79
- data/lib/mini_kraken/core/cons_cell.rb +0 -82
- data/lib/mini_kraken/core/cons_cell_visitor.rb +0 -102
- data/lib/mini_kraken/core/def_relation.rb +0 -53
- data/lib/mini_kraken/core/designation.rb +0 -55
- data/lib/mini_kraken/core/disj2.rb +0 -72
- data/lib/mini_kraken/core/environment.rb +0 -73
- data/lib/mini_kraken/core/equals.rb +0 -193
- data/lib/mini_kraken/core/formal_arg.rb +0 -22
- data/lib/mini_kraken/core/formal_ref.rb +0 -25
- data/lib/mini_kraken/core/freshness.rb +0 -45
- data/lib/mini_kraken/core/goal_arg.rb +0 -12
- data/lib/mini_kraken/core/goal_template.rb +0 -102
- data/lib/mini_kraken/core/k_boolean.rb +0 -35
- data/lib/mini_kraken/core/outcome.rb +0 -63
- data/lib/mini_kraken/core/variable.rb +0 -41
- data/lib/mini_kraken/core/variable_ref.rb +0 -84
- data/lib/mini_kraken/core/vocabulary.rb +0 -446
- data/lib/mini_kraken/glue/fresh_env.rb +0 -103
- data/lib/mini_kraken/glue/fresh_env_factory.rb +0 -83
- data/spec/core/association_walker_spec.rb +0 -192
- data/spec/core/conde_spec.rb +0 -147
- data/spec/core/conj2_spec.rb +0 -114
- data/spec/core/cons_cell_spec.rb +0 -107
- data/spec/core/def_relation_spec.rb +0 -97
- data/spec/core/disj2_spec.rb +0 -99
- data/spec/core/environment_spec.rb +0 -142
- data/spec/core/equals_spec.rb +0 -317
- data/spec/core/goal_template_spec.rb +0 -74
- data/spec/core/outcome_spec.rb +0 -56
- data/spec/core/variable_ref_spec.rb +0 -30
- data/spec/core/variable_spec.rb +0 -35
- data/spec/core/vocabulary_spec.rb +0 -219
- data/spec/glue/fresh_env_factory_spec.rb +0 -97
- data/spec/glue/fresh_env_spec.rb +0 -62
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
require_relative '../core/solver_adapter'
|
5
|
+
require_relative '../atomic/k_string'
|
6
|
+
require_relative 'conj2'
|
7
|
+
require_relative 'goal_relation'
|
8
|
+
|
9
|
+
module MiniKraken
|
10
|
+
module Rela
|
11
|
+
# A specialized relation that accepts a variable names(s) and a subgoal
|
12
|
+
# as its arguments.
|
13
|
+
class Fresh < GoalRelation
|
14
|
+
include Singleton
|
15
|
+
# Default initialization
|
16
|
+
def initialize
|
17
|
+
super('fresh', 2)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.build_goal(names, subgoals)
|
21
|
+
var_names = nil
|
22
|
+
|
23
|
+
case names
|
24
|
+
when String
|
25
|
+
var_names = Atomic::KString.new(names)
|
26
|
+
|
27
|
+
when Array
|
28
|
+
var_names = names.map do |nm|
|
29
|
+
nm.is_a?(String) ? Atomic::KString.new(nm) : nm
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
nested_goal = compose_goals(subgoals)
|
34
|
+
Core::Goal.new(instance, [var_names, nested_goal])
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.compose_goals(subgoals)
|
38
|
+
nested_goal = nil
|
39
|
+
|
40
|
+
case subgoals
|
41
|
+
when Core::Goal
|
42
|
+
nested_goal = subgoals
|
43
|
+
|
44
|
+
when Array
|
45
|
+
goal_array = subgoals
|
46
|
+
loop do
|
47
|
+
conjunctions = []
|
48
|
+
goal_array.each_slice(2) do |uno_duo|
|
49
|
+
if uno_duo.size == 2
|
50
|
+
conjunctions << Core::Goal.new(Conj2.instance, uno_duo)
|
51
|
+
else
|
52
|
+
conjunctions << uno_duo[0]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
if conjunctions.size == 1
|
56
|
+
nested_goal = conjunctions[0]
|
57
|
+
break
|
58
|
+
end
|
59
|
+
goal_array = conjunctions
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
nested_goal
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param actuals [Array<Array<KString>, Core::Term>] A two-elements array
|
67
|
+
# First element is an array of variable names to create.
|
68
|
+
# Second is a sub-goal object
|
69
|
+
# @param ctx [Core::Context] A context object
|
70
|
+
# @return [Fiber<Core::Context>] A Fiber that yields Context objects
|
71
|
+
def solver_for(actuals, ctx)
|
72
|
+
k_names = actuals.shift
|
73
|
+
# require 'debug'
|
74
|
+
subgoal = validated_args(actuals).first
|
75
|
+
|
76
|
+
ctx.enter_scope(Core::Scope.new)
|
77
|
+
if k_names.kind_of?(Atomic::KString)
|
78
|
+
ctx.add_vars(k_names.value)
|
79
|
+
else
|
80
|
+
# ... Array of KString
|
81
|
+
names = k_names.map(&:value)
|
82
|
+
ctx.add_vars(names)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Wrap the subgoal's solver by an adapter
|
86
|
+
orig_solver = subgoal.achieve(ctx)
|
87
|
+
Core::SolverAdapter.new(orig_solver) do |adp, context|
|
88
|
+
# puts "Adaptee #{adp.adaptee}"
|
89
|
+
result = adp.adaptee.resume(context)
|
90
|
+
context.leave_scope if result.nil?
|
91
|
+
result
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end # class
|
95
|
+
|
96
|
+
Fresh.instance.freeze
|
97
|
+
end # module
|
98
|
+
end # module
|
@@ -1,23 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative '../core/goal'
|
4
|
+
require_relative '../core/relation'
|
4
5
|
|
5
6
|
module MiniKraken
|
6
|
-
module
|
7
|
+
module Rela
|
7
8
|
# A specialization of a relation that accepts only goal(s)
|
8
9
|
# as its arguments.
|
9
|
-
class GoalRelation < Relation
|
10
|
-
def arity
|
11
|
-
2
|
12
|
-
end
|
13
|
-
|
10
|
+
class GoalRelation < Core::Relation
|
14
11
|
protected
|
15
12
|
|
13
|
+
# Validate that actuals
|
16
14
|
def validated_args(actuals)
|
17
15
|
actuals.each do |arg|
|
18
|
-
unless arg.kind_of?(Goal)
|
16
|
+
unless arg.kind_of?(Core::Goal)
|
19
17
|
prefix = "#{name} expects goal as argument, found a "
|
20
|
-
raise StandardError, prefix + "'#{arg.class}'"
|
18
|
+
raise StandardError, prefix + "'#{arg.class}': #{arg}"
|
21
19
|
end
|
22
20
|
end
|
23
21
|
|
@@ -0,0 +1,265 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
require_relative 'binary_relation'
|
5
|
+
|
6
|
+
require_relative '../core/duck_fiber'
|
7
|
+
require_relative '../core/log_var_ref'
|
8
|
+
require_relative '../atomic/atomic_term'
|
9
|
+
require_relative '../composite/all_composite'
|
10
|
+
|
11
|
+
|
12
|
+
module MiniKraken
|
13
|
+
module Rela
|
14
|
+
# Corresponds to the '==' relation in canonical miniKanren implementation
|
15
|
+
# in Scheme. Implements the core of the unification algorithm.
|
16
|
+
class Unify < BinaryRelation
|
17
|
+
include Singleton
|
18
|
+
|
19
|
+
symmetric # Unify relation is symmetric ("First Law of ==")
|
20
|
+
|
21
|
+
# Constructor. Initialize the name of the relation
|
22
|
+
def initialize
|
23
|
+
super('unify')
|
24
|
+
freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param actuals [Array<Term>] A two-elements array
|
28
|
+
# @param ctx [Context] A context object
|
29
|
+
# @return [Fiber<Context>] A DuckFiber instance that yields one Context.
|
30
|
+
def solver_for(actuals, ctx)
|
31
|
+
arg1, arg2 = *actuals
|
32
|
+
# context = unification(arg1, arg2, ctx)
|
33
|
+
# Core::DuckFiber.new(-> { context })
|
34
|
+
Core::DuckFiber.new(-> { unification(arg1, arg2, ctx) })
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param arg1 [Term]
|
38
|
+
# @param arg2 [Term]
|
39
|
+
# @param ctx [Context] A context object
|
40
|
+
# @return [Context] The updated context
|
41
|
+
def unification(arg1, arg2, ctx)
|
42
|
+
return ctx.succeeded! if arg1.equal?(arg2)
|
43
|
+
return ctx.failed! if arg1.nil? || arg2.nil?
|
44
|
+
|
45
|
+
new_arg1, new_arg2 = commute_cond(arg1, arg2, ctx)
|
46
|
+
do_unification(new_arg1, new_arg2, ctx)
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def do_unification(arg1, arg2, ctx)
|
52
|
+
table = [
|
53
|
+
# cond1 cond2 selector
|
54
|
+
[kind_of(Atomic::AtomicTerm), kind_of(Atomic::AtomicTerm),
|
55
|
+
:unify_atomic_terms],
|
56
|
+
[kind_of(Composite::CompositeTerm), kind_of(Atomic::AtomicTerm),
|
57
|
+
:unify_composite_atomic],
|
58
|
+
[kind_of(Composite::CompositeTerm), kind_of(Composite::CompositeTerm),
|
59
|
+
:unify_composite_terms],
|
60
|
+
[kind_of(Core::LogVarRef), kind_of(Atomic::AtomicTerm),
|
61
|
+
:unify_ref_atomic],
|
62
|
+
[kind_of(Core::LogVarRef), kind_of(Composite::CompositeTerm),
|
63
|
+
:unify_ref_composite],
|
64
|
+
[kind_of(Core::LogVarRef), kind_of(Core::LogVarRef),
|
65
|
+
:unify_references]
|
66
|
+
]
|
67
|
+
|
68
|
+
# require 'debug'
|
69
|
+
|
70
|
+
table.each do |(cond1, cond2, selector)|
|
71
|
+
if cell_success(arg1, cond1, ctx) &&
|
72
|
+
cell_success(arg2, cond2, ctx)
|
73
|
+
return send(selector, arg1, arg2, ctx)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
ctx.failed
|
78
|
+
end
|
79
|
+
|
80
|
+
=begin
|
81
|
+
# table: Commute
|
82
|
+
# |arg1 | arg2 | arg2.ground? || Commute |
|
83
|
+
# | isa? Atomic | isa? Atomic | dont_care || Yes |
|
84
|
+
# | isa? Atomic | isa? CompositeTerm | dont_care || Yes |
|
85
|
+
# | isa? Atomic | isa? LogVarRef | dont_care || Yes |
|
86
|
+
# | isa? CompositeTerm | isa? Atomic | true || No |
|
87
|
+
# | isa? CompositeTerm | isa? CompositeTerm | false || Yes |
|
88
|
+
# | isa? CompositeTerm | isa? CompositeTerm | true || No |
|
89
|
+
# | isa? CompositeTerm | isa? LogVarRef | dont_care || Yes |
|
90
|
+
# | isa? LogVarRef | isa? Atomic | dont_care || No |
|
91
|
+
# | isa? LogVarRef | isa? CompositeTerm | dont_care || No |
|
92
|
+
# | isa? LogVarRef | isa? LogVarRef | false || Yes |
|
93
|
+
# | isa? LogVarRef | isa? LogVarRef | true || No |
|
94
|
+
=end
|
95
|
+
|
96
|
+
def weight_arg(arg, ctx)
|
97
|
+
case arg
|
98
|
+
when Atomic::AtomicTerm
|
99
|
+
1
|
100
|
+
when Composite::CompositeTerm
|
101
|
+
2
|
102
|
+
when Core::LogVarRef
|
103
|
+
# Move unbound argument to the right...
|
104
|
+
arg.unbound?(ctx) ? 3 : 4
|
105
|
+
else
|
106
|
+
raise StandardError
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def kind_of(aClass)
|
111
|
+
->(ar, _) { ar.kind_of?(aClass) }
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def cell_success(arg, cond, ctx)
|
117
|
+
case cond
|
118
|
+
when Class
|
119
|
+
arg.kind_of?(cond.class)
|
120
|
+
when Proc
|
121
|
+
cond.call(arg, ctx)
|
122
|
+
else
|
123
|
+
raise StandardError
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Unification of two atomic terms
|
128
|
+
# @param arg1 [Atomic::AtomicTerm]
|
129
|
+
# @param arg2 [Atomic::AtomicTerm]
|
130
|
+
# @param ctx [Core::Context] A context object
|
131
|
+
# @return [Core::Context] Updated context
|
132
|
+
def unify_atomic_terms(arg1, arg2, ctx)
|
133
|
+
arg1.eql?(arg2) ? ctx.succeeded! : ctx.failed!
|
134
|
+
end
|
135
|
+
|
136
|
+
# Unification of a composite term with an atomic term
|
137
|
+
# @param _composite [Composite::CompositeTerm]
|
138
|
+
# @param _atomic [Atomic::AtomicTerm]
|
139
|
+
# @param ctx [Core::Context] A context object
|
140
|
+
# @return [Core::Context] a failure Context
|
141
|
+
def unify_composite_atomic(_composite, _atomic, ctx)
|
142
|
+
ctx.failed!
|
143
|
+
end
|
144
|
+
|
145
|
+
# Unification of a composite term with an atomic term
|
146
|
+
# @param arg1 [Composite::CompositeTerm]
|
147
|
+
# @param arg2 [Composite::CompositeTerm]
|
148
|
+
# @param ctx [Core::Context] A context object
|
149
|
+
# @return [Core::Context] Updated context
|
150
|
+
def unify_composite_terms(arg1, arg2, ctx)
|
151
|
+
return ctx.succeeded! if arg1.null? && arg2.null?
|
152
|
+
|
153
|
+
# We do parallel iteration
|
154
|
+
visitor1 = Composite::ConsCellVisitor.df_visitor(arg1)
|
155
|
+
visitor2 = Composite::ConsCellVisitor.df_visitor(arg2)
|
156
|
+
skip_children1 = skip_children2 = false
|
157
|
+
|
158
|
+
loop do
|
159
|
+
# side can be: :car, :cdr, :stop
|
160
|
+
side1, cell1 = visitor1.resume(skip_children1)
|
161
|
+
side2, cell2 = visitor2.resume(skip_children2)
|
162
|
+
if side1 != side2
|
163
|
+
ctx.failed!
|
164
|
+
elsif side1 == :stop
|
165
|
+
break
|
166
|
+
else
|
167
|
+
# A cell can be: nil, Atomic::AtomicTerm, Composite::ConsCell, LogVarRef
|
168
|
+
case [cell1.class, cell2.class]
|
169
|
+
when [Composite::ConsCell, Composite::ConsCell]
|
170
|
+
skip_children1 = skip_children2 = false
|
171
|
+
ctx.blackboard.succeeded!
|
172
|
+
when [Composite::ConsCell, Core::LogVarRef]
|
173
|
+
skip_children1 = true
|
174
|
+
skip_children2 = false
|
175
|
+
unification(cell1, cell2, ctx)
|
176
|
+
when [Core::LogVarRef, Composite::ConsCell]
|
177
|
+
skip_children1 = false
|
178
|
+
skip_children2 = true
|
179
|
+
do_unification(cell1, cell2, ctx)
|
180
|
+
else
|
181
|
+
skip_children1 = skip_children2 = false
|
182
|
+
unification(cell1, cell2, ctx)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
break if ctx.failure?
|
187
|
+
end
|
188
|
+
|
189
|
+
ctx
|
190
|
+
end
|
191
|
+
|
192
|
+
# Unification of a logical variable reference with an atomic term
|
193
|
+
# @param ref [Core::LogVarRef]
|
194
|
+
# @param atomic [Atomic::AtomicTerm]
|
195
|
+
# @param ctx [Core::Context] A context object
|
196
|
+
# @return [Core::Context] Updated context
|
197
|
+
def unify_ref_atomic(ref, atomic, ctx)
|
198
|
+
if ref.unbound?(ctx)
|
199
|
+
ctx.associate(ref, atomic)
|
200
|
+
ctx.succeeded!
|
201
|
+
else
|
202
|
+
assocs = ctx.associations_for(ref.name)
|
203
|
+
first_assoc = assocs.first
|
204
|
+
if first_assoc.kind_of?(Core::Association) &&
|
205
|
+
first_assoc.value.eql?(atomic)
|
206
|
+
# Trying to associate again to the same value is OK
|
207
|
+
ctx.succeeded!
|
208
|
+
else
|
209
|
+
ctx.failed!
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Unification of a logical variable and a composite
|
215
|
+
# @param ref [Core::LogVarRef]
|
216
|
+
# @param composite [Core::CompositeTerm]
|
217
|
+
# @param ctx [Core::Context] A context object
|
218
|
+
# @return [Core::Context] Updated context
|
219
|
+
def unify_ref_composite(ref, composite, ctx)
|
220
|
+
if ref.unbound?(ctx)
|
221
|
+
ctx.associate(ref, composite)
|
222
|
+
else
|
223
|
+
# Assumption: ref has only one existing association...
|
224
|
+
as = ctx.associations_for(ref.name)
|
225
|
+
first_assoc = as.first.value
|
226
|
+
unification(first_assoc, composite, ctx)
|
227
|
+
return ctx if ctx.failure?
|
228
|
+
|
229
|
+
# The association can be sometimes be redundant...
|
230
|
+
ctx.associate(ref, composite) unless first_assoc.pinned?(ctx)
|
231
|
+
end
|
232
|
+
ctx.succeeded!
|
233
|
+
ctx
|
234
|
+
end
|
235
|
+
|
236
|
+
# Unification of two logical variable references
|
237
|
+
# @param ref1 [Core::LogVarRef]
|
238
|
+
# @param ref2 [Core::LogVarRef]
|
239
|
+
# @param ctx [Core::Context] A context object
|
240
|
+
# @return [Core::Context] Updated context
|
241
|
+
def unify_references(ref1, ref2, ctx)
|
242
|
+
return ctx.succeeded! if ref1.name == ref2.name
|
243
|
+
|
244
|
+
if ref1.unbound?(ctx) || ref2.unbound?(ctx)
|
245
|
+
ctx.fuse([ref1.name, ref2.name])
|
246
|
+
ctx.succeeded!
|
247
|
+
elsif ref1.floating?(ctx) && ref2.floating?(ctx)
|
248
|
+
raise StandarrError if ctx.associations_for(ref1.name).size > 1
|
249
|
+
val1 = ctx.associations_for(ref1.name)[0].value
|
250
|
+
raise StandarrError if ctx.associations_for(ref2.name).size > 1
|
251
|
+
val2 = ctx.associations_for(ref2.name)[0].value
|
252
|
+
unification(val1, val2, ctx)
|
253
|
+
ctx.fuse([ref1.name, ref2.name]) if ctx.success?
|
254
|
+
else
|
255
|
+
raise NotImplementedError
|
256
|
+
end
|
257
|
+
# if both refs are fresh, fuse them
|
258
|
+
# if one ref is fresh & the other one isn't then bind fresh one (occurs check)
|
259
|
+
# More cases...
|
260
|
+
|
261
|
+
ctx
|
262
|
+
end
|
263
|
+
end # class
|
264
|
+
end # module
|
265
|
+
end # module
|
data/lib/mini_kraken/version.rb
CHANGED
data/mini_kraken.gemspec
CHANGED
@@ -40,8 +40,8 @@ Gem::Specification.new do |spec|
|
|
40
40
|
spec.authors = ['Dimitri Geshef']
|
41
41
|
spec.email = ['famished.tiger@yahoo.com']
|
42
42
|
|
43
|
-
spec.summary = 'Implementation of
|
44
|
-
spec.description = 'Implementation of
|
43
|
+
spec.summary = 'Implementation of miniKanren language in Ruby. WIP'
|
44
|
+
spec.description = 'Implementation of miniKanren language in Ruby. WIP'
|
45
45
|
spec.homepage = 'https://github.com/famished-tiger/mini_kraken'
|
46
46
|
spec.license = 'MIT'
|
47
47
|
|
data/spec/.rubocop.yml
CHANGED
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../spec_helper' # Use the RSpec framework
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
# Load the class under test
|
7
|
+
require_relative '../../lib/mini_kraken/atomic/atomic_term'
|
8
|
+
|
9
|
+
module MiniKraken
|
10
|
+
module Atomic
|
11
|
+
describe AtomicTerm do
|
12
|
+
let(:a_value) { :serenity }
|
13
|
+
let(:other_value) { :fuzziness }
|
14
|
+
subject { AtomicTerm.new(a_value) }
|
15
|
+
|
16
|
+
context 'Initialization:' do
|
17
|
+
it 'should be created with a Ruby datatype instance' do
|
18
|
+
expect { AtomicTerm.new(a_value) }.not_to raise_error
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'knows its value' do
|
22
|
+
expect(subject.value).to eq(a_value)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'freezes its value' do
|
26
|
+
expect(subject.value).to be_frozen
|
27
|
+
end
|
28
|
+
end # context
|
29
|
+
|
30
|
+
context 'Provided services:' do
|
31
|
+
it 'should know that it is a pinned term' do
|
32
|
+
ctx = double('mock-ctx')
|
33
|
+
expect(subject.pinned?(ctx)).to be_truthy
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should know that it is not a floating term' do
|
37
|
+
ctx = double('mock-ctx')
|
38
|
+
expect(subject.floating?(ctx)).to be_falsy
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should know that it is not an unbound term' do
|
42
|
+
ctx = double('mock-ctx')
|
43
|
+
expect(subject.unbound?(ctx)).to be_falsy
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'performs data value comparison' do
|
47
|
+
itself = subject # Renaming as workaround to Rubocop complaint
|
48
|
+
expect(subject == itself).to be_truthy
|
49
|
+
expect(subject == subject.value).to be_truthy
|
50
|
+
|
51
|
+
expect(subject == other_value).to be_falsy
|
52
|
+
expect(subject == AtomicTerm.new(other_value)).to be_falsy
|
53
|
+
|
54
|
+
# Same duck type, same value
|
55
|
+
yet_another = OpenStruct.new(value: a_value)
|
56
|
+
expect(subject == yet_another).to be_truthy
|
57
|
+
|
58
|
+
# Same duck type, different value
|
59
|
+
still_another = OpenStruct.new(value: other_value)
|
60
|
+
expect(subject == still_another).to be_falsy
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'performs type and data value comparison' do
|
64
|
+
expect(subject).to be_eql(subject)
|
65
|
+
|
66
|
+
# Same type, same value
|
67
|
+
other = AtomicTerm.new(a_value)
|
68
|
+
expect(subject).to be_eql(other)
|
69
|
+
|
70
|
+
# Same type, other value
|
71
|
+
another = AtomicTerm.new(other_value)
|
72
|
+
expect(subject).not_to be_eql(another)
|
73
|
+
|
74
|
+
# Different type, same value
|
75
|
+
yet_another = OpenStruct.new(value: other_value)
|
76
|
+
expect(subject).not_to be_eql(yet_another)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'returns itself when receiving quote message' do
|
80
|
+
ctx = double('mock-ctx')
|
81
|
+
expect(subject.quote(ctx)).to eq(subject)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should know it has no dependency on variable(s)' do
|
85
|
+
ctx = double('mock-ctx')
|
86
|
+
expect(subject.dependencies(ctx)).to be_empty
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should dup itself' do
|
90
|
+
substitutions = double('fake-substitutions')
|
91
|
+
duplicate = subject.dup_cond(substitutions)
|
92
|
+
expect(duplicate).to eq(subject) # same value
|
93
|
+
expect(duplicate).not_to be_equal(subject) # different object ids
|
94
|
+
end
|
95
|
+
end # context
|
96
|
+
end # describe
|
97
|
+
end # module
|
98
|
+
end # module
|