mini_kraken 0.1.01 → 0.1.02
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 +3 -0
- data/README.md +0 -3
- data/lib/mini_kraken/core/any_value.rb +29 -0
- data/lib/mini_kraken/core/association.rb +21 -0
- data/lib/mini_kraken/core/association_walker.rb +179 -0
- data/lib/mini_kraken/core/atomic_term.rb +64 -0
- data/lib/mini_kraken/core/binary_relation.rb +61 -0
- data/lib/mini_kraken/core/composite_term.rb +54 -0
- data/lib/mini_kraken/core/cons_cell.rb +44 -0
- data/lib/mini_kraken/core/duck_fiber.rb +44 -0
- data/lib/mini_kraken/core/environment.rb +59 -0
- data/lib/mini_kraken/core/equals.rb +216 -0
- data/lib/mini_kraken/core/fail.rb +8 -5
- data/lib/mini_kraken/core/freshness.rb +42 -0
- data/lib/mini_kraken/core/goal.rb +31 -6
- data/lib/mini_kraken/core/k_integer.rb +15 -0
- data/lib/mini_kraken/core/k_symbol.rb +15 -0
- data/lib/mini_kraken/core/nullary_relation.rb +11 -3
- data/lib/mini_kraken/core/outcome.rb +35 -0
- data/lib/mini_kraken/core/relation.rb +31 -4
- data/lib/mini_kraken/core/succeed.rb +13 -1
- data/lib/mini_kraken/core/term.rb +7 -0
- data/lib/mini_kraken/core/variable.rb +45 -3
- data/lib/mini_kraken/core/variable_ref.rb +76 -0
- data/lib/mini_kraken/core/vocabulary.rb +161 -0
- data/lib/mini_kraken/glue/fresh_env.rb +31 -0
- data/lib/mini_kraken/glue/run_star_expression.rb +43 -0
- data/lib/mini_kraken/version.rb +1 -1
- data/spec/core/association_spec.rb +38 -0
- data/spec/core/association_walker_spec.rb +191 -0
- data/spec/core/cons_cell_spec.rb +63 -0
- data/spec/core/duck_fiber_spec.rb +62 -0
- data/spec/core/environment_spec.rb +154 -0
- data/spec/core/equals_spec.rb +289 -0
- data/spec/core/fail_spec.rb +16 -0
- data/spec/core/goal_spec.rb +36 -10
- data/spec/core/k_symbol_spec.rb +72 -0
- data/spec/core/succeed_spec.rb +43 -0
- data/spec/core/variable_ref_spec.rb +31 -0
- data/spec/core/variable_spec.rb +11 -3
- data/spec/core/vocabulary_spec.rb +188 -0
- data/spec/glue/fresh_env_spec.rb +36 -0
- data/spec/glue/run_star_expression_spec.rb +247 -0
- data/spec/support/factory_methods.rb +54 -0
- metadata +46 -13
- data/lib/mini_kraken/core/facade.rb +0 -45
- data/lib/mini_kraken/core/formal_arg.rb +0 -6
- data/lib/mini_kraken/core/publisher.rb +0 -27
- data/lib/mini_kraken/core/run_star_expression.rb +0 -34
- data/lib/mini_kraken/dsl/kraken_dsl.rb +0 -12
- data/spec/core/facade_spec.rb +0 -38
- data/spec/core/run_star_expression_spec.rb +0 -43
- data/spec/dsl/kraken_dsl_spec.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6ff930902d2d1d8967acaab13146a2f1f884b4dab5cdf5dc23b14e043baac76
|
4
|
+
data.tar.gz: 702bd83538e9e94edac5e17943af72438d3c91296221c80e6172d1cbbf1df71c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4885be585b956b690762ff66365098d4ed0cd6bd141410863afa1701431f6cc4fc6749fc5a2fff4e2689cbd3f9ac7035f057458de2b364df6a34015a252deaed
|
7
|
+
data.tar.gz: 6180e86da36cdb2d2d3fd0daf6e53061513064b40df79980f36c73d97418e0016c5985bb03d573a338508a8c2d1745dfa2340c62c261177716d57ec4676e5cba
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
# MiniKraken
|
2
2
|
|
3
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/mini_kraken`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
6
3
|
|
7
4
|
## Installation
|
8
5
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module MiniKraken
|
2
|
+
module Core
|
3
|
+
class AnyValue
|
4
|
+
attr_reader :rank
|
5
|
+
|
6
|
+
def initialize(aRank)
|
7
|
+
@rank = aRank
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
rank == other.rank
|
12
|
+
end
|
13
|
+
|
14
|
+
# Use same text representation as in Reasoned Schemer.
|
15
|
+
def to_s
|
16
|
+
"_#{rank}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def ground?(_env)
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [AnyValue]
|
24
|
+
def quote(_env)
|
25
|
+
self
|
26
|
+
end
|
27
|
+
end # class
|
28
|
+
end # module
|
29
|
+
end # module
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module MiniKraken
|
2
|
+
module Core
|
3
|
+
# A record that a given vairable is associated with a value.
|
4
|
+
class Association
|
5
|
+
# @return [String] name of the variable beig association the value.
|
6
|
+
attr_reader :var_name
|
7
|
+
|
8
|
+
# @return [Term] the MiniKraken value associated with the variable
|
9
|
+
attr_reader :value
|
10
|
+
|
11
|
+
|
12
|
+
# @param aVariable [Variable, String] A variable or its name.
|
13
|
+
# @param aValue [Term] value being associated to the variable.
|
14
|
+
def initialize(aVariable, aValue)
|
15
|
+
@var_name = aVariable.respond_to?(:name) ? aVariable.name : aVariable
|
16
|
+
@value = aValue
|
17
|
+
end
|
18
|
+
|
19
|
+
end # class
|
20
|
+
end # module
|
21
|
+
end # module
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'set'
|
2
|
+
require_relative 'atomic_term'
|
3
|
+
require_relative 'composite_term'
|
4
|
+
|
5
|
+
module MiniKraken
|
6
|
+
module Core
|
7
|
+
class AssociationWalker
|
8
|
+
attr_reader :visitees
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@visitees = Set.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param aName [String]
|
15
|
+
# @param anEnv [Vocabulary]
|
16
|
+
# @return [Term, NilClass]
|
17
|
+
def find_ground(aName, anEnv)
|
18
|
+
# require 'debug'
|
19
|
+
assocs = anEnv[aName]
|
20
|
+
walk_assocs(assocs, anEnv)
|
21
|
+
end
|
22
|
+
|
23
|
+
def walk_assocs(assocs, anEnv)
|
24
|
+
# Treat easy cases first...
|
25
|
+
return nil if assocs.empty?
|
26
|
+
assoc_atomic = assocs.find { |assc| assc.value.kind_of?(AtomicTerm) }
|
27
|
+
return assoc_atomic.value if assoc_atomic
|
28
|
+
|
29
|
+
result = nil
|
30
|
+
assocs.each do |assc|
|
31
|
+
unless visitees.include?(assc)
|
32
|
+
visitees.add(assc)
|
33
|
+
sub_result = walk_value(assc.value, anEnv)
|
34
|
+
if sub_result
|
35
|
+
result = sub_result
|
36
|
+
break
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
result
|
42
|
+
end
|
43
|
+
|
44
|
+
def walk_value(aTerm, anEnv)
|
45
|
+
return aTerm if aTerm.kind_of?(AtomicTerm) || aTerm.kind_of?(AnyValue)
|
46
|
+
result = nil
|
47
|
+
|
48
|
+
if aTerm.kind_of?(CompositeTerm)
|
49
|
+
children = aTerm.children.compact
|
50
|
+
walk_results = children.map do |child|
|
51
|
+
walk_value(child, anEnv)
|
52
|
+
end
|
53
|
+
result = aTerm unless walk_results.any?(&:nil?)
|
54
|
+
else # VariableRef or Variable
|
55
|
+
name = aTerm.respond_to?(:name) ? aTerm.name : aTerm.var_name
|
56
|
+
result = find_ground(name, anEnv)
|
57
|
+
end
|
58
|
+
|
59
|
+
result
|
60
|
+
end
|
61
|
+
|
62
|
+
# A composite term is fresh when all its members are nil or all non-nil members
|
63
|
+
# are all fresh.
|
64
|
+
# A composite term is bound when it is not fresh and not ground
|
65
|
+
# A composite term is a ground term when all its non-nil members are ground.
|
66
|
+
# @param aTerm [Term]
|
67
|
+
# @param anEnv [Vocabulary]
|
68
|
+
# @return [Freshness]
|
69
|
+
def determine_freshness(aTerm, anEnv)
|
70
|
+
# require 'debug'
|
71
|
+
result = nil
|
72
|
+
|
73
|
+
if aTerm.kind_of?(AtomicTerm)
|
74
|
+
result = Freshness.new(:ground, aTerm)
|
75
|
+
elsif aTerm.kind_of?(CompositeTerm)
|
76
|
+
children = aTerm.children.compact
|
77
|
+
walk_results = children.map { |chd| determine_freshness(chd, anEnv) }
|
78
|
+
|
79
|
+
degree = nil
|
80
|
+
if walk_results.all?(&:fresh?)
|
81
|
+
degree = :fresh
|
82
|
+
elsif walk_results.all?(&:ground?)
|
83
|
+
degree = :ground
|
84
|
+
else
|
85
|
+
degree = :bound
|
86
|
+
end
|
87
|
+
result = Freshness.new(degree, aTerm)
|
88
|
+
else # VariableRef or Variable
|
89
|
+
name = aTerm.respond_to?(:name) ? aTerm.name : aTerm.var_name
|
90
|
+
assocs = anEnv[name]
|
91
|
+
if assocs.empty?
|
92
|
+
result = Freshness.new(:fresh, nil)
|
93
|
+
else
|
94
|
+
result = freshness_associated(assocs, anEnv)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
result
|
99
|
+
end
|
100
|
+
|
101
|
+
def freshness_associated(assocs, anEnv)
|
102
|
+
assoc_atomic = assocs.find { |assc| assc.value.kind_of?(AtomicTerm) }
|
103
|
+
return Freshness.new(:ground, assoc_atomic.value) if assoc_atomic
|
104
|
+
|
105
|
+
raw_results = assocs.map do |assc|
|
106
|
+
unless visitees.include?(assc)
|
107
|
+
visitees.add(assc)
|
108
|
+
determine_freshness(assc.value, anEnv)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
raw_results.compact!
|
113
|
+
result = nil
|
114
|
+
if raw_results.all?(&:fresh?)
|
115
|
+
# TODO: What if multiple bindings?...
|
116
|
+
result = Freshness.new(:bound, assocs[0].value)
|
117
|
+
elsif raw_results.any?(&:ground?)
|
118
|
+
ground_value = raw_results.find(&:ground?).associated
|
119
|
+
result = Freshness.new(:ground, ground_value)
|
120
|
+
else
|
121
|
+
# TODO: What if multiple bindings?...
|
122
|
+
result = Freshness.new(:bound, raw_results[0].associated)
|
123
|
+
end
|
124
|
+
|
125
|
+
result
|
126
|
+
end
|
127
|
+
|
128
|
+
def quote_term(aTerm, anEnv)
|
129
|
+
# require 'debug'
|
130
|
+
result = nil
|
131
|
+
|
132
|
+
if aTerm.kind_of?(AtomicTerm)
|
133
|
+
result = aTerm.quote(anEnv)
|
134
|
+
elsif aTerm.kind_of?(ConsCell)
|
135
|
+
result = aTerm.quote(anEnv)
|
136
|
+
else # VariableRef or Variable
|
137
|
+
name = aTerm.respond_to?(:name) ? aTerm.name : aTerm.var_name
|
138
|
+
assocs = anEnv[name]
|
139
|
+
if assocs.empty?
|
140
|
+
result = nil
|
141
|
+
else
|
142
|
+
result = quote_associated(assocs, anEnv)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
result
|
147
|
+
end
|
148
|
+
|
149
|
+
def quote_associated(assocs, anEnv)
|
150
|
+
assoc_atomic = assocs.find { |assc| assc.value.kind_of?(AtomicTerm) }
|
151
|
+
return assoc_atomic.value if assoc_atomic
|
152
|
+
|
153
|
+
raw_results = assocs.map do |assc|
|
154
|
+
unless visitees.include?(assc)
|
155
|
+
visitees.add(assc)
|
156
|
+
quote_term(assc.value, anEnv)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
raw_results.compact!
|
161
|
+
result = nil
|
162
|
+
if raw_results.empty?
|
163
|
+
result = nil
|
164
|
+
elsif raw_results.all? { |res| res.fresh?(anEnv) }
|
165
|
+
# TODO: What if multiple bindings?...
|
166
|
+
result = quote_term(assocs[0].value, anEnv)
|
167
|
+
elsif raw_results.any? { |res| res.ground?(anEnv) }
|
168
|
+
ground_res = raw_results.find { |res| res.ground?(anEnv) }
|
169
|
+
result = quote_term(ground_res, anEnv)
|
170
|
+
else
|
171
|
+
# TODO: What if multiple bindings?...
|
172
|
+
result = quote_term(raw_results[0], anEnv)
|
173
|
+
end
|
174
|
+
|
175
|
+
result
|
176
|
+
end
|
177
|
+
end # class
|
178
|
+
end # module
|
179
|
+
end # module
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative 'term'
|
2
|
+
require_relative 'freshness'
|
3
|
+
|
4
|
+
module MiniKraken
|
5
|
+
module Core
|
6
|
+
# An atomic term is an elementary Minikraken term that cannot be
|
7
|
+
# decomposed into simpler MiniKraken data value(s).
|
8
|
+
class AtomicTerm < Term
|
9
|
+
# @return [Object] Internal representation of a MiniKraken data value
|
10
|
+
attr_reader :value
|
11
|
+
|
12
|
+
# @param aValue [Object] Ruby representation of MiniKraken data value
|
13
|
+
def initialize(aValue)
|
14
|
+
@value = aValue
|
15
|
+
@value.freeze
|
16
|
+
end
|
17
|
+
|
18
|
+
# An atomic term is by definition a ground term: since it doesn't contain
|
19
|
+
# any bound variable (in Prolog sense).
|
20
|
+
# @param _env [Vocabulary]
|
21
|
+
# @return [Freshness]
|
22
|
+
def freshness(_env)
|
23
|
+
Freshness.new(:ground, self)
|
24
|
+
end
|
25
|
+
|
26
|
+
# An atomic term is a ground term: by definition it doesn't contain
|
27
|
+
# any fresh variable.
|
28
|
+
# @param _env [Vocabulary]
|
29
|
+
# @return [FalseClass]
|
30
|
+
def fresh?(_env)
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
# An atomic term is a ground term: by definition it doesn't contain
|
35
|
+
# any fresh variable.
|
36
|
+
# @param _env [Vocabulary]
|
37
|
+
# @return [TrueClass]
|
38
|
+
def ground?(_env)
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [AtomicTerm]
|
43
|
+
def quote(_env)
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# Data equality testing
|
48
|
+
# @return [Boolean]
|
49
|
+
def ==(other)
|
50
|
+
if other.respond_to?(:value)
|
51
|
+
value == other.value
|
52
|
+
else
|
53
|
+
value == other
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Type and data equality testing
|
58
|
+
# @return [Boolean]
|
59
|
+
def eql?(other)
|
60
|
+
(self.class == other.class) && value.eql?(other.value)
|
61
|
+
end
|
62
|
+
end # class
|
63
|
+
end # module
|
64
|
+
end # module
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative 'relation'
|
2
|
+
require_relative 'composite_term'
|
3
|
+
|
4
|
+
module MiniKraken
|
5
|
+
module Core
|
6
|
+
class BinaryRelation < Relation
|
7
|
+
# @param aName [String] Name of the relation.
|
8
|
+
# @param alternateName [String, NilClass] Alternative name (optional).
|
9
|
+
def initialize(aName, alternateName = nil)
|
10
|
+
super(aName, alternateName)
|
11
|
+
freeze
|
12
|
+
end
|
13
|
+
|
14
|
+
# Number of arguments for the relation.
|
15
|
+
# @return [Integer]
|
16
|
+
def arity
|
17
|
+
2
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
# table: Commute
|
23
|
+
# |arg1 | arg2 | arg2.ground? || Commute |
|
24
|
+
# | isa? Atomic | isa? Atomic | dont_care || Yes |
|
25
|
+
# | isa? Atomic | isa? CompositeTerm | dont_care || Yes |
|
26
|
+
# | isa? Atomic | isa? VariableRef | dont_care || Yes |
|
27
|
+
# | isa? CompositeTerm | isa? Atomic | true || No |
|
28
|
+
# | isa? CompositeTerm | isa? CompositeTerm | false || Yes |
|
29
|
+
# | isa? CompositeTerm | isa? CompositeTerm | true || No |
|
30
|
+
# | isa? CompositeTerm | isa? VariableRef | dont_care || Yes |
|
31
|
+
# | isa? VariableRef | isa? Atomic | dont_care || No |
|
32
|
+
# | isa? VariableRef | isa? CompositeTerm | dont_care || No |
|
33
|
+
# | isa? VariableRef | isa? VariableRef | false || Yes |
|
34
|
+
# | isa? VariableRef | isa? VariableRef | true || No |
|
35
|
+
def commute_cond(arg1, arg2, env)
|
36
|
+
commuting = true
|
37
|
+
arg2_is_var_ref = arg2.kind_of?(VariableRef)
|
38
|
+
|
39
|
+
if arg1.kind_of?(CompositeTerm)
|
40
|
+
if arg2_is_var_ref
|
41
|
+
commuting = true
|
42
|
+
else
|
43
|
+
commuting = !arg2.ground?(env)
|
44
|
+
end
|
45
|
+
elsif arg1.kind_of?(VariableRef)
|
46
|
+
if arg2_is_var_ref
|
47
|
+
commuting = !arg2.ground?(env)
|
48
|
+
else
|
49
|
+
commuting = false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if commuting
|
54
|
+
[arg2, arg1]
|
55
|
+
else
|
56
|
+
[arg1, arg2]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end # class
|
60
|
+
end # module
|
61
|
+
end # module
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative 'term'
|
2
|
+
require_relative 'freshness'
|
3
|
+
|
4
|
+
module MiniKraken
|
5
|
+
module Core
|
6
|
+
# An composite term is an Minikraken term that can be
|
7
|
+
# decomposed into simpler MiniKraken data value(s).
|
8
|
+
class CompositeTerm < Term
|
9
|
+
|
10
|
+
def children
|
11
|
+
raise NotImplementedError, 'This method must re-defined in subclass(es).'
|
12
|
+
end
|
13
|
+
|
14
|
+
# A composite term is fresh when all its members are nil or all non-nil members
|
15
|
+
# are all fresh
|
16
|
+
# A composite term is bound when it is not fresh and not ground
|
17
|
+
# A composite term is a ground term when all its non-nil members are ground.
|
18
|
+
# @param _env [Vocabulary]
|
19
|
+
# @return [Freshness]
|
20
|
+
def freshness(_env)
|
21
|
+
env.freshness_composite(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param env [Environment]
|
25
|
+
# @return [Boolean]
|
26
|
+
def fresh?(env)
|
27
|
+
env.fresh_value?(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
# A composite is ground if all its children are ground
|
31
|
+
def ground?(anEnv)
|
32
|
+
children.all? do |child|
|
33
|
+
child.nil? || child.ground?(anEnv)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# # Data equality testing
|
38
|
+
# # @return [Boolean]
|
39
|
+
# def ==(other)
|
40
|
+
# if other.respond_to?(:value)
|
41
|
+
# value == other.value
|
42
|
+
# else
|
43
|
+
# value == other
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
|
47
|
+
# # Type and data equality testing
|
48
|
+
# # @return [Boolean]
|
49
|
+
# def eql?(other)
|
50
|
+
# (self.class == other.class) && value.eql?(other.value)
|
51
|
+
# end
|
52
|
+
end # class
|
53
|
+
end # module
|
54
|
+
end # module
|