home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 December
/
simtel1292_SIMTEL_1292_Walnut_Creek.iso
/
msdos
/
ddjmag
/
ddj8812.arc
/
PXREF.ASC
< prev
next >
Wrap
Text File
|
1988-12-31
|
19KB
|
585 lines
_STRUCTURED PROGRAMMING COLUMN_
by Kent Porter
[LISTING 1]
1| Program number;
2|
3| { Puts line numbers at start of each line, stores in file by same
4| name except extension is .NUM }
5|
6| USES crt;
7|
8| VAR Filename, Newname : STRING [80];
9| I, O : TEXT;
10| Line : STRING [135];
11| Nbr, len, p : WORD;
12| Num : STRING [4];
13|
14| BEGIN
15| Nbr := 0;
16| Newname := '';
17| IF ParamCount < 1 THEN BEGIN
18| Writeln ('USAGE: NUMBER <Filename.ext>');
19| EXIT;
20| END;
21| Filename := ParamStr (1);
22|
23| Len := pos ('.', Filename);
24| IF len = 0 THEN
25| Newname := Filename + '.NUM'
26| ELSE BEGIN
27| FOR p := 1 TO len DO
28| Newname := Newname + Filename [p];
29| Newname := Newname + 'NUM';
30| END;
31| Assign (I, Filename);
32| {$I-}
33| Reset (I);
34| {$I+}
35| IF IOResult <> 0 THEN BEGIN
36| Writeln ('Unable to open ', Filename);
37| EXIT;
38| END;
39| Assign (O, Newname);
40| Rewrite (O);
41| Writeln;
42|
43| WHILE NOT eof (I) DO BEGIN
44| Readln (I, Line);
45| INC (Nbr);
46| GotoXY (1, WhereY-1); Writeln (Nbr);
47| Str (Nbr:4, Num);
48| Line := Num + '| ' + Line;
49| Writeln (O, Line);
50| END;
51| Close (O);
52| Close (I);
53| GotoXY (1, WhereY-1); Writeln (nbr, ' lines in file');
54| Writeln ('Output is in ', Newname);
55| END.
56|
57|
[LISTING 2]
Case-sensitive symbolic cross-reference for number.pas
0 (3), 15, 24, 35
1 (7), 17, 21, 27, 46, 46, 53, 53
135 (1), 10
4 (2), 12, 47
80 (1), 8
Assign (2), 31, 39
Close (2), 51, 52
crt (1), 6
eof (1), 43
EXIT (2), 19, 37
Filename (7), 8, 21, 23, 25, 28, 31, 36
GotoXY (2), 46, 53
I (6), 9, 31, 33, 43, 44, 52
IOResult (1), 35
Len (1), 23
len (3), 11, 24, 27
Line (5), 10, 44, 48, 48, 49
Nbr (5), 11, 15, 45, 46, 47
nbr (1), 53
Newname (9), 8, 16, 25, 28, 28, 29, 29, 39, 54
NOT (1), 43
Num (3), 12, 47, 48
number (1), 1
O (5), 9, 39, 40, 49, 51
p (3), 11, 27, 28
ParamCount (1), 17
ParamStr (1), 21
pos (1), 23
Program (1), 1
Readln (1), 44
Reset (1), 33
Rewrite (1), 40
Str (1), 47
STRING (3), 8, 10, 12
TEXT (1), 9
USES (1), 6
WhereY (2), 46, 53
WORD (1), 11
Writeln (7), 18, 36, 41, 46, 49, 53, 54
-- 40 symbols reported
[LISTING 3]
1| PROGRAM Xref;
2|
3| { Builds and lists a Pascal/Modula-2 symbol cross-reference report }
4| { Uses binary trees and doubly-linked lists to effect B-Tree }
5| { Command line is XREF <filename.ext> [/C|/N] }
6| { /C makes xref case-sensitive }
7| { /N makes it non-case sensitive (default) }
8| { Turbo Pascal 5.0 (4.0 will work, too) }
9| { K. Porter, DDJ, December '88 Structured Programming Column }
10|
11| USES crt, printer;
12|
13| TYPE SymString = STRING [39];
14| CharSet = SET OF CHAR;
15| LineString = STRING [135];
16| XLinePtr = ^XLineNode; { Pointer to xref line number node }
17| XLineNode = RECORD { Xref line number structure (SLL) }
18| Line : WORD;
19| Next : XLinePtr;
20| END;
21|
22| SymTreePtr = ^SymTreeNode; { Pointer to symbol tree node }
23| SymTreeNode = RECORD { Binary tree symbol node }
24| Symbol : SymString;
25| UCsymbol : SymString;
26| Count : WORD;
27| XList : XLinePtr;
28| LLink, RLink : SymTreePtr;
29| END;
30|
31| CONST Quote = #39;
32| DQuote = #34;
33| Eject = #12;
34| SymChars : CharSet = ['0'..'9','A'..'Z','a'..'z','.','_','^'];
35| PComment : CharSet = ['{', '}', '(', '*', ')', Quote, DQuote];
36| Heading = ' symbolic cross-reference for ';
37|
38| VAR Filepath : STRING [80];
39| Case_Sensitive : BOOLEAN;
40| F : TEXT;
41| Head, Alpha : SymTreePtr;
42| CommentLevel : WORD;
43| Line : LineString;
44| LineNumber : WORD;
45| NSymbols : WORD;
46| { ------------------------------------------------------------------ }
47|
48| PROCEDURE FindEndOfComment (VAR line : LineString;
49| VAR i : WORD;
50| eoc : CHAR);
51| { Scan until end of current comment is found }
52|
53| VAR ch : CHAR;
54| Searching : BOOLEAN;
55|
56| BEGIN
57| Searching := TRUE;
58| WHILE Searching DO BEGIN
59| WHILE i <= Length (Line) DO BEGIN
60| ch := Line [i];
61| INC (i);
62| IF ch = eoc THEN
63| CASE eoc OF
64| '}': Searching := FALSE;
65| '*': IF line [i] = ')' THEN BEGIN
66| Searching := FALSE;
67| INC (i);
68| END;
69| Quote: Searching := FALSE;
70| DQuote: Searching := FALSE;
71| END;
72|
73| IF Searching = FALSE THEN BEGIN
74| DEC (CommentLevel);
75| EXIT;
76| END;
77| END;
78|
79| { If we get here, the comment continues on the next line }
80| Readln (F, Line);
81| i := 1;
82| INC (LineNumber);
83| END;
84| END;
85| { --------------------------- }
86|
87| FUNCTION UpShift (VAR Symbol : SymString) : SymString;
88| { Return upshifted version of passed string }
89|
90| VAR p : INTEGER;
91| s : SymString;
92|
93| BEGIN
94| s := '';
95| FOR p := 1 TO Length (Symbol) DO
96| s := s + UpCase (Symbol [p]);
97| UpShift := s;
98| END;
99| { --------------------------- }
100|
101| FUNCTION NewNode (VAR Symbol : SymString) : SymTreePtr;
102| { Allocate and set up new symbol node, return pointer }
103|
104| VAR node : SymTreePtr;
105|
106| BEGIN
107| NEW (node);
108| Node^.Symbol := Symbol;
109| Node^.UCSymbol := UpShift (Symbol);
110| Node^.Count := 1;
111| Node^.XList := NIL;
112| Node^.RLink := NIL;
113| Node^.LLink := NIL;
114| Node^.RLink := NIL;
115| NewNode := node;
116| END;
117| { --------------------------- }
118|
119| FUNCTION Token (VAR line : LineString;
120| VAR i : WORD) : SymString;
121| { Return next symbol or keyword from line }
122| { Index to next char returned as a side-effect }
123| { Also checks for comments }
124|
125| VAR sym : SymString;
126| ch, ScanChar : CHAR;
127| nch : WORD;
128|
129| BEGIN
130| { Scan for first valid alphanumeric or for comment }
131| ScanChar := #0;
132| WHILE (NOT (Line [i] IN SymChars)) AND (i <= Length (line)) DO BEGIN
133| ch := line [i];
134| INC (i);
135| IF ch IN PComment THEN BEGIN
136| CASE ch OF
137| Quote: BEGIN
138| INC (CommentLevel);
139| ScanChar := Quote;
140| END;
141| '{': BEGIN
142| INC (CommentLevel);
143| ScanChar := '}';
144| END;
145| '}': IF CommentLevel > 0 THEN
146| DEC (CommentLevel);
147| '(': IF line [i] = '*' THEN BEGIN
148| INC (CommentLevel);
149| ScanChar := '*';
150| INC (i);
151| END;
152| '*': IF line [i] = ')' THEN
153| IF CommentLevel > 0 THEN BEGIN
154| DEC (CommentLevel);
155| INC (i);
156| END;
157| END;
158| IF CommentLevel > 0 THEN
159| FindEndOfComment (line, i, ScanChar);
160| END;
161| END;
162|
163| { Pull out the symbol }
164| sym := '';
165| nch := 1;
166| IF i < Length (Line) THEN
167| REPEAT
168| ch := Line [i];
169| IF ch IN SymChars THEN BEGIN
170| IF (ch = '^') AND (nch = 1) THEN
171| { Skip leading pointer char }
172| ELSE BEGIN
173| sym := sym + ch;
174| INC (nch);
175| END;
176| INC (i);
177| END;
178| UNTIL (NOT (ch IN SymChars)) OR (i > Length (Line));
179| IF NOT Case_Sensitive THEN
180| Token := UpShift (sym)
181| ELSE
182| Token := sym;
183| END;
184| { --------------------------- }
185|
186| FUNCTION BNode (VAR sym : SymString) : SymTreePtr;
187| { Find sym's node in binary tree, or add it if it doesn't exist }
188|
189| VAR Node, Parent : SymTreePtr;
190|
191| BEGIN
192| Node := Head;
193| WHILE ((Node^.Symbol <> sym) AND (Node <> NIL)) DO BEGIN
194| Parent := Node;
195| IF sym < Node^.Symbol THEN
196| Node := Node^.LLink
197| ELSE
198| Node := Node^.RLink
199| END;
200| IF Node <> NIL THEN { Node exists for this symbol }
201| INC (Node^.Count)
202| ELSE BEGIN { Else add new node to binary tree }
203| Node := NewNode (sym);
204| IF sym < Parent^.Symbol THEN { Update parent's pointer }
205| Parent^.LLink := Node
206| ELSE
207| Parent^.RLink := Node
208| END;
209| BNode := Node;
210| END;
211| { --------------------------- }
212|
213| PROCEDURE Append (Target : SymTreePtr; LineNbr : WORD);
214| { Add line cross-ref to target's dependent list }
215|
216| VAR XR, Parent : XLinePtr;
217|
218| BEGIN
219| IF Target^.XList = NIL THEN BEGIN { First occurrence of symbol }
220| NEW (XR);
221| XR^.Line := LineNbr;
222| XR^.Next := NIL;
223| Target^.XList := XR;
224| END
225| ELSE BEGIN { Append to end of existing list }
226| XR := Target^.Xlist;
227| REPEAT
228| Parent := XR;
229| XR := XR^.Next
230| UNTIL XR = NIL; { Find list's tail }
231| NEW (XR); { Append there }
232| XR^.Line := LineNbr;
233| XR^.Next := NIL;
234| Parent^.Next := XR;
235| END;
236| END;
237| { --------------------------- }
238|
239| PROCEDURE AddToTree (VAR Symbol : SymString; LineNbr : WORD);
240| { Place symbol into binary tree, add line xref to dependent list }
241|
242| VAR Target : SymTreePtr;
243|
244| BEGIN
245| IF Head = NIL THEN BEGIN { The tree is empty, so start it }
246| Head := NewNode (Symbol); { Build first binary node }
247| Append (Head, LineNbr); { Build first XREF node }
248| END
249| ELSE BEGIN
250| Target := BNode (Symbol);
251| Append (Target, LineNbr);
252| END;
253| END;
254| { --------------------------- }
255|
256| PROCEDURE Process (VAR Line : LineString);
257| { Controls parsing and construction of BTree }
258|
259| VAR Symbol : SymString;
260| p, oldp : WORD;
261|
262| BEGIN
263| p := 1;
264| IF Length (Line) > 0 THEN
265| WHILE p <= Length (Line) DO BEGIN
266| oldp := p;
267| Symbol := Token (line, p); { Get symbol }
268| IF Symbol = 'BEGIN' THEN Symbol := '' { Weed out nuisances }
269| ELSE IF Symbol = 'END' THEN Symbol := ''
270| ELSE IF Symbol = 'IF' THEN Symbol := ''
271| ELSE IF Symbol = 'THEN' THEN Symbol := ''
272| ELSE IF Symbol = 'ELSE' THEN Symbol := ''
273| ELSE IF Symbol = 'DO' THEN Symbol := ''
274| ELSE IF Symbol = 'WHILE' THEN Symbol := ''
275| ELSE IF Symbol = 'FOR' THEN Symbol := ''
276| ELSE IF Symbol = 'TO' THEN Symbol := ''
277| ELSE IF Symbol = 'VAR' THEN Symbol := ''
278| ELSE IF Symbol = 'INC' THEN Symbol := ''
279| ELSE IF Symbol = 'DEC' THEN Symbol := ''
280| ELSE IF Symbol = 'OF' THEN Symbol := ''
281| ELSE IF Symbol = 'PROGRAM' THEN Symbol := ''
282| ELSE IF Symbol = 'END.' THEN Symbol := '';
283| IF Length (Symbol) > 0 THEN
284| AddToTree (Symbol, LineNumber); { Place info in BTree }
285| IF p = oldp THEN INC (p); { Prevents endless loop }
286| END;
287| END;
288| { --------------------------- }
289|
290| PROCEDURE Report (Node : SymTreePtr);
291| { Print symbol cross-reference listing }
292| { In-order (recursive) traversal of binary tree, printing the info
293| and dependent list for each node }
294|
295| VAR Col, Width : WORD;
296| Lnode : XLinePtr;
297|
298| PROCEDURE NewLine;
299| { Control pagination }
300| BEGIN
301| Writeln (LST);
302| Col := 0;
303| INC (LineNumber);
304| IF LineNumber > 58 THEN BEGIN
305| Write (LST, Eject);
306| Writeln (LST, 'Continuing cross-reference for ', Filepath);
307| Writeln (LST);
308| LineNumber := 2;
309| END;
310| END; { End nested procedure }
311|
312| BEGIN
313| IF node <> NIL THEN BEGIN
314| Report (Node^.LLink); { Follow left-hand path }
315|
316| { Print info from node }
317| Col := 0;
318| Write (LST, Node^.Symbol, ' (', Node^.Count, ')');
319| Col := Col + Length (Node^.Symbol) + 6;
320|
321| { Print line number references }
322| Lnode := Node^.XList;
323| While Lnode <> NIL DO BEGIN
324| IF Col > 0 THEN
325| Write (LST, ', ', Lnode^.Line)
326| ELSE
327| Write (LST, ' ', Lnode^.Line);
328| IF Lnode^.Line < 10 THEN Width := 1
329| ELSE IF Lnode^.Line > 99 THEN Width := 3
330| ELSE Width := 2;
331| Col := Col + Width + 2;
332| IF (Col > 70) AND (Lnode^.Next <> NIL) THEN NewLine;
333| Lnode := Lnode^.Next;
334| END;
335| NewLine;
336|
337| Report (Node^.RLink); { Then follow right-hand path }
338| END;
339| END;
340| { --------------------------- }
341|
342| PROCEDURE Alphabetize (sym : SymTreePtr);
343| { Rearrange tree when case-sensitive so that upper- and lower-case
344| identifiers occur in alphabetic order regardless of case }
345| { RECURSIVE: Traverses symbol table in-order, builds alpha list }
346|
347| PROCEDURE Resort (sym : SymTreePtr);
348| { NESTED: Place new node in tree headed by Alpha pointer }
349|
350| VAR Node, Parent : SymTreePtr;
351| UCsymbol : SymString;
352|
353| BEGIN
354| IF Alpha = NIL THEN BEGIN { Make first node in sorted tree }
355| Alpha := NewNode (sym^.symbol);
356| Alpha^.count := sym^.count;
357| Alpha^.XList := sym^.XList;
358| END
359| ELSE BEGIN { Add new node in order }
360| UCsymbol := UpShift (sym^.symbol);
361| Node := Alpha;
362| WHILE node <> NIL DO BEGIN { Find insertion point }
363| Parent := node;
364| IF UCsymbol < Node^.UCsymbol THEN { based on U/C symbol }
365| Node := Parent^.LLink
366| ELSE
367| Node := Parent^.RLink;
368| END;
369| Node := NewNode (sym^.symbol); { Add node }
370| Node^.Count := sym^.count;
371| Node^.XList := sym^.XList;
372| IF UCsymbol < Parent^.UCsymbol THEN
373| Parent^.LLink := node
374| ELSE
375| Parent^.RLink := node;
376| END;
377| END;
378|
379| BEGIN { Body of Alphabetize }
380| IF sym <> NIL THEN BEGIN
381| Alphabetize (sym^.LLink); { Do nodes to left }
382| Resort (sym); { Realphabetize this node }
383| Alphabetize (sym^.RLink); { Now do nodes to right }
384| Dispose (sym); { All thru with this node }
385| END;
386| END;
387| { --------------------------- }
388|
389| PROCEDURE Count (Node : SymTreePtr);
390| { Count nodes in tree }
391| BEGIN
392| IF node <> NIL THEN BEGIN
393| Count (Node^.LLink);
394| INC (NSymbols);
395| Count (Node^.RLink);
396| END
397| END;
398| { --------------------------- }
399|
400| BEGIN
401| { Initialize }
402| Head := NIL;
403| Alpha := NIL;
404| CommentLevel := 0;
405| LineNumber := 0;
406| NSymbols := 0;
407|
408| { Process command line }
409| IF ParamCount < 1 THEN BEGIN
410| Writeln ('USAGE: XREF <Filename.[ext]> [/C|/N]');
411| EXIT;
412| END;
413| Filepath := ParamStr (1);
414| IF pos ('.', Filepath) = 0 THEN
415| Filepath := Filepath + '.PAS'; { Default is Pascal file }
416| Case_Sensitive := FALSE; { Set default case sensitivity }
417| IF ParamCount = 2 THEN { or alter per command line }
418| IF (ParamStr (2) = '/c') OR (ParamStr (2) = '/C') THEN
419| Case_Sensitive := TRUE;
420|
421| { Open the file }
422| Assign (F, Filepath);
423| {$I-}
424| Reset (F);
425| {$I+}
426| IF IOResult <> 0 THEN BEGIN
427| Writeln ('Unable to open ', Filepath);
428| EXIT;
429| END;
430|
431| { Announce the program }
432| ClrScr;
433| IF Case_Sensitive THEN
434| Write ('Case-sensitive')
435| ELSE
436| Write ('Non-case sensitive');
437| Writeln (Heading, Filepath);
438| Writeln;
439|
440| { Process the file }
441| WHILE NOT eof (F) DO BEGIN
442| Readln (F, line);
443| INC (LineNumber);
444| GotoXY (1, WhereY-1); Writeln (LineNumber); { Meter line number }
445| Process (Line);
446| END;
447| Close (F);
448| GotoXY (1, WhereY-1); Writeln (LineNumber, ' lines in file');
449| IF CommentLevel <> 0 THEN
450| Writeln ('Unbalanced comments detected');
451|
452| { Alphabetize tree into non-ASCII order if case-sensitive }
453| LineNumber := 3;
454| IF Case_Sensitive THEN BEGIN
455| Alphabetize (Head);
456| Writeln (LST, 'Case-sensitive', Heading, Filepath);
457| Writeln (LST);
458| Report (Alpha);
459| Count (Alpha);
460| END
461| ELSE BEGIN
462| Writeln (LST, 'Non-case sensitive', Heading, Filepath);
463| Writeln (LST);
464| Report (Head);
465| Count (Head);
466| END;
467| Writeln (LST);
468| Writeln (LST, '-- ', NSymbols, ' symbols reported');
469| Write (LST, Eject);
470| END.