1

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?

  • What do you mean by user-defined function? – Rob Jan 14 '16 at 01:52
  • Like a function that would be declared in the programming language within the code. – Danilo Lekovic Jan 14 '16 at 01:57
  • Then it's no different than your current code. `typeof(MyClass).GetMethod("MyMethod")` – Rob Jan 14 '16 at 01:58
  • Can you give an example of the method that you are trying to call? Is it a static method or an instance method? – Yacoub Massad Jan 14 '16 at 02:00
  • Also, there are easier ways to call a method without emitting IL. For example, you can use reflection to call a method. – Yacoub Massad Jan 14 '16 at 02:01
  • When you say "user-defined" you mean in the language you are writing? Such a method would be dynamically generated by your compiler and you can pass the MethodBuilder for the method as the argument. I will note however that Reflection.Emit is not necessarily powerful enough to do everything an actual language compiler might need to do. For that you might need to look at something like [Common Compiler Infrastructure](http://cciast.codeplex.com/wikipage?title=What%20is%20CCI%20Code%3f&referringTitle=Home). – Mike Zboray Jan 14 '16 at 02:05
  • Say that the user wrote code like this: `def hello(string msg) { print(msg); } hello("Hello!");` How would I go about doing compiling this? – Danilo Lekovic Jan 14 '16 at 02:12

1 Answers1

2

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.

Community
  • 1
  • 1
Mike Zboray
  • 39,828
  • 3
  • 90
  • 122