MODULEAn old version of ScanNumber that does not depend on Lex.; IMPORT Wr, TextRefTbl, Text, TextConv, Rd, SynWr, SynLocation, Fmt, Lex, FloatMode, TextRd, Thread, SynScan; REVEAL Keyword = BRANDED OBJECT name: TEXT; keyword: BOOLEAN END; KeywordSet = BRANDED OBJECT table: TextRefTbl.T END; TYPE Symbol = Keyword; TokenClass = {IdeCase, InfixCase, CharCase, IntCase, RealCase, StringCase, DelimCase, EofCase }; InputList = REF InputListBase; InputListBase = RECORD rd: Rd.T; fileName: TEXT; acceptedCharPos, acceptedLinePos, acceptedLineCharPos: INTEGER; acceptedTokenBegPos, acceptedTokenEndPos: INTEGER; generateEOF: BOOLEAN; closeReader: BOOLEAN; rest: InputList; END; CONST EofChar = VAL(255, CHAR); REVEAL T = BRANDED REF RECORD swr: SynWr.T; scanBuffer: REF ARRAY OF CHAR; scanBufferSize: INTEGER; charTable: ARRAY CHAR OF CharacterClass; keySet: KeywordSet; lookAheadReady: BOOLEAN; lookAheadChar: CHAR; tokenReady: BOOLEAN; tokenClass: TokenClass; tokenBegPos: INTEGER; tokenEndPos: INTEGER; tokenChar: CHAR; tokenInt: INTEGER; tokenReal: LONGREAL; tokenString: TEXT; tokenDelim: CHAR; tokenSym: Symbol; input: InputList; scanPoint: INTEGER; firstPrompt, nextPrompt: TEXT; isFirstPrompt: BOOLEAN; (* The following variables are copies of "input^" fields, so that they can survive a PopInput. *) fileName: TEXT; acceptedCharPos, acceptedLinePos, acceptedLineCharPos: INTEGER; acceptedTokenBegPos, acceptedTokenEndPos: INTEGER; END; PROCEDURE SynScan NewKeywordSet (): KeywordSet = BEGIN RETURN NEW(KeywordSet, table:=NEW(TextRefTbl.Default).init(10)); END NewKeywordSet; PROCEDURECopyKeywordSet (keywordSet: KeywordSet): KeywordSet = VAR newKeySet: KeywordSet; key: TEXT; value: REFANY; iterator: TextRefTbl.Iterator; symbol: Symbol; BEGIN newKeySet := NEW(KeywordSet, table:=NEW(TextRefTbl.Default).init(keywordSet.table.size())); iterator := keywordSet.table.iterate(); WHILE iterator.next((*out*)key, (*out*)value) DO symbol := NARROW(value, Symbol); IF symbol.keyword THEN EVAL newKeySet.table.put(key, symbol); END; END; RETURN newKeySet; END CopyKeywordSet; PROCEDUREGetKeywordSet (sc: T): KeywordSet = BEGIN RETURN sc^.keySet; END GetKeywordSet; PROCEDUREUseKeywordSet (sc: T; keywordSet: KeywordSet) = BEGIN sc^.keySet := keywordSet; END UseKeywordSet; PROCEDUREBeKeyword (ide: TEXT; keywordSet: KeywordSet): Keyword = VAR value: REFANY; symbol: Symbol; BEGIN IF keywordSet.table.get(ide, (*VAR OUT*) value) THEN symbol := NARROW(value, Symbol); symbol.keyword := TRUE; ELSE symbol := NEW(Symbol, name:=Text.Sub(ide,0,Text.Length(ide)), keyword:=TRUE); EVAL keywordSet.table.put(ide, symbol); END; RETURN symbol; END BeKeyword; PROCEDUREGetKeyword (ide: TEXT; keywordSet: KeywordSet): Keyword = VAR value: REFANY; symbol: Symbol; BEGIN IF keywordSet.table.get(ide, (*VAR OUT*) value) THEN symbol := NARROW(value, Symbol); IF symbol.keyword THEN RETURN symbol ELSE RETURN NIL END; ELSE RETURN NIL; END; END GetKeyword; PROCEDUREIsDelimiter (sc: T; char: CHAR): BOOLEAN = BEGIN RETURN sc^.charTable[char] = CharacterClass.DelimCharCase; END IsDelimiter; PROCEDUREIsIdentifier (sc: T; string: TEXT): BOOLEAN = VAR class: CharacterClass; length: INTEGER; BEGIN length := Text.Length(string); IF length=0 THEN RETURN FALSE END; IF sc^.charTable[Text.GetChar(string,0)]=CharacterClass.LetterCharCase THEN FOR i:=0 TO length-1 DO class := sc^.charTable[Text.GetChar(string,i)]; IF (class # CharacterClass.LetterCharCase) AND (class # CharacterClass.DigitCharCase) THEN RETURN FALSE END; END; RETURN TRUE; ELSIF sc^.charTable[Text.GetChar(string,0)]=CharacterClass.SpecialCharCase THEN FOR i:=0 TO length-1 DO class := sc^.charTable[Text.GetChar(string,i)]; IF (class # CharacterClass.SpecialCharCase) THEN RETURN FALSE END; END; RETURN TRUE; ELSE RETURN FALSE; END; END IsIdentifier; PROCEDUREScanPoint (sc: T): INTEGER = BEGIN RETURN sc.scanPoint; END ScanPoint; PROCEDUREGetInputState (sc: T) = BEGIN WITH z = sc^ DO IF z.input = NIL THEN z.fileName := "<no input>"; z.acceptedCharPos := 0; z.acceptedLinePos := 0; z.acceptedLineCharPos := 0; z.acceptedTokenBegPos := 0; z.acceptedTokenEndPos := 0; ELSE z.fileName := z.input^.fileName; z.acceptedCharPos := z.input^.acceptedCharPos; z.acceptedLinePos := z.input^.acceptedLinePos; z.acceptedLineCharPos := z.input^.acceptedLineCharPos; z.acceptedTokenBegPos := z.input^.acceptedTokenBegPos; z.acceptedTokenEndPos := z.input^.acceptedTokenEndPos; END; END; END GetInputState; PROCEDURESetInputState (sc: T) = BEGIN WITH z = sc^ DO IF z.input # NIL THEN z.input^.acceptedCharPos := z.acceptedCharPos; z.input^.acceptedLinePos := z.acceptedLinePos; z.input^.acceptedLineCharPos := z.acceptedLineCharPos; z.input^.acceptedTokenBegPos := z.acceptedTokenBegPos; z.input^.acceptedTokenEndPos := z.acceptedTokenEndPos; END; END; END SetInputState; PROCEDURENewInput (fileName: TEXT; rd: Rd.T; rest: InputList; closeReader: BOOLEAN; generateEOF: BOOLEAN): InputList = VAR res: InputList; BEGIN res := NEW(InputList); res^.fileName := fileName; res^.rd := rd; res^.acceptedCharPos := 0; res^.acceptedLinePos := 1; res^.acceptedLineCharPos := 0; res^.acceptedTokenBegPos := 0; res^.acceptedTokenEndPos := 0; res^.generateEOF := generateEOF; res^.closeReader := closeReader; res^.rest := rest; RETURN res; END NewInput; PROCEDUREPushInput (sc: T; fileName: TEXT; rd: Rd.T; closeReader: BOOLEAN; generateEOF: BOOLEAN := TRUE) = BEGIN SetInputState(sc); sc^.input := NewInput(fileName, rd, sc^.input, closeReader, generateEOF); GetInputState(sc); END PushInput; PROCEDUREPopInput (sc: T) RAISES {NoReader} = BEGIN IF sc^.input = NIL THEN RAISE NoReader ELSE TRY TRY IF sc^.input^.closeReader THEN Rd.Close(sc^.input^.rd) END; FINALLY sc^.input := sc^.input^.rest; GetInputState(sc); END; EXCEPT Rd.Failure, Thread.Alerted => END; END; END PopInput; PROCEDURECurrentLocationInfo (sc: T; VAR(*out*) info: SynLocation.Info) = BEGIN info.fileName:=sc^.fileName; info.char := sc^.acceptedCharPos; info.line := sc^.acceptedLinePos; info.lineChar := sc^.acceptedLineCharPos; END CurrentLocationInfo; PROCEDURESetCharNo (sc: T; charNo, lineNo, lineCharNo: INTEGER) = BEGIN WITH z = sc^ DO z.acceptedCharPos := charNo; z.acceptedLinePos := lineNo; z.acceptedLineCharPos := lineCharNo; z.acceptedTokenBegPos := charNo; z.acceptedTokenEndPos := charNo; END; END SetCharNo; PROCEDUREPrintPrompt (sc: T) = BEGIN WITH z = sc^, pwr = SynWr.UnderlyingWr(sc.swr) DO IF z.isFirstPrompt THEN TRY Wr.PutText(pwr, z.firstPrompt); Wr.Flush(pwr); INC(z.acceptedCharPos, Text.Length(z.firstPrompt)); INC(z.acceptedLineCharPos, Text.Length(z.firstPrompt)); EXCEPT Wr.Failure, Thread.Alerted => END; z.isFirstPrompt := FALSE; ELSE TRY Wr.PutText(pwr, z.nextPrompt); Wr.Flush(pwr); INC(z.acceptedCharPos, Text.Length(z.nextPrompt)); INC(z.acceptedLineCharPos, Text.Length(z.nextPrompt)); EXCEPT Wr.Failure, Thread.Alerted => END; END; END; END PrintPrompt; PROCEDURELookChar (sc: T): CHAR RAISES {NoReader} = VAR char: CHAR; BEGIN WITH z = sc^ DO IF z.lookAheadReady THEN RETURN z.lookAheadChar END; IF z.input=NIL THEN RAISE NoReader END; TRY IF Rd.CharsReady(z.input^.rd) = 0 THEN PrintPrompt(sc); END; EXCEPT Rd.Failure => RAISE NoReader END; TRY char := Rd.GetChar(z.input^.rd); z.lookAheadChar := char; z.lookAheadReady := TRUE; RETURN char; EXCEPT | Rd.EndOfFile => IF z.input^.generateEOF THEN PopInput(sc); z.lookAheadChar := EofChar; z.lookAheadReady := TRUE; RETURN EofChar; ELSE PopInput(sc); RETURN LookChar(sc); END; | Rd.Failure, Thread.Alerted => RAISE NoReader; END; END; END LookChar; PROCEDUREGetChar (sc: T): CHAR RAISES {NoReader} = BEGIN WITH z = sc^ DO IF NOT z.lookAheadReady THEN EVAL LookChar(sc) END; z.lookAheadReady := FALSE; INC(z.acceptedCharPos); INC(z.acceptedLineCharPos); IF z.lookAheadChar='\n' THEN INC(z.acceptedLinePos); z.acceptedLineCharPos := 0; END; RETURN z.lookAheadChar; END; END GetChar; PROCEDUREHaveChar (sc: T; char: CHAR): BOOLEAN RAISES {NoReader} = BEGIN IF char = LookChar(sc) THEN char := GetChar(sc); RETURN TRUE ELSE RETURN FALSE END; END HaveChar; PROCEDUREScanNumber (sc: T): TokenClass RAISES {NoReader, Fail} = VAR integer: BOOLEAN; char: CHAR; buf: REF ARRAY OF CHAR; BEGIN WITH z = sc^ DO IF HaveChar(sc, '~') THEN z.scanBuffer[z.scanBufferSize]:= '-'; INC(z.scanBufferSize); END; integer := TRUE; LOOP char := LookChar(sc); IF (z.charTable[char] # CharacterClass.DigitCharCase) AND (char # '.') AND (char # 'e') AND (char # '~') THEN EXIT END; char := GetChar(sc); IF (char = '.') OR (char = 'e') THEN integer := FALSE END; IF (NOT integer) AND (char = '~') THEN char := '-' END; z.scanBuffer[z.scanBufferSize]:= char; INC(z.scanBufferSize); IF z.scanBufferSize = NUMBER(z.scanBuffer^) THEN buf := NEW(REF ARRAY OF CHAR, 2*z.scanBufferSize); SUBARRAY(buf^, 0, z.scanBufferSize) := z.scanBuffer^; z.scanBuffer := buf; END; END; TRY IF integer THEN z.tokenInt := Lex.Int( TextRd.New( Text.FromChars(SUBARRAY(z.scanBuffer^, 0, z.scanBufferSize)))); z.scanBufferSize := 0; RETURN TokenClass.IntCase ELSE z.tokenReal := Lex.LongReal( TextRd.New( Text.FromChars(SUBARRAY(z.scanBuffer^, 0, z.scanBufferSize)))); z.scanBufferSize := 0; RETURN TokenClass.RealCase; END; EXCEPT Lex.Error, FloatMode.Trap, Rd.Failure, Thread.Alerted => SyntaxMsg(sc, "Illegal numerical constant", ""); RAISE Fail; END; END; END ScanNumber; PROCEDUREDecodeCharFromProducer ( sc: T; VAR (*out*)charOut: CHAR) RAISES {TextConv.Fail} = CONST Octal01 = TextConv.CharSet{'0', '1'}; VAR charsIn: ARRAY[0..3] OF CHAR; availIn: INTEGER; BEGIN TRY charsIn[0] := GetChar(sc); availIn := 1; IF charsIn[0] = TextConv.Escape THEN charsIn[1] := GetChar(sc); INC(availIn); IF charsIn[1] IN Octal01 THEN charsIn[2] := GetChar(sc); charsIn[3] := GetChar(sc); INC(availIn, 2); END; END; EXCEPT ELSE (* p failure *) RAISE TextConv.Fail; END; EVAL TextConv.DecodeChar(charsIn, availIn, (*out*)charOut); END DecodeCharFromProducer; PROCEDUREDecodeChar (sc: T): CHAR RAISES {Fail} = VAR char: CHAR; BEGIN TRY DecodeCharFromProducer(sc, (*out*)char); EXCEPT | TextConv.Fail => SyntaxMsg(sc, "ill-formed character escape sequence", ""); RAISE Fail; END; RETURN char; END DecodeChar; PROCEDUREScanChar (sc: T): CHAR RAISES {NoReader, Fail} = VAR char: CHAR; BEGIN char := GetChar(sc); char := DecodeChar(sc); IF GetChar(sc) # '\'' THEN SyntaxMsg(sc, "closing \' expected", ""); RAISE Fail; END; RETURN char; END ScanChar; PROCEDUREScanString (sc: T): TEXT RAISES {NoReader, Fail} = VAR char: CHAR; string: TEXT; buf: REF ARRAY OF CHAR; BEGIN WITH z = sc^ DO char := GetChar(sc); WHILE LookChar(sc) # '\"' DO char := DecodeChar(sc); z.scanBuffer[z.scanBufferSize]:= char; INC(z.scanBufferSize); IF z.scanBufferSize = NUMBER(z.scanBuffer^) THEN buf := NEW(REF ARRAY OF CHAR, 2*z.scanBufferSize); SUBARRAY(buf^, 0, z.scanBufferSize) := z.scanBuffer^; z.scanBuffer := buf; END; END; char := GetChar(sc); string := Text.FromChars(SUBARRAY(z.scanBuffer^, 0, z.scanBufferSize)); z.scanBufferSize := 0; RETURN string; END; END ScanString; PROCEDUREScanAlphaNumIde (sc: T) RAISES {NoReader} = VAR class: CharacterClass; BEGIN WITH z = sc^ DO LOOP class := z.charTable[LookChar(sc)]; IF (class # CharacterClass.LetterCharCase) AND (class # CharacterClass.DigitCharCase) THEN EXIT END; z.scanBuffer[z.scanBufferSize]:=GetChar(sc); INC(z.scanBufferSize); END; END; END ScanAlphaNumIde; PROCEDUREScanSpecialIde (sc: T) RAISES {NoReader} = BEGIN WITH z = sc^ DO WHILE z.charTable[LookChar(sc)] = CharacterClass.SpecialCharCase DO z.scanBuffer[z.scanBufferSize]:=GetChar(sc); INC(z.scanBufferSize); END; END; END ScanSpecialIde; PROCEDUREScanComment (sc: T) RAISES {NoReader, Fail} = VAR level: CARDINAL; char: CHAR; BEGIN level := 1; WHILE 0 < level DO char := GetChar(sc); IF char=EofChar THEN SyntaxMsg(sc, "Open comment at end of file", ""); RAISE Fail; END; IF char = '*' THEN IF LookChar(sc) = ')' THEN char := GetChar(sc); level := level - 1 END; ELSIF char = '(' THEN IF LookChar(sc) = '*' THEN char := GetChar(sc); level := level + 1 END; END; END END ScanComment; PROCEDURENextToken (sc: T): TokenClass RAISES {NoReader, Fail} = VAR char: CHAR; class: CharacterClass; tokenClass1: TokenClass; BEGIN WITH z = sc^ DO WHILE z.charTable[LookChar(sc)] = CharacterClass.BlankCharCase DO char := GetChar(sc); END; z.tokenBegPos := z.acceptedCharPos; char := LookChar(sc); class := z.charTable[char]; IF class = CharacterClass.LetterCharCase THEN ScanAlphaNumIde(sc); tokenClass1 := TokenClass.IdeCase; ELSIF class = CharacterClass.DelimCharCase THEN z.tokenDelim := GetChar(sc); IF (char = '(') AND (LookChar(sc) = '*') THEN char := GetChar(sc); ScanComment(sc); tokenClass1 := NextToken(sc); ELSE tokenClass1 := TokenClass.DelimCase; END; ELSIF class = CharacterClass.SpecialCharCase THEN ScanSpecialIde(sc); tokenClass1 := TokenClass.InfixCase; ELSIF (char = '~') OR (class = CharacterClass.DigitCharCase) THEN tokenClass1 := ScanNumber(sc); ELSIF char = '\'' THEN z.tokenChar := ScanChar(sc); tokenClass1 := TokenClass.CharCase; ELSIF char = '\"' THEN z.tokenString := ScanString(sc); tokenClass1 := TokenClass.StringCase; ELSIF class = CharacterClass.EofCase THEN char := GetChar(sc); tokenClass1 := TokenClass.EofCase; ELSE char := GetChar(sc); SyntaxMsg(sc, "Illegal Char", ""); RAISE Fail; END; z.tokenEndPos := z.acceptedCharPos; RETURN tokenClass1; END; END NextToken; PROCEDURELookToken (sc: T): TokenClass RAISES {NoReader, Fail} = BEGIN WITH z = sc^ DO IF z.tokenReady THEN RETURN z.tokenClass END; z.tokenClass := NextToken(sc); z.tokenReady := TRUE; RETURN z.tokenClass; END; END LookToken; PROCEDUREGetToken (sc: T): TokenClass RAISES {NoReader, Fail} = VAR class: TokenClass; BEGIN WITH z = sc^ DO IF z.tokenReady THEN z.tokenReady := FALSE; class := z.tokenClass; ELSE class := NextToken(sc); END; INC(z.scanPoint); z.acceptedTokenBegPos := z.tokenBegPos; z.acceptedTokenEndPos := z.tokenEndPos; RETURN class; END; END GetToken; PROCEDUREPrintContext (sc: T) = VAR string: TEXT; BEGIN WITH z = sc^ DO SynWr.Text(sc.swr, " just before: ", loud:=TRUE); CASE z.tokenClass OF | TokenClass.IdeCase, TokenClass.InfixCase => string := Text.FromChars(SUBARRAY(z.scanBuffer^, 0, z.scanBufferSize)); z.scanBufferSize := 0; SynWr.Text(sc.swr, string, loud:=TRUE); | TokenClass.CharCase => SynWr.Char(sc.swr, z.tokenChar, loud:=TRUE); | TokenClass.IntCase => SynWr.Text(sc.swr, Fmt.Int(z.tokenInt), loud:=TRUE); | TokenClass.RealCase => SynWr.Text(sc.swr, Fmt.LongReal(z.tokenReal), loud:=TRUE); | TokenClass.StringCase => SynWr.Char(sc.swr, '\"', loud:=TRUE); SynWr.Text(sc.swr, z.tokenString, loud:=TRUE); SynWr.Char(sc.swr, '\"', loud:=TRUE); | TokenClass.DelimCase => SynWr.Char(sc.swr, z.tokenDelim, loud:=TRUE); | TokenClass.EofCase => SynWr.Text(sc.swr, "<EndOfInput>", loud:=TRUE); END; SynWr.Flush(sc.swr, loud:=TRUE); END; END PrintContext; PROCEDUREPrintSequel (sc: T) = VAR n: INTEGER; ch: CHAR; BEGIN IF sc^.input = NIL THEN RETURN END; n := 40; TRY WHILE (0 < n) AND (Rd.CharsReady(sc^.input^.rd) > 0) DO ch:=GetChar(sc); (* NOWARN *) IF sc^.input = NIL THEN RETURN END; IF (Rd.CharsReady(sc^.input^.rd)>0) OR (ch#'\n') THEN SynWr.Char(sc.swr, ch, loud:=TRUE); END; n := n - 1; END; IF Rd.CharsReady(sc^.input^.rd) > 0 THEN SynWr.Text(sc.swr, " ...", loud:=TRUE); END; EXCEPT Rd.Failure, SynScan.NoReader => END; END PrintSequel; PROCEDUREFlushInput (sc: T) = BEGIN IF sc^.input = NIL THEN RETURN END; TRY WHILE Rd.CharsReady(sc^.input^.rd) > 0 DO EVAL GetChar(sc); (* NOWARN *) IF sc^.input = NIL THEN RETURN END; END; EXCEPT Rd.Failure, SynScan.NoReader => END; END FlushInput; PROCEDUREErrorMsg (sc: T; msg: TEXT := "") = BEGIN IF NOT Text.Empty(msg) THEN SynWr.Text(sc.swr, msg, loud:=TRUE); SynWr.Char(sc.swr, '\n', loud:=TRUE); SynWr.Flush(sc.swr, loud:=TRUE); END; Reset(sc); END ErrorMsg; PROCEDURESyntaxMsg (sc: T; cause: TEXT := ""; culprit: TEXT := ""; errorReportStyle: ErrorReportStyle := ErrorReportStyle.LinePlusChar) = VAR info: SynLocation.Info; BEGIN CurrentLocationInfo(sc, (*out*)info); SynWr.Text(sc.swr, "Syntax error just before: ", loud:=TRUE); PrintSequel(sc); IF (NOT Text.Empty(cause)) OR (NOT Text.Empty(culprit)) THEN SynWr.Char(sc.swr, '\n', loud:=TRUE); SynWr.Text(sc.swr, " ", loud:=TRUE); SynWr.Text(sc.swr, cause, loud:=TRUE); SynWr.Char(sc.swr, ' ', loud:=TRUE); SynWr.Text(sc.swr, culprit, loud:=TRUE); END; SynWr.Char(sc.swr, '\n', loud:=TRUE); SynWr.Text(sc.swr, "Error detected ", loud:=TRUE); CASE errorReportStyle OF | ErrorReportStyle.LinePlusChar => IF Text.Empty(info.fileName) THEN SynLocation.PrintLineDifference(sc.swr, SynLocation.NewLineLocation(info), info.line); ELSE SynLocation.PrintLocation(sc.swr, SynLocation.NewLineLocation(info)); END; | ErrorReportStyle.CharRange => SynLocation.PrintLocation(sc.swr, SynLocation.NewCharLocation(info, info)); END; SynWr.Char(sc.swr, '\n', loud:=TRUE); SynWr.Flush(sc.swr, loud:=TRUE); Reset(sc); END SyntaxMsg; PROCEDUREGetTokenChar (sc: T; VAR (*out*) char: CHAR): BOOLEAN RAISES {NoReader, Fail} = BEGIN IF LookToken(sc) = TokenClass.CharCase THEN EVAL(GetToken(sc)); char := sc^.tokenChar; RETURN TRUE; ELSE RETURN FALSE; END; END GetTokenChar; PROCEDUREGetTokenNat (sc: T; VAR (*out*) nat: CARDINAL): BOOLEAN RAISES {NoReader, Fail} = BEGIN IF LookToken(sc) = TokenClass.IntCase THEN IF sc^.tokenInt >= 0 THEN EVAL(GetToken(sc)); nat := sc^.tokenInt; RETURN TRUE; ELSE RETURN FALSE; END; ELSE RETURN FALSE; END; END GetTokenNat; PROCEDUREGetTokenInt (sc: T; VAR (*out*) int: INTEGER): BOOLEAN RAISES {NoReader, Fail} = BEGIN IF LookToken(sc) = TokenClass.IntCase THEN EVAL(GetToken(sc)); int := sc^.tokenInt; RETURN TRUE; ELSE RETURN FALSE; END; END GetTokenInt; PROCEDUREGetTokenReal (sc: T; VAR (*ou*) real: LONGREAL): BOOLEAN RAISES {NoReader, Fail} = BEGIN IF LookToken(sc) = TokenClass.RealCase THEN EVAL(GetToken(sc)); real := sc^.tokenReal; RETURN TRUE; ELSE RETURN FALSE; END; END GetTokenReal; PROCEDUREGetTokenString (sc: T; VAR (*out*) string: TEXT): BOOLEAN RAISES {NoReader, Fail} = BEGIN IF LookToken(sc) = TokenClass.StringCase THEN EVAL(GetToken(sc)); string := sc^.tokenString; RETURN TRUE; ELSE RETURN FALSE; END; END GetTokenString; PROCEDUREGetTokenIde (sc: T; VAR (*ou*) ide: TEXT): BOOLEAN RAISES {NoReader, Fail} = VAR class: TokenClass; name: TEXT; value: REFANY; BEGIN WITH z = sc^ DO class := LookToken(sc); IF (class = TokenClass.IdeCase) OR (class = TokenClass.InfixCase) THEN IF z.scanBufferSize # 0 THEN name := Text.FromChars(SUBARRAY(z.scanBuffer^, 0, z.scanBufferSize)); IF z.keySet.table.get(name, (*VAR OUT*) value) THEN z.tokenSym := NARROW(value, Symbol); ELSE z.tokenSym := NEW(Symbol, name:=name, keyword:=FALSE); EVAL z.keySet.table.put(name, z.tokenSym); END; z.scanBufferSize := 0; END; IF z.tokenSym.keyword THEN RETURN FALSE END; EVAL(GetToken(sc)); ide := z.tokenSym.name; RETURN TRUE; ELSE RETURN FALSE; END; END; END GetTokenIde; PROCEDUREGetTokenName (sc: T; VAR (*ou*) text: TEXT): BOOLEAN RAISES {NoReader, Fail} = VAR class: TokenClass; name: TEXT; value: REFANY; BEGIN WITH z = sc^ DO class := LookToken(sc); IF (class = TokenClass.IdeCase) OR (class = TokenClass.InfixCase) THEN IF z.scanBufferSize # 0 THEN name := Text.FromChars(SUBARRAY(z.scanBuffer^, 0, z.scanBufferSize)); IF z.keySet.table.get(name, (*VAR OUT*) value) THEN z.tokenSym := NARROW(value, Symbol); ELSE z.tokenSym := NEW(Symbol, name:=name, keyword:=FALSE); EVAL z.keySet.table.put(name, z.tokenSym); END; z.scanBufferSize := 0; END; EVAL(GetToken(sc)); text := z.tokenSym.name; RETURN TRUE; ELSE RETURN FALSE; END; END; END GetTokenName; PROCEDUREGetTokenEof (sc: T): BOOLEAN RAISES {NoReader, Fail} = VAR class : TokenClass ; BEGIN class := LookToken(sc); IF class = TokenClass.EofCase THEN EVAL GetToken(sc); RETURN TRUE; ELSE RETURN FALSE ; END; END GetTokenEof; PROCEDUREHaveTokenIde (sc: T; ide: TEXT): BOOLEAN RAISES {NoReader, Fail} = VAR class: TokenClass; name: TEXT; value: REFANY; BEGIN WITH z = sc^ DO class := LookToken(sc); IF (class = TokenClass.IdeCase) OR (class = TokenClass.InfixCase) THEN IF z.scanBufferSize # 0 THEN name := Text.FromChars(SUBARRAY(z.scanBuffer^, 0, z.scanBufferSize)); IF z.keySet.table.get(name, (*VAR OUT*) value) THEN z.tokenSym := NARROW(value, Symbol); ELSE z.tokenSym := NEW(Symbol, name:=name, keyword:=FALSE); EVAL z.keySet.table.put(name, z.tokenSym); END; z.scanBufferSize := 0; END; IF z.tokenSym.keyword THEN RETURN FALSE END; IF NOT Text.Equal(ide, z.tokenSym.name) THEN RETURN FALSE END; EVAL(GetToken(sc)); RETURN TRUE; ELSE RETURN FALSE; END; END; END HaveTokenIde; PROCEDUREHaveTokenName (sc: T; text: TEXT): BOOLEAN RAISES {NoReader, Fail} = VAR class: TokenClass; name: TEXT; value: REFANY; BEGIN WITH z = sc^ DO class := LookToken(sc); IF (class = TokenClass.IdeCase) OR (class = TokenClass.InfixCase) THEN IF z.scanBufferSize # 0 THEN name := Text.FromChars(SUBARRAY(z.scanBuffer^, 0, z.scanBufferSize)); IF z.keySet.table.get(name, (*VAR OUT*) value) THEN z.tokenSym := NARROW(value, Symbol); ELSE z.tokenSym := NEW(Symbol, name:=name, keyword:=FALSE); EVAL z.keySet.table.put(name, z.tokenSym); END; z.scanBufferSize := 0; END; IF NOT Text.Equal(text, z.tokenSym.name) THEN RETURN FALSE END; EVAL(GetToken(sc)); RETURN TRUE; ELSE RETURN FALSE; END; END; END HaveTokenName; PROCEDUREHaveTokenKey (sc: T; key: TEXT): BOOLEAN RAISES {NoReader, Fail} = VAR class: TokenClass; name: TEXT; value: REFANY; BEGIN WITH z = sc^ DO class := LookToken(sc); IF (class = TokenClass.IdeCase) OR (class = TokenClass.InfixCase) THEN IF z.scanBufferSize # 0 THEN name := Text.FromChars(SUBARRAY(z.scanBuffer^, 0, z.scanBufferSize)); IF z.keySet.table.get(name, (*VAR OUT*) value) THEN z.tokenSym := NARROW(value, Symbol); ELSE z.tokenSym := NEW(Symbol, name:=name, keyword:=FALSE); EVAL z.keySet.table.put(name, z.tokenSym); END; z.scanBufferSize := 0; END; IF NOT z.tokenSym.keyword THEN RETURN FALSE END; IF NOT Text.Equal(key, z.tokenSym.name) THEN RETURN FALSE END; EVAL(GetToken(sc)); RETURN TRUE; ELSE RETURN FALSE; END; END; END HaveTokenKey; PROCEDUREHaveTokenDelim (sc: T; delim: CHAR): BOOLEAN RAISES {NoReader, Fail} = BEGIN IF LookToken(sc) = TokenClass.DelimCase THEN IF delim # sc^.tokenDelim THEN RETURN FALSE END; EVAL(GetToken(sc)); RETURN TRUE; ELSE RETURN FALSE; END; END HaveTokenDelim; PROCEDURESetChar (sc: T; n: CHAR; class: CharacterClass) = BEGIN sc^.charTable[n] := class; END SetChar; PROCEDURESetRange (sc: T; n: CHAR; m: CHAR; class: CharacterClass) = BEGIN FOR i := ORD(n) TO ORD(m) DO sc^.charTable[VAL(i, CHAR)] := class; END; END SetRange; PROCEDURETopLevel (sc: T): BOOLEAN = BEGIN RETURN Text.Empty(sc^.fileName); END TopLevel; PROCEDURESetPrompt (sc: T; newFirstPrompt, newNextPrompt: TEXT) = BEGIN sc^.firstPrompt := newFirstPrompt; sc^.nextPrompt := newNextPrompt; sc^.isFirstPrompt := TRUE; END SetPrompt; PROCEDUREFirstPrompt (sc: T) = BEGIN sc^.isFirstPrompt := TRUE; END FirstPrompt; PROCEDUREClear (sc: T) = VAR ch: CHAR; class: TokenClass; BEGIN IF TopLevel(sc) THEN FlushInput(sc) END; TRY IF sc^.lookAheadReady THEN ch:=GetChar(sc) END; (* NOWARN *) EXCEPT SynScan.NoReader => END; TRY IF sc^.tokenReady THEN class:=GetToken(sc) END; (* NOWARN *) EXCEPT SynScan.NoReader, SynScan.Fail => END; sc^.scanBufferSize := 0; END Clear; PROCEDUREReset (sc: T) = BEGIN Clear(sc); WHILE (sc^.input#NIL) AND NOT(Text.Empty(sc^.input^.fileName)) DO TRY PopInput(sc); (* NOWARN *) EXCEPT SynScan.NoReader => END; END; END Reset; PROCEDUREGetKeywordName (key : Keyword): TEXT = BEGIN RETURN key.name ; END GetKeywordName; PROCEDUREGetWriter (sc: T): SynWr.T = BEGIN RETURN sc.swr; END GetWriter; PROCEDURENew (swr: SynWr.T): T = VAR sc: T; BEGIN sc := NEW(T); sc.swr := swr; sc^.scanPoint := 0; sc^.scanBuffer := NEW(REF ARRAY OF CHAR, 256); sc^.keySet := NewKeywordSet(); sc^.lookAheadReady := FALSE; sc^.tokenReady := FALSE; sc^.scanBufferSize := 0; sc^.input := NIL; SetPrompt(sc, "", ""); SetRange(sc, VAL(9, CHAR), VAL(10, CHAR), CharacterClass.BlankCharCase); SetRange(sc, VAL(12, CHAR), VAL(13, CHAR), CharacterClass.BlankCharCase); SetChar(sc, ' ', CharacterClass.BlankCharCase); SetChar(sc, '!', CharacterClass.DelimCharCase); SetChar(sc, '\"', CharacterClass.ReservedCharCase); SetRange(sc, '#', '&', CharacterClass.SpecialCharCase); SetChar(sc, '\'', CharacterClass.ReservedCharCase); SetRange(sc, '(', ')', CharacterClass.DelimCharCase); SetRange(sc, '*', '+', CharacterClass.SpecialCharCase); SetChar(sc, ',', CharacterClass.DelimCharCase); SetChar(sc, '-', CharacterClass.SpecialCharCase); SetChar(sc, '.', CharacterClass.DelimCharCase); SetChar(sc, '/', CharacterClass.SpecialCharCase); SetRange(sc, '0', '9', CharacterClass.DigitCharCase); SetChar(sc, ':', CharacterClass.SpecialCharCase); SetChar(sc, ';', CharacterClass.DelimCharCase); SetChar(sc, '<', CharacterClass.SpecialCharCase); SetChar(sc, '=', CharacterClass.SpecialCharCase); SetChar(sc, '>', CharacterClass.SpecialCharCase); SetChar(sc, '?', CharacterClass.DelimCharCase); SetChar(sc, '@', CharacterClass.SpecialCharCase); SetRange(sc, 'A', 'Z', CharacterClass.LetterCharCase); SetChar(sc, '[', CharacterClass.DelimCharCase); SetChar(sc, '\\', CharacterClass.SpecialCharCase); SetChar(sc, ']', CharacterClass.DelimCharCase); SetChar(sc, '^', CharacterClass.SpecialCharCase); SetChar(sc, '_', CharacterClass.DelimCharCase); SetChar(sc, '`', CharacterClass.LetterCharCase); SetRange(sc, 'a', 'z', CharacterClass.LetterCharCase); SetChar(sc, '{', CharacterClass.DelimCharCase); SetChar(sc, '|', CharacterClass.DelimCharCase); SetChar(sc, '}', CharacterClass.DelimCharCase); SetChar(sc, '~', CharacterClass.ReservedCharCase); SetChar(sc, EofChar, CharacterClass.EofCase); RETURN sc; END New; PROCEDURESetup () = BEGIN END Setup; BEGIN END SynScan.
PROCEDURE ScanNat(sc: T): CARDINAL RAISES {NoReader, Fail} =
VAR nat: INTEGER;
BEGIN
nat := 0;
WHILE sc^.charTable[LookChar(sc)] = CharacterClass.DigitCharCase DO
nat := (10 * nat) + (ORD(GetChar(sc)) - ORD('0'));
IF (nat < 0) OR (nat > LAST(CARDINAL)) THEN
SyntaxMsg(sc, Integer constant is too big
, );
RAISE Fail;
END;
END;
RETURN nat;
END ScanNat;
PROCEDURE ScanNeg(sc: T): INTEGER RAISES {NoReader, Fail} =
VAR neg: INTEGER;
BEGIN
neg := 0;
WHILE sc^.charTable[LookChar(sc)] = CharacterClass.DigitCharCase DO
neg := (10 * neg) - (ORD(GetChar(sc)) - ORD('0'));
IF neg > 0 THEN
SyntaxMsg(sc, Integer constant is too big
, );
RAISE Fail;
END;
END;
RETURN neg;
END ScanNeg;
PROCEDURE ScanInt(): INTEGER RAISES {NoReader, Fail} = BEGIN IF HaveChar('~') THEN RETURN ScanNeg() ELSE RETURN ScanNat() END; END ScanInt;
PROCEDURE ScanNumber(sc: T): TokenClass RAISES {NoReader, Fail} = VAR negative: BOOLEAN; int: INTEGER; real, quot: LONGREAL; ch: CHAR; BEGIN WITH z = sc^ DO negative := HaveChar(sc, '~'); IF negative THEN int := ScanNeg(sc) ELSE int := ScanNat(sc) END; IF LookChar(sc) = '.' THEN ch := GetChar(sc); real := FLOAT(int, LONGREAL); quot := 1.0d0; WHILE z.charTable[LookChar(sc)] = CharacterClass.DigitCharCase DO quot := quot * 10.0d0; IF negative THEN real := real - (FLOAT((ORD(GetChar(sc)) - ORD('0')), LONGREAL) / quot); ELSE real := real + (FLOAT((ORD(GetChar(sc)) - ORD('0')), LONGREAL) / quot); END; END; z.tokenReal := real; RETURN TokenClass.RealCase; ELSE z.tokenInt := int; RETURN TokenClass.IntCase END; END; END ScanNumber;