Class: Multimethod::Parameter

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/multimethod/parameter.rb

Overview

Represents a Parameter in a Signature.

A Parameter has a name, type and position.

Parameters may also have a default value or may be a restarg, a parameter that collects all remaining arguments.

Restarg parameters have a lower score than other arguments.

Unlike Ruby parameters, Parameters are typed. Unspecified Parameter types default to Kernel.

Constant Summary collapse

DEFAULT_SCORE_BASE =

The score base used for all Parameters with defaults.

200
DEFAULT_SCORE =

The score used for all Parameters with defaults and no argument.

DEFAULT_SCORE_BASE + 100
RESTARG_SCORE =

The score used for all restarg Parameters.

DEFAULT_SCORE + 100
@@debug =
nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name = nil, type = nil, default = nil, restarg = false) ⇒ Parameter

Initialize a new Parameter.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/multimethod/parameter.rb', line 54

def initialize(name = nil, type = nil, default = nil, restarg = false)
  # $stderr.puts "initialize(#{name.inspect}, #{type}, #{default.inspect}, #{restarg.inspect})"

  @i = nil
  @type = type
  @type_object = nil
  @default = default
  @restarg = restarg
  @verbose = false

  self.name = name # may affect @restarg

  @signature = nil
end

Instance Attribute Details

#defaultObject

The Parameter’s default value expression.



42
43
44
# File 'lib/multimethod/parameter.rb', line 42

def default
  @default
end

#iObject

The Parameter’s offset in the Signature’s parameter list. Parameter 0 is the implied “self” Parameter.



36
37
38
# File 'lib/multimethod/parameter.rb', line 36

def i
  @i
end

#nameObject

The Parameter name.



32
33
34
# File 'lib/multimethod/parameter.rb', line 32

def name
  @name
end

#restargObject

True if the Parameter is a restarg: e.g.: “*args”



45
46
47
# File 'lib/multimethod/parameter.rb', line 45

def restarg
  @restarg
end

#signatureObject

The Parameter’s owning Signature.



48
49
50
# File 'lib/multimethod/parameter.rb', line 48

def signature
  @signature
end

#typeObject

The Paremeter’s type, defaults to Kernel.



39
40
41
# File 'lib/multimethod/parameter.rb', line 39

def type
  @type
end

#verboseObject

Defines level of verbosity during processing.



51
52
53
# File 'lib/multimethod/parameter.rb', line 51

def verbose
  @verbose
end

Class Method Details

.debug=(x) ⇒ Object



18
19
20
# File 'lib/multimethod/parameter.rb', line 18

def self.debug=(x)
  @@debug = x
end

Instance Method Details

#<=>(p) ⇒ Object

Compare two Parameters. Parameter name is insignificant.



85
86
87
88
89
90
91
# File 'lib/multimethod/parameter.rb', line 85

def <=>(p)
  x = type_object <=> p.type_object
  x = @restarg == p.restarg ? 0 : 1 if x == 0
  x = @default == p.default ? 0 : 1 if x == 0
  # $stderr.puts "#{to_s} <=> #{p.to_s} => #{x.inspect}"
  x
end

#all_types(arg_type) ⇒ Object

Returns a list of all parent Modules of an argument type, including itself, in most-specialized to least-specialized order.



207
208
209
# File 'lib/multimethod/parameter.rb', line 207

def all_types(arg_type)
  arg_type.ancestors
end

#scan_string(str, need_names = true) ⇒ Object

Scan a string for a Parameter specification.



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
# File 'lib/multimethod/parameter.rb', line 95

def scan_string(str, need_names = true)
  # @verbose ||= @@debug

  type = nil
  name = nil
  default = nil

  str.sub!(/\A\s+/, '')
  
  $stderr.puts "  str=#{str.inspect}" if @verbose
  
  if md = /\A(\w+(::\w+)*)\s+(\w+)/s.match(str)
    # $stderr.puts "   pre_match=#{md.pre_match.inspect}"
    # $stderr.puts "   md[0]=#{md[0].inspect}"
    str = md.post_match
    type = md[1]
    name = md[3]
  elsif md = /\A(\*?\w+)/s.match(str)
    # $stderr.puts "   pre_match=#{md.pre_match.inspect}"
    # $stderr.puts "   md[0]=#{md[0].inspect}"
    str = md.post_match
    type = nil
    name = md[1]
  else
    raise NameError, "Syntax error in multimethod parameter: expected type and/or name at #{str.inspect}"
  end
  
  $stderr.puts "  type=#{type.inspect}" if @verbose       
  $stderr.puts "  name=#{name.inspect}" if @verbose       
  
  # Parse parameter default.
  if md = /\A\s*=\s*/.match(str)
    str = md.post_match
    
    in_paren = 0
    default = ''
    until str.empty?
      # $stderr.puts "    default: str=#{str.inspect}"
      # $stderr.puts "    default: params=#{parameter_to_s}"
      
      if md = /\A(\s+)/s.match(str)
        str = md.post_match
        default = default + md[1]
      end
      
      if md = /\A("([^"\\]|\\.)*")/s.match(str)
        str = md.post_match
        default = default + md[1]
      elsif md = /\A('([^'\\]|\\.)*')/s.match(str)
        str = md.post_match
        default = default + md[1]
      elsif md = /\A(\()/.match(str)
        str = md.post_match
        in_paren = in_paren + 1
        default = default + md[1]
      elsif in_paren > 0 && md = /\A(\))/s.match(str)
        str = md.post_match
        in_paren = in_paren - 1
        default = default + md[1]
      elsif md = /\A(\))/s.match(str)
        break
      elsif in_paren == 0 && md = /\A,/s.match(str)
        break
      elsif md = /\A(\w+)/s.match(str)
        str = md.post_match
        default = default + md[1]
      elsif md = /\A(.)/s.match(str)
        str = md.post_match
        default = default + md[1] 
      end

      $stderr.puts "  default=#{default.inspect}" if @verbose       
    end
  end
  
  self.name = name unless @name
  self.type = type unless @type
  default = nil if default && default.empty?
  self.default = default unless @default

  str
end

#score(arg) ⇒ Object

Returns the score of this Parameter matching an argument type.

The score is determined by the relative distance of the Parameter to the argument type. A lower distance means a tighter match of this Parameter.

If arg is nil, this Parameter is being matched as a restarg or a parameter default.

Parameters with restargs or unspecfied default arguments score lower, see RESTARG_SCORE, DEFAULT_SCORE.



189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/multimethod/parameter.rb', line 189

def score(arg)
  if @restarg
    score = RESTARG_SCORE
  elsif @default && ! arg
    score = DEFAULT_SCORE
  else
    score = all_types(arg).index(type_object) 
  end

  # $stderr.puts "  score(#{signature.to_s}, #{to_s}, #{arg && arg.name}) => #{score}"

  score
end

#to_ruby_argObject

Return a String representing this Parameter as a Ruby method parameter.



240
241
242
# File 'lib/multimethod/parameter.rb', line 240

def to_ruby_arg
  "#{to_s_name}#{@default ? ' = ' + @default : ''}"
end

#to_sObject

Returns a String representing this Parameter in a Signature string.



234
235
236
# File 'lib/multimethod/parameter.rb', line 234

def to_s
  "#{@type}#{@type ? ' ' : ''}#{to_ruby_arg}"
end

#to_s_nameObject

Return a String representing this Parameter’s name. Restargs will be prefixed with ‘*’.



247
248
249
# File 'lib/multimethod/parameter.rb', line 247

def to_s_name
  (@restarg ? "*" : '') + (@name.to_s || "_arg_#{@i}")
end

#type_objectObject

Resolves type name



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/multimethod/parameter.rb', line 213

def type_object
  unless @type_object
    case @type
    when String
      @type_object = Table.instance.name_to_object(@type, 
                                                   @signature && @signature.mod, 
                                                   @signature && @signature.file, 
                                                   @signature && @signature.line)
    when Module
      @type_object = @type
    when NilClass
      @type_object = Kernel
    else
      raise("Incorrect parameter type #{@type.inspect}")
    end
  end
  @type_object
end