Dave,
had a lazy Sunday afternoon and was thinking about your problem - And, being a bit challenged and had nothing else to do, wrote a parser/analyzer for your "Language": Ended up with quite a bit of code, the problem is not as simple as it seems (Hope I didn't over-complicate things). But I wanted to have a generic parser that could easily be extended and also probably be used for other purposes. It seems to understand most of the commands you mentioned and could be a starting point for you. Note it's currently only accepting uppercase commands. If you don't use Turbo and its toolkit, just remove the MANIFEST statemens at the beginning of lines 110 to 130, they declare the variables in the lines "constant" - I just like to use symbolic names for the states and commands instead of values, it's just easier to read.
Now go ahead and tear it apart.....

Cheers,
Tobias

100 DIM command$(100)
110 MANIFEST : S_REJECT_LINE = -1 : S_START = 1 : S_HAVE_FNO = 2 : S_DONE = 3 : S_GET_VALUE = 4
120 REMark some constants for the commands
130 MANIFEST : C_INVALID% = -1 : C_CHANGE% = 1 : C_SPEED% = 2 : C_TAKEOFF% = 3 : C_LAND% = 4
140 MANIFEST : C_CHANGEALT% = 5 : C_CHANGEHEAD% = 6
150 REPeat InpLoop
160   INPUT #1,"Please enter a command Line:",command$
170   valid = parseLine(command$)
180   IF (valid) THEN
190     PRINT "Flight #";fNo$;", please ";
200     SELect ON command%
210       = C_SPEED%
220         PRINT "Change speed to "; value
230       = C_TAKEOFF%
240         PRINT "Take off"
250       = C_LAND%
260         PRINT "Land"
270       = C_CHANGEALT%
280         PRINT "Change altitude to "; value
290       = C_CHANGEHEAD%
300         PRINT "Change heading to "; value
310     END SELect
320   ELSE
330     PRINT "Invalid line"
340   END IF
350 END REPeat InpLoop
360 :
370 DEFine PROCedure InitParser (l$)
380   linePos = 1
390   lineToParse$ = l$
400 END DEFine
410 :
430 :
440 REMark ************************************************************************ 450 REMark * this procedure parses an entered command line into the parts needed
460 REMark * returns 1 in case of success. The variables are set as follows
470 REMark *   fno$             holds the flight number
480 REMark *   command%         holds the command to execute (see line 130)
490 REMark *   value            holds numeric value of command
500 REMark ************************************************************************
510 DEFine FuNction parseLine(command$)
520   InitParser command$
530   REMark Set status to "Start parsing line"
540   state = S_START
550 :
560   REMark enter the loop that walks through the state machine
570   REPeat parseLoop
580     token$ = getNextToken$
590     REMark did we hit the end of line?
600     IF token$ = CHR$(10) THEN
610 REMark check whether we're done or would have needed more input to be valid
620       REMark anything except S_DONE at the end of input is a wrong line
630       SELect ON state
640         = S_REJECT_LINE
650           PRINT "Line "&cmd$& " invalid at line end"
660           RETurn 0
670         = S_START
680           PRINT "Empty line entered"
690           RETurn 0
700         = S_HAVE_FNO
710           PRINT "Something after flight number is missing"
720           RETurn 0
730         = S_DONE
740           REMark valid end of command line
750           RETurn 1
760         = REMAINDER
770           PRINT "Invalid state"
780           RETurn 0
790       END SELect
800     ELSE
810       REMark Got a valid token, no line end
820 :
830       SELect ON state
840         = S_REJECT_LINE
850 PRINT "Line rejected: Line "& cmd$ & "invalid at "& token$ & " State is:"&state
860           RETurn 0
870         = S_START
880           fNo$ = getFlightNo$ (token$)
890           IF fNo$ <> "INVALID" THEN
900             state = S_HAVE_FNO
910           ELSE
920             PRINT "Invalid Flight number"
930             state = S_REJECT_LINE
940           END IF
950         = S_HAVE_FNO
960           command% = getCommand% (token$)
970           SELect ON command%
980             = C_INVALID%
990               state = S_REJECT_LINE
1000             = C_CHANGE%
1010 REMark special handling of numerical input according to # of digits
1020               token$ = peekNextToken$
1030               tl% = LEN(token$)
1040               SELect ON tl%
1050                 = 2
1060                   command% = C_CHANGEALT%
1070                 = 3
1080                  command% = C_CHANGEHEAD%
1090                = REMAINDER
1100                  state = S_REJECT_LINE
1110                  PRINT "Invalid number of digits after C command"
1120               END SELect
1130               state = S_GET_VALUE
1140             = C_SPEED%
1150               state = S_GET_SPEED
1160             = C_TAKEOFF%
1170               state = S_DONE
1180             = C_LAND%
1190               state = S_DONE
1200            END SELect
1210         = S_GET_VALUE
1220           value = getNumericValue (token$)
1230           state = S_DONE
1240         = S_NEW_ALT
1250           value = getNumericValue (token$)
1260           IF value <> -1 THEN
1270             state = S_DONE
1280           ELSE
1290             state = S_REJECT_LINE
1300             PRINT "Invalid numeric value "; token$; " encountered"
1310           END IF
1320         = REMAINDER
1330             PRINT "Invalid state:"&state
1340       END SELect
1350     END IF
1360   END REPeat parseLoop
1370 END DEFine parseLine
1380 :
1390 DEFine FuNction getCommand% (token$)
1400   LOCal cret%
1410   cret% = C_INVALID%
1420   IF token$(1) = "C"
1430     cret% = C_CHANGE%
1440   END IF
1450   IF token$(1) = "S"
1460     cret% = C_SPEED%
1470   END IF
1480   IF token$(1) = "T"
1490     cret% = C_TAKEOFF%
1500   END IF
1510   IF token$(1) = "L"
1520     cret% = C_LAND%
1530   END IF
1540   RETurn cret%
1550 END DEFine
1560 :
1570 DEFine FuNction getFlightNo$ (token$)
1580 REMark this should check for a valid flight number, but just returns the string it got for now
1590   RETurn token$
1600 END DEFine
1610 :
1620 DEFine FuNction getNumericValue (token$)
1630 LOCal newValue
1640   newValue = -1 : REMark this is the "invalid" marker
1650 REMark add some checks here that the conversion to numeric is actually a valid number
1660   newValue = token$
1670   RETurn newValue
1680 END DEFine
1690 :
1700 REMark this fn returns the next token from the input line, separated by any number of "white space'
1710 REMark characters. The token is removed from the line
1720 DEFine FuNction getNextToken$
1730   LOCal tmp$(100)
1740   tmp$ = peekNextToken$
1750   IF tmp$ <> CHR$(10)
1760     REMark remove the token from the input line
1770     lineToParse$ = lineToParse$ (linePos TO)
1780   END IF
1790   RETurn tmp$
1800 END DEFine
1810 :
1820 REMark this line returns the next token from the line, without removing it. Used to "peek forward"
1830 REMark Needed when the syntax is not LR(1)
1840 DEFine FuNction peekNextToken$
1850   LOCal startPos, endPos, llen, token$(100), inChar
1860   startPos = 1 : endPos = 1 : llen = LEN(lineToParse$)
1870   IF llen = 0 THEN
1880     RETurn CHR$(10)
1890   END IF
1900   REMark skip any white space before
1910   REPeat s1lp
1920     inChar = CODE (lineToParse$(startPos))
1930     SELect ON inChar
1940       = 32
1950         REMark space
1960         startPos = startPos + 1
1970       = 9
1980         REMark tab
1990         startPos = startPos + 1
2000       = 33 TO 128
2010         REMark ASCII
2020         EXIT s1lp
2030       = REMAINDER
2040         PRINT "Invalid input character "&inChar
2050     END SELect
2060     IF startPos > LEN (lineToParse$) THEN
2070       RETurn CHR$(10)
2080     END IF
2090   END REPeat s1lp
2100 REMark now we have startPos at the first character of the token. Advance endPos to the end of it
2110   endPos = startPos + 1
2120   REPeat s2lp
2130     IF (endPos > LEN(lineToParse$)) THEN
2140       EXIT s2lp
2150     END IF
2160     inChar = CODE (lineToParse$(endPos))
2170     SELect ON inChar
2180       = 32,9
2190         EXIT s2lp
2200       = 33 TO 128
2210         endPos = endPos + 1
2220       = REMAINDER
2230         PRINT "Invalid character in input line"
2240     END SELect
2250   END REPeat s2lp
2260   REMark remember where we were
2270   linePos = endPos
2280   REMark and deliver the found token
2290   token$ = lineToParse$(startPos TO endPos)
2300   RETurn token$
2310 END DEFine peekNextToken$

_______________________________________________
QL-Users Mailing List
http://www.q-v-d.demon.co.uk/smsqe.htm

Reply via email to