I am currently making a programming language in C#. I am stumped on how to perform function calls in a dynamic way. I am now sure how I would call a user-defined function. I understand that to output "hello world" something like this is needed:
ilg.Emit(OpCodes.Ldstr, "Hello, World!");
ilg.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine",
new Type[] {typeof(string)} ));
But what do I do if there's a user-defined function?
What's the best (or any) way to do this?
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.