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