Basic-Macros in fbc 1.08

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

Re: Basic-Macros in fbc 1.08

Post by coderJeff »

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
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Post by fxm »

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: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Basic-Macros in fbc 1.08

Post by coderJeff »

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
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Post by fxm »

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: PrintToc, CatPgFullIndex, CatPgFunctIndex, 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: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Basic-Macros in fbc 1.08

Post by coderJeff »

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
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Post by fxm »

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 (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
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Post by fxm »

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: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Basic-Macros in fbc 1.08

Post by coderJeff »

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
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Post by fxm »

coderJeff wrote: Changes merged in: __fb_uniqueid__(stack) returns empty string on undefined or empty stack
OK, thanks.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Post by fxm »

__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: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Basic-Macros in fbc 1.08

Post by coderJeff »

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
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Post by fxm »

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: 3455
Joined: Jun 16, 2005 9:45
Location: Netherlands
Contact:

Re: Basic-Macros in fbc 1.08

Post by marcov »

(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
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Basic-Macros in fbc 1.08

Post by fxm »

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: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Basic-Macros in fbc 1.08

Post by coderJeff »

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
Post Reply