|
@@ -60,6 +60,14 @@ VAR
|
|
|
the emitted output is unchanged. *)
|
|
the emitted output is unchanged. *)
|
|
|
twoPhase: BOOLEAN;
|
|
twoPhase: BOOLEAN;
|
|
|
|
|
|
|
|
|
|
+ (* Two-phase parse (docs/plan-two-phase-parse.md), milestone 1. When
|
|
|
|
|
+ TRUE each outermost procedure/method body is shadow-skipped at the
|
|
|
|
|
+ token level and the skip verified before the body parses normally.
|
|
|
|
|
+ Set by the driver (-twophase); default off. `procNest` counts the
|
|
|
|
|
+ Blocks being parsed so only outermost bodies probe. *)
|
|
|
|
|
+ twoPhaseParse: BOOLEAN;
|
|
|
|
|
+ procNest: CARDINAL;
|
|
|
|
|
+
|
|
|
(* 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
|
|
|
productions save it into locals before parsing the next operand.
|
|
productions save it into locals before parsing the next operand.
|
|
@@ -306,6 +314,75 @@ PROCEDURE GetUnit (): AST.Node;
|
|
|
RETURN astUnit
|
|
RETURN astUnit
|
|
|
END GetUnit;
|
|
END GetUnit;
|
|
|
|
|
|
|
|
|
|
+PROCEDURE SetTwoPhase (b: BOOLEAN);
|
|
|
|
|
+(* Driver switch for the two-phase parse probe (milestone 1). *)
|
|
|
|
|
+ BEGIN
|
|
|
|
|
+ twoPhaseParse := b
|
|
|
|
|
+ END SetTwoPhase;
|
|
|
|
|
+
|
|
|
|
|
+PROCEDURE TokIs (kw: ARRAY OF CHAR): BOOLEAN;
|
|
|
|
|
+(* The current lookahead token's source text equals kw. Compares text,
|
|
|
|
|
+ not token codes, so grammar renumbering cannot desync the skipper. *)
|
|
|
|
|
+ VAR tok: ARRAY [0 .. 63] OF CHAR;
|
|
|
|
|
+ BEGIN
|
|
|
|
|
+ M2S.GetName(M2S.nextPos, M2S.nextLen, tok);
|
|
|
|
|
+ RETURN SymTab.Equal(tok, kw)
|
|
|
|
|
+ END TokIs;
|
|
|
|
|
+
|
|
|
|
|
+PROCEDURE Adv;
|
|
|
|
|
+(* Advance one token: exactly the parser frame's Get for a grammar
|
|
|
|
|
+ without pragmas (its pragma branch is empty and the scanner never
|
|
|
|
|
+ returns a token above maxT, so the repeat loop runs once). A
|
|
|
|
|
+ separate name because the frame's Get is declared later in this
|
|
|
|
|
+ module: a bare Get here would bind the imported M2S.Get (which takes
|
|
|
|
|
+ a parameter) under single-pass compilers. *)
|
|
|
|
|
+ BEGIN
|
|
|
|
|
+ M2S.Get(sym);
|
|
|
|
|
+ INC(errDist)
|
|
|
|
|
+ END Adv;
|
|
|
|
|
+
|
|
|
|
|
+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, 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). *)
|
|
|
|
|
+ VAR savedSym, savedErr, depth, guard: CARDINAL;
|
|
|
|
|
+ nm: SymTab.Name;
|
|
|
|
|
+ ok: BOOLEAN;
|
|
|
|
|
+ BEGIN
|
|
|
|
|
+ savedSym := sym; savedErr := errDist;
|
|
|
|
|
+ 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 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") THEN
|
|
|
|
|
+ INC(depth)
|
|
|
|
|
+ END;
|
|
|
|
|
+ Adv;
|
|
|
|
|
+ INC(guard);
|
|
|
|
|
+ IF guard > 1000000 THEN ok := FALSE; EXIT END
|
|
|
|
|
+ END;
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ Adv;
|
|
|
|
|
+ M2S.GetName(M2S.nextPos, M2S.nextLen, nm);
|
|
|
|
|
+ IF (sym = 0) OR NOT SymTab.Equal(nm, pn) THEN ok := FALSE END
|
|
|
|
|
+ END;
|
|
|
|
|
+ M2S.PopPos;
|
|
|
|
|
+ sym := savedSym; errDist := savedErr;
|
|
|
|
|
+ IF NOT ok THEN
|
|
|
|
|
+ FileIO.WriteString(FileIO.StdOut, "twophase probe mismatch in ");
|
|
|
|
|
+ FileIO.WriteString(FileIO.StdOut, pn);
|
|
|
|
|
+ FileIO.WriteLn(FileIO.StdOut)
|
|
|
|
|
+ END
|
|
|
|
|
+ END ProbeBody;
|
|
|
|
|
+
|
|
|
CHARACTERS
|
|
CHARACTERS
|
|
|
eol = CHR(13) .
|
|
eol = CHR(13) .
|
|
|
lf = CHR(10) .
|
|
lf = CHR(10) .
|
|
@@ -337,7 +414,8 @@ PRODUCTIONS
|
|
|
M2
|
|
M2
|
|
|
= (. twoPhase := TRUE; astCur := AST.NoNode; astStmt := AST.NoNode; astDecl := AST.NoNode; astImp := AST.NoNode; astImpTail := AST.NoNode;
|
|
= (. twoPhase := TRUE; astCur := AST.NoNode; astStmt := AST.NoNode; astDecl := AST.NoNode; astImp := AST.NoNode; astImpTail := AST.NoNode;
|
|
|
astImp := AST.NoNode;
|
|
astImp := AST.NoNode;
|
|
|
- astImpTail := AST.NoNode; .)
|
|
|
|
|
|
|
+ astImpTail := AST.NoNode;
|
|
|
|
|
+ procNest := 0; .)
|
|
|
Unit "." .
|
|
Unit "." .
|
|
|
(* Units: program modules compile fully; DEFINITION and
|
|
(* Units: program modules compile fully; DEFINITION and
|
|
|
IMPLEMENTATION modules parse + check now but lower in step 4
|
|
IMPLEMENTATION modules parse + check now but lower in step 4
|
|
@@ -1432,7 +1510,10 @@ PRODUCTIONS
|
|
|
QbeGen.EndFunc(
|
|
QbeGen.EndFunc(
|
|
|
SymTab.ProcRes(pn)); .) ) .
|
|
SymTab.ProcRes(pn)); .) ) .
|
|
|
Block<pn: SymTab.Name> (. VAR m2: SymTab.Name; .)
|
|
Block<pn: SymTab.Name> (. VAR m2: SymTab.Name; .)
|
|
|
- = DeclSeq (. astBlkDecls := astDecl;
|
|
|
|
|
|
|
+ = (. IF twoPhaseParse AND (procNest = 0) THEN
|
|
|
|
|
+ ProbeBody(pn) END;
|
|
|
|
|
+ INC(procNest); .)
|
|
|
|
|
+ DeclSeq (. astBlkDecls := astDecl;
|
|
|
astStmt := AST.NoNode;
|
|
astStmt := AST.NoNode;
|
|
|
nLab := 0; nGot := 0; .)
|
|
nLab := 0; nGot := 0; .)
|
|
|
[ "BEGIN" (. (* an empty body is still a
|
|
[ "BEGIN" (. (* an empty body is still a
|
|
@@ -1447,6 +1528,7 @@ PRODUCTIONS
|
|
|
"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);
|
|
|
CheckGotos; .) .
|
|
CheckGotos; .) .
|
|
|
StatSeq (. VAR astSeq, astTail: AST.Node; .)
|
|
StatSeq (. VAR astSeq, astTail: AST.Node; .)
|
|
|
= (. astSeq := AST.NoNode;
|
|
= (. astSeq := AST.NoNode;
|