Basic-Macros in fbc 1.08

Forum for discussion about the documentation project.
coderJeff
Site Admin
Posts: 3282
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Basic-Macros in fbc 1.08

Postby coderJeff » Sep 15, 2020 3:44

fxm wrote:A number of '__FB_UNIQUEID_POP__(stack_id)' greater than the number of '__FB_UNIQUEID_PUSH__(stack_id)' induces a compiler runtime error.

Thanks, an easy fix, I just haven't pushed it to fbc/master yet.

I kind of have __FB_EVAL__ working but it's too buggy -- the error recovery is bad. When it works, though, can do some other meta stuff like:

Code: Select all

#macro assign( sym, expr )
   #define tmp __FB_EVAL__( expr )
   __FB_UNQUOTE__( __FB_EVAL__( "#undef " + sym ) )
   __FB_UNQUOTE__( __FB_EVAL__( "#define " + sym + " " + __FB_QUOTE__( tmp ) ) )
   #undef tmp
#endmacro

#define x

assign( "x", 1 )
print x                  '' 1

assign( "x", x+1 )
print x                  '' 2

assign( "x", cos(1/x) ) 
print x                  '' 0.877..

assign( "x", "hello" ) 
print x                  '' hello

assign( "x", x+x ) 
print x                  '' hellohello
fxm
Posts: 9827
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Postby fxm » Sep 15, 2020 20:46

coderJeff wrote:
First, consider this example that doesn't use unique ids.
It works, because '__counter__' variable is defined in a local SCOPE and therefore allows nesting.

Code: Select all

#macro repeat ? ( count )
   scope
      dim __counter__ as uinteger = count
      while( __counter__)
#endmacro

#macro end_repeat
         __counter__ -= 1
      wend
   end scope   
#endmacro


repeat 4
   print "outer"

   repeat 3
      print "--- inner"

   end_repeat

end_repeat
It goes all wrong though, if there happens to be some other '__counter__' symbol defined by either the user or another macro that is defined in an outer scope and then used in an inner scope.

To protect against a duplicate name from somewhere else, can use the unique id macros.

Code: Select all

#macro repeat ? ( count )
   __FB_UNIQUEID_PUSH__( ctx )
   scope
      dim __FB_UNIQUEID__( ctx ) as uinteger = count
      while( __FB_UNIQUEID__( ctx ) )
#endmacro

#macro end_repeat
         __FB_UNIQUEID__( ctx ) -= 1
      wend
   end scope   
   __FB_UNIQUEID_POP__( ctx )
#endmacro

repeat 4
   print "outer"

   repeat 3
      print "--- inner"

   end_repeat

end_repeat


It seems to me that by using "unique identifiers" in the second example, the [Scope ... End Scope] block becomes useless (unlike the first example where it is always mandatory), except if the user defines in the same scope an identifier of the form "LT_xxxx" because it could be matching to the first defined "unique identifier" (outside the [While...Wend] block) ?
Otherwise it is not demonstrative.
coderJeff
Site Admin
Posts: 3282
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Basic-Macros in fbc 1.08

Postby coderJeff » Sep 16, 2020 13:55

fxm wrote:It seems to me that by using "unique identifiers" in the second example, the [Scope ... End Scope] block becomes useless (unlike the first example where it is always mandatory),

OK, that's fair.

except if the user defines in the same scope an identifier of the form "LT_xxxx" because it could be matching to the first defined "unique identifier" (outside the [While...Wend] block) ?

Any user symbol of the form 'LT_xxxx' should be avoided.
More I was thinking that the first example of 'repeat' macro is nearly generic except for use of '__counter__' symbol. '__counter__'. Provided user never uses '__counter__' for something else like a global variable and try to refer to it inside the scope, it's all fine. No need to use unique identifiers.

Otherwise it is not demonstrative.

Perhaps following is a more interesting example. A pattern where there is a series of steps is required for setup, some work to do, and finishing cleanup. If any one step goes wrong, need to abort to the appropriate cleanup step.

Code: Select all

#macro setup
   __FB_UNIQUEID_PUSH__( __cleanup__ )
   scope
#endmacro

#macro end_setup
   __FB_UNIQUEID_PUSH__( __cleanup__ )
   scope
#endmacro

#macro abort
   goto __FB_UNIQUEID__( __cleanup__ )
#endmacro

#macro cleanup
   end scope
   __FB_UNIQUEID__( __cleanup__ ):
   __FB_UNIQUEID_POP__( __cleanup__ )
#endmacro

#macro end_cleanup
   end scope
   __FB_UNIQUEID__( __cleanup__ ):
   __FB_UNIQUEID_POP__( __cleanup__ )
#endmacro

function copyfile( byref srcfile as string, byref dstfile as string ) as boolean
   function = false

   const buffer_size = 1024

   setup '' buffer
      dim as ubyte ptr buffer = allocate( buffer_size )
      if( buffer = 0 ) then
         print "no memory"
         abort
      end if

   setup '' source file
      var f1 = freefile
      if( open( srcfile for binary access read as f1 ) <> 0 ) then
         print "unable to open source file"
         abort
      end if

   setup '' destination file
      var f2 = freefile
      if( open( dstfile for input as f2 ) = 0 ) then
         print "destination file already exists"
         abort
      else
         close f2
      end if

      if( open( dstfile for binary as f2 ) <> 0 ) then
         print "unable to open destination file"
         abort
      end if
   
   end_setup

   dim length as ulongint = lof(f1)
   dim bytes as ulongint = buffer_size

   while( length > 0 )
      if( length < bytes ) then
         bytes = length
      end if

      if( get( #f1, , *buffer, bytes ) <> 0 ) then
         print "read error"
         abort
      end if

      if( put( #f2, , *buffer, bytes ) <> 0 ) then
         print "write error"
         abort
      end if

      length -= bytes
   wend

   function = true

   cleanup '' destination file
      close f2

   cleanup '' source file
      close f1

   cleanup '' memory
      deallocate buffer

   end_cleanup

end function

''
if( command(1) = "" ) then
   print "need source file"
   end 1
end if

if( command(2) = "" ) then
   print "need destination file"
   end 1
end if

copyfile command(1), command(2)


I was attempting to also create macros for 'retry' and 'ignore' actions, but a found another bug. in next example __FB_UNIQUEID_POP__(id) isn't skipped over. I'm just trying to figure out if this was always a kind of bug, even in older versions of fbc, but maybe just didn't matter because macro expansions didn't have internal side effects.

Code: Select all

#macro M
   #if 0
      __FB_UNIQUEID_POP__(id)
   #endif
#endmacro

M


Ah yes, in older versions of fbc also, this causes some trouble:

Code: Select all

#define X(arg1)

#macro M
   #if 0
   X ( 1
   #endif
#endmacro

M
fxm
Posts: 9827
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Postby fxm » Sep 16, 2020 18:40

fxm wrote:About updating the documentation to include these "basic macros":
- I was thinking of just adding them to the already existing 'Predefined Symbols' list / 'Intrinsic Definitions' list.
- The only page where they would be functionally grouped together (under the 'Basic-Macros' tittle) would be the 'Intrinsic Defines' documentation page.
=> pages to update: /wiki/PrintToc, /wiki/CatPgFullIndex, /wiki/CatPgFunctIndex, /wiki/CatPgDddefines.
=> pages to create: one for each "basic macro":

Code: Select all

__FB_ARG_COUNT__        KeyPgDdfbargcount
__FB_ARG_LEFTOF__       KeyPgDdfbargleftof
__FB_ARG_RIGHTOF__      KeyPgDdfbargrightof
__FB_JOIN__             KeyPgDdfbjoin
__FB_QUOTE__            KeyPgDdfbquote
__FB_UNIQUEID__         KeyPgDdfbuniqueid
__FB_UNIQUEID_POP__     KeyPgDdfbuniqueidpop
__FB_UNIQUEID_PUSH__    KeyPgDdfbuniqueidpush
__FB_UNQUOTE__          KeyPgDdfbunquote


Done:
- KeyPgDdfbuniqueid → fxm [new page created]
- KeyPgDdfbuniqueidpop → fxm [new page created]
- KeyPgDdfbuniqueidpush → fxm [new page created]
- KeyPgDdfbargcount → fxm [new page created]
- KeyPgDdfbunquote → fxm [new page created]
- KeyPgDdfbquote → fxm [new page created]
- KeyPgDdfbargleftof → fxm [new page created]
- KeyPgDdfbargrightof → fxm [new page created]
- KeyPgDdfbjoin → fxm [new page created]
- CatPgDddefines → fxm [added links to basic-macros]
- CatPgFunctIndex → fxm [added links to basic-macros]
- CatPgFullIndex → fxm [added links to basic-macros]
- PrintToc → fxm [added links to basic-macros]
Any improvement (complement, correction) would be welcome.
coderJeff
Site Admin
Posts: 3282
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Basic-Macros in fbc 1.08

Postby coderJeff » Sep 17, 2020 3:20

fxm, that's awesome! For my first try at the features, this is a very good first offering on the documentation. Thank-you so much.

1)
FYI, as I've been working with these new macros, there are 2 terms I find myself using.

'text' aka 'unquoted text' which means to me regular source code / macro definition text.
- For example '#define X print' where 'X' resolves to the unquoted text print.

'quoted text' which means to me text that is enclosed in double quotes, and internal quotes are escaped with double-double quotes.
- For example '#define X __fb_quote__( print "hi" )' where 'X' resolves to the quoted text "print ""hi""".

I don't know if you have suggestion for better terms. I've been using those terms just for myself to keep it straight what each macro returns.
__fb_arg_count__ returns numerical value as unquoted text
__fb_uniqueid_push__ & __fb_uniqueid_pop__ return nothing
__fb_uniqueid__ returns unquoted text
__fb_quote__ return quoted text
__fb_unquote__ returns unquoted text
__fb_arg_leftof__, __fb_arg_rightof, __fb_join__, return unquoted text

2)
I see from your example on __fb_arg_count__, we still need something like __fb_arg__(N) to get arguments from the variadic argument list.
To note with __fb_arg_count__, it also respects parentheses level, so __fb_arg_count__( a, b(x,y), c(x,y), d ) returns 4, even though we have 5 commas.

3)
with updates coming soon, not sure if you will see them in next daily build or the one after:
__fb_uniqueid__ causes compile time error if stack has been defined, but is currently empty
__fb_uniqueid__ returns empty string if the stack has never been defined (let me know if you think this should also be an error)
__fb_uniqueid_pop__ will cause compile error if stack underflows (too many pops)

4)
I'm close on __fb_eval__. It works they way I want, expect if there is a syntax error the compiler crashes. After __fb_eval__(expr), and maybe __fb_arg__(N), that pretty much covers all the low level stuff I was thinking of. I imagine it may get changed or refined after user feedback.

fxm,
Fabulous support for us on the wiki & documentation. Thank-you.
fxm
Posts: 9827
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Postby fxm » Sep 17, 2020 5:28

coderJeff wrote:2)
I see from your example on __fb_arg_count__, we still need something like __fb_arg__(N) to get arguments from the variadic argument list.
.....

Good idea, I was thinking about it too.

coderJeff wrote:2)
.....
To note with __fb_arg_count__, it also respects parentheses level, so __fb_arg_count__( a, b(x,y), c(x,y), d ) returns 4, even though we have 5 commas.

I tried to rephrase the corresponding sentence in the documentation page (/wiki/KeyPgDdfbargcount).

coderJeff wrote:3)
with updates coming soon, not sure if you will see them in next daily build or the one after:
__fb_uniqueid__ causes compile time error if stack has been defined, but is currently empty
__fb_uniqueid__ returns empty string if the stack has never been defined (let me know if you think this should also be an error)
__fb_uniqueid_pop__ will cause compile error if stack underflows (too many pops)

- I think when the stack is empty or has never been filled (your first and second case), __fb_uniqueid__ should return an empty string with no error message. This can be useful to empty the stack without knowing beforehand the number of elements it contains, stopping when an empty string is returned.
(#if __FB_QUOTE__( __FB_UNIQUEID__( stk ) ) <> "" .....)
- On the other hand when the stack is empty (your third case), or even never filled, I would prefer __fb_uniqueid_pop__ should always induce an error message.
fxm
Posts: 9827
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Postby fxm » Sep 17, 2020 7:47

coderJeff wrote:I don't know if you have suggestion for better terms. I've been using those terms just for myself to keep it straight what each macro returns.
__fb_arg_count__ returns numerical value as unquoted text
__fb_uniqueid_push__ & __fb_uniqueid_pop__ return nothing
__fb_uniqueid__ returns unquoted text
__fb_quote__ return quoted text
__fb_unquote__ returns unquoted text
__fb_arg_leftof__, __fb_arg_rightof, __fb_join__, return unquoted text

Okay for the first 3 lines.

But unclear for the last 3 lines.
Example:
__fb_arg_leftof__( hello or "hello", or ) ) => hello
__fb_arg_rightof__( hello or "hello", or ) => "hello"
__fb_unquote__( """hello""" ) => "hello"
__fb_quote__( "hello" ) => $"""hello"""

Perhaps ('text' being for me the general term):
__fb_quote__ return the over-quoted text
__fb_unquote__ returns the sub-quoted text
__fb_arg_leftof__, __fb_arg_rightof, __fb_join__, return the initial left-text, initial right-text, initial joined-text
coderJeff
Site Admin
Posts: 3282
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Basic-Macros in fbc 1.08

Postby coderJeff » Sep 18, 2020 2:57

fxm wrote:__fb_quote__ return the over-quoted text
__fb_unquote__ returns the sub-quoted text
__fb_arg_leftof__, __fb_arg_rightof, __fb_join__, return the initial left-text, initial right-text, initial joined-text

I think that's good. Helps point out to the reader that something different is going on compared to plain text, or even strings, string literals, etc.

Changes merged in: __fb_uniqueid__(stack) returns empty string on undefined or empty stack
fxm
Posts: 9827
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Postby fxm » Sep 18, 2020 7:12

coderJeff wrote:Changes merged in: __fb_uniqueid__(stack) returns empty string on undefined or empty stack

OK, thanks.
fxm
Posts: 9827
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Postby fxm » Sep 18, 2020 8:08

__fb_uniqueid__ returns an unquoted text
__fb_quote__ return the over-quoted text
__fb_unquote__ returns the sub-quoted text

Corresponding documentation pages updated accordingly.
coderJeff
Site Admin
Posts: 3282
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Basic-Macros in fbc 1.08

Postby coderJeff » Sep 20, 2020 7:36

I have cleaned up the __fb_eval__ code that was added in an earlier update and now has a simple test in the test-suite.

__FB_EVAL__( arg ) macro evaluates arg at compile time.

- fbc creates a new lexer context and parses arg as an expression.
- arg must evaluate to a constant. Any expression that can be constant folded to a single value at compile time can be used.
- if the expression is invalid, a compile time error is generated

The 'assign( "var", expr )' is an OK example, I guess. On it's own, __fb_eval__ doesn't do much more than normal constant expressions in source.
fxm
Posts: 9827
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Postby fxm » Sep 20, 2020 9:31

The disturbing thing is that the "* .pp.bas" file can no longer be compiled in this case (disturbing for a ".bas" file).

With your above example:
file.bas

Code: Select all

#macro assign( sym, expr )
   #define tmp __FB_EVAL__( expr )
   __FB_UNQUOTE__( __FB_EVAL__( "#undef " + sym ) )
   __FB_UNQUOTE__( __FB_EVAL__( "#define " + sym + " " + __FB_QUOTE__( tmp ) ) )
   #undef tmp
#endmacro

#define x

assign( "x", 1 )
print x                  '' 1

assign( "x", x+1 )
print x                  '' 2

assign( "x", cos(1/x) )
print x                  '' 0.877..

assign( "x", "hello" )
print x                  '' hello

assign( "x", x+x )
print x                  '' hellohello

We get:
file.pp.bas

Code: Select all




"#undef " + "x"
1"#define " + "x" + " " + $"1"
print 1

"#undef " + "x"
1+1"#define " + "x" + " " + $"2"
print 2

"#undef " + "x"
cos(1/2)"#define " + "x" + " " + $"0.8775825618903728"
print .8775825618903728

"#undef " + "x"
"hello""#define " + "x" + " " + $"""hello"""
print "hello"

"#undef " + "x"
"hello"+"hello""#define " + "x" + " " + $"""hellohello"""
print "hellohello"

If it is an intermediate working file, we could have named it ".pp" only and kept ".pp.bas" for the final file to be compiled.
marcov
Posts: 3004
Joined: Jun 16, 2005 9:45
Location: Eindhoven, NL
Contact:

Re: Basic-Macros in fbc 1.08

Postby marcov » Sep 20, 2020 10:39

(fyi
.pp is already in use for Free Pascal (originally meaning something like pascal plus) and the build tool stuff "puppet".

It is easier to pick an unique extension that you can claim on github, ohloh etc)
fxm
Posts: 9827
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Postby fxm » Sep 20, 2020 13:34

Sometimes, for a simplest syntax, the "file.pp.bas" compiles but the output is different than from the "file.bas" directly compiled.

file.bas

Code: Select all

#macro m ( arg1, arg2 )
    Var x = arg1 + arg2
    Var y = __FB_EVAL__( arg1 + arg2 )
#endmacro

m("Free", "BASIC")

Print x
Print y

Sleep
Ouput:

Code: Select all

FreeBASIC
FreeBASIC

file.pp.bas

Code: Select all


 Var x ="Free" + "BASIC"
 Var y ="Free" + "BASIC" "FreeBASIC"

Print x
Print y

Sleep
Output:

Code: Select all

FreeBASIC
FreeBASICFreeBASIC
coderJeff
Site Admin
Posts: 3282
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Basic-Macros in fbc 1.08

Postby coderJeff » Sep 20, 2020 14:49

Thanks, fxm. I found the problem. __FB_EVAL__ is emitting the expression to .pp.bas when should only be emitting the result. I'll have a fix posted soon, so the PP output should be:

Code: Select all

 Var x ="Free" + "BASIC"
 Var y = "FreeBASIC"

Print x
Print y

Return to “Documentation”

Who is online

Users browsing this forum: No registered users and 1 guest