Nested macro definitions

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
BorisTheOld
Enthusiast
Enthusiast
Posts: 542
Joined: Tue Apr 24, 2012 5:08 pm
Location: Ontario, Canada

Nested macro definitions

Post by BorisTheOld »

Can something be added to PB to allow nested macro definitions as described below?

We use many macros to hide the details of our OOP classes. This requires that we specify the class name within macros in order to create unique names in the expanded code.

We can get around the problem with the following code structure. The class name is used in other macros to create unique labels, procedure names, etc.

Code: Select all

Macro BeginClass
EndMacro

Macro EndClass
  UndefineMacro QQClass
EndMacro

Macro BeginData
  Structure cls#QQClass
EndMacro

Macro EndData
  EndStructure
EndMacro

Macro QQClass :Application: EndMacro
BeginClass
BeginData
  a.i
EndData
EndClass

Macro QQClass :Container: EndMacro
BeginClass
BeginData
  a.i
EndData
EndClass
However, we would prefer to hide the CLASS macro definitions inside another macro, but this isn't allowed:

Code: Select all

Macro BeginClass(ClassName)
  Macro QQClass
    ClassName
  EndMacro
EndMacro

Macro EndClass
  UndefineMacro QQClass
EndMacro

Macro BeginData
  Structure cls#QQClass
EndMacro

Macro EndData
  EndStructure
EndMacro

BeginClass(Application)
BeginData
  a.i
EndData
EndClass

BeginClass(Container)
BeginData
  a.i
EndData
EndClass

FreeBasic has two ways of creating macros, a multi-line Macro/EndMacro feature, and a single-line #Define feature. An #UnDef statement undefines the macro.

In FreeBasic we use the single-line #Define statement for creating all our macros, as shown in the following example. The first parameter is the macro name and its parameters, and the rest of the line is the body of the macro which itself can be a #Define statement. This allows for nested macro definitions.

Code: Select all

#Define BeginClass(ClassName) #Define QQClass ClassName
#Define EndClass #UnDef QQClass
#Define BeginData Type cls##QQClass
#Define EndData End Type

BeginClass(Application)
BeginData
  a as integer
EndData
EndClass

BeginClass(Container)
BeginData
  a as integer
EndData
EndClass
This generates the following code:

Code: Select all

Type clsApplication
a as integer
End Type

Type clsContainer
a as integer
End Type
For ten years Caesar ruled with an iron hand, then with a wooden foot, and finally with a piece of string.
~ Spike Milligan
BorisTheOld
Enthusiast
Enthusiast
Posts: 542
Joined: Tue Apr 24, 2012 5:08 pm
Location: Ontario, Canada

Re: Nested macro definitions

Post by BorisTheOld »

Here's a suggestion for how the above request might be implemented. A new "DefineMacro" statement would allow for the creation of single line macros:

Code: Select all

  DefineMacro  macroname[(optional parameters)]  macrobody
As with the current macro feature, the macro body would be scanned recursively until all possible macro substitutions have been made. The expanded macro body should then be processed by the compiler. And if this new statement is a "DefineMacro", the macro should be registered so that it's available for future use.

The current "UndefineMacro" statement should be used to undefine the macro.

If there are no optional parameters, then the "DefineMacro" statement should be written as:

Code: Select all

  DefineMacro  macroname  macrobody
The macro body should always be preceeded by a space. Also, there should be no space between the macro name and the optional parameters, otherwise the optional parameters will be treated as part of the macro body.

Thanks
For ten years Caesar ruled with an iron hand, then with a wooden foot, and finally with a piece of string.
~ Spike Milligan
cxAlex
User
User
Posts: 88
Joined: Fri Oct 24, 2008 11:29 pm
Location: Austria
Contact:

Re: Nested macro definitions

Post by cxAlex »

With this little Trick, you can define a macro inside a macro:

Code: Select all

; EnableExplicit

CompilerIf Not Defined(__macrobuilder, #PB_Constant)
  #__macrobuilder = #True
  Macro _Macro1
    M
  EndMacro
  
  Macro _Macro2
    acro
  EndMacro
  
  Macro _Macro
    : _Macro1#_Macro2
  EndMacro
  
  Macro _EndOfMacro1
    EndM
  EndMacro
  
  Macro _EndOfMacro
    : _EndOfMacro1#_Macro2
  EndMacro
  
  Macro DefineMacro(_MacroHead, _MacroBody)
    _Macro _MacroHead
    _MacroBody#_EndOfMacro
  EndMacro
CompilerEndIf


; test:

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)
User avatar
skywalk
Addict
Addict
Posts: 4210
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Nested macro definitions

Post by skywalk »

Nice trick cxAlex :o
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
BorisTheOld
Enthusiast
Enthusiast
Posts: 542
Joined: Tue Apr 24, 2012 5:08 pm
Location: Ontario, Canada

Re: Nested macro definitions

Post by BorisTheOld »

cxAlex wrote:With this little Trick, you can define a macro inside a macro:
Thanks for the tip.

We played around with your code and determined that the inner "Macro" statement can be coded as normal. The trick to this technique seems to be the assembly of the inner "EndMacro" statement. It also needs to be on the last, or only, line of the macro body, otherwise a "missing EndMacro" message is created. Obviously, there is something magical happening in the parsing of the macro that allows this trick to work, because a syntax error is created if an actual "EndMacro" statement is used for the inner macro.

The other thing to take into account is the placement of the ":" statement separators. Any trailing spaces between the macro body and the ":" will be treated as part of the macro body, which will cause embedded blanks in later macro substitutions. It's just something to keep in mind.

Although this trick works, it's still a good idea to have an official way of doing this, rather than rely on a quirk in the macro feature.

Code: Select all

  Macro EndOfMacro1
    EndM
  EndMacro
 
  Macro EndOfMacro2
    acro
  EndMacro
 
  Macro EndOfMacro
    EndOfMacro1#EndOfMacro2
  EndMacro
 
  Macro BeginClass(ClassName)
    Macro QQClass
      ClassName: EndOfMacro
  EndMacro

;  Macro BeginClass(ClassName)                  ; this also works
;    Macro QQClass : ClassName: EndOfMacro
;  EndMacro

Macro EndClass
  UndefineMacro QQClass
EndMacro

Macro BeginData
  Structure cls#QQClass
EndMacro

Macro EndData
  EndStructure
EndMacro

BeginClass(Application)
BeginData
a.i
EndData
EndClass

BeginClass(Container)
BeginData
b.i
EndData
EndClass
For ten years Caesar ruled with an iron hand, then with a wooden foot, and finally with a piece of string.
~ Spike Milligan
Post Reply