You can pass a MethodBuilder
as the argument for Emit, since MethodBuilder inherits from MethodInfo, it will call the correct method when invoked. Using your toy program, def hello(string msg) { print(msg); } hello("Hello!");
, here that shows how to emit code for this:
ILGenerator ilg;
var asmName = new AssemblyName("DynamicAssembly");
var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndCollect);
var modBuilder = asmBuilder.DefineDynamicModule("DynamicAssembly");
var type = modBuilder.DefineType("<>CompilerFunctionClass", TypeAttributes.Class | TypeAttributes.Public);
type.DefineDefaultConstructor(MethodAttributes.Public);
var helloBuilder = type.DefineMethod("hello", MethodAttributes.Family | MethodAttributes.Static, typeof(void), new[] { typeof(string) });
// emitting code for hello later
var mainBuilder = type.DefineMethod("Main", MethodAttributes.Public);
ilg = mainBuilder.GetILGenerator();
ilg.Emit(OpCodes.Ldstr, "Hello, World!");
ilg.Emit(OpCodes.Call, helloBuilder);
ilg.Emit(OpCodes.Ret);
// Here we emit the code for hello.
ilg = helloBuilder.GetILGenerator();
ilg.Emit(OpCodes.Ldarg_0);
ilg.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine",
new Type[] { typeof(string) }));
ilg.Emit(OpCodes.Ret);
// just to show it works.
var t = type.CreateType();
dynamic d = Activator.CreateInstance(t);
d.Main(); // prints Hello, World!
Your compiler would probably discover all the top level function names first, and define methods for them, then later it can generate the code for each one.
Note that Reflection.Emit is fine for toy examples and learning projects, however it is not powerful enough to do the worked needed by a full-fledged compiler. See the comments here by Eric Lippert. He suggests using the Common Compiler Infrastructure to build a compiler. I haven't used it, so I can't say.