Intelligent Input Routines
R. M. Smythe
Burlington, Canada
This technique is described for the Apple, PET/CBM, and Atari computers. It could prove quite useful for educational, user-friendly databases, and other programming.
First there was the INPUT statement. You used it and put up with error messages. "The user shouldn't have typed a comma anyhow," you said. "Served him right." Then all the magazines came up with their Input Anything Subroutines using the GET statement. You've been using that and found that your programs are remarkably well protected against bad input. You made the input routine ignore inappropriate keystrokes. Now it's time for the next advance: Intelligent Input Subroutines (IIS). Rather than simply ignore faulty data input, the IIS attempts to figure out what the user meant and correct what was typed. Often users won't even know they mistyped an entry!
IIS: PET Examples
There are numerous uses for such a routine. Here is a simple one. The PET's "programmer's keyboard" differs from the standard keyboard in that the top row characters (from exclamation point to brackets) are obtained without shifting. On a typewriter you must shift for these characters. If the touch typist using your program shifts to hit the brackets, a weird graphics symbol appears, which will cause an error or, if caught at the time, have to be deleted.
A sophisticated input subroutine could monitor keystrokes, detect and intercept the shifted bracket, and replace it with the intended symbol before it appears on the screen.
Or suppose you have transferred to the lowercase alternate character set. You are inputting people's names. The IIS can capitalize the first character, and every character after a space, whether the user shifted or not.
But the real power of an IIS is revealed when it's used for specialized formatting of input. To illustrate, I will show examples that apply to education, that could be used to improve programs which have appeared in COMPUTE!.
Have you seen copper (II) sulfate printed by a computer this way: CUS04 ? Or students are told to input a quadratic as X2 + 2X-3 ; or, worse, as X"2 + 2*x-3. Programs displaying chemical formulae without lower case and subscripts, and mathematical expressions that do not display exponents are not examples of good user-oriented programs. Just by typing these characters: X,2 +,2,X,-, and 3 a student should see on the screen x2 + 2x-3, and the program should understand what was input. Anything else is pandering to the convenience of the computer (and the programmer) in the worst way.
Let's see how to devise an intelligent subroutine to accept algebraic expressions. The first step is to have an Input Anything Routine as your foundation. In brief, such a subroutine can use a GET statement to detect keystrokes. Each character is checked against allowed characters. Undesirable characters (e.g. cursor control characters, graphics symbols, etc.) are rejected, and the user sees nothing happen on the screen in response to such a keypress.
Suitable characters are added to the growing collection string (RESP$ in the accompanying examples) and printed to the screen. In the event the key pressed was DELETE, appropriate action must be taken. The routine keeps looping back to the GET statement until a CHR$(13) (RETURN) arrives, whereupon the subroutine returns to the main program. (This whole routine replaces INPUT RESP$ (and any accompanying error checking), but the final product is worth the extra coding!)
We will modify the Input Anything Subroutine in PET Program 1 to accept and display mathematical expressions. The first step is to plan what we want.
- Allow lower and upper case variables.
- Raise exponents to the line above.
- Accept numerals, letters, +, -, (and = if equations are anticipated.)
- Accept and process DELETE and RETURN.
- Reject everything else.
Since both coefficients and exponents are numbers, the computer must find some way to tell the difference. It is obvious that exponents always follow variables, which are letters (we won't allow exponents on the coefficients) so the IIS watches for numbers following letters.
One way it could do this is to set a flag (VAR = 1) when a letter is hit. If the flag is set when a number is typed, an exponent is intended. A +, -, or RETURN could reset the flag (VAR = 0). This is very flexible, but complicated, especially when you realize that you must provide for "unsetting" the flag if the user deletes a just-entered variable to correct an error. A routine which uses flags to advantage appears later.
In this case, if we restrict exponents to one digit, we can use a simpler method. When a number comes in from the keyboard, we need only look at the last character in RESP$. If it is a letter, we raise the exponent. This is done by printing a cursor-up, the number, then a cursor-down to return us to the input line.
The routine is in statements 1000-1290 of PET Program 2. It is contained in a little program to illustrate its use. Note how alternate correct answers can be provided. Unless an Intelligent Printing Subroutine (which can be part of the Intelligent Input Subroutine) is used, you must also read in a form of the answer containing cursor controls for screen display. One thing you must do when you code for the pressing of the DELETE key is to provide for erasure of the character on the line above the cursor as well as just to the left on the same line: the character to be removed might be an exponent.
In PET Program 3 we use an IIS for chemical formulae. Here we put any numeral on the line below. You can make the routine more flexible. For example, to allow for hydrates, which require non-subscripted coefficients, the routine could check for a dot (period) immediately preceding the arrival of a number, and act accordingly.
PET Program 4 shows an IIS for going the other way – formula to name. Here the use of flags is demonstrated. Normally we use lowercase, but, after brackets are opened, the routine promotes everything to uppercase whether the user shifts or not. The bracket flag (BFLAG%) is reset when brackets are closed. Note also how the routine makes allowances if the trained typist, by habit, shifts to hit brackets.
Not only will the use of Intelligent Input Subroutines make your programs more user-proof, but well designed subroutines can also give them a professional look. Your programs should always adapt to the user, not force them to conform to the computer.
Notes regarding the listings:
- Spaces were inserted for clarity. If they are omitted, the statements will fit into 80 character lines, except for a couple of statements with extra-long REMs which you will have to truncate.
- The letters "REM" were used only where necessary. Note the places where you can make REM-less comments (save space!).
Apple Routines
Here are some designs for Apple routines.
There have been some excellent "input anything" subroutines for Apple published. The problem, of course, was to get around the EXTRA IGNORED response and accompanying loss of part of the input when a comma or colon was included. One of the shortest I've seen is by Ben Colley (NIBBLE, volume 2, number 1, page 59).
Program 1 (Apple Version) shows the BASIC Input Anything Subroutine in action. The subroutine starts at line 100, and is amplified by many REMarks. Every character entered is collected in R1$. A count is made of characters, R1. Usually, of course, R1 will equal the length of R1$., but, if there is a deletion, instead of lopping off the last character of R1$, we decrease R1. By doing this we can regain previously typed in characters, as we must if we are allowing for the use of the right-arrow key. We just increase R1 again. There is a problem with providing for deleting, though. Since control characters do not show on the screen, if you have typed a control character in your string, and do some deletions, then what is on the screen might not be what is in the final version of R1$, which becomes RESP$ upon exiting from the subroutine. This is actually an Input almost Anything Subroutine, because line 155 rejects control characters. However, you can do it one better by using an IIS that actually prints the control characters, if there is a need for them, in inverse video.
Sometimes it is useful to allow the user to exit from a routine without finishing the input: for example, the user might make an incorrect choice at a menu, wind up in an undesired routine, and want out. Line 120 accomplishes this by using the ESCape key. Alternatively, line 220, which rejects a RETURN if there has been no input, can be the early exit. Change it to IF R1 = 0 THEN RETURN and check for R1 equalling zero in line 1010.
Program 2 is an IIS designed around a simpler Input Anything Subroutine. This time, when a character is deleted using the left-arrow key, it is erased on the screen. Thus there is no need to save characters for use of the right-arrow key. We can fill RESP$ directly, without the use of the intermediary R1$ and character counter R1.
This IIS accepts and displays chemical formulae. Called by the main program at line 10010, it begins at line 1000. The first job is to look for a RETURN, in line 1050. Letters are processed in lines 1050 and 1060. In lines 1070 to 1090, numbers are placed in the line below, as subscripts should be. First the vertical cursor position is found (CV%), and we VTAB to the line below it. (Mathematicians: just change the + to a minus in the VTAB expression to put your exponents above the line. You will have to check that the previous character input (the last one in RESP$) was a letter so you'll be sure that the number was not a coefficient.) Next, the routine checks to see if brackets had been input. Acting under the assumption that the user would never need to type an eight or nine in a chemical formula, these are promoted to the bracket corresponding to SHIFT-8 or SHIFT-9. Deletion is accomplished by shortening RESP$ in line 1160, after being careful to erase the previous character on the same line and below. (Remember, you could be erasing a subscript.) At this point the routine would check for and process any other desired input characters. However, if we wish to reject all other characters, we just go straight to line 1500.
Program 2 is actually a little routine to test out the subroutine (as is Program 1). It contains one other small subroutine of interest. If this were part of a program where the computer, as well as the user, were required to print out a formula, you would need a formatting routine. This one, appearing in lines 2000 to 2030, is quite simple. Send the formula to the subroutine as B$, and the formula is displayed with subscripts in their proper places. Again, just a minor adjustment is needed to turn this into a mathematical expression formatting subroutine. (See the example and discussion above on IIS's for PET: the logic is the same. Only the cursor control procedures are different.)
There is no doubt that Intelligent Input Subroutines are more difficult to devise and require more coding than simple INPUT statements. Their use, however, should lead to programs which are more user-oriented, a direction that we programmers should be heading as computer usage becomes more widespread.
Program 1: Atari Version
Here's an "input anything" subroutine for Atari users. It will accept upper or lowercase, punctuation, and numbers. It will not accept any editing or cursor controls except BACK S (backspace). Line 1080 zeroes out the system's inverse video flag to cancel reverse field, and line 1120 traps bad characters. Programmer's note: Line 1060 shows one way to safely DIMension an array from within a subroutine without worrying about an error message. If INIT is zero, the array should be dimensioned, and INIT is set to one, preventing the array from being redimensioned unless INIT is reset with CLR. This prevents you from having to use a TRAP statement here.
10 PRINT "TYPE IN A LINE AND HIT RETURN : " GOSUB 1000 20 PRINT "YOU TYPED : " :? I$ 30 END 1000 REM "Input (Almost) Anything" 1010 REM Subroutine for Atari 1020 REM Traps CTRL characters 1030 REM reverse field, all cursor 1040 REM controls except BACK S. 1050 REM Length of strine limited by LN 1060 IF INIT = 0 THEN DIM I$(100) : LN = 100 : 0 PEN #1, 4, 0, "K" : INIT = 1 1070 LNS = 1 1080 GET #1, A : POKE 694, 0 : REM Kill RUS 1090 IF A = 155 THEN ? : RETURN 1100 IF A = 126 AND LNS>1 THEN LNS = LNS - 1 : I$(LNS) = " " : PRINT CHR$(A); 1110 IF LNS>LN THEN 1080 1120 IF A<32 OR A = 96 OR A>122 THEN 1080 1130 I$(LNS, LNS) = CHR$(A) : ? CHR$(A); : LNS = LNS + 1 1140 GOTO 1080
Program 2: Atari Version
This program will let the user enter a chemical formula as described in the article, with numbers subscripted after capital letters. This routine only accepts uppercase letters, and changes a typed "9" or "0" to "(" and ")", respectively. There is an "intelligent printing routine" in the main program. Note that BACK S deletes the character behind and under the cursor, requiring two lines of space on the screen.