mini_kraken 0.2.02 → 0.2.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/CHANGELOG.md +18 -0
- data/README.md +3 -3
- data/lib/mini_kraken.rb +0 -1
- data/lib/mini_kraken/core/conde.rb +1 -1
- data/lib/mini_kraken/core/conj2.rb +3 -3
- data/lib/mini_kraken/core/cons_cell_visitor.rb +102 -0
- data/lib/mini_kraken/core/def_relation.rb +3 -0
- data/lib/mini_kraken/core/disj2.rb +2 -2
- data/lib/mini_kraken/core/environment.rb +1 -1
- data/lib/mini_kraken/core/equals.rb +59 -22
- data/lib/mini_kraken/core/goal_template.rb +42 -2
- data/lib/mini_kraken/core/k_boolean.rb +1 -1
- data/lib/mini_kraken/core/outcome.rb +11 -1
- data/lib/mini_kraken/core/variable_ref.rb +6 -0
- data/lib/mini_kraken/core/vocabulary.rb +6 -2
- data/lib/mini_kraken/glue/dsl.rb +17 -8
- data/lib/mini_kraken/glue/fresh_env.rb +31 -3
- data/lib/mini_kraken/glue/fresh_env_factory.rb +83 -0
- data/lib/mini_kraken/glue/run_star_expression.rb +2 -2
- data/lib/mini_kraken/version.rb +1 -1
- data/spec/core/conde_spec.rb +9 -9
- data/spec/core/conj2_spec.rb +7 -7
- data/spec/core/cons_cell_visitor_spec.rb +144 -0
- data/spec/core/def_relation_spec.rb +6 -5
- data/spec/core/disj2_spec.rb +5 -5
- data/spec/core/duck_fiber_spec.rb +1 -1
- data/spec/core/equals_spec.rb +34 -21
- data/spec/core/goal_spec.rb +2 -2
- data/spec/core/outcome_spec.rb +8 -0
- data/spec/core/variable_ref_spec.rb +3 -0
- data/spec/glue/dsl_chap1_spec.rb +10 -2
- data/spec/glue/dsl_chap2_spec.rb +100 -0
- data/spec/glue/fresh_env_factory_spec.rb +97 -0
- data/spec/glue/run_star_expression_spec.rb +3 -2
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ae3703cbb0a7a854af490478227c915d7d9af72715be80cbd4e3c95cb7b2c3f
|
4
|
+
data.tar.gz: 27e04d428e1015f176fbafba7c047a0b12c28e9150161a7a5702190c8dd2e2e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd9cd60953f88610ce3a5009e0cad4099e1896643ad01f1c6616a4c20fb4495a71f5c5ed11efe98873362055bda2ec9d0ba4a1ce972844588cacab528ac5333d
|
7
|
+
data.tar.gz: 1c9d27a9fb7fa014f9789a159aff5309bd9c03ce810e994951373ee550698518683359815c4db43b9d95b00ba889670b824af18c1019af5442f2ca3b42c795ba
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
## [0.2.03] - 2020-09-02
|
2
|
+
- The DSL (Domain Specific Language) supports the `caro` relation & passes frames up to 2-8 from Chapter 2.
|
3
|
+
|
4
|
+
### NEW
|
5
|
+
- Class `ConsCellVisitor`. Its method `df_visitor` builds a Fiber that walks over a ConsCell (list/graph).
|
6
|
+
- Method `Outcome#failure?`
|
7
|
+
- Method `Outcome#prune!` for removing associations of transient variables.
|
8
|
+
- Method `VariableRef#to_s` for providing a text representation of a variable reference
|
9
|
+
- Method `Vocabulary#prune` for removing associations of transient variables.
|
10
|
+
- Class `FreshEnvFactory` as its name implies, is used to build `FreshEnv` instances.
|
11
|
+
|
12
|
+
### CHANGED
|
13
|
+
- Method `Outcome#successful?` renamed to `Outcome#success?`
|
14
|
+
|
15
|
+
### FIXED
|
16
|
+
- Method `Equals#solver_for` now prunes associations of transient variables.
|
17
|
+
- Method `Equals#unify_composite_terms` now copes with Conscell vs. VariableRef unification.
|
18
|
+
|
1
19
|
## [0.2.02] - 2020-08-08
|
2
20
|
- The DSL (Domain Specific Language) now supports `conde` and passes all examples from Chapter 1.
|
3
21
|
|
data/README.md
CHANGED
@@ -23,14 +23,14 @@ ISBN: 9780262535519, (2018), MIT Press.
|
|
23
23
|
- [X] conde
|
24
24
|
- [X] conj2
|
25
25
|
- [X] disj2
|
26
|
-
- [X] defrel
|
26
|
+
- [X] defrel
|
27
|
+
- [X] caro
|
27
28
|
|
28
29
|
### TODO
|
29
30
|
|
30
31
|
- [ ] Occurs check
|
31
32
|
|
32
|
-
List-centric relations from Chapter 2
|
33
|
-
- [ ] caro
|
33
|
+
List-centric relations from Chapter 2
|
34
34
|
- [ ] cdro
|
35
35
|
- [ ] conso
|
36
36
|
- [ ] nullo
|
data/lib/mini_kraken.rb
CHANGED
@@ -43,14 +43,14 @@ unless MiniKraken::Core.constants(false).include? :Conj2
|
|
43
43
|
break unless outcome1
|
44
44
|
|
45
45
|
outcome1.parent = voc unless outcome1.parent
|
46
|
-
if outcome1.
|
46
|
+
if outcome1.success?
|
47
47
|
f2 = g2.attain(outcome1)
|
48
48
|
loop do
|
49
49
|
outcome2 = f2.resume
|
50
50
|
break unless outcome2
|
51
51
|
|
52
52
|
outcome2.parent = voc unless outcome2.parent
|
53
|
-
if outcome2.
|
53
|
+
if outcome2.success?
|
54
54
|
res = Outcome.new(:"#s", voc)
|
55
55
|
res.merge(outcome1)
|
56
56
|
res.merge(outcome2)
|
@@ -63,7 +63,7 @@ unless MiniKraken::Core.constants(false).include? :Conj2
|
|
63
63
|
else
|
64
64
|
Fiber.yield outcome1
|
65
65
|
end
|
66
|
-
if outcome1.
|
66
|
+
if outcome1.success? && (outcome2&.success? || outcome2.nil?)
|
67
67
|
voc.clear
|
68
68
|
end
|
69
69
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require_relative 'cons_cell'
|
5
|
+
|
6
|
+
module MiniKraken
|
7
|
+
module Core
|
8
|
+
# Factory class.
|
9
|
+
# Purpose: to create an enumerator specialized in the visit of cons cells.
|
10
|
+
class ConsCellVisitor
|
11
|
+
# Build a depth-first in-order expression tree visitor.
|
12
|
+
# The visitor is implemented as an Enumerator.
|
13
|
+
# The enumerator returns couples of the form: [:car or :cdr or :nil, visitee]
|
14
|
+
# [anExpr] the term to visit.
|
15
|
+
# @param aCell [ConsCell]
|
16
|
+
# @return [Fiber]
|
17
|
+
def self.df_visitor(aCell)
|
18
|
+
first = aCell # The visit will start from the provided cons cell
|
19
|
+
visitor = Fiber.new do |skipping|
|
20
|
+
# Initialization part: will run once
|
21
|
+
visitees = Set.new # Keep track of the conscell already visited
|
22
|
+
visit_stack = first.nil? ? [] : [[:car, first]] # The LIFO queue of cells to visit
|
23
|
+
|
24
|
+
until visit_stack.empty? # Traversal part (as a loop)
|
25
|
+
to_swap = false
|
26
|
+
side, cell = visit_stack.pop
|
27
|
+
next if visitees.include?(cell)
|
28
|
+
|
29
|
+
visitees << cell
|
30
|
+
|
31
|
+
skip_children = Fiber.yield [side, cell]
|
32
|
+
# require 'debug' if skip_children
|
33
|
+
next if skip_children || skipping
|
34
|
+
|
35
|
+
skipping = false
|
36
|
+
case cell.car
|
37
|
+
when ConsCell
|
38
|
+
visit_stack.push([:car, cell.car])
|
39
|
+
to_swap = true
|
40
|
+
else
|
41
|
+
Fiber.yield [:car, cell.car]
|
42
|
+
end
|
43
|
+
|
44
|
+
case cell.cdr
|
45
|
+
when ConsCell
|
46
|
+
if to_swap
|
47
|
+
visit_stack.insert(-2, [:cdr, cell.cdr])
|
48
|
+
else
|
49
|
+
visit_stack.push([:cdr, cell.cdr])
|
50
|
+
end
|
51
|
+
else
|
52
|
+
Fiber.yield [:cdr, cell.cdr]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Send stop mark
|
57
|
+
Fiber.yield [:stop, nil]
|
58
|
+
end
|
59
|
+
|
60
|
+
=begin
|
61
|
+
visitor = Enumerator.new do |requester| # requester argument is a Yielder
|
62
|
+
# Initialization part: will run once
|
63
|
+
visitees = Set.new # Keep track of the conscell already visited
|
64
|
+
visit_stack = first.nil? ? [] : [[ :car, first ]] # The LIFO queue of cells to visit
|
65
|
+
|
66
|
+
until visit_stack.empty? # Traversal part (as a loop)
|
67
|
+
to_swap = false
|
68
|
+
side, cell = visit_stack.pop()
|
69
|
+
next if visitees.include?(cell)
|
70
|
+
|
71
|
+
requester << [side, cell]
|
72
|
+
case cell.car
|
73
|
+
when ConsCell
|
74
|
+
visit_stack.push([:car, cell.car])
|
75
|
+
to_swap = true
|
76
|
+
else
|
77
|
+
requester << [:car, cell.car]
|
78
|
+
end
|
79
|
+
|
80
|
+
case cell.cdr
|
81
|
+
when ConsCell
|
82
|
+
if to_swap
|
83
|
+
visit_stack.insert(-2, [:cdr, cell.cdr])
|
84
|
+
else
|
85
|
+
visit_stack.push([:cdr, cell.cdr])
|
86
|
+
end
|
87
|
+
else
|
88
|
+
requester << [:cdr, cell.cdr]
|
89
|
+
end
|
90
|
+
|
91
|
+
visitees << cell
|
92
|
+
end
|
93
|
+
|
94
|
+
# Send stop mark
|
95
|
+
requester << [:stop, nil]
|
96
|
+
end
|
97
|
+
=end
|
98
|
+
return visitor
|
99
|
+
end
|
100
|
+
end # class
|
101
|
+
end # module
|
102
|
+
end # module
|
@@ -15,6 +15,7 @@ module MiniKraken
|
|
15
15
|
|
16
16
|
# @param aName [String] name of def relation
|
17
17
|
# @param aGoalTemplate [GoalTemplate]
|
18
|
+
# @param theFormals [Array<FormalArg>]
|
18
19
|
def initialize(aName, aGoalTemplate, theFormals, alternateName = nil)
|
19
20
|
super(aName, alternateName)
|
20
21
|
@formals = validated_formals(theFormals)
|
@@ -43,6 +44,8 @@ module MiniKraken
|
|
43
44
|
end
|
44
45
|
|
45
46
|
def validated_goal_template(aGoalTemplate)
|
47
|
+
raise StandardError unless aGoalTemplate
|
48
|
+
|
46
49
|
aGoalTemplate
|
47
50
|
end
|
48
51
|
end # class
|
@@ -44,7 +44,7 @@ unless MiniKraken::Core.constants(false).include? :Disj2
|
|
44
44
|
break unless outcome1
|
45
45
|
|
46
46
|
outcome1.parent = voc unless outcome1.parent
|
47
|
-
if outcome1.
|
47
|
+
if outcome1.success?
|
48
48
|
Fiber.yield outcome1
|
49
49
|
outcome1.clear
|
50
50
|
end
|
@@ -55,7 +55,7 @@ unless MiniKraken::Core.constants(false).include? :Disj2
|
|
55
55
|
break unless outcome2
|
56
56
|
|
57
57
|
outcome2.parent = voc unless outcome2.parent
|
58
|
-
if outcome2.
|
58
|
+
if outcome2.success?
|
59
59
|
Fiber.yield outcome2
|
60
60
|
outcome2.clear
|
61
61
|
end
|
@@ -57,7 +57,7 @@ module MiniKraken
|
|
57
57
|
# Roll up associations from descendent outcome object
|
58
58
|
# @param descendent [Outcome]
|
59
59
|
def do_propagate(descendent)
|
60
|
-
return unless descendent.
|
60
|
+
return unless descendent.success?
|
61
61
|
|
62
62
|
vars.each_key do |var_name|
|
63
63
|
# assocs = descendent[var_name]
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'singleton'
|
4
4
|
require_relative 'binary_relation'
|
5
|
-
|
5
|
+
require_relative 'cons_cell_visitor'
|
6
6
|
require_relative 'duck_fiber'
|
7
7
|
require_relative 'variable'
|
8
8
|
require_relative 'variable_ref'
|
@@ -23,7 +23,10 @@ unless MiniKraken::Core.constants(false).include? :Equals
|
|
23
23
|
# @return [Fiber<Outcome>] A Fiber(-like) instance that yields Outcomes
|
24
24
|
def solver_for(actuals, anEnv)
|
25
25
|
arg1, arg2 = *actuals
|
26
|
-
DuckFiber.new(:custom)
|
26
|
+
DuckFiber.new(:custom) do
|
27
|
+
outcome = unification(arg1, arg2, anEnv)
|
28
|
+
outcome.prune!
|
29
|
+
end
|
27
30
|
end
|
28
31
|
|
29
32
|
def unification(arg1, arg2, anEnv)
|
@@ -93,7 +96,10 @@ unless MiniKraken::Core.constants(false).include? :Equals
|
|
93
96
|
arg1.associate(arg2, result)
|
94
97
|
else
|
95
98
|
# Ground case...
|
96
|
-
|
99
|
+
arg1_associated = arg1_freshness.associated
|
100
|
+
unless arg1_associated.kind_of?(AtomicTerm)
|
101
|
+
result = unify_composite_terms(arg1_associated, arg2, anEnv)
|
102
|
+
end
|
97
103
|
end
|
98
104
|
elsif arg2.kind_of?(VariableRef)
|
99
105
|
freshness = [arg1.fresh?(anEnv), arg2.fresh?(anEnv)]
|
@@ -121,33 +127,64 @@ unless MiniKraken::Core.constants(false).include? :Equals
|
|
121
127
|
result
|
122
128
|
end
|
123
129
|
|
124
|
-
# @
|
130
|
+
# @param arg1 [ConsCell]
|
131
|
+
# @param arg2 [ConsCell]
|
132
|
+
# @return [Outcome]
|
125
133
|
def unify_composite_terms(arg1, arg2, anEnv)
|
126
134
|
# require 'debug'
|
127
|
-
result = Outcome.
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
135
|
+
result = Outcome.success(anEnv)
|
136
|
+
# We'll do parallel iteration
|
137
|
+
visitor1 = ConsCellVisitor.df_visitor(arg1)
|
138
|
+
visitor2 = ConsCellVisitor.df_visitor(arg2)
|
139
|
+
skip_children1 = false
|
140
|
+
skip_children2 = false
|
141
|
+
|
142
|
+
loop do
|
143
|
+
side1, cell1 = visitor1.resume(skip_children1)
|
144
|
+
side2, cell2 = visitor2.resume(skip_children2)
|
145
|
+
if side1 != side2
|
146
|
+
result = Outcome.failure(anEnv)
|
147
|
+
elsif side1 == :stop
|
148
|
+
break
|
149
|
+
else
|
150
|
+
case [cell1.class, cell2.class] # nil, AtomicTerm, ConsCell, VariableRef
|
151
|
+
when [ConsCell, ConsCell]
|
152
|
+
skip_children1 = false
|
153
|
+
skip_children2 = false
|
154
|
+
when [ConsCell, VariableRef]
|
155
|
+
skip_children1 = true
|
156
|
+
skip_children2 = false
|
157
|
+
sub_result = unification(cell1, cell2, anEnv)
|
158
|
+
result = merge_results(result, sub_result)
|
159
|
+
when [VariableRef, ConsCell]
|
160
|
+
skip_children1 = false
|
161
|
+
skip_children2 = true
|
162
|
+
sub_result = do_unification(cell1, cell2, anEnv)
|
163
|
+
result = merge_results(result, sub_result)
|
164
|
+
else
|
165
|
+
skip_children1 = false
|
166
|
+
skip_children2 = false
|
167
|
+
sub_result = unification(cell1, cell2, anEnv)
|
168
|
+
result = merge_results(result, sub_result)
|
144
169
|
end
|
145
|
-
result = memo
|
146
170
|
end
|
171
|
+
|
172
|
+
break if result.failure?
|
147
173
|
end
|
148
174
|
|
149
175
|
result
|
150
176
|
end
|
177
|
+
|
178
|
+
def merge_results(result1, result2)
|
179
|
+
raise StandardError if result2.kind_of?(Hash)
|
180
|
+
|
181
|
+
if result2.success?
|
182
|
+
result1.merge(result2)
|
183
|
+
result1
|
184
|
+
else
|
185
|
+
result2
|
186
|
+
end
|
187
|
+
end
|
151
188
|
end # class
|
152
189
|
|
153
190
|
Equals.instance.freeze
|
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'base_arg'
|
4
|
+
require_relative 'cons_cell_visitor'
|
4
5
|
|
5
6
|
module MiniKraken
|
6
7
|
module Core
|
7
8
|
# A meta-goal that is parametrized with generic formal arguments.
|
8
9
|
# The individual goals are instantiated when the formal arguments
|
9
|
-
# are bound to goal arguments
|
10
|
+
# are bound to goal arguments.
|
10
11
|
class GoalTemplate < BaseArg
|
11
12
|
# @return [Array<BaseArg>] Arguments of goal template.
|
12
13
|
attr_reader :args
|
@@ -14,13 +15,16 @@ module MiniKraken
|
|
14
15
|
# @return [Relation] Main relation for the goal template
|
15
16
|
attr_reader :relation
|
16
17
|
|
18
|
+
# @param aRelation [Core::Rzlation] the relation
|
19
|
+
# @param theArgs [Array<Core::BaseArg>] Arguments of goal template.
|
17
20
|
def initialize(aRelation, theArgs)
|
18
21
|
super()
|
19
22
|
@relation = validated_relation(aRelation)
|
20
23
|
@args = validated_args(theArgs)
|
21
|
-
freeze
|
24
|
+
args.freeze
|
22
25
|
end
|
23
26
|
|
27
|
+
# Factory method: Create a goal object.
|
24
28
|
# @param formals [Array<FormalArg>] Array of formal arguments
|
25
29
|
# @param actuals [Array<GoalArg>] Array of actual arguments
|
26
30
|
# @return [Goal] instantiate a goal object given the actuals and environment
|
@@ -50,6 +54,9 @@ module MiniKraken
|
|
50
54
|
goal_args << formals2actuals[arg.name]
|
51
55
|
elsif arg.kind_of?(GoalTemplate)
|
52
56
|
goal_args << arg.send(:do_instantiate, formals2actuals)
|
57
|
+
elsif arg.kind_of?(ConsCell)
|
58
|
+
# if list contains a formal_ref it must be replaced by the actual
|
59
|
+
goal_args << transform(arg, formals2actuals)
|
53
60
|
else
|
54
61
|
goal_args << arg
|
55
62
|
end
|
@@ -57,6 +64,39 @@ module MiniKraken
|
|
57
64
|
|
58
65
|
Goal.new(relation, goal_args)
|
59
66
|
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def transform(aConsCell, formals2actuals)
|
71
|
+
return aConsCell if aConsCell.null?
|
72
|
+
|
73
|
+
member = { car: :@car, cdr: :@cdr }
|
74
|
+
visitor = ConsCellVisitor.df_visitor(aConsCell)
|
75
|
+
side, cell = visitor.resume
|
76
|
+
result = ConsCell.new(nil, nil)
|
77
|
+
node = result
|
78
|
+
|
79
|
+
loop do
|
80
|
+
side, cell = visitor.resume
|
81
|
+
break if side == :stop
|
82
|
+
|
83
|
+
converted = nil
|
84
|
+
case cell
|
85
|
+
when FormalRef
|
86
|
+
converted = formals2actuals[cell.name]
|
87
|
+
when ConsCell
|
88
|
+
converted = ConsCell.new(nil, nil)
|
89
|
+
when GoalTemplate
|
90
|
+
converted = cell.send(:do_instantiate, formals2actuals)
|
91
|
+
else
|
92
|
+
converted = cell
|
93
|
+
end
|
94
|
+
node.instance_variable_set(member[side], converted)
|
95
|
+
node = converted if converted.kind_of?(ConsCell)
|
96
|
+
end
|
97
|
+
|
98
|
+
result
|
99
|
+
end
|
60
100
|
end # class
|
61
101
|
end # module
|
62
102
|
end # module
|