ack/lang/a68s/util/indent.p
1988-10-04 10:33:39 +00:00

429 lines
15 KiB
OpenEdge ABL

(*$R-,L-*)
PROGRAM INDENT(SOURCE, INPUT, OUTPUT);
CONST
SMALLINDENT=2; MIDINDENT=2; LARGEINDENT=4;
TYPE
STATETYPE =
(OPENER, MIDDLER, CLOSER, PRAGMENT, DOER, QUOTE, COLON, GO, STROP, OTHER);
CLAUSETYPE =
(BRIEF, CONDCL, CASECL, CLOSEDCL, LOOPCL, INDEXER, ROUTINE, JUMP,
EXIT, SEMICOMMA, STRING, HASH, CO, COMMENT, PR, PRAGMAT, UPPER, POINT, ANY);
TREEP=^TREE;
TREE=RECORD
(*TREE TO HOLD RESERVED WORD DICTIONARY*)
C: CHAR;
LEFT, RIGHT, NEXT: TREEP;
TIP: BOOLEAN;
ST: STATETYPE; CL: CLAUSETYPE;
END;
STACKP=^STACK;
STACK=PACKED RECORD
C: CLAUSETYPE; G: BOOLEAN;
NEXT: STACKP
END;
ALFA=PACKED ARRAY [1..10] OF CHAR;
VAR
SOURCE: TEXT;
ROOT: TREEP;
TOS: STACKP;
VETTEDCHARACTER: RECORD
WORD: PACKED ARRAY [1..80] OF CHAR; (*THE LONGEST CONCEIVABLE BOLDWORD!*)
INDEX: 0..80;
END;
STARTOFLINE,
LINENUMBERS: BOOLEAN; (*TRUE IFF THE SOURCE TEXT INCLUDES LINE NUMBERS*)
I: INTEGER;
INDENT, (*EXPECTED INDENT FOR SUBSEQUENT LINES*)
TEMPINDENT: INTEGER; (*INDENT FOR CURRENT LINE*)
INSTRAGMENT: BOOLEAN;
STROPSTATE: (INPOINT, INUPPER, INPRAGP, INPRAGUP);
GONEON: BOOLEAN; (*TRUE IFF THE LAST TOKEN WAS AN OPENER OR A MIDDLER*)
(**)
(**)
(**)
PROCEDURE SETUPTREE;
(*TO CREATE THE DICTIONARY*)
PROCEDURE INSERT(WORD: ALFA; S: STATETYPE; B: CLAUSETYPE);
VAR TREEPTR: TREEP; INDEX: INTEGER; FOUND: BOOLEAN;
BEGIN TREEPTR := ROOT; INDEX := 1;
WHILE WORD[INDEX]<>' ' DO
BEGIN
WITH TREEPTR^ DO
BEGIN
IF TREEPTR^.NEXT=NIL THEN
BEGIN NEW(NEXT); WITH NEXT^ DO
BEGIN C := WORD[INDEX];
LEFT := NIL; RIGHT := NIL; TIP := FALSE; NEXT := NIL
END
END;
TREEPTR := NEXT
END;
FOUND := FALSE;
WHILE NOT FOUND DO WITH TREEPTR^ DO
IF WORD[INDEX]<C THEN
BEGIN
IF LEFT=NIL THEN
BEGIN NEW(LEFT); WITH LEFT^ DO
BEGIN C := WORD[INDEX];
LEFT := NIL; RIGHT := NIL; TIP := FALSE; NEXT := NIL
END;
FOUND := TRUE
END;
TREEPTR := LEFT
END
ELSE IF WORD[INDEX]>C THEN
BEGIN
IF RIGHT=NIL THEN
BEGIN NEW(RIGHT); WITH RIGHT^ DO
BEGIN C := WORD[INDEX];
LEFT := NIL; RIGHT := NIL; TIP := FALSE; NEXT := NIL
END;
FOUND := TRUE
END;
TREEPTR := RIGHT
END
ELSE FOUND := TRUE;
INDEX := INDEX+1
END;
WITH TREEPTR^ DO
BEGIN TIP := TRUE; ST := S; CL := B END
END (*INSERT*);
(**)
BEGIN (*SETUPTREE*)
NEW(ROOT); ROOT^.NEXT := NIL;
INSERT('( ', OPENER , BRIEF );
INSERT('IF ', OPENER , CONDCL );
INSERT('if ', OPENER , CONDCL );
INSERT('CASE ', OPENER , CASECL );
INSERT('case ', OPENER , CASECL );
INSERT('BEGIN ', OPENER , CLOSEDCL );
INSERT('begin ', OPENER , CLOSEDCL );
INSERT('[ ', OPENER , INDEXER );
INSERT('! ', MIDDLER , BRIEF );
INSERT('THEN ', MIDDLER , CONDCL );
INSERT('then ', MIDDLER , CONDCL );
INSERT('IN ', MIDDLER , CASECL );
INSERT('in ', MIDDLER , CASECL );
INSERT('ELIF ', MIDDLER , CONDCL );
INSERT('elif ', MIDDLER , CONDCL );
INSERT('ELSE ', MIDDLER , CONDCL );
INSERT('else ', MIDDLER , CONDCL );
INSERT('OUSE ', MIDDLER , CASECL );
INSERT('ouse ', MIDDLER , CASECL );
INSERT('OUT ', MIDDLER , CASECL );
INSERT('out ', MIDDLER , CASECL );
INSERT('EXIT ', MIDDLER , EXIT );
INSERT('exit ', MIDDLER , EXIT );
INSERT('; ', MIDDLER , SEMICOMMA);
INSERT(', ', MIDDLER , SEMICOMMA);
INSERT(') ', CLOSER , BRIEF );
INSERT('FI ', CLOSER , CONDCL );
INSERT('fi ', CLOSER , CONDCL );
INSERT('ESAC ', CLOSER , CASECL );
INSERT('esac ', CLOSER , CASECL );
INSERT('END ', CLOSER , CLOSEDCL );
INSERT('end ', CLOSER , CLOSEDCL );
INSERT('] ', CLOSER , INDEXER );
INSERT('# ', PRAGMENT, HASH );
INSERT('CO ', PRAGMENT, CO );
INSERT('co ', PRAGMENT, CO );
INSERT('COMMENT ', PRAGMENT, COMMENT );
INSERT('comment ', PRAGMENT, COMMENT );
INSERT('PR ', PRAGMENT, PR );
INSERT('pr ', PRAGMENT, PR );
INSERT('PRAGMAT ', PRAGMENT, PRAGMAT );
INSERT('pragmat ', PRAGMENT, PRAGMAT );
INSERT('FOR ', DOER , LOOPCL );
INSERT('for ', DOER , LOOPCL );
INSERT('FROM ', DOER , LOOPCL );
INSERT('from ', DOER , LOOPCL );
INSERT('BY ', DOER , LOOPCL );
INSERT('by ', DOER , LOOPCL );
INSERT('TO ', DOER , LOOPCL );
INSERT('to ', DOER , LOOPCL );
INSERT('WHILE ', DOER , LOOPCL );
INSERT('while ', DOER , LOOPCL );
INSERT('DO ', DOER , LOOPCL );
INSERT('do ', DOER , LOOPCL );
INSERT('OD ', CLOSER , LOOPCL );
INSERT('od ', CLOSER , LOOPCL );
INSERT('GO ', GO , JUMP );
INSERT('go ', GO , JUMP );
INSERT('" ', QUOTE , STRING );
INSERT('UPPER ', STROP , UPPER );
INSERT('upper ', STROP , UPPER );
INSERT('POINT ', STROP , POINT );
INSERT('point ', STROP , POINT );
(*':' AFTER BOLD , COLON , ROUTINE ); *)
END;
(**)
(**)
PROCEDURE PUSH(CL: CLAUSETYPE);
VAR TEMP: STACKP;
BEGIN TEMP := TOS; NEW(TOS); WITH TOS^ DO
BEGIN C := CL; G := GONEON; NEXT := TEMP END
END;
(**)
(**)
PROCEDURE POP;
VAR TEMP: STACKP;
BEGIN
IF NOT GONEON AND NOT INSTRAGMENT THEN INDENT := INDENT-MIDINDENT;
TEMP := TOS; GONEON := TOS^.G; TOS := TOS^.NEXT; DISPOSE(TEMP)
END;
(**)
(**)
PROCEDURE VET(VAR SOURCE: TEXT);
(*MOVES NEXT INTERESTING TOKEN TO VETTED CHARACTER,
AND SETS INDENT AND TEMPINDENT ACCORDINGLY*)
VAR TREEPTR: TREEP;
CH: CHAR;
STATE: STATETYPE;
CLAUSE: CLAUSETYPE;
BOLD, FOUND: BOOLEAN;
(**)
PROCEDURE GAP(VAR SOURCE: TEXT);
(*ENSURE THAT AT LEAST (SMALLINDENT-1) BLANKS ARE PRESENT IN OUTPUT*)
VAR I: INTEGER;
BEGIN
I := SMALLINDENT-1;
WHILE NOT EOLN(SOURCE) AND (SOURCE^=' ') AND (I>0) DO
BEGIN GET(SOURCE); I := I-1 END;
IF NOT EOLN(SOURCE) THEN
FOR I := 2 TO SMALLINDENT DO WITH VETTEDCHARACTER DO
BEGIN WORD[I] := ' '; INDEX := I END
END;
(**)
PROCEDURE CHECK(CLAUSE: CLAUSETYPE);
BEGIN WITH TOS^ DO
IF C<>CLAUSE THEN (*ATTEMPT TO FIX BRACKETS MISMATCH*)
IF NEXT^.C=CLAUSE THEN (*ASSUME CLOSER WAS OMITTED*)
BEGIN
IF C IN [BRIEF, INDEXER] THEN INDENT := INDENT-SMALLINDENT
ELSE INDENT := INDENT-LARGEINDENT;
POP;
IF GONEON THEN
BEGIN GONEON := FALSE; INDENT := INDENT+MIDINDENT END
END
ELSE (*ASSUME OPENER WAS OMITTED*)
BEGIN
IF CLAUSE IN [BRIEF, INDEXER] THEN INDENT := INDENT+SMALLINDENT
ELSE INDENT := INDENT+LARGEINDENT;
IF NOT GONEON THEN
BEGIN GONEON := TRUE; INDENT := INDENT-MIDINDENT END;
PUSH(CLAUSE)
END
END;
(**)
BEGIN (*VET*)
(*ASSERT: (SOURCE^ IN [(!)[],.#";]) OR (UPPER & SOURCE^ IN [A..Z]) OR INPRAGMAT*)
CH := SOURCE^;
TEMPINDENT := INDENT;
VETTEDCHARACTER.INDEX := 0;
CASE STROPSTATE OF
INPOINT: BOLD := CH='.';
INUPPER: BOLD := CH IN ['.','A'..'Z'];
INPRAGUP,INPRAGP: BOLD := CH IN ['.','A'..'Z','a'..'z'];
END;
IF CH='.' THEN WITH VETTEDCHARACTER DO
BEGIN INDEX := 1; WORD[1] := '.'; GET(SOURCE); CH := SOURCE^ END;
TREEPTR := ROOT^.NEXT; FOUND := FALSE;
WHILE (TREEPTR<>NIL) AND NOT FOUND DO WITH TREEPTR^ DO
IF C=CH THEN WITH VETTEDCHARACTER DO
BEGIN
INDEX := INDEX+1; WORD[INDEX] := CH;
GET(SOURCE); CH := SOURCE^;
IF BOLD THEN
CASE STROPSTATE OF
INPRAGUP,INPRAGP,INPOINT: FOUND := NOT(CH IN ['A'..'Z', 'a'..'z']) AND TIP;
INUPPER: FOUND := NOT(CH IN ['A'..'Z']) AND TIP;
END
ELSE FOUND := TIP;
IF NOT FOUND THEN TREEPTR := NEXT
END
ELSE IF CH<C THEN TREEPTR := LEFT
ELSE TREEPTR := RIGHT;
IF FOUND THEN WITH TREEPTR^ DO
BEGIN STATE := ST; CLAUSE := CL END
ELSE WITH VETTEDCHARACTER DO
BEGIN
IF BOLD THEN
WHILE (CH IN ['A'..'Z', 'a'..'z']) DO
(*ABSORB REMAINDER OF UNRECOGNIZED BOLDWORD*)
BEGIN INDEX := INDEX+1; WORD[INDEX] := CH; GET(SOURCE); CH := SOURCE^ END
ELSE
BEGIN INDEX := INDEX+1; WORD[INDEX] := CH; GET(SOURCE); CH := SOURCE^ END;
IF (CH=':') AND NOT INSTRAGMENT THEN WITH VETTEDCHARACTER DO
(*START OF ROUTINE-TEXT*)
BEGIN STATE := COLON; CLAUSE := ROUTINE;
INDEX := INDEX+1; WORD[INDEX] := CH; GET(SOURCE)
END
ELSE BEGIN STATE := OTHER; CLAUSE := ANY END
END;
(**)
IF INSTRAGMENT THEN
IF (CLAUSE=TOS^.C) THEN
(*MATCHING CLOSE-STRAGMENT-TOKEN FOUND*)
BEGIN
IF STROPSTATE IN [INPRAGUP,INPRAGP] THEN
STROPSTATE := PRED(PRED(STROPSTATE));
POP;
INSTRAGMENT := FALSE;
IF CLAUSE=HASH THEN INDENT := INDENT-SMALLINDENT
ELSE IF CLAUSE<>STRING THEN INDENT := INDENT-LARGEINDENT;
TEMPINDENT := INDENT
END
ELSE IF (STROPSTATE IN [INPRAGUP,INPRAGP]) AND (STATE=STROP) THEN
IF CLAUSE=UPPER THEN STROPSTATE := INPRAGUP ELSE STROPSTATE := INPRAGP
ELSE (*NO ACTION*)
ELSE (*NOT INSTRAGMENT*)
BEGIN
IF STATE IN [MIDDLER, CLOSER] THEN (*MAYBE END OF ROUTINE-TEXT*)
WHILE TOS^.C=ROUTINE DO
BEGIN
POP; INDENT := INDENT-SMALLINDENT;
IF GONEON THEN
BEGIN GONEON := FALSE; INDENT := INDENT+MIDINDENT END
END;
(**)
IF STATE=GO THEN (*.GO OF .GO .TO*)
BEGIN PUSH(JUMP); STATE := OTHER END
ELSE IF STATE=DOER THEN (*CHANGE IT TO MIDDLER OR OPENER*)
IF TOS^.C=JUMP THEN (*.TO OF .GO .TO*)
BEGIN POP; STATE := OTHER END
ELSE IF (TOS^.C=LOOPCL) AND NOT GONEON THEN STATE := MIDDLER
ELSE STATE := OPENER;
(**)
IF STATE=COLON THEN (*START OF ROUTINE-TEXT*)
BEGIN
IF NOT GONEON THEN
BEGIN GONEON := TRUE; INDENT := INDENT-MIDINDENT END;
PUSH(CLAUSE);
INDENT := INDENT+SMALLINDENT
END
ELSE IF STATE=OPENER THEN (*START OF A NEW INDENT*)
BEGIN
PUSH(CLAUSE);
IF CLAUSE IN [BRIEF, INDEXER] THEN
BEGIN INDENT := INDENT+SMALLINDENT; IF STARTOFLINE THEN GAP(SOURCE) END
ELSE INDENT := INDENT+LARGEINDENT;
GONEON := TRUE
END
ELSE IF STATE=MIDDLER THEN
BEGIN
IF NOT (CLAUSE IN [EXIT, SEMICOMMA]) THEN CHECK(CLAUSE);
IF NOT GONEON THEN
BEGIN GONEON := TRUE; INDENT := INDENT-MIDINDENT END;
IF CLAUSE=SEMICOMMA THEN
BEGIN TEMPINDENT := INDENT-SMALLINDENT; GAP(SOURCE) END
ELSE IF TOS^.C=BRIEF THEN
(* ! OR !: OR .EXIT AFTER ( *)
BEGIN TEMPINDENT := INDENT-SMALLINDENT;
IF STARTOFLINE AND (SOURCE^<>':') AND (CLAUSE<>EXIT) THEN GAP(SOURCE)
END
ELSE TEMPINDENT := INDENT-LARGEINDENT
END
ELSE IF STATE=CLOSER THEN (*END OF INDENT*)
BEGIN
CHECK(CLAUSE); POP;
IF CLAUSE IN [BRIEF, INDEXER] THEN INDENT := INDENT-SMALLINDENT
ELSE INDENT := INDENT-LARGEINDENT;
TEMPINDENT := INDENT;
IF GONEON THEN
BEGIN GONEON := FALSE; INDENT := INDENT+MIDINDENT END
END
ELSE IF STATE=PRAGMENT THEN
BEGIN
TEMPINDENT := INDENT;
PUSH(CLAUSE);
INSTRAGMENT := TRUE;
IF CLAUSE IN [PR,PRAGMAT] THEN
STROPSTATE := SUCC(SUCC(STROPSTATE));
IF CLAUSE=HASH THEN
BEGIN INDENT := INDENT+SMALLINDENT; IF STARTOFLINE THEN GAP(SOURCE) END
ELSE INDENT := INDENT+LARGEINDENT
END
ELSE IF STATE=QUOTE THEN
BEGIN
IF GONEON THEN
BEGIN GONEON := FALSE; INDENT := INDENT+MIDINDENT END;
PUSH(STRING);
INSTRAGMENT := TRUE
END
ELSE (*STATE=OTHER*)
IF GONEON THEN
BEGIN GONEON := FALSE; INDENT := INDENT+MIDINDENT END
END
END (*OF VET*);
(**)
(**)
PROCEDURE MAIN(VAR SOURCE: TEXT);
VAR I: INTEGER;
BEGIN
INDENT := 0; INSTRAGMENT := FALSE;
STROPSTATE := INUPPER; (*THE DEFAULT is UPPER*)
GONEON := TRUE;
SETUPTREE;
LINENUMBERS := SOURCE^ IN ['0'..'9'];
TOS := NIL; PUSH(ANY); PUSH(ANY);
WHILE NOT EOF(SOURCE) DO
BEGIN
WHILE EOLN(SOURCE) DO BEGIN GET(SOURCE); WRITELN(OUTPUT) END;
BEGIN
STARTOFLINE := TRUE;
IF LINENUMBERS THEN
BEGIN
WHILE SOURCE^ IN ['0'..'9'] DO
BEGIN WRITE(OUTPUT, SOURCE^); GET(SOURCE) END;
IF NOT EOLN(SOURCE) AND (SOURCE^=' ') THEN (*FIRST BLANK AFTER LINE NUMBER IS OBLIGATORY*)
BEGIN WRITE(OUTPUT, ' '); GET(SOURCE) END
END;
IF TOS^.C=STRING THEN
(*DO NOT TINKER WITH BLANKS INSIDE STRING-DENOTATIONS*)
BEGIN
WHILE NOT EOLN(SOURCE) AND (SOURCE^=' ') DO
BEGIN WRITE(OUTPUT, ' '); GET(SOURCE) END;
STARTOFLINE := FALSE
END
ELSE WHILE NOT EOLN(SOURCE) AND (SOURCE^=' ') DO
GET(SOURCE); (*GET RID OF EXISTING INDENTATION*)
WHILE NOT EOLN(SOURCE) DO
BEGIN
IF (SOURCE^ IN ['(','!',')','[',']',',','.','#','"',';']) OR
((STROPSTATE<>INPOINT) AND (SOURCE^ IN ['A'..'Z'])) OR
(STROPSTATE IN [INPRAGUP,INPRAGP]) THEN
(*CHARACTER WHICH MIGHT AFFECT INDENTATION*)
BEGIN
VET(SOURCE);
IF STARTOFLINE THEN FOR I := 1 TO TEMPINDENT DO WRITE(OUTPUT, ' ');
WITH VETTEDCHARACTER DO
FOR I := 1 TO INDEX DO WRITE(OUTPUT, WORD[I])
END
ELSE
BEGIN
IF STARTOFLINE THEN FOR I := 1 TO INDENT DO WRITE(OUTPUT, ' ');
IF (SOURCE^<>' ') AND NOT INSTRAGMENT AND GONEON THEN
(*PREPARE TO INDENT ANY CONTINUATION LINE*)
BEGIN GONEON := FALSE; INDENT := INDENT+MIDINDENT END;
WRITE(OUTPUT, SOURCE^); GET(SOURCE);
END;
STARTOFLINE := FALSE
END;
GET(SOURCE); WRITELN(OUTPUT)
END;
END;
END;
(**)
FUNCTION ARGC: INTEGER; EXTERN;
(**)
BEGIN (*INDENT*)
IF ARGC=1 THEN
MAIN(INPUT)
ELSE
BEGIN
RESET(SOURCE);
MAIN(SOURCE);
END;
(*$G-*)
END.