; G+ MACRO LANGUAGE

;;;;;;;;;;;;;;;;;;; CONSTANTS ;;;;;;;;;;;;;;;;;;;;

; numeric BLACK, WHITE=1, RED, GREEN, BLUE

macro numeric p&
  local i
  i=0
  irp q, p
    match name==value, q
      i=value
      name=i
    else match name, q
      name=i
    end match
    i=i+1
  end irp
end macro

;;;;;;;;;;;;;;;;;;;; VARIABLE ;;;;;;;;;;;;;;;;;;;;

; int i=7, n='Z', j=$CAFEBABE

scope=0

macro variable type, p&
  irp q, p
    match name==value, q
      label name at @base+$
      type value
    else match name, q
      label name at @base+$
      type 0
    end match
  end irp
end macro

macro byte p&
  variable db, p
end macro

macro int p&
  irp q, p
    match name[]==values, q
      match {v}, values
        label name at @base+$
        dd v
      else
        error 'Use: ={...}'
      end match
    else match name[size], q
      label name at @base+$
      dd size dup(0)
    else
      variable dd, q
    end match
  end irp
end macro

macro integer p&
  int p
end macro

macro void p&
  int p
end macro

macro text p&
  irp q, p
    match name==value, q
      label name at @base+$
      db value, 0
    else match name[size], q
      label name at @base+$
      db size dup(0)
    else
      err ''
    end match
  end irp
end macro

;;;;;;;;;;;;;;;;;;;;; CLASS ;;;;;;;;;;;;;;;;;;;;;;

macro class type
  esc macro type name
    scope='c'
    name:
    namespace name
      ; ...
end macro

macro endc!
      ; ...
    end namespace
    scope=0
  esc end macro
end macro

; EXAMPLE:

; class POINT
;   int x, y
; endc

; POINT point

; 2-DO: single-line classes:

; class RGB   = byte a, r, g, b
; class POINT = int x, y
; class LINE  = POINT a, b
; class BOX   = int x, y, w, h
; class RECT  = int l, t, r, b

; class POINT3D   = int n, x, y, z
; class POLYGON   = POINT points[?]
; class POLYGON3D = POINT3D points[?]

; 2-DO: CPU portable memory manager,
; dynamic arrays: IMAGE images[?]

;;;;;;;;;;;;;;;;;;;; OPERATION ;;;;;;;;;;;;;;;;;;;

; unfinished. replace asm '' with instructions

macro let p&
  match a==&b, p
    lea a, b
  else match a==b, p
    mov a, b
  else match a>>>b, p
    sar a, b
  else match a<<b, p
    shl a, b
  else match a>>b, p
    shr a, b
  else match a&b, p
    and a, b
  else match a|b, p
    or a, b
  else match a+b, p
    match =1, b
      inc a
    else
      add a, b
    end match
  else match a-b, p
    match =1, b
      dec a
    else
      sub a, b
    end match
  else match a*b, p
    match =2, b
      shl a, 1
    else match =4, b
      shl a, 2
    else match =8, b
      shl a, 3
    else
      imul a, b
    end match
  else match a/b, p
    match =2, b
      sar a, 1
    else match =4, b
      sar a, 2
    else match =8, b
      sar a, 3
    else
      match =eax, a
        push edx
        cdq
        idiv
        pop edx
      else
        error 'Destiny must be eax'
      end match
    end match
  else
    error 'Invalid expression: let ', `p
  end match
end macro

macro let p&
  irp x, p
    let x
  end irp
end macro

;;;;;;;;;;;;;;;;;;;; FUNCTION ;;;;;;;;;;;;;;;;;;;;

its_name equ 0
its_parameters equ 0
its_locals equ 0

n_parameters=0
parameters_size=0
n_locals=0
locals_size=0

macro function name, p&
  local n
  n=0
  match =0, its_name
    its_name equ name
    its_parameters equ p
  else
    err 'endf expected'
  end match
  n=0
  match any, p
    irp x, p
      n=n+1
    end irp
  end match
  n_parameters=n
  parameters_size=n*4
  @#name:
  asm `name, ':'
  macro name q&
    n=0
    irp x, q
      n=n+1
    end irp
    if n>1
      if n<>n_parameters
        error `name, ': ',\
          'Invalid # parameters'
      end if
      irp x, q          ; pushr...
        indx (1+%%-%)   ; x=q[#]
        push x
      end irp
    end if
    call name
  end macro
  if n_parameters
    push ebp
    mov ebp, esp
    irp x, q            ; parameter
      x equ [ebp+%*4]   ; names
    end irp
  end if
  scope='f'
end macro

macro return v&
  match any, v
    mov eax, v
  end match
  if n_parameters \
    | n_locals
    mov esp, ebp  ; db $89, $EC
    pop ebp       ; db $5D
  end if
  if n_parameters
    ret n         ; db $C2, dw n
  else
    ret           ; db $C3
  end if
end macro

macro endf! v&
  return v
  restore its_name
  irp x, its_parameters
    restore x
  end irp
  scope=0
end macro