//
// Lexical anaylzer
//
//def isalpha(c)
//    if c >= 'A' and c <= 'Z'
//        return TRUE
//    //elsif c >= 'a' and c <= 'z'
//    //    return TRUE
//    elsif c == '_'
//        return TRUE
//    fin
//    return FALSE
//end
//def isnum(c)
//    return c >= '0' and c <= '9'
//end
//def isalphanum(c)
//    if c >= 'A' and c <= 'Z'
//        return TRUE
//    //elsif c >= 'a' and c <= 'z'
//    //    return TRUE
//    elsif c >= '0' and c <= '9'
//        return TRUE
//    elsif c == '_'
//        return TRUE
//    fin
//    return FALSE
//end
//def keymatch
//    byte i, keypos
//    word chrptr
//
//    keypos = 0
//    while keywrds[keypos] < tknlen
//        keypos = keypos + keywrds[keypos] + 2
//    loop
//    chrptr = tknptr - 1
//    while keywrds[keypos] == tknlen
//        for i = 1 to tknlen
//            if ^(chrptr + i) <> keywrds[keypos + i]; break; fin
//         next
//        if i > tknlen
//            return keywrds[keypos + keywrds[keypos] + 1]
//        fin
//        keypos = keypos + keywrds[keypos] + 2
//    loop
//    return ID_TKN
//end
def scannum
    word num
    num = 0

    if ^scanptr == '$'
        repeat
            scanptr++
            if ^scanptr >= '0' and ^scanptr <= '9'
                num = (num << 4) + ^scanptr - '0'
            elsif ^scanptr >= 'A' and ^scanptr <= 'F'
                num = (num << 4) + ^scanptr - '7'// 'A'-10
            elsif ^scanptr >= 'a' and ^scanptr <= 'f'
                num = (num << 4) + ^scanptr - 'W'// 'a'-10
            else
                break
            fin
        until not ^scanptr
    elsif ^scanptr < '0' or ^scanptr > '9'
        repeat
            num = num * 10 + ^scanptr - '0'
            scanptr++
        until ^scanptr < '0' or ^scanptr > '9'
    else
        num = ^scanptr
    fin
    return num
end

def scan
    //
    // Skip whitespace
    //
    while ^scanptr == ' '
        scanptr++
    loop
    tknptr  = scanptr
    scanchr, scanptr, token = scanid(scanptr, @keywrds) //scanchr = toupper(^scanptr)
    //
    // Scan for token based on first character
    //
    if token //if isalpha(scanchr)
        //
        // ID, either variable name or reserved word
        //
        //repeat
        //    ^scanptr = scanchr
        //    scanptr++
        //    scanchr = toupper(^scanptr)
        //until not isalphanum(scanchr)
        tknlen = scanptr - tknptr
    elsif scanchr >= '0' and scanchr <= '9' // isnum()
        //
        // Decimal constant
        //
        token    = INT_TKN
        constval = 0
        repeat
            constval = constval * 10 + ^scanptr - '0'
            scanptr++
        until ^scanptr < '0' or ^scanptr > '9'
    else
        //
        // Potential multiple character tokens
        //
        when scanchr
            is '$'
                //
                // Hexadecimal constant
                //
                token    = INT_TKN
                constval = 0
                repeat
                    scanptr++
                    if ^scanptr >= '0' and ^scanptr <= '9'
                        constval = (constval << 4) + ^scanptr - '0'
                    elsif ^scanptr >= 'A' and ^scanptr <= 'F'
                        constval = (constval << 4) + ^scanptr - '7'// 'A'-10
                    elsif ^scanptr >= 'a' and ^scanptr <= 'f'
                        constval = (constval << 4) + ^scanptr - 'W'// 'a'-10
                    else
                        break
                    fin
                until not ^scanptr
                break
            is '\''
                //
                // Character constant
                //
                token = CHR_TKN
                scanptr++
                if ^scanptr <> '\\'
                    constval = ^scanptr
                else
                    scanptr++
                    when ^scanptr
                        is 'n'
                        is 'N'
                            constval = $0D; break
                        is 'r'
                        is 'R'
                            constval = $0A; break
                        is 't'
                        is 'T'
                            constval = $09; break
                        is '\\'
                            constval = '\\'; break
                        otherwise
                            constval = scannum
                            scanptr--
                    wend
                fin
                if ^(scanptr + 1) <> '\''; exit_err(ERR_INVAL|ERR_CONST); fin
                scanptr = scanptr + 2
                break
            is '"'
                //
                // String constant
                //
                token     = STR_TKN
                constval  = strconstptr
                ^constval = 0
                strconstptr++
                scanptr++
                while ^scanptr and ^scanptr <> '"'
                    if ^scanptr <> '\\'
                        ^strconstptr = ^scanptr
                   else
                        scanptr++
                        when ^scanptr
                            is 'n'
                            is 'N'
                                ^strconstptr = $0D; break
                            is 'r'
                            is 'R'
                                ^strconstptr = $0A; break
                            is 't'
                            is 'T'
                                ^strconstptr = $09; break
                            is '\\'
                                ^strconstptr = '\\'; break
                            otherwise
                                ^strconstptr = scannum
                                scanptr--
                        wend
                    fin
                    strconstptr++
                    ^constval++
                    scanptr++
                loop
                if not ^scanptr; exit_err(ERR_INVAL|ERR_CONST); fin
                strconstptr++
                scanptr++
                break
            is '/'
                if ^(scanptr + 1) == '/'
                    token    = EOL_TKN
                    ^scanptr = $00
                else
                    token = DIV_TKN
                    scanptr++
                fin
                break
            is '='
                if ^(scanptr + 1) == '='
                    token   = EQ_TKN
                    scanptr = scanptr + 2
                elsif ^(scanptr + 1) == '>'
                    token   = PTRW_TKN
                    scanptr = scanptr + 2
                else
                    token = SET_TKN
                    scanptr++
                fin
                break
            is '-'
                if ^(scanptr + 1) == '>'
                    token   = PTRB_TKN
                    scanptr = scanptr + 2
                elsif ^(scanptr + 1) == '-'
                    token   = DEC_TKN
                    scanptr = scanptr + 2
                else
                    token = SUB_TKN
                    scanptr++
                fin
                break
            is '+'
                if ^(scanptr + 1) == '+'
                    token   = INC_TKN
                    scanptr = scanptr + 2
                else
                    token = ADD_TKN
                    scanptr++
                fin
                break
            is '>'
                if ^(scanptr + 1) == '>'
                    token   = SHR_TKN
                    scanptr = scanptr + 2
                elsif ^(scanptr + 1) == '='
                    token   = GE_TKN
                    scanptr = scanptr + 2
                else
                    token = GT_TKN
                    scanptr++
                fin
                break
            is '<'
                if ^(scanptr + 1) == '<'
                    token   = SHL_TKN
                    scanptr = scanptr + 2
                elsif ^(scanptr + 1) == '='
                    token   = LE_TKN
                    scanptr = scanptr + 2
                elsif ^(scanptr + 1) == '>'
                    token   = NE_TKN
                    scanptr = scanptr + 2
                else
                    token = LT_TKN
                    scanptr++
                fin
                break
            is ':'
                if ^(scanptr + 1) == ':'
                    token = TRIELSE_TKN;
                    scanptr = scanptr + 2
                else
                    token = COLON_TKN;
                    scanptr++
                fin
                break
            is '?'
                if ^(scanptr + 1) == '?'
                    token = TERNARY_TKN;
                    scanptr = scanptr + 2
                fin
                break
            is 0
            is ';'
                if token <> EOF_TKN
                    token = EOL_TKN
                fin
                break
            otherwise
                //
                // Simple single character tokens
                //
                token = scanchr | $80
                scanptr++
        wend
    fin
    tknlen = scanptr - tknptr
    return token
end
def rewind(ptr)#0
    scanptr = ptr
    tknptr  = ptr
end
def lookahead
    word backptr, backtkn
    byte prevtkn, prevlen, look
    backptr = scanptr
    backtkn = tknptr
    prevtkn = token
    prevlen = tknlen
    look    = scan
    scanptr = backptr
    tknptr  = backtkn
    token   = prevtkn
    tknlen  = prevlen
    return look
end
//
// Get next line of input
//
def nextln
    strconstptr = strconstbuff // Reset string constant buffer
    if ^scanptr == ';'
        scanptr++
        scan
    else
        if token <> EOL_TKN and token <> EOF_TKN; putc(token&$7F); puts("Extraneous characters\n"); exit_err(0); fin
        scanptr = inbuff
        ^instr  = fileio:read(refnum, inbuff, 127)
        if ^instr
            ^(instr + ^instr) = NULL // NULL terminate string
            lineno++
            if !(lineno & $0F); putc('.'); fin
            if scan == INCLUDE_TKN
                if incref; puts("Nested INCLUDEs not allowed\n"); exit_err(0); fin
                if scan <> STR_TKN; puts("Missing INCLUDE file\n"); exit_err(0); fin
                strcpy(@incfile, constval)
                incref    = fileio:open(@incfile)
                if not incref
                    puts("Unable to open INCLUDE file: ")
                    puts(@incfile)
                    putln
                    exit_err(0)
                fin
                fileio:newline(incref, $7F, $0D)
                refnum    = incref
                parsefile = @incfile
                srcline   = lineno
                lineno    = 0
                scan
                return nextln
            fin
        else
            if refnum == incref
                fileio:close(incref)
                incref    = 0
                refnum    = srcref
                parsefile = @srcfile
                lineno    = srcline
                return nextln
            else
                *instr = NULL // NULL terminated 0 length string
                token  = EOF_TKN
            fin
        fin
    fin
    return token
end
