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.
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