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