1 MODULE CRT
; (* H.Moessenboeck 17.11.93, Cocol-R Tables *)
3 IMPORT Texts
, Oberon
, Sets
;
6 maxSymbols
* = 300; (*max nr of t, nt, and pragmas*)
7 maxTerminals
* = 256; (*max nr of terminals*)
8 maxNt
* = 128; (*max nr of nonterminals*)
9 maxNodes
* = 1500; (*max nr of graph nodes*)
10 normTrans
* = 0; contextTrans
* = 1; (*transition codes*)
11 maxSetNr
= 128; (* max. number of symbol sets *)
12 maxClasses
= 50; (* max. number of character classes *)
15 t
* = 1; pr
* = 2; nt
* = 3; class
* = 4; char
* = 5; wt
* = 6; any
* = 7; eps
* = 8; sync
* = 9; sem
* = 10;
16 alt
* = 11; iter
* = 12; opt
* = 13;
22 classToken
* = 0; (*token class*)
23 litToken
* = 1; (*literal (e.g. keyword) not recognized by DFA*)
24 classLitToken
* = 2; (*token class that can also match a literal*)
27 Name
* = ARRAY 16 OF CHAR; (*symbol name*)
28 Position
* = RECORD (*position of stretch of source text*)
29 beg
*: LONGINT; (*start relative to beginning of file*)
30 len
*: INTEGER; (*length*)
31 col
*: INTEGER; (*column number of start position*)
35 typ
*: INTEGER; (*nt, t, pr, unknown*)
36 name
*: Name
; (*symbol name*)
37 struct
*: INTEGER; (*typ = nt: index of 1st node of syntax graph*)
38 (*typ = t: token kind: literal, class, ...*)
39 deletable
*: BOOLEAN; (*typ = nt: TRUE, if nonterminal is deletable*)
40 attrPos
*: Position
; (*position of attributes in source text*)
41 semPos
*: Position
; (*typ = pr: pos of sem action in source text*)
42 (*typ = nt: pos of local decls in source text *)
43 line
*: INTEGER; (*source text line number of item in this node*)
46 Set
* = ARRAY maxTerminals
DIV Sets
.size
OF SET;
49 typ
* : INTEGER; (* nt,sts,wts,char,class,any,eps,sem,sync,alt,iter,opt*)
50 next
*: INTEGER; (* index of successor node *)
51 (* next < 0: to successor in enclosing structure *)
52 p1
*: INTEGER; (* typ IN {nt, t, wt}: index to symbol list *)
53 (* typ = any: index to anyset *)
54 (* typ = sync: index to syncset *)
55 (* typ = alt: index of 1st node of 1st alternative*)
56 (* typ IN {iter, opt}: 1st node in subexpression *)
57 (* typ = char: ordinal character value *)
58 (* typ = class: index of character class *)
59 p2
*: INTEGER; (* typ = alt: index of 1st node of 2nd alternative*)
60 (* typ IN {char, class}: transition code *)
61 pos
*: Position
; (* typ IN {nt, t, wt}: pos of actual attribs *)
62 (* typ = sem: pos of sem action in source text. *)
63 line
*: INTEGER; (* source text line number of item in this node *)
66 MarkList
* = ARRAY maxNodes
DIV Sets
.size
OF SET;
68 FirstSets
= ARRAY maxNt
OF RECORD
69 ts
: Set
; (*terminal symbols*)
70 ready
: BOOLEAN; (*TRUE = ts is complete*)
72 FollowSets
= ARRAY maxNt
OF RECORD
73 ts
: Set
; (*terminal symbols*)
74 nts
: Set
; (*nts whose start set is to be included*)
77 name
: Name
; (*class name*)
78 set
: INTEGER (* ptr to set representing the class*)
80 SymbolTable
= ARRAY maxSymbols
OF SymbolNode
;
81 ClassTable
= ARRAY maxClasses
OF CharClass
;
82 GraphList
= ARRAY maxNodes
OF GraphNode
;
85 maxSet
*: INTEGER; (* index of last set *)
86 maxT
*: INTEGER; (* terminals stored from 0 .. maxT *)
87 maxP
*: INTEGER; (* pragmas stored from maxT+1 .. maxP *)
88 firstNt
*: INTEGER; (* index of first nt: available after CompSymbolSets *)
89 lastNt
*: INTEGER; (* index of last nt: available after CompSymbolSets *)
90 maxC
*: INTEGER; (* index of last character class *)
91 semDeclPos
*: Position
; (*position of global semantic declarations*)
92 importPos
*: Position
; (*position of imported identifiers*)
93 ignored
*: Set
; (* characters ignored by the scanner *)
94 ignoreCase
*: BOOLEAN; (* TRUE: scanner treats lower case as upper case*)
95 ddt
*: ARRAY 10 OF BOOLEAN; (* debug and test switches *)
96 nNodes
*: INTEGER; (* index of last graph node *)
97 root
*: INTEGER; (* index of root node, filled by ATG *)
102 first
: FirstSets
; (*first[i] = first symbols of st[i+firstNt]*)
103 follow
: FollowSets
; (*follow[i] = followers of st[i+firstNt]*)
104 chClass
: ClassTable
; (*character classes*)
105 set
: ARRAY 128 OF Set
; (*set[0] reserved for union of all synchronisation sets*)
106 dummyName
: INTEGER; (*for unnamed character classes*)
108 PROCEDURE ^MovePragmas
;
109 PROCEDURE ^DelNode
*(gn
: GraphNode
): BOOLEAN;
111 PROCEDURE Str(s
: ARRAY OF CHAR);
112 BEGIN Texts
.WriteString(w
, s
)
116 BEGIN Texts
.WriteLn(w
)
119 PROCEDURE Length(s
: ARRAY OF CHAR): INTEGER;
122 i
:=0; WHILE (i
< LEN(s
)) & (s
[i
] # 0X
) DO INC(i
) END;
126 PROCEDURE Restriction(n
: INTEGER);
128 NL
; Str("Restriction "); Texts
.WriteInt(w
, n
, 0); NL
; Texts
.Append(Oberon
.Log
, w
.buf
);
132 PROCEDURE ClearMarkList(VAR m
: MarkList
);
135 i
:= 0; WHILE i
< maxNodes
DIV Sets
.size
DO m
[i
] := {}; INC(i
) END;
138 PROCEDURE GetNode
*(gp
: INTEGER; VAR n
: GraphNode
);
143 PROCEDURE PutNode
*(gp
: INTEGER; n
: GraphNode
);
147 PROCEDURE DelGraph
*(gp
: INTEGER): BOOLEAN;
150 IF gp
= 0 THEN RETURN TRUE
END; (*end of graph found*)
152 RETURN DelNode(gn
) & DelGraph(ABS(gn
.next
));
155 PROCEDURE NewSym
*(typ
: INTEGER; name
: Name
; line
: INTEGER): INTEGER;
158 IF maxT
+ 1 = firstNt
THEN Restriction(6)
161 | t
: INC(maxT
); i
:= maxT
162 | pr
: DEC(maxP
); DEC(firstNt
); DEC(lastNt
); i
:= maxP
163 | nt
: DEC(firstNt
); i
:= firstNt
165 IF maxT
>= maxTerminals
THEN Restriction(6) END;
166 st
[i
].typ
:= typ
; st
[i
].name
:= name
;
167 st
[i
].struct
:= 0; st
[i
].deletable
:= FALSE
;
168 st
[i
].attrPos
.beg
:= -1;
169 st
[i
].semPos
.beg
:= -1;
175 PROCEDURE GetSym
*(sp
: INTEGER; VAR sn
: SymbolNode
);
179 PROCEDURE PutSym
*(sp
: INTEGER; sn
: SymbolNode
);
183 PROCEDURE FindSym
*(name
: Name
): INTEGER;
186 i
:= 0; (*search in terminal list*)
187 WHILE (i
<= maxT
) & (st
[i
].name
# name
) DO INC(i
) END;
188 IF i
<= maxT
THEN RETURN i
END;
189 i
:= firstNt
; (*search in nonterminal/pragma list*)
190 WHILE (i
< maxSymbols
) & (st
[i
].name
# name
) DO INC(i
) END;
191 IF i
< maxSymbols
THEN RETURN i
ELSE RETURN noSym
END
194 PROCEDURE NewSet
*(s
: Set
): INTEGER;
196 INC(maxSet
); IF maxSet
> maxSetNr
THEN Restriction(4) END;
201 PROCEDURE PrintSet(s
: ARRAY OF SET; indent
: INTEGER);
202 CONST maxLineLen
= 80;
203 VAR col
, i
, len
: INTEGER; empty
: BOOLEAN; sn
: SymbolNode
;
205 i
:= 0; col
:= indent
; empty
:= TRUE
;
207 IF Sets
.In(s
, i
) THEN
208 empty
:= FALSE
; GetSym(i
, sn
); len
:= Length(sn
.name
);
209 IF col
+ len
+ 2 > maxLineLen
THEN
211 WHILE col
< indent
DO Texts
.Write(w
, " "); INC(col
) END
213 Str(sn
.name
); Str(" ");
218 IF empty
THEN Str("-- empty set --") END;
219 NL
; Texts
.Append(Oberon
.Log
, w
.buf
)
222 PROCEDURE CompFirstSet
*(gp
: INTEGER; VAR fs
: Set
);
223 VAR visited
: MarkList
;
225 PROCEDURE CompFirst(gp
: INTEGER; VAR fs
: Set
);
226 VAR s
: Set
; gn
: GraphNode
; sn
: SymbolNode
;
229 WHILE (gp
# 0) & ~ Sets
.In(visited
, gp
) DO
230 GetNode(gp
, gn
); Sets
.Incl(visited
, gp
);
233 IF first
[gn
.p1
- firstNt
].ready
THEN
234 Sets
.Unite(fs
, first
[gn
.p1
- firstNt
].ts
);
236 GetSym(gn
.p1
, sn
); CompFirst(sn
.struct
, s
); Sets
.Unite(fs
, s
);
238 | t
, wt
: Sets
.Incl(fs
, gn
.p1
);
239 | any
: Sets
.Unite(fs
, set
[gn
.p1
])
241 CompFirst(gn
.p1
, s
); Sets
.Unite(fs
, s
);
242 IF gn
.typ
= alt
THEN CompFirst(gn
.p2
, s
); Sets
.Unite(fs
, s
) END
243 ELSE (* eps, sem, sync: nothing *)
245 IF ~
DelNode(gn
) THEN RETURN END;
250 BEGIN (* ComputeFirstSet *)
251 ClearMarkList(visited
);
254 NL
; Str("ComputeFirstSet: gp = "); Texts
.WriteInt(w
, gp
, 0); NL
;
259 PROCEDURE CompFirstSets
;
260 VAR i
: INTEGER; sn
: SymbolNode
;
262 i
:= firstNt
; WHILE i
<= lastNt
DO first
[i
-firstNt
].ready
:= FALSE
; INC(i
) END;
264 WHILE i
<= lastNt
DO (* for all nonterminals *)
265 GetSym(i
, sn
); CompFirstSet(sn
.struct
, first
[i
- firstNt
].ts
);
266 first
[i
- firstNt
].ready
:= TRUE
;
271 PROCEDURE CompExpected
*(gp
, sp
: INTEGER; VAR exp
: Set
);
273 CompFirstSet(gp
, exp
);
274 IF DelGraph(gp
) THEN Sets
.Unite(exp
, follow
[sp
- firstNt
].ts
) END
277 PROCEDURE CompFollowSets
;
278 VAR sn
: SymbolNode
; gn
: GraphNode
; curSy
, i
, size
: INTEGER; visited
: MarkList
;
280 PROCEDURE CompFol(gp
: INTEGER);
281 VAR s
: Set
; gn
: GraphNode
;
283 WHILE (gp
> 0) & ~ Sets
.In(visited
, gp
) DO
284 GetNode(gp
, gn
); Sets
.Incl(visited
, gp
);
286 CompFirstSet(ABS(gn
.next
), s
); Sets
.Unite(follow
[gn
.p1
- firstNt
].ts
, s
);
287 IF DelGraph(ABS(gn
.next
)) THEN
288 Sets
.Incl(follow
[gn
.p1
- firstNt
].nts
, curSy
- firstNt
)
290 ELSIF gn
.typ
IN {opt
, iter
} THEN CompFol(gn
.p1
)
291 ELSIF gn
.typ
= alt
THEN CompFol(gn
.p1
); CompFol(gn
.p2
)
297 PROCEDURE Complete(i
: INTEGER);
300 IF Sets
.In(visited
, i
) THEN RETURN END;
301 Sets
.Incl(visited
, i
);
303 WHILE j
<= lastNt
- firstNt
DO (* for all nonterminals *)
304 IF Sets
.In(follow
[i
].nts
, j
) THEN
305 Complete(j
); Sets
.Unite(follow
[i
].ts
, follow
[j
].ts
);
306 Sets
.Excl(follow
[i
].nts
, j
)
312 BEGIN (* CompFollowSets *)
313 curSy
:= firstNt
; size
:= (lastNt
- firstNt
+ 2) DIV Sets
.size
;
314 WHILE curSy
<= lastNt
+ 1 DO (* also for dummy root nt*)
315 Sets
.Clear(follow
[curSy
- firstNt
].ts
);
316 i
:= 0; WHILE i
<= size
DO follow
[curSy
- firstNt
].nts
[i
] := {}; INC(i
) END;
320 curSy
:= firstNt
; (*get direct successors of nonterminals*)
321 WHILE curSy
<= lastNt
DO
322 GetSym(curSy
, sn
); ClearMarkList(visited
); CompFol(sn
.struct
);
325 CompFol(root
); (*curSy=lastNt+1*)
327 curSy
:= 0; (*add indirect successors to follow.ts*)
328 WHILE curSy
<= lastNt
- firstNt
DO
329 ClearMarkList(visited
); Complete(curSy
);
335 PROCEDURE CompAnySets
;
336 VAR curSy
, i
: INTEGER; sn
: SymbolNode
;
338 PROCEDURE LeadingAny(gp
: INTEGER; VAR a
: GraphNode
): BOOLEAN;
341 IF gp
<= 0 THEN RETURN FALSE
END;
343 IF (gn
.typ
= any
) THEN a
:= gn
; RETURN TRUE
344 ELSE RETURN (gn
.typ
= alt
) & (LeadingAny(gn
.p1
, a
) OR LeadingAny(gn
.p2
, a
))
345 OR (gn
.typ
IN {opt
, iter
}) & LeadingAny(gn
.p1
, a
)
346 OR DelNode(gn
) & LeadingAny(gn
.next
, a
)
350 PROCEDURE FindAS(gp
: INTEGER);
351 VAR gn
, gn2
, a
: GraphNode
; s1
, s2
: Set
; p
: INTEGER;
355 IF gn
.typ
IN {opt
, iter
} THEN
357 IF LeadingAny(gn
.p1
, a
) THEN
358 CompFirstSet(ABS(gn
.next
), s1
); Sets
.Differ(set
[a
.p1
], s1
)
360 ELSIF gn
.typ
= alt
THEN
361 p
:= gp
; Sets
.Clear(s1
);
363 GetNode(p
, gn2
); FindAS(gn2
.p1
);
364 IF LeadingAny(gn2
.p1
, a
) THEN
365 CompFirstSet(gn2
.p2
, s2
); Sets
.Unite(s2
, s1
); Sets
.Differ(set
[a
.p1
], s2
)
367 CompFirstSet(gn2
.p1
, s2
); Sets
.Unite(s1
, s2
)
378 WHILE curSy
<= lastNt
DO (* for all nonterminals *)
379 GetSym(curSy
, sn
); FindAS(sn
.struct
);
385 PROCEDURE CompSyncSets
;
386 VAR curSy
, i
: INTEGER; sn
: SymbolNode
; visited
: MarkList
;
388 PROCEDURE CompSync(gp
: INTEGER);
389 VAR s
: Set
; gn
: GraphNode
;
391 WHILE (gp
> 0) & ~ Sets
.In(visited
, gp
) DO
392 GetNode(gp
, gn
); Sets
.Incl(visited
, gp
);
393 IF gn
.typ
= sync
THEN
394 CompExpected(ABS(gn
.next
), curSy
, s
);
395 Sets
.Incl(s
, eofSy
); Sets
.Unite(set
[0], s
);
396 gn
.p1
:= NewSet(s
); PutNode(gp
, gn
)
397 ELSIF gn
.typ
= alt
THEN CompSync(gn
.p1
); CompSync(gn
.p2
)
398 ELSIF gn
.typ
IN {iter
, opt
} THEN CompSync(gn
.p1
)
405 curSy
:= firstNt
; ClearMarkList(visited
);
406 WHILE curSy
<= lastNt
DO
407 GetSym(curSy
, sn
); CompSync(sn
.struct
);
413 PROCEDURE CompDeletableSymbols
*;
414 VAR changed
, del
: BOOLEAN; i
: INTEGER; sn
: SymbolNode
;
420 WHILE i
<= lastNt
DO (*for all nonterminals*)
422 IF ~sn
.deletable
& DelGraph(sn
.struct
) THEN
423 sn
.deletable
:= TRUE
; PutSym(i
, sn
); changed
:= TRUE
; del
:= TRUE
429 i
:= firstNt
; IF del
THEN NL
END;
432 IF sn
.deletable
THEN Str(" "); Str(sn
.name
); Str(" deletable"); NL
END;
435 Texts
.Append(Oberon
.Log
, w
.buf
)
436 END CompDeletableSymbols
;
439 PROCEDURE CompSymbolSets
*;
440 VAR i
: INTEGER; sn
: SymbolNode
;
442 i
:= NewSym(t
, "???", 0); (*unknown symbols get code maxT*)
444 CompDeletableSymbols
;
450 i
:= firstNt
; Str("First & follow symbols:"); NL
;
451 WHILE i
<= lastNt
DO (* for all nonterminals *)
452 GetSym(i
, sn
); Str(sn
.name
); NL
;
453 Str("first: "); PrintSet(first
[i
- firstNt
].ts
, 10);
454 Str("follow: "); PrintSet(follow
[i
- firstNt
].ts
, 10);
459 IF maxSet
>= 0 THEN NL
; NL
; Str("List of sets (ANY, SYNC): "); NL
END;
462 Str(" set["); Texts
.WriteInt (w
, i
, 2); Str("] = "); PrintSet(set
[i
], 16);
465 NL
; NL
; Texts
.Append(Oberon
.Log
, w
.buf
)
470 PROCEDURE GetFirstSet(sp
: INTEGER; VAR s
: Set
);
471 BEGIN s
:= first
[sp
- firstNt
].ts
474 PROCEDURE GetFollowSet(sp
: INTEGER; VAR s
: Set
);
475 BEGIN s
:= follow
[sp
- firstNt
].ts
478 PROCEDURE GetSet
*(nr
: INTEGER; VAR s
: Set
);
482 PROCEDURE MovePragmas
;
485 IF maxP
> firstNt
THEN
486 i
:= maxSymbols
- 1; maxP
:= maxT
;
488 INC(maxP
); IF maxP
>= firstNt
THEN Restriction(6) END;
489 st
[maxP
] := st
[i
]; DEC(i
)
494 PROCEDURE PrintSymbolTable
*;
497 PROCEDURE WriteTyp(typ
: INTEGER);
506 BEGIN (* PrintSymbolTable *)
507 Str("Symbol Table:"); NL
; NL
;
508 Str("nr name typ hasAttribs struct del line"); NL
; NL
;
511 WHILE i
< maxSymbols
DO
512 Texts
.WriteInt(w
, i
, 3); Str(" ");
513 j
:= 0; WHILE (j
< 8) & (st
[i
].name
[j
] # 0X
) DO Texts
.Write(w
, st
[i
].name
[j
]); INC(j
) END;
514 WHILE j
< 8 DO Texts
.Write(w
, " "); INC(j
) END;
516 IF st
[i
].attrPos
.beg
>= 0 THEN Str(" TRUE ") ELSE Str(" FALSE") END;
517 Texts
.WriteInt(w
, st
[i
].struct
, 10);
518 IF st
[i
].deletable
THEN Str(" TRUE ") ELSE Str(" FALSE") END;
519 Texts
.WriteInt(w
, st
[i
].line
, 6); NL
;
520 IF i
= maxT
THEN i
:= firstNt
ELSE INC(i
) END
522 NL
; NL
; Texts
.Append(Oberon
.Log
, w
.buf
)
523 END PrintSymbolTable
;
525 PROCEDURE NewClass
*(name
: Name
; set
: Set
): INTEGER;
527 INC(maxC
); IF maxC
>= maxClasses
THEN Restriction(7) END;
528 IF name
[0] = "#" THEN name
[1] := CHR(ORD("A") + dummyName
); INC(dummyName
) END;
529 chClass
[maxC
].name
:= name
; chClass
[maxC
].set
:= NewSet(set
);
533 PROCEDURE ClassWithName
*(name
: Name
): INTEGER;
536 i
:= maxC
; WHILE (i
>= 0) & (chClass
[i
].name
# name
) DO DEC(i
) END;
540 PROCEDURE ClassWithSet
*(s
: Set
): INTEGER;
543 i
:= maxC
; WHILE (i
>= 0) & ~ Sets
.Equal(set
[chClass
[i
].set
], s
) DO DEC(i
) END;
547 PROCEDURE GetClass
*(n
: INTEGER; VAR s
: Set
);
549 GetSet(chClass
[n
].set
, s
)
552 PROCEDURE GetClassName
*(n
: INTEGER; VAR name
: Name
);
554 name
:= chClass
[n
].name
558 CONST maxLineLen
= 80;
559 TYPE ListPtr
= POINTER TO ListNode
;
568 VAR gn
: GraphNode
; col
, i
, j
: INTEGER; l
, p
, q
: ListPtr
;
570 xList
: ARRAY maxSymbols
OF ListHdr
;
573 IF maxT
<= 0 THEN RETURN END;
575 (* initialise cross reference list *)
577 WHILE i
<= lastNt
DO (* for all symbols *)
578 GetSym(i
, sn
); xList
[i
].name
:= sn
.name
; xList
[i
].lptr
:= NIL;
579 IF i
= maxP
THEN i
:= firstNt
ELSE INC(i
) END
582 (* search lines where symbol has been referenced *)
584 WHILE i
<= nNodes
DO (* for all graph nodes *)
586 IF gn
.typ
IN {t
, wt
, nt
} THEN
587 NEW(l
); l^
.next
:= xList
[gn
.p1
].lptr
; l^
.line
:= gn
.line
;
588 xList
[gn
.p1
].lptr
:= l
593 (* search lines where symbol has been defined and insert in order *)
595 WHILE i
<= lastNt
DO (*for all symbols*)
596 GetSym(i
, sn
); p
:= xList
[i
].lptr
; q
:= NIL;
597 WHILE (p
# NIL) & (p^
.line
> sn
.line
) DO q
:= p
; p
:= p^
.next
END;
598 NEW(l
); l^
.next
:= p
;
600 IF q
# NIL THEN q^
.next
:= l
ELSE xList
[i
].lptr
:= l
END;
601 IF i
= maxP
THEN i
:= firstNt
ELSE INC(i
) END
604 (* print cross reference listing *)
605 NL
; Str("Cross reference list:"); NL
; NL
; Str("Terminals:"); NL
; Str(" 0 EOF"); NL
;
607 WHILE i
<= lastNt
DO (*for all symbols*)
608 Texts
.WriteInt(w
, i
, 3); Str(" ");
609 j
:= 0; WHILE (j
< 15) & (xList
[i
].name
[j
] # 0X
) DO Texts
.Write(w
, xList
[i
].name
[j
]); INC(j
) END;
610 l
:= xList
[i
].lptr
; col
:= 25;
612 IF col
+ 5 > maxLineLen
THEN
613 NL
; col
:= 0; WHILE col
< 25 DO Texts
.Write(w
, " "); INC(col
) END
615 IF l^
.line
= 0 THEN Str("undef") ELSE Texts
.WriteInt(w
, l^
.line
, 5) END;
620 IF i
= maxT
THEN NL
; Str("Pragmas:"); NL
END;
621 IF i
= maxP
THEN NL
; Str("Nonterminals:"); NL
; i
:= firstNt
ELSE INC(i
) END
623 NL
; NL
; Texts
.Append(Oberon
.Log
, w
.buf
)
627 PROCEDURE NewNode
*(typ
, p1
, line
: INTEGER): INTEGER;
629 INC(nNodes
); IF nNodes
> maxNodes
THEN Restriction(3) END;
630 gn
[nNodes
].typ
:= typ
; gn
[nNodes
].next
:= 0;
631 gn
[nNodes
].p1
:= p1
; gn
[nNodes
].p2
:= 0;
632 gn
[nNodes
].pos
.beg
:= -1; gn
[nNodes
].line
:= line
;
636 PROCEDURE CompleteGraph
*(gp
: INTEGER);
640 p
:= gn
[gp
].next
; gn
[gp
].next
:= 0; gp
:= p
644 PROCEDURE ConcatAlt
*(VAR gL1
, gR1
: INTEGER; gL2
, gR2
: INTEGER);
647 gL2
:= NewNode(alt
, gL2
, 0);
648 p
:= gL1
; WHILE gn
[p
].p2
# 0 DO p
:= gn
[p
].p2
END; gn
[p
].p2
:= gL2
;
649 p
:= gR1
; WHILE gn
[p
].next
# 0 DO p
:= gn
[p
].next
END; gn
[p
].next
:= gR2
652 PROCEDURE ConcatSeq
*(VAR gL1
, gR1
: INTEGER; gL2
, gR2
: INTEGER);
655 p
:= gn
[gR1
].next
; gn
[gR1
].next
:= gL2
; (*head node*)
656 WHILE p
# 0 DO (*substructure*)
657 q
:= gn
[p
].next
; gn
[p
].next
:= -gL2
; p
:= q
662 PROCEDURE MakeFirstAlt
*(VAR gL
, gR
: INTEGER);
664 gL
:= NewNode(alt
, gL
, 0); gn
[gL
].next
:= gR
; gR
:= gL
667 PROCEDURE MakeIteration
*(VAR gL
, gR
: INTEGER);
670 gL
:= NewNode(iter
, gL
, 0); p
:= gR
; gR
:= gL
;
672 q
:= gn
[p
].next
; gn
[p
].next
:= - gL
; p
:= q
676 PROCEDURE MakeOption
*(VAR gL
, gR
: INTEGER);
678 gL
:= NewNode(opt
, gL
, 0); gn
[gL
].next
:= gR
; gR
:= gL
681 PROCEDURE StrToGraph
*(str
: ARRAY OF CHAR; VAR gL
, gR
: INTEGER);
684 gR
:= 0; i
:= 1; len
:= Length(str
) - 1;
686 gn
[gR
].next
:= NewNode(char
, ORD(str
[i
]), 0); gR
:= gn
[gR
].next
;
689 gL
:= gn
[0].next
; gn
[0].next
:= 0
692 PROCEDURE DelNode
*(gn
: GraphNode
): BOOLEAN;
695 PROCEDURE DelAlt(gp
: INTEGER): BOOLEAN;
698 IF gp
<= 0 THEN RETURN TRUE
END; (*end of graph found*)
700 RETURN DelNode(gn
) & DelAlt(gn
.next
);
704 IF gn
.typ
= nt
THEN GetSym(gn
.p1
, sn
); RETURN sn
.deletable
705 ELSIF gn
.typ
= alt
THEN RETURN DelAlt(gn
.p1
) OR (gn
.p2
# 0) & DelAlt(gn
.p2
)
706 ELSE RETURN gn
.typ
IN {eps
, iter
, opt
, sem
, sync
}
710 PROCEDURE PrintGraph
*;
713 PROCEDURE WriteTyp(typ
: INTEGER);
730 BEGIN (* PrintGraph *)
731 Str("GraphList:"); NL
; NL
;
732 Str(" nr typ next p1 p2 line"); NL
; NL
;
736 Texts
.WriteInt(w
, i
, 3); Str(" ");
737 WriteTyp(gn
[i
].typ
); Texts
.WriteInt(w
, gn
[i
].next
, 7);
738 Texts
.WriteInt(w
, gn
[i
].p1
, 7);
739 Texts
.WriteInt(w
, gn
[i
].p2
, 7);
740 Texts
.WriteInt(w
, gn
[i
].line
, 7);
744 NL
; NL
; Texts
.Append(Oberon
.Log
, w
.buf
)
747 PROCEDURE FindCircularProductions
* (VAR ok
: BOOLEAN);
749 TYPE ListEntry
= RECORD
754 VAR changed
, onLeftSide
, onRightSide
: BOOLEAN; i
, j
, listLength
: INTEGER;
755 list
: ARRAY maxList
OF ListEntry
;
759 PROCEDURE GetSingles (gp
: INTEGER; VAR singles
: MarkList
);
762 IF gp
<= 0 THEN RETURN END; (* end of graph found *)
765 IF DelGraph(ABS(gn
.next
)) THEN Sets
.Incl(singles
, gn
.p1
) END
766 ELSIF gn
.typ
IN {alt
, iter
, opt
} THEN
767 IF DelGraph(ABS(gn
.next
)) THEN
768 GetSingles(gn
.p1
, singles
);
769 IF gn
.typ
= alt
THEN GetSingles(gn
.p2
, singles
) END
772 IF DelNode(gn
) THEN GetSingles(gn
.next
, singles
) END
775 BEGIN (* FindCircularProductions *)
776 i
:= firstNt
; listLength
:= 0;
777 WHILE i
<= lastNt
DO (* for all nonterminals i *)
778 ClearMarkList (singles
); GetSym (i
, sn
);
779 GetSingles (sn
.struct
, singles
); (* get nt's j such that i-->j *)
781 WHILE j
<= lastNt
DO (* for all nonterminals j *)
782 IF Sets
.In(singles
, j
) THEN
783 list
[listLength
].left
:= i
; list
[listLength
].right
:= j
;
784 list
[listLength
].deleted
:= FALSE
;
793 i
:= 0; changed
:= FALSE
;
794 WHILE i
< listLength
DO
795 IF ~ list
[i
].deleted
THEN
796 j
:= 0; onLeftSide
:= FALSE
; onRightSide
:= FALSE
;
797 WHILE j
< listLength
DO
798 IF ~ list
[j
].deleted
THEN
799 IF list
[i
].left
= list
[j
].right
THEN onRightSide
:= TRUE
END;
800 IF list
[j
].left
= list
[i
].right
THEN onLeftSide
:= TRUE
END
804 IF ~ onRightSide
OR ~ onLeftSide
THEN
805 list
[i
].deleted
:= TRUE
; changed
:= TRUE
813 WHILE i
< listLength
DO
814 IF ~ list
[i
].deleted
THEN
816 GetSym(list
[i
].left
, sn
); NL
; Str(" "); Str(sn
.name
); Str(" --> ");
817 GetSym(list
[i
].right
, sn
); Str(sn
.name
)
821 Texts
.Append(Oberon
.Log
, w
.buf
)
822 END FindCircularProductions
;
825 PROCEDURE LL1Test
* (VAR ll1
: BOOLEAN);
826 VAR sn
: SymbolNode
; curSy
: INTEGER;
828 PROCEDURE LL1Error (cond
, ts
: INTEGER);
832 GetSym (curSy
, sn
); Str(" LL1 error in "); Str(sn
.name
); Str(": ");
833 IF ts
> 0 THEN GetSym (ts
, sn
); Str(sn
.name
); Str(" is ") END;
835 1: Str(" start of several alternatives.")
836 |
2: Str(" start & successor of deletable structure")
837 |
3: Str(" an ANY node that matchs no symbol")
839 NL
; Texts
.Append(Oberon
.Log
, w
.buf
)
842 PROCEDURE Check (cond
: INTEGER; VAR s1
, s2
: Set
);
847 IF Sets
.In(s1
, i
) & Sets
.In(s2
, i
) THEN LL1Error(cond
, i
) END;
852 PROCEDURE CheckAlternatives (gp
: INTEGER);
853 VAR gn
, gn1
: GraphNode
; s1
, s2
: Set
; p
: INTEGER;
858 p
:= gp
; Sets
.Clear(s1
);
859 WHILE p
# 0 DO (*for all alternatives*)
860 GetNode(p
, gn1
); CompExpected(gn1
.p1
, curSy
, s2
);
861 Check(1, s1
, s2
); Sets
.Unite(s1
, s2
);
862 CheckAlternatives(gn1
.p1
);
865 ELSIF gn
.typ
IN {opt
, iter
} THEN
866 CompExpected(gn
.p1
, curSy
, s1
);
867 CompExpected(ABS(gn
.next
), curSy
, s2
);
869 CheckAlternatives(gn
.p1
)
870 ELSIF gn
.typ
= any
THEN
872 IF Sets
.Empty(s1
) THEN LL1Error(3, 0) END (*e.g. {ANY} ANY or [ANY] ANY*)
876 END CheckAlternatives
;
879 curSy
:= firstNt
; ll1
:= TRUE
;
880 WHILE curSy
<= lastNt
DO (*for all nonterminals*)
881 GetSym(curSy
, sn
); CheckAlternatives (sn
.struct
);
887 PROCEDURE TestCompleteness
* (VAR ok
: BOOLEAN);
888 VAR sp
: INTEGER; sn
: SymbolNode
;
890 sp
:= firstNt
; ok
:= TRUE
;
891 WHILE sp
<= lastNt
DO (*for all nonterminals*)
893 IF sn
.struct
= 0 THEN
894 ok
:= FALSE
; NL
; Str(" No production for "); Str(sn
.name
); Texts
.Append(Oberon
.Log
, w
.buf
)
898 END TestCompleteness
;
901 PROCEDURE TestIfAllNtReached
* (VAR ok
: BOOLEAN);
902 VAR gn
: GraphNode
; sp
: INTEGER; reached
: MarkList
; sn
: SymbolNode
;
904 PROCEDURE MarkReachedNts (gp
: INTEGER);
905 VAR gn
: GraphNode
; sn
: SymbolNode
;
910 IF ~ Sets
.In(reached
, gn
.p1
) THEN (*new nt reached*)
911 Sets
.Incl(reached
, gn
.p1
);
912 GetSym(gn
.p1
, sn
); MarkReachedNts(sn
.struct
)
914 ELSIF gn
.typ
IN {alt
, iter
, opt
} THEN
915 MarkReachedNts(gn
.p1
);
916 IF gn
.typ
= alt
THEN MarkReachedNts(gn
.p2
) END
922 BEGIN (* TestIfAllNtReached *)
923 ClearMarkList(reached
);
924 GetNode(root
, gn
); Sets
.Incl(reached
, gn
.p1
);
925 GetSym(gn
.p1
, sn
); MarkReachedNts(sn
.struct
);
927 sp
:= firstNt
; ok
:= TRUE
;
928 WHILE sp
<= lastNt
DO (*for all nonterminals*)
929 IF ~ Sets
.In(reached
, sp
) THEN
930 ok
:= FALSE
; GetSym(sp
, sn
); NL
; Str(" "); Str(sn
.name
); Str(" cannot be reached")
934 Texts
.Append(Oberon
.Log
, w
.buf
)
935 END TestIfAllNtReached
;
938 PROCEDURE TestIfNtToTerm
* (VAR ok
: BOOLEAN);
939 VAR changed
: BOOLEAN; gn
: GraphNode
; sp
: INTEGER;
943 PROCEDURE IsTerm (gp
: INTEGER): BOOLEAN;
948 IF (gn
.typ
= nt
) & ~ Sets
.In(termList
, gn
.p1
)
949 OR (gn
.typ
= alt
) & ~
IsTerm(gn
.p1
) & ~
IsTerm(gn
.p2
) THEN RETURN FALSE
956 BEGIN (* TestIfNtToTerm *)
957 ClearMarkList(termList
);
959 sp
:= firstNt
; changed
:= FALSE
;
960 WHILE sp
<= lastNt
DO
961 IF ~ Sets
.In(termList
, sp
) THEN
963 IF IsTerm(sn
.struct
) THEN Sets
.Incl(termList
, sp
); changed
:= TRUE
END
969 sp
:= firstNt
; ok
:= TRUE
;
970 WHILE sp
<= lastNt
DO
971 IF ~ Sets
.In(termList
, sp
) THEN
972 ok
:= FALSE
; GetSym(sp
, sn
); NL
; Str(" "); Str(sn
.name
); Str(" cannot be derived to terminals")
976 Texts
.Append(Oberon
.Log
, w
.buf
)
981 maxSet
:= 0; Sets
.Clear(set
[0]); Sets
.Incl(set
[0], eofSy
);
982 firstNt
:= maxSymbols
; maxP
:= maxSymbols
; maxT
:= -1; maxC
:= -1;
989 (* The dummy node gn[0] ensures that none of the procedures
990 above have to check for 0 indices. *)
992 gn
[0].typ
:= -1; gn
[0].p1
:= 0; gn
[0].p2
:= 0; gn
[0].next
:= 0; gn
[0].line
:= 0;