mini_kraken 0.2.02 → 0.3.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/.rubocop.yml +378 -333
- data/CHANGELOG.md +52 -0
- data/README.md +19 -19
- data/lib/mini_kraken.rb +0 -1
- data/lib/mini_kraken/atomic/all_atomic.rb +5 -0
- data/lib/mini_kraken/atomic/atomic_term.rb +96 -0
- data/lib/mini_kraken/atomic/k_boolean.rb +42 -0
- data/lib/mini_kraken/{core → atomic}/k_integer.rb +2 -5
- data/lib/mini_kraken/atomic/k_string.rb +17 -0
- data/lib/mini_kraken/{core → atomic}/k_symbol.rb +4 -8
- data/lib/mini_kraken/composite/all_composite.rb +4 -0
- data/lib/mini_kraken/composite/composite_term.rb +27 -0
- data/lib/mini_kraken/composite/cons_cell.rb +299 -0
- data/lib/mini_kraken/composite/cons_cell_visitor.rb +50 -0
- data/lib/mini_kraken/composite/list.rb +32 -0
- data/lib/mini_kraken/core/all_core.rb +8 -0
- data/lib/mini_kraken/core/any_value.rb +31 -7
- data/lib/mini_kraken/core/arity.rb +69 -0
- data/lib/mini_kraken/core/association.rb +29 -4
- data/lib/mini_kraken/core/association_copy.rb +50 -0
- data/lib/mini_kraken/core/base_term.rb +13 -0
- data/lib/mini_kraken/core/blackboard.rb +315 -0
- data/lib/mini_kraken/core/bookmark.rb +46 -0
- data/lib/mini_kraken/core/context.rb +492 -0
- data/lib/mini_kraken/core/duck_fiber.rb +21 -19
- data/lib/mini_kraken/core/entry.rb +40 -0
- data/lib/mini_kraken/core/fail.rb +20 -18
- data/lib/mini_kraken/core/fusion.rb +29 -0
- data/lib/mini_kraken/core/goal.rb +20 -29
- data/lib/mini_kraken/core/log_var.rb +22 -0
- data/lib/mini_kraken/core/log_var_ref.rb +108 -0
- data/lib/mini_kraken/core/nullary_relation.rb +2 -9
- data/lib/mini_kraken/core/parametrized_term.rb +61 -0
- data/lib/mini_kraken/core/relation.rb +14 -28
- data/lib/mini_kraken/core/scope.rb +67 -0
- data/lib/mini_kraken/core/solver_adapter.rb +58 -0
- data/lib/mini_kraken/core/specification.rb +48 -0
- data/lib/mini_kraken/core/succeed.rb +21 -17
- data/lib/mini_kraken/core/symbol_table.rb +137 -0
- data/lib/mini_kraken/core/term.rb +15 -4
- data/lib/mini_kraken/glue/dsl.rb +45 -81
- data/lib/mini_kraken/glue/run_star_expression.rb +28 -30
- data/lib/mini_kraken/rela/all_rela.rb +8 -0
- data/lib/mini_kraken/rela/binary_relation.rb +30 -0
- data/lib/mini_kraken/rela/conde.rb +143 -0
- data/lib/mini_kraken/rela/conj2.rb +65 -0
- data/lib/mini_kraken/rela/def_relation.rb +93 -0
- data/lib/mini_kraken/rela/disj2.rb +70 -0
- data/lib/mini_kraken/rela/fresh.rb +98 -0
- data/lib/mini_kraken/{core → rela}/goal_relation.rb +7 -9
- data/lib/mini_kraken/rela/unify.rb +258 -0
- data/lib/mini_kraken/version.rb +1 -1
- data/mini_kraken.gemspec +2 -2
- data/spec/.rubocop.yml +1 -1
- data/spec/atomic/atomic_term_spec.rb +98 -0
- data/spec/{core → atomic}/k_boolean_spec.rb +19 -34
- data/spec/{core → atomic}/k_symbol_spec.rb +3 -16
- data/spec/composite/cons_cell_spec.rb +225 -0
- data/spec/composite/cons_cell_visitor_spec.rb +158 -0
- data/spec/composite/list_spec.rb +50 -0
- data/spec/core/any_value_spec.rb +52 -0
- data/spec/core/arity_spec.rb +92 -0
- data/spec/core/association_copy_spec.rb +69 -0
- data/spec/core/association_spec.rb +31 -4
- data/spec/core/blackboard_spec.rb +287 -0
- data/spec/core/bookmark_spec.rb +40 -0
- data/spec/core/context_spec.rb +245 -0
- data/spec/core/core_spec.rb +40 -0
- data/spec/core/duck_fiber_spec.rb +16 -46
- data/spec/core/fail_spec.rb +5 -6
- data/spec/core/goal_spec.rb +24 -14
- data/spec/core/log_var_ref_spec.rb +105 -0
- data/spec/core/log_var_spec.rb +64 -0
- data/spec/core/nullary_relation_spec.rb +33 -0
- data/spec/core/parametrized_tem_spec.rb +39 -0
- data/spec/core/relation_spec.rb +33 -0
- data/spec/core/scope_spec.rb +73 -0
- data/spec/core/solver_adapter_spec.rb +70 -0
- data/spec/core/specification_spec.rb +43 -0
- data/spec/core/succeed_spec.rb +5 -5
- data/spec/core/symbol_table_spec.rb +142 -0
- data/spec/glue/dsl_chap1_spec.rb +96 -144
- data/spec/glue/dsl_chap2_spec.rb +350 -0
- data/spec/glue/run_star_expression_spec.rb +82 -906
- data/spec/rela/conde_spec.rb +153 -0
- data/spec/rela/conj2_spec.rb +123 -0
- data/spec/rela/def_relation_spec.rb +119 -0
- data/spec/rela/disj2_spec.rb +117 -0
- data/spec/rela/fresh_spec.rb +147 -0
- data/spec/rela/unify_spec.rb +369 -0
- data/spec/support/factory_atomic.rb +29 -0
- data/spec/support/factory_composite.rb +21 -0
- data/spec/support/factory_methods.rb +11 -26
- metadata +100 -64
- data/lib/mini_kraken/core/association_walker.rb +0 -183
- data/lib/mini_kraken/core/atomic_term.rb +0 -67
- data/lib/mini_kraken/core/base_arg.rb +0 -10
- data/lib/mini_kraken/core/binary_relation.rb +0 -63
- data/lib/mini_kraken/core/composite_goal.rb +0 -46
- data/lib/mini_kraken/core/composite_term.rb +0 -41
- data/lib/mini_kraken/core/conde.rb +0 -143
- data/lib/mini_kraken/core/conj2.rb +0 -79
- data/lib/mini_kraken/core/cons_cell.rb +0 -82
- data/lib/mini_kraken/core/def_relation.rb +0 -50
- data/lib/mini_kraken/core/designation.rb +0 -55
- data/lib/mini_kraken/core/disj2.rb +0 -72
- data/lib/mini_kraken/core/environment.rb +0 -73
- data/lib/mini_kraken/core/equals.rb +0 -156
- data/lib/mini_kraken/core/formal_arg.rb +0 -22
- data/lib/mini_kraken/core/formal_ref.rb +0 -25
- data/lib/mini_kraken/core/freshness.rb +0 -45
- data/lib/mini_kraken/core/goal_arg.rb +0 -12
- data/lib/mini_kraken/core/goal_template.rb +0 -62
- data/lib/mini_kraken/core/k_boolean.rb +0 -35
- data/lib/mini_kraken/core/outcome.rb +0 -53
- data/lib/mini_kraken/core/variable.rb +0 -41
- data/lib/mini_kraken/core/variable_ref.rb +0 -78
- data/lib/mini_kraken/core/vocabulary.rb +0 -442
- data/lib/mini_kraken/glue/fresh_env.rb +0 -75
- data/spec/core/association_walker_spec.rb +0 -192
- data/spec/core/conde_spec.rb +0 -147
- data/spec/core/conj2_spec.rb +0 -114
- data/spec/core/cons_cell_spec.rb +0 -107
- data/spec/core/def_relation_spec.rb +0 -96
- data/spec/core/disj2_spec.rb +0 -99
- data/spec/core/environment_spec.rb +0 -142
- data/spec/core/equals_spec.rb +0 -304
- data/spec/core/goal_template_spec.rb +0 -74
- data/spec/core/outcome_spec.rb +0 -48
- data/spec/core/variable_ref_spec.rb +0 -27
- data/spec/core/variable_spec.rb +0 -35
- data/spec/core/vocabulary_spec.rb +0 -219
- data/spec/glue/fresh_env_spec.rb +0 -62
@@ -1,45 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'context'
|
4
4
|
|
5
5
|
module MiniKraken
|
6
6
|
module Core
|
7
7
|
# A mock class that mimicks the behavior of a Fiber instance.
|
8
|
+
# More specifically, it responds to `resume` message & returns a Context.
|
8
9
|
class DuckFiber
|
9
|
-
# @return [
|
10
|
-
attr_reader :
|
10
|
+
# @return [Proc, #call] The callable object to yield.
|
11
|
+
attr_reader :callable
|
11
12
|
|
12
13
|
# @return [Symbol] one of: :initial, :yielded
|
13
14
|
attr_reader :state
|
14
15
|
|
15
|
-
#
|
16
|
-
|
16
|
+
# Constructor.
|
17
|
+
# @param aCallable [Proc, #call] The receiver of the 'call' message.
|
18
|
+
def initialize(aCallable)
|
19
|
+
@callable = valid_callable(aCallable)
|
17
20
|
@state = :initial
|
18
|
-
if outcomeKind == :custom && block_given?
|
19
|
-
@outcome = customization.call
|
20
|
-
else
|
21
|
-
@outcome = valid_outcome(outcomeKind)
|
22
|
-
end
|
23
21
|
end
|
24
22
|
|
23
|
+
# Quacks like a Fiber object.
|
24
|
+
# The first time, this method will return a Context objet.
|
25
|
+
# Subsequents calls just return nil (= no other solution available)
|
26
|
+
# @return [Core::Context, NilClass]
|
25
27
|
def resume(*_args)
|
26
28
|
if state == :initial
|
27
29
|
@state = :yielded
|
28
|
-
return
|
30
|
+
return callable.call
|
29
31
|
else
|
30
32
|
return nil
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
else
|
41
|
-
raise StandardError, "Unknonw outcome kind #{outcomeKind}"
|
36
|
+
private
|
37
|
+
|
38
|
+
def valid_callable(aCallable)
|
39
|
+
unless aCallable.kind_of?(Proc) || aCallable.respond_to?(:call)
|
40
|
+
err_msg = "Expected a Proc instead of #{aCallable.class}."
|
41
|
+
raise StandardError, err_msg
|
42
42
|
end
|
43
|
+
|
44
|
+
aCallable
|
43
45
|
end
|
44
46
|
end # class
|
45
47
|
end # module
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniKraken
|
4
|
+
module Core
|
5
|
+
# Mix-in module that implements the expected common behaviour of entries
|
6
|
+
# placed in the symbol table.
|
7
|
+
module Entry
|
8
|
+
# @return [String] User-defined name of the entry.
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
# @return [String] Suffix for building the internal name of the entry.
|
12
|
+
attr_accessor :suffix
|
13
|
+
|
14
|
+
alias label name
|
15
|
+
|
16
|
+
# Initialize the entry with given name
|
17
|
+
# @param aName [String] The name of the entry
|
18
|
+
def init_name(aName)
|
19
|
+
@name = aName.dup
|
20
|
+
@name.freeze
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return the internal name of the entry
|
24
|
+
# Internal names used to disambiguate entry names.
|
25
|
+
# There might be homonyns between variable because:
|
26
|
+
# - A child Scope may have a entry with same name as one of its
|
27
|
+
# ancestor(s).
|
28
|
+
# - Multiple calls to same defrel or procedure may imply multiple creation
|
29
|
+
# of a entry given name...
|
30
|
+
# @return [String] internal name
|
31
|
+
def i_name
|
32
|
+
if suffix =~ /^_/
|
33
|
+
label + suffix
|
34
|
+
else
|
35
|
+
(suffix.nil? || suffix.empty?) ? label : suffix
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end # module
|
39
|
+
end # module
|
40
|
+
end # module
|
@@ -4,24 +4,26 @@ require 'singleton'
|
|
4
4
|
require_relative 'duck_fiber'
|
5
5
|
require_relative 'nullary_relation'
|
6
6
|
|
7
|
-
|
8
|
-
module
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
include Singleton
|
7
|
+
module MiniKraken
|
8
|
+
module Core
|
9
|
+
# A nullary relation that always returns a failure outcome.
|
10
|
+
class Fail < NullaryRelation
|
11
|
+
include Singleton
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
# Constructor. Initialize the relation's name & freeze it...
|
14
|
+
def initialize
|
15
|
+
super('fail')
|
16
|
+
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
# Returns a Fiber-like object (a DuckFiber).
|
19
|
+
# When that object receives the message resume, it will
|
20
|
+
# signal a failure to the provided context.
|
21
|
+
# @param _actuals [Array] MUST be empty array for nullary relation.
|
22
|
+
# @param ctx [Core::Context] Runtime context
|
23
|
+
# @return [Core::DuckFiber]
|
24
|
+
def solver_for(_actuals, ctx)
|
25
|
+
DuckFiber.new(-> { ctx.failed! })
|
26
|
+
end
|
27
|
+
end # class
|
26
28
|
end # module
|
27
|
-
end #
|
29
|
+
end # module
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
|
4
|
+
module MiniKraken
|
5
|
+
module Core
|
6
|
+
# A record of the fusion / merge of two or more logical variables.
|
7
|
+
class Fusion
|
8
|
+
# @return [String] i_name of substituting variable.
|
9
|
+
attr_reader :i_name
|
10
|
+
|
11
|
+
# @return [Array<String>] i_names of variables being substituted
|
12
|
+
attr_reader :elements
|
13
|
+
|
14
|
+
# Records the fusion of two or more logical variables.
|
15
|
+
# The fused variables are substituted by a new variable
|
16
|
+
# @param aName [String] Internal name of the substituting variable
|
17
|
+
# @param fused [Array<String>] The i_names of the fused variables
|
18
|
+
def initialize(aName, fused)
|
19
|
+
@i_name = aName
|
20
|
+
@elements = fused
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Set] The empty set
|
24
|
+
def dependencies(_ctx)
|
25
|
+
@dependencies ||= Set.new(elements)
|
26
|
+
end
|
27
|
+
end # class
|
28
|
+
end # module
|
29
|
+
end # module
|
@@ -1,48 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'parametrized_term'
|
4
|
+
require_relative 'context'
|
4
5
|
|
5
6
|
module MiniKraken
|
6
|
-
require_relative 'goal_arg'
|
7
|
-
|
8
7
|
module Core
|
9
|
-
class Goal <
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
# @
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
# @param args [Array<Term>] The actual aguments of the goal
|
18
|
-
def initialize(aRelation, args)
|
19
|
-
super()
|
20
|
-
@relation = aRelation
|
21
|
-
@actuals = validated_actuals(args)
|
22
|
-
end
|
23
|
-
|
24
|
-
# Attempt to achieve the goal for a given context (environment)
|
25
|
-
# @param anEnv [Environment] The context in which the goal take place.
|
26
|
-
# @return [Fiber<Outcome>] A Fiber object that will generate the results.
|
27
|
-
def attain(anEnv)
|
28
|
-
relation.solver_for(actuals, anEnv)
|
8
|
+
class Goal < ParametrizedTerm
|
9
|
+
alias relation specification
|
10
|
+
|
11
|
+
# Attempt to obtain one or more solutions for the goal in a given context.
|
12
|
+
# @param ctx [Core::Context] The context in which the goal takes place.
|
13
|
+
# @return [Fiber<Context>] A Fiber object that will generate the results.
|
14
|
+
def achieve(ctx)
|
15
|
+
relation.solver_for(actuals, ctx)
|
29
16
|
end
|
30
17
|
|
31
18
|
private
|
32
19
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
raise StandardError,
|
20
|
+
def validated_specification(theSpec)
|
21
|
+
spec = super(theSpec)
|
22
|
+
unless spec.kind_of?(Relation)
|
23
|
+
raise StandardError, "Expected a Relation instead of #{theSpec.class}."
|
37
24
|
end
|
38
25
|
|
39
|
-
|
26
|
+
spec
|
27
|
+
end
|
28
|
+
|
29
|
+
def validated_actuals(args)
|
40
30
|
args.each do |actual|
|
41
|
-
if actual.kind_of?(
|
31
|
+
if actual.kind_of?(Term) || actual.respond_to?(:attain)
|
42
32
|
next
|
43
33
|
elsif actual.kind_of?(Array)
|
44
34
|
validated_actuals(actual)
|
45
35
|
else
|
36
|
+
prefix = 'Invalid goal argument'
|
46
37
|
actual_display = actual.nil? ? 'nil' : actual.to_s
|
47
38
|
raise StandardError, "#{prefix} '#{actual_display}'"
|
48
39
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'entry'
|
4
|
+
|
5
|
+
module MiniKraken
|
6
|
+
module Core
|
7
|
+
# Representation of a MiniKraken logical variable.
|
8
|
+
# It is a named slot that can be associated with one value at the time.
|
9
|
+
# In relational programming, there is no explicit assignment expression.
|
10
|
+
# A logical variable acquires a value through an algorithm called
|
11
|
+
# 'unification'.
|
12
|
+
class LogVar
|
13
|
+
include Entry # Add expected behaviour for symbol table entries
|
14
|
+
|
15
|
+
# Create a logical variable with given name
|
16
|
+
# @param aName [String] The name of the variable
|
17
|
+
def initialize(aName)
|
18
|
+
init_name(aName)
|
19
|
+
end
|
20
|
+
end # class
|
21
|
+
end # module
|
22
|
+
end # module
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require_relative 'term'
|
5
|
+
|
6
|
+
module MiniKraken
|
7
|
+
module Core
|
8
|
+
# Representation of a reference to a MiniKraken logical variable.
|
9
|
+
class LogVarRef < Term
|
10
|
+
# @return [String] User-friendly name of the variable.
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
# @return [String] Unique internal name of the variable.
|
14
|
+
attr_accessor :i_name
|
15
|
+
|
16
|
+
# Create a reference to a logical variable with given name
|
17
|
+
# @param aName [String] The name of the variable
|
18
|
+
def initialize(aName)
|
19
|
+
super()
|
20
|
+
init_name(aName)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return a text representation of this logical variable reference.
|
24
|
+
# @return [String]
|
25
|
+
def to_s
|
26
|
+
name
|
27
|
+
end
|
28
|
+
|
29
|
+
# Is the related log variable unbound in the given context?
|
30
|
+
# A log var is unbound when there is no association for the variable.
|
31
|
+
# @param aContext [Core::Context]
|
32
|
+
# @return [Boolean] true if log var is unbound
|
33
|
+
def unbound?(aContext)
|
34
|
+
vr = aContext.lookup(name)
|
35
|
+
raise StandardError, "Unknown variable #{name}" unless vr
|
36
|
+
|
37
|
+
bindings = aContext.associations_for(name)
|
38
|
+
bindings.empty? || (bindings.size == 1 && bindings[0].kind_of?(Fusion))
|
39
|
+
end
|
40
|
+
|
41
|
+
# Does the variable have at least one association AND
|
42
|
+
# each of these association refer to at least one unbound variable
|
43
|
+
# or a floating variable?
|
44
|
+
# @param aContext [Core::Context]
|
45
|
+
# @return [Boolean] true if log var is floating
|
46
|
+
def floating?(aContext)
|
47
|
+
vr = aContext.lookup(name)
|
48
|
+
raise StandardError, "Unknown variable #{name}" unless vr
|
49
|
+
|
50
|
+
assocs = aContext.associations_for(name)
|
51
|
+
unless assocs.empty?
|
52
|
+
assocs.none? { |as| as.pinned?(aContext) }
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Is the variable pinned?
|
59
|
+
# In other words, does the referenced variable have a definite value?
|
60
|
+
# @param aContext [Core::Context]
|
61
|
+
# @return [Boolean] true if log var is pinned
|
62
|
+
def pinned?(aContext)
|
63
|
+
return true if @pinned
|
64
|
+
|
65
|
+
vr = aContext.lookup(name)
|
66
|
+
raise StandardError, "Unknown variable #{name}" unless vr
|
67
|
+
|
68
|
+
assocs = aContext.associations_for(name)
|
69
|
+
unless assocs.empty?
|
70
|
+
@pinned = assocs.all? { |as| as.pinned?(aContext) }
|
71
|
+
else
|
72
|
+
false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return the list of variable (i_names) that this term depends on.
|
77
|
+
# For a variable reference, it will return the i_names of its variable
|
78
|
+
# @param ctx [Core::Context]
|
79
|
+
# @return [Set<String>] a set containing the i_name of the variable
|
80
|
+
def dependencies(ctx)
|
81
|
+
@i_name ||= ctx.lookup(name).i_name
|
82
|
+
s = Set.new
|
83
|
+
s << i_name
|
84
|
+
s
|
85
|
+
end
|
86
|
+
|
87
|
+
# Make a copy of self with all the variable reference being
|
88
|
+
# replaced by the corresponding value in the Hash.
|
89
|
+
# @param substitutions [Hash {String => Term}]
|
90
|
+
# @return [Term]
|
91
|
+
def dup_cond(substitutions)
|
92
|
+
key = i_name || name
|
93
|
+
if substitutions.include? key
|
94
|
+
val = substitutions[key]
|
95
|
+
val.kind_of?(Term) ? val.dup_cond(substitutions) : val
|
96
|
+
else
|
97
|
+
dup
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def init_name(aName)
|
104
|
+
@name = aName.dup
|
105
|
+
end
|
106
|
+
end # class
|
107
|
+
end # module
|
108
|
+
end # module
|
@@ -6,17 +6,10 @@ module MiniKraken
|
|
6
6
|
module Core
|
7
7
|
class NullaryRelation < Relation
|
8
8
|
# @param aName [String] Name of the relation.
|
9
|
-
|
10
|
-
|
11
|
-
super(aName, alternateName)
|
9
|
+
def initialize(aName)
|
10
|
+
super(aName, 0)
|
12
11
|
freeze
|
13
12
|
end
|
14
|
-
|
15
|
-
# Number of arguments for the relation.
|
16
|
-
# @return [Integer]
|
17
|
-
def arity
|
18
|
-
0
|
19
|
-
end
|
20
13
|
end # class
|
21
14
|
end # module
|
22
15
|
end # module
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'term'
|
4
|
+
require_relative 'specification'
|
5
|
+
|
6
|
+
module MiniKraken
|
7
|
+
module Core
|
8
|
+
# A specialization of Term class for objects that take arguments.
|
9
|
+
class ParametrizedTerm < Term
|
10
|
+
# @return [Specification] The specification that must be invoked with arguments.
|
11
|
+
attr_reader :specification
|
12
|
+
|
13
|
+
# @return [Array<Term>] The actual aguments of the goal
|
14
|
+
attr_reader :actuals
|
15
|
+
|
16
|
+
# Constructor.
|
17
|
+
# @param theSpecification [Specification] The callable object.
|
18
|
+
# @param theArgs [Array<Term>] The actual aguments
|
19
|
+
def initialize(theSpecification, theArgs)
|
20
|
+
super()
|
21
|
+
@specification = validated_specification(theSpecification)
|
22
|
+
args = specification.check_arity(theArgs)
|
23
|
+
@actuals = validated_actuals(args)
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize_copy(orig)
|
27
|
+
@specification = orig.specification
|
28
|
+
@actuals = []
|
29
|
+
end
|
30
|
+
|
31
|
+
# Make a copy of self with all the variable reference being
|
32
|
+
# replaced by the corresponding value in the Hash.
|
33
|
+
# @param substitutions [Hash {String => Term}]
|
34
|
+
# @return [Term]
|
35
|
+
def dup_cond(substitutions)
|
36
|
+
duplicate = dup
|
37
|
+
updated_actuals = actuals.map { |e| e.dup_cond(substitutions) }
|
38
|
+
duplicate.actuals.concat(updated_actuals)
|
39
|
+
|
40
|
+
duplicate
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
def validated_specification(theSpecification)
|
46
|
+
unless theSpecification.kind_of?(Specification)
|
47
|
+
msg_part1 = 'Expected kind_of Specification,'
|
48
|
+
msg_part2 = "instead of #{theSpecification.class}."
|
49
|
+
raise StandardError, "#{msg_part1} #{msg_part2}"
|
50
|
+
end
|
51
|
+
|
52
|
+
theSpecification
|
53
|
+
end
|
54
|
+
|
55
|
+
# This method should be overridden in subclasses
|
56
|
+
def validated_actuals(args)
|
57
|
+
args
|
58
|
+
end
|
59
|
+
end # class
|
60
|
+
end # module
|
61
|
+
end # module
|