as is
without express or implied warranty. Export of this
software outside of the United States of America may require an
export license.
MODULE; IMPORT Text, TextList; TextReader
IMPORT Debug,Fmt;
IMPORT Rd, Wr, Thread, RdCopy, TextWr; IMPORT FloatMode, Lex, Scan; EXCEPTION IncompatibleDelimiters; REVEAL T = Public BRANDED Brand OBJECT (* the remaining text is represented as (pushback & Sub(line, start)) *) (* for efficiency, we can advance in the text without reallocating *) (* the line contains valid data from character 0 to Text.Length(line) *) pushback, line : TEXT; start : CARDINAL := 0; OVERRIDES next := Next; nextS := NextS; nextE := NextE; get := Get; getLR := GetLR; getLongReal := GetLR; getInt := GetInt; getCard := GetCard; getBool := GetBool; nextSE := NextSE; init := Init; initFromRd := InitFromRd; isEmpty := IsEmpty; empty := IsEmpty; shatter := Shatter; pushBack := PushBack; save := Save; continue := Unwind; END;********************************************************************
REVEAL Continuation = BRANDED Brand & " Continuation" OBJECT t : T; pushback, line : TEXT; start : CARDINAL; END; PROCEDURE********************************************************************Save (t : T) : Continuation = BEGIN WITH c = NEW(Continuation) DO c.t := t; c.pushback := t.pushback; c.line := t.line; c.start := t.start; RETURN c END END Save; PROCEDUREUnwind (t : T; to : Continuation) = BEGIN <* ASSERT t = to.t *> t.pushback := to.pushback; t.line := to.line; t.start := to.start END Unwind;
PROCEDUREGet (self : T) : TEXT RAISES { NoMore } = CONST Delims = SET OF CHAR { ' ', '\t', '\n', '\r' }; BEGIN RETURN self.nextSE(Delims,TRUE) END Get; PROCEDURENextE (self : T; delims : TEXT; skipNulls : BOOLEAN) : TEXT RAISES { NoMore } = VAR res : TEXT; BEGIN IF self.next(delims,res,skipNulls) THEN RETURN res ELSE RAISE NoMore END END NextE; PROCEDURENextSE (self : T; READONLY delims : SET OF CHAR; skipNulls : BOOLEAN) : TEXT RAISES { NoMore } = VAR res : TEXT; BEGIN IF self.nextS(delims,res,skipNulls) THEN RETURN res ELSE RAISE NoMore END END NextSE; PROCEDUREIsEmpty (self : T) : BOOLEAN = BEGIN RETURN Text.Length(self.line) <= self.start END IsEmpty; PROCEDURENextS (self : T; READONLY delims: SET OF CHAR; VAR res : TEXT; skipNulls : BOOLEAN) : BOOLEAN = VAR min, lineLen: INTEGER; BEGIN IF Text.Length(self.pushback) > 0 THEN VAR saveLine := self.line; (* savePB := self.pushback; *) saveStart := self.start; found: BOOLEAN; BEGIN self.start := 0; self.line := self.pushback; self.pushback := ""; found := NextS(self, delims, res, skipNulls); self.pushback := Text.Sub(self.line, self.start); self.line := saveLine; self.start := saveStart; IF found THEN (* actually could merge with next. oops. *) RETURN TRUE; ELSE <* ASSERT skipNulls *> END; END; END; min := self.start; lineLen := Text.Length(self.line); WHILE min < lineLen AND NOT Text.GetChar(self.line, min) IN delims DO INC(min); (* this loop a little tighter than it used to be *) END; res := Text.Sub(self.line,self.start,min - (*Mika forgot*)self.start);
(* this can be implemented simply by increasing self.start
self.line := Text.Sub(self.line,min+1,LAST(CARDINAL)); (* save rest *) *) self.start := min+1; (* clear out any stray delimiters after the end, if skipNulls is true *) IF skipNulls THEN VAR len := Text.Length(self.line); BEGIN WHILE len > self.start AND Text.GetChar(self.line, self.start) IN delims DO INC(self.start) END END END; IF Text.Length(self.line) <= self.start THEN self.line := ""; self.start := 0; IF Text.Length(res) = 0 THEN RETURN FALSE; END; END; IF Text.Length(res) = 0 AND skipNulls THEN RETURN NextS(self,delims,res,skipNulls) END; RETURN TRUE END NextS; PROCEDURENext (self : T; delims : TEXT; VAR res : TEXT; skipNulls : BOOLEAN) : BOOLEAN = VAR dset := SET OF CHAR{}; BEGIN FOR i := 0 TO Text.Length(delims)-1 DO dset := dset + SET OF CHAR{Text.GetChar(delims, i)}; END; RETURN NextS(self, dset, res, skipNulls); END Next; PROCEDUREInit (self: T; line : TEXT) : T = BEGIN self.line := line; self.start := 0; self.pushback := ""; RETURN self; END Init; PROCEDUREInitFromRd (self : T; rd : Rd.T) : T RAISES { Rd.Failure, Thread.Alerted } = <* FATAL Wr.Failure *> (* cant happen *) VAR wr := NEW(TextWr.T).init(); BEGIN EVAL RdCopy.ToWriter(rd, wr); RETURN self.init(TextWr.ToText(wr)) END InitFromRd; PROCEDUREShatter (self : T; listDelims : TEXT; endDelims : TEXT; skipNulls := FALSE) : TextList.T = <* FATAL IncompatibleDelimiters *> (* force a program abort *) PROCEDURE CheckOverlap() = BEGIN FOR i := 0 TO Text.Length(listDelims) - 1 DO IF Text.FindChar(endDelims, Text.GetChar(listDelims, i)) # -1 THEN RAISE IncompatibleDelimiters END END END CheckOverlap; VAR res : TextList.T := NIL; BEGIN CheckOverlap(); TRY VAR reader := NEW(T).init(self.nextE(endDelims)); word : TEXT; BEGIN WHILE Next(reader, listDelims, word, skipNulls) DO res := TextList.Cons(word, res) END; res := TextList.ReverseD(res) END EXCEPT NoMore => <* ASSERT res = NIL *> (* skip *) END; RETURN res END Shatter;
PROCEDURE Simplify(self: T) =
BEGIN
IF self.start # 0 THEN
self.line := Text.Sub(self.line, self.start);
self.start := 0;
END;
IF Text.Length(self.pushback) > 0 THEN
self.line := self.pushback & self.line;
self.pushback := ;
END;
END Simplify;
PROCEDURE********************************************************************PushBack (self: T; t: TEXT) = BEGIN (* this is buggy if we change delimiters self.pushback := t & self.pushback; *) (* less efficient, but: *) self.line := Text.Sub(self.line,0,self.start) & t & Text.Sub(self.line,self.start) END PushBack;
PROCEDUREGetLR (t : T) : LONGREAL RAISES { NoMore, Lex.Error, FloatMode.Trap }= BEGIN RETURN Scan.LongReal(t.get()) END GetLR; PROCEDUREGetInt (t : T) : INTEGER RAISES { NoMore, Lex.Error, FloatMode.Trap }= BEGIN RETURN Scan.Int(t.get()) END GetInt; PROCEDUREGetBool (t : T) : BOOLEAN RAISES { NoMore, Lex.Error } = BEGIN RETURN Scan.Bool(t.get()) END GetBool; PROCEDUREGetCard (t : T) : CARDINAL RAISES { NoMore, Lex.Error, FloatMode.Trap }= BEGIN WITH res = Scan.Int(t.get()) DO IF res < FIRST(CARDINAL) OR res > LAST(CARDINAL) THEN RAISE Lex.Error END; RETURN res END END GetCard; PROCEDURENew (txt : TEXT) : T = BEGIN RETURN NEW(T).init(txt) END New; BEGIN END TextReader.