NOTE: This is a quick and dirty implementation. Please rewrite me to avoid all the allocations.
MODULE; IMPORT Rd, Thread, FloatMode, Lex, Text, TextRd, Word; <*FATAL Rd.Failure, Thread.Alerted*> PROCEDURE Scan Skip (txt: TEXT; len, start: INTEGER; blanks: BOOLEAN): INTEGER = (* Return the index of the first character of "txt" at or beyond "start" that's not in "chars". *) VAR i : CARDINAL := NUMBER(buf); buf : ARRAY [0..63] OF CHAR; BEGIN LOOP IF (start >= len) THEN RETURN len; END; IF (i >= NUMBER (buf)) THEN i := 0; Text.SetChars (buf, txt, start); END; IF (buf[i] IN Lex.Blanks) # blanks THEN RETURN start; END; INC (start); INC (i); END; END Skip; PROCEDUREScanWord (txt: TEXT): Rd.T RAISES {Lex.Error} = (* Ensure that "txt" contains exactly one non-blank substring, and return its span [start..stop) *) VAR len := Text.Length (txt); start := Skip (txt, len, 0, blanks := TRUE); stop := Skip (txt, len, start, blanks := FALSE); finish := Skip (txt, len, stop, blanks := TRUE); BEGIN IF finish < len THEN RAISE Lex.Error; END; RETURN TextRd.New (Text.Sub (txt, start, stop-start)); END ScanWord; PROCEDUREBool (txt: TEXT): BOOLEAN RAISES {Lex.Error} = VAR rd := ScanWord(txt); res := Lex.Bool(rd); BEGIN IF NOT Rd.EOF(rd) THEN RAISE Lex.Error END; RETURN res END Bool; PROCEDUREInt (txt: TEXT; defaultBase: [2..16]): INTEGER RAISES {Lex.Error, FloatMode.Trap} = VAR rd := ScanWord(txt); res := Lex.Int(rd, defaultBase); BEGIN IF NOT Rd.EOF(rd) THEN RAISE Lex.Error END; RETURN res END Int; PROCEDUREUnsigned (txt: TEXT; defaultBase: [2..16]): Word.T RAISES {Lex.Error, FloatMode.Trap} = VAR rd := ScanWord(txt); res := Lex.Unsigned(rd, defaultBase); BEGIN IF NOT Rd.EOF(rd) THEN RAISE Lex.Error END; RETURN res END Unsigned; PROCEDUREReal (txt: TEXT): REAL RAISES {Lex.Error, FloatMode.Trap} = VAR rd := ScanWord(txt); res := Lex.Real(rd); BEGIN IF NOT Rd.EOF(rd) THEN RAISE Lex.Error END; RETURN res END Real; PROCEDURELongReal (txt: TEXT): LONGREAL RAISES {Lex.Error, FloatMode.Trap} = VAR rd := ScanWord(txt); res := Lex.LongReal(rd); BEGIN IF NOT Rd.EOF(rd) THEN RAISE Lex.Error END; RETURN res END LongReal; PROCEDUREExtended (txt: TEXT): EXTENDED RAISES {Lex.Error, FloatMode.Trap} = VAR rd := ScanWord(txt); res := Lex.Extended(rd); BEGIN IF NOT Rd.EOF(rd) THEN RAISE Lex.Error END; RETURN res END Extended; BEGIN END Scan.