Created
March 23, 2011 09:18
-
-
Save jbevain/882825 to your computer and use it in GitHub Desktop.
boo il macro
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Boo.Lang.Compiler | |
| import Boo.Lang.Compiler.Ast | |
| import Boo.Lang.PatternMatching | |
| import System | |
| import System.Collections.Generic | |
| import System.Reflection | |
| import System.Reflection.Emit | |
| macro il(obj as ReferenceExpression): | |
| ilgen = ReferenceExpression(il.LexicalInfo, Context.GetUniqueName("il", "generator")) | |
| locals = Dictionary[of string, ReferenceExpression]() | |
| labels = Dictionary[of string, ReferenceExpression]() | |
| def opcode_field(name as string): | |
| for field in typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public): | |
| return field if field.Name == name or field.Name.ToLower() == name | |
| raise "Unknown opcode ${name}" | |
| def is_label_unique(name as string): | |
| return not labels.ContainsKey(name) | |
| def label_for(expression as Expression): | |
| label = cast(ReferenceExpression, expression).Name | |
| raise "Unknown label ${label}" if not labels.ContainsKey(label) | |
| return labels[label] | |
| def local_for(expression as Expression): | |
| local = cast(ReferenceExpression, expression).Name | |
| raise "Unknown local ${local}" if not locals.ContainsKey(local) | |
| return locals[local] | |
| def should_preprocess(statement as Statement): | |
| return statement isa LabelStatement | |
| def preprocess(statement as Statement): | |
| match statement: | |
| case LabelStatement(Name: name): | |
| raise "Label defined multiple times: ${name}" if not is_label_unique(name) | |
| label = ReferenceExpression(statement.LexicalInfo, Context.GetUniqueName("label", name)) | |
| labels.Add (name, label) | |
| return [| $label = $ilgen.DefineLabel() |] | |
| otherwise: | |
| raise "Wrong statement type in il macro: ${statement.GetType().Name}" | |
| def emit(name as string, args as ExpressionCollection): | |
| assert len(args) <=1 | |
| field = opcode_field(name) | |
| opcode = cast(OpCode, field.GetValue(null)) | |
| arg = (args[0] if len(args) > 0 else null) | |
| match opcode.OperandType: | |
| case OperandType.InlineNone: | |
| return [| $ilgen.Emit(OpCodes.$(field.Name)) |] | |
| case OperandType.InlineString: | |
| return [| $ilgen.Emit(OpCodes.$(field.Name), $arg) |] | |
| case OperandType.InlineVar: | |
| return [| $ilgen.Emit(OpCodes.$(field.Name), $(local_for(arg))) |] | |
| case OperandType.InlineBrTarget: | |
| return [| $ilgen.Emit(OpCodes.$(field.Name), $(label_for(arg))) |] | |
| otherwise: | |
| raise "NotImplemented: ${opcode.OperandType}" | |
| def process(statement as Statement): | |
| match statement: | |
| case ExpressionStatement(Expression: MethodInvocationExpression(Target: ReferenceExpression(Name: name), Arguments: args)): | |
| return emit(name, args) | |
| case DeclarationStatement(Declaration: Declaration(Name: name, Type: type)): | |
| local = ReferenceExpression(statement.LexicalInfo, Context.GetUniqueName("local", name)) | |
| locals.Add(name, local) | |
| return [| $local = $ilgen.DeclareLocal(typeof($type)) |] | |
| case LabelStatement(Name: name): | |
| label = labels[name] | |
| return [| $ilgen.MarkLabel($label) |] | |
| otherwise: | |
| raise "Wrong statement type in il macro: ${statement.GetType().Name}" | |
| yield [| $ilgen = $(obj).GetILGenerator() |] | |
| for statement in il.Body.Statements: | |
| if should_preprocess(statement): | |
| yield preprocess(statement) | |
| for statement in il.Body.Statements: | |
| yield process(statement) | |
| dyn = DynamicMethod("foo", typeof(string), (,)) | |
| il dyn: | |
| str as string | |
| ldstr "foo" | |
| stloc str | |
| br exit | |
| :exit | |
| ldloc str | |
| ret | |
| d = dyn.CreateDelegate(typeof (Func[of string])) as Func[of string] | |
| print d() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment