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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/README.md +0 -3
  4. data/lib/mini_kraken/core/any_value.rb +29 -0
  5. data/lib/mini_kraken/core/association.rb +21 -0
  6. data/lib/mini_kraken/core/association_walker.rb +179 -0
  7. data/lib/mini_kraken/core/atomic_term.rb +64 -0
  8. data/lib/mini_kraken/core/binary_relation.rb +61 -0
  9. data/lib/mini_kraken/core/composite_term.rb +54 -0
  10. data/lib/mini_kraken/core/cons_cell.rb +44 -0
  11. data/lib/mini_kraken/core/duck_fiber.rb +44 -0
  12. data/lib/mini_kraken/core/environment.rb +59 -0
  13. data/lib/mini_kraken/core/equals.rb +216 -0
  14. data/lib/mini_kraken/core/fail.rb +8 -5
  15. data/lib/mini_kraken/core/freshness.rb +42 -0
  16. data/lib/mini_kraken/core/goal.rb +31 -6
  17. data/lib/mini_kraken/core/k_integer.rb +15 -0
  18. data/lib/mini_kraken/core/k_symbol.rb +15 -0
  19. data/lib/mini_kraken/core/nullary_relation.rb +11 -3
  20. data/lib/mini_kraken/core/outcome.rb +35 -0
  21. data/lib/mini_kraken/core/relation.rb +31 -4
  22. data/lib/mini_kraken/core/succeed.rb +13 -1
  23. data/lib/mini_kraken/core/term.rb +7 -0
  24. data/lib/mini_kraken/core/variable.rb +45 -3
  25. data/lib/mini_kraken/core/variable_ref.rb +76 -0
  26. data/lib/mini_kraken/core/vocabulary.rb +161 -0
  27. data/lib/mini_kraken/glue/fresh_env.rb +31 -0
  28. data/lib/mini_kraken/glue/run_star_expression.rb +43 -0
  29. data/lib/mini_kraken/version.rb +1 -1
  30. data/spec/core/association_spec.rb +38 -0
  31. data/spec/core/association_walker_spec.rb +191 -0
  32. data/spec/core/cons_cell_spec.rb +63 -0
  33. data/spec/core/duck_fiber_spec.rb +62 -0
  34. data/spec/core/environment_spec.rb +154 -0
  35. data/spec/core/equals_spec.rb +289 -0
  36. data/spec/core/fail_spec.rb +16 -0
  37. data/spec/core/goal_spec.rb +36 -10
  38. data/spec/core/k_symbol_spec.rb +72 -0
  39. data/spec/core/succeed_spec.rb +43 -0
  40. data/spec/core/variable_ref_spec.rb +31 -0
  41. data/spec/core/variable_spec.rb +11 -3
  42. data/spec/core/vocabulary_spec.rb +188 -0
  43. data/spec/glue/fresh_env_spec.rb +36 -0
  44. data/spec/glue/run_star_expression_spec.rb +247 -0
  45. data/spec/support/factory_methods.rb +54 -0
  46. metadata +46 -13
  47. data/lib/mini_kraken/core/facade.rb +0 -45
  48. data/lib/mini_kraken/core/formal_arg.rb +0 -6
  49. data/lib/mini_kraken/core/publisher.rb +0 -27
  50. data/lib/mini_kraken/core/run_star_expression.rb +0 -34
  51. data/lib/mini_kraken/dsl/kraken_dsl.rb +0 -12
  52. data/spec/core/facade_spec.rb +0 -38
  53. data/spec/core/run_star_expression_spec.rb +0 -43
  54. data/spec/dsl/kraken_dsl_spec.rb +0 -31
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e60a12ea5711e80f10de8f1a2c018199975fc83baeecab2d15fdb2ea6c6b91c3
4
- data.tar.gz: 8fd4f903f896ac08e13f00881592b7e81e4684f7eb9acd2f7be9d4b03fee6a2d
3
+ metadata.gz: d6ff930902d2d1d8967acaab13146a2f1f884b4dab5cdf5dc23b14e043baac76
4
+ data.tar.gz: 702bd83538e9e94edac5e17943af72438d3c91296221c80e6172d1cbbf1df71c
5
5
  SHA512:
6
- metadata.gz: e5f333b6bfbb58f2726c1351fa672c95c1b3b171c322411ce87617f0a4e748f8bab1beecb0a4fc035dc15ec9ae382d2b1dd3fcd59707a37402db33a12248a878
7
- data.tar.gz: '0196ee55adc9516fa4cfcea24dd74ec33b061771e9c273543d648d00b1244038d6c945a95b76f44c1c7f02f853f92a32a541ea051d5d6d5facf9d307640a2e13'
6
+ metadata.gz: 4885be585b956b690762ff66365098d4ed0cd6bd141410863afa1701431f6cc4fc6749fc5a2fff4e2689cbd3f9ac7035f057458de2b364df6a34015a252deaed
7
+ data.tar.gz: 6180e86da36cdb2d2d3fd0daf6e53061513064b40df79980f36c73d97418e0016c5985bb03d573a338508a8c2d1745dfa2340c62c261177716d57ec4676e5cba
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## [0.1.02] - 2020-04-28
2
+ Major code refactoring. Passes all frames 1:1 up to 1:36 of "Reasoned Schemer" book
3
+
1
4
  ## [0.1.01] - 2020-03-01
2
5
  First code commit
3
6
  ### Added
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