|
|
@@ -73,6 +73,10 @@ VAR
|
|
|
astNArgs: CARDINAL;
|
|
|
astArgs: ARRAY [0 .. 31] OF AST.Node;
|
|
|
|
|
|
+ (* Statement-AST result slot (slice 5): the most recently parsed
|
|
|
+ statement. StatSeq folds these into an NkBlock. *)
|
|
|
+ astStmt: AST.Node;
|
|
|
+
|
|
|
(* Class of the method named by the last `obj.Method` designator
|
|
|
(InvalidType when the callee is an ordinary procedure). Set by
|
|
|
Design, consumed by the following ArgList. *)
|
|
|
@@ -140,6 +144,19 @@ PROCEDURE FwdVarFlush;
|
|
|
nFvarRefs := 0
|
|
|
END FwdVarFlush;
|
|
|
|
|
|
+PROCEDURE AstCallNode (callee: AST.Node) : AST.Node;
|
|
|
+(* Builds NkCall(callee, actuals...) from astArgs/astNArgs. *)
|
|
|
+ VAR n: AST.Node; j: CARDINAL;
|
|
|
+ BEGIN
|
|
|
+ n := AST.MakeNode(AST.NkCall);
|
|
|
+ AST.SetChild(n, 0, callee);
|
|
|
+ j := 0;
|
|
|
+ WHILE (j < astNArgs) AND (AST.NChild(n) < AST.MaxChild) DO
|
|
|
+ AST.SetChild(n, AST.NChild(n), astArgs[j]); INC(j)
|
|
|
+ END;
|
|
|
+ RETURN n
|
|
|
+ END AstCallNode;
|
|
|
+
|
|
|
CHARACTERS
|
|
|
eol = CHR(13) .
|
|
|
lf = CHR(10) .
|
|
|
@@ -169,7 +186,7 @@ TOKENS
|
|
|
|
|
|
PRODUCTIONS
|
|
|
M2
|
|
|
- = (. AST.Init; twoPhase := TRUE; astCur := AST.NoNode; .)
|
|
|
+ = (. AST.Init; twoPhase := TRUE; astCur := AST.NoNode; astStmt := AST.NoNode; .)
|
|
|
Unit "." .
|
|
|
(* Units: program modules compile fully; DEFINITION and
|
|
|
IMPLEMENTATION modules parse + check now but lower in step 4
|
|
|
@@ -988,12 +1005,20 @@ PRODUCTIONS
|
|
|
"END"
|
|
|
GetIdent<m2> (. IF NOT SymTab.Equal(pn, m2) THEN
|
|
|
SemError(202) END; .) .
|
|
|
- StatSeq
|
|
|
- = Statement { ";" [ Statement ] } .
|
|
|
+ StatSeq (. VAR astSeq: AST.Node; .)
|
|
|
+ = (. astSeq := AST.MakeNode(AST.NkBlock); .)
|
|
|
+ Statement (. IF astStmt # AST.NoNode THEN
|
|
|
+ AST.SetChild(astSeq,
|
|
|
+ AST.NChild(astSeq), astStmt) END; .)
|
|
|
+ { ";" [ Statement (. IF astStmt # AST.NoNode THEN
|
|
|
+ AST.SetChild(astSeq,
|
|
|
+ AST.NChild(astSeq), astStmt) END; .) ] }
|
|
|
+ (. astStmt := astSeq; .) .
|
|
|
(* Trailing/empty statements (`a; ; b`, Coco/R's generated `;;`)
|
|
|
are accepted: the statement after ';' is optional. *)
|
|
|
Statement (. VAR lx: QbeGen.QVal; .)
|
|
|
- = AssOrCall
|
|
|
+ = (. astStmt := AST.NoNode; .)
|
|
|
+ ( AssOrCall
|
|
|
| IfStat
|
|
|
| WhileStat
|
|
|
| RepeatStat
|
|
|
@@ -1009,7 +1034,8 @@ PRODUCTIONS
|
|
|
| InclExclStat
|
|
|
| "EXIT" (. IF QbeGen.TopLoop(lx) THEN
|
|
|
QbeGen.Jmp(lx)
|
|
|
- ELSE SemError(230) END; .) .
|
|
|
+ ELSE SemError(230) END;
|
|
|
+ astStmt := AST.MakeNode(AST.NkExit); .) ) .
|
|
|
(* INCL(set, elem) / EXCL(set, elem): PIM set-element builtins. *)
|
|
|
InclExclStat (. VAR at, et2: SymTab.TypeIndex;
|
|
|
dk: INTEGER;
|
|
|
@@ -1148,8 +1174,8 @@ PRODUCTIONS
|
|
|
WithStat (. VAR nW: CARDINAL; .)
|
|
|
= "WITH" (. nW := 0; .)
|
|
|
WithItem<nW> { "," WithItem<nW> }
|
|
|
- "DO" [ StatSeq ] "END"
|
|
|
- (. WHILE nW > 0 DO
|
|
|
+ "DO" [ StatSeq ] "END" (. astStmt := AST.NoNode;
|
|
|
+ WHILE nW > 0 DO
|
|
|
SymTab.PopScope;
|
|
|
QbeGen.PopWith;
|
|
|
DEC(nW)
|
|
|
@@ -1179,10 +1205,14 @@ PRODUCTIONS
|
|
|
ct2, res0: SymTab.TypeIndex;
|
|
|
q2, mg0: QbeGen.QVal;
|
|
|
isR, conv, wconv: BOOLEAN;
|
|
|
- called, sfx: BOOLEAN; .)
|
|
|
+ called, sfx: BOOLEAN;
|
|
|
+ astLhs: AST.Node; .)
|
|
|
= Design<dt, dk, qd, qn, sfx>
|
|
|
+ (. astLhs := astCur; astNArgs := 0; .)
|
|
|
( ":="
|
|
|
- Expr<et, qe> (. IF (dt # SymTab.InvalidType)
|
|
|
+ Expr<et, qe> (. astStmt := AST.MakeBin(
|
|
|
+ AST.NkAssign, 0, astLhs, astCur);
|
|
|
+ IF (dt # SymTab.InvalidType)
|
|
|
AND (dk # SymTab.KindVar)
|
|
|
AND (dk # SymTab.KindParam)
|
|
|
AND (dk # SymTab.KindField) THEN
|
|
|
@@ -1291,11 +1321,13 @@ PRODUCTIONS
|
|
|
END
|
|
|
END; .)
|
|
|
| ArgList<qn, dt, qd, FALSE, FALSE, methCls, ct2, q2, called>
|
|
|
+ (. astStmt := AstCallNode(astLhs); .)
|
|
|
| (* bare `P;`: proper parameterless
|
|
|
procedure call; anything else
|
|
|
here is 233 (was a bare syntax
|
|
|
error before 4.2) *)
|
|
|
- (. IF (dk = SymTab.KindProc)
|
|
|
+ (. astStmt := AstCallNode(astLhs);
|
|
|
+ IF (dk = SymTab.KindProc)
|
|
|
AND NOT sfx THEN
|
|
|
res0 := SymTab.ProcRes(qn);
|
|
|
IF res0 #
|
|
|
@@ -1517,62 +1549,97 @@ PRODUCTIONS
|
|
|
IfStat (. VAR t: SymTab.TypeIndex;
|
|
|
q, lThen, lElse, lEnd:
|
|
|
QbeGen.QVal;
|
|
|
- hasElse: BOOLEAN; .)
|
|
|
- = "IF" (. hasElse := FALSE; .)
|
|
|
- Expr<t, q> (. IF NOT SymTab.BoolCheck(t) THEN
|
|
|
+ hasElse: BOOLEAN;
|
|
|
+ astCond, astIf, astLast,
|
|
|
+ astNode: AST.Node; .)
|
|
|
+ = "IF" (. hasElse := FALSE; .)
|
|
|
+ Expr<t, q> (. astCond := astCur; astStmt := AST.NoNode;
|
|
|
+ IF NOT SymTab.BoolCheck(t) THEN
|
|
|
SemError(214) END;
|
|
|
QbeGen.NewLabel(lThen);
|
|
|
QbeGen.NewLabel(lElse);
|
|
|
QbeGen.NewLabel(lEnd);
|
|
|
QbeGen.Jnz(q, lThen, lElse);
|
|
|
QbeGen.EmitLabel(lThen); .)
|
|
|
- "THEN" [ StatSeq ] (. QbeGen.Jmp(lEnd); .)
|
|
|
+ "THEN" [ StatSeq ] (. astNode := AST.MakeNode(AST.NkIf);
|
|
|
+ AST.SetChild(astNode, 0, astCond);
|
|
|
+ AST.SetChild(astNode, 1, astStmt);
|
|
|
+ astIf := astNode;
|
|
|
+ astLast := astNode;
|
|
|
+ QbeGen.Jmp(lEnd); .)
|
|
|
{ "ELSIF" (. QbeGen.EmitLabel(lElse);
|
|
|
QbeGen.NewLabel(lElse); .)
|
|
|
- Expr<t, q> (. IF NOT SymTab.BoolCheck(t) THEN
|
|
|
+ Expr<t, q> (. astCond := astCur; astStmt := AST.NoNode;
|
|
|
+ IF NOT SymTab.BoolCheck(t) THEN
|
|
|
SemError(214) END;
|
|
|
QbeGen.NewLabel(lThen);
|
|
|
QbeGen.Jnz(q, lThen, lElse);
|
|
|
QbeGen.EmitLabel(lThen); .)
|
|
|
- "THEN" [ StatSeq ] (. QbeGen.Jmp(lEnd); .) }
|
|
|
+ "THEN" [ StatSeq ] (. astNode := AST.MakeNode(AST.NkIf);
|
|
|
+ AST.SetChild(astNode, 0, astCond);
|
|
|
+ AST.SetChild(astNode, 1, astStmt);
|
|
|
+ AST.SetChild(astLast, 2, astNode);
|
|
|
+ astLast := astNode;
|
|
|
+ QbeGen.Jmp(lEnd); .) }
|
|
|
[ "ELSE" (. QbeGen.EmitLabel(lElse);
|
|
|
- hasElse := TRUE; .)
|
|
|
- [ StatSeq ] ]
|
|
|
- "END" (. IF hasElse THEN
|
|
|
+ hasElse := TRUE;
|
|
|
+ astStmt := AST.NoNode; .)
|
|
|
+ [ StatSeq ] (. AST.SetChild(astLast, 2, astStmt); .) ]
|
|
|
+ "END" (. astStmt := astIf;
|
|
|
+ IF hasElse THEN
|
|
|
QbeGen.EmitLabel(lEnd)
|
|
|
ELSE QbeGen.EmitLabel(lElse);
|
|
|
QbeGen.EmitLabel(lEnd)
|
|
|
END; .) .
|
|
|
WhileStat (. VAR t: SymTab.TypeIndex;
|
|
|
q, lTop, lBody, lEnd:
|
|
|
- QbeGen.QVal; .)
|
|
|
- = "WHILE" (. QbeGen.NewLabel(lTop);
|
|
|
+ QbeGen.QVal;
|
|
|
+ astCond, astNode: AST.Node; .)
|
|
|
+ = "WHILE" (. astStmt := AST.NoNode;
|
|
|
+ QbeGen.NewLabel(lTop);
|
|
|
QbeGen.NewLabel(lBody);
|
|
|
QbeGen.NewLabel(lEnd);
|
|
|
QbeGen.EmitLabel(lTop); .)
|
|
|
- Expr<t, q> (. IF NOT SymTab.BoolCheck(t) THEN
|
|
|
+ Expr<t, q> (. astCond := astCur;
|
|
|
+ IF NOT SymTab.BoolCheck(t) THEN
|
|
|
SemError(214) END;
|
|
|
QbeGen.Jnz(q, lBody, lEnd);
|
|
|
QbeGen.EmitLabel(lBody); .)
|
|
|
- "DO" [ StatSeq ] (. QbeGen.Jmp(lTop); .)
|
|
|
+ "DO" [ StatSeq ] (. astNode := AST.MakeNode(AST.NkWhile);
|
|
|
+ AST.SetChild(astNode, 0, astCond);
|
|
|
+ AST.SetChild(astNode, 1, astStmt);
|
|
|
+ astStmt := astNode;
|
|
|
+ QbeGen.Jmp(lTop); .)
|
|
|
"END" (. QbeGen.EmitLabel(lEnd); .) .
|
|
|
RepeatStat (. VAR t: SymTab.TypeIndex;
|
|
|
- q, lTop, lEnd: QbeGen.QVal; .)
|
|
|
- = "REPEAT" (. QbeGen.NewLabel(lTop);
|
|
|
+ q, lTop, lEnd: QbeGen.QVal;
|
|
|
+ astCond, astBody, astNode:
|
|
|
+ AST.Node; .)
|
|
|
+ = "REPEAT" (. astStmt := AST.NoNode;
|
|
|
+ QbeGen.NewLabel(lTop);
|
|
|
QbeGen.NewLabel(lEnd);
|
|
|
QbeGen.EmitLabel(lTop); .)
|
|
|
- [ StatSeq ]
|
|
|
- "UNTIL" Expr<t, q> (. IF NOT SymTab.BoolCheck(t) THEN
|
|
|
+ [ StatSeq ] (. astBody := astStmt; .)
|
|
|
+ "UNTIL" Expr<t, q> (. astCond := astCur;
|
|
|
+ astNode := AST.MakeNode(AST.NkRepeat);
|
|
|
+ AST.SetChild(astNode, 0, astBody);
|
|
|
+ AST.SetChild(astNode, 1, astCond);
|
|
|
+ astStmt := astNode;
|
|
|
+ IF NOT SymTab.BoolCheck(t) THEN
|
|
|
SemError(214) END;
|
|
|
QbeGen.Jnz(q, lEnd, lTop);
|
|
|
QbeGen.EmitLabel(lEnd); .) .
|
|
|
- LoopStat (. VAR lTop, lEnd: QbeGen.QVal; .)
|
|
|
+ LoopStat (. VAR lTop, lEnd: QbeGen.QVal; astNode: AST.Node; .)
|
|
|
= "LOOP" (. QbeGen.NewLabel(lTop);
|
|
|
+ astStmt := AST.NoNode;
|
|
|
QbeGen.NewLabel(lEnd);
|
|
|
QbeGen.PushLoop(lEnd);
|
|
|
QbeGen.EmitLabel(lTop); .)
|
|
|
[ StatSeq ]
|
|
|
- "END" (. QbeGen.Jmp(lTop);
|
|
|
+ "END" (. astNode := AST.MakeNode(AST.NkLoop);
|
|
|
+ AST.SetChild(astNode, 0, astStmt);
|
|
|
+ astStmt := astNode;
|
|
|
+ QbeGen.Jmp(lTop);
|
|
|
QbeGen.PopLoop;
|
|
|
QbeGen.EmitLabel(lEnd); .) .
|
|
|
(* FOR with static-sign BY (literal, non-zero; 220 otherwise).
|
|
|
@@ -1586,9 +1653,14 @@ PRODUCTIONS
|
|
|
lTop, lBody, lEnd:
|
|
|
QbeGen.QVal;
|
|
|
by: INTEGER;
|
|
|
- ok: BOOLEAN; .)
|
|
|
+ ok: BOOLEAN;
|
|
|
+ astVar, astLo, astHi, astBy,
|
|
|
+ astNode: AST.Node; .)
|
|
|
= "FOR" (. by := 1; .)
|
|
|
- GetIdent<lv> (. ok := SymTab.Lookup(lv);
|
|
|
+ GetIdent<lv> (. astVar := AST.MakeLeaf(AST.NkIdent, lv);
|
|
|
+ astStmt := AST.NoNode;
|
|
|
+ astBy := AST.NoNode;
|
|
|
+ ok := SymTab.Lookup(lv);
|
|
|
IF NOT ok THEN
|
|
|
SemError(201)
|
|
|
ELSIF (SymTab.SymKind(lv) #
|
|
|
@@ -1600,13 +1672,16 @@ PRODUCTIONS
|
|
|
SymTab.SymType(lv)) THEN
|
|
|
SemError(220); ok := FALSE
|
|
|
END; .)
|
|
|
- ":=" Expr<tlo, qlo> (. IF NOT SymTab.IsIntFamily(tlo) THEN
|
|
|
+ ":=" Expr<tlo, qlo> (. astLo := astCur;
|
|
|
+ IF NOT SymTab.IsIntFamily(tlo) THEN
|
|
|
SemError(220); ok := FALSE
|
|
|
END; .)
|
|
|
- "TO" Expr<thi, qhi> (. IF NOT SymTab.IsIntFamily(thi) THEN
|
|
|
+ "TO" Expr<thi, qhi> (. astHi := astCur;
|
|
|
+ IF NOT SymTab.IsIntFamily(thi) THEN
|
|
|
SemError(220); ok := FALSE
|
|
|
END; .)
|
|
|
- [ "BY" Expr<tby, qby> (. IF (tby #
|
|
|
+ [ "BY" Expr<tby, qby> (. astBy := astCur;
|
|
|
+ IF (tby #
|
|
|
SymTab.InvalidType)
|
|
|
AND NOT SymTab.IsIntFamily(tby) THEN
|
|
|
SemError(220); ok := FALSE
|
|
|
@@ -1634,7 +1709,14 @@ PRODUCTIONS
|
|
|
QbeGen.Jnz(qk, lBody, lEnd);
|
|
|
QbeGen.EmitLabel(lBody); .)
|
|
|
[ StatSeq ]
|
|
|
- "END" (. IF ok THEN
|
|
|
+ "END" (. astNode := AST.MakeNode(AST.NkFor);
|
|
|
+ AST.SetChild(astNode, 0, astVar);
|
|
|
+ AST.SetChild(astNode, 1, astLo);
|
|
|
+ AST.SetChild(astNode, 2, astHi);
|
|
|
+ AST.SetChild(astNode, 3, astBy);
|
|
|
+ AST.SetChild(astNode, 4, astStmt);
|
|
|
+ astStmt := astNode;
|
|
|
+ IF ok THEN
|
|
|
QbeGen.LoadVar(lv, FALSE,
|
|
|
qt);
|
|
|
QbeGen.IntStr(by, qb);
|
|
|
@@ -1651,7 +1733,8 @@ PRODUCTIONS
|
|
|
"OF" CaseAlt<tsel, qsel, lEnd>
|
|
|
{ "|" CaseAlt<tsel, qsel, lEnd> }
|
|
|
[ "ELSE" [ StatSeq ] ]
|
|
|
- "END" (. QbeGen.EmitLabel(lEnd); .) .
|
|
|
+ "END" (. astStmt := AST.NoNode;
|
|
|
+ QbeGen.EmitLabel(lEnd); .) .
|
|
|
(* Compare-chain lowering: each alternative ends its match-tests
|
|
|
with "jmp lAfter", so the no-match fallthrough skips the body:
|
|
|
"cmp; jnz(lBody,lF); lF: ... ; jmp lAfter; lBody: S; jmp lEnd;
|
|
|
@@ -1720,10 +1803,16 @@ PRODUCTIONS
|
|
|
ReturnStat (. VAR t: SymTab.TypeIndex;
|
|
|
q, qt: QbeGen.QVal;
|
|
|
res: SymTab.TypeIndex;
|
|
|
- hadE, conv: BOOLEAN; .)
|
|
|
- = "RETURN" (. hadE := FALSE; .)
|
|
|
- [ Expr<t, q> (. hadE := TRUE; .) ]
|
|
|
- (. conv := FALSE;
|
|
|
+ hadE, conv: BOOLEAN;
|
|
|
+ astVal, astNode: AST.Node; .)
|
|
|
+ = "RETURN" (. hadE := FALSE; astStmt := AST.NoNode; .)
|
|
|
+ [ Expr<t, q> (. hadE := TRUE; astVal := astCur; .) ]
|
|
|
+ (. astNode := AST.MakeNode(AST.NkReturn);
|
|
|
+ IF hadE THEN
|
|
|
+ AST.SetChild(astNode, 0, astVal)
|
|
|
+ END;
|
|
|
+ astStmt := astNode;
|
|
|
+ conv := FALSE;
|
|
|
IF NOT SymTab.InProc() THEN
|
|
|
SemError(232)
|
|
|
ELSE res := SymTab.CurRes();
|
|
|
@@ -1753,8 +1842,16 @@ PRODUCTIONS
|
|
|
END
|
|
|
END; .) .
|
|
|
HaltStat (. VAR t: SymTab.TypeIndex;
|
|
|
- q: QbeGen.QVal; .)
|
|
|
- = "HALT" [ "(" Expr<t, q> ")" ] (. QbeGen.HaltQ; .) .
|
|
|
+ q: QbeGen.QVal;
|
|
|
+ astVal, astNode: AST.Node; .)
|
|
|
+ = "HALT" (. astVal := AST.NoNode; .)
|
|
|
+ [ "(" Expr<t, q> (. astVal := astCur; .) ")" ]
|
|
|
+ (. astNode := AST.MakeNode(AST.NkHalt);
|
|
|
+ IF astVal # AST.NoNode THEN
|
|
|
+ AST.SetChild(astNode, 0, astVal)
|
|
|
+ END;
|
|
|
+ astStmt := astNode;
|
|
|
+ QbeGen.HaltQ; .) .
|
|
|
(* Designator: scalar loads, array addresses, and index suffixes.
|
|
|
Each index descends one level (bounds-checked, trap on breach);
|
|
|
nested levels reload the inner descriptor address. q ends as the
|