Class: Mirah::AST::MacroDefinition

Inherits:
Node
  • Object
show all
Includes:
Named, Scoped
Defined in:
lib/mirah/ast/intrinsics.rb

Instance Attribute Summary collapse

Attributes included from Named

#name

Attributes inherited from Node

#children, #inferred_type, #newline, #parent, #position

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Scoped

#containing_scope, #scope

Methods included from Named

#string_value, #to_s, #validate_name

Methods inherited from Node

#<<, ===, #[], #[]=, #_dump, _load, #_set_parent, child, child_name, #child_nodes, #each, #empty?, #expr?, #inferred_type!, #initialize_copy, #insert, #inspect, #inspect_children, #line_number, #log, #precompile, #resolve_if, #resolved!, #resolved?, #simple_name, #string_value, #temp, #to_s, #top_level?, #validate_child, #validate_children

Constructor Details

#initialize(parent, line_number, name, &block) ⇒ MacroDefinition

Returns a new instance of MacroDefinition.



263
264
265
266
# File 'lib/mirah/ast/intrinsics.rb', line 263

def initialize(parent, line_number, name, &block)
  super(parent, line_number, &block)
  self.name = name
end

Instance Attribute Details

#proxyObject

Returns the value of attribute proxy.



256
257
258
# File 'lib/mirah/ast/intrinsics.rb', line 256

def proxy
  @proxy
end

Class Method Details

.new(*args, &block) ⇒ Object



258
259
260
261
# File 'lib/mirah/ast/intrinsics.rb', line 258

def self.new(*args, &block)
  real_node = super
  real_node.proxy = NodeProxy.new(real_node)
end

Instance Method Details

#annotate(type, class_name) ⇒ Object



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
# File 'lib/mirah/ast/intrinsics.rb', line 329

def annotate(type, class_name)
  node = type.unmeta.node
  if node
    extension = node.annotation('duby.anno.Extensions')
    extension ||= begin
      node.annotations << Annotation.new(
          nil, nil, BiteScript::ASM::Type.getObjectType('duby/anno/Extensions'))
      node.annotations[-1].runtime = false
      node.annotations[-1]
    end
    extension['macros'] ||= []
    macro = Annotation.new(nil, nil,
                           BiteScript::ASM::Type.getObjectType('duby/anno/Macro'))
    macro['name'] = name
    macro['signature'] = BiteScript::Signature.signature(*signature)
    macro['class'] = class_name
    extension['macros'] << macro
    # TODO deal with optional blocks.
  else
    puts "Warning: No ClassDefinition for #{type.name}. Macros can't be loaded from disk."
  end
end

#argument_typesObject



291
292
293
294
295
296
297
298
299
300
301
# File 'lib/mirah/ast/intrinsics.rb', line 291

def argument_types
  arguments.map do |arg|
    if arg.kind_of?(BlockArgument)
      TypeReference::BlockType
    else
      # TODO support typed args. Also there should be a way
      # to accept any AST node.
      Mirah::JVM::Types::Object
    end
  end
end

#build_and_load_extension(parent, name, state) ⇒ Object



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/mirah/ast/intrinsics.rb', line 311

def build_and_load_extension(parent, name, state)
  transformer = Mirah::Transform::Transformer.new(state)
  transformer.filename = name.gsub(".", "/")
  orig_factory = Mirah::AST.type_factory
  new_factory = orig_factory.dup
  Mirah::AST.type_factory = new_factory
  ast = build_ast(name, parent, transformer)
  classes = compile_ast(name, ast, transformer)
  loader = Mirah::Util::ClassLoader.new(
      JRuby.runtime.jruby_class_loader, classes)
  klass = loader.loadClass(name, true)
  if state.save_extensions
    annotate(parent, name)
  end
  Mirah::AST.type_factory = orig_factory
  klass
end

#build_ast(name, parent, transformer) ⇒ Object



383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/mirah/ast/intrinsics.rb', line 383

def build_ast(name, parent, transformer)
  # TODO should use a new type factory too.

  ast = Mirah::AST.parse_ruby("begin;end")
  ast = transformer.transform(ast, nil)

  # Start building the extension class
  extension = transformer.define_class(position, name)
  #extension.superclass = Mirah::AST.type(nil, 'duby.lang.compiler.Macro')
  extension.implements(Mirah::AST.type(nil, 'duby.lang.compiler.Macro'))

  extension.static_scope.import('duby.lang.compiler.Node', 'Node')
  extension.static_scope.package = scope.static_scope.package

  # The constructor just saves the state
  extension.define_constructor(
      position,
      ['mirah', Mirah::AST.type(nil, 'duby.lang.compiler.Compiler')],
      ['call', Mirah::AST.type(nil, 'duby.lang.compiler.Call')]) do |c|
    transformer.eval("@mirah = mirah;@call = call", '-', c, 'mirah', 'call')
  end

  node_type = Mirah::AST.type(nil, 'duby.lang.compiler.Node')

  # expand() parses the arguments out of call and then passes them off to
  # _expand
  expand = extension.define_method(
      position, 'expand', node_type)
  args = []
  arguments.each_with_index do |arg, i|
    # TODO optional args
    args << if arg.kind_of?(BlockArgument)
      "@call.block"
    else
      "Node(args.get(#{i}))"
    end
  end
  expand.body = transformer.eval(<<-end)
    args = @call.arguments
    _expand(#{args.join(', ')})
  end
  actual_args = arguments.map do |arg|
    type = if arg.kind_of?(BlockArgument)
      Mirah::AST.type(nil, 'duby.lang.compiler.Block')
    else
      node_type
    end
    [arg.name, type, arg.position]
  end
  m = extension.define_method(position, '_expand', node_type, *actual_args)
  scope.static_scope.imports.each do |short, long|
    m.static_scope.import(long, short)
  end
  scope.static_scope.search_packages.each do |package|
    m.static_scope.import(package, '*')
  end
  m.body = self.body
  ast.body = extension
  ast
end

#compile_ast(name, ast, transformer) ⇒ Object



352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
# File 'lib/mirah/ast/intrinsics.rb', line 352

def compile_ast(name, ast, transformer)
  begin
    # FIXME: This is JVM specific, and should move out of platform-independent code
    typer = Mirah::JVM::Typer.new(transformer)
    typer.infer(ast, false)
    typer.resolve(true)
    typer.errors.each do |e|
      raise e
    end
  ensure
    puts ast.inspect if transformer.state.verbose
  end
  # FIXME: This is JVM specific, and should move out of platform-independent code
  compiler = Mirah::JVM::Compiler::JVMBytecode.new
  ast.compile(compiler, false)
  class_map = {}
  compiler.generate do |outfile, builder|
    bytes = builder.generate
    name = builder.class_name.gsub(/\//, '.')
    class_map[name] = Mirah::Util::ClassLoader.binary_string bytes
    if transformer.state.save_extensions
      outfile = "#{transformer.destination}#{outfile}"
      FileUtils.mkdir_p(File.dirname(outfile))
      File.open(outfile, 'wb') do |f|
        f.write(bytes)
      end
    end
  end
  class_map
end

#infer(typer, expression) ⇒ Object



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/mirah/ast/intrinsics.rb', line 268

def infer(typer, expression)
  resolve_if(typer) do
    self_type = scope.static_scope.self_type
    extension_name = "%s$%s" % [self_type.name,
                                typer.transformer.tmp("Extension%s")]
    klass = build_and_load_extension(self_type,
                                     extension_name,
                                     typer.transformer.state)

    # restore the self type since we're sharing a type factory
    typer.known_types['self'] = self_type

    arg_types = argument_types
    macro = self_type.add_compiled_macro(klass, name, arg_types)
    if arguments[-1].kind_of?(BlockArgument) && arguments[-1].optional?
      arg_types.pop
      self_type.add_method(name, arg_types, macro)
    end
    proxy.__inline__(Noop.new(parent, position))
    proxy.infer(typer, expression)
  end
end

#signatureObject



303
304
305
306
307
308
309
# File 'lib/mirah/ast/intrinsics.rb', line 303

def signature
  args = argument_types
  if args.size > 0 && args[-1].block?
    args[-1] = BiteScript::ASM::Type.getObjectType('duby.lang.compiler.Block')
  end
  [nil] + args
end