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