MacroBuilder module

Share your advanced PureBasic knowledge/code with the community.
User avatar
eddy
Addict
Addict
Posts: 1479
Joined: Mon May 26, 2003 3:07 pm
Location: Nantes

MacroBuilder module

Post by eddy »

  • Define Macro
  • Override method name
original tip from cxAlex : http://www.purebasic.fr/english/viewtop ... =3&t=53826

Code: Select all

;:=============================================================================
;:- MacroBuilder.pbi
;:- Author          : Eddy
;:- Date            : September 8, 2013
;:- Compiler        : PureBasic 5.20 beta 16 LTS
;:- Target OS       : Mac, Linux, Windows
;:- Source --------------------------------------------------------------------
;:- http://www.purebasic.fr/english/viewtopic.php?f=40&t=56522
;:=============================================================================
CompilerIf Not Defined(MacroBuilder, #PB_Module)
   DeclareModule MacroBuilder
      EnableExplicit
      
      Macro _DoubleQuote
         "
      EndMacro
      
      Macro _Colon
         : 
      EndMacro
      
      Macro ConcatMacro(a, b)
         a#b
      EndMacro
      
      Macro DefineMacro(_MacroHead, _MacroBody)
         _Colon#Macro _MacroHead#_Colon#_MacroBody#_Colon#EndMacro
      EndMacro
      
      Macro Override(_Name, _Prefix=_)
         DefineMacro(_Name, ConcatMacro(_Prefix, _Name))
      EndMacro
   EndDeclareModule
   
   Module MacroBuilder
   EndModule
CompilerEndIf


CompilerIf #PB_Compiler_IsMainFile
   ; ********************
   ; EXAMPLE
   ; ********************
   
   UseModule MacroBuilder
   
   DefineMacro(Add(x, y), (x + y))
   DefineMacro(Sub(x, y), (x-y))
   DefineMacro(Mul(x, y), (x*y))
   DefineMacro(Div(x, y), (x / y))
   
   Debug Add(2, 3)
   Debug Sub(2, 3)
   Debug Mul(2, 3)
   Debug Div(2, 3)
   
   Override(OpenConsole)
   
   Procedure OpenConsole(Title$)
      MessageRequester(Title$, "Console is open... or not :)")
   EndProcedure
   
   OpenConsole("OpenConsole")
CompilerEndIf
Last edited by eddy on Sun Jun 14, 2015 5:14 pm, edited 2 times in total.
Imagewin10 x64 5.72 | IDE | PB plugin | Tools | Sprite | JSON | visual tool
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: MacroBuilder module

Post by luis »

The result of the macro Override being a macro outputting the original identifier preceded by "_" and so seamlessly replacing all the "OpenConsole" in the code with a hidden "_Openconsole" is a pretty clever idea, looks almost native :)
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
eddy
Addict
Addict
Posts: 1479
Joined: Mon May 26, 2003 3:07 pm
Location: Nantes

Re: MacroBuilder module

Post by eddy »

- just a minor optimization (extra parameters removed) :wink:

Code: Select all

Macro DefineMacro(_MacroHead, _MacroBody, _Start=, _End=End)

Code: Select all

Macro DefineMacro(_MacroHead, _MacroBody)
Last edited by eddy on Mon Sep 09, 2013 12:22 am, edited 2 times in total.
Imagewin10 x64 5.72 | IDE | PB plugin | Tools | Sprite | JSON | visual tool
BorisTheOld
Enthusiast
Enthusiast
Posts: 542
Joined: Tue Apr 24, 2012 5:08 pm
Location: Ontario, Canada

Re: MacroBuilder module

Post by BorisTheOld »

@eddy

Here's our version of the above technique. We use it to dynamically create macros that can be used to generate unique procedure and data names in classes and modules. This is just a small sample to show how we create modular code without actually using modules.

We create a standard "_Block_" macro that can be used whenever a unique name needs to be created. The "_Blank_" and "_Colon_" parameters are needed to ensure that PB doesn't misinterpret the macro contents before it's fully generated.

Code: Select all

Macro _Colon_
  :
EndMacro
     
Macro CreateBlockName(_BlockName_, _Blank_=)
  _Blank_#Macro _B#_Blank_#lock_#_Colon_#_BlockName_#_Colon_#EndMacro
EndMacro
     
Macro DestroyBlockName(_Blank_=)
  UndefineMacro _B#_Blank_#lock_
EndMacro

Macro BeginClass (bvsClassName)
  CreateBlockName(bvsClassName)  
EndMacro

Macro EndClass
  DestroyBlockName()
EndMacro

Macro typObject
  I
EndMacro

Macro DeclareExternalFunction (bvsProcName, bvsDataParmType)
  Declare.bvsDataParmType cls#_Block_#_fun#bvsProcName
EndMacro

Macro BeginClassInterface
  DeclareExternalFunction(Create, typObject) ()
  Interface obj#_Block_
EndMacro

Macro EndClassInterface
  EndInterface
EndMacro

BeginClass (ButtonBar)  ;  Macro _Block_:ButtonBar:EndMacro
BeginClassInterface     ;  Declare.I clsButtonBar_funCreate ()
                        ;  Interface objButtonBar    
EndClassInterface       ;  EndInterface
EndClass                ;  UndefineMacro _Block_

BeginClass (Button)     ;  Macro _Block_:Button:EndMacro
BeginClassInterface     ;  Declare.I clsButton_funCreate ()
                        ;  Interface objButton    
EndClassInterface       ;  EndInterface
EndClass                ;  UndefineMacro _Block_
And for those of you who really love to read my code, here are the two procedures referenced above. As you can see, much of the code is identical, even though the procedures are from two different classes. The underlying code uses the "_Block_" macro to create names that are unique to each class, thereby allowing the entire program to be compiled as a single source file.

Code: Select all

ExternalFunction(Create, typObject) ()            ;  Procedure.I clsButtonBar_funCreate ()
;
;  Create  -  create a class instance
;
  Local(Me, strButtonBar)                         ;    Protected *Self.strButtonBar

  Me = AllocateMemory(SizeOf(strButtonBar))       ;    *Self = AllocateMemory(SizeOf(strButtonBar))
  If IsObject(Me)                                 ;    If *Self <> 0
    InitializeStructure(Me, strButtonBar)         ;      InitializeStructure(*Self, strButtonBar)
    Me\prpVirtualTable = LabelPtr(VirtualTable)   ;      *Self\prpVirtualTable = ?clsButtonBar_lblVirtualTable
    ClassCall(Constructor) (Me)                   ;      clsButtonBar_subConstructor (*Self)
  EndIf                                           ;    EndIf
  ProcedureReturn Me                              ;    ProcedureReturn *Self
EndFunction                                       ;  EndProcedure

Code: Select all

ExternalFunction(Create, typObject) ()            ;  Procedure.I clsButton_funCreate ()
;
;  Create  -  create a class instance
;
  Local(Me, strButton)                            ;    Protected *Self.strButton

  Me = AllocateMemory(SizeOf(strButton))          ;    *Self = AllocateMemory(SizeOf(strButton))
  If IsObject(Me)                                 ;    If *Self <> 0
    InitializeStructure(Me, strButton)            ;      InitializeStructure(*Self, strButton)
    Me\prpVirtualTable = LabelPtr(VirtualTable)   ;      *Self\prpVirtualTable = ?clsButton_lblVirtualTable
    ClassCall(Constructor) (Me)                   ;      clsButton_subConstructor (*Self)
  EndIf                                           ;    EndIf
  ProcedureReturn Me                              ;    ProcedureReturn *Self
EndFunction                                       ;  EndProcedure
Here are the macros used in the above methods. In total, we use about 500 macros in generating our code. As you can see, I'm a big fan of long names and Polish notation. :)

Code: Select all

Macro Me
  *Self
EndMacro

Macro ExternalFunction (bvsProcName, bvsDataParmType)
  Procedure.bvsDataParmType cls#_Block_#_fun#bvsProcName
EndMacro

Macro EndFunction
  EndProcedure
EndMacro

Macro ClassCall (bvsProcName)
  cls#_Block_#_sub#bvsProcName
EndMacro

Macro Local (bvsDataParmName, bvsDataParmType)
  Protected bvsDataParmName.bvsDataParmType
EndMacro

Macro IsObject (bvsDataParmName)
  bvsDataParmName <> 0
EndMacro

Macro LabelPtr (bvsDataParmName)
  ?cls#_Block_#_lbl#bvsDataParmName
EndMacro
For ten years Caesar ruled with an iron hand, then with a wooden foot, and finally with a piece of string.
~ Spike Milligan
User avatar
eddy
Addict
Addict
Posts: 1479
Joined: Mon May 26, 2003 3:07 pm
Location: Nantes

Re: MacroBuilder module

Post by eddy »

Updated
- ConcatMacro
- Multiple method overrding

Code: Select all

   UseModule MacroBuilder

   Override(OpenConsole)   
   Procedure OpenConsole(Text$)
      MessageRequester(Title$, "The console said "+Text$)
   EndProcedure
   OpenConsole("Eddy")

   Override(OpenConsole)   
   Procedure OpenConsole(Text$)
      MessageRequester(Title$, "Hello "+Text$)
   EndProcedure   
   OpenConsole("Eddy")
Imagewin10 x64 5.72 | IDE | PB plugin | Tools | Sprite | JSON | visual tool
DontTalkToMe
Enthusiast
Enthusiast
Posts: 334
Joined: Mon Feb 04, 2013 5:28 pm

Re: MacroBuilder module

Post by DontTalkToMe »

I would have a use for this, but unfortunately if you invoke DefineMacro from another macro the calling macro just terminates prematurely.

Code: Select all


UseModule MacroBuilder

Macro test()
 Debug "hello"
 DefineMacro(Add(x, y), (x + y))
 Debug "world"
EndMacro   
   
test()
   
 
Should print

hello
world

instead prints

hello

I have the need to create a macro from another macro (and that works) but then continue with the rest of the macro body.
DontTalkToMe
Enthusiast
Enthusiast
Posts: 334
Joined: Mon Feb 04, 2013 5:28 pm

Re: MacroBuilder module

Post by DontTalkToMe »

Simplified

Code: Select all

Macro _C_
  :
EndMacro
     
Macro CreateMacro(_MacroHead, _MacroBody_)
 Macro _MacroHead#_C_#_MacroBody_#_C_#EndMacro 
EndMacro

Macro MyMacro(p)
 Debug "hello " + p
 CreateMacro(foo(), "working")
 Debug "world " + p
EndMacro


MyMacro("param") ; should print hello param, world param

Debug foo() ; the foo() macro has been defined
output

Code: Select all

hello param
working
Macros do not seem to work reliably unless you stick to trivial usages, these quirks should be fixed.

Unfortunately I'm not optimist judging from other old standing macro issues still unresolved. :cry:
Post Reply