mini_kraken 0.1.05 → 0.1.06

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4ce5f0f43f5e84f377cf90e2ed49d6ff54de93b66641655fb41dd6b8feef277
4
- data.tar.gz: 2081e486f8beba0b0073461464c632fa63af83cec27a48fb3d84c1a972b821f9
3
+ metadata.gz: 800d58c5efac32c005931c5a019f4440060728d1061c381bd10f95d554a525b1
4
+ data.tar.gz: 217448cd67ca952439e81da5c89393ddd57d061d7ca9d5ed310119d1aab32cbf
5
5
  SHA512:
6
- metadata.gz: ea0580c9219f1a1fd586d024f06e11cf6dd1f979182c6f5a15473c65b78fa7244ba01ca9191f31e73de78b20d7aa88c60d86afa1207dbaab246c64c6650f0d1f
7
- data.tar.gz: 9af43ee21606cac23d2512002491d0264fcbc7a74b9974edbd70c23f4bcbf25c7cc7010845aad88dee2d507a1f6d070dd402cd9a6f2abe0234999617b3ae7b68
6
+ metadata.gz: e52d5bdc7a34e1b303f2423fd8463766852bd61dc14d77e7bce43bcf653fd43d2331eb8f4bbc1beee13958e4621c9729b728d0cf5bbb5336bd5e277bf859a473
7
+ data.tar.gz: 75b7a091579f667b41949905861ec314eb6a269ca5316b67df80cbf9f02d6d86ecd1c0ff74d61de3018c802e7c48ff7a2e4719bcb75f0ef45cca0092ebd3cb25
data/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## [0.1.06] - 2020-05-20
2
+ - Implementation of `conj2` (two arguments conjunction)
3
+
4
+ ### New
5
+ - Class `CompositeGoal`
6
+ - Class `Conj2` as subclass of `GoalRelation` that implements the conjunction of two subgoals
7
+ - Mixin module `Designation` to factor out the common methods in `Variable` and `VariableRef` classes
8
+ - Class `GoalArg` abstract class, that is a generalization for anything that be be argument of a goal.
9
+ - Class `GoalRelation` as subclass of `Relation`. A goal that is linked to a such relation may have goals as its arguments only.
10
+
11
+ ### Changed
12
+ - Class `Goal` is new subclass of class `GoalArg`. Therefore a goal can be an argument to another goal.
13
+ - Class `Term` is new subclass of class `GoalArg`. Therefore a term can be an argument of a goal.
14
+ - Classes `Variable`, `VariableRef` now include mix-in module `Designation`
15
+ - File `cd_implementation.txt` Updated with changes of class relationship
16
+
1
17
  ## [0.1.05] - 2020-05-09
2
18
  - Changed implementation of fused variables
3
19
  - Magic comments for frozen string literal
data/README.md CHANGED
@@ -13,12 +13,14 @@ ISBN: 9780262535519, (2018), MIT Press.
13
13
  ### Features
14
14
  - [X] ==
15
15
  - [X] run\*
16
- - [X] fresh
16
+ - [X] fresh
17
+ - [X] conj2
17
18
 
18
19
  ### TODO
19
20
  - [ ] disj2
20
- - [ ] conj2
21
+ - [ ] defrel
21
22
  - [ ] conde
23
+ - [ ] Occurs check
22
24
 
23
25
  ## Installation
24
26
 
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'environment'
4
+
5
+ module MiniKraken
6
+ module Core
7
+ class CompositeGoal
8
+ # @return [Operator] The operator corresponding to this goal
9
+ attr_reader :operator
10
+
11
+ # @return [Array<Goal>] The child goals (sub-goals)
12
+ attr_reader :children
13
+
14
+ # @param anOperator [Operator] The operator corresponding to this goal
15
+ # @param theChildren [Array<Goal>] The child goals (sub-goals)
16
+ def initialize(anOperator, theChildren)
17
+ @operator = anOperator
18
+ @children = validated_children(theChildren)
19
+ end
20
+
21
+ # Attempt to achieve the goal for a given context (environment)
22
+ # @param anEnv [Environment] The context in which the goal take place.
23
+ # @return [Fiber<Outcome>] A Fiber object that will generate the results.
24
+ def attain(anEnv)
25
+ operator.solver_for(children, anEnv)
26
+ end
27
+
28
+ private
29
+
30
+ def validated_children(theChildren)
31
+ my_arity = operator.arity
32
+ if args.size != my_arity
33
+ err_msg = "Goal has #{theChildren.size} arguments, expected #{my_arity}"
34
+ raise StandardError, err_msg
35
+ end
36
+
37
+ prefix = 'Invalid goal argument '
38
+ theChildren.each do |subg|
39
+ raise StandardError, prefix + subg.to_s unless subg.kind_of?(Goal)
40
+ end
41
+
42
+ theChildren.dup
43
+ end
44
+ end # class
45
+ end # module
46
+ end # module
@@ -8,6 +8,8 @@ module MiniKraken
8
8
  # An composite term is an Minikraken term that can be
9
9
  # decomposed into simpler MiniKraken data value(s).
10
10
  class CompositeTerm < Term
11
+ # Abstract method (to override). Return the child terms.
12
+ # @return [Array<Term>]
11
13
  def children
12
14
  raise NotImplementedError, 'This method must re-defined in subclass(es).'
13
15
  end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require_relative 'duck_fiber'
5
+ require_relative 'goal'
6
+ require_relative 'goal_relation'
7
+ require_relative 'outcome'
8
+
9
+ module MiniKraken
10
+ module Core
11
+ # The conjunction is a relation that accepts only goal(s) as its two
12
+ # arguments. It succeeds if and only both its goal arguments succeeds.
13
+ class Conj2 < GoalRelation
14
+ include Singleton
15
+
16
+ def initialize
17
+ super('conj2', nil)
18
+ end
19
+
20
+ # @param actuals [Array<Term>] A two-elements array
21
+ # @param anEnv [Vocabulary] A vocabulary object
22
+ # @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
23
+ def solver_for(actuals, anEnv)
24
+ g1, g2 = *validated_args(actuals)
25
+ Fiber.new { conjunction(g1, g2, anEnv) }
26
+ end
27
+
28
+ # Yields [Outcome, NilClass] result of the conjunction
29
+ # @param g1 [Goal] First goal argument
30
+ # @param g2 [Goal] Second goal argument
31
+ # @param voc [Vocabulary] A vocabulary object
32
+ def conjunction(g1, g2, voc)
33
+ # require 'debug'
34
+ outcome1 = nil
35
+ outcome2 = nil
36
+ if g1.relation.kind_of?(Fail) || g2.relation.kind_of?(Fail)
37
+ Fiber.yield Outcome.new(:"#u", voc)
38
+ else
39
+ f1 = g1.attain(voc)
40
+ loop do
41
+ outcome1 = f1.resume
42
+ break unless outcome1
43
+
44
+ outcome1.parent = voc unless outcome1.parent
45
+ if outcome1.successful?
46
+ f2 = g2.attain(outcome1)
47
+ loop do
48
+ outcome2 = f2.resume
49
+ break unless outcome2
50
+
51
+ outcome2.parent = voc unless outcome2.parent
52
+ if outcome2.successful?
53
+ res = Outcome.new(:"#s", voc)
54
+ res.merge(outcome1)
55
+ res.merge(outcome2)
56
+ Fiber.yield res
57
+ else
58
+ Fiber.yield outcome2
59
+ end
60
+ outcome2.clear
61
+ end
62
+ else
63
+ Fiber.yield outcome1
64
+ end
65
+ voc.clear if outcome1&.successful? && outcome2&.successful?
66
+ end
67
+ end
68
+
69
+ Fiber.yield nil
70
+ end
71
+
72
+ private
73
+
74
+ def validated_args(actuals)
75
+ actuals.each do |arg|
76
+ unless arg.kind_of?(Goal)
77
+ prefix = 'conj2 expects goal as argument, found a '
78
+ raise StandardError, prefix + "'#{arg.class}'"
79
+ end
80
+ end
81
+
82
+ actuals
83
+ end
84
+ end # class
85
+ end # module
86
+ end # module
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniKraken
4
+ module Core
5
+ # Wordnet definition: Identifying word or words by which someone or
6
+ # something is called and classified or distinguished from others.
7
+ # Mix-in module that contains factored code for managing named entries
8
+ # in a vocabulary such as variables and variable references.
9
+ module Designation
10
+ # @return [String] User-defined name of the variable
11
+ attr_reader :name
12
+
13
+ def init_designation(aName)
14
+ @name = valid_name(aName)
15
+ end
16
+
17
+ # @param voc [Vocabulary]
18
+ # @return [Freshness]
19
+ def freshness(voc)
20
+ voc.freshness_ref(self)
21
+ end
22
+
23
+ # @param voc [Vocabulary]
24
+ # @return [Boolean]
25
+ def fresh?(voc)
26
+ frsh = freshness(voc)
27
+ frsh.degree == :fresh || frsh.degree == :bound
28
+ end
29
+
30
+ # @param voc [Vocabulary]
31
+ # @return [Boolean]
32
+ def bound?(voc)
33
+ frsh = freshness(voc)
34
+ frsh.degree == :bound
35
+ end
36
+
37
+ # @param voc [Vocabulary]
38
+ # @return [Boolean]
39
+ def ground?(voc)
40
+ frsh = freshness(voc)
41
+ frsh.degree == :bound
42
+ end
43
+
44
+ private
45
+
46
+ def valid_name(aName)
47
+ if aName.empty?
48
+ raise StandardError, 'Variable name may not be empty.'
49
+ end
50
+
51
+ aName
52
+ end
53
+ end # class
54
+ end # module
55
+ end # module
@@ -17,15 +17,9 @@ module MiniKraken
17
17
  super('equals', '==')
18
18
  end
19
19
 
20
- =begin
21
- double data flow:
22
- A goal has actual arguments
23
- When its corresponding relation is invoked
24
- this one will return two things: (a success, the bindings/constraints
25
- resulting from the relation)
26
- the bindings/constraints can be undone if enclosing fails, otherwise
27
- the bindings/constraints are rolled up.
28
- =end
20
+ # @param actuals [Array<Term>] A two-elements array
21
+ # @param anEnv [Vocabulary] A vocabulary object
22
+ # @return [Fiber<Outcome>] A Fiber(-like) instance that yields Outcomes
29
23
  def solver_for(actuals, anEnv)
30
24
  arg1, arg2 = *actuals
31
25
  DuckFiber.new(:custom) { unification(arg1, arg2, anEnv) }
@@ -3,8 +3,10 @@
3
3
  require_relative 'environment'
4
4
 
5
5
  module MiniKraken
6
+ require_relative 'goal_arg'
7
+
6
8
  module Core
7
- class Goal
9
+ class Goal < GoalArg
8
10
  # @return [Relation] The relation corresponding to this goal
9
11
  attr_reader :relation
10
12
 
@@ -35,7 +37,7 @@ module MiniKraken
35
37
 
36
38
  prefix = 'Invalid goal argument '
37
39
  args.each do |actl|
38
- raise StandardError, prefix + actl.to_s unless actl.kind_of?(Term)
40
+ raise StandardError, prefix + actl.to_s unless actl.kind_of?(GoalArg)
39
41
  end
40
42
 
41
43
  args.dup
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniKraken
4
+ module Core
5
+ # The generalization of any iem that can be
6
+ # passed as arugement to a goal.
7
+ class GoalArg
8
+ end # class
9
+ end # module
10
+ end # module
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'relation'
4
+
5
+ module MiniKraken
6
+ module Core
7
+ # A specialization of a relation that accepts only goal(s)
8
+ # as its arguments.
9
+ class GoalRelation < Relation
10
+ def arity
11
+ 2
12
+ end
13
+ end # class
14
+ end # module
15
+ end # module
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'goal_arg'
4
+
3
5
  module MiniKraken
4
6
  module Core
5
- # The generalization of data value in MiniKraken
6
- class Term
7
+ # The generalization of any data value that can be
8
+ # passed as arugement to a goal.
9
+ class Term < GoalArg
7
10
  end # class
8
11
  end # module
9
12
  end # module
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'designation'
3
4
  require_relative 'any_value'
4
5
  require_relative 'vocabulary'
5
6
 
@@ -8,32 +9,17 @@ module MiniKraken
8
9
  # Representation of a MiniKraken variable.
9
10
  # It is a named slot that can be associated with one value.
10
11
  class Variable
11
- # @return [String] User-defined name of the variable
12
- attr_reader :name
12
+ include Designation # Mixin: Acquire name attribute
13
13
 
14
14
  # @return [String] Internal variable name used by MiniKraken
15
15
  attr_accessor :i_name
16
16
 
17
17
  # @param aName [String] The name of the variable
18
18
  def initialize(aName)
19
- @name = valid_name(aName)
19
+ init_designation(aName)
20
20
  @i_name = name.dup
21
21
  end
22
22
 
23
- def fresh?(anEnvironment)
24
- anEnvironment.fresh?(self)
25
- end
26
-
27
- # @param env [Environment]
28
- # @return [Freshness]
29
- def freshness(env)
30
- env.freshness_ref(self)
31
- end
32
-
33
- def ground?(anEnvironment)
34
- !fresh?(anEnvironment)
35
- end
36
-
37
23
  def fused?
38
24
  name != i_name
39
25
  end
@@ -44,16 +30,6 @@ module MiniKraken
44
30
  val = anEnvironment.quote_ref(self)
45
31
  val.nil? ? AnyValue.new(name, anEnvironment) : val
46
32
  end
47
-
48
- private
49
-
50
- def valid_name(aName)
51
- if aName.empty?
52
- raise StandardError, 'Variable name may not be empty.'
53
- end
54
-
55
- aName
56
- end
57
33
  end # class
58
34
  end # module
59
35
  end # module
@@ -8,31 +8,12 @@ module MiniKraken
8
8
  # A variable reference represents the occurrence of a variable (name) in a
9
9
  # MiniKraken term.
10
10
  class VariableRef < Term
11
- # @return [String] Name of the variable
12
- attr_reader :var_name
11
+ include Designation # Mixin: Acquire name attribute
12
+ alias var_name name
13
13
 
14
14
  # @param aName [String] The name of the variable
15
15
  def initialize(aName)
16
- @var_name = valid_name(aName)
17
- end
18
-
19
- # @param env [Environment]
20
- # @return [Boolean]
21
- def fresh?(env)
22
- env.fresh?(self)
23
- end
24
-
25
- # @param env [Environment]
26
- # @return [Boolean]
27
- def bound?(env)
28
- freshness = env.freshness_ref(self)
29
- freshness.degree == :bound
30
- end
31
-
32
- # @param env [Environment]
33
- # @return [Boolean]
34
- def ground?(env)
35
- !fresh?(env)
16
+ init_designation(aName)
36
17
  end
37
18
 
38
19
  # @param aValue [Term]
@@ -54,12 +35,6 @@ module MiniKraken
54
35
  freshness.associated
55
36
  end
56
37
 
57
- # @param env [Environment]
58
- # @return [Freshness]
59
- def freshness(env)
60
- env.freshness_ref(self)
61
- end
62
-
63
38
  # @param env [Environment]
64
39
  def quote(env)
65
40
  val = env.quote_ref(self)
@@ -236,7 +236,7 @@ module MiniKraken
236
236
  walker.find_ground(name, self)
237
237
  end
238
238
 
239
- # @param var [CompositeTerm] the composite term to check.
239
+ # @param val [CompositeTerm] the composite term to check.
240
240
  # @return [Boolean]
241
241
  def fresh_value?(val)
242
242
  walker = AssociationWalker.new
@@ -291,7 +291,7 @@ module MiniKraken
291
291
  end
292
292
 
293
293
  # Return the variable with given internal variable name.
294
- # @param aName [String] internal variable name
294
+ # @param i_name [String] internal variable name
295
295
  # @return [Variable]
296
296
  def i_name2var(i_name)
297
297
  var = nil
@@ -31,10 +31,10 @@ module MiniKraken
31
31
  if result # ... more than one result...
32
32
  elsif outcome.successful?
33
33
  env.propagate(outcome)
34
- # require 'debug'
35
34
  result = Core::ConsCell.new(var.quote(outcome))
36
35
  else
37
36
  result = Core::NullList
37
+ env.associations.freeze
38
38
  end
39
39
  end
40
40
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniKraken
4
- VERSION = '0.1.05'
4
+ VERSION = '0.1.06'
5
5
  end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/mini_kraken/core/k_symbol'
5
+ require_relative '../../lib/mini_kraken/core/fail'
6
+ require_relative '../../lib/mini_kraken/core/succeed'
7
+ require_relative '../../lib/mini_kraken/core/equals'
8
+ require_relative '../../lib/mini_kraken/core/environment'
9
+ require_relative '../../lib/mini_kraken/core/variable'
10
+ require_relative '../../lib/mini_kraken/core/variable_ref'
11
+
12
+ # Load the class under test
13
+ require_relative '../../lib/mini_kraken/core/conj2'
14
+
15
+
16
+ module MiniKraken
17
+ module Core
18
+ describe Conj2 do
19
+ subject { Conj2.instance }
20
+
21
+ context 'Initialization:' do
22
+ it 'should be initialized without argument' do
23
+ expect { Conj2.instance }.not_to raise_error
24
+ end
25
+
26
+ it 'should know its name' do
27
+ expect(subject.name).to eq('conj2')
28
+ end
29
+ end # context
30
+
31
+ context 'Provided services:' do
32
+ let(:env) { Environment.new }
33
+ let(:pea) { KSymbol.new(:pea) }
34
+ let(:corn) { KSymbol.new(:corn) }
35
+ let(:meal) { KSymbol.new(:meal) }
36
+ let(:fails) { Goal.new(Fail.instance, []) }
37
+ let(:succeeds) { Goal.new(Succeed.instance, []) }
38
+ let(:var_q) { Variable.new('q') }
39
+ let(:ref_q) { VariableRef.new('q') }
40
+
41
+ it 'should complain when one of its argument is not a goal' do
42
+ err = StandardError
43
+ expect { subject.solver_for([succeeds, pea], env) }.to raise_error(err)
44
+ expect { subject.solver_for([pea, succeeds], env) }.to raise_error(err)
45
+ end
46
+
47
+ it 'should yield one failure if one of the goal is fail' do
48
+ # Fail as first argument
49
+ solver = subject.solver_for([fails, succeeds], env)
50
+ expect(solver.resume).not_to be_successful
51
+ expect(solver.resume).to be_nil
52
+
53
+ # Fail as second argument
54
+ solver = subject.solver_for([succeeds, fails], env)
55
+ expect(solver.resume).not_to be_successful
56
+ expect(solver.resume).to be_nil
57
+ end
58
+
59
+ it 'yield success if both arguments are succeed goals' do
60
+ # Covers frame 1-50
61
+ solver = subject.solver_for([succeeds, succeeds], env)
62
+ outcome = solver.resume
63
+ expect(outcome).to be_successful
64
+ expect(outcome.associations).to be_empty
65
+ expect(solver.resume).to be_nil
66
+ end
67
+
68
+ it 'should yield success and set associations' do
69
+ # # Weird: this example succeeds if run alone...
70
+ # # Covers frame 1-51
71
+ # env.add_var(var_q)
72
+ # sub_goal = Goal.new(Equals.instance, [corn, ref_q])
73
+ # solver = subject.solver_for([succeeds, sub_goal], env)
74
+ # outcome = solver.resume
75
+ # expect(outcome).to be_successful
76
+ # expect(outcome.associations).not_to be_empty
77
+ # expect(outcome.associations['q'].first.value).to eq(corn)
78
+ end
79
+
80
+ it 'should yield fails and set no associations' do
81
+ # Covers frame 1-52
82
+ env.add_var(var_q)
83
+ sub_goal = Goal.new(Equals.instance, [corn, ref_q])
84
+ solver = subject.solver_for([fails, sub_goal], env)
85
+ outcome = solver.resume
86
+ expect(outcome).not_to be_successful
87
+ expect(outcome.associations).to be_empty
88
+ end
89
+
90
+ it 'should yield fails when sub-goals are incompatible' do
91
+ # Covers frame 1-53
92
+ env.add_var(var_q)
93
+ sub_goal1 = Goal.new(Equals.instance, [corn, ref_q])
94
+ sub_goal2 = Goal.new(Equals.instance, [meal, ref_q])
95
+ solver = subject.solver_for([sub_goal1, sub_goal2], env)
96
+ outcome = solver.resume
97
+ expect(outcome).not_to be_successful
98
+ expect(outcome.associations).to be_empty
99
+ end
100
+
101
+ it 'should yield success when sub-goals are same and successful' do
102
+ # Covers frame 1-54
103
+ env.add_var(var_q)
104
+ sub_goal1 = Goal.new(Equals.instance, [corn, ref_q])
105
+ sub_goal2 = Goal.new(Equals.instance, [corn, ref_q])
106
+ solver = subject.solver_for([sub_goal1, sub_goal2], env)
107
+ outcome = solver.resume
108
+ expect(outcome).to be_successful
109
+ expect(outcome.associations).not_to be_empty
110
+ expect(outcome.associations['q'].first.value).to eq(corn)
111
+ end
112
+ end # context
113
+ end # describe
114
+ end # module
115
+ end # module
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../spec_helper' # Use the RSpec framework
4
4
  require_relative '../../lib/mini_kraken/core/goal'
5
+ require_relative '../../lib/mini_kraken/core/conj2'
5
6
  require_relative '../../lib/mini_kraken/core/equals'
6
7
  require_relative '../../lib/mini_kraken/core/fail'
7
8
  require_relative '../../lib/mini_kraken/core/succeed'
@@ -37,12 +38,16 @@ module MiniKraken
37
38
  end # context
38
39
 
39
40
  context 'Provided services:' do
41
+ let(:corn) { k_symbol(:corn) }
42
+ let(:meal) { k_symbol(:meal) }
40
43
  let(:ref_q) { Core::VariableRef.new('q') }
41
44
  let(:ref_x) { Core::VariableRef.new('x') }
42
45
  let(:ref_y) { Core::VariableRef.new('y') }
43
46
  let(:ref_s) { Core::VariableRef.new('s') }
44
47
  let(:ref_t) { Core::VariableRef.new('t') }
45
48
  let(:ref_u) { Core::VariableRef.new('u') }
49
+ let(:fails) { Core::Goal.new(Core::Fail.instance, []) }
50
+ let(:succeeds) { Core::Goal.new(Core::Succeed.instance, []) }
46
51
 
47
52
  it 'should return a null list with the fail goal' do
48
53
  # Reasoned S2, frame 1:7
@@ -356,6 +361,66 @@ module MiniKraken
356
361
  expect(ref_y.fresh?(fresh_env_y)).to be_truthy
357
362
  expect(result.car).to eq(cons(any_value(0), cons(any_value(1), cons(any_value(0)))))
358
363
  end
364
+
365
+ it 'should support conjunction of two succeed' do
366
+ goal = conj2_goal(succeeds, succeeds)
367
+ instance = RunStarExpression.new('q', goal)
368
+
369
+ # Reasoned S2, frame 1:50
370
+ # (run* q (conj2 succeed succeed)) ;; => (_0)
371
+ result = instance.run
372
+ expect(ref_q.fresh?(instance.env)).to be_truthy
373
+ expect(result.car).to eq(any_value(0))
374
+ end
375
+
376
+ # TODO: fix erratic RSpec failure
377
+ # it 'should support conjunction of one succeed and a successful goal' do
378
+ # subgoal = equals_goal(corn, ref_q)
379
+ # goal = conj2_goal(succeeds, subgoal)
380
+ # instance = RunStarExpression.new('q', goal)
381
+
382
+ # # Reasoned S2, frame 1:51
383
+ # # (run* q (conj2 succeed (== 'corn q)) ;; => ('corn)
384
+ # result = instance.run
385
+ # expect(ref_q.fresh?(instance.env)).to be_falsy
386
+ # expect(result.car).to eq(corn)
387
+ # end
388
+
389
+ it 'should support conjunction of one fail and a successful goal' do
390
+ subgoal = equals_goal(corn, ref_q)
391
+ goal = conj2_goal(fails, subgoal)
392
+ instance = RunStarExpression.new('q', goal)
393
+
394
+ # Reasoned S2, frame 1:52
395
+ # (run* q (conj2 fail (== 'corn q)) ;; => ()
396
+ expect(instance.run).to be_null
397
+ expect(ref_q.fresh?(instance.env)).to be_truthy
398
+ end
399
+
400
+ it 'should support conjunction of two contradictory goals' do
401
+ subgoal1 = equals_goal(corn, ref_q)
402
+ subgoal2 = equals_goal(meal, ref_q)
403
+ goal = conj2_goal(subgoal1, subgoal2)
404
+ instance = RunStarExpression.new('q', goal)
405
+
406
+ # Reasoned S2, frame 1:53
407
+ # (run* q (conj2 (== 'corn q)(== 'meal q)) ;; => ()
408
+ expect(instance.run).to be_null
409
+ expect(ref_q.fresh?(instance.env)).to be_truthy
410
+ end
411
+
412
+ it 'should succeed the conjunction of two identical goals' do
413
+ subgoal1 = equals_goal(corn, ref_q)
414
+ subgoal2 = equals_goal(corn, ref_q)
415
+ goal = conj2_goal(subgoal1, subgoal2)
416
+ instance = RunStarExpression.new('q', goal)
417
+
418
+ # Reasoned S2, frame 1:54
419
+ # (run* q (conj2 (== 'corn q)(== 'corn q)) ;; => ('corn)
420
+ result = instance.run
421
+ expect(ref_q.fresh?(instance.env)).to be_falsy
422
+ expect(result.car).to eq(corn)
423
+ end
359
424
  end # context
360
425
  end # describe
361
426
  end # module
@@ -34,6 +34,14 @@ module MiniKraken
34
34
  Core::Goal.new(Core::Equals.instance, [arg1, arg2])
35
35
  end
36
36
 
37
+ # Factory method for constructing a goal using the Equals relation.
38
+ # @param g1 [Core::Goal]
39
+ # @param g2 [Core::Goal]
40
+ # @return [Core::Goal]
41
+ def conj2_goal(g1, g2)
42
+ Core::Goal.new(Core::Conj2.instance, [g1, g2])
43
+ end
44
+
37
45
  # Factory method for constructing a KSymbol instance
38
46
  # @param aSymbol [Symbol]
39
47
  # @return [Core::KSymbol]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_kraken
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.05
4
+ version: 0.1.06
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-09 00:00:00.000000000 Z
11
+ date: 2020-05-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -73,14 +73,19 @@ files:
73
73
  - lib/mini_kraken/core/association_walker.rb
74
74
  - lib/mini_kraken/core/atomic_term.rb
75
75
  - lib/mini_kraken/core/binary_relation.rb
76
+ - lib/mini_kraken/core/composite_goal.rb
76
77
  - lib/mini_kraken/core/composite_term.rb
78
+ - lib/mini_kraken/core/conj2.rb
77
79
  - lib/mini_kraken/core/cons_cell.rb
80
+ - lib/mini_kraken/core/designation.rb
78
81
  - lib/mini_kraken/core/duck_fiber.rb
79
82
  - lib/mini_kraken/core/environment.rb
80
83
  - lib/mini_kraken/core/equals.rb
81
84
  - lib/mini_kraken/core/fail.rb
82
85
  - lib/mini_kraken/core/freshness.rb
83
86
  - lib/mini_kraken/core/goal.rb
87
+ - lib/mini_kraken/core/goal_arg.rb
88
+ - lib/mini_kraken/core/goal_relation.rb
84
89
  - lib/mini_kraken/core/k_integer.rb
85
90
  - lib/mini_kraken/core/k_symbol.rb
86
91
  - lib/mini_kraken/core/nullary_relation.rb
@@ -97,6 +102,7 @@ files:
97
102
  - mini_kraken.gemspec
98
103
  - spec/core/association_spec.rb
99
104
  - spec/core/association_walker_spec.rb
105
+ - spec/core/conj2_spec.rb
100
106
  - spec/core/cons_cell_spec.rb
101
107
  - spec/core/duck_fiber_spec.rb
102
108
  - spec/core/environment_spec.rb
@@ -140,6 +146,7 @@ summary: Implementation of Minikanren language in Ruby. WIP
140
146
  test_files:
141
147
  - spec/core/association_spec.rb
142
148
  - spec/core/association_walker_spec.rb
149
+ - spec/core/conj2_spec.rb
143
150
  - spec/core/cons_cell_spec.rb
144
151
  - spec/core/duck_fiber_spec.rb
145
152
  - spec/core/environment_spec.rb