cParamMask = $fffe0000
cMethodMask = $ffff0000


macro __search_method flag, num, count, list, cname, method {
  define flag FALSE
  rept count i:1 \{ match mname, method \\{ match class | name , list\\#i \\\{ match =mname, name \\\\{
    define flag TRUE
    num equ i
  \\\\} \\\} \\} \}
}


macro object name, parent {
  obj@name   equ name
  obj@parent equ parent

  name#.param?num = 0

  name#@count equ 0

  match any, parent \{
    name#@count equ parent#@count
    rept parent\#@count i:1 \\{
      name\\#@method\\#i equ parent\\#@method\\#i
      match class | mname, parent\\#i \\\{
        name\\\#mname = i or cMethodMask
;        display 3, "Copy:", \\\`name,\\\`mname, ' = ', \\\`i, 13, 10
      \\\}
    \\}
  \}

  macro param paramname*, getval*, setval* \{
    paramname = name#.param?num or cParamMask
    name#.param?num = name#.param?num + 1

      if getval eq NONE
        paramname\#.get = -1
      else
        paramname\#.get = getval
      end if

      if setval eq NONE  ; read only
        paramname\#.set = -1
      else
        paramname\#.set = setval
      end if
  \}

  macro method mname, [marg] \{
  \common
  \local f1, n1
    name\#mname\#.arguments equ marg
    define f1 FALSE
    __search_method f1, n1, name\#@count, name\#@method, parent, mname
    match =FALSE, f1 \\{
      rept 1 x:name\\#@count+1 \\\{
        name\\\#@count equ x
        name\\\#@method\\\#x equ name | mname
;        display 3,'New method:', \\\`name,\\\`mname, ' = ', \\\`x, 13, 10
        methods.\\\#name\\\#mname\\\#.inherited equ NONE
      \\\}
    \\}
    match =TRUE, f1 \\{
      match num, n1 \\\{
        match class | mmname, name\\\#@method\\\#num \\\\{
          methods.\\\\#name\\\\#mname\\\\#.inherited equ methods.\\\\#class\\\\#mmname
        \\\\}
        name\\\#@method\\\#num equ name | mname
;        display 3,'Redefinition:', \\\`name,\\\`mname, ' = ', \\\`num, 13, 10
      \\\}
    \\}
  \}

  macro abstract mname, [marg] \{
  \common
    method mname, marg
    methods.\#name\#mname = 0
  \}


  macro name#@members \{
    macro method mname, [marg] \\{ \\}
    macro abstract mname, [marg] \\{ \\}
    match any, parent \\{
      parent\\#@members
    \\}
    purge method, abstract
}


endobj fix } obj_helper


macro obj_helper {
  match name, obj@name \{
    local vtable

    virtual at 0
      name dd ?           ; pointer to the methods table.
      name\#@members

      rept name\#@count i:1 \\{
        match class | meth, name\\#@method\\#i \\\{
          name\\\#meth = (i-1) or cMethodMask
;          display 1, "Method constant", \\\`name,\\\`meth, " = ", \\\`i, 13, 10
        \\\}
      \\}

      sizeof.\#name = $
    end virtual

    label vtable
    label vtables.\#name dword

    if used vtables.\#name\#.parent | used vtables.\#name
      .parent dd 0
    end if

    match parent, obj@parent \\{
      if defined vtables.\\#parent\\#.parent
        store dword vtables.\\#parent\\#.parent at .parent
      end if
    \\}

    if used vtables.\#name
      .mcount dd name\#@count     ; methods count
      rept name\#@count i:1 \\{
        match class | meth, name\\#@method\\#i \\\{
            dd methods.\\\#class\\\#meth
        \\\}
      \\}
    end if
    .size = $ - vtable
  \}
  purge method, param
  restore obj@name, obj@parent
}


macro method name {
  local f1, f2
  define f1 TRUE
  if used methods.#name
    match =NONE, methods.#name#.inherited \{
      define f1 FALSE
      dd 0
    \}
    match =TRUE, f1 \{
      dd methods.\#name\#.inherited
    \}
  end if
  match =name#.arguments, name#.arguments \{
    "Error! Method not defined in any object."
  \}

  f2 equ FALSE
  match arguments, name#.arguments \{
    f2 equ TRUE
    proc methods.\#name, .self, arguments
  \}
  match =FALSE, f2 \{
    proc methods.\#name, .self
  \}
  name#.@here = 1
}



; Object manipulation macros. They all generate some assembly code and its syntax is
; designed to resemble the usual assembly language syntax. These macros are some
; kind of quasiinstructions.

; These macros uses FreshLib in order to allocate/deallocate dynamic memory.



macro create target, class, [arg] {
common
   if ~target eq eax
        push    eax
   end if

        stdcall GetMem, sizeof.#class
        mov     dword [eax], vtables.#class

   if ~target eq eax
        mov     target, eax
   end if

reverse
   if ~arg eq
        pushx   arg
   end if
common

   if defined class#.Create & (class#.Create and $ffff0000) = cMethodMask
        exec    eax, class#:Create
   end if

   if ~target eq eax
        pop     eax
   end if
}


macro destroy ptrObj {
   if defined TObject.Destroy & (TObject.Destroy and $ffff0000) = cMethodMask
        push    eax
        exec    ptrObj, TObject:Destroy
        pop     eax
   end if
       stdcall  FreeMem, ptrObj
}


macro exec ptrobj, method, [arg] {
common
  local ind
        ind equ NONE
        match class:meth, method \{
          ind equ class\#.\#meth
        \}

        match =NONE, ind \{
          disp 2, "Error! Invalid syntax. Use class:method.", 13, 10
          err
        \}

        if defined ind & ~(ind and $ffff0000) = cMethodMask
          match meth, ind \{
            disp 2, 'Error! ', \`meth, ' is not a method.', 13, 10
            err
          \}
        end if

reverse
        if ~arg eq
          pushx   arg
        end if
common
        if ptrobj eqtype 1
          pushd [ptrobj]
        else
          pushd   ptrobj          ; push .self argument
        end if

        if ptrobj eqtype eax
          mov  eax, [ptrobj]
        else
          if ptrobj eqtype 1
            mov  eax, [ptrobj]
            mov  eax, [eax]
          else
            mov  eax, ptrobj
            mov  eax, [eax]
          end if
        end if

        call dword [eax+8+4*(ind and $ffff)]
}


macro getm  target, ptrobj, method {
  local ind
  ind equ NONE
  match class:meth, method \{
    ind equ class\#.\#meth
  \}

  match =NONE, ind \{
    disp 2, "Error! Invalid syntax. Use class:method.", 13, 10
    err
  \}

  if defined ind & ~(ind and $ffff0000) = cMethodMask
    match meth, ind \{
      disp 2, 'Error! ', \`meth, ' is not a method.', 13, 10
      err
    \}
  end if

  if ~target eq eax
    push  eax
  end if

  if ptrobj eqtype eax
    mov  eax, [ptrobj]
  else
    if ptrobj eqtype 1
      mov  eax, [ptrobj]
      mov  eax, [eax]
    else
      mov  eax, ptrobj
      mov  eax, [eax]
    end if
  end if

  push  dword [eax+8+4*(ind and $ffff)]
  pop   target

  if ~target eq eax
    pop eax
  end if
}


macro inherited [arg] {
common
  if ~arg eq
reverse
    pushx arg
common
  end if
  push [.self]
  call  dword [.__info.start-4]
}



macro get target, obj, param {
local flag

  flag equ FALSE

  match class:par, param \{
    flag equ TRUE
  \}

  match =FALSE, flag \{
    disp 2, "Error! Invalid syntax. Use class:method.", 13, 10
    err
  \}

  match class:par, param \{

    if class\#.\#par\#.get < sizeof.\#class ; field
      if obj eqtype eax
        if target eqtype eax
          mov target, [obj+class\#.\#par\#.get]
        else
          pushd [obj+class\#.\#par\#.get]
          popd  target
        end if
      else
        if ~target eq eax
          push eax
        end if
        if obj eqtype 1
          mov eax, [obj]
        else
          mov eax, obj
        end if
        if target eqtype eax
          mov target, [eax+class\#.\#par\#.get]
        else
          pushd [eax+class\#.\#par\#.get]
          popd  target
        end if
        if ~target eq eax
          pop eax
        end if
      end if
    else        ; by method
      if ~target eq eax
        push eax
      end if
      exec obj, class\#:\#par\#.get
      if ~target eq eax
        mov target, eax
        pop eax
      end if
    end if
  \}
}


macro set obj, param, value {
local flag

  flag equ FALSE

  match class:par, param \{
    flag equ TRUE
  \}

  match =FALSE, flag \{
    disp 2, "Error! Invalid syntax. Use class:method.", 13, 10
    err
  \}

  match class:par, param \{

    if class\#.\#par\#.set < sizeof.\#class ; field
      if obj eqtype eax
        if value eqtype eax
          mov [obj+class\#.\#par\#.set], value
        else
          pushd value
          popd  [obj+class\#.\#par\#.set]
        end if
      else
        if value eq eax
          push ecx
          mov  ecx, obj
          mov [ecx+class\#.\#par\#.set], value
          pop ecx
        else
          push eax
          mov  eax, obj
          if value eqtype eax
            mov [eax+class\#.\#par\#.set], value
          else
            pushd value
            popd  [eax+class\#.\#par\#.set]
          end if
          pop eax
        end if
      end if
    else        ; by method
      push eax
      exec obj, class\#:\#par\#.set, value
      pop  eax
    end if
  \}
}


; returns ZF = 1 if the type matches
; returns ZF = 0 if the type does not matches.

macro istype ptrobj, class {
  local ..itis, ..loop
        push    ecx

  if ptrobj eqtype eax    ; register
    if ~ptrobj eq ecx
        mov     ecx, ptrobj
    end if
  else
        mov     ecx, ptrobj
  end if

        test    ecx, ecx

..loop:
        mov     ecx, [ecx]    ; get vtable, or the parent vtable
        jecxz   ..itis        ; if jump, ZF=0
        cmp     ecx, vtables.#class
        jne     ..loop
; here ZF = 1
..itis:
        pop     ecx
}