flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > Simple error I can't see or smth I don't understand

Author
Thread Post new topic Reply to topic
^_^



Joined: 29 Apr 2013
Posts: 4
^_^ 01 Dec 2013, 12:52
This macro allows you to use anonymous functions in fasm. It can be used like
Code:
mov eax, lambda[list of regs to preserve](list of arguments)
        function body
        ret
endlambda
stdcall eax, list of arguments
    

It's basically wrapper on top of proc macro. Weird thing is - it works only for full declaration (with args and preserve_list) otherwise fasm says "invalid macro argument" and points to endlambda, which is "endp }". I cannot see why, code parts are similar and was almost copy'n'paste'd.
Code (start with mov overloading):
Code:
format pe console
include "win32axp.inc"

;;============================================================
struc reequ arg
 {
    restore .
    . equ arg
 }

__macro_list equ 

macro list_append arg
{
    ;replace with rept
    match any, __macro_list
    \{
        __macro_list equ any, arg
    \}

    match , __macro_list
    \{
        __macro_list equ arg
    \}

}

__lambda_counter equ 0
macro __inc_lc
 {
    __lambda_counter equ __lambda_counter + 1
    rept 1 c:__lambda_counter
    \{
        res equ c
    \}
    
    match num, res
    \{
        __lambda_proc_current reequ __lambda_proc_\#num
        __lambda_macro_current reequ __lambda_macro_\#num
        list_append __lambda_macro_\#num
    \}
 }

macro __parse_lambda [arguments]
{
    common
    restore __lambda_preserve
    restore __lambda_args
    restore __lambda_parsed
    __lambda_parsed equ 0
    match =lambda[pres](args), arguments
    \{
        __lambda_preserve equ pres
        __lambda_args equ args
        __inc_lc 
        __lambda_parsed equ 4
    \} ;4
    match =lambda[pres](), arguments
    \{
        __lambda_preserve equ pres
        __inc_lc 
        __lambda_parsed equ 3
    \} ;3
    match =lambda(args), arguments 
    \{
        __lambda_args equ args
        __inc_lc 
        __lambda_parsed equ 2
    \} ;2
    match =lambda(), arguments 
    \{
        __inc_lc
        __lambda_parsed equ 1
    \} ;1
}

__lambda_parsed equ 0
macro mov dest*, [src*]
{
    common
    __parse_lambda src

    restore __matched
    __matched equ 0
    match =0, __lambda_parsed
    \{
        ;has not been parsed
        mov dest, src
        __matched equ 1 ;?
    \}

    match =1 =0, __lambda_parsed __matched
    \{
        ;without args
        match _lp _lm, __lambda_proc_current __lambda_macro_current ;do i really need this
        \\{
            mov dest, _lp 
            macro _lm {
            proc _lp
        \\}
    \}
    
    match =2 =0, __lambda_parsed __matched
    \{
        ;with arguments
        match _lp _lm, __lambda_proc_current __lambda_macro_current ;
        \\{ 
            mov dest, _lp 
            macro _lm {
            proc _lp __lambda_args
        \\}
    \}
    
    match =3 =0, __lambda_parsed __matched
    \{
        ;with preserve list
        match _lp _lm, __lambda_proc_current __lambda_macro_current ;
        \\{
            mov dest, _lp 
            macro _lm {
            proc _lp uses __lambda_preserve
        \\}       
    \}
    
    match =4 =0, __lambda_parsed __matched
    \{
        ;with preserve list and arguments
        match _lp _lm, __lambda_proc_current __lambda_macro_current ;
        \\{
            mov dest, _lp 
            macro _lm {
            proc _lp uses __lambda_preserve, __lambda_args
        \\}
    \}
}

;macro push [args]
;{
;    ;push and lea are unsupported right now
;}

endlambda fix endp }

macro __define_lambdas
{
    match __ml, __macro_list
    \{
        irp mac, __ml
        \\{
            mac
        \\}
    \}
}
;;============================================================

align 16
entry $
    ;mov ebx, lambda[esi edi ebx](x, y) ;this works
    ;            mov eax, [x]           ;others isn't
    ;            add eax, [y]
    ;            ret
    ;        endlambda

    mov edi, lambda[edx]()
                xor edx, edx
                mov eax, 3
                ret
            endlambda
            
    ;mov esi, lambda()
    ;            mov eax, 8
    ;            ret
    ;        endlambda
        
    ;mov edi, lambda(x, y)
    ;            mov eax, [x]
    ;            imul eax, [y]
    ;            ret
    ;        endlambda

    ;stdcall ebx, 2, 3
    call edi
    ;call esi
    ;stdcall edi, 2, 8

    ret

__define_lambdas
    
Post 01 Dec 2013, 12:52
View user's profile Send private message Reply with quote
l_inc



Joined: 23 Oct 2009
Posts: 881
l_inc 01 Dec 2013, 23:08
^_^
I can point to the very location of the error. But the global cause of such problems is the fragile and awry architecture of the macros (I mean the architecture of your macros, not the one of the fasm's macrolanguage). It's full of hidden problems which are going to progressively show up. Thus the architecture should be completely and very carefully reconsidered. In particular it's very error prone to open a brace at a code location separate from the code location where the corresponding closing brace is expected. Macroblocks should rather be declared in monolithic code pieces.

The actual difference between the working and non-working macroexpansion is that the working one engages the last match macroblock in the mov redefinition macro ("match =4 =0 ..."). The non-working examples make the _lm macro include the following match macroblock with it's closing brace used as the _lm definition ending. This way the _lm macro definition is closed before the first instruction of the lamda-function. Thus when it comes to endlamda the closing brace after endp is not consumed and therefore used as an argument of the endp macro. As long as endp does not take any arguments, this results in the error you see.

_________________
Faith is a superposition of knowledge and fallacy
Post 01 Dec 2013, 23:08
View user's profile Send private message Reply with quote
baldr



Joined: 19 Mar 2008
Posts: 1651
baldr 02 Dec 2013, 01:44
^_^,

l_inc pinned the problem exactly. It can be circumvented with some refactoring (to merge all cases into one):
Code:
        format  PE console
        include "Win32AX.Inc"

macro __lambdas {}; empty lambdas list

struc reequ [val] {
common
  restore .
  . equ val
}

macro mov dest*, [src*] {
common
  local __lambda
  lambda? equ -; not yet
  match =lambda args, src \{
    __proc equ proc __lambda args
    lambda? reequ +
  \}
  match =lambda, src \{
    __proc equ proc __lambda
    lambda? reequ +
  \}
  match -, lambda? \{; still not
    mov dest, src
    __proc equ
  \}
  match +, lambda? \{
    mov dest, __lambda
  \}
  restore lambda?
  match , __proc \{
    restore __proc
    rept 0 \\{; skip following 'match'
  \}
  match ___proc, __proc \{; expand '__proc'
    restore __proc
    macro __lambdas \\{
      __lambdas; process previous lambdas
      ___proc; start current lambda definition
  \}
}

endlambda fix endp }

        .code
here:
        mov     edi, lambda uses ebx, mask
          and   eax, [mask]
          ret
        endlambda
        stdcall edi, 1
        mov     edi, lambda
          mov   eax, STD_OUTPUT_HANDLE
          ret
        endlambda
        stdcall edi
        ret

__lambdas

        .end    here    
For simplicity lambda follows proc macro arguments pattern, except for name; rept 0 uses aforementioned preprocessor behavior to implement something like else clause for enclosing match (it skips everything up to corresponding closing brace, i.e. that of the following match block). Lambdas list is maintained using chained macros for simplicity too.
Post 02 Dec 2013, 01:44
View user's profile Send private message Reply with quote
l_inc



Joined: 23 Oct 2009
Posts: 881
l_inc 02 Dec 2013, 09:28
baldr wrote:
rept 0 uses aforementioned preprocessor behavior to implement something like else clause for enclosing match

This is a really nice one. Never thought of this way of implementing an "elsematch".

_________________
Faith is a superposition of knowledge and fallacy
Post 02 Dec 2013, 09:28
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20357
Location: In your JS exploiting you and your system
revolution 02 Dec 2013, 09:32
l_inc wrote:
baldr wrote:
rept 0 uses aforementioned preprocessor behavior to implement something like else clause for enclosing match

This is a really nice one. Never thought of this way of implementing an "elsematch".
Sure, it is kind of tricksy and all, but ultimately it is a hack due to the preprocessor design. Perhaps fasm2 can fix some of these ugly hacks?
Post 02 Dec 2013, 09:32
View user's profile Send private message Visit poster's website Reply with quote
l_inc



Joined: 23 Oct 2009
Posts: 881
l_inc 02 Dec 2013, 20:09
revolution
Killjoy. Razz

_________________
Faith is a superposition of knowledge and fallacy
Post 02 Dec 2013, 20:09
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20357
Location: In your JS exploiting you and your system
revolution 02 Dec 2013, 20:55
l_inc wrote:
Killjoy. Razz
I'm all for clever and novel solutions to problems. But sometimes a different path (a harder path) is ultimately going to be a better path (and often the more boring path). "Shortcuts can lead to long delays".
Post 02 Dec 2013, 20:55
View user's profile Send private message Visit poster's website Reply with quote
baldr



Joined: 19 Mar 2008
Posts: 1651
baldr 02 Dec 2013, 21:45
revolution,

Actually I used that hack only for proper cleanup: __proc symbolic constant is either empty or proc macro invocation. I can't restore it neither after macro directive, nor before it, except if inside match block to expand symbol; but match won't succeed with empty symbolic constant. So I choose to use rept 0 as a kind of goto.

Probably I'd overlooked some option available.
Post 02 Dec 2013, 21:45
View user's profile Send private message Reply with quote
l_inc



Joined: 23 Oct 2009
Posts: 881
l_inc 03 Dec 2013, 12:48
baldr
You could do it pretty conventionally by just declaring __proc as local. Another way would be to make a nested match and use ___proc as an expansion argument of the internal match. This way you don't need an additional restore for the constant you expand (in this case ___proc), cause its lifetime is limited by the external match macroblock.

But come on, guys! How do you define a "hack", if such a lovely implementation of an "elsematch" falls into this category? I mean, this approach is quite general-purpose and appropriate for any case, where one needs an "elsematch", which is a crucial criterion for something to be not a hack. Is it just because you wouldn't think of it at the first place? Well, not every originally nontrivial solution is a hack.

_________________
Faith is a superposition of knowledge and fallacy
Post 03 Dec 2013, 12:48
View user's profile Send private message Reply with quote
^_^



Joined: 29 Apr 2013
Posts: 4
^_^ 03 Dec 2013, 23:10
I merged those matches to one and expanded everything i could, so it works now (well, sort of), but after that example by baldr I can say that I really do not understand how this works.
l_inc wrote:
Macroblocks should rather be declared in monolithic code pieces.

But macro is just inserting pieces of text, will it really make a difference if I do it manually?
l_inc wrote:
This way the _lm macro definition is closed before the first instruction of the lamda-function

But how is this even possible? I thought match block would not be expanded, and this closing-match bracket will still be closing-match bracket, I have no idea how it becomes end of macro.
Post 03 Dec 2013, 23:10
View user's profile Send private message Reply with quote
baldr



Joined: 19 Mar 2008
Posts: 1651
baldr 04 Dec 2013, 00:16
^_^ wrote:
…I can say that I really do not understand how this works.
PREPSRC is invaluable tool for macro debugging once you grok its output.
^_^ wrote:
But how is this even possible? I thought match block would not be expanded, and this closing-match bracket will still be closing-match bracket, I have no idea how it becomes end of macro.
Proprocessor works pretty straightforwardly, after next-to-last match was expanded, its expansion is immediately followed by yet unprocessed last match, therefore source fragment becomes
Code:
;macro __lambda_macro_1{
; proc __lambda_proc_1 uses __lambda_preserve

;;;
;;; Here next-to-last 'match' expansion ends; following is the expansion of 'mov' macro top nesting level.
;;;

; match=4=0,__lambda_parsed __matched
;{
; match _lp _lm,__lambda_proc_current __lambda_macro_current
; \{
; mov edi,_lp
; macro _lm{
; proc _lp uses __lambda_preserve,__lambda_args
; \}
;}    
When parsing that, preprocessor places entire match inside __lambda_macro_1 definition, except for closing brace (as it terminates macro definition). Consequently, match wouldn't be preprocessed (yet).

If you have questions about my refactoring, just ask.
Post 04 Dec 2013, 00:16
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< 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-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.