flat assembler
Message board for the users of flat assembler.

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

Goto page Previous  1, 2

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: 78
Location: Minsk, Belarus
I've fixed another bug with overwriting results when nesting was used. Also case-insensitive imacros (with "?" as postfix) are acceptable now. And new instruction "ipurge" was added, it purges an imacro Smile
Code:
macro imacro_preprocess resultn, 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

                        repeat 1, n:resultn
                                resultn = resultn + 1
                                local result#n
                                match :command args, inline
                                        imacro_preprocess resultn, command#_imacro result#n, args
                                else match :command, inline
                                        imacro_preprocess resultn, command#_imacro result#n
                                end match
                                match text, preprocessed result#n
                                        redefine preprocessed text
                                end match
                        end repeat

                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&

        local resultn
        resultn = 0
        imacro_preprocess resultn, 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
        else 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

macro ipurge iname
        purge iname#_imacro
end macro

imacro concat first*,rest*&
        result = string first
        iterate s, rest
                result = string result + (s) shl (((bsr result - 1) shr 3 + 1) shl 3)
        end iterate
end imacro

imacro tohex number*,digits:8
        virtual at 0
                repeat digits
                        digit = ((number) shr ((%%-%) shl 2)) and 0Fh
                        if digit < 10
                                db '0'+digit
                        else
                                db 'A'+digit-10
                        end if
                end repeat
                load result:$ from 0
        end virtual
end imacro    


And a new problem Sad It seems that "macro ?" doesn't process lines inside other macros Sad
It works:
Code:
display <? concat '0x', <? tohex 12345 ?>, 10 ?>    
It doesn't work:
Code:
macro test
        display <? concat '0x', <? tohex 12345 ?>, 10 ?>
end macro
test    
Code:
flat assembler  version g.htee8
imacro.asm [157]:
        test
macro ? [4] macro imacro_preprocess [73] macro execute [1] macro test [1]:
        display <? concat '0x', <? tohex 12345 ?>, 10 ?>
Processed: display <? concat '0x', <? tohex 12345 ?>, 10 ?>
Error: invalid expression.    
My guess was that it is because of conditional/unconditional execution. I've tried to add "!" to the "macro ?" and "macro imacro_preprocess", and it seems that it goes into an endless loop as the result.


Last edited by VEG on 01 Jun 2017, 09:35; edited 1 time in total
Post 01 Jun 2017, 09:05
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 78
Location: Minsk, Belarus
It seems that "macro ?!" also doesn't preprocess lines inside macros:
Code:
macro ?! line&
        display `line, 10
        line
end macro

macro test
        db 0
end macro    
Outputs:
Code:
flat assembler  version g.htee8

macro test



1 pass, 0 bytes.    

And this variant:
Code:
macro ?! line&
        display `line, 10
        line
end macro

macro test
        db 0
end macro
db 1
test    
Outputs something crazy:
Code:
flat assembler  version g.htee8

macro test
db 1
test
db 0
end macro

1 pass, 2 bytes.    
When I remove ! from the "macro ?", output is more logical (but without macros at all):
Code:
flat assembler  version g.htee8
db 1
test

1 pass, 2 bytes.    

Was it done to avoid preprocessing of "macro ?" itself? How does "macro ?!" work? Maybe it will be ok to pass macros bodies to the "macro ?!" at least. It will be ok to ignore just "macro ?" or "macro ?!" bodies themselves for avoiding of preprocessing of own code.
Post 01 Jun 2017, 09:21
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6966
Location: Kraków, Poland
VEG wrote:
How does "macro ?!" work?
This one intercept absolutely every line with no exception. It is only really useful when you need to process the syntax completely on your own (possibly inside a section of code, because you can "purge ?" later).
Post 01 Jun 2017, 09:46
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6966
Location: Kraków, Poland
VEG wrote:
And a new problem Sad It seems that "macro ?" doesn't process lines inside other macros Sad
It works:
Code:
display <? concat '0x', <? tohex 12345 ?>, 10 ?>    
It doesn't work:
Code:
macro test
        display <? concat '0x', <? tohex 12345 ?>, 10 ?>
end macro
test    
Code:
flat assembler  version g.htee8
imacro.asm [157]:
        test
macro ? [4] macro imacro_preprocess [73] macro execute [1] macro test [1]:
        display <? concat '0x', <? tohex 12345 ?>, 10 ?>
Processed: display <? concat '0x', <? tohex 12345 ?>, 10 ?>
Error: invalid expression.    
My guess was that it is because of conditional/unconditional execution. I've tried to add "!" to the "macro ?" and "macro imacro_preprocess", and it seems that it goes into an endless loop as the result.
It does not work because "imacro_preprocess" processes the line "test", but it is no longer processed inside the "test" macro itself. To handle this you need to define "test" in such way that "imacro_preprocess" would get called for the lines inside "test" too. There is a couple of ways to do that.

First, you could simply make the "test" macro unconditional:
Code:
macro test!
        display <? concat '0x', <? tohex 12345 ?>, 10 ?> 
end macro 
test    


Second, you could define the macro with an analogous interceptor inside:
Code:
macro test
        macro ? line&
                ; ...
        end macro
        display <? concat '0x', <? tohex 12345 ?>, 10 ?>
        purge ?
end macro 
test    
Post 01 Jun 2017, 09:59
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6966
Location: Kraków, Poland
I remade your sample to show how some of the things may be done differently. I think that use of the namespace is simpler that the name suffix. I also added a "pmacro" variant which can be used to define macro that has preprocessing done for its line. We cannot safely do it for every macro, because applying this universally would also affect out own macros while they need to stay "low level".
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 
                        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 
                                imacro_preprocess imacro.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& 
        imacro_preprocess line
end macro

define imacro

macro imacro! definition&
        esc macro imacro.definition
end macro

macro end?.imacro! 
        esc end macro  
end macro  

macro ipurge? iname
        purge imacro.iname
end macro

macro pmacro! definition&
        esc macro definition
                macro ? line&
                        imacro_preprocess line
                end macro
end macro

macro end?.pmacro?!
                purge ?
        esc end macro
end macro

macro ppurge? pname
        purge pname
end macro

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

imacro concat first*,rest*&
        local result
        result = string first 
        iterate s, rest 
                result = string result + (s) shl (((bsr result - 1) shr 3 + 1) shl 3) 
        end iterate
        redefine $result result
end imacro 

imacro tohex number*,digits:8
        local result
        virtual at 0 
                repeat digits 
                        digit = ((number) shr ((%%-%) shl 2)) and 0Fh 
                        if digit < 10 
                                db '0'+digit 
                        else 
                                db 'A'+digit-10 
                        end if 
                end repeat 
                load result:$ from 0 
        end virtual
        redefine $result result
end imacro

pmacro test
        display <? concat '0x', <? tohex 12345 ?>, 10 ?> 
end pmacro
test    
Post 01 Jun 2017, 11:05
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6966
Location: Kraków, Poland
Another side note: this (excerpt from your macro) does not work like you probably expected it to:
Code:
local result#n 
match :command args, inline 
        imacro_preprocess resultn, command#_imacro result#n, args    
The LOCAL directive defines a parameter (just like it did in fasm 1) so it works similarly to how the macro parameters work. It means that it is only replaced when a token with such name is encountered, so it would replace "result0" but not "result#0". What you should do it in this case is to just use this one:
Code:
local result    
at startup, and then every "result#n" is going to become local, because "result" will be replaced with a symbol in local namespace and attaching additional suffix to it will create an additional symbol in local namespace. If this feels like a undocumented feature, you can use "result.value#n" instead - this defines "value#n" symbol in the namespace of "result" symbol, which is local, so everything then is local.
Post 01 Jun 2017, 11:19
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6966
Location: Kraków, Poland
VEG wrote:
Tomasz Grysztar, oh, nice idea! I haven't thought about this use case. It seems that my variant also works with symbolic variables.
What I had in mind there was to allow these inline macros to work like a true preprocessing, for example:
Code:
imacro d mode
        if mode = 16
                $result equ dw
        else
                $result equ dd
        end if
end imacro

<? d 16 ?> -1    
Post 01 Jun 2017, 12:04
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 78
Location: Minsk, Belarus
Thank you for your examples.

Tomasz Grysztar wrote:
I think that use of the namespace is simpler that the name suffix.

I had used suffix because I thought that it will help to honor current namespace. But it doesn't. Also it doesn't allow to call <? nsname.mname 1,2,3 ?> at all because match gets just the first piece before the dot, so it needs an improvement which will collect all pieces of name with dots. I just thought that it would be nice to preserve behavior of standard macros. It is not a frequent use case of course, so I'll try to do it later.
Post 01 Jun 2017, 13:16
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6966
Location: Kraków, Poland
VEG wrote:
I had used suffix because I thought that it will help to honor current namespace. But it doesn't. Currently, it doesn't allow to call <? nsname.mname 1,2,3 ?> at all because match gets just the first piece before the dot, so it needs an improvement which will collect all pieces of name with dots. I just thought that it would be nice to preserve behavior of standard macros. It is not a frequent use case of course, so I'll try to do it later.
Try matching at the separating space:
Code:
        match iname= args, line
                esc macro iname#_imacro result, args    
And perhaps even use a sub-namespace instead of a suffix?
Code:
        match iname= args, line
                esc macro iname.imacro result, args    
Post 01 Jun 2017, 13:45
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 78
Location: Minsk, Belarus
Quote:
Try matching at the separating space:
Nice, I like this simple solution. I think that I had seen it somewhere in the examples, but haven't noticed how useful it could be and forgot about it.
Quote:
And perhaps even use a sub-namespace instead of a suffix?
But an inline macro name can coincide with a real namespace, and it will define an "imacro" macro inside. It seems that "_imacro" postfix doesn't have any visible drawbacks. Maybe it looks not so beautiful (especially in error messages). But it is not a native solution. We don't have a separate class for inline macros, so we need some thing for differentiation. Actually, first thing that I had tried was the "imacro." prefix, because it looks like the most beautiful and simple solution for a general case. And then I remembered about namespaces...

About pmacro. If "macro" instruction will be redefined after enabling the "macro ?" and defining the imacro_preprocess macros, maybe it will not affect them? Hm... Maybe it will be better to redefine the "macro" instruction, but add some flag which will disable preprocessing inside current macro. Or add an "llmacro" (low-level macro) instruction for special cases. It worth to think how to make it better Smile
Post 01 Jun 2017, 15:25
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6966
Location: Kraków, Poland
VEG wrote:
About pmacro. If "macro" instruction will be redefined after enabling the "macro ?" and defining the imacro_preprocess macros, maybe it will not affect them?
One of the problems is that even the "imacro" needs to use the "macro" internally.
Post 01 Jun 2017, 15:56
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6966
Location: Kraków, Poland
As a curiosity: it also might be possible to redefine "macro" locally by embedding the area of source in a child namespace while using the "macro" instruction from the outer namespace when needed. But this is a very very tricky and fragile construction:
Code:
define original_macro macro

namespace embedded
postpone
        end namespace
end postpone

match _macro, original_macro
        _macro macro?! definition&
                _macro end?.macro?!
                        purge ?
                        esc end macro
                        purge end?.macro?
                end macro
                esc _macro definition
                esc esc _macro ? line&
                        imacro_preprocess line
                esc esc end macro
        end macro
end match

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

macro test
        display <? concat '0x', <? tohex 12345 ?>, 10 ?>
end macro

test    
Post 01 Jun 2017, 16:17
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6966
Location: Kraków, Poland
Scrap the above, it actually works much better if there is a proxy macro for END too:
Code:
define original_macro macro
define original_end end

namespace embedded
postpone
        end namespace
end postpone

define end?

match _macro:_end, original_macro:original_end
        iterate directive, macro,struc,virtual,namespace,if,match,rmatch,rawmatch,while,repeat,iterate,rept,irp,irpv,postpone
                _macro end?.directive?!
                       esc  _end directive
                _end macro
        end iterate
        _macro end?! directive
                end?.directive
        _end macro
        _macro macro?! definition&
                esc _macro definition
                _macro ? line&
                        imacro_preprocess line
                _end macro
        _end macro
        _macro end?.macro?!
                purge ?
                esc _end macro
        _end macro
end match

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

macro test
        display <? concat '0x', <? tohex 12345 ?>, 10 ?>
end macro

test    
This one is less tricky (the double ESC that previous one had to use is something that should not have to happen normally) and it works correctly even with nested macros.

Note that to allow "imacro" definitions to be processed correctly in the embedded namespace you have to make them use the original MACRO and END directives in the same way.
Post 01 Jun 2017, 18:59
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 78
Location: Minsk, Belarus
Looks frightening. Maybe it would be better to add some construction like "postpone", which will allow to execute some instructions every time when any new block of the code starts. Like this:
Code:
beforemacro
    imacro_init
end beforemacro    

It will enable the inline macros for every following macro (or any other kind of block which create new "context" for code).
Post 09 Dec 2017, 09:26
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6966
Location: Kraków, Poland
The problem is: if you try to re-define portions of the same language that you used to build these redefinitions, it is going to create self-interference no matter what. The less troublesome route is to define another separate language on top of the basic one which is necessary make higher layers work. That's why my proposed solution was to separate namespaces of the two languages so that they would not interfere with each other.
Post 09 Dec 2017, 15:56
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 Previous  1, 2

< 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 © 1999-2018, Tomasz Grysztar.

Powered by rwasa.