''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'''                                                                    '''
''' SIMPLE LITTLE COMPILER     *** released to the public domain ***   '''
'''                                                                    '''
''' By Rowan Crowe, first released Monday 28-Dec-1998                  '''
'''   rowan@sensation.net.au                                           '''
'''                                                                    '''
''' This is a very simple compiler which is designed to be flexible    '''
''' by having only a limited set of statements. This version is also   '''
''' very simple as it's a bootstrap - the compiler will be rewritten   '''
''' in the new language and then all of the fancy features will be     '''
''' added into that version. This version has unoptimized but valid    '''
''' code generation. See example files for statements and structure.   '''
'''                                                                    '''
''' NOTE: this is work in progress! It's not yet fully functional or   '''
'''       totally bug free.                                            '''
'''                                                                    '''
''' Requires MoonRock compiler v0.50 or higher:                        '''
'''   http://www.rowan.sensation.net.au/moonrock.html                  '''
'''                                                                    '''
'''   MRC slc                                                          '''
'''       or                                                           '''
'''   MRC slc !DEBUG                                                   '''
'''                                                                    '''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''


begin def

pragma subnullstringcheck ON

stack 8k

%BPLIMIT = 256        ' max number of discrete elements per line
dim bp$[%BPLIMIT]
dim bpt%[%BPLIMIT]
dim bpi%[%BPLIMIT]

%STACKLIMIT = 100
dim stk%[%STACKLIMIT]
dim stklabelstart%[%STACKLIMIT]
dim stklabelend$[%STACKLIMIT]
dim stkp$[%STACKLIMIT]
dim stkswitchc1%[%STACKLIMIT]
dim stkswitchc2%[%STACKLIMIT]

set =1, %STK_IF, %STK_IF_ELSE, %STK_REPEAT, %STK_WHILE, %STK_FUNCTION, _
        %STK_SWITCH, %STK_CASE

set =1, %VAR_NONE, %VAR_LOCAL, %VAR_FUNCPARM, %VAR_GLOBAL

set =1, %PRECISION_CHAR, %PRECISION_INT

%OPERATOR = 1         ' + - / * < = > & ! | %
%VARIABLE = 2         ' variable
%BRACKET_OPEN = 3     ' (
%COMMA = 4
%FUNCTION = 5         ' function()
%NUMBER = 6           ' decimal number
%ARRAY_BRACKET_OPEN = 7  ' [
%ARRAY_BRACKET_CLOSE = 8 ' ]
%POINTER = 9          ' *pointer
%KEYWORD = 10          ' <keywords>
%OPENBRACE = 11       ' {
%CLOSEBRACE = 12      ' }
%BRACKET_CLOSE = 13   ' )
%STRING_CONSTANT = 14 ' "something"
%COMMENT = 15         ' # comment

common bpp%, bpptr%, negflag%, locallabel%, jumplabel%, strconstlabel%, switchval%, caselabel%
common ihandle%, ohandle%
common curline%, curline$, stkptr%, braceptr%, vartype%, incomment%, signed%
common funclabelend%
common localvartable$, globalvartable$, strconstbuf$, functype$, externtable$
common deftable$
common dseg$
common tw$, t$, t1$    ' hack to prevent memory leaks

bundle thisline valdef%, global%, extern%

common ismain% ' is main() in this file?
'common precision%

sub Parse(l$)
sub Evaluate(l$)
sub Logical
sub Compare(nocall%)
sub AddSubtract
sub DivideMultiply
Sub ArrayBracket
sub Bracket

sub wo(l$)
sub writecurrentline(p%)
sub ProcessKeyword

strsegsize 64k

begin code

print "Simple Little Compiler v0.04 [DOS]; Copyright (C) 1998-2001 by Rowan Crowe.\n\n"
cmd$ = cmdline : cmd$ = ltrim(cmd$) : cmd$ = rtrim(cmd$)
cmd$ = lcase(cmd$)


if cmd$ = "" then
  print "http://www.rowan.sensation.net.au/slc.html\n\n"
  print "usage: slc <sourcefile>\n\n"
  end
endif
ptr% = cinstr(cmd$, 46)  ' .
if ptr% <> 0 then
  ' strip off .slc extension
  ptr% = ptr% - 1
  cmd$ = left(cmd$, ptr%)
endif

ismain% = FALSE

stkptr% = 0
signed% = TRUE  ' compares are signed by default
vartype% = %VAR_GLOBAL
localvartable$ = "ú"
globalvartable$ = "ú"
functype$ = "ú"
externtable$ = "ú"
deftable$ = "ú"
'dseg$ = ""

f$ = cmd$ + ".slc"
ihandle% = fopen(f$, readonly)
f$ = cmd$ + ".asm"
ohandle% = fcreate(f$, archive)

call wo(".model\tsmall,basic\ncode\tsegment word public\nassume\tcs:code\nassume\tds:code\n")

incomment% = FALSE
f% = feof(ihandle%)
while f% = FALSE
  curline% = curline% + 1
  thisline.valdef% = FALSE
  thisline.global% = FALSE
  thisline.extern% = FALSE
  finput(ihandle%, l$)
  l$ = ltrim(l$) : l$ = rtrim(l$)
  if l$ <> "" then
    curline$ = l$
    t$ = "000" + str(curline%)
    fprint(ohandle%, "; " + right(t$, 4) + "  " + l$ + "\n")
    call Evaluate(l$)
  endif
  f% = feof(ihandle%)
wend
fclose(ihandle%)

' write out external func defs
tft$ = functype$
ptr% = instr(tft$, "úE:")
while ptr% <> 0
  ptr% = ptr% + 3
  tft$ = mid(tft$, ptr%) ' chop off leading "úE:"
  n$ = left(tft$, 32)      ' get the name limited to 32 chars
  ptr2% = cinstr(n$, 250)       'ú'
  ptr2% = ptr2% - 1
  n$ = left(n$, ptr2%)
  t$ = "ú" + n$ + "ú"
  ptr3% = instr(externtable$, t$)
  if ptr3% = 0 then
    t$ = "*** warning: '" + n$ + "' called but not defined; assumed to be external\n"
    print t$
    t$ = "; " + t$
    call wo(t$)
    t$ = "extrn\t_" + n$ + ":NEAR\n"
    call wo(t$)
  endif
  ptr% = instr(tft$, "úE:")
wend

' find funcs that are defined as extern but not actually called
tet$ = mid(externtable$, 2)
l% = len(tet$)
l% = l% - 1
tet$ = left(tet$, l%)
ptr% = instr(tet$, "ú")
while ptr% <> 0
  tet$ = mid(tet$, 2)      ' chop off leading 'ú'
  n$ = left(tet$, 32)      ' get the name limited to 32 chars
  ptr2% = cinstr(n$, 250)  ' find trailing 'ú'
  ptr2% = ptr2% - 1
  n$ = left(n$, ptr2%)
  t$ = ":" + n$ + "ú"
  ptr3% = instr(functype$, n$)
  if ptr3% = 0 then
    print "*** warning: function '" + n$ + "' defined as extern, but not called\n"
  endif
  ptr% = instr(tet$, "ú")
wend

call wo("code\tends\n")

call wo("end\n")
fclose(ohandle%)

if incomment% = TRUE then
  print "\h07*** warning: open comment block at end of file\n\n"
endif

if braceptr% < 0 then
  print "*** error: brace mismatch: too many '}' close braces\n"
  end
endif
if braceptr% > 0 then
  print "*** error: brace mismatch: too many '{' open braces\n"
  end
endif

print "processed " + curline% + " line(s) successfully, outfile is '" + f$ + "'\n"
print "remaining compiler near memory is " + freemem(0) + " bytes.\n"
'fs% = freestack
'us% = unusedstack
'print "free stack is " + fs% + " bytes (" + us% + " bytes never used)\n\n"

cc$ = getenv("COMSPEC")
t$ = "/C tasm " + cmd$ + " /t"
print cc$ + t$ + "\n"
rv% = exec(cc$, t$)
if ismain% = TRUE then
  t$ = "/C tlink alib\\startup+" + cmd$ + "," + cmd$ + ",,slc/t"
  print cc$ + t$ + "\n"
  rv% = exec(cc$, t$)
endif

#ifdef DEBUG
  s% = unusedstack
  print "unused stack space=" + s% + "\n"
#endif
end


#preserve
sub Evaluate(l$)
#ifdef AUDIT
  t$ = "000" + str(curline%)
  print "\n" + right(t$, 4) + " " + l$ + "\n"
#endif
call Parse(l$)
#ifdef DEBUG
  for i% = 0 to bpp%
    print bp$[i%] + "["
    select case bpt%[i%]
      case %NUMBER
        print "NUMBER"
      case %OPERATOR
        print "OPERATOR"
      case %VARIABLE
        print "VARIABLE"
      case %BRACKET_OPEN
        print "BRACKET_OPEN"
      case %BRACKET_CLOSE
        print "BRACKET_CLOSE"
      case %COMMA
        print "COMMA"
      case %FUNCTION
        print "FUNCTION"
      case %ARRAY_BRACKET_OPEN
        print "ARRAY_BRACKET_OPEN"
      case %ARRAY_BRACKET_CLOSE
        print "ARRAY_BRACKET_CLOSE"
      case %POINTER
        print "POINTER"
      case %KEYWORD
        print "KEYWORD"
      case %OPENBRACE
        print "OPENBRACE"
      case %CLOSEBRACE
        print "CLOSEBRACE"
      case %STRING_CONSTANT
        print "STRINGCONSTANT:" + bp$[i%]
      case else
        print "unknown:" + bpt%[i%]
    end select
    print "]ú"
  next
  print "\n"
#endif

bpptr% = 0

while bpptr% <= bpp%
  if bpt%[bpptr%] = %KEYWORD then
    call ProcessKeyword
  else
    call Logical
  endif
wend
end sub


#preserve
sub Logical
#ifdef AUDIT
  print "> Logical ('" + bp$[bpptr%] + "')\n"
#endif
call Compare(TRUE)
if bpptr% > bpp% then exit sub
r% = sinstr(bp$[bpptr%], "&|")
while r% <> 0
  select case bp$[bpptr%]
    case "||", "|"
      bpptr% = bpptr% + 1
      call wo("\tpush\tax\n")
      call Compare(TRUE)
      call wo("\tpop\tcx\n\txchg\tcx,ax\n\tor\tax,cx\n")
    case "&&", "&"
      bpptr% = bpptr% + 1
      call wo("\tpush\tax\n")
      call Compare(TRUE)
      call wo("\tpop\tcx\n\txchg\tcx,ax\n\tand\tax,cx\n")
  end select
  r% = sinstr(bp$[bpptr%], "&|")
  if bpptr% > bpp% then exit while
wend
#ifdef AUDIT
  print "< Logical ('" + bp$[bpptr%] + "')\n"
#endif
end sub


#preserve
sub Compare(docall%)
#ifdef AUDIT
  print ">  Compare ('" + bp$[bpptr%] + "')\n"
#endif
if docall% = TRUE then call AddSubtract
if bpptr% = bpp% then exit sub
r% = sinstr(bp$[bpptr%], "!<=>")
while r% <> 0
  select case bp$[bpptr%]
    case "<<"
    ' this probably shouldn't go here, but it's difficult to put it into
    ' logical() without doing something other than sinstr to find a match.
      bpptr% = bpptr% + 1
      call wo("\tpush\tax\n")
      call AddSubtract
      call wo("\tpop\tcx\n\txchg\tcx,ax\n\tshl\tax,cl\n")
    case ">>"
    ' this probably shouldn't go here, but it's difficult to put it into
    ' logical() without doing something other than sinstr to find a match.
      bpptr% = bpptr% + 1
      call wo("\tpush\tax\n")
      call AddSubtract
      call wo("\tpop\tcx\n\txchg\tcx,ax\n\tshr\tax,cl\n")
    case "=="
      bpptr% = bpptr% + 1
      call wo("\tpush\tax\n")
      call AddSubtract
      call wo("\tpop\tcx\n\tcmp\tax,cx\n\tmov\tax,-1\n")
      t$ = "LL" + str(locallabel%) : locallabel% = locallabel% + 1
      t1$ = "\tjz\t" + t$ + "\n"
      call wo(t1$)
      call wo("\tinc\tax\n")
      t1$ = t$ + ":\n"
      call wo(t1$)
    case "!="
      bpptr% = bpptr% + 1
      call wo("\tpush\tax\n")
      call AddSubtract
      call wo("\tpop\tcx\n\tcmp\tax,cx\n\tmov\tax,-1\n")
      t$ = "LL" + locallabel% : locallabel% = locallabel% + 1
      t1$ = "\tjnz\t" + t$ + "\n"
      call wo(t1$)
      call wo("\tinc\tax\n")
      t1$ = t$ + ":\n"
      call wo(t1$)
    case "<"
      bpptr% = bpptr% + 1
      call wo("\tpush\tax\n")
      call AddSubtract
      call wo("\tpop\tcx\n\tcmp\tcx,ax\n\tmov\tax,-1\n")
      t$ = "LL" + locallabel% : locallabel% = locallabel% + 1
      if signed% = TRUE then
        t1$ = "\tjl\t" + t$ + "\n"
      else
        t1$ = "\tjb\t" + t$ + "\n"
      endif
      call wo(t1$)
      call wo("\tinc\tax\n")
      t1$ = t$ + ":\n"
      call wo(t1$)
    case ">"
      bpptr% = bpptr% + 1
      call wo("\tpush\tax\n")
      call AddSubtract
      call wo("\tpop\tcx\n\tcmp\tcx,ax\n\tmov\tax,-1\n")
      t$ = "LL" + locallabel% : locallabel% = locallabel% + 1
      if signed% = TRUE then
        t1$ = "\tjg\t" + t$ + "\n"
      else
        t1$ = "\tjb\t" + t$ + "\n"
      endif
      call wo(t1$)
      call wo("\tinc\tax\n")
      t1$ = t$ + ":\n"
      call wo(t1$)
    case "<="
      bpptr% = bpptr% + 1
      call wo("\tpush\tax\n")
      call AddSubtract
      call wo("\tpop\tcx\n\tcmp\tcx,ax\n\tmov\tax,-1\n")
      t$ = "LL" + locallabel% : locallabel% = locallabel% + 1
      if signed% = TRUE then
        t1$ = "\tjle\t" + t$ + "\n"
      else
        t1$ = "\tjbe\t" + t$ + "\n"
      endif
      call wo(t1$)
      call wo("\tinc\tax\n")
      t1$ = t$ + ":\n"
      call wo(t1$)
    case "=>"
      bpptr% = bpptr% + 1
      call wo("\tpush\tax\n")
      call AddSubtract
      call wo("\tpop\tcx\n\tcmp\tcx,ax\n\tmov\tax,-1\n")
      t$ = "LL" + locallabel% : locallabel% = locallabel% + 1
      if signed% = TRUE then
        t1$ = "\tjge\t" + t$ + "\n"
      else
        t1$ = "\tjae\t" + t$ + "\n"
      endif
      call wo(t1$)
      call wo("\tinc\tax\n")
      t1$ = t$ + ":\n"
      call wo(t1$)
    case "="
      ' we seem to fall here when you do something like var1[var2] = ...
      bpptr% = bpptr% - 1  ' to catch +1 when we return
      exit while
  end select
  if bpptr% > bpp% then exit while
  r% = sinstr(bp$[bpptr%], "!<=>")
wend
#ifdef AUDIT
  print "<  Compare ('" + bp$[bpptr%] + "')\n"
#endif
end sub


#preserve
sub AddSubtract
#ifdef AUDIT
  print ">   AddSubtract ('" + bp$[bpptr%] + "')\n"
#endif
call DivideMultiply
if bpptr% > bpp% then
  exit sub
endif
r% = sinstr(bp$[bpptr%], "+-")
while r% <> 0
  if bp$[bpptr%] = "+" then
    bpptr% = bpptr% + 1
    call wo("\tpush\tax\n")
    call DivideMultiply
    call wo("\tpop\tcx\n\tadd\tax,cx\n")
  else
    bpptr% = bpptr% + 1
    call wo("\tpush\tax\n")
    call DivideMultiply
    call wo("\tmov\tcx,ax\n\tpop\tax\n\tsub\tax,cx\n")
  endif
  if bpptr% > bpp% then exit while
  r% = sinstr(bp$[bpptr%], "+-")
wend
#ifdef AUDIT
  print "<   AddSubtract ('" + bp$[bpptr%] + "')\n"
#endif
end sub


#preserve
sub DivideMultiply
#ifdef AUDIT
  print ">    DivideMultiply ('" + bp$[bpptr%] + "')\n"
#endif
call ArrayBracket
if bpptr% > bpp% then
  exit sub
endif
r% = sinstr(bp$[bpptr%], "*/%")
while r% <> 0
  select case bp$[bpptr%]
    case "*"
      bpptr% = bpptr% + 1
      call wo("\tpush\tax\n")
      call ArrayBracket
      call wo("\tmov\tcx,ax\n\tpop\tax\n")
      if signed% = TRUE then
        call wo("\timul\tcx\n")
      else
        call wo("\tmul\tcx\n")
      endif
    case "/"
      bpptr% = bpptr% + 1
      call wo("\tpush\tax\n")
      call ArrayBracket
      call wo("\tmov\tcx,ax\n\tpop\tax\n")
      if signed% = TRUE then
        call wo("\tcwd\n\tidiv\tcx\n")
      else
        call wo("\txor\tdx,dx\n\tdiv\tcx\n")
      endif
    case "%"
      bpptr% = bpptr% + 1
      call wo("\tpush\tax\n")
      call ArrayBracket
      call wo("\tmov\tcx,ax\n\tpop\tax\n\tcwd\n\tdiv\tcx\n")
      if signed% = TRUE then
        call wo("\tcwd\n\tidiv\tcx\n")
      else
        call wo("\txor\tdx,dx\n\tdiv\tcx\n")
      endif
      call wo("\tmov\tax,dx\n")
  end select
  if bpptr% > bpp% then exit while
  r% = sinstr(bp$[bpptr%], "*/%")
wend
#ifdef AUDIT
  print "<    DivideMultiply ('" + bp$[bpptr%] + "')\n"
#endif
end sub

#preserve
sub ArrayBracket
#ifdef AUDIT
  print ">     ArrayBracket ('" + bp$[bpptr%] + "')\n"
#endif
if bpptr% > bpp% then exit sub

if bpt%[bpptr%] = %VARIABLE then
  bpptr1% = bpptr% + 1
  if bpt%[bpptr1%] = %ARRAY_BRACKET_OPEN then
    tmp% = bpptr% ' point to variable name
    t1$ = "û" + lcase(bp$[bpptr%]) + "ú"
    ptr% = instr(localvartable$, t1$)
    if ptr% = 0 then
      call writecurrentline(bpi%[tmp%])
      print "- variable '" + bp$[tmp%] + "' undefined\n"
      end
    endif
    bpptr% = bpptr% + 2 ' point to one past '['
    ptr% = ptr% - 1
    vs% = sbyte(localvartable$, ptr%)
    select case vs%
      case 98 ' b
        precision% = %PRECISION_CHAR
      case 119 ' w
        precision% = %PRECISION_INT
    end select

    call Logical
    bpptr% = bpptr% + 1
    if bp$[bpptr%] = "=" then
    ' assignment: var[...] = ...
      bpptr% = bpptr% + 1
      call wo("\tpush\tax\n")
      call Logical
      call wo("\tpop\tsi\n")
      select case vs%
        case 98 ' b
          t1$ = "\tadd\tsi,word ptr [" + bp$[tmp%] + "]\n\tmov\tbyte ptr [si],al\n"
          call wo(t1$)
        case 119 ' w
          t1$ = "\tadd\tsi,si\n\tadd\tsi,word ptr [" + bp$[tmp%] + "]\n\tmov\tword ptr [si],ax\n"
          call wo(t1$)
      end select
    else
      '... var[] ...
'      bpptr% = bpptr% + 1
      call wo("\tmov\tsi,ax\n")
      select case vs%
        case 98 ' b
          t1$ = "\tadd\tsi,word ptr [" + bp$[tmp%] + "]\n\tmov\tal,byte ptr [si]\n"
          if signed% = TRUE then
            t1$ = t1$ + "\tcbw\n"
          else
            t1$ = t1$ + "\tmov\tah,0\n"
          endif
          call wo(t1$)
        case 119 ' w
          t1$ = "\tadd\tsi,si\n\tadd\tsi,word ptr [" + bp$[tmp%] + "]\n\tmov\tax,word ptr [si]\n"
          call wo(t1$)
      end select
    endif
  else
    call Bracket
  endif
else
  call Bracket
endif

#ifdef AUDIT
  print "<     ArrayBracket ('" + bp$[bpptr%] + "')\n"
#endif

end sub


#preserve
sub Bracket
if bpptr% > bpp% then exit sub
precision% = %PRECISION_INT  ' assume int unless otherwise specified

#ifdef AUDIT
  print ">     Bracket ('" + bp$[bpptr%] + "')\n"
#endif

negjump:

select case bpt%[bpptr%]
'  if bpptr% = 0 then if bpt%[1] = %ARRAY_BRACKET then bpptr% = bpptr% + 1 : exit sub

  case %ARRAY_BRACKET_CLOSE  ' closing ]
    bpptr% = bpptr% + 1
    exit sub
  case %OPERATOR
    select case bp$[bpptr%]
      case "="
        ' ignore
        exit sub
      case "&"
        ' special case:  &variable
        t% = bpptr% + 1
        if bpt%[t%] = %VARIABLE then
          t1$ = "\tlea\tax,offset " + bp$[t%] + "\n"
          call wo(t1$)
          bpptr% = bpptr% + 2
          exit sub  ' bad...really need an 'exit to 2nd last select'
        endif
      case "-"
        ' special case:  negate
        t% = bpptr% + 1
        if bpt%[t%] = %NUMBER then
          bpptr% = bpptr% + 1
          bp$[t%] = "-" + bp$[t%]
          goto bracket_do_number
        endif
        ' variables are too hard and will require too much duplicated code
        ' in this section, let's just use a neg() function for the moment.
    end select
    call writecurrentline(bpi%[bpptr%])
    print "- syntax error: unexpected operator '" + bp$[bpptr%] + "'\n"
    end
  case %COMMENT
    ' ignore rest of line
    bpptr% = bpp% + 2 ' clear the end of line
    exit sub
  case %STRING_CONSTANT
    l% = len(bp$[bpptr%])
    sc$ = "SC" + str(strconstlabel%) + " db '"
    for i% = 1 to l%
      c% = sbyte(bp$[bpptr%], i%)
      select case c%
        case 39 ' '
          sc$ = sc$ + "',39,'"
        case 92 ' \
          i% = i% + 1
          d% = sbyte(bp$[bpptr%], i%)
          select case d%
            case 110  ' n
              sc$ = sc$ + "',13,10,'"
            case 48   ' 0
                sc$ = sc$ + "',0,'"
            case 92   ' \
                sc$ = sc$ + "',92,'"
            case 116  ' t
                sc$ = sc$ + "',9,'"
          end select
        case else
          sc$ = sc$ + chr(c%)
      end select
    next
    strconstbuf$ = strconstbuf$ + sc$ + "',0\n"
    tw$ = "\tmov\tax,offset SC" + str(strconstlabel%) + "\n"
    call wo(tw$)
    strconstlabel% = strconstlabel% + 1
    bpptr% = bpptr% + 1

  case %NUMBER
bracket_do_number:
    t1$ = bp$[bpptr%]
    tw$ = "\tmov\tax," + t1$ + "\n"
    call wo(tw$)
    bpptr% = bpptr% + 1
  case %FUNCTION
    ' this should probably set precision% based on the return value of
    ' the function.
    fn% = bpptr%
    bpptr% = bpptr% + 1
    if bp$[bpptr%] <> "(" then
      call writecurrentline(bpi%[bpptr%])
      print "- syntax error: expected '(' after function name ('" + bp$[bpptr%] + "' found)\n"
      end
    endif
    bpptr% = bpptr% + 1
    sp% = 0
    if bp$[bpptr%] <> ")" then
      found% = FALSE
      while found% = FALSE
        call Logical
        if bp$[bpptr%] = ")" then found% = TRUE : exit while
        if bp$[bpptr%] = "," then
          bpptr% = bpptr% + 1
          if thisline.valdef% = FALSE then
            sp% = sp% + 2
            call wo("\tpush\tax\n")
          endif
        endif
        if bpptr% > bpp% then exit while
      wend
      if found% = FALSE then
        call writecurrentline(bpi%[bpptr%])
        print "- syntax error: expected closing ')'\n"
        end
      endif
      call wo("\tpush\tax\n")
      sp% = sp% + 2
    endif

    if thisline.extern% = FALSE then  ' external func def, so don't call it!
      t1$ = "\tcall\t_" + bp$[fn%] + "\n"
      call wo(t1$)
      ' check if it's in the function type table, if not then mark it as
      ' an external function call by default. a function definition found later
      ' in the source file will remark it as internal.
      t1$ = ":" + bp$[fn%] + "ú"
      ptr% = instr(functype$, t1$)
      if ptr% = 0 then
        functype$ = functype$ + "E:" + bp$[fn%] + "ú"
      endif
    else
      bpptr% = bpp% + 1  ' ignore rest of function definition
      exit sub
      ' in future this will store the parameter types and check them
      ' each time you use this function
    endif
    bpptr% = bpptr% + 1
  case %POINTER
    bpptr% = bpptr% + 1
    if bpt%[bpptr%] = %BRACKET_OPEN then
      bpptr% = bpptr% + 1
      if bpt%[bpptr%] <> %VARIABLE then
        if bpt%[bpptr%] <> %POINTER then  ' check for *(*var...
          if bpt%[bpptr%] <> %FUNCTION then  ' permit *(func()...
            call writecurrentline(bpi%[bpptr%])
            print "- syntax error: first parameter must be a variable, pointer or function\n"
            end
          endif
        endif
      endif
      if bpt%[bpptr%] = %FUNCTION then
        ' *(func()...
        funcreturnspointer% = TRUE
        vs% = 119
        precision% = %PRECISION_INT
        ' no way to know what size the function returns yet, so we'll
        ' default to int
      else
        ' set precision% based on the size of *(firstvar+....
        funcreturnspointer% = FALSE
        t1$ = "û" + lcase(bp$[bpptr%]) + "ú"
        ptr% = instr(localvartable$, t1$)
        if ptr% = 0 then
          call writecurrentline(bpi%[bpptr%])
          print "- variable '" + bp$[bpptr%] + "' undefined\n"
          end
        endif
        ptr% = ptr% - 1
        vs% = sbyte(localvartable$, ptr%)
        select case vs%
          case 98 ' b
            precision% = %PRECISION_CHAR
          case 119 ' w
            precision% = %PRECISION_INT
        end select
      endif

      tbpptr% = bpptr%
      call Logical
      if bpt%[bpptr%] <> %BRACKET_CLOSE then
        call writecurrentline(bpi%[bpptr%])
        print "- syntax error: expected closing ')'\n"
        end
      endif
      bpptr1% = bpptr% + 1
      if bp$[bpptr1%] = "=" then
      ' *(something) = ...

        if funcreturnspointer% = FALSE
          t1$ = "û" + lcase(bp$[tbpptr%]) + "ú"  ' temp variable from above
          ptr% = instr(localvartable$, t1$)
          if ptr% = 0 then
            call writecurrentline(bpi%[bpptr%])
            print "- variable '" + bp$[bpptr%] + "' undefined\n"
            end
          endif

          ptr% = ptr% - 1
          vs% = sbyte(localvartable$, ptr%)
        endif

        call wo("\tpush\tax\n")
        bpptr% = bpptr% + 2
        call Logical
        call wo("\tpop\tsi\n")
        select case vs%
          case 98  ' b
            call wo("\tmov\tbyte ptr [si],al\n")
            precision% = %PRECISION_CHAR
          case 119 ' w
            call wo("\tmov\tword ptr [si],ax\n")
            precision% = %PRECISION_INT
        end select
        exit sub  ' end of line
      endif
      select case precision%
        case %PRECISION_CHAR
          call wo("\tmov\tsi,ax\n\tmov\tal,byte ptr [si]\n")
          if signed% = TRUE then
            call wo("\tcbw\n")
          else
            call wo("\tmov\tah,0\n")
          endif
        case %PRECISION_INT
          call wo("\tmov\tsi,ax\n\tmov\tax,word ptr [si]\n")  ' ******
      end select
    else
      t1$ = "û" + lcase(bp$[bpptr%]) + "ú"
      ptr% = instr(localvartable$, t1$)
      if ptr% = 0 then
        call writecurrentline(bpi%[bpptr%])
        print "- variable '" + bp$[bpptr%] + "' undefined\n"
        end
      endif
      ptr% = ptr% - 1
      vs% = sbyte(localvartable$, ptr%)
      bpptr1% = bpptr% + 1
      if bp$[bpptr1%] = "=" then
        vn% = bpptr%
      ' it's an assignment
        bpptr% = bpptr% + 2
        call Logical
        t1$ = "\tmov\tsi,word ptr [" + bp$[vn%] + "]\n"
        call wo(t1$)
        select case vs%
          case 98  ' b
            call wo("\tmov\tbyte ptr [si],al\n")
          case 119 ' w
            call wo("\tmov\tword ptr [si],ax\n")
        end select
        exit sub  ' end of line
      endif
      tw$ = "\tmov\tsi,word ptr [" + bp$[bpptr%] + "]\n"
      call wo(tw$)
      select case vs%
        case 98  ' b
          call wo("\tmov\tal,byte ptr [si]\n")
          if signed% = TRUE then
            call wo("\tcbw\n")
          else
            call wo("\tmov\tah,0\n")
          endif
        case 119 ' w
          call wo("\tmov\tax,word ptr [si]\n")
      end select
    endif
    bpptr% = bpptr% + 1
  case %VARIABLE
bracket_do_variable:
    t1$ = "û" + lcase(bp$[bpptr%]) + "ú"
    ptr% = instr(localvartable$, t1$)
    if ptr% = 0 then
      call writecurrentline(bpi%[bpptr%])
      print "- variable '" + bp$[bpptr%] + "' undefined\n"
      end
    endif
    ptr% = ptr% - 1
    vs% = sbyte(localvartable$, ptr%)
    select case vs%
      case 98 ' b
        precision% = %PRECISION_CHAR
      case 119 ' w
        precision% = %PRECISION_INT
    end select
    ptr% = ptr% - 1
    vt% = sbyte(localvartable$, ptr%)
    vn% = bpptr%
    t% = bpptr% + 1

    bpptr1% = bpptr% + 1
    if bp$[bpptr1%] = "=" then
      bpptr% = bpptr% + 2
      call Logical
      select case vs%
        case 98  ' b
          if vt% = 80 then  ' pointer = somevalue
            tw$ = "\tmov\tword ptr [" + bp$[vn%] + "],ax\n"
          else
            tw$ = "\tmov\tbyte ptr [" + bp$[vn%] + "],al\n"
          endif
        case 119 ' w
          tw$ = "\tmov\tword ptr [" + bp$[vn%] + "],ax\n"
      end select
      call wo(tw$)
      exit sub
    endif
    select case vs%
      case 98  ' b
        if vt% = 80 then  ' raw pointer
          tw$ = "\tmov\tax,word ptr [" + bp$[bpptr%] + "]\n"
        else
          tw$ = "\tmov\tal,byte ptr [" + bp$[bpptr%] + "]\n"
          if signed% = TRUE then
            tw$ = tw$ + "\tcbw\n"
          else
            tw$ = tw$ + "\tmov\tah,0\n"
          endif
        endif
      case 119 ' w
        tw$ = "\tmov\tax,word ptr [" + bp$[bpptr%] + "]\n"
    end select
    call wo(tw$)
    bpptr% = bpptr% + 1
  case %COMMA
    call wo("\tpush\tax\n")
    bpptr% = bpptr% + 1
  case %BRACKET_CLOSE
    bpptr% = bpptr% + 1
  case %BRACKET_OPEN
    bpptr% = bpptr% + 1
    call Logical
    if bpt%[bpptr%] <> %BRACKET_CLOSE then
      call writecurrentline(bpi%[bpptr%])
      print "- syntax error: expected closing ')'\n"
      end
    endif
    bpptr% = bpptr% + 1
  case %OPENBRACE
    if vartype% = %VAR_FUNCPARM then
      call wo("\n")
      vartype% = %VAR_LOCAL
    endif
    bpptr% = bpptr% + 1
  case %CLOSEBRACE
    select case stk%[stkptr%]
      case %STK_CASE
        ' search backwards to find matching STK_SWITCH
        i% = stkptr%
        while i% <> 0
          i% = i% - 1
          if stk%[i%] = %STK_SWITCH then exit while
        wend
        ' there's always a matching STK_SWITCH so we don't need to check
        ' for a non exit.
        t1$ = "\tjmp\tJ" + stklabelend$[i%] + "\n"  ' use the STK_SWITCH
                                                    ' end label
        call wo(t1$)
        stkptr% = stkptr% - 1
      case %STK_SWITCH
        t1$ = "SW" + str(stkswitchc1%[stkptr%]) + "_" + _
          stkswitchc2%[stkptr%] + ":\nJ" + stklabelend$[stkptr%] + ":\n"
        call wo(t1$)
        stkptr% = stkptr% - 1
      case %STK_IF
        t2$ = "J" + stklabelend$[stkptr%] + ":\n"
        stkptr% = stkptr% - 1
        bpptr1% = bpptr% + 1
        if bp$[bpptr1%] = "else" then
          bpptr% = bpptr% + 2
          if bp$[bpptr%] <> "{" then
            call writecurrentline(bpi%[bpptr%])
            print "- syntax error: expected opening '{' after else\n"
            end
          endif
          bpptr% = bpptr% + 1
          stkptr% = stkptr% + 1 ' move to top of stack again, most fields
                                ' already present
          stk%[stkptr%] = %STK_IF_ELSE
          stklabelend$[stkptr%] = str(jumplabel%)
          jumplabel% = jumplabel% + 1
          t1$ = "\tjmp\tJ" + stklabelend$[stkptr%] + "\n"
          call wo(t1$)
        endif
        call wo(t2$) ' from above
      case %STK_IF_ELSE
        t1$ = "J" + stklabelend$[stkptr%] + ":\n"
        call wo(t1$)
        stkptr% = stkptr% - 1
      case %STK_REPEAT
        ' nothing
      case %STK_WHILE
        t1$ = "\tjmp\tJ" + str(stklabelstart%[stkptr%]) + "\nJ" + stklabelend$[stkptr%] + ":\n"
        call wo(t1$)
        stkptr% = stkptr% - 1
      case %STK_FUNCTION
        t1$ = "J" + stklabelend$[stkptr%] + ":\n\tret\n" + stkp$[stkptr%] + "\tendp\n"
        call wo(t1$)
        call wo(strconstbuf$)
        strconstbuf$ = ""
        if dseg$ <> "" then
'          call wo("code\tends\ndata\tsegment word public\nassume\tcs:data\n")
          call wo(dseg$)
'          call wo("data\tends\ncode\tsegment word public\nassume\tcs:code\n")
          dseg$ = ""
        endif
        stkptr% = stkptr% - 1
        vartype% = %VAR_GLOBAL
      case else
        call writecurrentline(bpi%[bpptr%])
        print "- syntax error: unknown block type at close\n"
        end
    end select
    bpptr% = bpptr% + 1
  case else
      call writecurrentline(bpi%[bpptr%])
      print "- syntax error: unexpected token '" + bp$[bpptr%] + "'\n"
      end
'    endif
end select
#ifdef AUDIT
  print "<     Bracket ('" + bp$[bpptr%] + "')\n"
#endif

end sub


#preserve
sub Parse(l$)
bpp% = 0

if l$ = "" then exit sub
l$ = l$ + ")"
p$ = ""
lt% = len(l$)
for i% = 1 to lt%

  c% = sbyte(l$, i%)
  if incomment% = TRUE then
    if c% = 42 then  ' *
      t% = i% + 1
      c% = sbyte(l$, t%)
      if c% = 47 then   ' /
        ' end of comment
        incomment% = FALSE
        i% = i% + 1  ' skip '/'
      endif
    endif
    goto parse_next_skip
  endif

  bpi%[bpp%] = i%
  select case c%
    case 32,43,45,47,42,60,61,62,40,41,44,38,124,33,91,93,123,125,34,35,39,37
      ' +-/*<=>(),&|![]{}"#'%
      if p$ <> "" then
        p$ = lcase(p$)
        bp$[bpp%] = p$
        t% = asc(p$)
        p$ = ""
        if t% > 95 then
          if t% < 123 then
            bpt%[bpp%] = %VARIABLE
            tbp$ = "ú" + bp$[bpp%] + "ú"
            ptr% = instr("úifúrepeatúuntilúwhileúvoidúcharúintúreturnúasmúglobalúexternúswitchúcaseúdefineúendúpragmaú", tbp$)
            if ptr% <> 0 then
              bpt%[bpp%] = %KEYWORD
            else
              t$ = "ú" + bp$[bpp%] + "="
              ptr% = instr(deftable$, t$)
              if ptr% <> 0 then
                t$ = mid(deftable$, ptr%)
                ptr% = instr(t$, "=")
                ptr% = ptr% + 1
                t$ = mid(t$, ptr%)
                ptr% = instr(t$, "ú")
                ptr% = ptr% - 1
                bp$[bpp%] = left(t$, ptr%)
                t1% = asc(bp$[bpp%])
                if t1% > 47 then if t1% < 58 then bpt%[bpp%] = %NUMBER
                if t1% = 39 then bpt%[bpp%] = %NUMBER  ' allow 'xy'
              endif
            endif
          endif
        endif
        if t% > 47 then
          if t% < 58 then
            bpt%[bpp%] = %NUMBER
          endif
        endif

        bpp% = bpp% + 1
      endif

      bp$[bpp%] = chr(c%)
      t% = sbyte(l$, i%)

      select case t%
        case 123
          bpt%[bpp%] = %OPENBRACE
          braceptr% = braceptr% + 1
        case 125
          bpt%[bpp%] = %CLOSEBRACE
          braceptr% = braceptr% - 1
        case 91
          bpt%[bpp%] = %ARRAY_BRACKET_OPEN
        case 93
          bpt%[bpp%] = %ARRAY_BRACKET_CLOSE
        case 43, 45, 33, 37
          bpt%[bpp%] = %OPERATOR
        case 47  ' /
          bpt%[bpp%] = %OPERATOR
          if bpp% > 0 then
            bpp_1% = bpp% - 1
            if bp$[bpp_1%] = "*" then
              ' end of a comment */
              if incomment% = FALSE then
                call writecurrentline(bpi%[bpp%])
                print "- syntax error: unexpected end of comment (not in a comment block)\n"
                end
              endif
            endif
          endif
        case 38  ' &
          bpt%[bpp%] = %OPERATOR
          bpp_1% = bpp% - 1
          if bp$[bpp_1%] = "&" then
            bpp% = bpp% - 1
            bp$[bpp%] = "&&" ' logical AND
          endif
        case 124  ' |
          bpt%[bpp%] = %OPERATOR
          bpp_1% = bpp% - 1
          if bp$[bpp_1%] = "|" then
            bpp% = bpp% - 1
            bp$[bpp%] = "||" ' logical OR
          endif
        case 42  ' *
          bpt%[bpp%] = %OPERATOR
          if bpp% = 0 then bpt%[0] = %POINTER
          if bpp% > 0 then ' prevent negative index
            bpp_1% = bpp% - 1
            if bpt%[bpp_1%] = %OPERATOR then bpt%[bpp%] = %POINTER
            if bpt%[bpp_1%] = %COMMA then bpt%[bpp%] = %POINTER
            if bpt%[bpp_1%] = %BRACKET_OPEN then bpt%[bpp%] = %POINTER

            if bpt%[bpp_1%] = %KEYWORD then bpt%[bpp%] = %POINTER
            ' this is to allow char *p and int *p

            ' section below must be the last one for * as it may modify
            ' bpp%
            if bp$[bpp_1%] = "/" then
              if incomment% = TRUE then
                call writecurrentline(bpi%[bpp%])
                print "- syntax error: unexpected start of comment (already in a comment block)\n"
                end
              else
                incomment% = TRUE
                bpp% = bpp% - 2
              endif
            endif
          endif

        case 60  ' <
          bpt%[bpp%] = %OPERATOR
          bpp_1% = bpp% - 1
          if bp$[bpp_1%] = "<" then
            bpp% = bpp% - 1
            bp$[bpp%] = "<<"
          endif
        case 61  ' =
          bpt%[bpp%] = %OPERATOR
          bpp_1% = bpp% - 1
          if bp$[bpp_1%] = "<" then
            bpp% = bpp% - 1
            bp$[bpp%] = "<="
          endif
          if bp$[bpp_1%] = "!" then
            bpp% = bpp% - 1
            bp$[bpp%] = "!="
          endif
          if bp$[bpp_1%] = "=" then
            bpp% = bpp% - 1
            bp$[bpp%] = "=="
          endif
        case 62  ' >
          bpt%[bpp%] = %OPERATOR
          bpp_1% = bpp% - 1
          if bp$[bpp_1%] = "=" then
            bpp% = bpp% - 1
            bp$[bpp%] = "=>"
          endif
          bpp_1% = bpp% - 1
          if bp$[bpp_1%] = ">" then
            bpp% = bpp% - 1
            bp$[bpp%] = ">>"
          endif
        case 40
          bpt%[bpp%] = %BRACKET_OPEN
          bpp_1% = bpp% - 1
          if bpt%[bpp_1%] = %VARIABLE then
            bpt%[bpp_1%] = %FUNCTION
          endif
        case 41
          bpt%[bpp%] = %BRACKET_CLOSE
        case 44
          bpt%[bpp%] = %COMMA
        case 32
          bpp% = bpp% - 1 ' to cancel out the add below
        case 35
          bpt%[bpp%] = %COMMENT
          exit for
        case 39   ' '
'         'ab'
'         0123
' look for terminator at position i+2 or i+3
          i2% = i% + 2
          i3% = i% + 3
          posi% = 0
          if sbyte(l$, i2%) = 39 then posi% = 2
          if sbyte(l$, i3%) = 39 then posi% = 3
          if posi% = 0 then
            call writecurrentline(bpi%[bpptr%])
            print "- syntax error: constant unterminated or wrong size\n"
            end
          endif
          i1% = i% + 1      ' point to first character of constant
          i% = i% + posi%   ' point at terminator
          posi% = posi% - 1
          bp$[bpp%] = "'" + mid(l$, i1%, posi%) + "'"
          bpt%[bpp%] = %NUMBER
        case 34
          bpt%[bpp%] = %STRING_CONSTANT
          i% = i% + 1
          j% = i%
          found% = FALSE
          while j% <= lt%
            qt% = sbyte(l$, j%)
            if qt% = 34 then found% = TRUE : exit while
            j% = j% + 1
          wend
          if found% = FALSE then
            call writecurrentline(bpi%[bpptr%])
            print "- syntax error: unterminated string constant\n"
            end
          endif
          l% = j% - i%
          bp$[bpp%] = mid(l$, i%, l%)
          i% = j%
      end select
      bpp% = bpp% + 1
'    case 32
'     ' nothing
    case else
      p$ = p$ + chr(c%)
  end select

parse_next_skip:
next

bpp% = bpp% - 1
bp$[bpp%] = "<EOL>"
bpt%[bpp%] = 0
bpi%[bpp%] = i%
bpp% = bpp% - 1

end sub


sub wo(l$)
fprint(ohandle%, l$)
end sub


#preserve
sub ProcessKeyword
' bp$[bpptr%] = KEYWORD
select case bp$[bpptr%]
  case "pragma"
    bpptr% = bpptr% + 1
    t$ = lcase(bp$[bpptr%])
    select case t$
      case "signed"
        signed% = TRUE
      case "unsigned"
        signed% = FALSE
      case else
        call writecurrentline(bpi%[bpptr%])
        print "- syntax error: unknown 'pragma' parameter\n"
      end
    end select
    bpptr% = bpp% + 1 ' EOL

  case "end"
    bpptr% = bpp% + 1 ' EOL
    ' ignore in this implementation
    ' a future version will probably need this to signal EOF.
  case "define"
    bpptr% = bpptr% + 1
    deftable$ = deftable$ + bp$[bpptr%] + "="
    bpptr% = bpptr% + 1
    deftable$ = deftable$ + bp$[bpptr%] + "ú"
    bpptr% = bpp% + 1  ' EOL
  case "switch"
    bpptr% = bpptr% + 1
    if bpptr% >= bpp% then
      call writecurrentline(bpi%[bpptr%])
      print "- syntax error: expected expression after 'switch'\n"
      end
    endif
    if bpt%[bpptr%] <> %BRACKET_OPEN then
      call writecurrentline(bpi%[bpptr%])
      print "- syntax error: expected opening '(' after 'switch'\n"
      end
    endif

    while bpptr% <= bpp%
      call Logical
    wend
    t1$ = "local SV" + str(switchval%) + ":word\n\tmov\tword ptr [SV" _
      + str(switchval%) + "],ax\n"
    call wo(t1$)
    stkptr% = stkptr% + 1
    stkswitchc1%[stkptr%] = switchval%
    stkswitchc2%[stkptr%] = 0
    stklabelend$[stkptr%] = str(jumplabel%)
    stk%[stkptr%] = %STK_SWITCH
    jumplabel% = jumplabel% + 1
    switchval% = switchval% + 1

  case "case"
    t1$ = "SW" + str(stkswitchc1%[stkptr%]) + "_" + str(stkswitchc2%[stkptr%]) + _
      ":\n\tmov\tax,word ptr [SV" + str(stkswitchc1%[stkptr%]) + "]\n"
    call wo(t1$)
    bpptr% = bpptr% + 1
    while bpptr% <= bpp%
      select case bp$[bpptr%]
        case "<", "==", ">", "<=", "=>", "!="
          call Compare(FALSE)
        case "else"
          bpptr% = bpptr% + 1
          if bp$[bpptr%] <> "{" then
            call writecurrentline(bpi%[bpptr%])
            print "- syntax error: expected opening '{' after case else\n"
            end
          endif
          call wo("\tmov\tax,-1\n") ' unconditional compare match
        case else
          ' assume we want to do an "equals" compare
          bpptr% = bpptr% - 1
          bp$[bpptr%] = "=="
          bpt%[bpptr%] = %OPERATOR
          call Compare(FALSE)
      end select
      if bp$[bpptr%] = "{" then exit while
    wend
    if bp$[bpptr%] <> "{" then
      call writecurrentline(bpi%[bpptr%])
      print "- syntax error: expected opening '{' after case\n"
      end
    endif
    bpptr% = bpp% + 1 ' point to EOL
    stkswitchc2%[stkptr%] = stkswitchc2%[stkptr%] + 1
    t1$ = "\tor\tax,ax\n\tjnz\tLL" + str(locallabel%) + "\n\tjmp\t" + _
      "SW" + str(stkswitchc1%[stkptr%]) + "_" + str(stkswitchc2%[stkptr%]) + _
      "\nLL" + str(locallabel%) + ":\n"
    call wo(t1$)
    locallabel% = locallabel% + 1
    stkptr% = stkptr% + 1
    stklabelend$[stkptr%] = str(jumplabel%)
    stk%[stkptr%] = %STK_CASE
    jumplabel% = jumplabel% + 1
#ifdef 0
  case "default"
    t1$ = "SW" + str(stkswitchc1%[stkptr%]) + "_" + str(stkswitchc2%[stkptr%]) + _
      ":\n\tmov\tax,word ptr [SV" + str(stkswitchc1%[stkptr%]) + "]\n"
    call wo(t1$)
    bpptr% = bpptr% + 1
    if bp$[bpptr%] <> "{" then
      call writecurrentline(bpi%[bpptr%])
      print "- syntax error: expected opening '{' after default\n"
      end
    endif
    bpptr% = bpp% + 1 ' point to EOL
    stkswitchc2%[stkptr%] = stkswitchc2%[stkptr%] + 1
    stkptr% = stkptr% + 1
    stklabelend$[stkptr%] = str(jumplabel%)
    stk%[stkptr%] = %STK_CASE
    jumplabel% = jumplabel% + 1
#endif
  case "if"
    bpptr% = bpptr% + 1
    if bpptr% >= bpp% then
      call writecurrentline(bpi%[bpptr%])
      print "- syntax error: expected expression after 'if'\n"
      end
    endif
    if bpt%[bpptr%] <> %BRACKET_OPEN then
      call writecurrentline(bpi%[bpptr%])
      print "- syntax error: expected opening '(' after 'if'\n"
      end
    endif

    while bpptr% <= bpp%
      call Logical
    wend
    t1$ = "\tor\tax,ax\n\tjnz\tLL" + str(locallabel%) + "\n"
    call wo(t1$)
    t1$ = "\tjmp\tJ" + str(jumplabel%) + "\nLL" + str(locallabel%) + ":\n"
    call wo(t1$)
    stkptr% = stkptr% + 1
    stklabelend$[stkptr%] = str(jumplabel%)
    stk%[stkptr%] = %STK_IF
    locallabel% = locallabel% + 1
    jumplabel% = jumplabel% + 1
  case "repeat"
    t1$ = "J" + str(jumplabel%) + ":\n"
    call wo(t1$)
    stkptr% = stkptr% + 1
    stklabelstart%[stkptr%] = jumplabel%
    jumplabel% = jumplabel% + 1
    stklabelend$[stkptr%] = str(jumplabel%)
    jumplabel% = jumplabel% + 1
    stk%[stkptr%] = %STK_REPEAT
    bpptr% = bpptr% + 1
  case "until"
    bpptr% = bpptr% + 1
    if bpptr% => bpp% then
      call writecurrentline(bpi%[bpptr%])
      print "- syntax error: expected expression after 'until'\n"
      end
    endif
    while bpptr% <= bpp%
      call Logical
    wend
    t1$ = "\tor\tax,ax\n\tjnz\tLL" + str(locallabel%) + "\n"
    call wo(t1$)
    t1$ = "\tjmp\tJ" + stklabelstart%[stkptr%] + "\nLL" + str(locallabel%) + ":\n"
    call wo(t1$)
    t1$ = "J" + stklabelend$[stkptr%] + ":\n"
    call wo(t1$)
    locallabel% = locallabel% + 1
    stkptr% = stkptr% - 1
  case "while"
    t1$ = "J" + str(jumplabel%) + ":\n"
    call wo(t1$)
    stkptr% = stkptr% + 1
    stklabelstart%[stkptr%] = jumplabel%
    jumplabel% = jumplabel% + 1
    bpptr% = bpptr% + 1
    if bpptr% => bpp% then
      call writecurrentline(bpi%[bpptr%])
      print "- syntax error: expected expression after 'while'\n"
      end
    endif
    while bpptr% <= bpp%
      call Logical
    wend
    t1$ = "\tor\tax,ax\n\tjnz\tLL" + str(locallabel%) + "\n"
    call wo(t1$)
    t1$ = "\tjmp\tJ" + str(jumplabel%) + "\nLL" + str(locallabel%) + ":\n"
    call wo(t1$)
    stklabelend$[stkptr%] = str(jumplabel%)
    stk%[stkptr%] = %STK_WHILE
    locallabel% = locallabel% + 1
    jumplabel% = jumplabel% + 1

  case "int", "char", "void"
    valdef$ = bp$[bpptr%]
    thisline.valdef% = TRUE
valdef_again:
    psz$ = "word"
'    valdef$ = bp$[bpptr%]
    if valdef$ = "char" then psz$ = "byte"

    bpptr% = bpptr% + 1
    select case bpt%[bpptr%]
      case %POINTER
        bpptr% = bpptr% + 1
        t1$ = "û" + lcase(bp$[bpptr%]) + "ú"
        ptr% = instr(localvartable$, t1$)
        if ptr% <> 0 then
          call writecurrentline(bpi%[bpptr%])
          print "- error: pointer '" + bp$[bpptr%] + "' already defined\n"
          end
        endif

        localvartable$ = localvartable$ + "P" + left(psz$, 1) + "û" + lcase(bp$[bpptr%]) + "ú"
        if thisline.global% = TRUE then
          globalvartable$ = globalvartable$ + "P" + left(psz$, 1) + "û" + lcase(bp$[bpptr%]) + "ú"
        endif
        psz$ = "word"
        goto pointer_variable
      case %VARIABLE
        t1$ = "û" + lcase(bp$[bpptr%]) + "ú"
        ptr% = instr(localvartable$, t1$)
        if ptr% <> 0 then
          call writecurrentline(bpi%[bpptr%])
          print "- error: variable '" + bp$[bpptr%] + "' already defined\n"
          end
        endif
        localvartable$ = localvartable$ + "V" + left(psz$, 1) + "û" + lcase(bp$[bpptr%]) + "ú"
pointer_variable:
        select case vartype%
          case %VAR_FUNCPARM
            t1$ = ", " + bp$[bpptr%] + ":" + psz$
            call wo(t1$)
          case %VAR_LOCAL
            t1$ = "local\t" + bp$[bpptr%] + ":" + psz$ + "\n"
            call wo(t1$)
          case %VAR_GLOBAL
            if thisline.global% = TRUE then
              if thisline.extern% = FALSE then dseg$ = dseg$ + "public\t" + bp$[bpptr%] + "\n"
            endif
            if thisline.extern% = TRUE then
              t1$ = "extrn\t" + bp$[bpptr%] + ":WORD\n"
              call wo(t1$)
            else
              dseg$ = dseg$ + bp$[bpptr%] + "\tdw\t?\n"
            endif
            globalvartable$ = globalvartable$ + "V" + left(psz$, 1) + "û" + lcase(bp$[bpptr%]) + "ú"
        end select
        bpptr% = bpptr% + 1
        if bpt%[bpptr%] = %COMMA then
          bpptr% = bpptr% + 1
          if thisline.valdef% = TRUE then
            select case bp$[bpptr%]
              case "char", "int"
                valdef$ = bp$[bpptr%]  ' new variable size
                goto valdef_again
            end select
            if bpt%[bpptr%] = %VARIABLE then
              bpptr% = bpptr% - 1
              goto valdef_again
            endif
          endif
          bpptr% = bpptr% - 1
        endif
      case %FUNCTION
        localvartable$ = globalvartable$
        ' clear out any previous local variables but retain global variables in
        ' "view" of this new function

        if thisline.extern% = TRUE then
          t1$ = "extrn\t _" + bp$[bpptr%] + ":NEAR\n"
          call wo(t1$)
'          ' add it to functype$ as an external function
'          functype$ = functype$ + "E:" + bp$[bpptr%] + "ú"
          ' add it to externtable$ - explicitly defined external func
          externtable$ = externtable$ + bp$[bpptr%] + "ú"
          exit select
        endif
        if thisline.global% = TRUE then
          t1$ = "public\t _" + bp$[bpptr%] + "\n"
          call wo(t1$)
        endif
        if bp$[bpptr%] = "main" then
          call wo("public\t_main\n")
          ismain% = TRUE
          print "*** info: main() function detected, will call link at end of compile.\n"
        endif
        ' remove previous external function definition, if it exists.
        ' this doesn't remove it entirely, but it gets rid of the "úE:"
        ' signature
        t1$ = "úE:" + bp$[bpptr%] + "ú"
        ptr% = instr(functype$, t1$)
        if ptr% <> 0 then
          ptr2% = ptr% + 3
          functype$ = left(functype$, ptr%) + mid(functype$, ptr2%)
        endif
        ' add it to functype$ as an internal function
        functype$ = functype$ + "I:" + bp$[bpptr%] + "ú"

        thisline.valdef% = FALSE ' can't do int x,y,z
        t1$ = "_" + bp$[bpptr%] + "\tproc near"
        call wo(t1$)
'        call wo("\tpush\tbp\n\tmov\tbp,sp\n")
        vartype% = %VAR_FUNCPARM
'        if valdef$ = "void" then
'          call wo("\n")
'          vartype% = %VAR_LOCAL
'        endif
        stkptr% = stkptr% + 1
        stk%[stkptr%] = %STK_FUNCTION
        stkp$[stkptr%] = "_" + bp$[bpptr%]
        stklabelend$[stkptr%] = str(jumplabel%)
        funclabelend% = jumplabel%
        jumplabel% = jumplabel% + 1
        bpptr% = bpptr% + 2
      case else
        call writecurrentline(bpi%[bpptr%])
        print "- syntax error: unexpected parameter '" + bp$[bpptr%] + "'\n"
        end
    end select
  case "return"
    bpptr% = bpptr% + 1
    if bpptr% => bpp% then
      call writecurrentline(bpi%[bpptr%])
      print "- syntax error: expected expression after 'return'\n"
      end
    endif
    while bpptr% <= bpp%
      call Logical
    wend
    t1$ = "\tjmp\tJ" + funclabelend% + "\n"
    call wo(t1$)
  case "asm"
    bpptr% = bpp% + 1
    ptr% = cinstr(curline$, 32)
    if ptr% <> 0 then
      t1$ = mid(curline$, ptr%)
      t1$ = "\t" + ltrim(t1$) + "\n"
      call wo(t1$)
    endif
  case "global"
    thisline.global% = TRUE
    bpptr% = bpptr% + 1
  case "extern"
    thisline.global% = TRUE
    thisline.extern% = TRUE
    bpptr% = bpptr% + 1
  case else
    call writecurrentline(bpi%[bpptr%])
    print "- compiler error: unknown keyword '" + bp$[bpptr%] + "'\n"
    end
end select
end sub


sub writecurrentline(p%)
print curline% + ":  "
t1$ = left(curline$, p%)
l% = len(t1$)
c% = sbyte(t1$, l%)
if c% <> 32 then t1$ = t1$ + " "
print t1$ + "##"
p% = p% + 1
t1$ = mid(curline$, p%)
c% = asc(t1$)
if c% <> 32 then print " "
print t1$ + "\n"
end sub
