Author Topic: MASM Macro grammar ambiguity  (Read 1088 times)

johnsa

  • Member
  • ****
  • Posts: 713
    • Uasm
MASM Macro grammar ambiguity
« on: December 27, 2017, 01:48:19 AM »
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:

Code: [Select]

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

  • Member
  • *****
  • Posts: 1614
    • https://github.com/nidud/asmc
Re: MASM Macro grammar ambiguity
« Reply #1 on: December 27, 2017, 03:05:02 AM »
You may try using exitm<>

Code: [Select]
macroA macro arg1:REQ, arg2:VARARG
    echo arg2
    ENDM

macroB macro arg1:REQ, arg2:VARARG
    exitm<echo arg1, arg2>
    ENDM

macroA macroB(10,20,30), 40, 50

outout:
Code: [Select]
40, 50

Code: [Select]
macroA macro arg1:REQ, arg2:VARARG
    echo arg1 [arg2]
    ENDM

output:
Code: [Select]
echo 10, 20,30 [40,50]

AW

  • Member
  • *****
  • Posts: 1602
  • Let's Make ASM Great Again!
Re: MASM Macro grammar ambiguity
« Reply #2 on: December 27, 2017, 04:04:22 AM »
This also works (but does not pre-process the arguments as appears to be the aim  :( )

Code: [Select]
.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

  • Member
  • ****
  • Posts: 713
    • Uasm
Re: MASM Macro grammar ambiguity
« Reply #3 on: December 27, 2017, 04:24:39 AM »
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:
Code: [Select]

_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):
Code: [Select]

;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

  • Member
  • ****
  • Posts: 713
    • Uasm
Re: MASM Macro grammar ambiguity
« Reply #4 on: December 27, 2017, 04:26:30 AM »
So it was the expansion of _DEREF ...., _DEREFI() that was giving me this vararg headache.

nidud

  • Member
  • *****
  • Posts: 1614
    • https://github.com/nidud/asmc
Re: MASM Macro grammar ambiguity
« Reply #5 on: December 27, 2017, 05:24:18 AM »
That was very inefficient, imagine 100k vector instances, where the properties amount to 16bytes, but each instance has 10 methods (*8 bytes) etc..

Or the ability to write the constructor as you wish:
Code: [Select]
Class::Class proc uses rsi rdi

    mov rdi,[rcx]

    .if malloc( sizeof( Class ) )

mov rsi,rax
        .if rdi
            mov rax,[rdi].Class.lpVtbl
            mov [rsi].Class.lpVtbl,rax
        .elseif malloc( sizeof( ClassVtbl ) )
            mov [rsi].Class.lpVtbl,rax
            mov rdi,rax
    for x,<Method1,Method2,...>
                lea rax,Class@&x&
                stosq
            endm
        .endif
        mov rax,rsi
    .endif
    ret

Class::Class endp

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