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.



266
267
268
269
# File 'lib/mirah/ast/intrinsics.rb', line 266

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.



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

def proxy
  @proxy
end

Class Method Details

.new(*args, &block) ⇒ Object



261
262
263
264
# File 'lib/mirah/ast/intrinsics.rb', line 261

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

Instance Method Details

#annotate(type, class_name) ⇒ Object



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

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



294
295
296
297
298
299
300
301
302
303
304
# File 'lib/mirah/ast/intrinsics.rb', line 294

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



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

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



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
443
444
445
# File 'lib/mirah/ast/intrinsics.rb', line 386

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



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
382
383
384
# File 'lib/mirah/ast/intrinsics.rb', line 355

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



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

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



306
307
308
309
310
311
312
# File 'lib/mirah/ast/intrinsics.rb', line 306

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