T:Syntax10.Scn.FntBalloonElemsAllocfSyntax10.Scn.Fnt Syntax10i.Scn.Fnt   q9Syntax10b.Scn.Fnt) F,j"Keyword" This records contains information about one keyword. It is used in HashT. "word" Contains the keyword (BEGIN, PROCEDURE, etc.) "class" Contains the class of the keyword, concerning its effect on indentation, i.e. class is one of IK, IBK, IEK, SK. "w" This Texts.Writer is used to insert Tab - Characters. It is declared globally due to runtime optimization. w is initialized in the Module Body with Texts.OpenWriter(w); "b" This Texts.Buffer is used to insert Tab - Characters. It is declared globally due to runtime optimization. b is initialized in the Module Body with NEW(b); Texts.OpenWriter(b); "exp" This boolean Variable is TRUE whenever an exported name can occur during the process of changing fonts. "bText" Contains the text to be formatted "cmtFnt" Contains the typeface for comments, usually the italic version of the Font found at the beginning of the module. "expNamFnt" Contains the typeface for exported names, usually the bold version of the Font found at the beginning of the module. "oldNotifier" Contains the Notifier of the text to be restored after formating or NIL if the Notifier doesn't have to be restored. oldNotifier # NIL only when called by Beautifier.Format * "fileName" Contains the name of the file the formated text has to be stored in after formating or is empty if it doesn't have to be restored. fileName is used only when called by Beautifier.Format ^ or Beautifier.Format filename "hashT" This hashtable contains the keywords and their indentation class using the Keyword-Record. It is initialized in the Module-Body of Beautifier and used in getKeyword(...) "fC" (firstCharacter) This table realizes a function {A..Y} -> {0..10} that assigns consecutive numbers to all characters a keyword can start with. These numbers are needed by the keyword-hash function. "lC" (lastCharacter) This table realizes afunction {A..Y} -> {0..8} that assigns consecutive numbers to all characters a keyword can end with. These numbers are needed by the keyword-hash function. "Init" Initializes a keyword with s and class "noExpLine" This variable is needed to prevent that definitions like JSampRow = POINTER TO ARRAY (JPEGMAXDIMENSION * RGBPIXELSIZE) OF CHAR; are beautified to JSampRow = POINTER TO ARRAY (JPEGMAXDIMENSION* RGBPIXELSIZE) OF CHAR; noExpLine contains the line number of the last line where a = or : character has occured. Within such a line nothing can be made bold. The occurence of a ; character sets noExpLine to -1. "passComments" passComments(VAR s: Texts.Reader); Passes by comments, i.e. scans for (* and *) and exits if either the end of the text is reached or if the number of comment closings *) is greater than comments openings (*. Therefore passComments must be called after a comment opening has been found in the text and advances s until it is no longer in a comment. "passProcHead" passProcHead(VAR s: Texts.Scanner; bText: Texts.Text; VAR noProc: BOOLEAN; VAR procName: ARRAY OF CHAR); Passes by a Procedure-heading. passProcHead(...) is called after the keyword PROCEDURE was read in. "noProc" The keyword PROCEDURE doesn't neccessarily start a procedure heading, it can also start a Procedure-Type definition. In this case passProcHead noProc becomes TRUE "procName" Contains the name of the Procedure. "getKeyword" PROCEDURE getKeyword(bText: Texts.Text; VAR s: Texts.Scanner; section: BOOLEAN): SHORTINT; Scans bText for the next key word and returns one of the Constants IK, IBK, IEK or SK depending on what type of keyword was found. section must indicate, wheter s is currently within a section or not. "section" section indicates whether a Section Keyword (SK) has been encountered previously whose rannge is not yet finished. "InsertInd" InsertInd(VAR r: Texts.Reader; VAR s: Texts.Scanner; t: Texts.Text; ind: SHORTINT; decInd: BOOLEAN); Inserts ind Tab - Characters at the beginning of each line from r.pos to s.pos. If decInd is true the last line is shifted one tab-position to the left. After execution r.pos = s.pos - 1 or the end of the text was reached. "IndentCheck" PROCEDURE IndentCheck(bText: Texts.Text); Inserts correct Tabulation into bText. This is done in two steps: a) Deleting existing tabulation -- within this step blanks and tabs at end of lines are deleted as well b) Inserting correct tabulation "ChangeFont" ChangeFont(bText: Texts.Text; expNamFnt, cmtFnt: Fonts.Font); Scans bText for comments and exported names in order to change their font to cmtFnt and expNamFnt respectively. Within this step blanks between an exported name and it's export marker (* or -) are deleted. "MarkedViewer" MarkedViewer(): TextFrames.Frame; Returns the currently marked viewer. (Use F1 to mark a viewer) "NoNotify" NoNotify(t: Texts.Text; op: INTEGER; beg, end: LONGINT); END NoNotify; Dummy notifier that is used to prevent the text beeing displayed while the process of formatting is running. "ParseCmdLine" ParseCmdLine(VAR bText: Texts.Text; VAR oldNotifier: Texts.Notifier); Parses the command line provided with the command call and tries to determine the text on which formating must be performed. It opens the Text using OpenText(...) and returns the Text to be beautified. If the command line was correct, bText contains a value # NIL; OldNotifier will be assigned the standard Text-Notifier in order to restore it after the formatting process has finished. "OpenText" PROCEDURE OpenText (fileName: ARRAY OF CHAR; VAR t: Texts.Text; VAR f: TextFrames.Frame); Opens fileName in a TextFrame f and returns the Text t. "GetFontsFromText" GetFontsFromText(bText: Texts.Text; VAR expNamFnt, cmtFnt: Fonts.Font); Tries to determine which font is used within bText by looking up the font of it's first Element (usually the name MODULE). cmtFnt and expNamFnt are set to the italic and bold equivalents respectively. "UpdateText" Updates the text after formating, i.e. restores the Notifier or saves the source-code respectivly. "Format" Call Beautifier.Format * | ^ | filename for beautification of Oberon-2 Source Code. "RemSemicolons" PROCEDURE RemSemicolons(bText: Texts.Text); Removes all semicolons from bText that are not needed. "lastSC" lastSC contains the position of the last semicolon that was found. "eCount" eCount counts the number of ENDs neccessary to end the current procedure. "RemoveSemicolons" Call Beautifier.RemSemicolons * | ^ | filename for removal of unneccessary semicolons in Oberon-2 Source Code. "FoldProcedures" PROCEDURE FoldProcedures(bText: Texts.Text); Automatically folds all procedures in bText, if they are not yet folded. FoldProcedures(...) even folds local procedures. "FoldProc" Call Beautifier.FoldProc * | ^ | filename to automatically fold all procedures in Oberon-2 Source Code. "ReadOperator" PROCEDURE ReadOperator(VAR r: Texts.Reader; VAR buf: ARRAY OF CHAR; VAR opFlags: SET); Reads the next operator using r. After completion the buffer contains a stretch of the text which ends with the operator. "opFlags" opFlags contains the kind of operator which was read in by ReadOperator(...) The following elements are relevant for opFlags: TwoCharOp ..... The operator consists of two characters (e.g. <= or := ) SpcAfterOnly ... The operator needs a blank after it only (e.g. , or ; ) SpcAllOps ........ Used internally to determine if * and - are arithmetic operators or export markers in the current context. "SpaceOperators" PROCEDURE SpaceOperators(bText: Texts.Text); Inserts correct spacing to operators in bText. "SpaceFormParms" PROCEDURE SpaceFormParms (bText: Texts.Text); Inserts Spaces before formal- and Receiver- Parameterlists. "RemSpaces" PROCEDURE RemSpaces (bText: Texts.Text); Removes all superflouus spaces in bText. "linStart" TRUE, while r is reading spaces at the beginning of a line. "SpaceOps" Call Beautifier.SpaceOps * | ^ | filename to automatically insert missing blanks in Oberon-2 Source Code. "InitHashTable" Initializes the hash table used in getKeyword(...). "Beautify" Call Beautifier.Beautify * | ^ | Filename.Mod for complete beautification of Oberon-2 Source-Code. InfoElemsAllocVSyntax10.Scn.FntIzStampElemsAlloc19 Dec 97m"Title": Beautifier "Author": Lichtenberger Robert, tanis@sport2.uibk.ac.at "Abstract": Formats Oberon-2 source-code in compliance with the style-guidelines of the institute of system software at the University of Linz, Austria "Keywords": Beautifier, PrettyPrinter, Source-Code, Format "Version": 2.0 "From": 15.07.95 "Until":  "Changes": 05 Oct 96 RLI Removal of semicolon before END Modulename. prevented 09 Oct 97 RLI Bug in FoldProcedures fixed: comments are passed now, Bug reported by B. Treutwein 09 Oct 97 RLI Bug in ChangeFont fixed comments in upper part of Modules are now formatted correctly 10 Dec 97 RLI Tricky Bug in RemSemicolons removed. Happened within the sequence "END; END END" 10 Dec 97 RLI Very Tricky Bug in ChangeFont fixed. See noExpLine - Balloon Information for details. 11 Dec 97 RLI Very very tricky Bug in InsertInd fixed. Happened with TextPrinter.Mod only. "Hints": Call Beautifier.Format * | ^ | filename. `  Syntax10i.Scn.Fnt318FoldElemsNew38858/8J8u891>!R8,8E$ 808898"F8EB $D8')$11(-I 878 ($"#$78H8 18 MarkElemsAllocxN8 8q8-"M8*8M  / !9%$!#&%#  "LinkElemsAllocBeautifier.Modx$ # %"&838X " B  "%+<*4!D! (P(!8 Syntax10b.Scn.Fnt8U""D,% 48/8g8 8x808Z6:89H:6z8  8l8Z8C2l:8/8~8q8P8/8.=h)8*8m.8  88  86,/ 88'86!%f*#88A8UMODULE Beautifier;   IMPORT FoldElems, Fonts, Oberon, Out, Strings, TextFrames, Texts, Viewers, MenuViewers; CONST Tab = 09X; LF = 0DX; SK = 0; (* Section - Keyword *) IK = 1; (* Indent Keyword *) IEK = 2; (* Indent ending Keyword *) IBK = 3; (* Indent breaking Keyword *) NK = -1; (* No keyword -- used when end of text is reached*) TwoCharOp = 0; SpcAfterOnly = 1; SpcAllOps = 2; TYPE Keyword = RECORD word: ARRAY 10 OF CHAR; class: SHORTINT END;  VAR w: Texts.Writer; b: Texts.Buffer; fC, lC: ARRAY 23 OF INTEGER; hashT: ARRAY 51 OF Keyword; PROCEDURE (VAR k: Keyword) Init (s: ARRAY 10 OF CHAR; class: SHORTINT);  BEGIN COPY(s, k.word); k.class := class END Init;  PROCEDURE passComments (VAR s: Texts.Reader);  VAR cmt: INTEGER; ch: CHAR; BEGIN cmt := 1; WHILE ~s.eot & (cmt > 0) DO Texts.Read(s, ch); WHILE ch = "(" DO Texts.Read(s, ch); IF ch = "*" THEN INC(cmt) END END; (* Nested comment opening *) WHILE ch = "*" DO Texts.Read(s, ch); IF ch = ")" THEN DEC(cmt) END END (* comment closing *) END END passComments;  PROCEDURE passProcHead (VAR s: Texts.Scanner; bText: Texts.Text; VAR noProc: BOOLEAN; VAR procName: ARRAY OF CHAR);  VAR ch: CHAR; BEGIN noProc := FALSE; Texts.Scan(s); (* --- Type-bound procedures can have Receiver *) WHILE ~s.eot & (s.class = Texts.Char) & (s.c = Texts.ElemChar) DO Texts.Scan(s) END; IF (s.class = Texts.Char) & (s.c = "(") THEN REPEAT Texts.Scan(s) UNTIL (s.class = Texts.Char) & (s.c = ")") OR s.eot; Texts.Scan(s) END; WHILE ~s.eot & (s.class = Texts.Char) & (s.c = Texts.ElemChar) DO Texts.Scan(s) END; (* --- Check name of procedure *) IF s.class # Texts.Name THEN noProc := TRUE; RETURN END; COPY(s.s, procName); (* --- Formal parameters *) Texts.OpenScanner(s, bText, Texts.Pos(s) - 1); Texts.Read(s, ch); WHILE ~s.eot & (ch # ";") DO IF ch = "(" THEN WHILE ~s.eot & (ch # ")") DO Texts.Read(s, ch) END ELSE Texts.Read(s, ch) END END END passProcHead;  PROCEDURE MarkedViewer (): TextFrames.Frame;  VAR v: Viewers.Viewer; BEGIN v := Oberon.MarkedViewer(); IF (v # NIL) & (v.dsc # NIL) & (v.dsc.next IS TextFrames.Frame) THEN RETURN v.dsc.next(TextFrames.Frame) ELSE RETURN NIL END END MarkedViewer;  PROCEDURE NoNotify (t: Texts.Text; op: INTEGER; beg, end: LONGINT);  END NoNotify;  PROCEDURE ParseCmdLine (VAR bText: Texts.Text; VAR oldNotifier: Texts.Notifier);  VAR beg, end, time: LONGINT; f: TextFrames.Frame; par: Oberon.ParList; s: Texts.Scanner; t: Texts.Text; PROCEDURE OpenText (fileName: ARRAY OF CHAR; VAR t: Texts.Text; VAR f: TextFrames.Frame);  VAR menuF: TextFrames.Frame; v: Viewers.Viewer; x, y: INTEGER; BEGIN menuF := TextFrames.NewMenu(fileName, "Edit.Store System.Close"); t := TextFrames.Text(fileName); f := TextFrames.NewText(t, 0); Oberon.AllocateUserViewer(0, x, y); v := MenuViewers.New(menuF, f, TextFrames.menuH, x, y) END OpenText;  BEGIN oldNotifier := NIL; par := Oberon.Par; Texts.OpenScanner(s, par.text, par.pos); Texts.Scan(s); IF s.class = Texts.Name THEN (* Called by filename *) OpenText(s.s, bText, f) ELSIF (s.class = Texts.Char) & (s.c = "*") THEN (* Called by selected viewer *) f := MarkedViewer() ELSIF (s.class = Texts.Char) & (s.c = "^") THEN (* Called by selection *) Oberon.GetSelection(t, beg, end, time); IF time >= 0 THEN (* Selection found *) Texts.OpenScanner(s, t, beg); Texts.Scan(s); IF s.class = Texts.Name THEN OpenText(s.s, bText, f) END END END; IF f # NIL THEN bText := f.text; oldNotifier := f.text.notify; bText.notify := NoNotify ELSE Out.String("Could not find TextFrames.Frame. Program aborted."); Out.Ln; HALT(99) END END ParseCmdLine;  PROCEDURE GetFontsFromText (bText: Texts.Text; VAR expNamFnt, cmtFnt: Fonts.Font);  VAR r: Texts.Reader; ch: CHAR; boldfont, italicfont: ARRAY 32 OF CHAR; i: INTEGER; BEGIN (* --- Get the fontname *) Texts.OpenReader(r, bText, 0); Texts.Read(r, ch); COPY(r.fnt.name, boldfont); COPY(r.fnt.name, italicfont); (* --- Get fonts *) i := Strings.Pos(".", boldfont, 0); Strings.Insert("b", i, boldfont); expNamFnt := Fonts.This(boldfont); Strings.Insert("i", i, italicfont); cmtFnt := Fonts.This(italicfont) END GetFontsFromText;  PROCEDURE UpdateText (bText: Texts.Text; oldNotifier: Texts.Notifier);  BEGIN IF oldNotifier # NIL THEN bText.notify := oldNotifier; bText.notify(bText, Texts.replace, 0, bText.len) END END UpdateText;  PROCEDURE getKeyword (t: Texts.Text; VAR s: Texts.Scanner; section: BOOLEAN): SHORTINT;  VAR i, firstC, lastC: INTEGER; pos: LONGINT; noProc: BOOLEAN; dummy: ARRAY 32 OF CHAR; BEGIN WHILE ~s.eot DO Texts.Scan(s); IF s.class = Texts.Name THEN (* --- hash value calculation *) firstC := ORD(s.s[0]) - ORD('A'); lastC := ORD(s.s[s.len - 1]) - ORD('A'); IF (firstC >= 0) & (firstC < 23) & (lastC < 23) & (lastC >= 0) THEN i := (fC[firstC] + 11 * lC[lastC]) MOD 51; IF (i # 17) & (i # 35) & (i # 4) & (hashT[i].word = s.s) THEN RETURN hashT[i].class ELSIF (i = 35) & (s.nextCh = LF) & (s.s = "VAR") THEN RETURN IBK ELSIF (i = 4) & (s.s = "BEGIN") THEN IF section THEN RETURN IBK ELSE RETURN IK END ELSIF (i = 17) THEN passProcHead(s, t, noProc, dummy); IF ~noProc THEN RETURN hashT[i].class END END END ELSIF (s.class = Texts.Char) THEN IF (s.c = "|") THEN RETURN IBK END; IF (s.c = "(") & (s.nextCh = "*") THEN Texts.Scan(s); passComments(s) END END END; RETURN NK END getKeyword;  PROCEDURE InsertInd (VAR r: Texts.Reader; VAR s: Texts.Scanner; t: Texts.Text; ind: SHORTINT; decInd: BOOLEAN);  VAR ch, ch2: CHAR; lastLF, sPos: LONGINT; i: INTEGER; BEGIN FOR i := 1 TO ind DO Texts.Write(w, Tab) END; sPos := Texts.Pos(s); lastLF := - 1; (* --- Trace reader to position of Scanner *) WHILE (Texts.Pos(r) + 1 < sPos) & ~r.eot DO Texts.Read(r, ch); Texts.Read(r, ch2); IF ~r.eot THEN Texts.OpenReader(r, t, Texts.Pos(r) - 1); END; IF (ch = LF) & (ch2 # LF) & (ind > 0) THEN lastLF := Texts.Pos(r); sPos := sPos + ind; Texts.Copy(w.buf, b); Texts.Insert(t, lastLF, b); Texts.OpenReader(r, t, lastLF + ind) END END; Texts.OpenBuf(w.buf); (* Flush buffer *) Texts.OpenScanner(s, t, sPos); (* --- Check if we have to move the last line one tab position to the left *) IF decInd & (lastLF # - 1) THEN Texts.Delete(t, lastLF, lastLF + 1); Texts.OpenScanner(s, t, Texts.Pos(s) - 1); Texts.OpenReader(r, t, Texts.Pos(r) - 1) END END InsertInd;  PROCEDURE IndentCheck (bText: Texts.Text);  VAR r: Texts.Reader; s: Texts.Scanner; ch: CHAR; ind, i: SHORTINT; pos: LONGINT; section: BOOLEAN; leadStart, trailStart: LONGINT; BEGIN (* --- Kill leading and trailing Tabs/Blanks *) pos := 0; Texts.OpenReader(r, bText, pos); Texts.Read(r, ch); trailStart := 0; WHILE (~r.eot) DO IF ch = LF THEN leadStart := Texts.Pos(r); Texts.Read(r, ch); WHILE (ch = " ") OR (ch = Tab) DO Texts.Read(r, ch) END; pos := Texts.Pos(r) - 1; Texts.Delete(bText, leadStart, pos); Texts.OpenReader(r, bText, leadStart + 1) ELSE WHILE (ch # LF) & ~r.eot DO IF (ch = " ") OR (ch = Tab) THEN IF trailStart = - 1 THEN trailStart := Texts.Pos(r) - 1 END ELSE trailStart := - 1 END; Texts.Read(r, ch) END; IF trailStart > - 1 THEN pos := Texts.Pos(r) - 1; Texts.Delete(bText, trailStart, pos); Texts.OpenReader(r, bText, trailStart + 1); trailStart := - 1 END END END; (* --- Insert correct tabulation *) Texts.OpenScanner(s, bText, 0); Texts.OpenReader(r, bText, 0); Texts.Scan(s); section := FALSE; ind := 0; WHILE ~s.eot & (ind >= 0) DO i := getKeyword(bText, s, section); IF i = IK THEN InsertInd(r, s, bText, ind, FALSE); INC(ind) ELSIF i = IEK THEN InsertInd(r, s, bText, ind, TRUE); DEC(ind); IF ind = 0 THEN section := FALSE END ELSIF i = IBK THEN InsertInd(r, s, bText, ind, TRUE) ELSIF i = SK THEN InsertInd(r, s, bText, ind, section); IF ~section THEN INC(ind) END; section := TRUE END END END IndentCheck;  PROCEDURE ChangeFont (bText: Texts.Text; expNamFnt, cmtFnt: Fonts.Font);  VAR s: Texts.Scanner; ch: CHAR; oPos, pos: LONGINT; exp: BOOLEAN; noExpLine: INTEGER; BEGIN pos := 0; noExpLine := -1; exp := TRUE; Texts.OpenScanner(s, bText, pos); WHILE ~s.eot DO Texts.Scan(s); oPos := pos; pos := Texts.Pos(s); IF (s.class = Texts.Char) & (s.c = "(") & (s.nextCh = "*") THEN (* comment *) oPos := pos; Texts.Scan(s); passComments(s); pos := Texts.Pos(s); Texts.ChangeLooks(bText, oPos - 2, pos, {0}, cmtFnt, s.col, s.voff); Texts.OpenScanner(s, bText, pos) ELSIF (s.class = Texts.Char) & ((s.c = "=") OR (s.c = ":")) THEN noExpLine := s.line; ELSIF (s.class = Texts.Char) & (s.c = ";") THEN noExpLine := -1; ELSIF (s.class = Texts.Name) THEN IF (s.s = "BEGIN") THEN exp := FALSE ELSIF (s.s = "PROCEDURE") THEN exp := TRUE ELSIF exp & (noExpLine # s.line) & (s.nextCh = " ") THEN (* probably spaces between name and '*' *) Texts.Read(s, ch); WHILE ~s.eot & (ch = " ") DO Texts.Read(s, ch) END; IF (ch = "*") OR (ch = "-") THEN Texts.Delete(bText, pos - 1, Texts.Pos(s) - 1); Texts.ChangeLooks(bText, oPos, pos, {0}, expNamFnt, s.col, s.voff); Texts.OpenScanner(s, bText, pos) ELSE (* rewind *) Texts.OpenScanner(s, bText, Texts.Pos(s) - 1) END ELSIF exp & (noExpLine # s.line) & ((s.nextCh = "*") OR (s.nextCh = "-")) THEN (* "regular" exported name *) Texts.ChangeLooks(bText, oPos - 1, pos, {0}, expNamFnt, s.col, s.voff); Texts.OpenScanner(s, bText, pos) END END END END ChangeFont;  PROCEDURE Format*;  VAR bText: Texts.Text; oldNotifier: Texts.Notifier; expNamFnt, cmtFnt: Fonts.Font; BEGIN bText := NIL; oldNotifier := NIL; ParseCmdLine(bText, oldNotifier); IF bText = NIL THEN Out.String("Usage: Beautifier.Format * | ^ | Filename.Mod"); Out.Ln ELSE GetFontsFromText(bText, expNamFnt, cmtFnt); FoldElems.ExpandAll(bText, 0, TRUE); ChangeFont(bText, expNamFnt, cmtFnt); IndentCheck(bText); FoldElems.CollapseAll(bText, {FoldElems.tempLeft}); UpdateText(bText, oldNotifier) END END Format;  PROCEDURE RemSemicolons (bText: Texts.Text);  VAR s: Texts.Scanner; lastSC, pos: LONGINT; eCount: INTEGER; err: BOOLEAN; procName: ARRAY 24 OF CHAR; i: INTEGER; BEGIN lastSC := - 1; eCount := 0; Texts.OpenScanner(s, bText, 0); Texts.Scan(s); WHILE ~s.eot DO IF (s.class = Texts.Char) THEN IF (s.c = ';') THEN lastSC := Texts.Pos(s) - 1; Texts.Scan(s) END END; IF (s.c = "(") & (s.nextCh = "*") THEN Texts.Scan(s); passComments(s); Texts.Scan(s) END; IF (s.class = Texts.Name) THEN (* --- delete semicolons *) IF (lastSC # - 1) & (eCount > 0) & ((s.s = "END") OR (s.s = "ELSE") OR (s.s = "ELSIF") OR (s.s = "UNTIL")) THEN pos := Texts.Pos(s); Texts.Delete(bText, lastSC - 1, lastSC); Texts.OpenScanner(s, bText, pos - 1) END; IF (s.s = "IF") OR (s.s = "WHILE") OR (s.s = "FOR") OR (s.s = "RECORD") OR (s.s = "WITH") OR (s.s = "LOOP") OR (s.s = "CASE") THEN INC(eCount) ELSIF (s.s = "END") THEN DEC(eCount) END; IF (s.s = "PROCEDURE") THEN passProcHead(s, bText, err, procName); IF ~err THEN INC(eCount) END END END; lastSC := - 1; Texts.Scan(s) END END RemSemicolons;  PROCEDURE RemoveSemicolons*;  VAR bText: Texts.Text; oldNotifier: Texts.Notifier; BEGIN ParseCmdLine(bText, oldNotifier); IF bText = NIL THEN Out.String("Usage: Beautifier.RemSemicolons * | ^ | Filename.Mod"); Out.Ln ELSE FoldElems.ExpandAll(bText, 0, TRUE); RemSemicolons(bText); FoldElems.CollapseAll(bText, {FoldElems.tempLeft}); UpdateText(bText, oldNotifier) END END RemoveSemicolons;  PROCEDURE FoldProcedures (bText: Texts.Text);  VAR s: Texts.Scanner; start, end: LONGINT; procName: ARRAY 24 OF CHAR; err: BOOLEAN; ch: CHAR; e: FoldElems.Elem; te: Texts.Elem; BEGIN Texts.OpenScanner(s, bText, 0); Texts.Scan(s); WHILE ~s.eot DO IF(s.class = Texts.Name) & (s.s = "PROCEDURE") THEN (* FoldProcedures *) passProcHead(s, bText, err, procName); IF ~err THEN start := Texts.Pos(s); (* --- Read to end of line *) Texts.Read(s, ch); WHILE (ch # LF) & ((s.elem = NIL) OR ~(s.elem IS FoldElems.Elem)) DO Texts.Read(s, ch) END; te := s.elem; (* --- find end of procedure *) Texts.OpenScanner(s, bText, start); Texts.Scan(s); end := - 1; WHILE ~s.eot & (end = - 1) DO IF (s.class = Texts.Name) & (s.s = "END") THEN Texts.Scan(s); IF (s.class = Texts.Name) & (s.s = procName) THEN end := Texts.Pos(s) END ELSE Texts.Scan(s) END END; (* --- Check, whether Procedure has not yet been folded *) IF (te = NIL) OR ~(te IS FoldElems.Elem) THEN (* --- Insert FoldElems *) NEW(e); e.mode := FoldElems.expRight; e.W := FoldElems.elemW; e.H := FoldElems.elemH; e.handle := FoldElems.FoldHandler; e.visible := TRUE; Texts.WriteElem(w, e); Texts.Insert(bText, end, w.buf); NEW(e); e.mode := FoldElems.expLeft; e.W := FoldElems.elemW; e.H := FoldElems.elemH; e.handle := FoldElems.FoldHandler; NEW(e.hidden); Texts.OpenBuf(e.hidden); e.visible := TRUE; Texts.WriteElem(w, e); Texts.Insert(bText, start, w.buf); Texts.OpenScanner(s, bText, end) END END ELSIF (s.class = Texts.Name) & (s.s = "BEGIN") THEN (* Fold Module-Body *) start := Texts.Pos(s) - 1; Texts.OpenReader(s, bText, start); (* --- Read to end of line *) Texts.Read(s, ch); WHILE (ch # LF) & ((s.elem = NIL) OR ~(s.elem IS FoldElems.Elem)) DO Texts.Read(s, ch) END; te := s.elem; (* --- Find end of Module *) WHILE ~s.eot DO Texts.Scan(s); WHILE ~s.eot & (s.class = Texts.Name) & (s.s = "END") DO end := Texts.Pos(s) - 5; Texts.Scan(s) END END; (* --- Check, whether Procedure has not yet been folded *) IF (te = NIL) OR ~(te IS FoldElems.Elem) THEN (* --- Insert FoldElems *) NEW(e); e.mode := FoldElems.expRight; e.W := FoldElems.elemW; e.H := FoldElems.elemH; e.handle := FoldElems.FoldHandler; e.visible := TRUE; Texts.WriteElem(w, e); Texts.Insert(bText, end, w.buf); NEW(e); e.mode := FoldElems.expLeft; e.W := FoldElems.elemW; e.H := FoldElems.elemH; e.handle := FoldElems.FoldHandler; NEW(e.hidden); Texts.OpenBuf(e.hidden); e.visible := TRUE; Texts.WriteElem(w, e); Texts.Insert(bText, start, w.buf); Texts.OpenScanner(s, bText, end) END ELSIF (s.class = Texts.Char) & (s.c = "(") & (s.nextCh = "*") THEN passComments(s) END; Texts.Scan(s) END END FoldProcedures;  PROCEDURE FoldProc*;  VAR bText: Texts.Text; oldNotifier: Texts.Notifier; BEGIN ParseCmdLine(bText, oldNotifier); IF bText = NIL THEN Out.String("Usage: Beautifier.FoldProc * | ^ | Filename.Mod"); Out.Ln ELSE FoldElems.ExpandAll(bText, 0, TRUE); FoldProcedures(bText); FoldElems.CollapseAll(bText, {FoldElems.tempLeft}); UpdateText(bText, oldNotifier) END END FoldProc;  PROCEDURE ReadOperator (VAR r: Texts.Reader; VAR buf: ARRAY OF CHAR; VAR opFlags: SET);  VAR i, cmt: INTEGER; ch, ech: CHAR; opFound: BOOLEAN; BEGIN opFound := FALSE; EXCL(opFlags, TwoCharOp); EXCL(opFlags, SpcAfterOnly); Texts.Read(r, ch); WHILE ~r.eot & ~opFound DO (* --- Move buffer content *) FOR i := 0 TO 7 DO buf[i] := buf[i + 1] END; buf[8] := ch; (* --- Leave out comments *) WHILE ~r.eot & (ch = "(") DO Texts.Read(r, ch); IF (ch = "*") THEN passComments(r); Texts.Read(r, ch) END END; (* --- Leave out String and Character constants *) IF (ch = "'") OR (ch = '"') THEN REPEAT Texts.Read(r, ech) UNTIL r.eot OR (ch = ech) END; (* --- Check for spcAllOps & Spacing of parameter lists *) IF (buf = "PROCEDURE") THEN EXCL(opFlags, SpcAllOps) END; IF (buf[4] = "B") & (buf[5] = "E") & (buf[6] = "G") & (buf[7] = "I") & (buf[8] = "N") THEN INCL(opFlags, SpcAllOps) END; (* --- Check for Operators *) IF (ch = "<") OR (ch = ">") OR (ch = ":") THEN opFound := TRUE; INCL(opFlags, TwoCharOp) ELSIF (ch = "+") OR ((SpcAllOps IN opFlags) & ((ch = "-") OR (ch = "*")) ) OR (ch = "/") OR (ch = "=") OR (ch = "#") OR (ch = "&") THEN opFound := TRUE ELSIF (ch = ";") OR (ch = ",") THEN opFound := TRUE; INCL(opFlags, SpcAfterOnly) END; Texts.Read(r, ch) END; IF opFound THEN FOR i := 0 TO 7 DO buf[i] := buf[i + 1] END; buf[8] := ch; IF (TwoCharOp IN opFlags) & (ch = "=") THEN FOR i := 0 TO 7 DO buf[i] := buf[i + 1] END; Texts.Read(r, ch); buf[8] := ch ELSE EXCL(opFlags, TwoCharOp); IF buf[7] = ":" THEN INCL(opFlags, SpcAfterOnly) END END END END ReadOperator;  PROCEDURE SpaceOperators (bText: Texts.Text);  VAR r: Texts.Reader; opFlags: SET; buffer: ARRAY 10 OF CHAR; ch: CHAR; pos: LONGINT; PROCEDURE InsertSpace (pos: LONGINT);  BEGIN Texts.Write(w, " "); Texts.Insert(bText, pos, w.buf); Texts.OpenReader(r, bText, pos) END InsertSpace;  BEGIN Texts.OpenReader(r, bText, 0); COPY(" ", buffer); ReadOperator(r, buffer, opFlags); WHILE ~r.eot DO pos := Texts.Pos(r); IF TwoCharOp IN opFlags THEN IF (buffer[8] # " ") THEN InsertSpace(pos - 1) END; IF (buffer[5] # " ") THEN InsertSpace(pos - 3) END ELSE IF SpcAfterOnly IN opFlags THEN IF (buffer[8] # " ") & (buffer[8] # LF) THEN InsertSpace(pos - 1) END ELSE IF (buffer[8] # " ") THEN InsertSpace(pos - 1) END; IF (buffer[6] # " ") THEN InsertSpace(pos - 2) END END END; ReadOperator(r, buffer, opFlags) END END SpaceOperators;  PROCEDURE SpaceFormParms (bText: Texts.Text);  VAR s: Texts.Scanner; r: Texts.Reader; ch: CHAR; BEGIN Texts.OpenScanner(s, bText, 0); Texts.Scan(s); WHILE ~s.eot DO IF (s.class = Texts.Name) & (s.s = "PROCEDURE") THEN Texts.OpenReader(r, bText, Texts.Pos(s) - 1); Texts.Read(r, ch); (* --- Search through the Procedure Heading *) WHILE ~r.eot & (ch # ";") DO IF (ch = "(") THEN (* --- Parameterlist found *) Texts.OpenReader(r, bText, Texts.Pos(r) - 2); Texts.Read(r, ch); IF ch # " " THEN (* --- Insert space *) Texts.Write(w, ' '); Texts.Insert(bText, Texts.Pos(r) , w.buf); Texts.OpenReader(r, bText, Texts.Pos(r)) END; (* --- Search for end of parameterlist *) WHILE ~r.eot & (ch # ")") DO Texts.Read(r, ch) END; Texts.OpenScanner(s, bText, Texts.Pos(s)) END; Texts.Read(r, ch) END END; Texts.Scan(s) END END SpaceFormParms;  PROCEDURE RemSpaces (bText: Texts.Text);  VAR r: Texts.Reader; linStart: BOOLEAN; ch, ech: CHAR; start, end: LONGINT; BEGIN linStart := TRUE; Texts.OpenReader(r, bText, 0); Texts.Read(r, ch); WHILE ~r.eot DO IF ch = LF THEN linStart := TRUE ELSIF ch # " " THEN linStart := FALSE; (* --- Pass by comments *) WHILE ~r.eot & (ch = "(") DO Texts.Read(r, ch); IF ch = "*" THEN passComments(r) END END; (* --- Pass by string & character constants *) IF (ch = "'") OR (ch = '"') THEN ech := ch; REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch = ech) END ELSIF ~linStart THEN start := Texts.Pos(r); REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch # " "); end := Texts.Pos(r); IF (end - start) > 1THEN Texts.Delete(bText, start, end - 1) END; Texts.OpenReader(r, bText, start) END; Texts.Read(r, ch) END END RemSpaces;  PROCEDURE SpaceOps*;  VAR bText: Texts.Text; oldNotifier: Texts.Notifier; BEGIN ParseCmdLine(bText, oldNotifier); IF bText = NIL THEN Out.String("Usage: Beautifier.SpaceOps * | ^ | Filename.Mod"); Out.Ln ELSE FoldElems.ExpandAll(bText, 0, TRUE); SpaceOperators(bText); SpaceFormParms(bText); RemSpaces(bText); FoldElems.CollapseAll(bText, {FoldElems.tempLeft}); UpdateText(bText, oldNotifier) END END SpaceOps;  PROCEDURE Beautify*;  VAR bText: Texts.Text; oldNotifier: Texts.Notifier; expNamFnt, cmtFnt: Fonts.Font; BEGIN ParseCmdLine(bText, oldNotifier); IF bText = NIL THEN Out.String("Usage: Beautifier.SpaceOps * | ^ | Filename.Mod"); Out.Ln ELSE GetFontsFromText(bText, expNamFnt, cmtFnt); FoldElems.ExpandAll(bText, 0, TRUE); ChangeFont(bText, expNamFnt, cmtFnt); IndentCheck(bText); RemSemicolons(bText); FoldProcedures(bText); SpaceOperators(bText); SpaceFormParms(bText); FoldElems.CollapseAll(bText, {FoldElems.tempLeft}); UpdateText(bText, oldNotifier); END END Beautify;  PROCEDURE InitHashTable;  VAR i: INTEGER; BEGIN (* --- empty the Character Functions *) FOR i := 0 TO 22 DO fC[i] := 0; lC[i] := 0 END; (* --- empty Hash-Table *) FOR i := 0 TO 50 DO COPY("", hashT[i].word) END (* --- Set Character Functions *) ; fC[1] := 0; fC[2] := 1; fC[4] := 2; fC[5] := 3; fC[8] := 4; fC[11] := 5; fC[15] := 6; fC[17] := 7; fC[20] := 8; fC[21] := 9; fC[22] := 10; lC[3] := 0; lC[4] := 1; lC[5] := 2; lC[7] := 3; lC[11] := 4; lC[13] := 5; lC[15] := 6; lC[17] := 7; lC[19] := 8; (* --- Put Keywords into hashtable *) hashT[1].Init("UNTIL", IEK); hashT[2].Init("END", IEK); hashT[4].Init("BEGIN", IK); (* only if ~section , else BEGIN is IBK *) hashT[7].Init("RECORD", IK); hashT[11].Init("TYPE", SK); hashT[12].Init("CASE", IK); hashT[13].Init("ELSE", IBK); hashT[17].Init("PROCEDURE", SK); (* only if not a type definition *) hashT[20].Init("LOOP", IK); hashT[21].Init("WHILE", IK); hashT[24].Init("ELSIF", IBK); hashT[26].Init("IF", IK); hashT[29].Init("FOR", IK); hashT[35].Init("VAR", IBK); (* only if nextCh = LF *) hashT[38].Init("CONST", SK); hashT[41].Init("IMPORT", SK); hashT[43].Init("WITH", IK); hashT[44].Init("REPEAT", IK) END InitHashTable;  BEGIN Texts.OpenWriter(w); NEW(b); Texts.OpenBuf(b); InitHashTable END Beautifier.