Class: Kapusta::Compiler::MacroExpander::Lowering

Inherits:
Object
  • Object
show all
Defined in:
lib/kapusta/compiler/macro_expander.rb

Instance Method Summary collapse

Constructor Details

#initializeLowering

Returns a new instance of Lowering.



171
172
173
# File 'lib/kapusta/compiler/macro_expander.rb', line 171

def initialize
  @gensyms = {}
end

Instance Method Details

#collected_gensymsObject



175
176
177
# File 'lib/kapusta/compiler/macro_expander.rb', line 175

def collected_gensyms
  @gensyms.map { |prefix, sym| [sym, prefix] }
end

#copy_position(target, source) ⇒ Object



200
201
202
203
204
205
206
# File 'lib/kapusta/compiler/macro_expander.rb', line 200

def copy_position(target, source)
  return target unless target.respond_to?(:line=) && source.respond_to?(:line)

  target.line ||= source.line
  target.column ||= source.column
  target
end

#gensym_local_for(prefix) ⇒ Object



282
283
284
# File 'lib/kapusta/compiler/macro_expander.rb', line 282

def gensym_local_for(prefix)
  @gensyms[prefix] ||= MacroExpander.fresh_local_gensym(prefix)
end

#lower(form) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/kapusta/compiler/macro_expander.rb', line 179

def lower(form)
  case form
  when Quasiquote then copy_position(lower_quasi(form.form), form)
  when Unquote, UnquoteSplice
    raise Error, Kapusta::Errors.format(:unquote_outside_quasiquote)
  when AutoGensym
    raise Error, Kapusta::Errors.format(:auto_gensym_outside_quasiquote, name: form.name)
  when List then copy_position(List.new(form.items.map { |item| lower(item) }), form)
  when Vec then copy_position(Vec.new(form.items.map { |item| lower(item) }), form)
  when HashLit
    copy_position(
      HashLit.new(form.entries.map do |entry|
        entry.is_a?(Array) ? [lower(entry[0]), lower(entry[1])] : entry
      end),
      form
    )
  else
    form
  end
end

#lower_quasi(form) ⇒ Object



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/kapusta/compiler/macro_expander.rb', line 208

def lower_quasi(form)
  case form
  when AutoGensym then gensym_local_for(form.name)
  when Sym then List.new([Sym.new('quasi-sym'), form.name])
  when List then lower_quasi_list(form)
  when Vec then lower_quasi_vec(form)
  when HashLit then lower_quasi_hash(form)
  when Unquote then lower(form.form)
  when UnquoteSplice
    raise Error, Kapusta::Errors.format(:unquote_splice_outside_list)
  when Quasiquote
    raise Error, Kapusta::Errors.format(:nested_quasiquote)
  else
    form
  end
end

#lower_quasi_hash(hash) ⇒ Object



249
250
251
252
253
254
255
256
257
258
# File 'lib/kapusta/compiler/macro_expander.rb', line 249

def lower_quasi_hash(hash)
  parts = []
  hash.entries.each do |entry|
    next unless entry.is_a?(Array)

    key, value = entry
    parts << lower_quasi(key) << lower_quasi(value)
  end
  List.new([Sym.new('quasi-hash'), *parts])
end

#lower_quasi_item(item) ⇒ Object



260
261
262
263
264
265
266
267
# File 'lib/kapusta/compiler/macro_expander.rb', line 260

def lower_quasi_item(item)
  if item.is_a?(Unquote) && unpack_call?(item.form)
    inner = lower(item.form.items[1])
    List.new([Sym.new('.'), inner, 0])
  else
    lower_quasi(item)
  end
end

#lower_quasi_list(list) ⇒ Object



225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/kapusta/compiler/macro_expander.rb', line 225

def lower_quasi_list(list)
  items = list.items
  return List.new([Sym.new('quasi-list')]) if items.empty?

  if (tail_expr = splice_tail(items))
    head_items = items[0...-1].map { |item| lower_quasi(item) }
    return List.new([Sym.new('quasi-list-tail'), Vec.new(head_items), tail_expr])
  end

  lowered_items = items.map { |item| lower_quasi_item(item) }
  List.new([Sym.new('quasi-list'), *lowered_items])
end

#lower_quasi_vec(vec) ⇒ Object



238
239
240
241
242
243
244
245
246
247
# File 'lib/kapusta/compiler/macro_expander.rb', line 238

def lower_quasi_vec(vec)
  items = vec.items
  if (tail_expr = splice_tail(items))
    head_items = items[0...-1].map { |item| lower_quasi(item) }
    return List.new([Sym.new('quasi-vec-tail'), Vec.new(head_items), tail_expr])
  end

  lowered_items = items.map { |item| lower_quasi_item(item) }
  List.new([Sym.new('quasi-vec'), *lowered_items])
end

#splice_tail(items) ⇒ Object



269
270
271
272
273
274
275
276
# File 'lib/kapusta/compiler/macro_expander.rb', line 269

def splice_tail(items)
  last = items.last
  return unless last
  return lower(last.form) if last.is_a?(UnquoteSplice)
  return lower(last.form.items[1]) if last.is_a?(Unquote) && unpack_call?(last.form)

  nil
end

#unpack_call?(form) ⇒ Boolean

Returns:

  • (Boolean)


278
279
280
# File 'lib/kapusta/compiler/macro_expander.rb', line 278

def unpack_call?(form)
  form.is_a?(List) && form.head.is_a?(Sym) && form.head.name == 'unpack'
end