Skip to content

Instantly share code, notes, and snippets.

@jbevain
Created March 23, 2011 09:18
Show Gist options
  • Select an option

  • Save jbevain/882825 to your computer and use it in GitHub Desktop.

Select an option

Save jbevain/882825 to your computer and use it in GitHub Desktop.
boo il macro
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