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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

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

Initialize a new Parameter.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/multimethod/parameter.rb', line 49

def initialize(name = nil, type = nil, default = nil, restarg = false)
  # $stderr.puts "initialize(#{name.inspect}, #{type}, #{default.inspect}, #{restarg.inspect})"
  if name
    # Default type if name is specified
    type ||= Kernel
  end

  @i = nil
  @type = type
  @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.



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

def default
  @default
end

#iObject

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



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

def i
  @i
end

#nameObject

The Parameter name.



27
28
29
# File 'lib/multimethod/parameter.rb', line 27

def name
  @name
end

#restargObject

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



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

def restarg
  @restarg
end

#signatureObject

The Parameter’s owning Signature.



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

def signature
  @signature
end

#typeObject

The Paremeter’s type, defaults to Kernel.



34
35
36
# File 'lib/multimethod/parameter.rb', line 34

def type
  @type
end

#verboseObject

Defines level of verbosity during processing.



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

def verbose
  @verbose
end

Instance Method Details

#<=>(p) ⇒ Object

Compare two Parameters. Only type and restarg are significant.



82
83
84
85
86
87
88
# File 'lib/multimethod/parameter.rb', line 82

def <=>(p)
  x = @type <=> p.type 
  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.



193
194
195
# File 'lib/multimethod/parameter.rb', line 193

def all_types(arg_type)
  arg_type.ancestors
end

#scan_string(str, need_names = true) ⇒ Object

Scan a string for a Parameter specification.



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

def scan_string(str, need_names = true)
  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
    end
  end
  
  self.name = name unless @name
  type ||= Kernel
  self.type = type unless @type
  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.

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



175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/multimethod/parameter.rb', line 175

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.



217
218
219
# File 'lib/multimethod/parameter.rb', line 217

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

#to_sObject

Returns a String representing this Parameter in a Signature string.



211
212
213
# File 'lib/multimethod/parameter.rb', line 211

def to_s
  "#{@type} #{to_ruby_arg}"
end

#to_s_nameObject

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



224
225
226
# File 'lib/multimethod/parameter.rb', line 224

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

#type_objectObject

Resolves type by name



199
200
201
202
203
204
205
206
207
# File 'lib/multimethod/parameter.rb', line 199

def type_object
  if @type.kind_of?(String)
    @type = Table.instance.name_to_object(@type, 
                                          @signature.mod, 
                                          @signature.file, 
                                          @signature.line)
  end
  @type
end