Mocking Boo

time to read 24 min | 4735 words

Okay, I built it to relax a bit, because I am extremely annoyed at the moment. I apologize in advance for the code quality, it is POC only, but still, I wouldn't generally release it like this.

What is this? Do you see the highlighted bit at the bottom? This is Boo code that invokes a Macro on compile. It will generate an adapter and an interface, so you don't have to do it manually. The implementation code is below:

image

class AdapterMacro(AbstractAstMacro):

      def Expand(macro as MacroStatement):

            if macro.Arguments.Count != 1 or not macro.Arguments[0] isa ReferenceExpression:

                  raise "adapter must be called with a single argument"

            entity = NameResolutionService.Resolve(macro.Arguments[0].ToString())

            raise "adapter only accept types" unless entity.EntityType == EntityType.Type

            BuildType(macro, entity)

           

      def GetModule(node as Node) as Boo.Lang.Compiler.Ast.Module:

            return node if node isa Boo.Lang.Compiler.Ast.Module

            return GetModule(node.ParentNode)

     

      def BuildType(macro as MacroStatement, type as IType):

            adapter = ClassDefinition(Name: "${type.Name}Adapter")

            adapter.Members.Extend(

                  Field(Name: "theTarget", Type: SimpleTypeReference(type.FullName) )

            )

           

            ctor = Constructor()

            ctor.Parameters.Add(ParameterDeclaration("target", SimpleTypeReference(type.FullName) ) )

            ctor.Body.Add(

                  BinaryExpression(BinaryOperatorType.Assign,

                        ReferenceExpression("theTarget"),

                        ReferenceExpression(Name: "target")

                  )

            )

            adapter.Members.Add(ctor)

            adapterInterface = InterfaceDefinition(Name: "I${type.Name}")

            GetModule(macro).Members.Add(adapter)

            GetModule(macro).Members.Add(adapterInterface)

            adapter.BaseTypes.Add( SimpleTypeReference(adapterInterface.FullName) )

            for member in type.GetMembers():

                  AddMethod(adapter, adapterInterface,  member) if member isa IMethod

           

            BooPrinterVisitor(System.Console.Out).Visit(adapterInterface)

            BooPrinterVisitor(System.Console.Out).Visit(adapter)

           

      def AddMethod(adapter as ClassDefinition,

            adapterInterface as InterfaceDefinition,

            method as IMethod):

           

            return unless method.IsPublic

           

            interfaceMethod = Method(Name: method.Name)

            forwarder = Method(Name: method.Name)

           

            if method.ReturnType.IsByRef or method.ReturnType.IsArray:

                  return

           

            args = []

            for param in method.GetParameters():

                 

                  if param.IsByRef or param.Type.IsArray:

                        return

 

                  forwarder.Parameters.Extend(

                        ParameterDeclaration(

                              Name: param.Name,

                              Type: SimpleTypeReference(param.Type.FullName)

                              )

                        )

                  interfaceMethod.Parameters.Extend(

                        ParameterDeclaration(

                              Name: param.Name,

                              Type: SimpleTypeReference(param.Type.FullName)

                              )

                        )

                  args.Add( ReferenceExpression(param.Name) )

                 

            adapterInterface.Members.Add(interfaceMethod)

            adapter.Members.Add(forwarder)

           

            mie = MethodInvocationExpression(

                  Target: AstUtil.CreateReferenceExpression("theTarget.${method.Name}")

                  )

            mie.Arguments.Extend(args)

            forwarder.ReturnType = SimpleTypeReference(method.ReturnType.FullName)

            interfaceMethod.ReturnType = SimpleTypeReference(method.ReturnType.FullName)

            if method.ReturnType == typeof(void):

                  forwarder.Body.Add(mie)

            else:

                  forwarder.Body.Add(ReturnStatement(mie))