|
@@ -76,6 +76,19 @@ VAR
|
|
|
spanName: ARRAY [0 .. 1023] OF SymTab.Name;
|
|
spanName: ARRAY [0 .. 1023] OF SymTab.Name;
|
|
|
spanStart: ARRAY [0 .. 1023] OF M2S.INT32;
|
|
spanStart: ARRAY [0 .. 1023] OF M2S.INT32;
|
|
|
spanIdx: ARRAY [0 .. 1023] OF CARDINAL;
|
|
spanIdx: ARRAY [0 .. 1023] OF CARDINAL;
|
|
|
|
|
+ spanTy: ARRAY [0 .. 1023] OF INTEGER;
|
|
|
|
|
+ spanNode: ARRAY [0 .. 1023] OF AST.Node;
|
|
|
|
|
+
|
|
|
|
|
+ (* Two-phase parse (milestone 2c): phase-1 skips a module-level proc
|
|
|
|
|
+ body (DeferSkip) and phase 2 re-parses it at unit end (RepassAll).
|
|
|
|
|
+ `deferredBody` reports the skip to ProcDecl's close choice;
|
|
|
|
|
+ `repassing` disables deferral during phase 2; `allowDefer` limits
|
|
|
|
|
+ deferral to plain ProcDecls (methods stay inline); `curProcNode` is
|
|
|
|
|
+ the NkProcDecl whose body is about to be parsed. *)
|
|
|
|
|
+ deferredBody: BOOLEAN;
|
|
|
|
|
+ repassing: BOOLEAN;
|
|
|
|
|
+ allowDefer: BOOLEAN;
|
|
|
|
|
+ curProcNode: AST.Node;
|
|
|
|
|
|
|
|
(* Result slot for the expression-AST builder (slice 2). Every
|
|
(* Result slot for the expression-AST builder (slice 2). Every
|
|
|
Expr/SimExpr/Term/Fact leaves its node here; the combination
|
|
Expr/SimExpr/Term/Fact leaves its node here; the combination
|
|
@@ -364,14 +377,11 @@ PROCEDURE NoteSpan (pn: SymTab.Name; start: M2S.INT32);
|
|
|
END NoteSpan;
|
|
END NoteSpan;
|
|
|
|
|
|
|
|
PROCEDURE ProbeBody (pn: SymTab.Name);
|
|
PROCEDURE ProbeBody (pn: SymTab.Name);
|
|
|
-(* Shadow-skip the body starting at the current token: consume tokens
|
|
|
|
|
- counting block openers until the matching END, check the procedure
|
|
|
|
|
- name after it, record the body span for the re-parse pass, verify
|
|
|
|
|
- SeekTo repositions exactly, then restore the exact scanner/parser
|
|
|
|
|
- state. The body then parses normally, so a correct skipper is
|
|
|
|
|
- output-neutral; a wrong one reports to StdOut (never into the
|
|
|
|
|
- listing). The SeekTo check's side effects are discarded by the
|
|
|
|
|
- trailing PopPos, so it is output-neutral too. *)
|
|
|
|
|
|
|
+(* Shadow-skip a body: consume tokens counting block openers until the
|
|
|
|
|
+ matching END, check the name, record the span, verify SeekTo
|
|
|
|
|
+ repositions exactly, then restore the exact scanner/parser state.
|
|
|
|
|
+ Superseded by DeferSkip for deferred bodies; kept for the plain
|
|
|
|
|
+ probe of non-deferred Bodies (methods). *)
|
|
|
VAR savedSym, savedErr, depth, guard: CARDINAL;
|
|
VAR savedSym, savedErr, depth, guard: CARDINAL;
|
|
|
nm: SymTab.Name;
|
|
nm: SymTab.Name;
|
|
|
ok, sok: BOOLEAN;
|
|
ok, sok: BOOLEAN;
|
|
@@ -420,6 +430,71 @@ PROCEDURE ProbeBody (pn: SymTab.Name);
|
|
|
END
|
|
END
|
|
|
END ProbeBody;
|
|
END ProbeBody;
|
|
|
|
|
|
|
|
|
|
+PROCEDURE Openers (): BOOLEAN;
|
|
|
|
|
+(* TRUE when the current token opens a block that must be closed by an
|
|
|
|
|
+ END (BEGIN is excluded: a block's BEGIN shares the declaration END;
|
|
|
|
|
+ REPEAT..UNTIL and EXIT/RETURN need no END). *)
|
|
|
|
|
+ BEGIN
|
|
|
|
|
+ RETURN TokIs("MODULE") OR TokIs("PROCEDURE") OR TokIs("CLASS")
|
|
|
|
|
+ OR TokIs("RECORD") OR TokIs("IF") OR TokIs("CASE")
|
|
|
|
|
+ OR TokIs("WHILE") OR TokIs("FOR") OR TokIs("LOOP")
|
|
|
|
|
+ OR TokIs("WITH")
|
|
|
|
|
+ END Openers;
|
|
|
|
|
+
|
|
|
|
|
+PROCEDURE DeferSkip (pn: SymTab.Name): BOOLEAN;
|
|
|
|
|
+(* Phase 1 of the deferral: skip the body starting at the current token
|
|
|
|
|
+ (the first token after the heading's ';') up to the matching END,
|
|
|
|
|
+ verify the END's name, and LEAVE the scanner positioned exactly at
|
|
|
|
|
+ that END (so Block's trailing "END" ident consumes it for real), with
|
|
|
|
|
+ the span recorded for the re-parse pass. On any inconsistency,
|
|
|
|
|
+ restore the scanner and return FALSE so the caller parses normally. *)
|
|
|
|
|
+ VAR depth, guard: CARDINAL;
|
|
|
|
|
+ nm: SymTab.Name;
|
|
|
|
|
+ ok: BOOLEAN;
|
|
|
|
|
+ start, endPos: M2S.INT32;
|
|
|
|
|
+ BEGIN
|
|
|
|
|
+ deferredBody := FALSE;
|
|
|
|
|
+ start := M2S.nextPos;
|
|
|
|
|
+ M2S.PushPos;
|
|
|
|
|
+ depth := 0; guard := 0; ok := TRUE;
|
|
|
|
|
+ LOOP
|
|
|
|
|
+ IF sym = 0 THEN ok := FALSE; EXIT END;
|
|
|
|
|
+ IF TokIs("END") THEN
|
|
|
|
|
+ IF depth = 0 THEN EXIT END;
|
|
|
|
|
+ DEC(depth)
|
|
|
|
|
+ ELSIF Openers() THEN
|
|
|
|
|
+ INC(depth)
|
|
|
|
|
+ END;
|
|
|
|
|
+ Adv;
|
|
|
|
|
+ INC(guard);
|
|
|
|
|
+ IF guard > 1000000 THEN ok := FALSE; EXIT END
|
|
|
|
|
+ END;
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ (* sym is the matching END; check the name after it, then seek back
|
|
|
|
|
+ so Block sees END (its "END" ident then consumes it for real). *)
|
|
|
|
|
+ endPos := M2S.nextPos;
|
|
|
|
|
+ Adv;
|
|
|
|
|
+ M2S.GetName(M2S.nextPos, M2S.nextLen, nm);
|
|
|
|
|
+ IF (sym = 0) OR NOT SymTab.Equal(nm, pn) THEN ok := FALSE END;
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ M2S.SeekTo(endPos); Adv
|
|
|
|
|
+ END
|
|
|
|
|
+ END;
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ NoteSpan(pn, start);
|
|
|
|
|
+ spanTy[nSpans - 1] := Lower.CurScopeProc();
|
|
|
|
|
+ spanNode[nSpans - 1] := curProcNode;
|
|
|
|
|
+ deferredBody := TRUE;
|
|
|
|
|
+ RETURN TRUE
|
|
|
|
|
+ ELSE
|
|
|
|
|
+ M2S.PopPos;
|
|
|
|
|
+ FileIO.WriteString(FileIO.StdOut, "twophase defer mismatch in ");
|
|
|
|
|
+ FileIO.WriteString(FileIO.StdOut, pn);
|
|
|
|
|
+ FileIO.WriteLn(FileIO.StdOut);
|
|
|
|
|
+ RETURN FALSE
|
|
|
|
|
+ END
|
|
|
|
|
+ END DeferSkip;
|
|
|
|
|
+
|
|
|
CHARACTERS
|
|
CHARACTERS
|
|
|
eol = CHR(13) .
|
|
eol = CHR(13) .
|
|
|
lf = CHR(10) .
|
|
lf = CHR(10) .
|
|
@@ -485,7 +560,9 @@ PRODUCTIONS
|
|
|
refActual[refN] := at; INC(refN)
|
|
refActual[refN] := at; INC(refN)
|
|
|
END; .) .
|
|
END; .) .
|
|
|
Unit
|
|
Unit
|
|
|
- = (. isGen := FALSE; .)
|
|
|
|
|
|
|
+ = (. isGen := FALSE;
|
|
|
|
|
+ allowDefer := FALSE;
|
|
|
|
|
+ repassing := FALSE; .)
|
|
|
[ "GENERIC" (. isGen := TRUE; .) ]
|
|
[ "GENERIC" (. isGen := TRUE; .) ]
|
|
|
( DefUnit | ImplUnit | ProgModule ) .
|
|
( DefUnit | ImplUnit | ProgModule ) .
|
|
|
(* Step 4.3: one session compiles DEFINITION, its IMPLEMENTATION
|
|
(* Step 4.3: one session compiles DEFINITION, its IMPLEMENTATION
|
|
@@ -566,6 +643,12 @@ PRODUCTIONS
|
|
|
ImplUnit (. VAR m1, m2: SymTab.Name;
|
|
ImplUnit (. VAR m1, m2: SymTab.Name;
|
|
|
g: SymTab.Name;
|
|
g: SymTab.Name;
|
|
|
k: CARDINAL;
|
|
k: CARDINAL;
|
|
|
|
|
+ rp: CARDINAL;
|
|
|
|
|
+ rpOk: BOOLEAN;
|
|
|
|
|
+ rErrs: INTEGER;
|
|
|
|
|
+ rDecl, rStmt: AST.Node;
|
|
|
|
|
+ rnm: SymTab.Name;
|
|
|
|
|
+ rSym, rErr: CARDINAL;
|
|
|
fname: SymTab.Name;
|
|
fname: SymTab.Name;
|
|
|
unres: BOOLEAN;
|
|
unres: BOOLEAN;
|
|
|
astNode: AST.Node; .)
|
|
astNode: AST.Node; .)
|
|
@@ -603,6 +686,44 @@ PRODUCTIONS
|
|
|
|
|
|
|
|
nLab := 0; nGot := 0; .)
|
|
nLab := 0; nGot := 0; .)
|
|
|
[ StatSeq ] ]
|
|
[ StatSeq ] ]
|
|
|
|
|
+ (. IF twoPhaseParse THEN
|
|
|
|
|
+ rSym := sym; rErr := errDist;
|
|
|
|
|
+ rDecl := astDecl; rStmt := astStmt;
|
|
|
|
|
+ M2S.PushPos;
|
|
|
|
|
+ repassing := TRUE;
|
|
|
|
|
+ rp := 0;
|
|
|
|
|
+ WHILE rp < nSpans DO
|
|
|
|
|
+ k := 0;
|
|
|
|
|
+ WHILE (k <= HIGH(rnm))
|
|
|
|
|
+ AND (spanName[rp][k] # CHR(0)) DO
|
|
|
|
|
+ rnm[k] := spanName[rp][k]; INC(k)
|
|
|
|
|
+ END;
|
|
|
|
|
+ rnm[k] := CHR(0);
|
|
|
|
|
+ M2S.SeekTo(spanStart[rp]);
|
|
|
|
|
+ Adv;
|
|
|
|
|
+ rpOk := SymTab.ReenterDeferred(spanIdx[rp]);
|
|
|
|
|
+ IF NOT rpOk THEN
|
|
|
|
|
+ FileIO.WriteString(FileIO.StdOut,
|
|
|
|
|
+ "twophase reenter failed");
|
|
|
|
|
+ FileIO.WriteLn(FileIO.StdOut)
|
|
|
|
|
+ END;
|
|
|
|
|
+ Lower.ReenterScopeProc(spanTy[rp]);
|
|
|
|
|
+ QbeGen.BeginFunc("");
|
|
|
|
|
+ Block(rnm);
|
|
|
|
|
+ AST.SetChild(spanNode[rp], 1, astStmt);
|
|
|
|
|
+ AST.SetChild(spanNode[rp], 2, astBlkDecls);
|
|
|
|
|
+ Lower.ScopeLeave;
|
|
|
|
|
+ SymTab.CloseProc;
|
|
|
|
|
+ QbeGen.EndFunc(SymTab.ProcRes(rnm));
|
|
|
|
|
+ INC(rp)
|
|
|
|
|
+ END;
|
|
|
|
|
+ rErrs := M2S.errors;
|
|
|
|
|
+ repassing := FALSE;
|
|
|
|
|
+ M2S.PopPos;
|
|
|
|
|
+ sym := rSym; errDist := rErr;
|
|
|
|
|
+ M2S.errors := rErrs;
|
|
|
|
|
+ astDecl := rDecl; astStmt := rStmt
|
|
|
|
|
+ END; .)
|
|
|
"END"
|
|
"END"
|
|
|
GetIdent<m2> (. IF instActive THEN InstNameGet(m2) END;
|
|
GetIdent<m2> (. IF instActive THEN InstNameGet(m2) END;
|
|
|
FlushPend;
|
|
FlushPend;
|
|
@@ -640,6 +761,12 @@ PRODUCTIONS
|
|
|
SymTab.EndUnit; .) ) .
|
|
SymTab.EndUnit; .) ) .
|
|
|
ProgModule (. VAR m1, m2: SymTab.Name;
|
|
ProgModule (. VAR m1, m2: SymTab.Name;
|
|
|
k: CARDINAL;
|
|
k: CARDINAL;
|
|
|
|
|
+ rp: CARDINAL;
|
|
|
|
|
+ rpOk: BOOLEAN;
|
|
|
|
|
+ rErrs: INTEGER;
|
|
|
|
|
+ rDecl, rStmt: AST.Node;
|
|
|
|
|
+ rnm: SymTab.Name;
|
|
|
|
|
+ rSym, rErr: CARDINAL;
|
|
|
fname: SymTab.Name;
|
|
fname: SymTab.Name;
|
|
|
unres: BOOLEAN;
|
|
unres: BOOLEAN;
|
|
|
astNode: AST.Node; .)
|
|
astNode: AST.Node; .)
|
|
@@ -668,6 +795,44 @@ PRODUCTIONS
|
|
|
|
|
|
|
|
nLab := 0; nGot := 0; .)
|
|
nLab := 0; nGot := 0; .)
|
|
|
[ StatSeq ] ]
|
|
[ StatSeq ] ]
|
|
|
|
|
+ (. IF twoPhaseParse THEN
|
|
|
|
|
+ rSym := sym; rErr := errDist;
|
|
|
|
|
+ rDecl := astDecl; rStmt := astStmt;
|
|
|
|
|
+ M2S.PushPos;
|
|
|
|
|
+ repassing := TRUE;
|
|
|
|
|
+ rp := 0;
|
|
|
|
|
+ WHILE rp < nSpans DO
|
|
|
|
|
+ k := 0;
|
|
|
|
|
+ WHILE (k <= HIGH(rnm))
|
|
|
|
|
+ AND (spanName[rp][k] # CHR(0)) DO
|
|
|
|
|
+ rnm[k] := spanName[rp][k]; INC(k)
|
|
|
|
|
+ END;
|
|
|
|
|
+ rnm[k] := CHR(0);
|
|
|
|
|
+ M2S.SeekTo(spanStart[rp]);
|
|
|
|
|
+ Adv;
|
|
|
|
|
+ rpOk := SymTab.ReenterDeferred(spanIdx[rp]);
|
|
|
|
|
+ IF NOT rpOk THEN
|
|
|
|
|
+ FileIO.WriteString(FileIO.StdOut,
|
|
|
|
|
+ "twophase reenter failed");
|
|
|
|
|
+ FileIO.WriteLn(FileIO.StdOut)
|
|
|
|
|
+ END;
|
|
|
|
|
+ Lower.ReenterScopeProc(spanTy[rp]);
|
|
|
|
|
+ QbeGen.BeginFunc("");
|
|
|
|
|
+ Block(rnm);
|
|
|
|
|
+ AST.SetChild(spanNode[rp], 1, astStmt);
|
|
|
|
|
+ AST.SetChild(spanNode[rp], 2, astBlkDecls);
|
|
|
|
|
+ Lower.ScopeLeave;
|
|
|
|
|
+ SymTab.CloseProc;
|
|
|
|
|
+ QbeGen.EndFunc(SymTab.ProcRes(rnm));
|
|
|
|
|
+ INC(rp)
|
|
|
|
|
+ END;
|
|
|
|
|
+ rErrs := M2S.errors;
|
|
|
|
|
+ repassing := FALSE;
|
|
|
|
|
+ M2S.PopPos;
|
|
|
|
|
+ sym := rSym; errDist := rErr;
|
|
|
|
|
+ M2S.errors := rErrs;
|
|
|
|
|
+ astDecl := rDecl; astStmt := rStmt
|
|
|
|
|
+ END; .)
|
|
|
"END"
|
|
"END"
|
|
|
GetIdent<m2> (. IF instActive THEN InstNameGet(m2) END;
|
|
GetIdent<m2> (. IF instActive THEN InstNameGet(m2) END;
|
|
|
FlushPend;
|
|
FlushPend;
|
|
@@ -1540,17 +1705,32 @@ PRODUCTIONS
|
|
|
SymTab.CloseProc;
|
|
SymTab.CloseProc;
|
|
|
QbeGen.AbortFunc; .)
|
|
QbeGen.AbortFunc; .)
|
|
|
|
|
|
|
|
|
|
- Block<pn> (. AST.SetChild(astNode, 1, astStmt);
|
|
|
|
|
- AST.SetChild(astNode, 2, astBlkDecls);
|
|
|
|
|
- astDecl := astNode;
|
|
|
|
|
- Lower.ScopeLeave;
|
|
|
|
|
- SymTab.CloseProc;
|
|
|
|
|
- QbeGen.EndFunc(
|
|
|
|
|
- SymTab.ProcRes(pn)); .) ) .
|
|
|
|
|
- Block<pn: SymTab.Name> (. VAR m2: SymTab.Name; .)
|
|
|
|
|
- = (. IF twoPhaseParse AND (procNest = 0) THEN
|
|
|
|
|
- ProbeBody(pn) END;
|
|
|
|
|
- INC(procNest); .)
|
|
|
|
|
|
|
+ (. allowDefer := TRUE; deferredBody := FALSE;
|
|
|
|
|
+ curProcNode := astNode; .)
|
|
|
|
|
+ Block<pn> (. IF deferredBody THEN
|
|
|
|
|
+ (* body skipped: close like
|
|
|
|
|
+ FORWARD (no body node) *)
|
|
|
|
|
+ astDecl := astNode;
|
|
|
|
|
+ Lower.ScopeLeave;
|
|
|
|
|
+ SymTab.CloseProc;
|
|
|
|
|
+ QbeGen.AbortFunc
|
|
|
|
|
+ ELSE
|
|
|
|
|
+ AST.SetChild(astNode, 1, astStmt);
|
|
|
|
|
+ AST.SetChild(astNode, 2, astBlkDecls);
|
|
|
|
|
+ astDecl := astNode;
|
|
|
|
|
+ Lower.ScopeLeave;
|
|
|
|
|
+ SymTab.CloseProc;
|
|
|
|
|
+ QbeGen.EndFunc(
|
|
|
|
|
+ SymTab.ProcRes(pn))
|
|
|
|
|
+ END; .) ) .
|
|
|
|
|
+ Block<pn: SymTab.Name> (. VAR m2: SymTab.Name; dfr: BOOLEAN; .)
|
|
|
|
|
+ = (. dfr := FALSE;
|
|
|
|
|
+ IF allowDefer AND twoPhaseParse
|
|
|
|
|
+ AND NOT repassing THEN
|
|
|
|
|
+ dfr := DeferSkip(pn)
|
|
|
|
|
+ END;
|
|
|
|
|
+ allowDefer := FALSE;
|
|
|
|
|
+ IF NOT dfr THEN INC(procNest) END; .)
|
|
|
DeclSeq (. astBlkDecls := astDecl;
|
|
DeclSeq (. astBlkDecls := astDecl;
|
|
|
astStmt := AST.NoNode;
|
|
astStmt := AST.NoNode;
|
|
|
nLab := 0; nGot := 0; .)
|
|
nLab := 0; nGot := 0; .)
|
|
@@ -1558,16 +1738,22 @@ PRODUCTIONS
|
|
|
body: mark it so Lower
|
|
body: mark it so Lower
|
|
|
does not read it as a
|
|
does not read it as a
|
|
|
definition heading *)
|
|
definition heading *)
|
|
|
- IF astStmt = AST.NoNode THEN
|
|
|
|
|
- astStmt :=
|
|
|
|
|
- AST.MakeNode(AST.NkBlock)
|
|
|
|
|
|
|
+ IF NOT dfr THEN
|
|
|
|
|
+ IF astStmt = AST.NoNode THEN
|
|
|
|
|
+ astStmt :=
|
|
|
|
|
+ AST.MakeNode(AST.NkBlock)
|
|
|
|
|
+ END
|
|
|
END; .)
|
|
END; .)
|
|
|
[ StatSeq ] ]
|
|
[ StatSeq ] ]
|
|
|
"END"
|
|
"END"
|
|
|
GetIdent<m2> (. IF NOT SymTab.Equal(pn, m2) THEN
|
|
GetIdent<m2> (. IF NOT SymTab.Equal(pn, m2) THEN
|
|
|
SemError(202) END;
|
|
SemError(202) END;
|
|
|
- DEC(procNest);
|
|
|
|
|
|
|
+ IF NOT dfr THEN DEC(procNest) END;
|
|
|
CheckGotos; .) .
|
|
CheckGotos; .) .
|
|
|
|
|
+ (* Two-phase, phase 2 (milestone 2c): re-parse every deferred module-
|
|
|
|
|
+ level body once the unit's declarations are complete. Inlined at
|
|
|
|
|
+ both call sites because a stand-alone epsilon production is
|
|
|
|
|
+ "unreachable" to Coco/R (referenced only from actions). *)
|
|
|
StatSeq (. VAR astSeq, astTail: AST.Node; .)
|
|
StatSeq (. VAR astSeq, astTail: AST.Node; .)
|
|
|
= (. astSeq := AST.NoNode;
|
|
= (. astSeq := AST.NoNode;
|
|
|
astTail := AST.NoNode; .)
|
|
astTail := AST.NoNode; .)
|