struc (__name__) text?! terminator
  local lines, start_line, start_indx, end_indx
  lines = 0
  start_line = 1
  macro ?! line&
    if `line = `terminator
      virtual at 0
        file __file__
        ln = 1
        start_indx = 0
        end_indx = 0
        repeat $
          load @a: 1 from % - 1
          if @a = 10 | % = %%
            ln = ln + 1
            if ln = start_line
              start_indx = %
            else if ln = start_line + lines
              end_indx = % - 1
              break
            end if
          end if
        end repeat
        load __name__: end_indx - start_indx from start_indx
      end virtual
      purge ?
    else
      if lines = 0
        start_line = __line__
      end if
      lines = lines + 1
    end if
  end macro
end struc

; compile-time errors
asmerr__invalid_param text """
invalid parameter
"""
asmerr__value_outof_range text """
value is out of range
"""
asmerr__invalid_array_size text """
invalid size of array
"""


define @datatype

macro datatypes types&
  irp <size, type>, types
    @datatype.type equ ::type::
    sizeof.type = size
    macro type args:?&
      match [value], args
        local count
        count = +value
        emit size: count dup ?
      else
        emit size: args
      end match
    end macro
    struc (__name__) type args:?&
      label .: size
      type args
      __name__.__size = size
      __name__.__length = $ - .
    end struc
  end irp
end macro

datatypes \
  1,db?, 2,dw?, 4,dd?, 6,dp?, 8,dq?, 10,dt?, 16,ddq?, 32,dqq?, 64,ddqq?

datatypes \
  1,byte?, 2,word?, 4,dword?, 6,fword?, 8,qword?, 10,tbyte?,\
  16,dqword?, 16,oword?, 32,qqword?, 32,yword?, 64,dqqword?, 64,zword?


macro datatypes_reservable types&
  irp <size, type>, types
    @datatype.type equ ::type::
    sizeof.type = size
    macro type value*
      local count
      count = +value
      emit size: count dup ?
    end macro
    struc (__name__) type value*
      label .: size
      type value
      __name__.__size = size
      __name__.__length = $ - .
    end struc
  end irp
end macro

datatypes_reservable \
  1,rb?, 2,rw?, 4,rd?, 6,rp?, 8,rq?, 10,rt?, 16,rdq?, 32,rqq?, 64,rdqq?


struc (__name__) text?! terminator
  local lines, start_line, start_indx, end_indx
  lines = 0
  start_line = 1
  macro ?! line&
    if `line = `terminator
      virtual at 0
        file __file__
        ln = 1
        start_indx = 0
        end_indx = 0
        repeat $
          load @a: 1 from % - 1
          if @a = 10 | % = %%
            ln = ln + 1
            if ln = start_line
              start_indx = %
            else if ln = start_line + lines
              end_indx = % - 1
              break
            end if
          end if
        end repeat
        load __name__: end_indx - start_indx from start_indx
      end virtual
      purge ?
    else
      if lines = 0
        start_line = __line__
      end if
      lines = lines + 1
    end if
  end macro
end struc


macro align n*, x:?
  emit 1: (n - 1) - ($ + n - 1) mod n dup x
end macro


macro struct? name
  macro end?.struct?!
        end namespace
      else if @n < 0
        err asmerr__invalid_array_size
      end if
      if `__name__ <> `name
        __name__.__size = sizeof.name
        __name__.__length = sizeof.name * @n
        if @n > 0
          emit 1: sizeof.name * (@n - 1) dup ?
          match arguments, @args
            local path, disp, offs, value, indx, list
            path equ ''
            value equ
            disp = 0
            offs = 0
            indx = 0
            list = 0
            irp argument, arguments
              local arg, d, l
              define arg argument

              match @p:@v, argument
                if ~ list
                  path equ ''
                  disp = 0
                  define d @p:
                  match .[@b]more, d
                    offs = +@b
                    l = __name__.__length - __name__.__size * offs
                    if l > 0
                      disp = __name__.__size * offs
                    else
                      err asmerr__invalid_param
                    end if
                    redefine d more
                  end match
                  while 1
                    match :, d
                      break
                    else match .@a[@b]more, d
                      offs = +@b
                      eval 'path equ "', path, '.', `@a, '"'
                      eval 'l =', path, '.__length -', path, '.__size * offs'
                      if l > 0
                        eval 'disp = disp +', path, '.__size * offs'
                      else
                        err asmerr__invalid_param
                      end if
                      redefine d more
                    else match @a[@b]more, d
                      offs = +@b
                      eval 'path equ "', path, '.', `@a, '"'
                      eval 'l =', path, '.__length -', path, '.__size * offs'
                      if l > 0
                        eval 'disp = disp +', path, '.__size * offs'
                      else
                        err asmerr__invalid_param
                      end if
                      redefine d more
                    else match .@a more, d
                      offs = 0
                      eval 'path equ "', path, '.', `@a, '"'
                      redefine d more
                    else match @a more, d
                      offs = 0
                      eval 'path equ "', path, '.', `@a, '"'
                      redefine d more
                    end match
                  end while
                  value equ
                  indx = 0
                  list = 0
                  redefine arg @v
                else
                  err asmerr__invalid_param
                end if
              end match

              match <x>, arg
                if ~ list
                  value equ x
                else
                  err asmerr__invalid_param
                end if
              else match <x, arg
                if ~ list
                  value equ x
                  list = 1
                else
                  err asmerr__invalid_param
                end if
              else match x>, arg
                if list
                  value equ x
                  indx = indx + 1
                  list = 0
                else
                  err asmerr__invalid_param
                end if
              else
                value equ arg
                indx = indx + list
              end match

              local param, len
              eval 'define param ', path
              match p, param
                virtual at 0
                  match type val, value
                    match ::type::, @datatype.type
                      type (0 + val)
                      align p.__size
                    else
                      emit p.__size: (0 + value)
                    end match
                  else
                    if value eqtype ''
                      emit 1: value
                    else
                      emit p.__size: (0 + value)
                    end if
                  end match
                  len = $
                  load value: len from 0
                end virtual
                if len <= ((p.__length - offs) / p.__size - indx) * p.__size
                  store value: len at p + disp + p.__size * indx
                  indx = indx + len / p.__size - 1
                else
                  err asmerr__value_outof_range
                end if
              end match
            end irp
          end match
        end if
      end if
    end struc

    virtual at 0
      name name
      sizeof.name = $
    end virtual
    purge end?.struct?
  end macro

    struc (__name__) name args&
      local @n, arguments, @args
      @n = 1
      define @args args
      match [count], @args
        @n = count
      else match [count] more, @args
        @n = count
        redefine @args more
      end match
      label .: sizeof.name * @n
      if @n > 0
        namespace .
end macro


struct POINT
  x dd ?
  y rd 1
end struct

point POINT x:1, y:2

struct LINE
  StartPoint POINT
  EndPoint POINT
end struct

line LINE StartPoint.x:1, StartPoint.y:2, EndPoint.x:5, EndPoint.y:6

struct LIST
  letters rb 4
end struct

; it's possible to fill the array with values
list1 LIST letters:<1,2,3,4>

; or it's possible to override data types
list2 LIST letters:<word 0x1111, word 0x2222>
list3 LIST letters:<dword 0x44444444>

; it's also possible to use smaller datatypes to initialize the variable
; but in this case the value will be padded with zeroes to data type size
point2 POINT x:<word 0x5555>

; point2 POINT x:<word 0x5555, word 0x6666> is not allowed

; it's possible to address any item in the array separately
list4 LIST letters[0]: 'a', letters[1]: 'b', letters[2]: 'c'
list5 LIST letters[2]: <word 0x7777>

struct TWO_LINES
  lines LINE [2]
end struct

my_lines TWO_LINES lines[1].StartPoint.x: 1, lines[1].EndPoint.y: 5

other_points POINT [2] .[0].x: 1, .[1].y:2

struct POINTS
  points_count db ?
  points POINT [0]
end struct

mypoints POINTS



macro log.number num
  local n, s
  n = +num
  if n = 0
    s = '0'
  else
    s = 0
    while n > 0
      s = s shl 8 + (n mod 10 + '0')
      n = n / 10
    end while
  end if
  display string s,13,10
end macro

log.number my_lines.lines.__length
