You need to write an abstract interpreter that executes code with type values. Thus, you go to your abstract interpreter through AST and write down messages or known types for each variable. And when you are done, you will select possible types using structural type equivalence (aka duck typing).
PS: in addition to the type of output, you can take a look at “How program history can improve code completion” Romain Robbes explains how to further improve automatic completion in dynamic languages using the latest information and collaborative filtering.
So how does abstract interpretation work for a piece of code, for example
def groups(array,&block)
groups = Hash.new
array.each { |ea|
key = block.call(ea)
groups[key] = [] unless groups.include? key
groups[key] << ea
}
return groups
end
you start with
array = { :messages => [], :types => [] }
block = { :messages => [], :types => [] }
and then
array = { :messages => [], :types => [] }
block = { :messages => [], :types => [] }
groups = { :messages => [], :types => [Hash] }
and then
array = { :messages => [:each], :types => [] }
block = { :messages => [], :types => [] }
groups = { :messages => [], :types => [Hash] }
and then
array = { :messages => [:each], :types => [] }
block = { :messages => [:call], :types => [] }
groups = { :messages => [], :types => [Hash] }
key = { :messages => [], :types => [] }
and then
array = { :messages => [:each], :types => [] }
block = { :messages => [:call], :types => [] }
groups = { :messages => [:include?,:[]], :types => [Hash] }
group_elements = { :messages => [], :types => [Array] }
key = { :messages => [], :types => [] }
and then
array = { :messages => [:each], :types => [] }
block = { :messages => [:call], :types => [] }
groups = { :messages => [:include?,:[]], :types => [Hash] }
group_elements = { :messages => [:<<], :types => [Array] }
key = { :messages => [], :types => [] }
therefore, we can conclude that
array perhaps Enumerableblock, perhaps, Procgroups Hash arraykey -