# LIST_ON : REM CHANGE TO COMMENT LINE IF NOT WANTED
# XREF_ON : REM CHANGE TO COMMENT LINE IF NOT WANTED
# CONSTANT UBN~VERSION$ = "1.060202 RC0"
# CONSTANT UBN_TITLE$ = "Urbane XUBN~VERSION$ - Tandy Color Computer 3 Disk Extended Color Basic Preprocessor."
' Original Left Hello A. Nani Mouse Inx.
# CONSTANT IN_FILE$ = "UBN_IN.TXT"
# CONSTANT OUT_FILE$ = "UBN_OUT.TXT"
# CONSTANT LST_FILE$ = "UBN_LST.TXT"
# CONSTANT XRF_FILE$ = "UBN_XRF.TXT"
# CONSTANT NUM_M1 = -1
# CONSTANT NUM_0 = 0
# CONSTANT NUM_1 = 1
# CONSTANT NUM_2 = 2
# CONSTANT NUM_3 = 3
# CONSTANT NUM_4 = 4
# CONSTANT TRUE = 1
# CONSTANT FALSE = 0
# CONSTANT UBN_XREF_LIST_INITIAL = 0 : REM ON = 1, OFF = 0
# CONSTANT UBN_LIST_LIST_INITIAL = 0 : REM ON = 1, OFF = 0
# CONSTANT UBN_MEM_PCLEAR = 1
# CONSTANT UBN_MEM_FILES_COUNT = 4
# CONSTANT UBN_MEM_FILES_SPACE = 100
# CONSTANT UBN_MEM_CLEAR = 8500
# CONSTANT UBN_MEM_LINE_LABEL_MAX_COUNT = 100
# CONSTANT UBN_MEM_VARIABLE_NAMES_MAX_COUNT = 150
# CONSTANT UBN_MEM_KEYWORD_TABLE_MAX_COUNT = 200
# CONSTANT UBN_MEM_ILLEGAL_2CH_VARIABLE_TABLE_MAX_COUNT = 10
# CONSTANT ASC_TAB = 09 : REM TAB
# CONSTANT ASC_BLANK = 32 : REM ASC(" ")
# CONSTANT ASC_DOUBLE_QUOTE = 34 : REM ASC(""")
' CONSTANT ASC_DOUBLE_QUOTE$ is Defined as a program value, not a constant!
# CONSTANT ASC_POUND = 35 : REM ASC("#")
# CONSTANT ASC_DOLLAR = 36 : REM ASC("$")
# CONSTANT ASC_PERCENT = 37 : REM ASC("%")
# CONSTANT ASC_AMPERSAND = 38 : REM ASC("&")
# CONSTANT ASC_SINGLE_QUOTE = 39 : REM ASC("'")
# CONSTANT ASC_PLUS = 43 : REM ASC("+")
# CONSTANT ASC_MINUS = 45 : REM ASC("-")
# CONSTANT ASC_PERIOD = 46 : REM ASC(".")
# CONSTANT ASC_0 = 48 : REM ASC("0")
# CONSTANT ASC_7 = 55 : REM ASC("7")
# CONSTANT ASC_8 = 56 : REM ASC("8")
# CONSTANT ASC_9 = 57 : REM ASC("9")
# CONSTANT ASC_COLON = 58 : REM ASC(":")
# CONSTANT ASC_EQUALS = 61 : REM ASC("=")
# CONSTANT ASC_TILDE = 64 : REM ASC("~")
# CONSTANT ASC_A_U = 65 : REM ASC("A")
# CONSTANT ASC_E_U = 69 : REM ASC("E")
# CONSTANT ASC_F_U = 70 : REM ASC("F")
# CONSTANT ASC_H_U = 72 : REM ASC("H")
# CONSTANT ASC_O_U = 79 : REM ASC("O")
# CONSTANT ASC_X_U = 88 : REM ASC("X")
# CONSTANT ASC_Z_U = 90 : REM ASC("Z")
# CONSTANT ASC_UNDERSCORE = 95 : REM ASC("_")
# CONSTANT ASC_a_L = 97 : REM ASC("a")
# CONSTANT ASC_e_L = 101 : REM ASC("e")
# CONSTANT ASC_f_L = 102 : REM ASC("f")
# CONSTANT ASC_h_L = 104 : REM ASC("h")
# CONSTANT ASC_o_L = 111 : REM ASC("o")
# CONSTANT ASC_x_L = 120 : REM ASC("x")
# CONSTANT ASC_z_L = 122 : REM ASC("z")
# CONSTANT UBN_XRF_TITLE = 1
# CONSTANT UBN_XRF_LINE_LABEL = 2
# CONSTANT UBN_XRF_LINE_REFERENCE = 3
# CONSTANT UBN_XRF_VARIABLE = 4
# CONSTANT UBN_XRF_VARIABLE_REFERENCE = 5
' CONSTANT UBN_XRF_CONSTANT = 6
' CONSTANT UBN_XRF_CONSTANT_REFERENCE = 7
# CONSTANT UBN_XRF_KEYWORD_REFERENCE = 8
' Start of Executation
GOTO UBN_START
GET_IN_LINE_CHAR IN_LINE_CHAR_COUNTER = IN_LINE_CHAR_COUNTER + NUM_1
IF IN_LINE_CHAR_COUNTER > I_LINE_LENGTH GOTO END_IN_LINE
LAST_CH = IN_LINE_CH
IN_LINE_CH$ = MID$(IN_LINE$, IN_LINE_CHAR_COUNTER, NUM_1)
IN_LINE_CH = ASC(IN_LINE_CH$)
IF IN_LINE_CH >= ASC_BLANK THEN RETURN
IF IN_LINE_CH = ASC_TAB GOTO SUBSTUTE_SPACE_FOR_TAB
PRINT #FILE_L, LINE_NUMBER; " " ; IN_LINE$
PRINT #FILE_L, LINE_NUMBER; " Control Character "; IN_LINE_CH; " Found in Input Line at "; IN_LINE_CHAR_COUNTER
RETURN
SUBSTUTE_SPACE_FOR_TAB IN_LINE_CH$ = CHR$(ASC_BLANK)
IN_LINE_CH = ASC_BLANK
RETURN
END_IN_LINE IN_LINE_CH = NUM_M1
IN_LINE_CH$ = CHR$(ASC_TILDE)
RETURN
'''
' On Entry, first character of label or variable is in "IN_LINE_CH$" and "IN_LINE_CH"
' On Exit, "IN_LINE_CH$" and "IN_LINE_CH" have terminating character (next character not part of VLString).
GET_VLSTRING VLSTRING$ = IN_LINE_CH$
NEXT_VLSTRING_CH GOSUB GET_IN_LINE_CHAR
IF IN_LINE_CH < NUM_0 THEN RETURN
IF IN_LINE_CH >= ASC_A_U AND IN_LINE_CH <= ASC_Z_U GOTO VLSTRING_ADD_UPPER_ALPHA
IF IN_LINE_CH >= ASC_a_L AND IN_LINE_CH <= ASC_z_L GOTO VLSTRING_ADD_LOWER_ALPHA
IF IN_LINE_CH >= ASC_0 AND IN_LINE_CH <= ASC_9 GOTO VLSTRING_ADD_NUMBER
IF IN_LINE_CH = ASC_UNDERSCORE GOTO VLSTRING_ADD_SPECIAL
IF IN_LINE_CH = ASC_TILDE GOTO VLSTRING_ADD_SPECIAL
IF IN_LINE_CH = ASC_DOLLAR GOTO VLSTRING_ADD_DOLLAR
' REM Character Found that cannot be in Variable or line Label
RETURN
VLSTRING_ADD_LOWER_ALPHA IN_LINE_CH = IN_LINE_CH - ( ASC_a_L - ASC_A_U )
IN_LINE_CH$ = CHR$(IN_LINE_CH)
VLSTRING_ADD_UPPER_ALPHA REM
VLSTRING_ADD_NUMBER REM
VLSTRING_ADD_SPECIAL REM
VLSTRING_ADD_DOLLAR REM
VLSTRING$ = VLSTRING$ + IN_LINE_CH$
' If an "ASC_DOLLAR" is found, declare label or variable end found
IF IN_LINE_CH = ASC_DOLLAR THEN LAST_CH = IN_LINE_CH : GOSUB GET_IN_LINE_CHAR : RETURN
GOTO NEXT_VLSTRING_CH
'''
' On Entry, "ASC_DOUBLE_QUOTE" is in "IN_LINE_CH$" and "IN_LINE_CH"
' On Exit, "IN_LINE_CH$" and "IN_LINE_CH" have terminating character or next character.
GET_QSTRING QSTRING$ = IN_LINE_CH$
QSTRING_SKIP_VARIABLE = FALSE
MORE_QSTRING GOSUB GET_IN_LINE_CHAR
IF IN_LINE_CH <= NUM_0 GOTO BAD_QSTRING
QSTRING$ = QSTRING$ + IN_LINE_CH$
IF IN_LINE_CH = ASC_BLANK THEN QSTRING_SKIP_VARIABLE = FALSE
IF QSTRING_SKIP_VARIABLE = TRUE GOTO SKIP_VARIABLE
IF IN_LINE_CH = ASC_X_U OR IN_LINE_CH = ASC_x_L GOTO GET_QSTRING_VARIABLE
SKIP_VARIABLE IF IN_LINE_CH <> ASC_DOUBLE_QUOTE GOTO MORE_QSTRING
GOSUB GET_IN_LINE_CHAR
RETURN
GET_QSTRING_VARIABLE GOSUB GET_IN_LINE_CHAR
IF IN_LINE_CH <= NUM_0 GOTO BAD_QSTRING
IF IN_LINE_CH = ASC_DOUBLE_QUOTE GOTO NO_QSVARIABLE
IF IN_LINE_CH >= ASC_A_U AND IN_LINE_CH <= ASC_Z_U GOTO GET_QSTRING_VAR
IF IN_LINE_CH >= ASC_a_L AND IN_LINE_CH <= ASC_z_L GOTO GET_QSTRING_VAR
IF IN_LINE_CH = ASC_UNDERSCORE GOTO GET_QSTRING_VAR
GOTO MORE_QSTRING
NO_QSVARIABLE QSTRING$ = QSTRING$ + IN_LINE_CH$
GOSUB GET_IN_LINE_CHAR
RETURN
GET_QSTRING_VAR QSTRING_CHAR_COUNTER_SAVE = IN_LINE_CHAR_COUNTER
QSTRING_LAST_CHAR_SAVE$ = IN_LINE_CH$
GOSUB GET_VLSTRING
IF LAST_CH <> ASC_DOLLAR GOTO NOT_QSTRING_VAR
FOR VARIABLE_NAMES_COUNTER = VARIABLE_NAMES_START TO VARIABLE_NAMES_COUNT
IF VLSTRING$ = VARIABLE_NAMES$(VARIABLE_NAMES_COUNTER,VARIABLE_NAMES_NAME) GOTO HAVE_QSTRING_VARIABLE
NEXT VARIABLE_NAMES_COUNTER
' Have New Variable
VARIABLE_NAMES_COUNT = VARIABLE_NAMES_COUNT + NUM_1
IF VARIABLE_NAMES_COUNT > VARIABLE_NAMES_MAX_COUNT THEN PRINT #FILE_L, " VARIABLE_NAMES_MAX_COUNT Exceeded" : STOP
VARIABLE_NAMES$(VARIABLE_NAMES_COUNT,VARIABLE_NAMES_NAME) = VLSTRING$
VARIABLE_NAMES$(VARIABLE_NAMES_COUNT,VARIABLE_NAMES_2CH) = "Pending"
GOSUB GET_NEW_2CH_VARIABLE
VARIABLE_NAMES$(VARIABLE_NAMES_COUNT,VARIABLE_NAMES_2CH) = NEW_2CH_VARIABLE$ + "$"
VARIABLE_NAMES_COUNTER = VARIABLE_NAMES_COUNT
HAVE_QSTRING_VARIABLE IF ASC(VARIABLE_NAMES$(VARIABLE_NAMES_COUNTER,VARIABLE_NAMES_2CH)) = ASC_DOUBLE_QUOTE GOTO ADD_QSTRING_CONSTANT
' Add existing 2ch variable to QUOTE string
QSTRING$ = QSTRING$ + VARIABLE_NAMES$(VARIABLE_NAMES_COUNTER,VARIABLE_NAMES_2CH)
GOTO MORE_QSTRING
' Do Not Include the 'X'
' Add Constant Without Leading and Trailing Quotes
ADD_QSTRING_CONSTANT QSTRING_LENGTH = LEN(QSTRING$) - NUM_1
QSTRING$ = LEFT$(QSTRING$,QSTRING_LENGTH)
QSTRING_CONSTANT_LENGTH = LEN(VARIABLE_NAMES$(VARIABLE_NAMES_COUNTER,VARIABLE_NAMES_2CH)) - NUM_2
QSTRING$ = QSTRING$ + MID$(VARIABLE_NAMES$(VARIABLE_NAMES_COUNTER,VARIABLE_NAMES_2CH),NUM_2,QSTRING_CONSTANT_LENGTH)
GOTO MORE_QSTRING
NOT_QSTRING_VAR IN_LINE_CHAR_COUNTER = QSTRING_CHAR_COUNTER_SAVE
IN_LINE_CH$ = QSTRING_LAST_CHAR_SAVE$
QSTRING_SKIP_VARIABLE = TRUE
QSTRING$ = QSTRING$ + IN_LINE_CH$
' PRINT #FILE_L, LINE_NUMBER ; " QSTRING$ = "; QSTRING$; ", IN_LINE_CH = "; IN_LINE_CH
' PRINT #FILE_L, LINE_NUMBER ; " VLSTRING$ = "; VLSTRING$
GOTO MORE_QSTRING
GOSUB GET_IN_LINE_CHAR
RETURN
BAD_QSTRING PRINT #FILE_L, LINE_NUMBER ; " ERROR - Missing end of Quoted String " ; QSTRING$
ERRORS = ERRORS + NUM_1
RETURN
GET_NEW_2CH_VARIABLE NEW_2CH_VARIABLE$ = CHR$(HIGH_2CH_VARIABLE) + CHR$(LOW_2CH_VARIABLE)
LOW_2CH_VARIABLE = LOW_2CH_VARIABLE + NUM_1
IF LOW_2CH_VARIABLE <= ASC_Z_U GOTO CHECK_NEW_2CH_VARIABLE
LOW_2CH_VARIABLE = ASC_A_U
HIGH_2CH_VARIABLE = HIGH_2CH_VARIABLE + NUM_1
IF HIGH_2CH_VARIABLE <= ASC_Z_U GOTO CHECK_NEW_2CH_VARIABLE
HIGH_2CH_VARIABLE = ASC_A_U : REM Ignore Possibility of 'ZZ' being exceeded FOR NOW (676 Variables)
CHECK_NEW_2CH_VARIABLE FOR ILLEGAL_2CH_VARIABLE_TABLE_COUNTER = ILLEGAL_2CH_VARIABLE_TABLE_START TO ILLEGAL_2CH_VARIABLE_TABLE_COUNT
IF NEW_2CH_VARIABLE$ = ILLEGAL_2CH_VARIABLE_TABLE$(ILLEGAL_2CH_VARIABLE_TABLE_COUNTER) GOTO GET_NEW_2CH_VARIABLE
NEXT ILLEGAL_2CH_VARIABLE_TABLE_COUNTER
RETURN
' This is checking if only allowable characters are present
' It does NOT check for valid number syntax
CHECK_IF_NUMBER IS_NUMBER = FALSE
NUMBER_LENGTH = LEN(VLSTRING$)
FOR NUMBER_COUNTER = NUM_1 TO NUMBER_LENGTH
NUMBER_CH$ = MID$(VLSTRING$, NUMBER_COUNTER, NUM_1)
NUMBER_CH = ASC(NUMBER_CH$)
IF NUMBER_CH >= ASC_0 AND NUMBER_CH <= ASC_9 GOTO MORE_NUMBERS
IF NUMBER_CH = ASC_PERIOD OR NUMBER_CH = ASC_PLUS OR NUMBER_CH = ASC_MINUS GOTO MORE_NUMBERS
IF NUMBER_CH = ASC_E_U OR NUMBER_CH = ASC_e_L GOTO MORE_NUMBERS
RETURN : REM Not a number
MORE_NUMBERS NEXT NUMBER_COUNTER
IS_NUMBER = TRUE
RETURN
' Prepare line number for output, no leading space, No trailing space
PREPARE_LINE_NUMBER LNR_$ = STR$(LINE_NUMBER)
' IF LEN(LNR_$) < 2 THEN PRINT #FILE_L, "LINE NUMBER LENGTH LESS THAN 2 >>" ; LEN(LNR_$)
LN_LENGTH = LEN(LNR_$) - NUM_1
LN_$ = MID$(LNR_$, NUM_2, LN_LENGTH)
RETURN
NEXT_LINE LINE INPUT #FILE_I, IN_LINE$
IN_LINE_CHAR_COUNTER = NUM_0
LAST_CH = ASC_TILDE
DEF_CONSTANT = FALSE
DATA_LINE = FALSE
VLSTRING_COUNT = NUM_0
' Let User Know we are still processing
IF PASS = NUM_2 THEN PRINT LINE_NUMBER ;
I_EOF = EOF(FILE_I)
IF I_EOF = NUM_M1 GOTO I_FILE_END
I_LINE_LENGTH = LEN(IN_LINE$)
IF I_LINE_LENGTH = NUM_0 GOTO BLANK_LINE
''' CHARACTERS IN COLUMN ONE
' "A-Za-z", "_", "0-9" = LINE LABLE
' "#" = Urbane COMMAND
' " " = NO LINE LABLE
' "'" = COMMENT
' any other character = COMMENT
COLUMN_ONE GOSUB GET_IN_LINE_CHAR
IF IN_LINE_CH = ASC_POUND GOTO URBANE_COMMAND
IF IN_LINE_CH = ASC_BLANK GOTO NO_LINE_LABLE
IF IN_LINE_CH = ASC_SINGLE_QUOTE GOTO COMMENT
IF IN_LINE_CH >= ASC_A_U AND IN_LINE_CH <= ASC_Z_U GOTO LINE_LABEL
IF IN_LINE_CH >= ASC_a_L AND IN_LINE_CH <= ASC_z_L GOTO LINE_LABEL
IF IN_LINE_CH >= ASC_0 AND IN_LINE_CH <= ASC_9 GOTO LINE_LABEL
IF IN_LINE_CH = ASC_UNDERSCORE GOTO LINE_LABEL
COMMENT ON PASS GOTO OUTPUT_LINE, PASS2_CONTINUE
URBANE_COMMAND IF INSTR(NUM_2, IN_LINE$, "LIST_ON") <> NUM_0 THEN OUTPUT_LISTING = TRUE
IF INSTR(NUM_2, IN_LINE$, "LIST_OFF") <> NUM_0 THEN OUTPUT_LISTING = FALSE
IF INSTR(NUM_2, IN_LINE$, "XREF_ON") <> NUM_0 THEN OUTPUT_XREF = TRUE
IF INSTR(NUM_2, IN_LINE$, "XREF_OFF") <> NUM_0 THEN OUTPUT_XREF = FALSE
IF INSTR(NUM_2, IN_LINE$, "CONSTANT") <> NUM_0 THEN DEF_CONSTANT = TRUE : GOTO NO_LINE_LABLE
' ON PASS GOTO OUTPUT_LINE, PASS2_CONTINUE
BLANK_LINE ON PASS GOTO OUTPUT_LINE, PASS2_CONTINUE
NO_LINE_LABLE ON PASS GOTO OUTPUT_LINE, PARSE_LINE
LINE_LABEL GOSUB GET_VLSTRING
VLSTRING_COUNT = NUM_1
IF LAST_CH <> ASC_DOLLAR GOTO LINE_LABEL_GOOD
PRINT #FILE_L, "WARNING - '$' SHOULD NOT BE USED IN LABELS " ; VLSTRING$
PRINT "WARNING - '$' SHOULD NOT BE USED IN LABELS " ; VLSTRING$
WARNINGS = WARNINGS + NUM_1
LINE_LABEL_GOOD IF PASS = NUM_2 GOTO PARSE_LINE
FOR LINE_LABEL_COUNTER = LINE_LABEL_START TO LINE_LABEL_COUNT
IF VLSTRING$ = LINE_LABELS$(LINE_LABEL_COUNTER,LINE_LABELS_NAME) GOTO DUP_LINE_LABEL
NEXT LINE_LABEL_COUNTER
LINE_LABEL_COUNT = LINE_LABEL_COUNT + NUM_1
IF LINE_LABEL_COUNT > LINE_LABEL_MAX_COUNT THEN PRINT #FILE_L, " LINE_LABEL_MAX_COUNT Exceeded" : STOP
LINE_LABELS$(LINE_LABEL_COUNT,LINE_LABELS_NAME) = VLSTRING$
GOSUB PREPARE_LINE_NUMBER
LINE_LABELS$(LINE_LABEL_COUNT,LINE_LABELS_NUMBER) = LN_$
IF OUTPUT_XREF = TRUE THEN PRINT #FILE_X, " "; UBN_XRF_LINE_LABEL; " "; LN_$; " "; VLSTRING$
GOTO OUTPUT_LINE
DUP_LINE_LABEL PRINT #FILE_L, LINE_NUMBER ; " ERROR - DUPLICATE LINE LABEL = " ; VLSTRING$
ERRORS = ERRORS + NUM_1
GOTO OUTPUT_LINE
PARSE_LINE GOSUB PREPARE_LINE_NUMBER
OUTPUT_STRING$ = LN_$
CONSTANT_NAME$ = ""
CONSTANT_INDEX = NUM_0
EQUALS_FOUND = FALSE
PARSE_CONTINUE IF IN_LINE_CH < NUM_0 GOTO OUTPUT_LINE
IF IN_LINE_CH >= ASC_A_U AND IN_LINE_CH <= ASC_Z_U GOTO LABEL_OR_VARIABLE
IF IN_LINE_CH >= ASC_a_L AND IN_LINE_CH <= ASC_z_L GOTO LABEL_OR_VARIABLE
IF IN_LINE_CH >= ASC_0 AND IN_LINE_CH <= ASC_9 GOTO LABEL_OR_VARIABLE
IF IN_LINE_CH = ASC_UNDERSCORE GOTO LABEL_OR_VARIABLE
IF IN_LINE_CH = ASC_DOUBLE_QUOTE GOTO HAVE_QUOTED_STRING
IF IN_LINE_CH = ASC_AMPERSAND GOTO HEX_OR_OCTAL
IF IN_LINE_CH = ASC_EQUALS THEN EQUALS_FOUND = TRUE
' Skip blanks
IF IN_LINE_CH = ASC_BLANK AND LAST_CH = ASC_BLANK GOTO PARSE_GET_NEXT_CH
OUTPUT_STRING$ = OUTPUT_STRING$ + IN_LINE_CH$
PARSE_GET_NEXT_CH GOSUB GET_IN_LINE_CHAR
GOTO PARSE_CONTINUE
LABEL_OR_VARIABLE VLSTRING_COUNT = VLSTRING_COUNT + NUM_1
GOSUB GET_VLSTRING
GOSUB CHECK_IF_NUMBER
IF IS_NUMBER = TRUE GOTO HAVE_NUMBER
FOR KEYWORD_TABLE_COUNTER = KEYWORD_TABLE_START TO KEYWORD_TABLE_COUNT
IF VLSTRING$ = KEYWORD_TABLE$(KEYWORD_TABLE_COUNTER) GOTO HAVE_KEYWORD
NEXT KEYWORD_TABLE_COUNTER
FOR LINE_LABEL_COUNTER = LINE_LABEL_START TO LINE_LABEL_COUNT
IF VLSTRING$ = LINE_LABELS$(LINE_LABEL_COUNTER,VARIABLE_NAMES_NAME) GOTO HAVE_LINE_LABEL
NEXT LINE_LABEL_COUNTER
FOR VARIABLE_NAMES_COUNTER = VARIABLE_NAMES_START TO VARIABLE_NAMES_COUNT
IF VLSTRING$ = VARIABLE_NAMES$(VARIABLE_NAMES_COUNTER,VARIABLE_NAMES_NAME) GOTO HAVE_VARIABLE
NEXT VARIABLE_NAMES_COUNTER
' NEW_VARIABLE FOUND
VARIABLE_NAMES_COUNT = VARIABLE_NAMES_COUNT + NUM_1
IF VARIABLE_NAMES_COUNT > VARIABLE_NAMES_MAX_COUNT THEN PRINT #FILE_L, " VARIABLE_NAMES_MAX_COUNT Exceeded" : STOP
VARIABLE_NAMES$(VARIABLE_NAMES_COUNT,VARIABLE_NAMES_NAME) = VLSTRING$
VARIABLE_NAMES$(VARIABLE_NAMES_COUNT,VARIABLE_NAMES_2CH) = "Pending"
IF DEF_CONSTANT <> TRUE GOTO GET_NEW_2CH
CONSTANT_NAME$ = VLSTRING$
CONSTANT_INDEX = VARIABLE_NAMES_COUNT
IF OUTPUT_XREF = TRUE THEN PRINT #FILE_X, " "; UBN_XRF_VARIABLE; " "; LN_$; " "; VLSTRING$
GOTO PARSE_CONTINUE
' Generate new 2 character variable name and put it into variable name table
GET_NEW_2CH GOSUB GET_NEW_2CH_VARIABLE
VARIABLE_NAMES$(VARIABLE_NAMES_COUNT,VARIABLE_NAMES_2CH) = NEW_2CH_VARIABLE$
VARIABLE_NAMES_COUNTER = VARIABLE_NAMES_COUNT
IF OUTPUT_XREF = TRUE THEN PRINT #FILE_X, " "; UBN_XRF_VARIABLE; " "; LN_$; " "; VLSTRING$
IF LAST_CH <> ASC_DOLLAR GOTO HAVE_VARIABLE
VARIABLE_NAMES$(VARIABLE_NAMES_COUNT,VARIABLE_NAMES_2CH) = NEW_2CH_VARIABLE$ + "$"
GOTO HAVE_VARIABLE
HAVE_KEYWORD IF OUTPUT_XREF = TRUE AND DATA_LINE = FALSE THEN PRINT #FILE_X, " "; UBN_XRF_KEYWORD_REFERENCE; " "; LN_$; " "; VLSTRING$
' Add keyword to output string
IF DATA_LINE = TRUE GOTO HAVE_DATA_LINE
IF VLSTRING$ = "REM" GOTO HAVE_REM
IF VLSTRING$ = "LET" GOTO HAVE_LET
IF VLSTRING$ = "DATA" THEN DATA_LINE = TRUE
IF VLSTRING$ = "CONSTANT" GOTO PARSE_CONTINUE
HAVE_DATA_LINE OUTPUT_STRING$ = OUTPUT_STRING$ + VLSTRING$
GOTO PARSE_CONTINUE
' Line with no Line Label and first Keyword "REM" can be removed
' DECB requires the "REM keyword if there is a label on the line
HAVE_REM IF VLSTRING_COUNT = NUM_1 GOTO PASS2_CONTINUE
OUTPUT_STRING$ = OUTPUT_STRING$ + "REM"
GOTO OUTPUT_LINE
' Remove unnecessary "LET" and next Blank
HAVE_LET IF IN_LINE_CH = ASC_BLANK GOTO PARSE_GET_NEXT_CH
GOTO PARSE_CONTINUE
HAVE_LINE_LABEL IF OUTPUT_XREF = TRUE THEN PRINT #FILE_X, " "; UBN_XRF_LINE_REFERENCE; " "; LN_$; " "; LINE_LABELS$(LINE_LABEL_COUNTER,LINE_LABELS_NUMBER)
' Add line number to output string
OUTPUT_STRING$ = OUTPUT_STRING$ + LINE_LABELS$(LINE_LABEL_COUNTER,LINE_LABELS_NUMBER)
GOTO PARSE_CONTINUE
HAVE_NUMBER IF DEF_CONSTANT <> TRUE GOTO ADD_NUMBER
IF CONSTANT_INDEX = NUM_0 GOTO ADD_NUMBER
IF EQUALS_FOUND <> TRUE GOTO ADD_NUMBER
NUM_PREFIX$ = ""
IF LAST_CH = ASC_MINUS THEN NUM_PREFIX$ = "-"
IF LAST_CH = ASC_PLUS THEN NUM_PREFIX$ = "+"
VARIABLE_NAMES$(CONSTANT_INDEX,VARIABLE_NAMES_2CH) = NUM_PREFIX$ + VLSTRING$
GOTO PARSE_CONTINUE
' Add number to output string
ADD_NUMBER OUTPUT_STRING$ = OUTPUT_STRING$ + VLSTRING$
GOTO PARSE_CONTINUE
HAVE_VARIABLE IF DEF_CONSTANT = TRUE GOTO PARSE_CONTINUE
IF OUTPUT_XREF = TRUE THEN PRINT #FILE_X, " "; UBN_XRF_VARIABLE_REFERENCE; " "; LN_$; " "; VLSTRING$
' Add existing 2ch variable to output string
OUTPUT_STRING$ = OUTPUT_STRING$ + VARIABLE_NAMES$(VARIABLE_NAMES_COUNTER,VARIABLE_NAMES_2CH)
GOTO PARSE_CONTINUE
HAVE_QUOTED_STRING GOSUB GET_QSTRING
IF DEF_CONSTANT <> TRUE GOTO ADD_QUOTED
IF CONSTANT_INDEX = NUM_0 GOTO ADD_QUOTED
IF EQUALS_FOUND <> TRUE GOTO ADD_QUOTED
VARIABLE_NAMES$(CONSTANT_INDEX,VARIABLE_NAMES_2CH) = QSTRING$
GOTO PARSE_CONTINUE
' Copy Quoted string to output, with double quotes
ADD_QUOTED OUTPUT_STRING$ = OUTPUT_STRING$ + QSTRING$
GOTO PARSE_CONTINUE
' On Entry, "ASC_AMPERSAND" is in "IN_LINE_CH$" and "IN_LINE_CH"
' On Exit, "IN_LINE_CH$" and "IN_LINE_CH" have next character.
HEX_OR_OCTAL HO_STRING$ = IN_LINE_CH$
GOSUB GET_IN_LINE_CHAR
HO_STRING$ = HO_STRING$ + IN_LINE_CH$
HEX = FALSE
OCTAL = FALSE
IF IN_LINE_CH = ASC_H_U OR IN_LINE_CH = ASC_h_L THEN HEX = TRUE
IF IN_LINE_CH = ASC_O_U OR IN_LINE_CH = ASC_o_L THEN OCTAL = TRUE
IF HEX = FALSE AND OCTAL = FALSE GOTO BAD_HEX_OR_OCTAL
MORE_HEX_OR_OCTAL GOSUB GET_IN_LINE_CHAR
IF IN_LINE_CH >= ASC_A_U AND IN_LINE_CH <= ASC_F_U GOTO HOSTRING_ADD_UPPER_HEX
IF IN_LINE_CH >= ASC_a_L AND IN_LINE_CH <= ASC_f_L GOTO HOSTRING_ADD_LOWER_HEX
IF IN_LINE_CH >= ASC_0 AND IN_LINE_CH <= ASC_7 GOTO HOSTRING_ADD_OCTAL
IF IN_LINE_CH >= ASC_8 AND IN_LINE_CH <= ASC_9 GOTO HOSTRING_ADD_UPPER_HEX
' End of Hex or Octal Number
OUTPUT_STRING$ = OUTPUT_STRING$ + HO_STRING$
GOTO PARSE_CONTINUE
HOSTRING_ADD_LOWER_HEX IN_LINE_CH = IN_LINE_CH - ( ASC_a_L - ASC_A_U )
IN_LINE_CH$ = CHR$(IN_LINE_CH)
HOSTRING_ADD_UPPER_HEX HO_STRING$ = HO_STRING$ + IN_LINE_CH$
IF HEX = TRUE GOTO MORE_HEX_OR_OCTAL
PRINT #FILE_L, "WARNING - Invalid Octal Number " ; HO_STRING$
PRINT "WARNING - Invalid Octal Number " ; HO_STRING$
WARNINGS = WARNINGS + NUM_1
GOTO OUTPUT_LINE
HOSTRING_ADD_OCTAL HO_STRING$ = HO_STRING$ + IN_LINE_CH$
GOTO MORE_HEX_OR_OCTAL
BAD_HEX_OR_OCTAL PRINT #FILE_L, "WARNING - '&' Not followed by 'H' or 'O' " ; HO_STRING$
PRINT "WARNING - '&' Not followed by 'H' or 'O' " ; HO_STRING$
WARNINGS = WARNINGS + NUM_1
' GOTO OUTPUT_LINE
OUTPUT_LINE IF PASS = NUM_1 AND OUTPUT_LISTING = TRUE THEN PRINT #FILE_L, LINE_NUMBER ; IN_LINE$
IF DEF_CONSTANT = TRUE GOTO PASS2_CONTINUE
IF PASS = NUM_2 THEN PRINT #FILE_O, OUTPUT_STRING$
PASS2_CONTINUE LINE_NUMBER = LINE_NUMBER + NUM_1
GOTO NEXT_LINE
I_FILE_END LINE_COUNT = LINE_NUMBER - NUM_1
PRINT #FILE_L, "Urbane Pass "; PASS; " Done, Lines = "; LINE_COUNT
PRINT "Urbane Pass "; PASS; " Done, Lines = "; LINE_COUNT
IF PASS = NUM_2 GOTO ALL_DONE
' GOSUB DUMP_LINE_LABELS
PRINT #FILE_L, " MEM = " ; MEM
PRINT " MEM = " ; MEM
LOAD_KEYWORDS DIM KEYWORD_TABLE$(UBN_MEM_KEYWORD_TABLE_MAX_COUNT)
KEYWORD_TABLE_MAX_COUNT = UBN_MEM_KEYWORD_TABLE_MAX_COUNT
KEYWORD_TABLE_START = NUM_1
KEYWORD_TABLE_COUNT = NUM_0
LOOP_KEYWORDS READ KEYWORD$
IF KEYWORD$ = "\" GOTO END_OF_KEYWORDS
KEYWORD_TABLE_COUNT = KEYWORD_TABLE_COUNT + NUM_1
KEYWORD_TABLE$(KEYWORD_TABLE_COUNT) = KEYWORD$
GOTO LOOP_KEYWORDS
END_OF_KEYWORDS DIM ILLEGAL_2CH_VARIABLE_TABLE$(UBN_MEM_ILLEGAL_2CH_VARIABLE_TABLE_MAX_COUNT)
ILLEGAL_2CH_VARIABLE_TABLE_MAX_COUNT = UBN_MEM_ILLEGAL_2CH_VARIABLE_TABLE_MAX_COUNT
ILLEGAL_2CH_VARIABLE_TABLE_START = NUM_1
ILLEGAL_2CH_VARIABLE_TABLE_COUNT = NUM_0
LOOP_ILLEGAL_2CH_VARIABLES READ ILLEGAL_2CH_VARIABLE$
IF ILLEGAL_2CH_VARIABLE$ = "\" GOTO END_OF_ILLEGAL_2CH_VARIABLES
ILLEGAL_2CH_VARIABLE_TABLE_COUNT = ILLEGAL_2CH_VARIABLE_TABLE_COUNT + NUM_1
ILLEGAL_2CH_VARIABLE_TABLE$(ILLEGAL_2CH_VARIABLE_TABLE_COUNT) = ILLEGAL_2CH_VARIABLE$
GOTO LOOP_ILLEGAL_2CH_VARIABLES
END_OF_ILLEGAL_2CH_VARIABLES DIM VARIABLE_NAMES$(UBN_MEM_VARIABLE_NAMES_MAX_COUNT,NUM_2)
VARIABLE_NAMES_MAX_COUNT = UBN_MEM_VARIABLE_NAMES_MAX_COUNT
VARIABLE_NAMES_START = NUM_1
VARIABLE_NAMES_COUNT = NUM_0
VARIABLE_NAMES_NAME = NUM_1
VARIABLE_NAMES_2CH = NUM_2
HIGH_2CH_VARIABLE = ASC_A_U
LOW_2CH_VARIABLE = ASC_A_U
PASS = NUM_2
LINE_NUMBER = NUM_1
I_EOF = NUM_0
PRINT #FILE_L, " ---------PASS 2 STARTING------"
PRINT " ---------PASS 2 STARTING------"
CLOSE FILE_I
OPEN "I", #NUM_1, IN_FILE$
GOTO NEXT_LINE
' REM Startup code here so Basic does not have to search it each time it is looking for a line number
UBN_START FILES UBN_MEM_FILES_COUNT, UBN_MEM_FILES_SPACE
PCLEAR UBN_MEM_PCLEAR
CLEAR UBN_MEM_CLEAR
PRINT "Urbane Starting - Urbane XUBN~VERSION$ "
PRINT " MEM = " ; MEM
PASS = NUM_1
LINE_NUMBER = NUM_1
ERRORS = NUM_0
WARNINGS = NUM_0
ASC_DOUBLE_QUOTE$ = CHR$(34)
OUTPUT_LISTING = UBN_LIST_LIST_INITIAL
OUTPUT_XREF = UBN_XREF_LIST_INITIAL
FILE_I = NUM_1
FILE_O = NUM_2
FILE_L = NUM_3
FILE_X = NUM_4
OPEN "I", #NUM_1, IN_FILE$
OPEN "O", #NUM_2, OUT_FILE$
OPEN "O", #NUM_3, LST_FILE$
OPEN "O", #NUM_4, XRF_FILE$
DIM LINE_LABELS$(UBN_MEM_LINE_LABEL_MAX_COUNT,NUM_2)
LINE_LABEL_MAX_COUNT = UBN_MEM_LINE_LABEL_MAX_COUNT
LINE_LABEL_START = NUM_1
LINE_LABEL_COUNT = NUM_0
LINE_LABELS_NAME = NUM_1
LINE_LABELS_NUMBER = NUM_2
PRINT #FILE_L, " "; UBN_TITLE$
PRINT #FILE_X, " "; UBN_XRF_TITLE; " "; NUM_0; " "; UBN_TITLE$
PRINT #FILE_L, " MEM = " ; MEM
GOTO NEXT_LINE
' Keyword Table
DATA REM
DATA ATTR,AUDIO,BACKUP,CIRCLE,CLEAR,CLOAD,CLOADM,CLOSE,CLS,COLOR,CONSTANT,CONT,COPY,CSAVE
DATA CSAVEM,DATA,DEF,DEFUSR,DEL,DIM,DIR,DLOAD,DRAW,DRIVE,DSKI$,DSKINI,DSKO$,EDIT
DATA END,EXEC,FIELD,FILES,FN,FOR,GET,GOSUB
DATA GOTO,HBUFF,HCIRCLE,HCLS,HCOLOR,HDRAW,HGET,HLINE,HPAINT,HPRINT,HPUT,HRESET
DATA HSCREEN,HSET,HSTAT,IF,INPUT,KILL,LET,LINE,LIST,LLIST,LOCATE,LPOKE,LOAD,LOADM,LSET,MERGE
DATA MOTOR,NEW,NEXT,OLD,ON,OPEN,PAINT,PALETTE,PCLEAR,PCLS,PCOPY,PLAY,PMODE,POKE,PRESET,PRINT
DATA PSET,PUT,READ,RENAME,RENUM,RESET,RSET,RESTORE,RESUME,RETURN,RUN,SAVE,SAVEM,SCREEN
DATA SET,SKIPF,SOUND,STOP,TO,TROFF,TRON,WIDTH,WRITE,UNLOAD,VERIFY
DATA MEM,TIMER,AND,NOT,OR,ELSE,STEP,FREE,AS,BRK,ERR,CMP,RGB,USING
DATA ERROR,FREE,OFF,TAB,USING,THEN,TO,G,BF,B,R,A,"?"
DATA AS,BF,OR,PI
' Functions, keywords that require next character to be an open parentheses
DATA ABS,ASC,ATN,BUTTON,CHR$,COS,CVN,EOF,ERLIN,ERNO,EXP,FIX,HEX$,HPOINT,INKEY$,INSTR,INT
DATA JOYSTK,LEFT$,LEN,LOC,LOF,LOG,LPEEK,MID$,MKN$,PEEK,POINT,POS,PPOINT,RIGHT$,RND,SGN,SIN
DATA STRING$,STR$,SQR,TAN,USR,VAL,VARPTR
DATA "\"
' Illegal Two Character Variable Table NOTE: "PI" is a variable that is NOT replaced
DATA AS,BF,FN,IF,ON,OR,PI,TO,"\"
ALL_DONE PRINT #FILE_L, " MEM = " ; MEM
PRINT " MEM = " ; MEM
' GOSUB DUMP_VARIABLES
PRINT "Urbane Done, Errors = " ; ERRORS ; " Warnings = "; WARNINGS ; " Lines = " ; LINE_COUNT ; "Line Label Count = " ; LINE_LABEL_COUNT ; " Variables Count = " ; VARIABLE_NAMES_COUNT
PRINT #FILE_L, "Urbane Done, Errors = " ; ERRORS ; " Warnings = "; WARNINGS ; " Lines = " ; LINE_COUNT ; "Line Label Count = " ; LINE_LABEL_COUNT ; " Variables Count = " ; VARIABLE_NAMES_COUNT
CLOSE FILE_I
CLOSE FILE_O
CLOSE FILE_L
CLOSE FILE_X
END