Task: Compiler_lexical analyzer








VB: dim shared source as string, the_ch as string, tok as string, toktyp as string
dim shared line_n as integer, col_n as integer, text_p as integer, err_line as integer, err_col as integer, errors as integer

declare function isalnum&(s as string)
declare function isalpha&(s as string)
declare function isdigit&(s as string)
declare sub divide_or_comment
declare sub error_exit(line_n as integer, col_n as integer, msg as string)
declare sub follow(c as string, typ2 as string, typ1 as string)
declare sub nextch
declare sub nexttok
declare sub read_char_lit
declare sub read_ident
declare sub read_number
declare sub read_string

const c_integer = "Integer", c_ident = "Identifier", c_string = "String"

dim out_fn as string, out_tok as string

if command$(1) = "" then print "Expecting a filename": end
open command$(1) for binary as #1
source = space$(lof(1))
get #1, 1, source
close #1

out_fn = command$(2): if out_fn <> "" then open out_fn for output as #1

line_n = 1: col_n = 0: text_p = 1: the_ch = " "

do
    call nexttok
    select case toktyp
        case c_integer, c_ident, c_string: out_tok = tok
        case else:                         out_tok = ""
    end select
    if out_fn = "" then
        print err_line, err_col, toktyp, out_tok
    else
        print #1, err_line, err_col, toktyp, out_tok
    end if
loop until errors or tok = ""
if out_fn <> "" then close #1
end


sub nexttok
    toktyp = ""
    restart: err_line = line_n: err_col = col_n: tok = the_ch
    select case the_ch
        case " ", chr$(9), chr$(10): call nextch:          goto restart
        case "/": call divide_or_comment: if tok = "" then goto restart

        case "%": call nextch: toktyp = "Op_mod"
        case "(": call nextch: toktyp = "LeftParen"
        case ")": call nextch: toktyp = "RightParen"
        case "*": call nextch: toktyp = "Op_multiply"
        case "+": call nextch: toktyp = "Op_add"
        case ",": call nextch: toktyp = "Comma"
        case "-": call nextch: toktyp = "Op_subtract"
        case ";": call nextch: toktyp = "Semicolon"
        case "{": call nextch: toktyp = "LeftBrace"
        case "}": call nextch: toktyp = "RightBrace"

        case "&": call follow("&", "Op_and",          "")
        case "|": call follow("|", "Op_or",           "")
        case "!": call follow("=", "Op_notequal",     "Op_not")
        case "<": call follow("=", "Op_lessequal",    "Op_less")
        case "=": call follow("=", "Op_equal",        "Op_assign")
        case ">": call follow("=", "Op_greaterequal", "Op_greater")

        case chr$(34): call read_string
        case chr$(39): call read_char_lit

        case "": toktyp = "End_of_input"

        case else
            if isdigit&(the_ch) then
                call read_number
            elseif isalpha&(the_ch) then
                call read_ident
            else
                call nextch
            end if
    end select
end sub

sub follow(c as string, if_both as string, if_one as string)
    call nextch
    if the_ch = c then
        tok = tok + the_ch
        call nextch
        toktyp = if_both
    else
        if if_one = "" then call error_exit(line_n, col_n, "Expecting " + c): exit sub
        toktyp = if_one
    end if
end sub

sub read_string
    toktyp = c_string
    call nextch
    do
        tok = tok + the_ch
        select case the_ch
            case chr$(10): call error_exit(line_n, col_n, "EOL in string"): exit sub
            case "":       call error_exit(line_n, col_n, "EOF in string"): exit sub
            case chr$(34): call nextch: exit sub
            case else:     call nextch
        end select
    loop
end sub

sub read_char_lit
    toktyp = c_integer
    call nextch
    if the_ch = chr$(39) then
        call error_exit(err_line, err_col, "Empty character constant"): exit sub
    end if

    if the_ch = "\" then
        call nextch
        if the_ch = "n" then
            tok = "10"
        elseif the_ch = "\" then
            tok = "92"
        else
            call error_exit(line_n, col_n, "Unknown escape sequence:" + the_ch): exit sub
        end if
    else
        tok = ltrim$(str$(asc(the_ch)))
    end if

    call nextch
    if the_ch <> chr$(39) then
        call error_exit(line_n, col_n, "Multi-character constant"): exit sub
    end if
    call nextch
end sub

sub divide_or_comment
    call nextch
    if the_ch <> "*" then
        toktyp = "Op_divide"
    else  
        tok = ""
        call nextch
        do
            if the_ch = "*" then
                call nextch
                if the_ch = "/" then
                    call nextch
                    exit sub
                end if
            elseif the_ch = "" then
                call error_exit(line_n, col_n, "EOF in comment"): exit sub
            else
                call nextch
            end if
        loop
    end if
end sub

sub read_ident
    do
        call nextch
        if not isalnum&(the_ch) then exit do
        tok = tok + the_ch
    loop
    select case tok
        case "else":  toktyp = "keyword_else"
        case "if":    toktyp = "keyword_if"
        case "print": toktyp = "keyword_print"
        case "putc":: toktyp = "keyword_putc"
        case "while": toktyp = "keyword_while"
        case else:    toktyp = c_ident
    end select
end sub

sub read_number
    toktyp = c_integer
    do
        call nextch
        if not isdigit&(the_ch) then exit do
        tok = tok + the_ch
    loop

    if isalpha&(the_ch) then
        call error_exit(err_line, err_col, "Bogus number: " + tok + the_ch): exit sub
    end if
end sub

function isalpha&(s as string)
  dim c as string
  c = left$(s, 1)
  isalpha& = c <> "" and instr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", c) > 0
end function

function isdigit&(s as string)
  dim c as string
  c = left$(s, 1)
  isdigit& = c <> "" and instr("0123456789", c) > 0
end function

function isalnum&(s as string)
  dim c as string
  c = left$(s, 1)
  isalnum& = c <> "" and instr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", c) > 0
end function


sub nextch
    the_ch = ""
    col_n = col_n + 1
    if text_p > len(source) then exit sub

    the_ch = mid$(source, text_p, 1)
    text_p = text_p + 1

    if the_ch = chr$(13) then
        the_ch = chr$(10)
        if text_p <= len(source) then
            if mid$(source, text_p, 1) = chr$(10) then
                text_p = text_p + 1
            end if
        end if
    end if

    if the_ch = chr$(10) then
        line_n = line_n + 1
        col_n = 0
    end if

end sub

sub error_exit(line_n as integer, col_n as integer, msg as string)
    errors = -1
    print line_n, col_n, msg
    end
end sub