Skip to content

Commit

Permalink
Merge pull request #413 from uujava/varargs_for_macro
Browse files Browse the repository at this point in the history
support varargs for macros
  • Loading branch information
ribrdb committed Feb 26, 2016
2 parents 7b9c6b6 + 2d53555 commit 923ec32
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 2 deletions.
9 changes: 8 additions & 1 deletion src/org/mirah/jvm/mirrors/macro_member.mirah
Expand Up @@ -60,7 +60,14 @@ class MacroMember < Member
macrodef.arguments.required.each do |name|
argumentTypes.add(types.loadMacroType(name))
end

vararg = macrodef.arguments.rest
if vararg and vararg.trim.length > 0
component_type = types.loadMacroType(vararg.trim)
type = types.getArrayType(component_type)
argumentTypes.add(type)
flags |= Opcodes.ACC_VARARGS
end

kind = if macrodef.isStatic
MemberKind.STATIC_METHOD
else
Expand Down
64 changes: 64 additions & 0 deletions src/org/mirah/macros/builder.mirah
Expand Up @@ -48,6 +48,8 @@ import mirah.lang.ast.StringCodeSource
import mirah.lang.ast.StringConcat
import mirah.lang.ast.TypeName
import mirah.lang.ast.Unquote
import mirah.lang.ast.FunctionalCall
import mirah.lang.ast.TypeRefImpl
import org.mirah.typer.TypeFuture
import org.mirah.typer.Typer

Expand Down Expand Up @@ -256,6 +258,7 @@ class MacroBuilder; implements org.mirah.macros.Compiler
import mirah.lang.ast.CallSite
import mirah.lang.ast.Node
import mirah.lang.ast.*
import java.lang.reflect.Array as ReflectArray

$MacroDef[name: `macroDef.name`, arguments: `argdef`, isStatic: `isStatic`]
class `name` implements Macro
Expand All @@ -272,6 +275,28 @@ class MacroBuilder; implements org.mirah.macros.Compiler
_expand(`casts`)
end

def _varargs(index:int, type:Class ):Object
parameters = @call.parameters
block = @call.block
vsize = parameters.size - index

vargs = if block
ReflectArray.newInstance(type, vsize + 1)
else
ReflectArray.newInstance(type, vsize)
end

# add block as last item
ReflectArray.set(vargs, vsize, type.cast(block)) if block

# downcount
while vsize > 0
vsize -= 1
ReflectArray.set(vargs, vsize, type.cast(parameters.get(index + vsize)))
end
vargs
end

def gensym: String
@mirah.scoper.getScope(@call).temp('$gensym')
end
Expand Down Expand Up @@ -326,6 +351,24 @@ class MacroBuilder; implements org.mirah.macros.Compiler
arg.type = SimpleString.new("mirah.lang.ast.#{arg.type.typeref.name}")
end
end

macroDef.arguments.optional.each do |oarg: RequiredArgument|
if oarg.type.nil?
oarg.type = SimpleString.new('mirah.lang.ast.Node')
elsif oarg.type.typeref.name.indexOf('.') == -1
oarg.type = SimpleString.new("mirah.lang.ast.#{oarg.type.typeref.name}")
end
end

rarg = macroDef.arguments.rest
if rarg
if rarg.type.nil?
rarg.type = SimpleString.new('mirah.lang.ast.Node')
elsif rarg.type.typeref.name.indexOf('.') == -1
rarg.type = SimpleString.new("mirah.lang.ast.#{rarg.type.typeref.name}")
end
end

block = macroDef.arguments.block
if block
type = block.type || SimpleString.new('mirah.lang.ast.Block')
Expand All @@ -346,6 +389,14 @@ class MacroBuilder; implements org.mirah.macros.Compiler
end
i += 1
end
if args.rest
rtype_name = args.rest.type.typeref.name
array_type = TypeRefImpl.new(rtype_name, true)
type = TypeRefImpl.new(rtype_name, false)
casts.add(
Cast.new(array_type, fetchMacroVarArg(i, type))
)
end
casts
end

Expand All @@ -360,6 +411,12 @@ class MacroBuilder; implements org.mirah.macros.Compiler
required.add(SimpleString.new(arg.position, name))
end
entries = [HashEntry.new(SimpleString.new('required'), Array.new(required))]
if args.rest
name = args.rest.type.typeref.name
# FIXME these should probably be inferred instead of assuming the package.
name = "mirah.lang.ast.#{name}" unless name.startsWith('mirah.lang.ast.')
entries.add HashEntry.new(SimpleString.new('rest'), SimpleString.new(name))
end
Annotation.new(SimpleString.new('org.mirah.macros.anno.MacroArgs'), entries)
end

Expand All @@ -371,6 +428,13 @@ class MacroBuilder; implements org.mirah.macros.Compiler
SimpleString.new('get'), [Fixnum.new(i)], nil)
end

# Returns a node to fetch the i'th macro argument during expansion.
def fetchMacroVarArg(i: int, type: TypeName): Node
index = Fixnum.new(i)
clazz = Call.new(type, SimpleString.new('class'), Collections.emptyList, nil)
FunctionalCall.new(SimpleString.new('_varargs'), [Fixnum.new(i), clazz], nil)
end

def fetchMacroBlock: Node
Call.new(FieldAccess.new(SimpleString.new('call')),
SimpleString.new('block'), Collections.emptyList, nil)
Expand Down
39 changes: 38 additions & 1 deletion test/jvm/macros_test.rb
Expand Up @@ -491,7 +491,7 @@ def test_explicit_import_of_as_yet_unresolved_type_in_file_with_macro
cls1, cls2 = compile([<<-EOF1, <<-EOF2])
package org.bar.p1
import org.bar.p2.MultiPackageExplicitRef
macro def foo1; end
puts MultiPackageExplicitRef.class
EOF1
Expand Down Expand Up @@ -675,5 +675,42 @@ def self.main(args: String[]):void

assert_run_output("nil\ntest\nself nil\nself test\n", cls)
end

def test_macro_varargs
cls, = compile(<<-CODE)
class MacroWithVarargs
macro def self.vararg(first:Node, *args:Node)
list = NodeList.new
list.add quote do
puts `first`
end
args.each do |arg:Node|
m = if arg.kind_of? Block
body = Block(arg).body
quote do
puts `body`
end
else
quote do
puts `arg`
end
end
list.add m
end
list
end
def self.main(*args:String):void
vararg 1
vararg 1, 2
vararg 1, 2, 3
vararg 1,2 {"test"}
end
end
CODE

assert_run_output("1\n1\n2\n1\n2\n3\n1\n2\ntest\n", cls)
end
end

0 comments on commit 923ec32

Please sign in to comment.