Class: Duby::Typer::Simple

Inherits:
BaseTyper show all
Defined in:
lib/duby/typer.rb

Direct Known Subclasses

JVM

Constant Summary

Constants included from Duby

Duby::TransformError, VERSION

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from BaseTyper

#log, #to_s

Methods included from Duby

compile, parse, run, typer_plugins

Constructor Details

#initialize(self_type) ⇒ Simple

Returns a new instance of Simple.



35
36
37
38
39
40
41
42
43
44
# File 'lib/duby/typer.rb', line 35

def initialize(self_type)
  @known_types = {}
  
  @known_types["self"] = type_reference(self_type)
  @known_types["fixnum"] = type_reference("fixnum")
  @known_types["float"] = type_reference("float")
  @known_types["string"] = type_reference("string")
  @known_types["boolean"] = type_reference("boolean")
  @errors = []
end

Instance Attribute Details

#errorsObject

Returns the value of attribute errors.



33
34
35
# File 'lib/duby/typer.rb', line 33

def errors
  @errors
end

#known_typesObject

Returns the value of attribute known_types.



33
34
35
# File 'lib/duby/typer.rb', line 33

def known_types
  @known_types
end

Instance Method Details

#alias_type(short, long) ⇒ Object



244
245
246
247
# File 'lib/duby/typer.rb', line 244

def alias_type(short, long)
  @known_types[type_reference(short, false, false)] = type_reference(long, false, false)
  @known_types[type_reference(short, false, true)] = type_reference(long, false, true)
end

#boolean_typeObject



70
71
72
# File 'lib/duby/typer.rb', line 70

def boolean_type
  known_types["boolean"]
end

#cycle(count) ⇒ Object



206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/duby/typer.rb', line 206

def cycle(count)
  @cycling = true
  count.times do |i|
    begin
      log "[Cycle #{i}]: Started..."
      yield i
    ensure
      log "[Cycle #{i}]: Complete!"
    end
  end
ensure
  @cycling = false
end

#cycling=(c) ⇒ Object



202
203
204
# File 'lib/duby/typer.rb', line 202

def cycling=(c)
  @cycling = c
end

#cycling?Boolean

Returns:

  • (Boolean)


198
199
200
# File 'lib/duby/typer.rb', line 198

def cycling?
  @cycling
end

#default_typeObject



54
55
56
# File 'lib/duby/typer.rb', line 54

def default_type
  nil
end

#defer(node) ⇒ Object



279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/duby/typer.rb', line 279

def defer(node)
  if @error_next
    log "Marking #{node} as an error"
    @error_next = false
    error(node)
  else
    return if deferred_nodes.include? node
    log "Deferring inference for #{node}"
  
    deferred_nodes << node
  end
end

#deferred_nodesObject



249
250
251
# File 'lib/duby/typer.rb', line 249

def deferred_nodes
  @deferred_nodes ||= []
end

#define_type(name, superclass, interfaces) ⇒ Object



82
83
84
85
86
87
88
89
90
91
# File 'lib/duby/typer.rb', line 82

def define_type(name, superclass, interfaces)
  log "New type defined: '#{name}' < '#{superclass}'"
  known_types[name] = type_definition(name, superclass, interfaces)
  
  old_self, known_types["self"] = known_types["self"], known_types[name]
  yield
  known_types["self"] = old_self
  
  known_types[name]
end

#error(node, error_or_msg = nil, backtrace = nil) ⇒ Object



264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/duby/typer.rb', line 264

def error(node, error_or_msg=nil, backtrace=nil)
  if error_or_msg.kind_of? InferenceError
    error = error_or_msg
  elsif error_or_msg
    error = InferenceError.new(error_or_msg, node)
    error.set_backtrace(backtrace) if backtrace
  else
    error = InferenceError.new("Unable to infer type.", node)
  end
  @errors << error
  node.resolve_if(self) do
    AST.error_type
  end
end

#field_type(cls, name) ⇒ Object



136
137
138
# File 'lib/duby/typer.rb', line 136

def field_type(cls, name)
  field_type_hash(cls)[name]
end

#field_type_hash(cls) ⇒ Object



120
121
122
# File 'lib/duby/typer.rb', line 120

def field_type_hash(cls)
  field_types[cls] ||= {}
end

#field_typesObject



116
117
118
# File 'lib/duby/typer.rb', line 116

def field_types
  @field_types ||= {}
end

#fixnum_typeObject



58
59
60
# File 'lib/duby/typer.rb', line 58

def fixnum_type
  known_types["fixnum"]
end

#float_typeObject



62
63
64
# File 'lib/duby/typer.rb', line 62

def float_type
  known_types["float"]
end

#get_method_type_hash(target_type, name, parameter_types) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
# File 'lib/duby/typer.rb', line 224

def get_method_type_hash(target_type, name, parameter_types)
  method_types[target_type] ||= {}
  method_types[target_type][name] ||= {}
  method_types[target_type][name][parameter_types.size] ||= {}
  
  current = method_types[target_type][name][parameter_types.size]

  parameter_types.each {|type| current[type] ||= {}; current = current[type]}

  current
end

#infer(node) ⇒ Object



253
254
255
256
257
258
259
260
261
262
# File 'lib/duby/typer.rb', line 253

def infer(node)
  begin
    node.infer(self)
  rescue InferenceError => ex
    ex.node ||= node
    error(node, ex)
  rescue Exception => ex
    error(node, ex.message, ex.backtrace)
  end
end

#infer_signature(method_def) ⇒ Object



124
125
# File 'lib/duby/typer.rb', line 124

def infer_signature(method_def)
end

#learn_field_type(cls, name, type) ⇒ Object



127
128
129
130
131
132
133
134
# File 'lib/duby/typer.rb', line 127

def learn_field_type(cls, name, type)
  log "Learned field type under #{cls} : #{name} = #{type}"

  # TODO check for compatibility?
  field_type_hash(cls)[name] ||= known_types[type] || type

  type
end

#learn_local_type(scope, name, type) ⇒ Object



93
94
95
96
97
98
99
100
# File 'lib/duby/typer.rb', line 93

def learn_local_type(scope, name, type)
  log "Learned local type under #{scope} : #{name} = #{type}"

  # TODO check for compatibility?
  local_type_hash(scope)[name] ||= known_types[type] || type

  type
end

#learn_method_type(target_type, name, parameter_types, type, exceptions) ⇒ Object



140
141
142
143
144
145
146
147
148
# File 'lib/duby/typer.rb', line 140

def learn_method_type(target_type, name, parameter_types, type, exceptions)
  log "Learned method #{name} (#{parameter_types}) on #{target_type} = #{type}"

  get_method_type_hash(target_type, name, parameter_types)[:type] = known_types[type] || type

  # if it's any args are imported types, also add a mapping for the expanded name
  imported_types = parameter_types.map {|param| known_types[param] || param}
  get_method_type_hash(target_type, name, imported_types)[:type] = type
end

#local_type(scope, name) ⇒ Object



102
103
104
105
106
# File 'lib/duby/typer.rb', line 102

def local_type(scope, name)
  log "Retrieved local type in #{scope} : #{name} = #{local_type_hash(scope)[name]}"

  local_type_hash(scope)[name]
end

#local_type_hash(scope) ⇒ Object



112
113
114
# File 'lib/duby/typer.rb', line 112

def local_type_hash(scope)
  local_types[scope] ||= {}
end

#local_typesObject



108
109
110
# File 'lib/duby/typer.rb', line 108

def local_types
  @local_types ||= {}
end

#method_type(target_type, name, parameter_types) ⇒ Object



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
180
181
182
183
# File 'lib/duby/typer.rb', line 150

def method_type(target_type, name, parameter_types)
  if (target_type && target_type.error?) ||
      parameter_types.any? {|t| t && t.error?}
    return AST.error_type
  end
  constructor = (name == 'new' && target_type && target_type.meta?)

  if constructor
    # constructor handled different from other methods
    simple_type = get_method_type_hash(target_type.unmeta, 'initialize', parameter_types)[:type]
  else
    simple_type = get_method_type_hash(target_type, name, parameter_types)[:type]
  end


  if !simple_type
    log "Method type for \"#{name}\" #{parameter_types} on #{target_type} not found."

    # allow plugins a go if we're in the inference phase
    simple_type = plugins do |plugin|
      plugin.method_type(self, target_type, name, parameter_types)
    end
  end

  return nil unless simple_type

  if constructor
    log "Method type for \"#{name}\" #{parameter_types} on #{target_type} = #{target_type}"
    target_type.unmeta
  else
    log "Method type for \"#{name}\" #{parameter_types} on #{target_type} = #{simple_type}"
    simple_type
  end
end

#method_typesObject



220
221
222
# File 'lib/duby/typer.rb', line 220

def method_types
  @method_types ||= {}
end

#nameObject



46
47
48
# File 'lib/duby/typer.rb', line 46

def name
  "Simple"
end

#no_typeObject



78
79
80
# File 'lib/duby/typer.rb', line 78

def no_type
  AST::TypeReference::NoType
end

#null_typeObject



74
75
76
# File 'lib/duby/typer.rb', line 74

def null_type
  AST::TypeReference::NullType
end

#pluginsObject



185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/duby/typer.rb', line 185

def plugins
  if cycling?
    Duby.typer_plugins.each do |plugin|
      log "Invoking plugin: #{plugin}"

      result = yield plugin
      return result if result
    end
  end
  
  nil
end

#resolve(raise = false) ⇒ Object



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/duby/typer.rb', line 292

def resolve(raise = false)
  count = deferred_nodes.size + 1
  
  log "Entering type inference cycle"
  
  retried = false
  cycle(count) do |i|
    old_deferred = @deferred_nodes
    @deferred_nodes = deferred_nodes.select do |node|
      type = infer(node)

      log "[Cycle #{i}]: Inferred type for #{node}: #{type || 'FAILED'}"

      type == default_type
    end
    
    if @deferred_nodes.size == 0
      log "[Cycle #{i}]:  Resolved all types, exiting"
      break
    elsif old_deferred == @deferred_nodes
      if @error_next || retried
        log "[Cycle #{i}]: Made no progress, bailing out"
        break
      else
        # Retry this iteration, and mark the first deferred
        # type as an error.
        retried = true
        @error_next = true
        redo
      end
    end
    retried = false
  end

  # done with n sweeps, if any remain raise errors
  error_nodes = @errors.map {|e| e.node} + deferred_nodes
  if raise && !error_nodes.empty?
    msg = "Could not infer typing for nodes:"
    error_nodes.map do |e|
      msg << "\n  "
      msg << "#{e} at line #{e.line_number} (child of #{e.parent})"
    end
    raise InferenceError.new(msg)
  end
end

#self_typeObject



50
51
52
# File 'lib/duby/typer.rb', line 50

def self_type
  known_types["self"]
end

#string_typeObject



66
67
68
# File 'lib/duby/typer.rb', line 66

def string_type
  known_types["string"]
end

#type_definition(name, superclass, interfaces) ⇒ Object



240
241
242
# File 'lib/duby/typer.rb', line 240

def type_definition(name, superclass, interfaces)
  AST::TypeDefinition.new(name, AST::TypeReference.new(superclass), interfaces)
end

#type_reference(name, array = false, meta = false) ⇒ Object



236
237
238
# File 'lib/duby/typer.rb', line 236

def type_reference(name, array=false, meta=false)
  AST::TypeReference.new(name, array, meta)
end