flat assembler
Message board for the users of flat assembler.

flat assembler > Programming Language Design > [fasmg] Inline macros. Why not?

Goto page 1, 2  Next

Do you like the idea of inline macros?
Yes, I like
80%
 80%  [ 4 ]
No, I don't
0%
 0%  [ 0 ]
Don't care
20%
 20%  [ 1 ]
Total Votes : 5

Author
Thread Post new topic Reply to topic
VEG



Joined: 06 Feb 2013
Posts: 77
Location: Minsk, Belarus
I've read somewhere here that Tomasz Grysztar doesn't like the idea of inline macros. But why? Maybe because it can add some ambiguity or it seems not easy to implement? Or it has some drawbacks?

Maybe you will like this idea of syntax which will fit FASMG well IMHO. And it will not be required to rewrite much things. The main idea is to use <? ... ?> construction in place where it is possible to put a variable name. Like:
Code:
db <? ... ?> varname = <? ... ?> mov eax, <? ... ?> invoke demo, 1, 2, <? ... ?>, 4
Between <? and ?> we can put just one instruction which will be translated by preprocessor to a call of a "struc" macro before current instruction, but with an anonymous label. Like this:
Code:
invoke demo, 1, 2, <? makeinlinestr "teststr" ?>, 4
It has to be translated to this:
Code:
anonlabel makeinlinestr "teststr" invoke demo, 1, 2, anonlabel, 4

All instructions from every <? ?> have to be added before the current instruction in the order from left to right. It is possible to enclose one <? ?> into another. Like this:
Code:
invoke demo, 1, 2, <? makeinlinestr <? i32tohex 12345 ?> ?>, 4
It has to be translated to this:
Code:
anonlabel1 i32tohex 12345 anonlabel2 makeinlinestr anonlabel1 invoke demo, 1, 2, anonlabel2, 4


These makeinlinestr, i32tohex and similar macros (which will be usable inline) can be just struc macros which emit nothing at this place and just set a value to a variable. If it emits something, it will be emitted exactly before an actual instruction.

It will allow even things like:
Code:
errcode = 123 ; some calculations err "error code: ", <? i32tostr errcode ?>, ', it is not good'
So, it will be a really universal thing, as I can see. And it will be pretty safe because of using a really rare combination of operators <? ... ?>.

What do you think?


Last edited by VEG on 01 Jun 2017, 13:47; edited 3 times in total
Post 30 May 2017, 15:14
View user's profile Send private message Visit poster's website Reply with quote
zhak



Joined: 12 Apr 2005
Posts: 489
Location: Belarus
allowing a macro to return a result of some inner calculations could be a nice feature
Post 30 May 2017, 15:37
View user's profile Send private message Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6876
Location: Kraków, Poland
VEG wrote:
I've read somewhere here that Tomasz Grysztar doesn't like the idea of inline macros. But why? Maybe because it can add some ambiguity or it seems not easy to implement? Or it has some drawbacks?
It is not that I don't like them, it is just that my design of fasm was based on some assumptions about the syntax of assembly languages, including one that this is a line-based language where the individual commands reside in separate lines. These assumptions are embedded deeply in the design of fasm and fasmg parsers and symbol structures and it is not possible to implement anything resembling inline macros without altering the basic building blocks of these engines.

Of course it is possible to emulate inline macros by pre-processing all lines with "macro ?". I don't know whether this is viable - it would be slow, of course, bo so are many other macro-based solutions in fasmg. And the syntax you proposed would be quite easy to detect and pre-process with MATCH in the "macro ?" handler.
Post 30 May 2017, 15:40
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6876
Location: Kraków, Poland
zhak wrote:
allowing a macro to return a result of some inner calculations could be a nice feature
Allowing to return a value is not very useful when the only place where the macro can be recognized and processed is as the initial symbol of a line.
Post 30 May 2017, 15:41
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6876
Location: Kraków, Poland
Just because I simply cannot resist writing some interesting macros for fasmg, I implemented the pre-processor using "macro ?" that I mentioned above. It uses the syntax with "<?" and "?>" markers that you suggested:
Code:
macro preprocess_and_execute line& local buffer,preprocessed,inline,cursor define buffer line : define preprocessed : while 1 match a <=? b, preprocessed buffer redefine preprocessed a redefine buffer b match x =?> y, buffer redefine cursor x redefine buffer y else err 'missing ?>' break end match redefine inline : while 1 match <=? d, cursor match i, inline redefine inline i <? end match redefine cursor d ?> else match c <=? d, cursor match i, inline redefine inline i c <? end match redefine cursor d ?> else match i, inline cursor redefine inline i break end match match =?> y, buffer redefine buffer y else match x =?> y, buffer match c, cursor redefine cursor c x end match redefine buffer y else err 'missing ?>' break end match end while match :command, inline preprocess_and_execute command match text, preprocessed $result redefine preprocessed text end match end match else match a, preprocessed buffer redefine preprocessed a break end match end while match :command:, preprocessed command end match end macro macro ? line& preprocess_and_execute line end macro
An inline macro should define "$result" variable holding the returned value (it can be symbolic).

A simple example how it works:
Code:
macro low value $result = value and 0FFFFh end macro macro high value $result = value shr 16 end macro include '8086.inc' address = 12345678h mov ax,<? low address ?> mov dx,<? high address ?>
And nesting is also allowed.
Post 30 May 2017, 16:45
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 77
Location: Minsk, Belarus
Tomasz Grysztar, you're crazy (in a good sense) =)

I've tested how it affects time of assembly. In my situation it is +33% of time (4 seconds instead of 3). It seems that I can live with it. Thank you very much.

It is really a must have feature for me. IMHO, 1 line = 1 macroinstruction is good for instructions which actually emit some bytes to output. But if some instruction just makes some work with variables, like conversion from int to string or just merging some strings into one variable, and doesn't emit anything, and this data will be used just as an argument of the next instruction which will emit some code, it looks not good and actually makes the code more cluttered.

Also I think I'll add some kind of prefix for these inline macro, just to avoid possible intersection with general macros. I'll add the "imacro" macros which will define such kind of inline macros with names like "imacro.usermacroname", and I'll add to the <? ?> handler automatic addition of the "imacro.", so it still will be possible to write <? usermacroname ?>, and it will not prevent a programmer from creation and using a normal (not inline) macro with the same name if it will be required.
Post 30 May 2017, 17:17
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6876
Location: Kraków, Poland
VEG wrote:
Tomasz Grysztar, you're crazy (in a good sense) =)
People keep telling me that. Wink
Post 30 May 2017, 18:20
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 77
Location: Minsk, Belarus
It seems that I completely misunderstand FASMG macros Sad I thought that it will be the easiest one, but it doesn't work. It has to work line a normal macro, but with adding of the _imacro postfix for an inline macro name. (I've decided to use postfix because in this case it will take into account namespaces).
Code:
macro imacro line& match iname args, line esc macro iname#_imacro args else match iname, line esc macro iname#_imacro end match end macro macro end?.imacro esc end macro end macro imacro low value $result = value and 0FFFFh end imacro
Result:
Code:
flat assembler version g.ht7g2 imacro.asm [94]: imacro low value macro ? [1] macro imacro_preprocess [63] macro imacro [1]: match iname args, line Error: missing end directive.
Inline macros is not a built-in feature, so all error messages have this preprocessing macro in the stack.

The preprocess macro which uses inline macros with _imacro postfix:
Code:
macro imacro_preprocess line& local buffer,preprocessed,inline,cursor define buffer line : define preprocessed : while 1 match a <=? b, preprocessed buffer redefine preprocessed a redefine buffer b match x =?> y, buffer redefine cursor x redefine buffer y ; dd <? ?> ; catches this error ; dd <? sub 2, <? ?> ?> ; doesn't catch it ; else match =?> y, buffer ; err 'empty <? ?>' ; break else err 'missing ?>' break end match redefine inline : while 1 match <=? d, cursor match i, inline redefine inline i <? end match redefine cursor d ?> else match c <=? d, cursor match i, inline redefine inline i c <? end match redefine cursor d ?> else match i, inline cursor redefine inline i break end match match =?> y, buffer redefine buffer y else match x =?> y, buffer match c, cursor redefine cursor c x end match redefine buffer y else err 'missing ?>' break end match end while match :command, inline match inst args, command imacro_preprocess inst#_imacro args else match inst, command imacro_preprocess inst#_imacro end match match text, preprocessed $result redefine preprocessed text end match end match else match a, preprocessed buffer redefine preprocessed a break end match end while match :command:, preprocessed command end match end macro macro ? line& imacro_preprocess line end macro


Last edited by VEG on 31 May 2017, 18:09; edited 2 times in total
Post 31 May 2017, 18:05
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6876
Location: Kraków, Poland
When you start the nested macro, END MATCH becomes part of the inner macro and the original block remains unclosed.

You may find this article helpful: https://board.flatassembler.net/topic.php?t=19496
Post 31 May 2017, 18:08
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6876
Location: Kraków, Poland
I modified your sample to show how you can get it working:
Code:
macro imacro line& local begin match iname args, line macro begin esc macro iname#_imacro args end macro else match iname, line macro begin esc macro iname#_imacro end macro end match begin end macro macro end?.imacro! esc end macro end macro imacro low value $result = value and 0FFFFh end imacro
First, it uses the "begin" proxy macro to start the new macro only after closing the MATCH block. Second, the "end?.imacro" needs to be unconditional (hence the "!" modifier) to be unrolled while a macro definition is in process (otherwise it is consumed into a macro body).
Post 31 May 2017, 18:16
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 77
Location: Minsk, Belarus
Oh, nice trick with the "begin" local macro. But it seems that it has some conflicts with the preprocessing macro:
Code:
macro imacro_preprocess line& local buffer,preprocessed,inline,cursor define buffer line : define preprocessed : while 1 match a <=? b, preprocessed buffer redefine preprocessed a redefine buffer b match x =?> y, buffer redefine cursor x redefine buffer y ; dd <? ?> ; catches this error ; dd <? sub 2, <? ?> ?> ; doesn't catch it ; else match =?> y, buffer ; err 'empty <? ?>' ; break else err 'missing ?>' break end match redefine inline : while 1 match <=? d, cursor match i, inline redefine inline i <? end match redefine cursor d ?> else match c <=? d, cursor match i, inline redefine inline i c <? end match redefine cursor d ?> else match i, inline cursor redefine inline i break end match match =?> y, buffer redefine buffer y else match x =?> y, buffer match c, cursor redefine cursor c x end match redefine buffer y else err 'missing ?>' break end match end while match :command, inline match inst args, command imacro_preprocess inst#_imacro args else match inst, command imacro_preprocess inst#_imacro end match match text, preprocessed $result redefine preprocessed text end match end match else match a, preprocessed buffer redefine preprocessed a break end match end while match :command:, preprocessed command end match end macro macro ? line& imacro_preprocess line end macro macro imacro line& local begin match iname args, line macro begin esc macro iname#_imacro args end macro else match iname, line macro begin esc macro iname#_imacro end macro end match begin end macro macro end?.imacro! esc end macro end macro imacro low value $result = value and 0FFFFh end imacro
Result:
Code:
flat assembler version g.ht7g2 imacro.asm [102]: imacro low value macro ? [1] macro imacro_preprocess [64]: match :command:, preprocessed Error: missing end directive.
It seems that it starts a new macro and the end of preprocessing macro becomes a part of this inner macro =(
Post 31 May 2017, 18:34
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 77
Location: Minsk, Belarus
I've replaced this:
Code:
match :command:, preprocessed command end match end macro
to this (using your trick):
Code:
local execute match :command:, preprocessed macro execute command end macro end match execute end macro
And it seems that it works.
Post 31 May 2017, 18:37
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6876
Location: Kraków, Poland
You may need to make all things unconditional otherwise the "macro ?" jumps in in wrong places:
Code:
macro imacro! line& match iname args, line macro begin_imacro! esc macro iname#_imacro args end macro else match iname, line macro begin_imacro! esc macro iname#_imacro end macro end match begin_imacro end macro macro end?.imacro! esc end macro purge begin_imacro end macro
I remade the sample without use of LOCAL, because I just noticed a problem with LOCAL in current version of fasmg. I think it should have been defined as unconditional in a manner similar to ESC.
Post 31 May 2017, 18:47
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 77
Location: Minsk, Belarus
But the "macro ?" is supposed to not affect any code without <? ?>. Is this fix ok for such purpose?

I've just read why unconditional macros could be required (and it is understandable). But it seems that the FASMG Manual says nothing about unconditional ESC (and LOCAL). Or you're talking about stuff from this topic?
Post 31 May 2017, 19:26
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6876
Location: Kraków, Poland
VEG wrote:
But the "macro ?" is supposed to not affect any code without <? ?>. Is this fix ok for such purpose?
It should be OK, but it is better to skip unnecessary processing of "macro ?" where possible.

VEG wrote:
I've just read why unconditional macros could be required (and it is understandable). But it seems that the FASMG Manual says nothing about unconditional ESC (and LOCAL). Or you're talking about stuff from this topic?
Yes, the manual currently does not define a complete "catalog" of unconditional directives other that control directives (these are unambiguously defined as unconditional in section 11). The details of what other instructions are unconditional are still being worked out, as shown on the example of LOCAL. I may update the documentation with a precise information in the future.
Post 31 May 2017, 19:34
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 77
Location: Minsk, Belarus
A critical bug:
Code:
address = 12345678h dw <? low address ?>,<? high address ?>
It outputs 0x1234 twice. I think that it is because this macro uses one result variable for all results, so previous results are overwritten to the latest one.

I'm trying to resolve it like this:
Code:
macro imacro_preprocess line& local buffer,preprocessed,inline,cursor,resultn resultn = 0 define buffer line : define preprocessed : while 1 match a <=? b, preprocessed buffer redefine preprocessed a redefine buffer b match x =?> y, buffer redefine cursor x redefine buffer y else err 'missing ?>' break end match redefine inline : while 1 match <=? d, cursor match i, inline redefine inline i <? end match redefine cursor d ?> else match c <=? d, cursor match i, inline redefine inline i c <? end match redefine cursor d ?> else match i, inline cursor redefine inline i break end match match =?> y, buffer redefine buffer y else match x =?> y, buffer match c, cursor redefine cursor c x end match redefine buffer y else err 'missing ?>' break end match end while local result#resultn match :command args, inline imacro_preprocess command#_imacro result#resultn, args else match :command, inline imacro_preprocess command#_imacro result#resultn else err 'should not be here' end match match text, preprocessed result#resultn redefine preprocessed text end match resultn = resultn + 1 else match a, preprocessed buffer redefine preprocessed a break end match end while local execute match :command:, preprocessed macro execute command end macro end match execute end macro macro ? line& imacro_preprocess line end macro macro imacro line& local begin match iname args, line macro begin esc macro iname#_imacro result, args end macro else match iname, line macro begin esc macro iname#_imacro result end macro end match begin end macro macro end?.imacro! esc end macro end macro imacro low value result = value and 0FFFFh end imacro imacro high value result = value shr 16 end imacro address = 12345678h dw <? low address ?>,<? high address ?> address = 11223344h dw <? low address ?>,<? high address ?>
But it doesn't work because local result#resultn always generates "resultresultn", not "result0" and etc. How to attach a number from a variable to another variable name?

I've changed the code a bit. Now the result variable isn't a global variable, it is a local variable which is passed as the first argument for an imacro, and the imacro sets a value to this variable. Also I'm using just "result" as a name of the result variable, like it is in good old Pascal/Delphi Smile

An idea. Maybe it will be very convenient to allow another variant of label which will generate a new local variable every time when this instruction is executed. Like when "newlocal temp" will be used inside a loop, this "temp" variable will be new every iteration of this loop. It will make such code much simpler. It will be possible to use just "local result", and every iteration this "result" will be a new local variable.


Last edited by VEG on 01 Jun 2017, 06:38; edited 3 times in total
Post 01 Jun 2017, 06:00
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 77
Location: Minsk, Belarus
I've used such trick:
Code:
repeat 1, n:resultn local result#n result reequ result#n end repeat
and now I'm using just "result" in every place where "result#resultn" was used. It seems that "result" contains right names like "result0", but the code still outputs the same value twice.

The whole code:
Code:
macro imacro_preprocess line& local buffer,preprocessed,inline,cursor,result,resultn resultn = 0 define buffer line : define preprocessed : while 1 match a <=? b, preprocessed buffer redefine preprocessed a redefine buffer b match x =?> y, buffer redefine cursor x redefine buffer y else err 'missing ?>' break end match redefine inline : while 1 match <=? d, cursor match i, inline redefine inline i <? end match redefine cursor d ?> else match c <=? d, cursor match i, inline redefine inline i c <? end match redefine cursor d ?> else match i, inline cursor redefine inline i break end match match =?> y, buffer redefine buffer y else match x =?> y, buffer match c, cursor redefine cursor c x end match redefine buffer y else err 'missing ?>' break end match end while repeat 1, n:resultn local result#n result reequ result#n end repeat match :command args, inline imacro_preprocess command#_imacro result, args else match :command, inline imacro_preprocess command#_imacro result end match match text, preprocessed result redefine preprocessed text end match resultn = resultn + 1 else match a, preprocessed buffer redefine preprocessed a break end match end while local execute match :command:, preprocessed macro execute command end macro end match execute end macro macro ? line& imacro_preprocess line end macro macro imacro line& local begin match iname args, line macro begin esc macro iname#_imacro result, args end macro else match iname, line macro begin esc macro iname#_imacro result end macro end match begin end macro macro end?.imacro! esc end macro end macro imacro low value result = value and 0FFFFh end imacro imacro high value result = value shr 16 end imacro address = 12345678h dw <? low address ?>,<? high address ?> address = 11223344h dw <? low address ?>,<? high address ?>
Post 01 Jun 2017, 06:07
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 77
Location: Minsk, Belarus
Ok, I've fixed it. I moved the whole block which uses the "result" variable into the "repeat" block, and now I'm using it like result#n, and it works.

So, working version without the bug:
Code:
macro imacro_preprocess line& local buffer,preprocessed,inline,cursor,resultn resultn = 0 define buffer line : define preprocessed : while 1 match a <=? b, preprocessed buffer redefine preprocessed a redefine buffer b match x =?> y, buffer redefine cursor x redefine buffer y else err 'missing ?>' break end match redefine inline : while 1 match <=? d, cursor match i, inline redefine inline i <? end match redefine cursor d ?> else match c <=? d, cursor match i, inline redefine inline i c <? end match redefine cursor d ?> else match i, inline cursor redefine inline i break end match match =?> y, buffer redefine buffer y else match x =?> y, buffer match c, cursor redefine cursor c x end match redefine buffer y else err 'missing ?>' break end match end while repeat 1, n:resultn local result#n match :command args, inline imacro_preprocess command#_imacro result#n, args else match :command, inline imacro_preprocess command#_imacro result#n end match ; if ~ defined result#n ; err 'result is undefined' ; end if match text, preprocessed result#n redefine preprocessed text end match end repeat resultn = resultn + 1 else match a, preprocessed buffer redefine preprocessed a break end match end while local execute match :command:, preprocessed macro execute command end macro end match execute end macro macro ? line& imacro_preprocess line end macro macro imacro line& local begin match iname args, line macro begin esc macro iname#_imacro result, args end macro else match iname, line macro begin esc macro iname#_imacro result end macro end match begin end macro macro end?.imacro! esc end macro end macro imacro low value result = value and 0FFFFh end imacro imacro high value result = value shr 16 end imacro address = 12345678h dw <? low address ?>,<? high address ?> address = 11223344h dw <? low address ?>,<? high address ?>
But how can I assign an alias for a name from a "repeat" block for further using without inclusion of the whole block into the repeat?

UPD. Now I understand why an alias will not work. It's just because this code will remember the name of an alias:
Code:
match text, preprocessed result redefine preprocessed text end match


Last edited by VEG on 01 Jun 2017, 06:44; edited 2 times in total
Post 01 Jun 2017, 06:26
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6876
Location: Kraków, Poland
VEG wrote:
A critical bug:
Code:
address = 12345678h dw <? low address ?>,<? high address ?>
It outputs 0x1234 twice. I think that it is because this macro uses one result variable for all results, so previous results are overwritten to the latest one.
You're right, I made the macro that originally relied on $result being symbolic. In sample I used "=" because it seemed to work (though I still noted that it can be symbolic) but now you found the problems with it.

So the only good way to use that original macro is in fact to define $result symbolically:
Code:
imacro low value local x x = value and 0FFFFh $result equ x end imacro imacro high value local x x = value shr 16 $result equ x end imacro
Note that I designed it this way because I wanted the ability for inner macro to return any text which would replace the <? ... ?> block in the original line:
Code:
imacro var number if number = 0 $result equ edx else local offset offset = number*4 $result equ dword [ebp+offset] end if end imacro inc <? var 0 ?> inc <? var 1 ?>
Post 01 Jun 2017, 06:41
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 77
Location: Minsk, Belarus
Tomasz Grysztar, oh, nice idea! I haven't thought about this use case. It seems that my variant also works with symbolic variables.

Thank you so much. It was really one of features I was dreaming for. I'll write a bunch of useful inline macros and publish them.
Post 01 Jun 2017, 07:21
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page 1, 2  Next

< Last Thread | Next Thread >

Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 2004-2018, Tomasz Grysztar.

Powered by rwasa.