Class: Porolog::Predicate

Inherits:
Object show all
Defined in:
lib/porolog/predicate.rb,
lib/porolog/predicate/builtin.rb

Overview

A Porolog::Predicate corresponds to a Prolog predicate. It contains rules (Porolog::Rule) and belongs to a Porolog::Scope. When provided with arguments, it produces a Porolog::Arguments.

Defined Under Namespace

Modules: Builtin Classes: Error, NameError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, scope_name = Predicate.scope.name, builtin: false) ⇒ Predicate

Initializes a Porolog::Predicate and registers it by its name.

Parameters:

  • name (#to_sym)

    the input object to read from

  • scope_name (defaults to: Predicate.scope.name)

    the name of the scope in which to register the Predicate; if omitted, defaults to the name of the current scope

Raises:



74
75
76
77
78
79
80
81
82
83
# File 'lib/porolog/predicate.rb', line 74

def initialize(name, scope_name = Predicate.scope.name, builtin: false)
  @name    = name.to_sym
  @rules   = []
  @builtin = builtin
  
  raise NameError, "Cannot name a predicate 'predicate'" if @name == :predicate
  
  scope = Scope[scope_name] || Scope.new(scope_name)
  scope[@name] = self
end

Instance Attribute Details

#nameObject (readonly)

Name of the Predicate.



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/porolog/predicate.rb', line 22

class Predicate
  
  # Error class for rescuing or detecting any Predicate error.
  class Error          < PorologError   ; end
  # Error class indicating an error with the name of a Predicate.
  class NameError      < Error          ; end
  
  attr_reader :name, :rules
  
  # A unique value used to verify instantiations.
  UNIQUE_VALUE = Object.new.freeze
  private_constant :UNIQUE_VALUE
  
  # Returns the current scope, or sets the current scope if a paramter is provided
  # (creating the new scope if necessary).
  # @param scope_name [Object] the name (or otherwise object) used to register a scope.
  # @return [Porolog::Scope] the current Scope.
  def self.scope(scope_name = nil)
    if scope_name
      @@current_scope = Scope[scope_name] || Scope.new(scope_name)
    else
      @@current_scope
    end
  end
  
  # Sets the current scope (creating the new scope if necessary.
  # @param scope_name [Object] the name (or otherwise object) used to register a scope.
  # @return [Porolog::Scope] the current Scope.
  def self.scope=(scope_name)
    @@current_scope = Scope[scope_name] || Scope.new(scope_name)  if scope_name
    @@current_scope
  end
  
  # Resets the current scope to default and deregisters builtin predicates.
  # @return [Porolog::Scope] the current Scope.
  def self.reset
    scope(:default)
    @builtin_predicate_ids = {}
  end
  
  # Looks up a Predicate in the current scope by its name.
  # @param name [Object] the name (or otherwise object) used to register a scope.
  # @return [Porolog::Predicate] the Predicate with the given name.
  def self.[](name)
    @@current_scope[name]
  end
  
  reset
  
  # Initializes a Porolog::Predicate and registers it by its name.
  # @param name [#to_sym] the input object to read from
  # @param scope_name the name of the scope in which to register the Predicate; if omitted, defaults to the name of the current scope
  def initialize(name, scope_name = Predicate.scope.name, builtin: false)
    @name    = name.to_sym
    @rules   = []
    @builtin = builtin
    
    raise NameError, "Cannot name a predicate 'predicate'" if @name == :predicate
    
    scope = Scope[scope_name] || Scope.new(scope_name)
    scope[@name] = self
  end
  
  # Creates a new Predicate, or returns an existing Predicate if one already exists with the given name in the current scope.
  # @param args the args to be passed to initialize, first of which is the name of the predicate.
  # @return [Porolog::Predicate] a new or existing Predicate.
  def self.new(*args, **)
    scope[args.first.to_sym] || super
  end
  
  # Create Arguments for the Predicate.
  # Provides the syntax option:
  #     p.(x,y,z)
  # for
  #     p.arguments(x,y,z)
  # @return [Porolog::Arguments] Arguments of the Predicate with the given arguments.
  def call(*args, &block)
    Arguments.new(self, args, &block)
  end
  
  # Create Arguments for the Predicate.
  # @param args the args of the Predicate.
  # @param block [Proc,nil] the block to be called when satisfying the Predicate.
  # @return [Porolog::Arguments] Arguments of the Predicate with the given arguments.
  def arguments(*args, &block)
    Arguments.new(self, args, &block)
  end
  
  # Add a Rule to the Predicate.
  # @param rule [Object] a rule to add to the Predicate.
  # @return [Porolog::Predicate] the Predicate.
  def <<(rule)
    @rules << rule
    self
  end
  
  # A pretty print String of the Predicate.
  # @return [String] the Predicate as a String.
  def inspect
    if @rules.size == 1
      "#{@name}:-#{@rules.first.inspect}"
    else
      @rules.each_with_object(["#{@name}:-"]) do |rule, lines|
        lines << rule.inspect
      end.join("\n")
    end
  end
  
  # Satisfy the Predicate within the supplied Goal.
  # Satisfy of each rule of the Predicate is called with the Goal and success block.
  # @param goal [Porolog::Goal] the Goal within which to satisfy the Predicate.
  # @param block [Proc] the block to be called each time a Rule of the Predicate is satisfied.
  # @return [Boolean] whether any Rule was satisfied.
  def satisfy(goal, &block)
    if builtin?
      satisfy_builtin(goal, &block)
    else
      satisfied = false
      @rules.each do |rule|
        rule.satisfy(goal) do |subgoal|
          satisfied = true
          block.call(subgoal)
        end
        break if goal.terminated?
      end
      satisfied
    end
  end
  
  # @return [Boolean] whether the Predicate is a builtin predicate.
  def builtin?
    @builtin
  end
  
  # Satisfy the builtin Predicate within the supplied Goal.
  # Call the builtin Predicate method with the Goal and success block.
  # The arguments and block are extracted from the Goal's Arguments.
  # @param goal [Porolog::Goal] the Goal within which to satisfy the Predicate.
  # @param block [Proc] the block to be called each time a Rule of the Predicate is satisfied.
  # @return [Boolean] whether any Rule was satisfied.
  def satisfy_builtin(goal, &block)
    predicate = goal.arguments.predicate.name
    arguments = goal.arguments.arguments
    arg_block = goal.arguments.block
    
    Predicate.call_builtin(predicate, goal, block, *arguments, &arg_block)
  end
  
  # Call a builtin predicate
  # @param predicate [Symbol] the name of the predicate to call.
  # @param args [Array<Object>] arguments for the predicate call.
  # @param arg_block [Proc] the block provided in the Arguments of the Predicate.
  # @return [Boolean] whether the predicate was satisfied.
  def self.call_builtin(predicate, *args, &arg_block)
    Porolog::Predicate::Builtin.instance_method(predicate).bind(self).call(*args, &arg_block)
  end
  
end

#rulesObject (readonly)

Rules of the Predicate.



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/porolog/predicate.rb', line 22

class Predicate
  
  # Error class for rescuing or detecting any Predicate error.
  class Error          < PorologError   ; end
  # Error class indicating an error with the name of a Predicate.
  class NameError      < Error          ; end
  
  attr_reader :name, :rules
  
  # A unique value used to verify instantiations.
  UNIQUE_VALUE = Object.new.freeze
  private_constant :UNIQUE_VALUE
  
  # Returns the current scope, or sets the current scope if a paramter is provided
  # (creating the new scope if necessary).
  # @param scope_name [Object] the name (or otherwise object) used to register a scope.
  # @return [Porolog::Scope] the current Scope.
  def self.scope(scope_name = nil)
    if scope_name
      @@current_scope = Scope[scope_name] || Scope.new(scope_name)
    else
      @@current_scope
    end
  end
  
  # Sets the current scope (creating the new scope if necessary.
  # @param scope_name [Object] the name (or otherwise object) used to register a scope.
  # @return [Porolog::Scope] the current Scope.
  def self.scope=(scope_name)
    @@current_scope = Scope[scope_name] || Scope.new(scope_name)  if scope_name
    @@current_scope
  end
  
  # Resets the current scope to default and deregisters builtin predicates.
  # @return [Porolog::Scope] the current Scope.
  def self.reset
    scope(:default)
    @builtin_predicate_ids = {}
  end
  
  # Looks up a Predicate in the current scope by its name.
  # @param name [Object] the name (or otherwise object) used to register a scope.
  # @return [Porolog::Predicate] the Predicate with the given name.
  def self.[](name)
    @@current_scope[name]
  end
  
  reset
  
  # Initializes a Porolog::Predicate and registers it by its name.
  # @param name [#to_sym] the input object to read from
  # @param scope_name the name of the scope in which to register the Predicate; if omitted, defaults to the name of the current scope
  def initialize(name, scope_name = Predicate.scope.name, builtin: false)
    @name    = name.to_sym
    @rules   = []
    @builtin = builtin
    
    raise NameError, "Cannot name a predicate 'predicate'" if @name == :predicate
    
    scope = Scope[scope_name] || Scope.new(scope_name)
    scope[@name] = self
  end
  
  # Creates a new Predicate, or returns an existing Predicate if one already exists with the given name in the current scope.
  # @param args the args to be passed to initialize, first of which is the name of the predicate.
  # @return [Porolog::Predicate] a new or existing Predicate.
  def self.new(*args, **)
    scope[args.first.to_sym] || super
  end
  
  # Create Arguments for the Predicate.
  # Provides the syntax option:
  #     p.(x,y,z)
  # for
  #     p.arguments(x,y,z)
  # @return [Porolog::Arguments] Arguments of the Predicate with the given arguments.
  def call(*args, &block)
    Arguments.new(self, args, &block)
  end
  
  # Create Arguments for the Predicate.
  # @param args the args of the Predicate.
  # @param block [Proc,nil] the block to be called when satisfying the Predicate.
  # @return [Porolog::Arguments] Arguments of the Predicate with the given arguments.
  def arguments(*args, &block)
    Arguments.new(self, args, &block)
  end
  
  # Add a Rule to the Predicate.
  # @param rule [Object] a rule to add to the Predicate.
  # @return [Porolog::Predicate] the Predicate.
  def <<(rule)
    @rules << rule
    self
  end
  
  # A pretty print String of the Predicate.
  # @return [String] the Predicate as a String.
  def inspect
    if @rules.size == 1
      "#{@name}:-#{@rules.first.inspect}"
    else
      @rules.each_with_object(["#{@name}:-"]) do |rule, lines|
        lines << rule.inspect
      end.join("\n")
    end
  end
  
  # Satisfy the Predicate within the supplied Goal.
  # Satisfy of each rule of the Predicate is called with the Goal and success block.
  # @param goal [Porolog::Goal] the Goal within which to satisfy the Predicate.
  # @param block [Proc] the block to be called each time a Rule of the Predicate is satisfied.
  # @return [Boolean] whether any Rule was satisfied.
  def satisfy(goal, &block)
    if builtin?
      satisfy_builtin(goal, &block)
    else
      satisfied = false
      @rules.each do |rule|
        rule.satisfy(goal) do |subgoal|
          satisfied = true
          block.call(subgoal)
        end
        break if goal.terminated?
      end
      satisfied
    end
  end
  
  # @return [Boolean] whether the Predicate is a builtin predicate.
  def builtin?
    @builtin
  end
  
  # Satisfy the builtin Predicate within the supplied Goal.
  # Call the builtin Predicate method with the Goal and success block.
  # The arguments and block are extracted from the Goal's Arguments.
  # @param goal [Porolog::Goal] the Goal within which to satisfy the Predicate.
  # @param block [Proc] the block to be called each time a Rule of the Predicate is satisfied.
  # @return [Boolean] whether any Rule was satisfied.
  def satisfy_builtin(goal, &block)
    predicate = goal.arguments.predicate.name
    arguments = goal.arguments.arguments
    arg_block = goal.arguments.block
    
    Predicate.call_builtin(predicate, goal, block, *arguments, &arg_block)
  end
  
  # Call a builtin predicate
  # @param predicate [Symbol] the name of the predicate to call.
  # @param args [Array<Object>] arguments for the predicate call.
  # @param arg_block [Proc] the block provided in the Arguments of the Predicate.
  # @return [Boolean] whether the predicate was satisfied.
  def self.call_builtin(predicate, *args, &arg_block)
    Porolog::Predicate::Builtin.instance_method(predicate).bind(self).call(*args, &arg_block)
  end
  
end

Class Method Details

.[](name) ⇒ Porolog::Predicate

Looks up a Predicate in the current scope by its name.

Parameters:

  • name (Object)

    the name (or otherwise object) used to register a scope.

Returns:



65
66
67
# File 'lib/porolog/predicate.rb', line 65

def self.[](name)
  @@current_scope[name]
end

.call_builtin(predicate, *args, &arg_block) ⇒ Boolean

Call a builtin predicate

Parameters:

  • predicate (Symbol)

    the name of the predicate to call.

  • args (Array<Object>)

    arguments for the predicate call.

  • arg_block (Proc)

    the block provided in the Arguments of the Predicate.

Returns:

  • (Boolean)

    whether the predicate was satisfied.



175
176
177
# File 'lib/porolog/predicate.rb', line 175

def self.call_builtin(predicate, *args, &arg_block)
  Porolog::Predicate::Builtin.instance_method(predicate).bind(self).call(*args, &arg_block)
end

.new(*args) ⇒ Porolog::Predicate

Creates a new Predicate, or returns an existing Predicate if one already exists with the given name in the current scope.

Parameters:

  • args

    the args to be passed to initialize, first of which is the name of the predicate.

Returns:



88
89
90
# File 'lib/porolog/predicate.rb', line 88

def self.new(*args, **)
  scope[args.first.to_sym] || super
end

.resetPorolog::Scope

Resets the current scope to default and deregisters builtin predicates.

Returns:



57
58
59
60
# File 'lib/porolog/predicate.rb', line 57

def self.reset
  scope(:default)
  @builtin_predicate_ids = {}
end

.scope(scope_name = nil) ⇒ Porolog::Scope

Returns the current scope, or sets the current scope if a paramter is provided (creating the new scope if necessary).

Parameters:

  • scope_name (Object) (defaults to: nil)

    the name (or otherwise object) used to register a scope.

Returns:



39
40
41
42
43
44
45
# File 'lib/porolog/predicate.rb', line 39

def self.scope(scope_name = nil)
  if scope_name
    @@current_scope = Scope[scope_name] || Scope.new(scope_name)
  else
    @@current_scope
  end
end

.scope=(scope_name) ⇒ Porolog::Scope

Sets the current scope (creating the new scope if necessary.

Parameters:

  • scope_name (Object)

    the name (or otherwise object) used to register a scope.

Returns:



50
51
52
53
# File 'lib/porolog/predicate.rb', line 50

def self.scope=(scope_name)
  @@current_scope = Scope[scope_name] || Scope.new(scope_name)  if scope_name
  @@current_scope
end

Instance Method Details

#<<(rule) ⇒ Porolog::Predicate

Add a Rule to the Predicate.

Parameters:

  • rule (Object)

    a rule to add to the Predicate.

Returns:



113
114
115
116
# File 'lib/porolog/predicate.rb', line 113

def <<(rule)
  @rules << rule
  self
end

#arguments(*args, &block) ⇒ Porolog::Arguments

Create Arguments for the Predicate.

Parameters:

  • args

    the args of the Predicate.

  • block (Proc, nil)

    the block to be called when satisfying the Predicate.

Returns:



106
107
108
# File 'lib/porolog/predicate.rb', line 106

def arguments(*args, &block)
  Arguments.new(self, args, &block)
end

#builtin?Boolean

Returns whether the Predicate is a builtin predicate.

Returns:

  • (Boolean)

    whether the Predicate is a builtin predicate.



152
153
154
# File 'lib/porolog/predicate.rb', line 152

def builtin?
  @builtin
end

#call(*args, &block) ⇒ Porolog::Arguments

Create Arguments for the Predicate. Provides the syntax option:

p.(x,y,z)

for

p.arguments(x,y,z)

Returns:



98
99
100
# File 'lib/porolog/predicate.rb', line 98

def call(*args, &block)
  Arguments.new(self, args, &block)
end

#inspectString

A pretty print String of the Predicate.

Returns:

  • (String)

    the Predicate as a String.



120
121
122
123
124
125
126
127
128
# File 'lib/porolog/predicate.rb', line 120

def inspect
  if @rules.size == 1
    "#{@name}:-#{@rules.first.inspect}"
  else
    @rules.each_with_object(["#{@name}:-"]) do |rule, lines|
      lines << rule.inspect
    end.join("\n")
  end
end

#satisfy(goal, &block) ⇒ Boolean

Satisfy the Predicate within the supplied Goal. Satisfy of each rule of the Predicate is called with the Goal and success block.

Parameters:

  • goal (Porolog::Goal)

    the Goal within which to satisfy the Predicate.

  • block (Proc)

    the block to be called each time a Rule of the Predicate is satisfied.

Returns:

  • (Boolean)

    whether any Rule was satisfied.



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/porolog/predicate.rb', line 135

def satisfy(goal, &block)
  if builtin?
    satisfy_builtin(goal, &block)
  else
    satisfied = false
    @rules.each do |rule|
      rule.satisfy(goal) do |subgoal|
        satisfied = true
        block.call(subgoal)
      end
      break if goal.terminated?
    end
    satisfied
  end
end

#satisfy_builtin(goal, &block) ⇒ Boolean

Satisfy the builtin Predicate within the supplied Goal. Call the builtin Predicate method with the Goal and success block. The arguments and block are extracted from the Goal’s Arguments.

Parameters:

  • goal (Porolog::Goal)

    the Goal within which to satisfy the Predicate.

  • block (Proc)

    the block to be called each time a Rule of the Predicate is satisfied.

Returns:

  • (Boolean)

    whether any Rule was satisfied.



162
163
164
165
166
167
168
# File 'lib/porolog/predicate.rb', line 162

def satisfy_builtin(goal, &block)
  predicate = goal.arguments.predicate.name
  arguments = goal.arguments.arguments
  arg_block = goal.arguments.block
  
  Predicate.call_builtin(predicate, goal, block, *arguments, &arg_block)
end