News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

MASM Macro grammar ambiguity

Started by johnsa, December 27, 2017, 01:48:19 AM

Previous topic - Next topic

johnsa

So I ran into a bit of a headache the other day, which appears to me to be a grammar ambiguity given that macros do not use parentheses to mark the start and end of it's parameter list, if we nest two macros with VARARG parameters the result is a bit hard to follow:



macroA arg1:REQ, arg2:VARARG
macroA ENDM

macroB arg1:REQ, arg2:VARARG
macroB ENDM

; now invoke macroA with arg1 supplied by macroB
macroA macroB 10,20,30, 40, 50

(macroA (macroB 10,20,30), 40, 50) ; tried various bracket combinations..



Is the 40, 50 part of macroB's vararg list, or part of macroA's .. I tried various combinations of brackets, but those are normally just passed straight through to the macro..

Does anyone know if there is a hard rule for how this should be handled or is it really a grey area ?

nidud

#1
deleted

aw27

This also works (but does not pre-process the arguments as appears to be the aim  :( )


.386
.model flat, stdcall

includelib \masm32\lib\msvcrt.lib
printf proto C :ptr, :vararg
includelib \masm32\lib\kernel32.lib
ExitProcess proto :dword

.data

m1msg db "varargs from macroA: %d",10,0
m2msg db "varargs from macroB: %d",10,0

.code

macroA MACRO arg1:REQ, arg2:VARARG
LOCAL count, item
count=0
FOR item,<arg2>
count = count + 1
ENDM
invoke printf, offset m1msg, count
arg1
ENDM

macroB MACRO arg1:REQ, arg2:VARARG
LOCAL count, item

count=0
FOR item,<arg2>
count = count + 1
ENDM
invoke printf, offset m2msg, count
ENDM

main proc
macroA <macroB 1,2,3, 4, 5, 6, 7, 8, 9, 10>, 11, 12, 13, 14
invoke ExitProcess,0
ret
main endp

end main



varargs from macroA: 4
varargs from macroB: 9

johnsa

That was sort of closer to what I was trying to do, and oddly I was getting strange results where arguments where all out of whack.. but now all of a sudden it appears to be working ... :icon_confused:

What I was busy doing in any event is the following:
I've changed the way the OO layer in UASM works, so now two backing classes are created, one for the vtable itself, and one for the static members and properties. This means that an actual instance need only keep the vtbl pointer where-as before it had the full vtable in it. That was very inefficient, imagine 100k vector instances, where the properties amount to 16bytes, but each instance has 10 methods (*8 bytes) etc.. I also used the opportuinity to clean up the code produced and optimise it when object pointers are already in rcx it doesn't bother copying the value into rcx etc.
The other advantage to this approach was that now the COM invocation and OO invocation are identical / compatible so i've removed _VCOM and _VCOMINVOKE, and they can all share the same _V and _VINVOKE macro library functions.

I also added support for & (address-of) to both C style calls and the object method calls.

While I was doing all of this I though it would be super useful to extend the pointer operator -> to allow multiple occurences, so something like: myObject->myOtherObject->MyMethod(1,2,3.0)
Basically creating a de-referencing chain.. so the new macros i've create to implement the de-referencing are _DEREF and _DEREFI. Because of how I'm trying to implement this entire thing as a front-end shim so all the heavy lifting is done by the pre-existing macro engine in terms of evaluating parameters, handling nesting requires that whatever I do must be expanded from a single line, into another single line. I don't have the luxury of being able to use AddLineQueueX to expand the statements fully.

So the new macros look like this:


_DEREF MACRO itype:REQ, proc:REQ, args:REQ, derefs:VARARG
    LOCAL i, ptrstr, typestr
    i = 0
    FOR dref, <derefs>
        IF i MOD 2 EQ 0
            ptrstr TEXTEQU <&dref&>
        ELSE
            typestr TEXTEQU <&dref&>
            IF i EQ 1
                % IF(OPATTR(ptrstr)) EQ 0x30
                    % mov rcx, &ptrstr&
                ELSE
                    % mov rcx, @CatStr(<[>, <&ptrstr&>, <].>, <&typestr&>)
                ENDIF
            ELSE
                % mov rcx, @CatStr(<[>, <&ptrstr&>, <].>, <&typestr&>)
            ENDIF
        ENDIF
    i = i + 1
    ENDM
    IFNB <args>
    %  _VINVOKE rcx, itype, proc, <&args&>
    ELSE
       _VINVOKE rcx, itype, proc
    ENDIF
ENDM

_DEREFI MACRO itype:REQ, proc:REQ, args:REQ, derefs:VARARG
    LOCAL i, ptrstr, typestr
    i = 0
    FOR dref, <derefs>
        IF i MOD 2 EQ 0
            ptrstr TEXTEQU <&dref&>
        ELSE
            typestr TEXTEQU <&dref&>
            IF i EQ 1
                % IF(OPATTR(ptrstr)) EQ 0x30
                    % mov rcx, &ptrstr&
                ELSE
                    % mov rcx, @CatStr(<[>, <&ptrstr&>, <].>, <&typestr&>)
                ENDIF
            ELSE
                % mov rcx, @CatStr(<[>, <&ptrstr&>, <].>, <&typestr&>)
            ENDIF
        ENDIF
        i = i + 1
    ENDM
    IFNB <args>
       % EXITM<_V(rcx, itype, proc, <&args&>)>
    ELSE
       % EXITM<_V(rcx, itype, proc)>
    ENDIF
ENDM



Then all the pre-parse has to do is evaluate the invocation and generate a new line as follows:
(This also allows support for direct register assumed as pointer, or indirect register assumed as pointer to type.

(Test-cases):


;person1->Calc(2.0)
_DEREF Person, Calc, <2.0>, person1, Person
;mov rcx,[person1].Person
;_VINVOKE rcx, Person, Calc, <2.0>

;person1->lpComp->Func(2.0)
_DEREF Company, Func, <2.0>, person1, Person, <rcx>, Person.lpComp
;mov rcx,[person1].Person
;mov rcx,[rcx].Person.lpComp
;_VINVOKE rcx, Company, Func, <2.0>

;assume rsi:PTR Person
lea rsi,person1
;[rsi]->Calc(3.0)
;assume rsi:nothing
_DEREF Person, Calc, <3.0>, <[rsi]>, Person
;mov rcx,[rsi].Person
;_VINVOKE rcx, Person, Calc, <3.0>

;assume rsi:PTR Person
lea rsi,person1
;[rsi]->lpComp->Func(2.0)
;assume rsi:nothing
_DEREF Company, Func, <2.0>, <[rsi]>, Person, <rcx>, Person.lpComp
;mov rcx,[rsi].Person
;mov rcx,[rcx].Person.lpComp
;_VINVOKE rcx, Company, Func, <2.0>

mov rsi,person1
;rsi->Calc(3.0)
_DEREF Person, Calc, <3.0>, <rsi>, Person
;mov rcx,rsi
;_VINVOKE rcx, Person, Calc, <3.0>

mov rsi,person1
;rsi->lpComp->Func(2.0)
_DEREF Company, Func, <2.0>, <rsi>, Person, <rcx>, Person.lpComp
;mov rcx,rsi
;mov rcx,[rcx].Person.lpComp
;_VINVOKE rcx, Company, Func, <2.0>

;assume rsi:PTR Person
lea rsi,person1
xor rax,rax
;[rsi+rax]->Calc(3.0)
;assume rsi:nothing
_DEREF Person, Calc, <3.0>, <[rsi+rax]>, Person
;mov rcx,[rsi+rax].Person
;_VINVOKE rcx, Person, Calc, <3.0>

;person1->Calc( person2->Calc(1.0) )
;person1->Calc( _DEREFI(Person, Calc, <1.0>, person2, Person) )
;person1->Calc( _V(person2, Person, Calc, 1.0) )
_DEREF Person, Calc, _DEREFI(Person, Calc, <1.0>, person2, Person), person1, Person
;mov rcx,[person1].Person
;_VINVOKE rcx, Person, Calc, <_V(person2, Person, Calc, 1.0)>

;normal proc to method
;person1->Calc( NormalProc(2.0) )
;person1->Calc( arginvoke? )
_DEREF Person, Calc, arginvoke(1,1,NormalProc,<2.0>), person1, Person
;mov rcx,[person1].Person
;_VINVOKE rcx, Person, Calc, <_V(person2, Person, Calc, 1.0)>

;method to normal proc
;NormalProc( person1->Calc(2.0) )
;NormalProc2( eax, person1->Calc(2.0) )
NormalProc( _DEREFI(Person, Calc, <2.0>, person1, Person) )
NormalProc2( eax, _DEREFI(Person, Calc, <2.0>, person1, Person) )



johnsa

So it was the expansion of _DEREF ...., _DEREFI() that was giving me this vararg headache.

nidud

#5
deleted