Class: Argtrace::OutputModule

Inherits:
Object
  • Object
show all
Defined in:
lib/argtrace/typelib.rb

Overview

helper to convert TypeLib into RBS. OutputMoudle acts like Module tree node.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeOutputModule

Returns a new instance of OutputModule.



66
67
68
69
# File 'lib/argtrace/typelib.rb', line 66

def initialize
  @children = {}
  @signatures = []
end

Instance Attribute Details

#actual_moduleObject

Returns the value of attribute actual_module.



64
65
66
# File 'lib/argtrace/typelib.rb', line 64

def actual_module
  @actual_module
end

#childrenObject

Returns the value of attribute children.



64
65
66
# File 'lib/argtrace/typelib.rb', line 64

def children
  @children
end

#nameObject

Returns the value of attribute name.



64
65
66
# File 'lib/argtrace/typelib.rb', line 64

def name
  @name
end

#signaturesObject

Returns the value of attribute signatures.



64
65
66
# File 'lib/argtrace/typelib.rb', line 64

def signatures
  @signatures
end

Instance Method Details

#add_signature(signature) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/argtrace/typelib.rb', line 71

def add_signature(signature)
  # this is root node, so use Kernel as const resolve source.
  @actual_module = Kernel

  constname = class_const_name(signature.defined_class)
  unless constname
    # cannot handle this
    return
  end

  add_signature_inner(constname, signature)
end

#add_signature_inner(name_consts, signature) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/argtrace/typelib.rb', line 105

def add_signature_inner(name_consts, signature)
  if name_consts.empty?
    @signatures << signature
  else
    unless @children.key?(name_consts.first)
      mod = OutputModule.new
      mod.name = name_consts.first
      mod.actual_module = @actual_module.const_get(name_consts.first)
      @children[name_consts.first] = mod
    end
    current_resolving_name = name_consts.shift
    @children[current_resolving_name].add_signature_inner(name_consts, signature)
  end
end

#blocktype_to_rbs(blockparam) ⇒ Object



160
161
162
163
164
165
166
167
168
# File 'lib/argtrace/typelib.rb', line 160

def blocktype_to_rbs(blockparam)
  unless blockparam
    return ""
  end
  params = blockparam.type.params
    .map{|p| type_union_to_rbs(p.type)}
    .join(", ")
  return " { (#{params}) -> untyped }"
end

#class_const_name(klass) ⇒ Object

split class name into consts (e.g. Argtrace::TypeLib to [“Argtrace”, “TypeLib”])



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/argtrace/typelib.rb', line 85

def class_const_name(klass)
  if /^[A-Za-z0-9_:]+$/ =~ klass.to_s
    # this should be normal name
    consts = klass.to_s.split("::")

    # assertion
    resolved_class = consts.inject(Kernel){|mod, const| mod.const_get(const)}
    if klass != resolved_class
      $stderr.puts "----- argtrace bug -----"
      $stderr.puts "#{klass} => #{consts} => #{resolved_class}"
      $stderr.puts "------------------------"
      raise "Failed to resolve class by constant"
    end

    return consts
  else
    return nil
  end
end

#param_to_rbs(param) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/argtrace/typelib.rb', line 170

def param_to_rbs(param)
  case param.mode
  when :req
    return "#{type_union_to_rbs(param.type)} #{param.name}"
  when :opt
    return "?#{type_union_to_rbs(param.type)} #{param.name}"
  when :keyreq
    return "#{param.name}: #{type_union_to_rbs(param.type)}"
  when :key
    return "?#{param.name}: #{type_union_to_rbs(param.type)}"
  when :block
    return nil
  end
end

#sig_to_rbs(indent_level, signature) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/argtrace/typelib.rb', line 147

def sig_to_rbs(indent_level, signature)
  indent = "  " * indent_level
  sig_name = signature.is_singleton_method ? "self.#{signature.method_id}" : signature.method_id
  params = signature.params
    .filter{|p| p.mode != :block}
    .map{|p| param_to_rbs(p)}
    .compact
    .join(", ")
  rettype = type_union_to_rbs(signature.return_type)
  blocktype = blocktype_to_rbs(signature.params.find{|p| p.mode == :block})
  return "#{indent}def #{sig_name} : (#{params})#{blocktype} -> #{rettype}"
end

#to_rbsObject



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

def to_rbs
  # this is root node
  lines = []
  @children.keys.sort.each do |child_name|
    lines << @children[child_name].to_rbs_inner(0)
    lines << ""
  end
  return lines.join("\n")
end

#to_rbs_inner(indent_level) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/argtrace/typelib.rb', line 130

def to_rbs_inner(indent_level)
  indent = "  " * indent_level
  classmod_def = @actual_module.class == Class ? "class" : "module"

  lines = []
  lines << "#{indent}#{classmod_def} #{name}"
  @children.keys.sort.each do |child_name|
    lines << @children[child_name].to_rbs_inner(indent_level + 1)
    lines << ""
  end
  @signatures.each do |sig|
    lines << sig_to_rbs(indent_level + 1, sig)
  end
  lines << "#{indent}end"
  return lines.join("\n")
end

#type_to_rbs(type) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/argtrace/typelib.rb', line 204

def type_to_rbs(type)
  if type.data.is_a?(Symbol)
    return type.data.inspect
  elsif true == type.data || false == type.data || BooleanClass == type.data
    return "bool"
  elsif nil == type.data || NilClass == type.data
    return "nil"
  elsif Array == type.data
    if type.subdata
      case type.subdata
      when true, false, BooleanClass
        elementtype = "bool"
      else
        elementtype = type.subdata.to_s
      end
      return "Array[#{elementtype}]"
    else
      return "Array"
    end
  else
    return type.data.to_s
  end
end

#type_union_to_rbs(typeunion) ⇒ Object



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/argtrace/typelib.rb', line 185

def type_union_to_rbs(typeunion)
  if typeunion.union.size == 0
    return "untyped"
  end
  # TODO: ugly
  if typeunion.union.size == 1 and NilClass == typeunion.union.first.data
    # TODO: I can't distinguish nil and untyped.
    return "untyped"
  end
  if typeunion.union.size == 2 and typeunion.union.any?{|x| NilClass == x.data}
    # type is nil and sometype, so represent it as "sometype?"
    sometype = typeunion.union.find{|x| NilClass != x.data}
    return "#{type_to_rbs(sometype)}?"
  end

  ret = typeunion.union.map{|type| type_to_rbs(type)}.join("|")
  return ret
end