-- BasicScanner.mesa -- edited by Brotz and Hilton, September 23, 1982 5:14 PM DIRECTORY Ascii, BasicDefs, BasicImpDefs, Real, RealOps, Storage, String; BasicScanner: PROGRAM IMPORTS BasicImpDefs, Real, RealOps, Storage, String EXPORTS BasicImpDefs = BEGIN OPEN BasicDefs, BasicImpDefs; charTableArray: CharTable; charTablePtr: PUBLIC POINTER TO CharTable _ @charTableArray; inputLine: PUBLIC STRING _ [200]; inputLineIndex: PUBLIC CARDINAL _ 0; token: PUBLIC STRING _ [40]; haveToken: PUBLIC BOOLEAN _ FALSE; endOfLine: PUBLIC BOOLEAN _ TRUE; optionBase: PUBLIC CARDINAL _ 0; optionBaseCalled: PUBLIC BOOLEAN _ FALSE; -- optionBaseCalled will be set to TRUE when the first DIM statement is called -- or when OPTION BASE is called realPosOrNeg: PUBLIC PositiveOrNegative _ positive; -- Sets the sign for real numbers. Is set to negative in Primary and reset -- to positive in GetRealNumber. inputToken: STRING _ [40]; userInputLine: PUBLIC STRING _ [200]; userInputLineIndex: PUBLIC CARDINAL _ 0; noMoreInput: PUBLIC BOOLEAN _ FALSE; statementSeparatorChar: PUBLIC CHARACTER _ ':; AppendTailOfRealNumber: PROCEDURE [realString: STRING] = -- Ends having gotten the token following the tail of a real number. BEGIN IF GetToken[] AND token[0] IN ['0 .. '9] THEN BEGIN String.AppendString[realString, token]; IF GetToken[] AND String.LowerCase[token[0]] = 'e THEN BEGIN String.AppendChar[realString, 'E]; IF GetToken[] AND token[0] = '+ OR token[0] = '- THEN BEGIN String.AppendString[realString, token]; IF GetToken[] AND token[0] IN ['0 .. '9] THEN {String.AppendString[realString, token]; [] _ GetToken[]} ELSE ParseError["Missing exponential factor in real number representation."L]; END ELSE ParseError["Missing +/- in real number representation."L]; END; END ELSE ParseError["Illegal real number representation after decimal point."L]; END; -- of GetTailOfRealNumber -- CheckForEqualSign: PUBLIC PROCEDURE = BEGIN IF GetToken[] AND token[0] = '= THEN RETURN ELSE ParseError["Missing = within statement."L]; END; -- of CheckForEqualSign -- CheckForVariable: PUBLIC PROCEDURE = BEGIN IF GetToken[] AND charTableArray[token[0]] = letter AND ~IsReservedWord[token] THEN RETURN ELSE ParseError["Missing variable within statement."L]; END; -- of CheckForVariable -- FindInfixReservedWord: PUBLIC PROCEDURE [word: STRING] RETURNS [wordStart: CARDINAL] = -- Starting at inputLineIndex, finds the index in inputLine, "wordStart", at which the -- reserved word "word" begins. Use this procedure to find where an infix reserved word -- such as THEN, ELSE, TO, STEP, etc. begins so as to fake the GetToken procedure into -- terminating tokens before such a word. BEGIN wordIndex: CARDINAL _ 0; i: CARDINAL; IF word.length = 0 THEN ERROR; wordStart _ 0; FOR i _ inputLineIndex, i + 1 UNTIL i >= inputLine.length DO SELECT inputLine[i] FROM '" => BEGIN i _ i + 1; UNTIL i >= inputLine.length DO IF inputLine[i] = '" THEN {IF i + 1 = inputLine.length OR inputLine[i + 1] # '" THEN EXIT ELSE i _ i + 1}; i _ i + 1; ENDLOOP; wordIndex _ 0; END; Ascii.SP, Ascii.TAB => LOOP; statementSeparatorChar => RETURN[0]; String.UpperCase[word[wordIndex]], String.LowerCase[word[wordIndex]] => BEGIN IF wordIndex = 0 THEN wordStart _ i; wordIndex _ wordIndex + 1; IF wordIndex = word.length THEN RETURN; END; ENDCASE => IF wordIndex > 0 THEN {i _ wordStart; wordIndex _ 0}; ENDLOOP; END; -- of FindInfixReservedWord -- GetInputValue: PUBLIC PROCEDURE [isString: BOOLEAN] RETURNS [value: BasicValue] = BEGIN start, end, i: CARDINAL; string: STRING; hasDot: BOOLEAN _ FALSE; FOR start _ inputLineIndex, start + 1 UNTIL start >= inputLine.length DO SELECT inputLine[start] FROM Ascii.SP, Ascii.TAB => NULL; ENDCASE => EXIT; ENDLOOP; inputLineIndex _ start; IF start >= inputLine.length THEN BEGIN IF isString THEN value _ BasicValue[string, string[stringValue: Storage.String[0]]] ELSE value _ BasicValue[integer, integer[integerValue: 0]]; RETURN; END; IF inputLine[start] = '" THEN BEGIN IF ~isString THEN RunTimeError["Quoted string not allowed for this input!"L]; start _ end _ start + 1; UNTIL end >= inputLine.length DO IF inputLine[end] = '" THEN BEGIN end _ end + 1; IF end = inputLine.length OR inputLine[end] # '" THEN {inputLineIndex _ end; end _ end - 1; EXIT}; END; end _ end + 1; REPEAT FINISHED => RunTimeError["Input string missing right quote!"L]; ENDLOOP; END ELSE BEGIN FOR end _ start, end + 1 UNTIL end >= inputLine.length DO SELECT inputLine[end] FROM Ascii.SP, Ascii.TAB => EXIT; ENDCASE; ENDLOOP; inputLineIndex _ end; END; string _ Storage.String[end - start]; FOR i _ start, i + 1 UNTIL i >= end DO String.AppendChar[string, inputLine[i]]; SELECT inputLine[i] FROM '" => i _ i + 1; '. => hasDot _ TRUE; ENDCASE; ENDLOOP; SELECT TRUE FROM isString => {value _ BasicValue[string, string[stringValue: string]]; RETURN}; hasDot => value _ BasicValue[real, real[realValue: Real.StringToReal[string]]]; ENDCASE => value _ BasicValue[integer, integer [integerValue: String.StringToLongNumber[string, 10]]]; Storage.FreeString[string]; END; -- of GetInputValue -- GetRealNumber: PUBLIC PROCEDURE [integerPart: STRING] RETURNS [realNum: REAL] = -- Ends having gotten the next token after the complete real number. BEGIN realString: STRING _ [40]; IF realPosOrNeg = negative THEN {String.AppendChar[realString, '-]; realPosOrNeg _ positive}; IF integerPart[0] ~IN ['0 .. '9] THEN ParseError["Illegal integer part in real number."L]; String.AppendString[realString, integerPart]; String.AppendChar[realString, '.]; AppendTailOfRealNumber[realString]; realNum _ Real.StringToReal[realString]; END; -- of GetRealNumber -- GetSubstring: PUBLIC PROCEDURE [varPtr: VariablePtr] RETURNS [value: BasicValue] = -- This procedure has not been updated -- BEGIN -- startIndex: CARDINAL _ 0; -- endIndex: CARDINAL; -- [startIndex, endIndex] _ GetSubstringIndex[varPtr]; -- IF token[0] = '] THEN value _ ExtractSubstring[varPtr, startIndex, endIndex]; END; -- of GetSubstring -- GetSubstringIndex: PUBLIC PROCEDURE [varPtr: VariablePtr] RETURNS [startIndex, endIndex: CARDINAL] = BEGIN -- This procedure has not been updated -- -- Assumes that the left bracket has already been GetToken'ed. subscript: BasicValue; IF GetToken[] THEN BEGIN -- subscript _ Expression[]; SELECT subscript.type FROM integer => startIndex _ NumericToDecimal[subscript.integerValue]; real => startIndex _ RealOps.RoundC[subscript.realValue]; ENDCASE => BEGIN RunTimeError["Substring subscripts must have a numeric value."L]; Storage.FreeString[subscript.stringValue]; RETURN; END; IF token[0] = ', THEN BEGIN -- There are two subscripts. Get the second one. IF GetToken[] THEN BEGIN -- subscript _ Expression[]; SELECT subscript.type FROM integer => startIndex _ NumericToDecimal[subscript.integerValue]; real => startIndex _ RealOps.RoundC[subscript.realValue]; ENDCASE => BEGIN RunTimeError["Substring subscripts must have a numeric value."L]; Storage.FreeString[subscript.stringValue]; RETURN; END; END ELSE ParseError["Missing substring index."L]; END ELSE endIndex _ WITH v: varPtr SELECT FROM string => v.value.stringValue.length ENDCASE => ERROR; IF ~haveToken OR token[0] # '] THEN ParseError["Missing right bracket."L]; END ELSE ParseError["Missing array index."L]; END; -- of GetSubstringIndex -- GetToken: PUBLIC PROCEDURE RETURNS [b: BOOLEAN] = BEGIN firstCharIsDigit: BOOLEAN; AppendCharAndBump: PROCEDURE = BEGIN String.AppendChar[token, inputLine[inputLineIndex]]; inputLineIndex _ inputLineIndex + 1; END; -- of AppendCharAndBump -- token.length _ 0; endOfLine _ FALSE; haveToken _ TRUE; DO IF inputLineIndex = inputLine.length THEN {endOfLine _ TRUE; RETURN[(haveToken _ (token.length # 0))]}; SELECT charTableArray[inputLine[inputLineIndex]] FROM letter => BEGIN IF token.length = 0 THEN firstCharIsDigit _ FALSE; IF firstCharIsDigit = TRUE THEN RETURN[TRUE]; AppendCharAndBump[]; IF IsReservedWord[token] THEN RETURN[TRUE]; END; digit => {IF token.length = 0 THEN firstCharIsDigit _ TRUE; AppendCharAndBump[]}; statementSeparator => BEGIN IF token.length = 0 THEN {AppendCharAndBump[]; RETURN[(haveToken _ FALSE)]}; RETURN[TRUE]; END; punctuation => {IF token.length = 0 THEN AppendCharAndBump[]; RETURN[TRUE]}; illegal => BEGIN ParseError["Illegal characer found while parsing!"L]; endOfLine _ TRUE; RETURN[(haveToken _ FALSE)]; END; white => inputLineIndex _ inputLineIndex + 1; ENDCASE; ENDLOOP; END; -- of GetToken -- InitCharTable: PUBLIC PROCEDURE = BEGIN ch: CHARACTER; i: CARDINAL; whiteString: STRING = " "L; punctuationString: STRING = "!#%~&*()-`=+\|[{]}_^;:'"",<.>/?"L; FOR ch IN [0C..177C] DO charTableArray[ch] _ illegal; ENDLOOP; FOR ch IN ['0..'9] DO charTableArray[ch] _ digit; ENDLOOP; FOR ch IN ['A..'Z] DO charTableArray[ch] _ letter; ENDLOOP; FOR ch IN ['a..'z] DO charTableArray[ch] _ letter; ENDLOOP; charTableArray['$] _ letter; FOR i IN [0..whiteString.length) DO charTableArray[whiteString[i]] _ white; ENDLOOP; FOR i IN [0..punctuationString.length) DO charTableArray[punctuationString[i]] _ punctuation; ENDLOOP; charTableArray[statementSeparatorChar] _ statementSeparator; END; -- of InitCharTable -- NumericToDecimal: PUBLIC PROCEDURE [number: Numeric] RETURNS [decimal: CARDINAL] = BEGIN decimalString: STRING _ [20]; decimalString.length _ 0; String.AppendLongDecimal[decimalString, number]; decimal _ String.StringToDecimal[decimalString]; END; -- of NumericToDecimal -- ParseSubscripts: PUBLIC PROCEDURE RETURNS [nIndices: CARDINAL] = -- Parses one or two subscripts and returns how many it found. BEGIN -- Assumes that the left parenthesis or bracket has already been GetToken'ed. isBracket: BOOLEAN = (token[0] = '[); nIndices _ 0; IF GetToken[] THEN BEGIN Expression[]; nIndices _ 1; IF token[0] = ', THEN BEGIN -- There are two subscripts. Get the second one. IF GetToken[] THEN {Expression[]; nIndices _ 2} ELSE ParseError["Missing subscript!"L]; END; IF ~haveToken OR (isBracket AND token[0] # ']) OR (~isBracket AND token[0] # ')) THEN ParseError [IF isBracket THEN "Missing right bracket!"L ELSE "Missing right parenthesis!"L]; END ELSE ParseError["Missing subscript!"L]; END; -- of ParseSubscripts -- ScanQuotedString: PUBLIC PROCEDURE RETURNS [quoteString: STRING] = -- Allocates and returns a string containing the contents of inputLine from inputLineIndex -- up to a terminating quote. Two double quote marks in a row are reduced to a single -- double quote mark in the returned string. BEGIN startIndex: CARDINAL _ inputLineIndex; i: CARDINAL; UNTIL inputLineIndex = inputLine.length DO IF inputLine[inputLineIndex] = '" THEN BEGIN inputLineIndex _ inputLineIndex + 1; IF inputLineIndex = inputLine.length OR inputLine[inputLineIndex] # '" THEN EXIT; -- inputLine[inputLineIndex - 2] is the last good char in quoted string. END; inputLineIndex _ inputLineIndex + 1; REPEAT FINISHED => BEGIN -- inputLineIndex became equal to inputLine.length before we saw the terminating quote. ParseError["The string expression is missing a matching "" ."L]; RETURN[Storage.String[0]]; END; ENDLOOP; quoteString _ Storage.String[inputLineIndex - startIndex - 1]; FOR i _ startIndex, i + 1 UNTIL i = inputLineIndex - 1 DO String.AppendChar[quoteString, inputLine[i]]; IF inputLine[i] = '" THEN i _ i + 1; ENDLOOP; END; -- of ScanQuotedString -- END. -- of BasicScanner -- (1792)\f1