' fixqb - Fixes ancient QB code

Enum TokenType
	t_undef
	t_eof
	t_eol
	t_comment
	t_string
	t_identifier
	t_symbol
	t_whitespace
	t_integer
	t_double
	t_k_enum
	t_k_type
	t_k_as
	t_k_dim
	t_k_shared
	t_k_function
	t_k_sub
	t_k_end
	t_k_exit
	t_k_if
	t_k_for
	t_k_while
	t_k_do
	t_k_next
	t_k_declare
	t_k_loop
	t_k_else
	t_k_elseif
	t_k_then
	t_k_return
	t_k_wend
	t_k_select
	t_k_case
	t_k_and
	t_k_or
	t_k_xor
	t_k_to
	t_k_until
	t_k_binary
	t_k_random
	t_k_input
	t_k_output
	t_k_goto
	t_k_print
	t_k_byval
	t_k_byref
End Enum

Type Token
	sval as string
	ival as integer
	dval as double
	ttype as TokenType
End Type

dim shared ungottenchar as integer = 0

function isNumber(c as integer) as integer
	if c >= asc("0") and c <= asc("9") then return -1
	return 0
end function

function isAlpha(c as integer) as integer
	if (c >= asc("a") and c <= asc("z")) or (c >= asc("A") and c <= asc("Z")) then return -1
	return 0
end function

function isAlphaNum(c as integer) as integer
	return isNumber(c) or isAlpha(c)
end function

function isWhitespace(c as integer) as integer
	return c = asc(" ") or c = asc("	")
end function

'reads a single character in from the specified file
'if the end of file is reached, returns ""
function getchar(f as integer) as integer
	if ungottenchar <> 0 then
		dim ret as integer = ungottenchar
		ungottenchar = 0
		return ret
	else
		if eof(f) then return -1
		
		dim b as ubyte
		
		get #f, , b
		
		return b
	end if
end function

'pushes a character "back" into the file
'do note that you can't unget more than one character...
sub ungetchar(c as integer)
	ungottenchar = c
end sub

sub readtoken(f as integer, byref tok as Token)
	dim ret as string
	dim c as integer
	
	tok.ttype = t_undef
	
	c = getchar(f)
	if c = -1 then
		tok.ttype = t_eof
		exit sub
	end if
	do while c <> -1
		if isWhitespace(c) then 'the second one is a tab, by the way
			tok.ttype = t_whitespace
			ret = chr(c)
			c = getchar(f)
			do while isWhitespace(c)
				ret &= chr(c)
				c = getchar(f)
			loop
			ungetchar(c)
			
			tok.sval = ret
			exit do
		elseif isNumber(c) then
			ret = chr(c)
actuallyanumber:
			tok.ttype = t_integer
			
			c = getchar(f)
			
			'the following rules should be equivalent to the following regular expression:
			'\d+(?:\.\d+)?(?:[dDeE]?(?:[+-]?\d+)?)?[!#fF]
			do while isNumber(c)
				ret &= chr(c)
				c = getchar(f)
			loop
			if c = asc(".") then
				tok.ttype = t_double
				ret &= "."
				c = getchar(f)
				do while isNumber(c)
					ret &= chr(c)
					c = getchar(f)
				loop
			end if
			
			if lcase(chr(c)) = "d" or lcase(chr(c)) = "e" then
				tok.ttype = t_double
				ret &= chr(c)
				c = getchar(f)
			end if
			if c = asc("+") or c = asc("-") then
				tok.ttype = t_double
				ret &= chr(c)
				do while isNumber(c)
					ret &= chr(c)
					c = getchar(f)
				loop
			end if
			if c = asc("!") or c = asc("#") or c = asc("f") or c = asc("F") then
				tok.ttype = t_double
				ret &= chr(c)
				c = getchar(f)
			end if
			
			ungetchar(c)
			
			if tok.ttype = t_double then
				tok.dval = cdbl(ret)
			else
				tok.ival = cint(ret)
			end if
			exit do
		elseif c = asc("'") then
			tok.ttype = t_comment
			ret = ""
			c = getchar(f)
			do while c <> 10 and c <> 13 and c <> -1
				ret &= chr(c)
				c = getchar(f)
			loop
			
			dim p as integer = getchar(f)
			
			if p <> 10 then
				ungetchar(p)
			end if
			
			tok.sval = ret
			exit do
		elseif c = asc("!") or c = asc("@") or c = asc("#") or c = asc("$") or c = asc("%") _
			or c = asc("^") or c = asc("&") or c = asc("*") or c = asc("(") or c = asc(")") _
			or c = asc("{") or c = asc("}") or c = asc("-") or c = asc("+") or c = asc("=") _
			or c = asc("`") or c = asc("[") or c = asc("]") or c = asc(".") or c = asc(",") _
			or c = asc("<") or c = asc(">") or c = asc(":") or c = asc(";") or c = asc("_") _
			or c = asc("\") or c = asc("/") then
			
			ret = chr(c)
			
			tok.ttype = t_symbol
			dim p as integer = getchar(f)
			ungetchar(p)
			
			'bloody unary minus
			if c = asc("-") and isNumber(p) then
				goto actuallyanumber
			end if
			
			if _
				(c = asc(">") and p = asc("=")) or _
				(c = asc("<") and p = asc("=")) or _
				(c = asc("&") and p = asc("=")) or _
				(c = asc("+") and p = asc("=")) or _
				(c = asc("-") and p = asc("=")) or _
				(c = asc("*") and p = asc("=")) or _
				(c = asc("/") and p = asc("=")) _
			then
				
				'consume the peek char
				getchar(f)
				ret &= chr(p)
			end if
			
			tok.sval = ret
			exit do
		elseif c = asc("""") then
			'string!
			tok.ttype = t_string
			ret = ""
			c = getchar(f)
			do until c = asc("""") or c = -1
				'print chr(c);
				ret &= chr(c)
				c = getchar(f)
			loop
			'print
			if c = -1 then
				print "Unexpected end of file in string"
				end
			end if
			tok.sval = ret
			exit do
		elseif isAlpha(c) then
			'first, let's read it in. Then we'll decide what it is...
			ret = chr(c)
			c = getchar(f)
			do while isAlphaNum(c) or c = asc("_")
				ret &= chr(c)
				c = getchar(f)
			loop
			ungetchar(c)
			
			select case lcase(ret)
				case "if"
					tok.ttype = t_k_if
				case "else"
					tok.ttype = t_k_else
				case "elseif"
					tok.ttype = t_k_elseif
				case "end"
					tok.ttype = t_k_end
				case "exit"
					tok.ttype = t_k_exit
				case "do"
					tok.ttype = t_k_do
				case "while"
					tok.ttype = t_k_while
				case "wend"
					tok.ttype = t_k_wend
				case "dim"
					tok.ttype = t_k_dim
				case "declare"
					tok.ttype = t_k_declare
				case "shared"
					tok.ttype = t_k_shared
				case "next"
					tok.ttype = t_k_next
				case "then"
					tok.ttype = t_k_then
				case "return"
					tok.ttype = t_k_return
				case "as"
					tok.ttype = t_k_as
				case "sub"
					tok.ttype = t_k_sub
				case "function"
					tok.ttype = t_k_function
				case "type"
					tok.ttype = t_k_type
				case "enum"
					tok.ttype = t_k_enum
				case "select"
					tok.ttype = t_k_select
				case "case"
					tok.ttype = t_k_case
				case "and"
					tok.ttype = t_k_and
				case "or"
					tok.ttype = t_k_or
				case "xor"
					tok.ttype = t_k_xor
				case "for"
					tok.ttype = t_k_for
				case "to"
					tok.ttype = t_k_to
				case "until"
					tok.ttype = t_k_until
				case "binary"
					tok.ttype = t_k_binary
				case "random"
					tok.ttype = t_k_random
				case "input"
					tok.ttype = t_k_input
				case "output"
					tok.ttype = t_k_output
				case "goto"
					tok.ttype = t_k_goto
				case "print"
					tok.ttype = t_k_print
				case "loop"
					tok.ttype = t_k_loop
				case "byval"
					tok.ttype = t_k_byval
				case "byref"
					tok.ttype = t_k_byref
				case else
					tok.ttype = t_identifier
					tok.sval = ret
			end select
			
			
			exit do
		elseif c = 13 or c = 10 then
			tok.ttype = t_eol
			dim p as integer = getchar(f)
			if p <> 10 then ungetchar(p)
			exit do
			
		else 'anything else...
			print "OMGWAT: " & chr(c) & " (" & c & ")"
			end
			c = getchar(f)
			
		end if
	loop
	
end sub


dim infile as string = command(1)

if infile = "" then
	print "Yo, pass me a file name, plskthx."
	end
end if



'so, like, basically, this is a basic parser. Except, we're not executing anything, just fixing
'symbols and the like.

'here-a-we-go!
dim f as integer = freefile

open infile for binary as #f

dim tok as token

do until tok.ttype = t_eof
	readtoken(f, tok)

	select case tok.ttype
		case t_undef
			'print "Undefined..."
		case t_eof
			'print "End of File!"
			exit do
		case t_eol
			'print "End of Line!"
			print
		case t_comment
			print "'" & tok.sval
		case t_integer
			print str(tok.ival);
		case t_double
			print str(tok.dval);
		case t_string
			print """" & tok.sval & """";
		case t_symbol
			print tok.sval;
		case t_identifier
			print tok.sval;
		case t_k_if
			print "IF";
		case t_k_then
			print "THEN";
		case t_k_else
			print "ELSE";
		case t_k_elseif
			print "ELSEIF";
		case t_k_enum
			print "ENUM";
		case t_k_type
			print "TYPE";
		case t_k_as
			print "AS";
		case t_k_dim
			print "DIM";
		case t_k_shared
			print "SHARED";
		case t_k_function
			print "FUNCTION";
		case t_k_sub
			print "SUB";
		case t_k_end
			print "END";
		case t_k_exit
			print "EXIT";
		case t_k_for
			print "FOR";
		case t_k_while
			print "WHILE";
		case t_k_do
			print "DO";
		case t_k_next
			print "NEXT";
		case t_k_declare
			print "DECLARE";
		case t_k_loop
			print "LOOP";
		case t_k_return
			print "RETURN";
		case t_k_wend
			print "WEND";
		case t_k_select
			print "SELECT";
		case t_k_case
			print "CASE";
		case t_k_and
			print "AND";
		case t_k_or
			print "OR";
		case t_k_xor
			print "XOR";
		case t_k_for
			print "FOR";
		case t_k_to
			print "TO";
		case t_k_until
			print "UNTIL";
		case t_k_binary
			print "BINARY";
		case t_k_random
			print "RANDOM";
		case t_k_input
			print "INPUT";
		case t_k_output
			print "OUTPUT";
		case t_k_goto
			print "GOTO";
		case t_k_print
			print "PRINT";
		case t_k_byval
			print "BYVAL";
		case t_k_byref
			print "BYREF";
		case t_whitespace
			print tok.sval;
		case else
			print "Unknown type: " & tok.ttype
			end
	end select
loop

close #f
