فهرست منبع

step3: procedures — nested, params, recursion, FORWARD, RETURN; 49/49 tests green

Eric Streit 4 هفته پیش
والد
کامیت
38a2fad7bf
91فایلهای تغییر یافته به همراه2535 افزوده شده و 500 حذف شده
  1. BIN
      M2c
  2. 367 103
      M2c.atg
  3. 63 58
      M2c.err
  4. 7 6
      M2c.lst
  5. 67 59
      M2c.mod
  6. BIN
      M2c.o
  7. 425 208
      M2cP.mod
  8. BIN
      M2cP.o
  9. 56 53
      M2cS.mod
  10. BIN
      M2cS.o
  11. 80 1
      MGen.def
  12. 382 9
      MGen.mod
  13. BIN
      MGen.o
  14. BIN
      PBadActual.MC4
  15. BIN
      PBadArity.MC4
  16. BIN
      PBadCall.MC4
  17. BIN
      PBadFwd.MC4
  18. BIN
      PBadNobody.MC4
  19. BIN
      PBadProper.MC4
  20. BIN
      PBadRet1.MC4
  21. BIN
      PBadRet2.MC4
  22. BIN
      PBadRet3.MC4
  23. BIN
      PBadUndecl.MC4
  24. BIN
      PChain.MC4
  25. BIN
      PEmpty.MC4
  26. BIN
      PFact.MC4
  27. BIN
      PFib.MC4
  28. BIN
      PFunc.MC4
  29. BIN
      PFwd.MC4
  30. BIN
      PLocal.MC4
  31. BIN
      PMixed.MC4
  32. BIN
      PNested.MC4
  33. BIN
      PParams.MC4
  34. BIN
      PProc.MC4
  35. BIN
      PRealFn.MC4
  36. BIN
      PRet.MC4
  37. BIN
      PVarPar.MC4
  38. 89 0
      SymTab.def
  39. 319 0
      SymTab.mod
  40. BIN
      SymTab.o
  41. 4 1
      compiler.frm
  42. 29 1
      run_tests.sh
  43. 1 1
      tests/bad_mismatch.LST
  44. 16 0
      tests/p_bad_actual.LST
  45. 9 0
      tests/p_bad_actual.mod
  46. 16 0
      tests/p_bad_arity.LST
  47. 9 0
      tests/p_bad_arity.mod
  48. 12 0
      tests/p_bad_call.LST
  49. 5 0
      tests/p_bad_call.mod
  50. 18 0
      tests/p_bad_fwd.LST
  51. 11 0
      tests/p_bad_fwd.mod
  52. 14 0
      tests/p_bad_nobody.LST
  53. 7 0
      tests/p_bad_nobody.mod
  54. 16 0
      tests/p_bad_proper.LST
  55. 9 0
      tests/p_bad_proper.mod
  56. 11 0
      tests/p_bad_return1.LST
  57. 4 0
      tests/p_bad_return1.mod
  58. 14 0
      tests/p_bad_return2.LST
  59. 7 0
      tests/p_bad_return2.mod
  60. 16 0
      tests/p_bad_return3.LST
  61. 9 0
      tests/p_bad_return3.mod
  62. 11 0
      tests/p_bad_undecl.LST
  63. 4 0
      tests/p_bad_undecl.mod
  64. 21 0
      tests/p_chain.LST
  65. 15 0
      tests/p_chain.mod
  66. 18 0
      tests/p_empty.LST
  67. 12 0
      tests/p_empty.mod
  68. 15 0
      tests/p_fact.LST
  69. 9 0
      tests/p_fact.mod
  70. 15 0
      tests/p_fib.LST
  71. 9 0
      tests/p_fib.mod
  72. 21 0
      tests/p_forward.LST
  73. 15 0
      tests/p_forward.mod
  74. 15 0
      tests/p_func.LST
  75. 9 0
      tests/p_func.mod
  76. 18 0
      tests/p_local.LST
  77. 12 0
      tests/p_local.mod
  78. 23 0
      tests/p_mixed.LST
  79. 17 0
      tests/p_mixed.mod
  80. 21 0
      tests/p_nested.LST
  81. 15 0
      tests/p_nested.mod
  82. 15 0
      tests/p_params.LST
  83. 9 0
      tests/p_params.mod
  84. 16 0
      tests/p_proc.LST
  85. 10 0
      tests/p_proc.mod
  86. 20 0
      tests/p_realfn.LST
  87. 14 0
      tests/p_realfn.mod
  88. 20 0
      tests/p_return.LST
  89. 14 0
      tests/p_return.mod
  90. 18 0
      tests/p_varpar.LST
  91. 12 0
      tests/p_varpar.mod

+ 367 - 103
M2c.atg

@@ -1,35 +1,41 @@
 COMPILER M2c
-(* Modula-2 program modules without procedures, with an MC64 backend
+(* Modula-2 program modules with procedures, MC64 backend
    (see MGen for details).
 
    - program module only, no DEFINITION / IMPLEMENTATION split
    - no local modules, no EXPORT, no PRIORITY
-   - no ProcedureDeclaration, FormalParameters, ProcedureType,
-     ProcedureCall, ActualParameters, FORWARD, RETURN
-   - statements: assignment, IF, CASE, WHILE, REPEAT, LOOP/EXIT, FOR, WITH
+   - procedures: declarations (nested), value and VAR parameters,
+     functions with RETURN, recursion, FORWARD headings;
+     no procedure types/variables, no cones
+   - statements: assignment, procedure call, IF, CASE, WHILE,
+     REPEAT, LOOP/EXIT, FOR, WITH, RETURN
    - symbol table (SymTab) with static type checking: error codes
-     200/201/202 and 210-224, same lenient rules as the reference
-     implementation (single pass, declare-before-use; INTEGER,
-     CARDINAL and subranges form one integer family; no mixed
-     INTEGER/REAL arithmetic; INTEGER assigns to REAL; 1-character
-     literal is CHAR; InvalidType suppresses follow-on errors)
-   - backend (MGen): emits a single-module MC64 image <ModName>.MC4
-     (current directory), runnable with mcint. Lowered: scalar
-     globals, CONST literals (startup stores), SET OF ordinals
-     (bit masks) with literals, IN, = and #; full scalar
-     expressions on the stack machine; control flow via E0/E1 jumps.
-     Everything else parses and type-checks but gets error 230
-     (not supported in this phase): ARRAY/RECORD/POINTER variables,
-     string variables, WITH, non-literal CONST expressions and BY
-     steps, EXIT outside LOOP, imported names used as values.
+     200/201/202 and 210-224 (as before), plus 231 (procedure
+     forward mismatch or missing body), 232 (bad RETURN), 233
+     (invalid procedure call); same lenient rules (single pass,
+     declare-before-use; INTEGER, CARDINAL and subranges form one
+     integer family; no mixed INTEGER/REAL arithmetic; INTEGER
+     assigns to REAL; 1-character literal is CHAR; InvalidType
+     suppresses follow-on errors)
+   - backend (MGen): single-module MC64 image <ModName>.MC4,
+     runnable with mcint. Procedures take table entries 1..N
+     (0 = module body), run with ENTER frames, called via ED
+     (global), EC (directly nested) or EE (display walk); actuals
+     evaluate left-to-right into temps, then push reversed.
+     Lowered: scalar globals and frame variables, CONST literals,
+     SET masks with literals/IN/=/#; full scalar expressions;
+     control flow via E0/E1 jumps. The rest parses and type-checks
+     but gets error 230: ARRAY/RECORD/POINTER variables, string
+     variables, WITH, non-literal CONST expressions and BY steps,
+     EXIT outside LOOP, imported names used as values.
    - test convention (the language has no I/O): a global
        VAR ExitCode : INTEGER;
      is printed as decimal + CRLF through an embedded helper;
      without it the program just ends.
-   - known semantic edges: INTEGER DIV/MOD truncate toward zero
-     (not Modula-2 floor); CARDINAL values past MAXINT compare as
-     signed; AND/OR are eager (no short-circuit); REAL widens to
-     binary64; assignment targets cost one dead load. *)
+   - known semantic edges: INTEGER DIV/MOD truncate toward zero;
+     CARDINAL past MAXINT compares as signed; AND/OR are eager;
+     REAL widens to binary64; at most 64 procedures, 64 actuals
+     per call, 16 names per FP-section, 8-deep nested calls. *)
 
 IMPORT SymTab, MGen;
 
@@ -63,10 +69,14 @@ PRODUCTIONS
                                            IF ~SymTab.Enter(m1, SymTab.KindModule)
                                            THEN SemError(200) END .)
       ";"
-      { Import } Block GetIdent<m2>     (. IF ~SymTab.Equal(m1, m2)
+      { Import } Block<FALSE> GetIdent<m2>
+                                        (. IF ~SymTab.Equal(m1, m2)
                                            THEN SemError(202) END .)
-      "."                               (. MGen.EndModule;
-                                           SymTab.PrintTable; .) .
+      "."                               (. IF SymTab.AnyForward() THEN
+                                           SemError(231)
+                                         END;
+                                         MGen.EndModule;
+                                         SymTab.PrintTable; .) .
   Import                                (. VAR n: SymTab.Name; .)
     = "FROM"
       GetIdent<n>                       (. IF ~SymTab.Enter(n, SymTab.KindImport)
@@ -81,10 +91,29 @@ PRODUCTIONS
       { ","
         GetIdent<n>                     (. IF ~SymTab.Enter(n, SymTab.KindImport)
                                            THEN SemError(200) END .) } .
-  Block      = { Declaration }
-               [ "BEGIN"                (. MGen.BeginBody; .)
-                 StatSeq ]
-               "END" .
+  Block <isProc: BOOLEAN>               (. VAR began: BOOLEAN; .)
+    =                                   (. began := FALSE; .)
+      { Declaration }
+      [ "BEGIN"                         (. began := TRUE;
+                                           IF isProc THEN
+                                             MGen.ProcEntry(
+                                               SymTab.CurProc(),
+                                               SymTab.ProcNLocals())
+                                           ELSE MGen.BeginBody
+                                           END; .)
+        StatSeq ]
+      "END"                             (. IF isProc THEN
+                                           IF ~began THEN
+                                             MGen.ProcEntry(
+                                               SymTab.CurProc(),
+                                               SymTab.ProcNLocals())
+                                           END;
+                                           IF SymTab.InFunction() THEN
+                                             MGen.PushInt(0)
+                                           END;
+                                           MGen.Leave(SymTab.CurNPar(),
+                                             SymTab.InFunction())
+                                         END; .) .
   Declaration = "CONST"
                           {
                             ConstDecl ";" }
@@ -93,7 +122,8 @@ PRODUCTIONS
                             TypeDecl ";" }
               | "VAR"
                           {
-                            VarDecl ";" } .
+                            VarDecl ";" }
+              | ProcedureDecl ";" .
   ConstDecl                             (. VAR n: SymTab.Name;
                                              t: SymTab.TypeIndex;
                                              lx: MGen.LitStr;
@@ -111,7 +141,9 @@ PRODUCTIONS
                                            MGen.DeclConst(n, lx, t);
                                            MGen.NoEmitExit; .) .
   ConstExpr <VAR t: SymTab.TypeIndex; VAR lx: MGen.LitStr>
-    = Expr<t, lx> .
+                                        (. VAR vD: BOOLEAN;
+                                             vnD: SymTab.Name; .)
+    = Expr<t, lx, vD, vnD> .
   TypeDecl                              (. VAR n: SymTab.Name;
                                              t0, t1: SymTab.TypeIndex; .)
     = GetIdent<n>                       (. IF ~SymTab.Enter(n, SymTab.KindType)
@@ -140,7 +172,9 @@ PRODUCTIONS
                                            i := 0;
                                            WHILE i < SymTab.PendCount() DO
                                              SymTab.PendName(i, nm);
-                                             MGen.DeclVar(nm);
+                                             IF SymTab.SymDepth(nm) = 0 THEN
+                                               MGen.DeclVar(nm)
+                                             END;
                                              INC(i)
                                            END;
                                            SymTab.FixPending(t); .) .
@@ -152,6 +186,65 @@ PRODUCTIONS
         GetIdent<n>                     (. IF ~SymTab.EnterPending(n,
                                              SymTab.KindVar)
                                            THEN SemError(200) END .) } .
+  ProcedureDecl                         (. VAR n, m: SymTab.Name;
+                                             rt: SymTab.TypeIndex;
+                                             hasR, ok: BOOLEAN;
+                                             endL: INTEGER; .)
+    = "PROCEDURE"                       (. hasR := FALSE; .)
+      GetIdent<n>                       (. IF SymTab.IsForward(n) THEN
+                                             SymTab.ReuseProc(n)
+                                           ELSIF ~SymTab.EnterProc(n) THEN
+                                             SemError(200)
+                                           END;
+                                           SymTab.OpenProcScope;
+                                           endL := MGen.NewLabel();
+                                           MGen.Jmp(endL); .)
+      [ "(" FormalParams ")" ]
+      [ ":" QualIdent<rt>               (. hasR := TRUE; .) ]
+                                        (. IF hasR THEN
+                                             ok := SymTab.SetProcRet(rt)
+                                           ELSE
+                                             ok := SymTab.SetProcRet(
+                                                     SymTab.InvalidType)
+                                           END;
+                                           IF ~ok THEN SemError(231) END;
+                                           IF ~SymTab.VerifyProc() THEN
+                                             SemError(231)
+                                           END; .)
+      ";"
+      ( Block<TRUE> GetIdent<m>         (. IF ~SymTab.Equal(n, m) THEN
+                                           SemError(202) END;
+                                           MGen.DefLabel(endL);
+                                           SymTab.CloseProc; .)
+      | "FORWARD"                     (. SymTab.SetForward;
+                                           SymTab.CloseProc;
+                                           MGen.DefLabel(endL); .) ) .
+  FormalParams = FPSection { ";" FPSection } .
+  FPSection                             (. VAR isV: BOOLEAN;
+                                             nn, i: CARDINAL;
+                                             pn: ARRAY [0 .. 15] OF SymTab.Name;
+                                             n: SymTab.Name;
+                                             t: SymTab.TypeIndex; .)
+    =                                   (. isV := FALSE; nn := 0; .)
+      [ "VAR"                           (. isV := TRUE; .) ]
+      GetIdent<n>                       (. IF nn <= HIGH(pn) THEN
+                                             MGen.CopyName(n, pn[nn])
+                                           END;
+                                           INC(nn); .)
+      { "," GetIdent<n>                 (. IF nn <= HIGH(pn) THEN
+                                             MGen.CopyName(n, pn[nn])
+                                           END;
+                                           INC(nn); .) }
+      ":" Type<t>                       (. i := 0;
+                                           WHILE i < nn DO
+                                             IF i <= HIGH(pn) THEN
+                                               IF ~SymTab.EnterParam(
+                                                    pn[i], isV, t) THEN
+                                                 SemError(200)
+                                               END
+                                             END;
+                                             INC(i)
+                                           END; .) .
   QualIdent <VAR t: SymTab.TypeIndex>
                                         (. VAR n, m: SymTab.Name; .)
     = GetIdent<n>                       (. IF ~SymTab.Lookup(n) THEN
@@ -175,7 +268,9 @@ PRODUCTIONS
     | SetType<t> | PointerType<t> .
   SimpleType <VAR t: SymTab.TypeIndex>
                                         (. VAR t1, t2: SymTab.TypeIndex;
-                                             lx1, lx2: MGen.LitStr; .)
+                                             lx1, lx2: MGen.LitStr;
+                                             vD: BOOLEAN;
+                                             vnD: SymTab.Name; .)
     = QualIdent<t> [ "["
                       (. MGen.NoEmitEnter; .)
                       ConstExpr<t1, lx1> (. IF (t1 # SymTab.InvalidType)
@@ -297,23 +392,39 @@ PRODUCTIONS
       "TO"
       Type<b>                           (. t := SymTab.NewPtr(b); .) .
 
-  (* Statements: ProcedureCall and RETURN removed *)
+  (* Statements: RETURN added; calls via AssignOrCall *)
   StatSeq    = Stat { ";"
                      Stat } .
   Stat                                  (. VAR lx: INTEGER; .)
-    = [ Assign | IfStat | CaseStat | WhileStat
-      | RepeatStat | LoopStat | ForStat | WithStat
+    = [ AssignOrCall | IfStat | CaseStat | WhileStat
+      | RepeatStat | LoopStat | ForStat | WithStat | ReturnStat
       | "EXIT"                 (. IF MGen.TopLoop(lx) THEN
                                     MGen.Jmp(lx)
                                   ELSE SemError(230) END; .) ] .
-  Assign                                (. VAR dt, et: SymTab.TypeIndex;
+  AssignOrCall                          (. VAR dt, et: SymTab.TypeIndex;
                                              dk: INTEGER;
                                              bn: SymTab.Name;
-                                             lxe, lxD: MGen.LitStr;
-                                             isR, conv: BOOLEAN; .)
-    = Design<dt, dk, bn, FALSE, lxD> ":="
-      Expr<et, lxe>                     (. IF (dt # SymTab.InvalidType)
+                                             lxD, lxe: MGen.LitStr;
+                                             vE: BOOLEAN;
+                                             vnE: SymTab.Name;
+                                             sfx: BOOLEAN;
+                                             okC: BOOLEAN;
+                                             isR, conv,
+                                             storable: BOOLEAN; .)
+    =                                   (. sfx := FALSE; .)
+      DesignHead<dt, dk, bn, FALSE, lxD>
+      ( DesignTail<dt, dk, bn, FALSE, lxD, sfx> ":="
+                                        (. storable :=
+                                             (dk = SymTab.KindVar)
+                                             OR (dk = SymTab.KindParam)
+                                             OR (dk = SymTab.KindVarPar);
+                                           IF storable THEN
+                                             MGen.StoreSetup(bn)
+                                           END; .)
+        Expr<et, lxe, vE, vnE>          (. IF (dt # SymTab.InvalidType)
                                            & (dk # SymTab.KindVar)
+                                           & (dk # SymTab.KindParam)
+                                           & (dk # SymTab.KindVarPar)
                                            & (dk # SymTab.KindField)
                                            & (dk # SymTab.KindImport) THEN
                                            SemError(210)
@@ -327,19 +438,45 @@ PRODUCTIONS
                                                    = SymTab.ClReal);
                                          conv := isR
                                                  & SymTab.IsIntFamily(et);
-                                         IF dk = SymTab.KindVar THEN
+                                         IF storable THEN
                                            IF conv THEN
-                                             MGen.IntToReal;
+                                             MGen.IntToReal
                                            END;
-                                           MGen.StoreVar(bn)
+                                           MGen.StoreFinish(bn)
                                          ELSE MGen.Drop
+                                         END; .)
+      | CallTail<bn, sfx, FALSE, okC, FALSE>
+      |                             (. MGen.ActBegin(bn);
+                                       IF MGen.ActEnd(bn, FALSE, FALSE) # 0 THEN
+                                         SemError(233)
+                                       END; .) ) .
+  CallTail <pn: SymTab.Name; sfx: BOOLEAN; inExpr: BOOLEAN;
+            VAR ok: BOOLEAN; hasDead: BOOLEAN>
+                                        (. VAR t: SymTab.TypeIndex;
+                                             lx: MGen.LitStr;
+                                             v: BOOLEAN;
+                                             vn: SymTab.Name; .)
+    = "("                               (. ok := FALSE;
+                                           IF hasDead THEN MGen.Drop END;
+                                           MGen.ActBegin(pn); .)
+      [ Expr<t, lx, v, vn>              (. IF MGen.ActValue(t, v, vn) # 0 THEN
+                                           SemError(233)
+                                         END; .)
+        { "," Expr<t, lx, v, vn>        (. IF MGen.ActValue(t, v, vn) # 0 THEN
+                                           SemError(233)
+                                         END; .) } ]
+      ")"                               (. IF MGen.ActEnd(pn, sfx, inExpr) # 0
+                                         THEN SemError(233)
+                                         ELSE ok := TRUE
                                          END; .) .
   IfStat                                (. VAR t: SymTab.TypeIndex;
                                              lxC: MGen.LitStr;
+                                             vC: BOOLEAN;
+                                             vnC: SymTab.Name;
                                              elseL, endL: INTEGER;
                                              hasElse: BOOLEAN; .)
     = "IF"
-      Expr<t, lxC>                      (. IF ~SymTab.BoolCheck(t) THEN
+      Expr<t, lxC, vC, vnC>             (. IF ~SymTab.BoolCheck(t) THEN
                                            SemError(214) END;
                                            elseL := MGen.NewLabel();
                                            endL := MGen.NewLabel();
@@ -349,7 +486,7 @@ PRODUCTIONS
       StatSeq
       { "ELSIF"                         (. MGen.Jmp(endL);
                                            MGen.DefLabel(elseL); .)
-        Expr<t, lxC>                    (. IF ~SymTab.BoolCheck(t) THEN
+        Expr<t, lxC, vC, vnC>           (. IF ~SymTab.BoolCheck(t) THEN
                                            SemError(214) END;
                                            elseL := MGen.NewLabel();
                                            MGen.Jz(elseL); .)
@@ -365,9 +502,11 @@ PRODUCTIONS
                                          MGen.DefLabel(endL); .) .
   CaseStat                              (. VAR st: SymTab.TypeIndex;
                                              lxS: MGen.LitStr;
+                                             vS: BOOLEAN;
+                                             vnS: SymTab.Name;
                                              tmp, endL: INTEGER; .)
     = "CASE"
-      Expr<st, lxS>                     (. tmp := MGen.TempGlobal();
+      Expr<st, lxS, vS, vnS>            (. tmp := MGen.TempGlobal();
                                            MGen.StoreTemp(tmp);
                                            endL := MGen.NewLabel(); .)
       "OF"
@@ -391,6 +530,8 @@ PRODUCTIONS
   Labels <sel: SymTab.TypeIndex; tmp: INTEGER; bodyL: INTEGER>
                                         (. VAR t, t2: SymTab.TypeIndex;
                                              lx1, lx2: MGen.LitStr;
+                                             v1, v2: BOOLEAN;
+                                             vn1, vn2: SymTab.Name;
                                              ta, tb, chunk: INTEGER;
                                              r, hasRange: BOOLEAN; .)
     = ConstExpr<t, lx1>                 (. IF ~SymTab.EqCheck(t, sel) THEN
@@ -430,11 +571,13 @@ PRODUCTIONS
                                            MGen.DefLabel(chunk); .) .
   WhileStat                             (. VAR t: SymTab.TypeIndex;
                                              lxC: MGen.LitStr;
+                                             vC: BOOLEAN;
+                                             vnC: SymTab.Name;
                                              topL, endL: INTEGER; .)
     = "WHILE"                           (. topL := MGen.NewLabel();
                                            endL := MGen.NewLabel();
                                            MGen.DefLabel(topL); .)
-      Expr<t, lxC>                      (. IF ~SymTab.BoolCheck(t) THEN
+      Expr<t, lxC, vC, vnC>             (. IF ~SymTab.BoolCheck(t) THEN
                                            SemError(214) END;
                                            MGen.Jz(endL); .)
       "DO"
@@ -443,12 +586,14 @@ PRODUCTIONS
                                            MGen.DefLabel(endL); .) .
   RepeatStat                            (. VAR t: SymTab.TypeIndex;
                                              lxC: MGen.LitStr;
+                                             vC: BOOLEAN;
+                                             vnC: SymTab.Name;
                                              topL: INTEGER; .)
     = "REPEAT"                          (. topL := MGen.NewLabel();
                                            MGen.DefLabel(topL); .)
       StatSeq
       "UNTIL"
-      Expr<t, lxC>                      (. IF ~SymTab.BoolCheck(t) THEN
+      Expr<t, lxC, vC, vnC>             (. IF ~SymTab.BoolCheck(t) THEN
                                            SemError(214) END;
                                            MGen.Jz(topL); .) .
   LoopStat                              (. VAR topL, exitL: INTEGER; .)
@@ -461,32 +606,54 @@ PRODUCTIONS
                                            MGen.DefLabel(exitL);
                                            MGen.PopLoop; .) .
   ForStat                               (. VAR n, lv: SymTab.Name;
+                                             fk: INTEGER;
                                              lo, hi: SymTab.TypeIndex;
                                              lxLo, lxHi: MGen.LitStr;
+                                             vLo, vHi: BOOLEAN;
+                                             vnLo, vnHi: SymTab.Name;
                                              byV, ht: INTEGER;
                                              lTop, lChk, lEnd: INTEGER;
-                                             neg: BOOLEAN; .)
+                                             neg, storable: BOOLEAN; .)
     = "FOR"
       GetIdent<n>                       (. IF ~SymTab.Lookup(n) THEN
-                                            SemError(201)
+                                            SemError(201);
+                                            fk := -1
                                           ELSIF (SymTab.SymKind(n) #
                                                  SymTab.KindVar)
+                                            & (SymTab.SymKind(n) #
+                                               SymTab.KindParam)
+                                            & (SymTab.SymKind(n) #
+                                               SymTab.KindVarPar)
                                             & (SymTab.SymKind(n) #
                                                SymTab.KindField) THEN
-                                            SemError(220)
+                                            SemError(220);
+                                            fk := -1
                                           ELSIF (SymTab.SymType(n) #
                                                  SymTab.InvalidType)
                                             & ~SymTab.IsIntFamily(
                                                 SymTab.SymType(n)) THEN
-                                            SemError(220) END;
-                                            MGen.CopyName(n, lv); .)
+                                            SemError(220);
+                                            fk := -1
+                                          ELSE
+                                            fk := SymTab.SymKind(n)
+                                          END;
+                                          MGen.CopyName(n, lv);
+                                          storable := (fk = SymTab.KindVar)
+                                            OR (fk = SymTab.KindParam)
+                                            OR (fk = SymTab.KindVarPar);
+                                          IF storable THEN
+                                            MGen.StoreSetup(lv)
+                                          END; .)
       ":="
-      Expr<lo, lxLo>                    (. IF (lo # SymTab.InvalidType)
+      Expr<lo, lxLo, vLo, vnLo>         (. IF (lo # SymTab.InvalidType)
                                            & ~SymTab.IsIntFamily(lo) THEN
                                            SemError(220) END;
-                                           MGen.StoreVar(lv); .)
+                                           IF storable THEN
+                                             MGen.StoreFinish(lv)
+                                           ELSE MGen.Drop
+                                           END; .)
       "TO"
-      Expr<hi, lxHi>                    (. IF (hi # SymTab.InvalidType)
+      Expr<hi, lxHi, vHi, vnHi>         (. IF (hi # SymTab.InvalidType)
                                            & ~SymTab.IsIntFamily(hi) THEN
                                            SemError(220) END;
                                            ht := MGen.TempGlobal();
@@ -500,12 +667,15 @@ PRODUCTIONS
                                            MGen.Jmp(lChk);
                                            MGen.DefLabel(lTop); .)
       StatSeq
-      "END"                             (. MGen.LoadVar(lv);
+      "END"                             (. MGen.PushVar(lv);
                                            MGen.PushInt(byV);
                                            MGen.Add;
-                                           MGen.StoreVar(lv);
+                                           IF storable THEN
+                                             MGen.StoreFinish(lv)
+                                           ELSE MGen.Drop
+                                           END;
                                            MGen.DefLabel(lChk);
-                                           MGen.LoadVar(lv);
+                                           MGen.PushVar(lv);
                                            MGen.LoadTemp(ht);
                                            IF neg THEN MGen.IGe
                                            ELSE MGen.ILe END;
@@ -546,18 +716,54 @@ PRODUCTIONS
                                              SymTab.PopScope;
                                              MGen.NoSupExit
                                            END; .) .
+  ReturnStat                            (. VAR t: SymTab.TypeIndex;
+                                             lx: MGen.LitStr;
+                                             v: BOOLEAN;
+                                             vn: SymTab.Name;
+                                             hasE, doRet, conv: BOOLEAN; .)
+    = "RETURN"                          (. hasE := FALSE; .)
+      [ Expr<t, lx, v, vn>              (. hasE := TRUE;
+                                           doRet := FALSE;
+                                           IF ~SymTab.InProc() THEN
+                                             SemError(232)
+                                           ELSIF ~SymTab.InFunction() THEN
+                                             SemError(232)
+                                           ELSIF ~SymTab.Assignable(
+                                                     t, SymTab.CurRet()) THEN
+                                             SemError(232)
+                                           ELSE doRet := TRUE
+                                           END;
+                                           conv := doRet
+                                                   & SymTab.IsIntFamily(t)
+                                                   & (SymTab.ClassOf(
+                                                        SymTab.CurRet())
+                                                      = SymTab.ClReal);
+                                           IF doRet THEN
+                                             IF conv THEN
+                                               MGen.IntToReal
+                                             END;
+                                             MGen.Leave(
+                                               SymTab.CurNPar(), TRUE)
+                                           ELSE MGen.Drop
+                                           END; .) ]
+                                        (. IF ~hasE THEN
+                                             IF ~SymTab.InProc() THEN
+                                               SemError(232)
+                                             ELSIF SymTab.InFunction() THEN
+                                               SemError(232)
+                                             ELSE MGen.Leave(
+                                               SymTab.CurNPar(), FALSE)
+                                             END
+                                           END; .) .
 
-  (* Expressions: Designator without ActualParameters.
-     Only record fields resolve qualified access; WITH pushes an
-     inner scope so field names also work unqualified in its body.
-     Each expression synthesizes its SymTab type in t and, for
-     single literals, the source text in lx ("" otherwise, used for
-     CONST validation). Values travel on the MC64 stack. *)
-  Design <VAR t: SymTab.TypeIndex; VAR k: INTEGER;
-          VAR bn: SymTab.Name; doLoad: BOOLEAN; VAR lx: MGen.LitStr>
-                                        (. VAR n, m: SymTab.Name;
-                                             it: SymTab.TypeIndex;
-                                             lxI: MGen.LitStr;
+  (* Expressions: Designator without ActualParameters (calls use
+     CallTail). Each expression synthesizes its SymTab type in t,
+     the source text in lx for single literals ("" otherwise), and
+     whether it is a plain variable (v/vn) for VAR actuals.
+     Values travel on the MC64 stack. *)
+  DesignHead <VAR t: SymTab.TypeIndex; VAR k: INTEGER;
+              VAR bn: SymTab.Name; doLoad: BOOLEAN; VAR lx: MGen.LitStr>
+                                        (. VAR n: SymTab.Name;
                                              cls: INTEGER; .)
     = GetIdent<n>                       (. MGen.CopyName(n, bn);
                                            lx[0] := 0C;
@@ -603,7 +809,10 @@ PRODUCTIONS
                                                    MGen.PushInt(0)
                                                  END
                                                END
-                                             ELSIF k = SymTab.KindVar THEN
+                                             ELSIF (k = SymTab.KindVar)
+                                                OR (k = SymTab.KindParam)
+                                                OR (k
+                                                    = SymTab.KindVarPar) THEN
                                                cls := SymTab.ClassOf(t);
                                                IF (cls = SymTab.ClInt)
                                                   OR (cls = SymTab.ClReal)
@@ -614,7 +823,7 @@ PRODUCTIONS
                                                   OR (cls
                                                       = SymTab.ClSet) THEN
                                                  IF doLoad THEN
-                                                   MGen.LoadVar(n)
+                                                   MGen.PushVar(n)
                                                  END
                                                ELSE SemError(230);
                                                  IF doLoad THEN
@@ -634,8 +843,17 @@ PRODUCTIONS
                                                  SemError(230)
                                                END
                                              END
-                                           END; .)
-      { "."                             (. lx[0] := 0C; .)
+                                           END; .) .
+  DesignTail <VAR t: SymTab.TypeIndex; VAR k: INTEGER;
+              VAR bn: SymTab.Name; doLoad: BOOLEAN; VAR lx: MGen.LitStr;
+              VAR sfx: BOOLEAN>
+                                        (. VAR m: SymTab.Name;
+                                             it: SymTab.TypeIndex;
+                                             lxI: MGen.LitStr;
+                                             vI: BOOLEAN;
+                                             vnI: SymTab.Name; .)
+    =                                   (. sfx := FALSE; .)
+      { "."                             (. sfx := TRUE; lx[0] := 0C; .)
         GetIdent<m>                     (. IF t = SymTab.InvalidType THEN
                                            ELSIF SymTab.ClassOf(t) #
                                                  SymTab.ClRecord THEN
@@ -650,8 +868,8 @@ PRODUCTIONS
                                            IF doLoad THEN
                                              MGen.Drop; MGen.PushInt(0)
                                            END; .)
-      | "["
-        Expr<it, lxI>                   (. MGen.Drop;
+      | "["                             (. sfx := TRUE; lx[0] := 0C; .)
+        Expr<it, lxI, vI, vnI>          (. MGen.Drop;
                                            IF t = SymTab.InvalidType THEN
                                            ELSIF SymTab.ClassOf(t) #
                                                  SymTab.ClArray THEN
@@ -670,13 +888,13 @@ PRODUCTIONS
                                              MGen.Drop; MGen.PushInt(0)
                                            END; .)
         { ","
-          Expr<it, lxI>                 (. MGen.Drop;
+          Expr<it, lxI, vI, vnI>        (. MGen.Drop;
                                            IF (it # SymTab.InvalidType)
                                            & ~SymTab.IsIntFamily(it) THEN
                                            SemError(218) END; .) }
         "]"
-      | "^"                             (. lx[0] := 0C;
-                                           IF t = SymTab.InvalidType THEN
+      | "^"                             (. sfx := TRUE; lx[0] := 0C; .)
+                                        (. IF t = SymTab.InvalidType THEN
                                            ELSIF SymTab.ClassOf(t) #
                                                  SymTab.ClPtr THEN
                                              SemError(219);
@@ -687,13 +905,21 @@ PRODUCTIONS
                                            IF doLoad THEN
                                              MGen.Drop; MGen.PushInt(0)
                                            END; .) } .
-  Expr <VAR t: SymTab.TypeIndex; VAR lx: MGen.LitStr>
+  Design <VAR t: SymTab.TypeIndex; VAR k: INTEGER;
+          VAR bn: SymTab.Name; doLoad: BOOLEAN; VAR lx: MGen.LitStr>
+                                        (. VAR sfxD: BOOLEAN; .)
+    = DesignHead<t, k, bn, doLoad, lx>
+      DesignTail<t, k, bn, doLoad, lx, sfxD> .
+  Expr <VAR t: SymTab.TypeIndex; VAR lx: MGen.LitStr;
+        VAR v: BOOLEAN; VAR vn: SymTab.Name>
                                         (. VAR t2: SymTab.TypeIndex;
                                              tc, op: INTEGER;
                                              lx2: MGen.LitStr;
+                                             v2: BOOLEAN;
+                                             vn2: SymTab.Name;
                                              r: BOOLEAN; .)
-    = SimExpr<t, lx> [ Rel<op> SimExpr<t2, lx2>
-      (. lx[0] := 0C;
+    = SimExpr<t, lx, v, vn> [ Rel<op> SimExpr<t2, lx2, v2, vn2>
+      (. lx[0] := 0C; v := FALSE;
          IF op = SymTab.OpIn THEN
            IF SymTab.InCheck(t, t2) THEN
              t := SymTab.BoolType();
@@ -738,14 +964,18 @@ PRODUCTIONS
     | ">"                     (. op := SymTab.OpGt; .)
     | ">="                    (. op := SymTab.OpGe; .)
     | "IN"                    (. op := SymTab.OpIn; .) .
-  SimExpr <VAR t: SymTab.TypeIndex; VAR lx: MGen.LitStr>
+  SimExpr <VAR t: SymTab.TypeIndex; VAR lx: MGen.LitStr;
+           VAR v: BOOLEAN; VAR vn: SymTab.Name>
                                         (. VAR t2, res2: SymTab.TypeIndex;
                                              op: INTEGER;
                                              lx2: MGen.LitStr;
+                                             v2: BOOLEAN;
+                                             vn2: SymTab.Name;
                                              neg, isR: BOOLEAN; .)
     =                                   (. neg := FALSE; .)
       [ "+" | "-"                       (. neg := TRUE; .) ]
-      Term<t, lx>                       (. IF neg THEN
+      Term<t, lx, v, vn>                (. IF neg THEN
+                                           v := FALSE;
                                            IF MGen.IsLit(lx) THEN
                                              MGen.NegFold(lx, lx)
                                            ELSE lx[0] := 0C
@@ -756,8 +986,8 @@ PRODUCTIONS
                                            ELSE MGen.NegInt
                                            END
                                          END; .)
-      { AddOp<op> Term<t2, lx2>
-      (. lx[0] := 0C;
+      { AddOp<op> Term<t2, lx2, v2, vn2>
+      (. lx[0] := 0C; v := FALSE;
          IF op = SymTab.OpOr THEN
            IF SymTab.BoolCheck(t) & SymTab.BoolCheck(t2) THEN
              t := SymTab.BoolType()
@@ -778,14 +1008,17 @@ PRODUCTIONS
     = "+"                     (. op := SymTab.OpAdd; .)
     | "-"                     (. op := SymTab.OpSub; .)
     | "OR"                    (. op := SymTab.OpOr; .) .
-  Term <VAR t: SymTab.TypeIndex; VAR lx: MGen.LitStr>
+  Term <VAR t: SymTab.TypeIndex; VAR lx: MGen.LitStr;
+        VAR v: BOOLEAN; VAR vn: SymTab.Name>
                                         (. VAR t2, res2: SymTab.TypeIndex;
                                              op: INTEGER;
                                              lx2: MGen.LitStr;
+                                             v2: BOOLEAN;
+                                             vn2: SymTab.Name;
                                              isR: BOOLEAN;
                                              mt: INTEGER; .)
-    = Fact<t, lx> { MulOp<op> Fact<t2, lx2>
-      (. lx[0] := 0C;
+    = Fact<t, lx, v, vn> { MulOp<op> Fact<t2, lx2, v2, vn2>
+      (. lx[0] := 0C; v := FALSE;
          IF op = SymTab.OpAnd THEN
            IF SymTab.BoolCheck(t) & SymTab.BoolCheck(t2) THEN
              t := SymTab.BoolType()
@@ -816,19 +1049,25 @@ PRODUCTIONS
     | "MOD"                   (. op := SymTab.OpMod; .)
     | "AND"                   (. op := SymTab.OpAnd; .)
     | "&"                     (. op := SymTab.OpAnd; .) .
-  Fact <VAR t: SymTab.TypeIndex; VAR lx: MGen.LitStr>
+  Fact <VAR t: SymTab.TypeIndex; VAR lx: MGen.LitStr;
+        VAR v: BOOLEAN; VAR vn: SymTab.Name>
                                         (. VAR s: ARRAY [0 .. 255] OF CHAR;
                                              t2, et, dt, st: SymTab.TypeIndex;
                                              dk: INTEGER;
                                              bnF: SymTab.Name;
                                              lxD, lx2: MGen.LitStr;
-                                             v: INTEGER;
+                                             v2: BOOLEAN;
+                                             vn2: SymTab.Name;
+                                             vi: INTEGER;
                                              c: CARDINAL;
-                                             b: LONGCARD; .)
+                                             b: LONGCARD;
+                                             sfxF: BOOLEAN;
+                                             okF: BOOLEAN; .)
     = integer                         (. LexString(s);
                                          MGen.CopyName(s, lx);
-                                         IF MGen.ParseInt(s, v) THEN
-                                           MGen.PushInt(v)
+                                         v := FALSE;
+                                         IF MGen.ParseInt(s, vi) THEN
+                                           MGen.PushInt(vi)
                                          ELSIF MGen.ParseCard(s, c) THEN
                                            MGen.PushBits(
                                              VAL(LONGCARD, c))
@@ -837,12 +1076,14 @@ PRODUCTIONS
                                          t := SymTab.IntType(); .)
     | real                            (. LexString(s);
                                          MGen.CopyName(s, lx);
+                                         v := FALSE;
                                          IF MGen.ParseReal(s, b) THEN
                                            MGen.PushBits(b)
                                          ELSE MGen.PushBits(0H)
                                          END;
                                          t := SymTab.RealType(); .)
     | string                          (. LexString(s);
+                                         v := FALSE;
                                          IF SymTab.StrLen(s) <= 3 THEN
                                            t := SymTab.CharType();
                                            MGen.CopyName(s, lx);
@@ -853,22 +1094,43 @@ PRODUCTIONS
                                            lx[0] := 0C;
                                            MGen.PushInt(0)
                                          END; .)
-    | Design<dt, dk, bnF, TRUE, lxD>   (. t := dt;
-                                         MGen.CopyName(lxD, lx); .)
+    | DesignHead<dt, dk, bnF, TRUE, lxD>
+      DesignTail<dt, dk, bnF, TRUE, lxD, sfxF>
+                                        (. t := dt;
+                                           MGen.CopyName(lxD, lx);
+                                           v := ~sfxF
+                                                & ((dk = SymTab.KindVar)
+                                                OR (dk = SymTab.KindParam)
+                                                OR (dk
+                                                    = SymTab.KindVarPar));
+                                           MGen.CopyName(bnF, vn); .)
+      [ CallTail<bnF, sfxF, TRUE, okF, TRUE>
+                                        (. IF okF THEN
+                                             IF SymTab.SymKind(bnF)
+                                                = SymTab.KindProc THEN
+                                               t := SymTab.ProcRet(bnF)
+                                             ELSE
+                                               t := SymTab.InvalidType
+                                             END
+                                           ELSE t := SymTab.InvalidType
+                                           END;
+                                           lx[0] := 0C; v := FALSE; .) ]
     | "("
-      Expr<et, lx> ")"                (. t := et; .)
+      Expr<et, lx, v, vn> ")"         (. t := et; .)
     | ( "NOT" | "~" )
-      Fact<t2, lx2>                   (. lx[0] := 0C;
+      Fact<t2, lx2, v2, vn2>          (. lx[0] := 0C; v := FALSE;
                                          IF SymTab.BoolCheck(t2) THEN
                                            t := SymTab.BoolType()
                                          ELSE SemError(212);
                                            t := SymTab.InvalidType END;
                                          MGen.Not; .)
-    | SetLit<st>                      (. lx[0] := 0C;
+    | SetLit<st>                      (. lx[0] := 0C; v := FALSE;
                                          t := st; .) .
   SetLit <VAR t: SymTab.TypeIndex>
                                         (. VAR first, et: SymTab.TypeIndex;
                                              lxE, lxE2: MGen.LitStr;
+                                             vE, vE2: BOOLEAN;
+                                             vnE, vnE2: SymTab.Name;
                                              hasR: BOOLEAN; .)
     = "{"
                                 (. MGen.PushInt(0);
@@ -887,11 +1149,13 @@ PRODUCTIONS
       "}" .
   Elem <VAR t: SymTab.TypeIndex; VAR lx: MGen.LitStr;
         VAR lx2: MGen.LitStr; VAR hasR: BOOLEAN>
-                                        (. VAR t2: SymTab.TypeIndex; .)
-    = Expr<t, lx>                       (. hasR := FALSE;
+                                        (. VAR t2: SymTab.TypeIndex;
+                                             vD, vD2: BOOLEAN;
+                                             vnD, vnD2: SymTab.Name; .)
+    = Expr<t, lx, vD, vnD>              (. hasR := FALSE;
                                            lx2[0] := 0C; .)
       [ ".."
-        Expr<t2, lx2>                   (. IF ~SymTab.SetElemCheck(t, t2) THEN
+        Expr<t2, lx2, vD2, vnD2>        (. IF ~SymTab.SetElemCheck(t, t2) THEN
                                            SemError(222) END;
                                            hasR := TRUE; .) ] .
 

+ 63 - 58
M2c.err

@@ -16,61 +16,66 @@
 | 15: Msg("'VAR' expected")
 | 16: Msg("'=' expected")
 | 17: Msg("':' expected")
-| 18: Msg("'[' expected")
-| 19: Msg("'..' expected")
-| 20: Msg("']' expected")
-| 21: Msg("'(' expected")
-| 22: Msg("')' expected")
-| 23: Msg("'ARRAY' expected")
-| 24: Msg("'OF' expected")
-| 25: Msg("'RECORD' expected")
-| 26: Msg("'SET' expected")
-| 27: Msg("'POINTER' expected")
-| 28: Msg("'TO' expected")
-| 29: Msg("'EXIT' expected")
-| 30: Msg("':=' expected")
-| 31: Msg("'IF' expected")
-| 32: Msg("'THEN' expected")
-| 33: Msg("'ELSIF' expected")
-| 34: Msg("'ELSE' expected")
-| 35: Msg("'CASE' expected")
-| 36: Msg("'|' expected")
-| 37: Msg("'WHILE' expected")
-| 38: Msg("'DO' expected")
-| 39: Msg("'REPEAT' expected")
-| 40: Msg("'UNTIL' expected")
-| 41: Msg("'LOOP' expected")
-| 42: Msg("'FOR' expected")
-| 43: Msg("'BY' expected")
-| 44: Msg("'-' expected")
-| 45: Msg("'WITH' expected")
-| 46: Msg("'^' expected")
-| 47: Msg("'#' expected")
-| 48: Msg("'<>' expected")
-| 49: Msg("'<' expected")
-| 50: Msg("'<=' expected")
-| 51: Msg("'>' expected")
-| 52: Msg("'>=' expected")
-| 53: Msg("'IN' expected")
-| 54: Msg("'+' expected")
-| 55: Msg("'OR' expected")
-| 56: Msg("'*' expected")
-| 57: Msg("'/' expected")
-| 58: Msg("'DIV' expected")
-| 59: Msg("'MOD' expected")
-| 60: Msg("'AND' expected")
-| 61: Msg("'&' expected")
-| 62: Msg("'NOT' expected")
-| 63: Msg("'~' expected")
-| 64: Msg("'{' expected")
-| 65: Msg("'}' expected")
-| 66: Msg("not expected")
-| 67: Msg("invalid MulOp")
-| 68: Msg("invalid Fact")
-| 69: Msg("invalid AddOp")
-| 70: Msg("invalid Rel")
-| 71: Msg("invalid ByLit")
-| 72: Msg("invalid SimpleType")
-| 73: Msg("invalid Type")
-| 74: Msg("invalid Declaration")
-| 75: Msg("invalid Import")
+| 18: Msg("'PROCEDURE' expected")
+| 19: Msg("'(' expected")
+| 20: Msg("')' expected")
+| 21: Msg("'FORWARD' expected")
+| 22: Msg("'[' expected")
+| 23: Msg("'..' expected")
+| 24: Msg("']' expected")
+| 25: Msg("'ARRAY' expected")
+| 26: Msg("'OF' expected")
+| 27: Msg("'RECORD' expected")
+| 28: Msg("'SET' expected")
+| 29: Msg("'POINTER' expected")
+| 30: Msg("'TO' expected")
+| 31: Msg("'EXIT' expected")
+| 32: Msg("':=' expected")
+| 33: Msg("'IF' expected")
+| 34: Msg("'THEN' expected")
+| 35: Msg("'ELSIF' expected")
+| 36: Msg("'ELSE' expected")
+| 37: Msg("'CASE' expected")
+| 38: Msg("'|' expected")
+| 39: Msg("'WHILE' expected")
+| 40: Msg("'DO' expected")
+| 41: Msg("'REPEAT' expected")
+| 42: Msg("'UNTIL' expected")
+| 43: Msg("'LOOP' expected")
+| 44: Msg("'FOR' expected")
+| 45: Msg("'BY' expected")
+| 46: Msg("'-' expected")
+| 47: Msg("'WITH' expected")
+| 48: Msg("'RETURN' expected")
+| 49: Msg("'^' expected")
+| 50: Msg("'#' expected")
+| 51: Msg("'<>' expected")
+| 52: Msg("'<' expected")
+| 53: Msg("'<=' expected")
+| 54: Msg("'>' expected")
+| 55: Msg("'>=' expected")
+| 56: Msg("'IN' expected")
+| 57: Msg("'+' expected")
+| 58: Msg("'OR' expected")
+| 59: Msg("'*' expected")
+| 60: Msg("'/' expected")
+| 61: Msg("'DIV' expected")
+| 62: Msg("'MOD' expected")
+| 63: Msg("'AND' expected")
+| 64: Msg("'&' expected")
+| 65: Msg("'NOT' expected")
+| 66: Msg("'~' expected")
+| 67: Msg("'{' expected")
+| 68: Msg("'}' expected")
+| 69: Msg("not expected")
+| 70: Msg("invalid MulOp")
+| 71: Msg("invalid Fact")
+| 72: Msg("invalid AddOp")
+| 73: Msg("invalid Rel")
+| 74: Msg("invalid ByLit")
+| 75: Msg("invalid AssignOrCall")
+| 76: Msg("invalid SimpleType")
+| 77: Msg("invalid Type")
+| 78: Msg("invalid ProcedureDecl")
+| 79: Msg("invalid Declaration")
+| 80: Msg("invalid Import")

+ 7 - 6
M2c.lst

@@ -6,6 +6,7 @@ Grammar Tests:
 
 Deletable symbols:
      Case
+     DesignTail
      Stat
      Field
      FieldSeq
@@ -18,12 +19,12 @@ LL(1) conditions:         --  ok  --
 
 Statistics:
 
-  nr of terminals:        67 (limit   400)
-  nr of non-terminals:    46 (limit   210)
-  nr of pragmas:           0 (limit   433)
-  nr of symbolnodes:     113 (limit   500)
-  nr of graphnodes:      428 (limit  1500)
-  nr of conditionsets:     5 (limit   100)
+  nr of terminals:        70 (limit   400)
+  nr of non-terminals:    53 (limit   210)
+  nr of pragmas:           0 (limit   430)
+  nr of symbolnodes:     123 (limit   500)
+  nr of graphnodes:      507 (limit  1500)
+  nr of conditionsets:     7 (limit   100)
   nr of charactersets:     9 (limit   250)
 
 

+ 67 - 59
M2c.mod

@@ -113,69 +113,74 @@ MODULE M2c;
         | 15: Msg("'VAR' expected")
         | 16: Msg("'=' expected")
         | 17: Msg("':' expected")
-        | 18: Msg("'[' expected")
-        | 19: Msg("'..' expected")
-        | 20: Msg("']' expected")
-        | 21: Msg("'(' expected")
-        | 22: Msg("')' expected")
-        | 23: Msg("'ARRAY' expected")
-        | 24: Msg("'OF' expected")
-        | 25: Msg("'RECORD' expected")
-        | 26: Msg("'SET' expected")
-        | 27: Msg("'POINTER' expected")
-        | 28: Msg("'TO' expected")
-        | 29: Msg("'EXIT' expected")
-        | 30: Msg("':=' expected")
-        | 31: Msg("'IF' expected")
-        | 32: Msg("'THEN' expected")
-        | 33: Msg("'ELSIF' expected")
-        | 34: Msg("'ELSE' expected")
-        | 35: Msg("'CASE' expected")
-        | 36: Msg("'|' expected")
-        | 37: Msg("'WHILE' expected")
-        | 38: Msg("'DO' expected")
-        | 39: Msg("'REPEAT' expected")
-        | 40: Msg("'UNTIL' expected")
-        | 41: Msg("'LOOP' expected")
-        | 42: Msg("'FOR' expected")
-        | 43: Msg("'BY' expected")
-        | 44: Msg("'-' expected")
-        | 45: Msg("'WITH' expected")
-        | 46: Msg("'^' expected")
-        | 47: Msg("'#' expected")
-        | 48: Msg("'<>' expected")
-        | 49: Msg("'<' expected")
-        | 50: Msg("'<=' expected")
-        | 51: Msg("'>' expected")
-        | 52: Msg("'>=' expected")
-        | 53: Msg("'IN' expected")
-        | 54: Msg("'+' expected")
-        | 55: Msg("'OR' expected")
-        | 56: Msg("'*' expected")
-        | 57: Msg("'/' expected")
-        | 58: Msg("'DIV' expected")
-        | 59: Msg("'MOD' expected")
-        | 60: Msg("'AND' expected")
-        | 61: Msg("'&' expected")
-        | 62: Msg("'NOT' expected")
-        | 63: Msg("'~' expected")
-        | 64: Msg("'{' expected")
-        | 65: Msg("'}' expected")
-        | 66: Msg("not expected")
-        | 67: Msg("invalid MulOp")
-        | 68: Msg("invalid Fact")
-        | 69: Msg("invalid AddOp")
-        | 70: Msg("invalid Rel")
-        | 71: Msg("invalid ByLit")
-        | 72: Msg("invalid SimpleType")
-        | 73: Msg("invalid Type")
-        | 74: Msg("invalid Declaration")
-        | 75: Msg("invalid Import")
+        | 18: Msg("'PROCEDURE' expected")
+        | 19: Msg("'(' expected")
+        | 20: Msg("')' expected")
+        | 21: Msg("'FORWARD' expected")
+        | 22: Msg("'[' expected")
+        | 23: Msg("'..' expected")
+        | 24: Msg("']' expected")
+        | 25: Msg("'ARRAY' expected")
+        | 26: Msg("'OF' expected")
+        | 27: Msg("'RECORD' expected")
+        | 28: Msg("'SET' expected")
+        | 29: Msg("'POINTER' expected")
+        | 30: Msg("'TO' expected")
+        | 31: Msg("'EXIT' expected")
+        | 32: Msg("':=' expected")
+        | 33: Msg("'IF' expected")
+        | 34: Msg("'THEN' expected")
+        | 35: Msg("'ELSIF' expected")
+        | 36: Msg("'ELSE' expected")
+        | 37: Msg("'CASE' expected")
+        | 38: Msg("'|' expected")
+        | 39: Msg("'WHILE' expected")
+        | 40: Msg("'DO' expected")
+        | 41: Msg("'REPEAT' expected")
+        | 42: Msg("'UNTIL' expected")
+        | 43: Msg("'LOOP' expected")
+        | 44: Msg("'FOR' expected")
+        | 45: Msg("'BY' expected")
+        | 46: Msg("'-' expected")
+        | 47: Msg("'WITH' expected")
+        | 48: Msg("'RETURN' expected")
+        | 49: Msg("'^' expected")
+        | 50: Msg("'#' expected")
+        | 51: Msg("'<>' expected")
+        | 52: Msg("'<' expected")
+        | 53: Msg("'<=' expected")
+        | 54: Msg("'>' expected")
+        | 55: Msg("'>=' expected")
+        | 56: Msg("'IN' expected")
+        | 57: Msg("'+' expected")
+        | 58: Msg("'OR' expected")
+        | 59: Msg("'*' expected")
+        | 60: Msg("'/' expected")
+        | 61: Msg("'DIV' expected")
+        | 62: Msg("'MOD' expected")
+        | 63: Msg("'AND' expected")
+        | 64: Msg("'&' expected")
+        | 65: Msg("'NOT' expected")
+        | 66: Msg("'~' expected")
+        | 67: Msg("'{' expected")
+        | 68: Msg("'}' expected")
+        | 69: Msg("not expected")
+        | 70: Msg("invalid MulOp")
+        | 71: Msg("invalid Fact")
+        | 72: Msg("invalid AddOp")
+        | 73: Msg("invalid Rel")
+        | 74: Msg("invalid ByLit")
+        | 75: Msg("invalid AssignOrCall")
+        | 76: Msg("invalid SimpleType")
+        | 77: Msg("invalid Type")
+        | 78: Msg("invalid ProcedureDecl")
+        | 79: Msg("invalid Declaration")
+        | 80: Msg("invalid Import")
         
         (* add customized cases here *)
         | 200: Msg("duplicate identifier")
         | 201: Msg("undeclared identifier")
-        | 202: Msg("module name mismatch")
+        | 202: Msg("module/procedure name mismatch")
         | 210: Msg("incompatible assignment")
         | 211: Msg("arithmetic operand must be numeric")
         | 212: Msg("boolean operand required")
@@ -192,6 +197,9 @@ MODULE M2c;
         | 223: Msg("cyclical type definition")
         | 224: Msg("ordinal type required")
         | 230: Msg("not supported in this phase")
+        | 231: Msg("procedure forward mismatch or missing body")
+        | 232: Msg("bad RETURN")
+        | 233: Msg("invalid procedure call")
         ELSE         Msg("Error: "); WriteInt(lst, nr, 0);
         END;
         WriteLn(lst)


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 425 - 208
M2cP.mod



+ 56 - 53
M2cS.mod

@@ -5,7 +5,7 @@ IMPLEMENTATION MODULE M2cS;
 IMPORT FileIO, Storage;
 
 CONST
-  noSYMB  = 66; (*error token code*)
+  noSYMB  = 69; (*error token code*)
   (* not only for errors but also for not finished states of scanner analysis *)
   eof     = 32C (* MS-DOS Keyboard eof char *);
   EOF     = 0C;
@@ -102,57 +102,60 @@ PROCEDURE Get (VAR sym: CARDINAL);
   PROCEDURE CheckLiteral;
     BEGIN
       CASE CurrentCh(bp0) OF
-        "A": IF Equal("AND") THEN sym := 60; 
-             ELSIF Equal("ARRAY") THEN sym := 23; 
+        "A": IF Equal("AND") THEN sym := 63; 
+             ELSIF Equal("ARRAY") THEN sym := 25; 
              END
       | "B": IF Equal("BEGIN") THEN sym := 11; 
-             ELSIF Equal("BY") THEN sym := 43; 
+             ELSIF Equal("BY") THEN sym := 45; 
              END
-      | "C": IF Equal("CASE") THEN sym := 35; 
+      | "C": IF Equal("CASE") THEN sym := 37; 
              ELSIF Equal("CONST") THEN sym := 13; 
              END
-      | "D": IF Equal("DIV") THEN sym := 58; 
-             ELSIF Equal("DO") THEN sym := 38; 
+      | "D": IF Equal("DIV") THEN sym := 61; 
+             ELSIF Equal("DO") THEN sym := 40; 
              END
-      | "E": IF Equal("ELSE") THEN sym := 34; 
-             ELSIF Equal("ELSIF") THEN sym := 33; 
+      | "E": IF Equal("ELSE") THEN sym := 36; 
+             ELSIF Equal("ELSIF") THEN sym := 35; 
              ELSIF Equal("END") THEN sym := 12; 
-             ELSIF Equal("EXIT") THEN sym := 29; 
+             ELSIF Equal("EXIT") THEN sym := 31; 
              END
-      | "F": IF Equal("FOR") THEN sym := 42; 
+      | "F": IF Equal("FOR") THEN sym := 44; 
+             ELSIF Equal("FORWARD") THEN sym := 21; 
              ELSIF Equal("FROM") THEN sym := 8; 
              END
-      | "I": IF Equal("IF") THEN sym := 31; 
+      | "I": IF Equal("IF") THEN sym := 33; 
              ELSIF Equal("IMPORT") THEN sym := 9; 
-             ELSIF Equal("IN") THEN sym := 53; 
+             ELSIF Equal("IN") THEN sym := 56; 
              END
-      | "L": IF Equal("LOOP") THEN sym := 41; 
+      | "L": IF Equal("LOOP") THEN sym := 43; 
              END
-      | "M": IF Equal("MOD") THEN sym := 59; 
+      | "M": IF Equal("MOD") THEN sym := 62; 
              ELSIF Equal("MODULE") THEN sym := 5; 
              END
-      | "N": IF Equal("NOT") THEN sym := 62; 
+      | "N": IF Equal("NOT") THEN sym := 65; 
              END
-      | "O": IF Equal("OF") THEN sym := 24; 
-             ELSIF Equal("OR") THEN sym := 55; 
+      | "O": IF Equal("OF") THEN sym := 26; 
+             ELSIF Equal("OR") THEN sym := 58; 
              END
-      | "P": IF Equal("POINTER") THEN sym := 27; 
+      | "P": IF Equal("POINTER") THEN sym := 29; 
+             ELSIF Equal("PROCEDURE") THEN sym := 18; 
              END
-      | "R": IF Equal("RECORD") THEN sym := 25; 
-             ELSIF Equal("REPEAT") THEN sym := 39; 
+      | "R": IF Equal("RECORD") THEN sym := 27; 
+             ELSIF Equal("REPEAT") THEN sym := 41; 
+             ELSIF Equal("RETURN") THEN sym := 48; 
              END
-      | "S": IF Equal("SET") THEN sym := 26; 
+      | "S": IF Equal("SET") THEN sym := 28; 
              END
-      | "T": IF Equal("THEN") THEN sym := 32; 
-             ELSIF Equal("TO") THEN sym := 28; 
+      | "T": IF Equal("THEN") THEN sym := 34; 
+             ELSIF Equal("TO") THEN sym := 30; 
              ELSIF Equal("TYPE") THEN sym := 14; 
              END
-      | "U": IF Equal("UNTIL") THEN sym := 40; 
+      | "U": IF Equal("UNTIL") THEN sym := 42; 
              END
       | "V": IF Equal("VAR") THEN sym := 15; 
              END
-      | "W": IF Equal("WHILE") THEN sym := 37; 
-             ELSIF Equal("WITH") THEN sym := 45; 
+      | "W": IF Equal("WHILE") THEN sym := 39; 
+             ELSIF Equal("WITH") THEN sym := 47; 
              END
       ELSE
       END
@@ -222,7 +225,7 @@ PROCEDURE Get (VAR sym: CARDINAL);
             ELSE sym := 3; RETURN
             END;
       | 14: sym := 6; RETURN
-      | 15: IF (ch = ".") THEN state := 20; 
+      | 15: IF (ch = ".") THEN state := 22; 
             ELSE sym := 7; RETURN
             END;
       | 16: sym := 10; RETURN
@@ -230,33 +233,33 @@ PROCEDURE Get (VAR sym: CARDINAL);
       | 18: IF (ch = "=") THEN state := 24; 
             ELSE sym := 17; RETURN
             END;
-      | 19: sym := 18; RETURN
-      | 20: sym := 19; RETURN
-      | 21: sym := 20; RETURN
-      | 22: sym := 21; RETURN
-      | 23: sym := 22; RETURN
-      | 24: sym := 30; RETURN
-      | 25: sym := 36; RETURN
-      | 26: sym := 44; RETURN
-      | 27: sym := 46; RETURN
-      | 28: sym := 47; RETURN
+      | 19: sym := 19; RETURN
+      | 20: sym := 20; RETURN
+      | 21: sym := 22; RETURN
+      | 22: sym := 23; RETURN
+      | 23: sym := 24; RETURN
+      | 24: sym := 32; RETURN
+      | 25: sym := 38; RETURN
+      | 26: sym := 46; RETURN
+      | 27: sym := 49; RETURN
+      | 28: sym := 50; RETURN
       | 29: IF (ch = ">") THEN state := 30; 
             ELSIF (ch = "=") THEN state := 31; 
-            ELSE sym := 49; RETURN
+            ELSE sym := 52; RETURN
             END;
-      | 30: sym := 48; RETURN
-      | 31: sym := 50; RETURN
+      | 30: sym := 51; RETURN
+      | 31: sym := 53; RETURN
       | 32: IF (ch = "=") THEN state := 33; 
-            ELSE sym := 51; RETURN
+            ELSE sym := 54; RETURN
             END;
-      | 33: sym := 52; RETURN
-      | 34: sym := 54; RETURN
-      | 35: sym := 56; RETURN
-      | 36: sym := 57; RETURN
-      | 37: sym := 61; RETURN
-      | 38: sym := 63; RETURN
-      | 39: sym := 64; RETURN
-      | 40: sym := 65; RETURN
+      | 33: sym := 55; RETURN
+      | 34: sym := 57; RETURN
+      | 35: sym := 59; RETURN
+      | 36: sym := 60; RETURN
+      | 37: sym := 64; RETURN
+      | 38: sym := 66; RETURN
+      | 39: sym := 67; RETURN
+      | 40: sym := 68; RETURN
       | 41: sym := 0; ch := 0C; DEC(bp); RETURN
       ELSE sym := noSYMB; RETURN (*NextCh already done*)
       END
@@ -335,7 +338,7 @@ BEGIN
   start[ 28] := 42; start[ 29] := 42; start[ 30] := 42; start[ 31] := 42; 
   start[ 32] := 42; start[ 33] := 42; start[ 34] := 10; start[ 35] := 28; 
   start[ 36] := 42; start[ 37] := 42; start[ 38] := 37; start[ 39] :=  9; 
-  start[ 40] := 22; start[ 41] := 23; start[ 42] := 35; start[ 43] := 34; 
+  start[ 40] := 19; start[ 41] := 20; start[ 42] := 35; start[ 43] := 34; 
   start[ 44] := 16; start[ 45] := 26; start[ 46] := 15; start[ 47] := 36; 
   start[ 48] := 12; start[ 49] := 12; start[ 50] := 12; start[ 51] := 12; 
   start[ 52] := 12; start[ 53] := 12; start[ 54] := 12; start[ 55] := 12; 
@@ -347,8 +350,8 @@ BEGIN
   start[ 76] :=  1; start[ 77] :=  1; start[ 78] :=  1; start[ 79] :=  1; 
   start[ 80] :=  1; start[ 81] :=  1; start[ 82] :=  1; start[ 83] :=  1; 
   start[ 84] :=  1; start[ 85] :=  1; start[ 86] :=  1; start[ 87] :=  1; 
-  start[ 88] :=  1; start[ 89] :=  1; start[ 90] :=  1; start[ 91] := 19; 
-  start[ 92] := 42; start[ 93] := 21; start[ 94] := 27; start[ 95] := 42; 
+  start[ 88] :=  1; start[ 89] :=  1; start[ 90] :=  1; start[ 91] := 21; 
+  start[ 92] := 42; start[ 93] := 23; start[ 94] := 27; start[ 95] := 42; 
   start[ 96] := 42; start[ 97] :=  1; start[ 98] :=  1; start[ 99] :=  1; 
   start[100] :=  1; start[101] :=  1; start[102] :=  1; start[103] :=  1; 
   start[104] :=  1; start[105] :=  1; start[106] :=  1; start[107] :=  1; 


+ 80 - 1
MGen.def

@@ -19,7 +19,11 @@ DEFINITION MODULE MGen;
    Test convention (no I/O in the language): if the source declares
      VAR ExitCode : INTEGER;   (int family, VAR kind)
    the epilogue prints its value as decimal + CRLF through the
-   embedded print procedure; otherwise the program just ends. *)
+   embedded print procedure; otherwise the program just ends.
+
+   Step 3 adds procedures: user procedures take table entries 1..N
+   (0 = module body), run with ENTER frames, and are called via
+   ED (global), EC (directly nested) or EE (display walk). *)
 
 TYPE
   LitStr = ARRAY [0 .. 63] OF CHAR;
@@ -52,6 +56,11 @@ PROCEDURE LoadVar (name: ARRAY OF CHAR);
 PROCEDURE StoreVar (name: ARRAY OF CHAR);
 (* Pops into the global (dropped if unknown). *)
 
+PROCEDURE PushVar (name: ARRAY OF CHAR);
+(* Pushes a variable's value, frame-aware: globals via the global
+   table, same-frame locals/params direct, outer frames via the
+   display, VAR params indirect. Placeholder push-0 if unknown. *)
+
 PROCEDURE LoadTemp (t: INTEGER);
 PROCEDURE StoreTemp (t: INTEGER);
 (* Direct slot access for compiler temporaries. *)
@@ -138,6 +147,76 @@ PROCEDURE IntToReal;
 PROCEDURE Dup;
 PROCEDURE Drop;
 
+(* ---- frames, addresses, indirect access (see §5-§6) ---- *)
+
+PROCEDURE LoadLocal (sl: INTEGER);
+PROCEDURE StoreLocal (sl: INTEGER);
+(* 2C/3C direct frame access (locals negative, params >= 3).
+   Same-frame VAR-param slots hold addresses: read them with
+   LoadLocal, then LoadIndir0. *)
+
+PROCEDURE LoadIndir0;
+PROCEDURE StoreIndir0;
+(* 60H/70H slot-0 indirect (through VAR-param addresses). *)
+
+PROCEDURE FrameAddr (sl: INTEGER; np: CARDINAL);
+(* Pushes (display-frame(np) + slot*8) as a byte address. *)
+
+PROCEDURE LoadIndir;
+PROCEDURE StoreIndir;
+(* 41H/51H through a computed address. *)
+
+PROCEDURE LocalAddr (sl: INTEGER);
+(* 80H address of a same-frame slot. *)
+
+PROCEDURE GlobalAddr (name: ARRAY OF CHAR);
+(* 81H address of a global (push-0 if unknown). *)
+
+PROCEDURE PushAddr (name: ARRAY OF CHAR);
+(* Pushes a VAR-actual address: slot addresses for plain variables,
+   slot contents for VAR params; display-aware. Push-0 if unknown. *)
+
+PROCEDURE StoreSetup (name: ARRAY OF CHAR);
+(* Emits an early store address (VAR-param or outer-frame targets)
+   before the value expression; nothing for direct targets. *)
+
+PROCEDURE StoreFinish (name: ARRAY OF CHAR);
+(* Emits the store for a storable variable (global, param, local,
+   VAR-param, outer frame). *)
+
+(* ---- procedures (see §5.2) ---- *)
+
+PROCEDURE ProcEntry (num: INTEGER; nLoc: CARDINAL);
+(* Records the procedure address and emits ENTER for nLoc locals. *)
+
+PROCEDURE CallProc (num: INTEGER);
+(* ED: call a global procedure (OFP = NIL). *)
+
+PROCEDURE CallNested (num: INTEGER);
+(* EC: call a directly nested procedure (OFP = FP). *)
+
+PROCEDURE CallDisplay (num: INTEGER; np: CARDINAL);
+(* 11H np display walk, then EE call with that frame. *)
+
+PROCEDURE Leave (nPar: CARDINAL; func: BOOLEAN);
+(* 84H (proper) or 85H (function) procedure leave. *)
+
+(* ---- actual parameters (nested-call-safe frame stack) ---- *)
+
+PROCEDURE ActBegin (pn: ARRAY OF CHAR);
+(* Opens an actual-parameter frame for a call to pn. *)
+
+PROCEDURE ActValue (t: INTEGER; v: BOOLEAN;
+                    vn: ARRAY OF CHAR): INTEGER;
+(* Checks one actual against its formal (0 = ok): VAR formals need
+   a plain variable of identical type (address pushed), value
+   formals need assignment compatibility (INTEGER widens to REAL).
+   Always consumes the value (temp store). 1 means error 233. *)
+
+PROCEDURE ActEnd (pn: ARRAY OF CHAR; sfx, inExpr: BOOLEAN): INTEGER;
+(* Verifies arity and callee kind, reloads actuals reversed and
+   emits the depth-correct call (0 = ok, 1 = error 233). *)
+
 (* ---- control flow (E0/E1 with fixups) ---- *)
 
 PROCEDURE NewLabel (): INTEGER;

+ 382 - 9
MGen.mod

@@ -10,12 +10,21 @@ CONST
   MaxLab  = 255;
   MaxFix  = 2047;
   MaxLoop = 15;
+  MaxActDepth = 7;
+  MaxActN = 63;
 
   (* opcodes, see mc64-spec.md §11 *)
   OPdup = 20H;  OPswap = 21H;
   OPloadGlb = 2DH;  OPstoreGlb = 3DH;
+  OPloadLocal = 2CH;  OPstoreLocal = 3CH;
+  OPloadStk = 2EH;
+  OPloadIndir0 = 60H;  OPstoreIndir0 = 70H;
+  OPloadOuterN = 11H;
+  OPloadIndir = 41H;  OPstoreIndir = 51H;
+  OPlocalAddr = 80H;  OPglobalAddr = 81H;  OPstkAddr = 82H;
   OPext = 40H;  SUBdrop = 00H;
-  OPenter = 0D4H;  OPfctLeave = 85H;  OPprocCall = 0EDH;
+  OPenter = 0D4H;  OPprocLeave = 84H;  OPfctLeave = 85H;
+  OPprocCall = 0EDH;  OPnestedCall = 0ECH;  OPcallFrame = 0EEH;
   OPimmB = 8DH;  OPimmW = 8EH;  OPimm0 = 90H;
   OPadd = 0A6H;  OPsub = 0A7H;  OPumul = 0A8H;
   OPudiv = 0A9H;  OPumod = 0AAH;
@@ -55,6 +64,13 @@ TYPE
                | FALSE : w : LONGCARD;
              END;
            END;
+  ActFrame = RECORD
+    n : CARDINAL;
+    tmps : ARRAY [0 .. MaxActN] OF INTEGER;
+    known : BOOLEAN;
+    nf : CARDINAL;
+    pn : ARRAY [0 .. 63] OF CHAR;
+  END;
 
 VAR
   code : ARRAY [0 .. MaxCode] OF CHAR;
@@ -74,6 +90,11 @@ VAR
   loopTop : CARDINAL;
   noSup : CARDINAL;
   noEmit : CARDINAL;
+  actSt : ARRAY [0 .. MaxActDepth] OF ActFrame;
+  actTop : CARDINAL;
+  procAddr : ARRAY [0 .. 64] OF INTEGER;
+  maxNum : CARDINAL;
+  mainAddr : CARDINAL;
 
 (* ---------------- byte helpers ---------------- *)
 
@@ -181,6 +202,10 @@ PROCEDURE OpenModule (name: ARRAY OF CHAR);
     StrCpy(modName, name);
     nCode := 0; nGlb := 0; nInit := 0;
     nLab := 0; nFix := 0; loopTop := 0; noSup := 0; noEmit := 0;
+  actTop := 0;
+    maxNum := 0; mainAddr := 0;
+    i := 0;
+    WHILE i <= 64 DO procAddr[i] := -1; INC(i) END;
     inBody := FALSE;
     i := 0;
     WHILE i <= MaxGlb DO gNames[i][0] := 0C; INC(i) END;
@@ -251,6 +276,7 @@ PROCEDURE BeginBody;
   BEGIN
     IF inBody THEN RETURN END;
     inBody := TRUE;
+    mainAddr := nCode;
     i := 0;
     WHILE i < nInit DO
       PushValue(inits[i].kind, inits[i].ival, inits[i].bits);
@@ -297,6 +323,321 @@ PROCEDURE TempGlobal (): INTEGER;
     RETURN idx
   END TempGlobal;
 
+(* ---------------- frames and procedures ---------------- *)
+
+PROCEDURE EmitSlotB (op: CARDINAL; sl: INTEGER);
+(* Byte operand for a (possibly negative) frame slot. *)
+  BEGIN
+    IF sl >= 0 THEN EmitOpB(op, VAL(CARDINAL, sl))
+    ELSE EmitOpB(op, 256 - VAL(CARDINAL, -sl))
+    END
+  END EmitSlotB;
+
+PROCEDURE LoadLocal (sl: INTEGER);
+  BEGIN
+    EmitSlotB(OPloadLocal, sl)
+  END LoadLocal;
+
+PROCEDURE StoreLocal (sl: INTEGER);
+  BEGIN
+    EmitSlotB(OPstoreLocal, sl)
+  END StoreLocal;
+
+PROCEDURE LoadIndir0;
+  BEGIN
+    EmitOp(OPloadIndir0)
+  END LoadIndir0;
+
+PROCEDURE StoreIndir0;
+  BEGIN
+    EmitOp(OPstoreIndir0)
+  END StoreIndir0;
+
+PROCEDURE FrameAddr (sl: INTEGER; np: CARDINAL);
+(* Pushes (display-frame(np) + slot*8): 11H np, then the offset. *)
+  BEGIN
+    EmitOpB(OPloadOuterN, np);
+    IF sl >= 0 THEN EmitOpB(OPstkAddr, VAL(CARDINAL, sl))
+    ELSE
+      PushInt(sl * 8);
+      EmitOp(OPadd)
+    END
+  END FrameAddr;
+
+PROCEDURE LoadIndir;
+  BEGIN
+    EmitOp(OPloadIndir)
+  END LoadIndir;
+
+PROCEDURE StoreIndir;
+  BEGIN
+    EmitOp(OPstoreIndir)
+  END StoreIndir;
+
+PROCEDURE LocalAddr (sl: INTEGER);
+  BEGIN
+    EmitSlotB(OPlocalAddr, sl)
+  END LocalAddr;
+
+PROCEDURE GlobalAddr (name: ARRAY OF CHAR);
+  VAR idx : INTEGER;
+  BEGIN
+    idx := FindVar(name);
+    IF idx < 0 THEN EmitOp(OPimm0)
+    ELSE EmitOpB(OPglobalAddr, VAL(CARDINAL, idx))
+    END
+  END GlobalAddr;
+
+PROCEDURE PushAddr (name: ARRAY OF CHAR);
+(* VAR-actual address: slot contents for VAR params, slot address
+   for plain variables; display-aware. *)
+  VAR k, d, D, sl : INTEGER;
+    np : CARDINAL;
+  BEGIN
+    k := SymTab.SymKind(name);
+    d := SymTab.SymDepth(name);
+    D := SymTab.CurDepth();
+    sl := SymTab.SymSlot(name);
+    IF (k # SymTab.KindVar) & (k # SymTab.KindParam)
+       & (k # SymTab.KindVarPar) THEN
+      EmitOp(OPimm0); RETURN
+    END;
+    IF k = SymTab.KindVarPar THEN
+      IF D = d THEN LoadLocal(sl)
+      ELSE
+        np := VAL(CARDINAL, D - 1 - d);
+        FrameAddr(sl, np);
+        LoadIndir
+      END
+    ELSE
+      IF d = 0 THEN GlobalAddr(name)
+      ELSIF D = d THEN LocalAddr(sl)
+      ELSE
+        np := VAL(CARDINAL, D - 1 - d);
+        FrameAddr(sl, np)
+      END
+    END
+  END PushAddr;
+
+PROCEDURE StoreSetup (name: ARRAY OF CHAR);
+(* Early address for indirect stores; call before the value code. *)
+  VAR k, d, D, sl : INTEGER;
+    np : CARDINAL;
+  BEGIN
+    k := SymTab.SymKind(name);
+    IF (k # SymTab.KindVar) & (k # SymTab.KindParam)
+       & (k # SymTab.KindVarPar) THEN
+      RETURN
+    END;
+    d := SymTab.SymDepth(name);
+    D := SymTab.CurDepth();
+    sl := SymTab.SymSlot(name);
+    IF k = SymTab.KindVarPar THEN
+      IF D = d THEN LoadLocal(sl)
+      ELSE
+        np := VAL(CARDINAL, D - 1 - d);
+        FrameAddr(sl, np);
+        LoadIndir
+      END
+    ELSIF (d # 0) & (D # d) THEN
+      np := VAL(CARDINAL, D - 1 - d);
+      FrameAddr(sl, np)
+    END
+  END StoreSetup;
+
+PROCEDURE StoreFinish (name: ARRAY OF CHAR);
+(* Completes the store; call after the value code. *)
+  VAR k, d, D, sl : INTEGER;
+  BEGIN
+    k := SymTab.SymKind(name);
+    IF (k # SymTab.KindVar) & (k # SymTab.KindParam)
+       & (k # SymTab.KindVarPar) THEN
+      RETURN
+    END;
+    d := SymTab.SymDepth(name);
+    D := SymTab.CurDepth();
+    sl := SymTab.SymSlot(name);
+    IF d = 0 THEN StoreVar(name)
+    ELSIF D = d THEN
+      IF k = SymTab.KindVarPar THEN StoreIndir0
+      ELSE StoreLocal(sl)
+      END
+    ELSE
+      IF k = SymTab.KindVarPar THEN StoreIndir0
+      ELSE StoreIndir
+      END
+    END
+  END StoreFinish;
+
+PROCEDURE PushVar (name: ARRAY OF CHAR);
+(* Frame-aware value load (see PushAddr for the address twin). *)
+  VAR k, d, D, sl : INTEGER;
+    np : CARDINAL;
+  BEGIN
+    k := SymTab.SymKind(name);
+    d := SymTab.SymDepth(name);
+    D := SymTab.CurDepth();
+    sl := SymTab.SymSlot(name);
+    IF (k # SymTab.KindVar) & (k # SymTab.KindParam)
+       & (k # SymTab.KindVarPar) THEN
+      EmitOp(OPimm0); RETURN
+    END;
+    IF d = 0 THEN LoadVar(name)
+    ELSIF D = d THEN
+      IF k = SymTab.KindVarPar THEN
+        LoadLocal(sl); LoadIndir0
+      ELSE LoadLocal(sl)
+      END
+    ELSE
+      np := VAL(CARDINAL, D - 1 - d);
+      FrameAddr(sl, np);
+      LoadIndir;
+      IF k = SymTab.KindVarPar THEN LoadIndir0 END
+    END
+  END PushVar;
+
+PROCEDURE ProcEntry (num: INTEGER; nLoc: CARDINAL);
+  VAR k : CARDINAL;
+  BEGIN
+    IF (num >= 0) & (num <= 64) THEN
+      procAddr[num] := VAL(INTEGER, nCode);
+      IF VAL(CARDINAL, num) > maxNum THEN
+        maxNum := VAL(CARDINAL, num)
+      END
+    END;
+    IF nLoc > 255 THEN k := 255 ELSE k := nLoc END;
+    EmitOpB(OPenter, 255 - k)
+  END ProcEntry;
+
+PROCEDURE CallProc (num: INTEGER);
+  BEGIN
+    EmitOpB(OPprocCall, VAL(CARDINAL, num))
+  END CallProc;
+
+PROCEDURE CallNested (num: INTEGER);
+  BEGIN
+    EmitOpB(OPnestedCall, VAL(CARDINAL, num))
+  END CallNested;
+
+PROCEDURE CallDisplay (num: INTEGER; np: CARDINAL);
+  BEGIN
+    EmitOpB(OPloadOuterN, np);
+    EmitOpB(OPcallFrame, VAL(CARDINAL, num))
+  END CallDisplay;
+
+PROCEDURE Leave (nPar: CARDINAL; func: BOOLEAN);
+  BEGIN
+    IF nPar > 255 THEN nPar := 255 END;
+    IF func THEN EmitOpB(OPfctLeave, nPar)
+    ELSE EmitOpB(OPprocLeave, nPar)
+    END
+  END Leave;
+
+(* ---------------- actual parameters ---------------- *)
+
+PROCEDURE ActFrameIdx (): CARDINAL;
+(* Active frame, capped (nesting past the cap reuses the top). *)
+  BEGIN
+    IF actTop = 0 THEN RETURN 0 END;
+    IF actTop - 1 > MaxActDepth THEN RETURN MaxActDepth END;
+    RETURN actTop - 1
+  END ActFrameIdx;
+
+PROCEDURE ActBegin (pn: ARRAY OF CHAR);
+  VAR fr : CARDINAL;
+  BEGIN
+    fr := actTop;
+    IF fr > MaxActDepth THEN fr := MaxActDepth
+    ELSE INC(actTop)
+    END;
+    StrCpy(actSt[fr].pn, pn);
+    actSt[fr].n := 0;
+    actSt[fr].known := SymTab.Lookup(pn)
+      & (SymTab.SymKind(pn) = SymTab.KindProc);
+    IF actSt[fr].known THEN actSt[fr].nf := SymTab.ProcNPar(pn)
+    ELSE actSt[fr].nf := 0
+    END
+  END ActBegin;
+
+PROCEDURE ActValue (t: INTEGER; v: BOOLEAN;
+                    vn: ARRAY OF CHAR): INTEGER;
+  VAR fr : CARDINAL;
+    i : CARDINAL;
+    ftyp : INTEGER;
+    fv : BOOLEAN;
+    err : INTEGER;
+  BEGIN
+    err := 0;
+    fr := ActFrameIdx();
+    i := actSt[fr].n;
+    IF i > MaxActN THEN
+      EmitOp(OPext); EmitOp(SUBdrop);
+      actSt[fr].n := i + 1;
+      RETURN 1
+    END;
+    IF actSt[fr].known & (i < actSt[fr].nf) THEN
+      ftyp := SymTab.ParamType(actSt[fr].pn, i);
+      fv := SymTab.ParamIsVar(actSt[fr].pn, i);
+      IF fv THEN
+        IF ~v THEN err := 1
+        ELSIF ~SymTab.SameType(t, ftyp) THEN err := 1
+        END;
+        EmitOp(OPext); EmitOp(SUBdrop);
+        PushAddr(vn)
+      ELSE
+        IF ~SymTab.Assignable(t, ftyp) THEN err := 1
+        ELSIF SymTab.IsIntFamily(t)
+              & (SymTab.ClassOf(ftyp) = SymTab.ClReal) THEN
+          IntToReal
+        END
+      END
+    END;
+    actSt[fr].tmps[i] := TempGlobal();
+    StoreTemp(actSt[fr].tmps[i]);
+    actSt[fr].n := i + 1;
+    RETURN err
+  END ActValue;
+
+PROCEDURE ActEnd (pn: ARRAY OF CHAR; sfx, inExpr: BOOLEAN): INTEGER;
+  VAR fr : CARDINAL;
+    i : CARDINAL;
+    n, num, F, d : INTEGER;
+  BEGIN
+    fr := ActFrameIdx();
+    IF actTop > 0 THEN DEC(actTop) END;
+    n := VAL(INTEGER, actSt[fr].n);
+    IF ~SymTab.Lookup(pn) THEN
+      IF inExpr THEN EmitOp(OPimm0) END;
+      RETURN 0
+    END;
+    IF (SymTab.SymKind(pn) # SymTab.KindProc)
+       OR sfx
+       OR (n # VAL(INTEGER, SymTab.ProcNPar(pn))) THEN
+      IF inExpr THEN EmitOp(OPimm0) END;
+      RETURN 1
+    END;
+    IF inExpr & (SymTab.ProcRet(pn) = SymTab.InvalidType) THEN
+      EmitOp(OPimm0);
+      RETURN 1
+    END;
+    i := actSt[fr].n;
+    WHILE i > 0 DO
+      DEC(i);
+      IF i <= MaxActN THEN LoadTemp(actSt[fr].tmps[i]) END
+    END;
+    num := SymTab.ProcNum(pn);
+    F := SymTab.CurDepth();
+    d := SymTab.SymDepth(pn);
+    IF d = 0 THEN CallProc(num)
+    ELSIF F = d THEN CallNested(num)
+    ELSE CallDisplay(num, VAL(CARDINAL, F - 1 - d))
+    END;
+    IF ~inExpr & (SymTab.ProcRet(pn) # SymTab.InvalidType) THEN
+      EmitOp(OPext); EmitOp(SUBdrop)
+    END;
+    RETURN 0
+  END ActEnd;
+
 PROCEDURE EmitMag (c: CARDINAL);
   BEGIN
     IF c <= 255 THEN EmitOpB(OPimmB, c)
@@ -750,15 +1091,36 @@ PROCEDURE EmitPrint;
     EmitOpB(OPfctLeave, 0)
   END EmitPrint;
 
+PROCEDURE PutS64At (off, addr, slot: CARDINAL);
+(* Procedure-table cell: signed (addr - slot). *)
+  VAR mag : LONGCARD;
+    bits : LONGCARD;
+    j : CARDINAL;
+  BEGIN
+    IF addr >= slot THEN bits := VAL(LONGCARD, addr - slot)
+    ELSE
+      mag := VAL(LONGCARD, slot - addr);
+      bits := 0FFFFFFFFFFFFFFFFH - mag + 1H
+    END;
+    FOR j := 0 TO 7 DO
+      img[off + j] := CHR(VAL(CARDINAL, bits MOD 256));
+      bits := bits DIV 256
+    END
+  END PutS64At;
+
 PROCEDURE EndModule;
   VAR i : CARDINAL;
-    codeOff, p1, pt : CARDINAL;
+    codeOff, p1, pt, prNum, tabBytes : CARDINAL;
+    k : CARDINAL;
+    addr : CARDINAL;
     exitIdx : INTEGER;
     sum : CARDINAL;
     fname : ARRAY [0 .. 127] OF CHAR;
     f : FileIO.File;
     ch : CHAR;
   BEGIN
+    IF ~inBody THEN mainAddr := nCode END;
+    prNum := maxNum + 1;
     (* epilogue: print ExitCode when the convention applies *)
     exitIdx := -1;
     IF SymTab.Lookup("ExitCode")
@@ -768,7 +1130,7 @@ PROCEDURE EndModule;
     END;
     IF exitIdx >= 0 THEN
       EmitOpB(OPloadGlb, VAL(CARDINAL, exitIdx));
-      EmitOpB(OPprocCall, 1);
+      EmitOpB(OPprocCall, prNum);
       EmitOp(OPext); EmitOp(SUBdrop)
     END;
     EmitOp(OPend);
@@ -791,11 +1153,20 @@ PROCEDURE EndModule;
       img[HeadSize + codeOff + i] := code[i]; INC(i)
     END;
     pt := codeOff + nCode;
+    tabBytes := (maxNum + 2) * 8;
     Put64At(HeadSize + DProcs, VAL(LONGCARD, pt));
-    Put64At(HeadSize + pt,
-      VAL(LONGCARD, codeOff) - VAL(LONGCARD, pt));
-    Put64At(HeadSize + pt + 8,
-      VAL(LONGCARD, codeOff + p1) - VAL(LONGCARD, pt + 8));
+    k := 0;
+    WHILE k <= maxNum + 1 DO
+      IF k = 0 THEN addr := codeOff + mainAddr
+      ELSIF k <= maxNum THEN
+        IF procAddr[k] < 0 THEN addr := pt + k * 8
+        ELSE addr := VAL(CARDINAL, procAddr[k]) + codeOff
+        END
+      ELSE addr := codeOff + p1
+      END;
+      PutS64At(HeadSize + pt + k * 8, addr, pt + k * 8);
+      INC(k)
+    END;
     i := 0;
     WHILE i < nGlb DO
       Put64At(HeadSize + DVarSizes + i * 8, 8H);
@@ -803,7 +1174,7 @@ PROCEDURE EndModule;
     END;
     sum := 0;
     i := HeadSize;
-    WHILE i < HeadSize + pt + 16 DO
+    WHILE i < HeadSize + pt + tabBytes DO
       IF ~((i >= 352) & (i <= 355)) THEN
         sum := sum + ORD(img[i])
       END;
@@ -818,7 +1189,7 @@ PROCEDURE EndModule;
     FileIO.Open(f, fname, TRUE);
     IF FileIO.Okay THEN
       i := 0;
-      WHILE i < HeadSize + pt + 16 DO
+      WHILE i < HeadSize + pt + tabBytes DO
         ch := img[i];
         FileIO.Write(f, ch);
         INC(i)
@@ -830,6 +1201,8 @@ PROCEDURE EndModule;
 BEGIN
   nCode := 0; nGlb := 0; nInit := 0;
   nLab := 0; nFix := 0; loopTop := 0; noSup := 0; noEmit := 0;
+  actTop := 0;
+  maxNum := 0; mainAddr := 0;
   inBody := FALSE;
   modName[0] := 0C
 END MGen.


BIN
PBadActual.MC4


BIN
PBadArity.MC4


BIN
PBadCall.MC4


BIN
PBadFwd.MC4


BIN
PBadNobody.MC4


BIN
PBadProper.MC4


BIN
PBadRet1.MC4


BIN
PBadRet2.MC4


BIN
PBadRet3.MC4


BIN
PBadUndecl.MC4


BIN
PChain.MC4


BIN
PEmpty.MC4


BIN
PFact.MC4


BIN
PFib.MC4


BIN
PFunc.MC4


BIN
PFwd.MC4


BIN
PLocal.MC4


BIN
PMixed.MC4


BIN
PNested.MC4


BIN
PParams.MC4


BIN
PProc.MC4


BIN
PRealFn.MC4


BIN
PRet.MC4


BIN
PVarPar.MC4


+ 89 - 0
SymTab.def

@@ -27,6 +27,9 @@ CONST
   KindModule = 4;
   KindPredef = 5;
   KindField  = 6;
+  KindProc   = 7;
+  KindParam  = 8;
+  KindVarPar = 9;
 
   (* type classes returned by ClassOf *)
   ClInvalid = 0;
@@ -175,4 +178,90 @@ PROCEDURE SetFor (elem: TypeIndex): TypeIndex;
 
 PROCEDURE StrLen (s: ARRAY OF CHAR): CARDINAL;
 
+(* ---------------- procedures ---------------- *)
+(* Step 3: nested procedures with value and VAR parameters, forward
+   declarations, static scoping. Proc numbers start at 1 (0 is the
+   module body); at most MaxProcs user procedures. *)
+
+PROCEDURE EnterProc (name: ARRAY OF CHAR): BOOLEAN;
+(* Enters a procedure name (KindProc, InvalidType) and assigns the
+   next proc number. FALSE on duplicate or table full. Sets the
+   current-proc context for following EnterParam/FwdParam calls. *)
+
+PROCEDURE IsForward (name: ARRAY OF CHAR): BOOLEAN;
+(* TRUE if the visible entry is a forward-declared procedure
+   whose body is still pending. *)
+
+PROCEDURE ReuseProc (name: ARRAY OF CHAR);
+(* Starts the full definition of a forward-declared procedure:
+   keeps its number, switches signature recording to define mode. *)
+
+PROCEDURE OpenProcScope;
+(* Pushes a scope for a procedure body, enters proc depth + 1,
+   resets slot counters, pushes return-type context. *)
+
+PROCEDURE CloseProc;
+(* Pops a procedure body scope, restores the enclosing context. *)
+
+PROCEDURE EnterParam (name: ARRAY OF CHAR; isVar: BOOLEAN;
+                      t: TypeIndex): BOOLEAN;
+(* Enters a value (KindParam) or VAR (KindVarPar) parameter in the
+   current procedure scope with its frame slot, appends it to the
+   define signature. FALSE on duplicate. *)
+
+PROCEDURE SetProcRet (t: TypeIndex): BOOLEAN;
+(* Sets the return type (InvalidType = proper procedure). TRUE
+   unless it mismatches a forward heading (error 231). *)
+
+PROCEDURE VerifyProc (): BOOLEAN;
+(* TRUE unless the define signature mismatches the forward
+   heading (error 231). *)
+
+PROCEDURE SetForward;
+(* Marks the current procedure forward-declared (body pending). *)
+
+PROCEDURE AnyForward (): BOOLEAN;
+(* TRUE if some procedure body is still missing (error 231). *)
+
+PROCEDURE ProcNum (name: ARRAY OF CHAR): INTEGER;
+(* Proc number, or -1 if absent / not a procedure. *)
+
+PROCEDURE ProcRet (name: ARRAY OF CHAR): TypeIndex;
+(* Return type (InvalidType = proper procedure or unknown). *)
+
+PROCEDURE ProcNPar (name: ARRAY OF CHAR): CARDINAL;
+(* Arity from the define signature, else the forward one. *)
+
+PROCEDURE ParamType (name: ARRAY OF CHAR; i: CARDINAL): TypeIndex;
+PROCEDURE ParamIsVar (name: ARRAY OF CHAR; i: CARDINAL): BOOLEAN;
+(* i-th formal parameter (0-based), InvalidType/FALSE if absent. *)
+
+PROCEDURE SymSlot (name: ARRAY OF CHAR): INTEGER;
+(* Frame slot (params >= 3, locals negative). Only meaningful
+   for variables at proc depth > 0. *)
+
+PROCEDURE SymDepth (name: ARRAY OF CHAR): INTEGER;
+(* Proc depth at declaration (0 = module level). *)
+
+PROCEDURE CurDepth (): INTEGER;
+(* Current proc depth (0 = module body). *)
+
+PROCEDURE CurProc (): INTEGER;
+(* Current proc number (0 = module body). *)
+
+PROCEDURE CurNPar (): CARDINAL;
+(* Arity of the current procedure (0 outside). *)
+
+PROCEDURE CurRet (): TypeIndex;
+(* Current return type (InvalidType = proper or outside). *)
+
+PROCEDURE InProc (): BOOLEAN;
+(* TRUE inside a procedure body. *)
+
+PROCEDURE InFunction (): BOOLEAN;
+(* TRUE inside a function (non-InvalidType return) body. *)
+
+PROCEDURE ProcNLocals (): CARDINAL;
+(* Local slot count of the current procedure (0 outside). *)
+
 END SymTab.

+ 319 - 0
SymTab.mod

@@ -8,6 +8,10 @@ CONST
   MaxPend   = 64;
   MaxMarks  = 16;
   ResDepth  = 64;
+  MaxProcs  = 64;
+  MaxParams = 256;
+  MaxPDepth = 16;
+  NoSlot    = -1000;
 
   (* descriptor forms *)
   FNone = 0; FAlias = 1; FSub = 2; FEnum = 3; FArray = 4;
@@ -20,6 +24,9 @@ TYPE
     kind : INTEGER;
     typ  : TypeIndex;
     lev  : CARDINAL;
+    slot : INTEGER;    (* frame slot (params >= 3, locals negative) *)
+    pdep : CARDINAL;   (* proc depth at declaration *)
+    pnum : INTEGER;    (* proc number (0 = not a procedure) *)
   END;
   Field = RECORD
     name  : Name;
@@ -27,6 +34,21 @@ TYPE
     owner : TypeIndex;
     next  : INTEGER;  (* index of next field of same owner, -1 = end *)
   END;
+  ProcRec = RECORD
+    ret     : TypeIndex;  (* InvalidType = proper procedure *)
+    fret    : TypeIndex;  (* forward heading return type *)
+    fwd     : BOOLEAN;    (* body still pending *)
+    everFwd : BOOLEAN;    (* had a forward heading *)
+    fHead   : INTEGER;    (* forward param list *)
+    fTail   : INTEGER;
+    dHead   : INTEGER;    (* define param list *)
+    dTail   : INTEGER;
+  END;
+  ParamRec = RECORD
+    typ   : TypeIndex;
+    isVar : BOOLEAN;
+    next  : INTEGER;
+  END;
 
 VAR
   syms : ARRAY [0 .. MaxSyms - 1] OF Symbol;
@@ -44,6 +66,16 @@ VAR
   fields : ARRAY [0 .. MaxFields - 1] OF Field;
   nFields : CARDINAL;
   dInt, dCard, dReal, dChar, dBool : TypeIndex;
+  procs : ARRAY [1 .. MaxProcs] OF ProcRec;
+  nProcs : CARDINAL;
+  params : ARRAY [0 .. MaxParams - 1] OF ParamRec;
+  nParams : CARDINAL;
+  pdep : CARDINAL;
+  curProc : INTEGER;
+  procSt : ARRAY [0 .. MaxPDepth] OF INTEGER;
+  locCnt : ARRAY [0 .. MaxPDepth] OF CARDINAL;
+  parCnt : ARRAY [0 .. MaxPDepth] OF CARDINAL;
+  retSt : ARRAY [0 .. MaxPDepth] OF TypeIndex;
 
 (* ---------------- strings ---------------- *)
 
@@ -98,6 +130,13 @@ PROCEDURE RawEnter (name: ARRAY OF CHAR; kind: INTEGER): INTEGER;
     syms[nSyms].kind := kind;
     syms[nSyms].typ := InvalidType;
     syms[nSyms].lev := curLev;
+    syms[nSyms].slot := NoSlot;
+    syms[nSyms].pdep := pdep;
+    syms[nSyms].pnum := 0;
+    IF (kind = KindVar) & (pdep > 0) THEN
+      syms[nSyms].slot := -(1 + VAL(INTEGER, locCnt[pdep]));
+      INC(locCnt[pdep])
+    END;
     INC(nSyms);
     RETURN VAL(INTEGER, nSyms - 1)
   END RawEnter;
@@ -548,6 +587,282 @@ PROCEDURE SetFor (elem: TypeIndex): TypeIndex;
     RETURN NewSet(e)
   END SetFor;
 
+(* ---------------- procedures ---------------- *)
+
+PROCEDURE NewParamRec (typ: TypeIndex; isVar: BOOLEAN): INTEGER;
+  BEGIN
+    IF nParams >= MaxParams THEN RETURN -1 END;
+    params[nParams].typ := typ;
+    params[nParams].isVar := isVar;
+    params[nParams].next := -1;
+    INC(nParams);
+    RETURN VAL(INTEGER, nParams - 1)
+  END NewParamRec;
+
+PROCEDURE AppParam (p: INTEGER; fwd: BOOLEAN);
+(* Appends param record p to the current proc's forward/define list. *)
+  VAR tail : INTEGER;
+  BEGIN
+    IF (curProc < 1) OR (curProc > VAL(INTEGER, nProcs)) THEN RETURN END;
+    IF fwd THEN
+      IF procs[curProc].fHead = -1 THEN procs[curProc].fHead := p
+      ELSE
+        tail := procs[curProc].fHead;
+        WHILE params[tail].next # -1 DO tail := params[tail].next END;
+        params[tail].next := p
+      END;
+      procs[curProc].fTail := p
+    ELSE
+      IF procs[curProc].dHead = -1 THEN procs[curProc].dHead := p
+      ELSE
+        tail := procs[curProc].dHead;
+        WHILE params[tail].next # -1 DO tail := params[tail].next END;
+        params[tail].next := p
+      END;
+      procs[curProc].dTail := p
+    END
+  END AppParam;
+
+PROCEDURE EnterProc (name: ARRAY OF CHAR): BOOLEAN;
+  VAR idx : INTEGER;
+  BEGIN
+    IF DupInLevel(name) THEN RETURN FALSE END;
+    IF nProcs >= MaxProcs THEN RETURN FALSE END;
+    idx := RawEnter(name, KindProc);
+    IF idx = -1 THEN RETURN FALSE END;
+    INC(nProcs);
+    syms[idx].pnum := VAL(INTEGER, nProcs);
+    procs[nProcs].ret := InvalidType;
+    procs[nProcs].fret := InvalidType;
+    procs[nProcs].fwd := FALSE;
+    procs[nProcs].everFwd := FALSE;
+    procs[nProcs].fHead := -1;
+    procs[nProcs].fTail := -1;
+    procs[nProcs].dHead := -1;
+    procs[nProcs].dTail := -1;
+    curProc := VAL(INTEGER, nProcs);
+    RETURN TRUE
+  END EnterProc;
+
+PROCEDURE IsForward (name: ARRAY OF CHAR): BOOLEAN;
+  VAR idx : INTEGER;
+  BEGIN
+    idx := Find(name);
+    IF idx = -1 THEN RETURN FALSE END;
+    IF syms[idx].kind # KindProc THEN RETURN FALSE END;
+    IF syms[idx].pnum < 1 THEN RETURN FALSE END;
+    RETURN procs[syms[idx].pnum].fwd
+  END IsForward;
+
+PROCEDURE ReuseProc (name: ARRAY OF CHAR);
+  VAR idx : INTEGER;
+  BEGIN
+    idx := Find(name);
+    IF idx = -1 THEN RETURN END;
+    curProc := syms[idx].pnum;
+    IF (curProc >= 1) & (curProc <= VAL(INTEGER, nProcs)) THEN
+      procs[curProc].fwd := FALSE;
+      procs[curProc].dHead := -1;
+      procs[curProc].dTail := -1
+    END
+  END ReuseProc;
+
+PROCEDURE OpenProcScope;
+  BEGIN
+    IF pdep < MaxPDepth THEN INC(pdep) END;
+    PushScope;
+    locCnt[pdep] := 0;
+    parCnt[pdep] := 0;
+    procSt[pdep] := curProc;
+    retSt[pdep] := InvalidType
+  END OpenProcScope;
+
+PROCEDURE CloseProc;
+  BEGIN
+    PopScope;
+    IF pdep > 0 THEN DEC(pdep) END;
+    curProc := procSt[pdep]
+  END CloseProc;
+
+PROCEDURE EnterParam (name: ARRAY OF CHAR; isVar: BOOLEAN;
+                      t: TypeIndex): BOOLEAN;
+  VAR idx, pi : INTEGER;
+  BEGIN
+    IF DupInLevel(name) THEN RETURN FALSE END;
+    IF isVar THEN idx := RawEnter(name, KindVarPar)
+    ELSE idx := RawEnter(name, KindParam)
+    END;
+    IF idx = -1 THEN RETURN FALSE END;
+    SetSymType(name, t);
+    syms[idx].slot := 3 + VAL(INTEGER, parCnt[pdep]);
+    INC(parCnt[pdep]);
+    pi := NewParamRec(t, isVar);
+    IF pi # -1 THEN AppParam(pi, FALSE) END;
+    RETURN TRUE
+  END EnterParam;
+
+PROCEDURE SetProcRet (t: TypeIndex): BOOLEAN;
+  BEGIN
+    IF (curProc < 1) OR (curProc > VAL(INTEGER, nProcs)) THEN
+      RETURN TRUE
+    END;
+    IF procs[curProc].everFwd
+       & ~SameType(t, procs[curProc].fret) THEN
+      RETURN FALSE
+    END;
+    procs[curProc].ret := t;
+    IF pdep > 0 THEN retSt[pdep] := t END;
+    RETURN TRUE
+  END SetProcRet;
+
+PROCEDURE VerifyProc (): BOOLEAN;
+  VAR fi, di : INTEGER;
+  BEGIN
+    IF (curProc < 1) OR (curProc > VAL(INTEGER, nProcs)) THEN
+      RETURN TRUE
+    END;
+    IF ~procs[curProc].everFwd THEN RETURN TRUE END;
+    fi := procs[curProc].fHead; di := procs[curProc].dHead;
+    WHILE (fi # -1) & (di # -1) DO
+      IF (params[fi].isVar # params[di].isVar)
+         OR ~SameType(params[fi].typ, params[di].typ) THEN
+        RETURN FALSE
+      END;
+      fi := params[fi].next; di := params[di].next
+    END;
+    RETURN (fi = -1) & (di = -1)
+  END VerifyProc;
+
+PROCEDURE SetForward;
+  BEGIN
+    IF (curProc < 1) OR (curProc > VAL(INTEGER, nProcs)) THEN RETURN END;
+    procs[curProc].fwd := TRUE;
+    procs[curProc].everFwd := TRUE;
+    procs[curProc].fret := procs[curProc].ret;
+    procs[curProc].fHead := procs[curProc].dHead;
+    procs[curProc].fTail := procs[curProc].dTail;
+    procs[curProc].dHead := -1;
+    procs[curProc].dTail := -1
+  END SetForward;
+
+PROCEDURE AnyForward (): BOOLEAN;
+  VAR i : CARDINAL;
+  BEGIN
+    i := 1;
+    WHILE i <= nProcs DO
+      IF procs[i].fwd THEN RETURN TRUE END;
+      INC(i)
+    END;
+    RETURN FALSE
+  END AnyForward;
+
+PROCEDURE ProcNum (name: ARRAY OF CHAR): INTEGER;
+  VAR idx : INTEGER;
+  BEGIN
+    idx := Find(name);
+    IF idx = -1 THEN RETURN -1 END;
+    IF syms[idx].kind # KindProc THEN RETURN -1 END;
+    RETURN syms[idx].pnum
+  END ProcNum;
+
+PROCEDURE SigHead (name: ARRAY OF CHAR): INTEGER;
+(* Param list head: define list if present, else forward list. *)
+  VAR n : INTEGER;
+  BEGIN
+    n := ProcNum(name);
+    IF n < 1 THEN RETURN -1 END;
+    IF procs[n].dHead # -1 THEN RETURN procs[n].dHead END;
+    RETURN procs[n].fHead
+  END SigHead;
+
+PROCEDURE ProcRet (name: ARRAY OF CHAR): TypeIndex;
+  VAR n : INTEGER;
+  BEGIN
+    n := ProcNum(name);
+    IF n < 1 THEN RETURN InvalidType END;
+    RETURN procs[n].ret
+  END ProcRet;
+
+PROCEDURE ProcNPar (name: ARRAY OF CHAR): CARDINAL;
+  VAR h : INTEGER;
+    c : CARDINAL;
+  BEGIN
+    h := SigHead(name); c := 0;
+    WHILE h # -1 DO INC(c); h := params[h].next END;
+    RETURN c
+  END ProcNPar;
+
+PROCEDURE ParamType (name: ARRAY OF CHAR; i: CARDINAL): TypeIndex;
+  VAR h : INTEGER;
+  BEGIN
+    h := SigHead(name);
+    WHILE (h # -1) & (i > 0) DO h := params[h].next; DEC(i) END;
+    IF h = -1 THEN RETURN InvalidType END;
+    RETURN params[h].typ
+  END ParamType;
+
+PROCEDURE ParamIsVar (name: ARRAY OF CHAR; i: CARDINAL): BOOLEAN;
+  VAR h : INTEGER;
+  BEGIN
+    h := SigHead(name);
+    WHILE (h # -1) & (i > 0) DO h := params[h].next; DEC(i) END;
+    IF h = -1 THEN RETURN FALSE END;
+    RETURN params[h].isVar
+  END ParamIsVar;
+
+PROCEDURE SymSlot (name: ARRAY OF CHAR): INTEGER;
+  VAR idx : INTEGER;
+  BEGIN
+    idx := Find(name);
+    IF idx = -1 THEN RETURN NoSlot END;
+    RETURN syms[idx].slot
+  END SymSlot;
+
+PROCEDURE SymDepth (name: ARRAY OF CHAR): INTEGER;
+  VAR idx : INTEGER;
+  BEGIN
+    idx := Find(name);
+    IF idx = -1 THEN RETURN 0 END;
+    RETURN VAL(INTEGER, syms[idx].pdep)
+  END SymDepth;
+
+PROCEDURE CurDepth (): INTEGER;
+  BEGIN
+    RETURN VAL(INTEGER, pdep)
+  END CurDepth;
+
+PROCEDURE CurProc (): INTEGER;
+  BEGIN
+    RETURN curProc
+  END CurProc;
+
+PROCEDURE CurNPar (): CARDINAL;
+  BEGIN
+    RETURN parCnt[pdep]
+  END CurNPar;
+
+PROCEDURE CurRet (): TypeIndex;
+  BEGIN
+    IF pdep = 0 THEN RETURN InvalidType END;
+    RETURN retSt[pdep]
+  END CurRet;
+
+PROCEDURE InProc (): BOOLEAN;
+  BEGIN
+    RETURN pdep > 0
+  END InProc;
+
+PROCEDURE InFunction (): BOOLEAN;
+  BEGIN
+    RETURN (pdep > 0) & (retSt[pdep] # InvalidType)
+  END InFunction;
+
+PROCEDURE ProcNLocals (): CARDINAL;
+  BEGIN
+    IF pdep = 0 THEN RETURN 0 END;
+    RETURN locCnt[pdep]
+  END ProcNLocals;
+
 (* ---------------- init ---------------- *)
 
 PROCEDURE Predef (name: ARRAY OF CHAR; kind: INTEGER; t: TypeIndex);
@@ -560,6 +875,10 @@ PROCEDURE Init;
     nSyms := 0; curLev := 0; mtop := 0;
     nPend := 0; nPendF := 0;
     nTypes := 0; nFields := 0;
+    nProcs := 0; nParams := 0;
+    pdep := 0; curProc := 0;
+    procSt[0] := 0; locCnt[0] := 0; parCnt[0] := 0;
+    retSt[0] := InvalidType;
     dInt := NewDesc(FInt, InvalidType);
     dCard := NewDesc(FInt, InvalidType);
     dReal := NewDesc(FReal, InvalidType);

BIN
SymTab.o


+ 4 - 1
compiler.frm

@@ -99,7 +99,7 @@ MODULE -->Grammar;
         (* add customized cases here *)
         | 200: Msg("duplicate identifier")
         | 201: Msg("undeclared identifier")
-        | 202: Msg("module name mismatch")
+        | 202: Msg("module/procedure name mismatch")
         | 210: Msg("incompatible assignment")
         | 211: Msg("arithmetic operand must be numeric")
         | 212: Msg("boolean operand required")
@@ -116,6 +116,9 @@ MODULE -->Grammar;
         | 223: Msg("cyclical type definition")
         | 224: Msg("ordinal type required")
         | 230: Msg("not supported in this phase")
+        | 231: Msg("procedure forward mismatch or missing body")
+        | 232: Msg("bad RETURN")
+        | 233: Msg("invalid procedure call")
         ELSE         Msg("Error: "); WriteInt(lst, nr, 0);
         END;
         WriteLn(lst)

+ 29 - 1
run_tests.sh

@@ -105,7 +105,7 @@ expect_run_none() {
 echo "=== Phase-0 tests (still green) ==="
 expect_ok ok_minimal.mod
 expect_ok ok_nobegin.mod
-expect_fail bad_mismatch.mod "module name mismatch"
+expect_fail bad_mismatch.mod "name mismatch"
 expect_fail bad_syntax.mod "expected"
 
 echo "=== Step-2 run tests (M2c -> mcint) ==="
@@ -133,5 +133,33 @@ expect_fail t_bad_exit.mod "not supported in this phase"
 expect_fail t_bad_const.mod "not supported in this phase"
 expect_fail t_bad_deref.mod "not supported in this phase"
 
+echo "=== Step-3 run tests (procedures) ==="
+expect_run p_proc.mod 7
+expect_run p_params.mod 21
+expect_run p_func.mod 42
+expect_run p_fact.mod 120
+expect_run p_fib.mod 55
+expect_run p_varpar.mod 3012
+expect_run p_nested.mod 42
+expect_run p_forward.mod 1
+expect_run p_return.mod 40
+expect_run p_local.mod 43
+expect_run p_mixed.mod 52
+expect_run p_chain.mod 9
+expect_run p_empty.mod 3
+expect_run p_realfn.mod 5
+
+echo "=== Step-3 rejection tests ==="
+expect_fail p_bad_call.mod "invalid procedure call"
+expect_fail p_bad_arity.mod "invalid procedure call"
+expect_fail p_bad_actual.mod "invalid procedure call"
+expect_fail p_bad_proper.mod "invalid procedure call"
+expect_fail p_bad_return1.mod "bad RETURN"
+expect_fail p_bad_return2.mod "bad RETURN"
+expect_fail p_bad_return3.mod "bad RETURN"
+expect_fail p_bad_fwd.mod "forward mismatch"
+expect_fail p_bad_nobody.mod "forward mismatch"
+expect_fail p_bad_undecl.mod "undeclared identifier"
+
 echo "=== $pass passed, $fail failed ==="
 test "$fail" = 0

+ 1 - 1
tests/bad_mismatch.LST

@@ -3,7 +3,7 @@ Listing:
     1  MODULE M;
     2  BEGIN
     3  END N.
-*****      ^ module name mismatch
+*****      ^ module/procedure name mismatch
 
     1 error
 

+ 16 - 0
tests/p_bad_actual.LST

@@ -0,0 +1,16 @@
+Listing:
+
+    1  MODULE PBadActual;
+    2  VAR ExitCode : INTEGER;
+    3  PROCEDURE Take(b : BOOLEAN) : INTEGER;
+    4  BEGIN
+    5    IF b THEN RETURN 1 ELSE RETURN 0 END
+    6  END Take;
+    7  BEGIN
+    8    ExitCode := Take(5)
+*****                     ^ invalid procedure call
+    9  END PBadActual.
+
+    1 error
+
+

+ 9 - 0
tests/p_bad_actual.mod

@@ -0,0 +1,9 @@
+MODULE PBadActual;
+VAR ExitCode : INTEGER;
+PROCEDURE Take(b : BOOLEAN) : INTEGER;
+BEGIN
+  IF b THEN RETURN 1 ELSE RETURN 0 END
+END Take;
+BEGIN
+  ExitCode := Take(5)
+END PBadActual.

+ 16 - 0
tests/p_bad_arity.LST

@@ -0,0 +1,16 @@
+Listing:
+
+    1  MODULE PBadArity;
+    2  VAR ExitCode : INTEGER;
+    3  PROCEDURE Add(a, b : INTEGER) : INTEGER;
+    4  BEGIN
+    5    RETURN a + b
+    6  END Add;
+    7  BEGIN
+    8    ExitCode := Add(1)
+*****                     ^ invalid procedure call
+    9  END PBadArity.
+
+    1 error
+
+

+ 9 - 0
tests/p_bad_arity.mod

@@ -0,0 +1,9 @@
+MODULE PBadArity;
+VAR ExitCode : INTEGER;
+PROCEDURE Add(a, b : INTEGER) : INTEGER;
+BEGIN
+  RETURN a + b
+END Add;
+BEGIN
+  ExitCode := Add(1)
+END PBadArity.

+ 12 - 0
tests/p_bad_call.LST

@@ -0,0 +1,12 @@
+Listing:
+
+    1  MODULE PBadCall;
+    2  VAR x : INTEGER;
+    3  BEGIN
+    4    x(1)
+*****       ^ invalid procedure call
+    5  END PBadCall.
+
+    1 error
+
+

+ 5 - 0
tests/p_bad_call.mod

@@ -0,0 +1,5 @@
+MODULE PBadCall;
+VAR x : INTEGER;
+BEGIN
+  x(1)
+END PBadCall.

+ 18 - 0
tests/p_bad_fwd.LST

@@ -0,0 +1,18 @@
+Listing:
+
+    1  MODULE PBadFwd;
+    2  VAR ExitCode : INTEGER;
+    3  PROCEDURE P(a : INTEGER) : INTEGER;
+    4  FORWARD;
+    5  PROCEDURE P(a, b : INTEGER) : INTEGER;
+*****                                ^ procedure forward mismatch or missing body
+    6  BEGIN
+    7    RETURN a + b
+    8  END P;
+    9  BEGIN
+   10    ExitCode := P(1, 2)
+   11  END PBadFwd.
+
+    1 error
+
+

+ 11 - 0
tests/p_bad_fwd.mod

@@ -0,0 +1,11 @@
+MODULE PBadFwd;
+VAR ExitCode : INTEGER;
+PROCEDURE P(a : INTEGER) : INTEGER;
+FORWARD;
+PROCEDURE P(a, b : INTEGER) : INTEGER;
+BEGIN
+  RETURN a + b
+END P;
+BEGIN
+  ExitCode := P(1, 2)
+END PBadFwd.

+ 14 - 0
tests/p_bad_nobody.LST

@@ -0,0 +1,14 @@
+Listing:
+
+    1  MODULE PBadNobody;
+    2  PROCEDURE P(a : INTEGER) : INTEGER;
+    3  FORWARD;
+    4  VAR ExitCode : INTEGER;
+    5  BEGIN
+    6    ExitCode := 0
+    7  END PBadNobody.
+*****                ^ procedure forward mismatch or missing body
+
+    1 error
+
+

+ 7 - 0
tests/p_bad_nobody.mod

@@ -0,0 +1,7 @@
+MODULE PBadNobody;
+PROCEDURE P(a : INTEGER) : INTEGER;
+FORWARD;
+VAR ExitCode : INTEGER;
+BEGIN
+  ExitCode := 0
+END PBadNobody.

+ 16 - 0
tests/p_bad_proper.LST

@@ -0,0 +1,16 @@
+Listing:
+
+    1  MODULE PBadProper;
+    2  VAR ExitCode : INTEGER;
+    3  PROCEDURE DoIt(x : INTEGER);
+    4  BEGIN
+    5    ExitCode := x
+    6  END DoIt;
+    7  BEGIN
+    8    ExitCode := DoIt(1) + 2
+*****                      ^ invalid procedure call
+    9  END PBadProper.
+
+    1 error
+
+

+ 9 - 0
tests/p_bad_proper.mod

@@ -0,0 +1,9 @@
+MODULE PBadProper;
+VAR ExitCode : INTEGER;
+PROCEDURE DoIt(x : INTEGER);
+BEGIN
+  ExitCode := x
+END DoIt;
+BEGIN
+  ExitCode := DoIt(1) + 2
+END PBadProper.

+ 11 - 0
tests/p_bad_return1.LST

@@ -0,0 +1,11 @@
+Listing:
+
+    1  MODULE PBadRet1;
+    2  BEGIN
+    3    RETURN
+*****    ^ bad RETURN
+    4  END PBadRet1.
+
+    1 error
+
+

+ 4 - 0
tests/p_bad_return1.mod

@@ -0,0 +1,4 @@
+MODULE PBadRet1;
+BEGIN
+  RETURN
+END PBadRet1.

+ 14 - 0
tests/p_bad_return2.LST

@@ -0,0 +1,14 @@
+Listing:
+
+    1  MODULE PBadRet2;
+    2  PROCEDURE P;
+    3  BEGIN
+    4    RETURN 1
+*****           ^ bad RETURN
+    5  END P;
+    6  BEGIN
+    7  END PBadRet2.
+
+    1 error
+
+

+ 7 - 0
tests/p_bad_return2.mod

@@ -0,0 +1,7 @@
+MODULE PBadRet2;
+PROCEDURE P;
+BEGIN
+  RETURN 1
+END P;
+BEGIN
+END PBadRet2.

+ 16 - 0
tests/p_bad_return3.LST

@@ -0,0 +1,16 @@
+Listing:
+
+    1  MODULE PBadRet3;
+    2  VAR ExitCode : INTEGER;
+    3  PROCEDURE F : INTEGER;
+    4  BEGIN
+    5    RETURN
+*****    ^ bad RETURN
+    6  END F;
+    7  BEGIN
+    8    ExitCode := F()
+    9  END PBadRet3.
+
+    1 error
+
+

+ 9 - 0
tests/p_bad_return3.mod

@@ -0,0 +1,9 @@
+MODULE PBadRet3;
+VAR ExitCode : INTEGER;
+PROCEDURE F : INTEGER;
+BEGIN
+  RETURN
+END F;
+BEGIN
+  ExitCode := F()
+END PBadRet3.

+ 11 - 0
tests/p_bad_undecl.LST

@@ -0,0 +1,11 @@
+Listing:
+
+    1  MODULE PBadUndecl;
+    2  BEGIN
+    3    Foo(1)
+*****    ^ undeclared identifier
+    4  END PBadUndecl.
+
+    1 error
+
+

+ 4 - 0
tests/p_bad_undecl.mod

@@ -0,0 +1,4 @@
+MODULE PBadUndecl;
+BEGIN
+  Foo(1)
+END PBadUndecl.

+ 21 - 0
tests/p_chain.LST

@@ -0,0 +1,21 @@
+Listing:
+
+    1  MODULE PChain;
+    2  VAR g : INTEGER; ExitCode : INTEGER;
+    3  PROCEDURE Set9(VAR v : INTEGER);
+    4  BEGIN
+    5    v := 9
+    6  END Set9;
+    7  PROCEDURE Mid(VAR w : INTEGER);
+    8  BEGIN
+    9    Set9(w)
+   10  END Mid;
+   11  BEGIN
+   12    g := 0;
+   13    Mid(g);
+   14    ExitCode := g
+   15  END PChain.
+
+    0 errors
+
+

+ 15 - 0
tests/p_chain.mod

@@ -0,0 +1,15 @@
+MODULE PChain;
+VAR g : INTEGER; ExitCode : INTEGER;
+PROCEDURE Set9(VAR v : INTEGER);
+BEGIN
+  v := 9
+END Set9;
+PROCEDURE Mid(VAR w : INTEGER);
+BEGIN
+  Set9(w)
+END Mid;
+BEGIN
+  g := 0;
+  Mid(g);
+  ExitCode := g
+END PChain.

+ 18 - 0
tests/p_empty.LST

@@ -0,0 +1,18 @@
+Listing:
+
+    1  MODULE PEmpty;
+    2  VAR ExitCode : INTEGER;
+    3  PROCEDURE Nop;
+    4  BEGIN
+    5  END Nop;
+    6  PROCEDURE Nop2;
+    7  END Nop2;
+    8  BEGIN
+    9    Nop;
+   10    Nop2;
+   11    ExitCode := 3
+   12  END PEmpty.
+
+    0 errors
+
+

+ 12 - 0
tests/p_empty.mod

@@ -0,0 +1,12 @@
+MODULE PEmpty;
+VAR ExitCode : INTEGER;
+PROCEDURE Nop;
+BEGIN
+END Nop;
+PROCEDURE Nop2;
+END Nop2;
+BEGIN
+  Nop;
+  Nop2;
+  ExitCode := 3
+END PEmpty.

+ 15 - 0
tests/p_fact.LST

@@ -0,0 +1,15 @@
+Listing:
+
+    1  MODULE PFact;
+    2  VAR ExitCode : INTEGER;
+    3  PROCEDURE Fact(n : INTEGER) : INTEGER;
+    4  BEGIN
+    5    IF n <= 1 THEN RETURN 1 ELSE RETURN n * Fact(n - 1) END
+    6  END Fact;
+    7  BEGIN
+    8    ExitCode := Fact(5)
+    9  END PFact.
+
+    0 errors
+
+

+ 9 - 0
tests/p_fact.mod

@@ -0,0 +1,9 @@
+MODULE PFact;
+VAR ExitCode : INTEGER;
+PROCEDURE Fact(n : INTEGER) : INTEGER;
+BEGIN
+  IF n <= 1 THEN RETURN 1 ELSE RETURN n * Fact(n - 1) END
+END Fact;
+BEGIN
+  ExitCode := Fact(5)
+END PFact.

+ 15 - 0
tests/p_fib.LST

@@ -0,0 +1,15 @@
+Listing:
+
+    1  MODULE PFib;
+    2  VAR ExitCode : INTEGER;
+    3  PROCEDURE Fib(n : INTEGER) : INTEGER;
+    4  BEGIN
+    5    IF n <= 1 THEN RETURN n ELSE RETURN Fib(n - 1) + Fib(n - 2) END
+    6  END Fib;
+    7  BEGIN
+    8    ExitCode := Fib(10)
+    9  END PFib.
+
+    0 errors
+
+

+ 9 - 0
tests/p_fib.mod

@@ -0,0 +1,9 @@
+MODULE PFib;
+VAR ExitCode : INTEGER;
+PROCEDURE Fib(n : INTEGER) : INTEGER;
+BEGIN
+  IF n <= 1 THEN RETURN n ELSE RETURN Fib(n - 1) + Fib(n - 2) END
+END Fib;
+BEGIN
+  ExitCode := Fib(10)
+END PFib.

+ 21 - 0
tests/p_forward.LST

@@ -0,0 +1,21 @@
+Listing:
+
+    1  MODULE PFwd;
+    2  VAR ExitCode : INTEGER;
+    3  PROCEDURE Even(n : INTEGER) : BOOLEAN;
+    4  FORWARD;
+    5  PROCEDURE Odd(n : INTEGER) : BOOLEAN;
+    6  BEGIN
+    7    IF n = 0 THEN RETURN FALSE ELSE RETURN Even(n - 1) END
+    8  END Odd;
+    9  PROCEDURE Even(n : INTEGER) : BOOLEAN;
+   10  BEGIN
+   11    IF n = 0 THEN RETURN TRUE ELSE RETURN Odd(n - 1) END
+   12  END Even;
+   13  BEGIN
+   14    IF Even(10) THEN ExitCode := 1 ELSE ExitCode := 0 END
+   15  END PFwd.
+
+    0 errors
+
+

+ 15 - 0
tests/p_forward.mod

@@ -0,0 +1,15 @@
+MODULE PFwd;
+VAR ExitCode : INTEGER;
+PROCEDURE Even(n : INTEGER) : BOOLEAN;
+FORWARD;
+PROCEDURE Odd(n : INTEGER) : BOOLEAN;
+BEGIN
+  IF n = 0 THEN RETURN FALSE ELSE RETURN Even(n - 1) END
+END Odd;
+PROCEDURE Even(n : INTEGER) : BOOLEAN;
+BEGIN
+  IF n = 0 THEN RETURN TRUE ELSE RETURN Odd(n - 1) END
+END Even;
+BEGIN
+  IF Even(10) THEN ExitCode := 1 ELSE ExitCode := 0 END
+END PFwd.

+ 15 - 0
tests/p_func.LST

@@ -0,0 +1,15 @@
+Listing:
+
+    1  MODULE PFunc;
+    2  VAR ExitCode : INTEGER;
+    3  PROCEDURE Double(x : INTEGER) : INTEGER;
+    4  BEGIN
+    5    RETURN x * 2
+    6  END Double;
+    7  BEGIN
+    8    ExitCode := Double(10) + Double(11)
+    9  END PFunc.
+
+    0 errors
+
+

+ 9 - 0
tests/p_func.mod

@@ -0,0 +1,9 @@
+MODULE PFunc;
+VAR ExitCode : INTEGER;
+PROCEDURE Double(x : INTEGER) : INTEGER;
+BEGIN
+  RETURN x * 2
+END Double;
+BEGIN
+  ExitCode := Double(10) + Double(11)
+END PFunc.

+ 18 - 0
tests/p_local.LST

@@ -0,0 +1,18 @@
+Listing:
+
+    1  MODULE PLocal;
+    2  VAR x : INTEGER; ExitCode : INTEGER;
+    3  PROCEDURE Get : INTEGER;
+    4  VAR x : INTEGER;
+    5  BEGIN
+    6    x := 40;
+    7    RETURN x + 2
+    8  END Get;
+    9  BEGIN
+   10    x := 1;
+   11    ExitCode := Get() + x
+   12  END PLocal.
+
+    0 errors
+
+

+ 12 - 0
tests/p_local.mod

@@ -0,0 +1,12 @@
+MODULE PLocal;
+VAR x : INTEGER; ExitCode : INTEGER;
+PROCEDURE Get : INTEGER;
+VAR x : INTEGER;
+BEGIN
+  x := 40;
+  RETURN x + 2
+END Get;
+BEGIN
+  x := 1;
+  ExitCode := Get() + x
+END PLocal.

+ 23 - 0
tests/p_mixed.LST

@@ -0,0 +1,23 @@
+Listing:
+
+    1  MODULE PMixed;
+    2  VAR log : INTEGER; ExitCode : INTEGER;
+    3  PROCEDURE Add1(x : INTEGER) : INTEGER;
+    4  BEGIN
+    5    log := log + 1;
+    6    RETURN x + 1
+    7  END Add1;
+    8  PROCEDURE Bump(x : INTEGER);
+    9  BEGIN
+   10    log := log + x
+   11  END Bump;
+   12  BEGIN
+   13    log := 0;
+   14    Bump(5);
+   15    Add1(0);
+   16    ExitCode := Add1(Add1(20)) * 2 + log
+   17  END PMixed.
+
+    0 errors
+
+

+ 17 - 0
tests/p_mixed.mod

@@ -0,0 +1,17 @@
+MODULE PMixed;
+VAR log : INTEGER; ExitCode : INTEGER;
+PROCEDURE Add1(x : INTEGER) : INTEGER;
+BEGIN
+  log := log + 1;
+  RETURN x + 1
+END Add1;
+PROCEDURE Bump(x : INTEGER);
+BEGIN
+  log := log + x
+END Bump;
+BEGIN
+  log := 0;
+  Bump(5);
+  Add1(0);
+  ExitCode := Add1(Add1(20)) * 2 + log
+END PMixed.

+ 21 - 0
tests/p_nested.LST

@@ -0,0 +1,21 @@
+Listing:
+
+    1  MODULE PNested;
+    2  VAR ExitCode : INTEGER;
+    3  PROCEDURE Outer;
+    4  VAR o : INTEGER;
+    5    PROCEDURE Inner : INTEGER;
+    6    BEGIN
+    7      RETURN o + 1
+    8    END Inner;
+    9  BEGIN
+   10    o := 41;
+   11    ExitCode := Inner()
+   12  END Outer;
+   13  BEGIN
+   14    Outer
+   15  END PNested.
+
+    0 errors
+
+

+ 15 - 0
tests/p_nested.mod

@@ -0,0 +1,15 @@
+MODULE PNested;
+VAR ExitCode : INTEGER;
+PROCEDURE Outer;
+VAR o : INTEGER;
+  PROCEDURE Inner : INTEGER;
+  BEGIN
+    RETURN o + 1
+  END Inner;
+BEGIN
+  o := 41;
+  ExitCode := Inner()
+END Outer;
+BEGIN
+  Outer
+END PNested.

+ 15 - 0
tests/p_params.LST

@@ -0,0 +1,15 @@
+Listing:
+
+    1  MODULE PParams;
+    2  VAR ExitCode : INTEGER;
+    3  PROCEDURE Sum6(a, b, c, d, e, f : INTEGER) : INTEGER;
+    4  BEGIN
+    5    RETURN a + b + c + d + e + f
+    6  END Sum6;
+    7  BEGIN
+    8    ExitCode := Sum6(1, 2, 3, 4, 5, 6)
+    9  END PParams.
+
+    0 errors
+
+

+ 9 - 0
tests/p_params.mod

@@ -0,0 +1,9 @@
+MODULE PParams;
+VAR ExitCode : INTEGER;
+PROCEDURE Sum6(a, b, c, d, e, f : INTEGER) : INTEGER;
+BEGIN
+  RETURN a + b + c + d + e + f
+END Sum6;
+BEGIN
+  ExitCode := Sum6(1, 2, 3, 4, 5, 6)
+END PParams.

+ 16 - 0
tests/p_proc.LST

@@ -0,0 +1,16 @@
+Listing:
+
+    1  MODULE PProc;
+    2  VAR g : INTEGER; ExitCode : INTEGER;
+    3  PROCEDURE Set7;
+    4  BEGIN
+    5    g := 7
+    6  END Set7;
+    7  BEGIN
+    8    Set7;
+    9    ExitCode := g
+   10  END PProc.
+
+    0 errors
+
+

+ 10 - 0
tests/p_proc.mod

@@ -0,0 +1,10 @@
+MODULE PProc;
+VAR g : INTEGER; ExitCode : INTEGER;
+PROCEDURE Set7;
+BEGIN
+  g := 7
+END Set7;
+BEGIN
+  Set7;
+  ExitCode := g
+END PProc.

+ 20 - 0
tests/p_realfn.LST

@@ -0,0 +1,20 @@
+Listing:
+
+    1  MODULE PRealFn;
+    2  VAR r : REAL; ExitCode : INTEGER;
+    3  PROCEDURE Half(x : REAL) : REAL;
+    4  BEGIN
+    5    RETURN x / 2.0
+    6  END Half;
+    7  PROCEDURE AddR(a, b : REAL) : REAL;
+    8  BEGIN
+    9    RETURN a + b
+   10  END AddR;
+   11  BEGIN
+   12    r := AddR(1.5, Half(7));
+   13    IF r = 5.0 THEN ExitCode := 5 ELSE ExitCode := 6 END
+   14  END PRealFn.
+
+    0 errors
+
+

+ 14 - 0
tests/p_realfn.mod

@@ -0,0 +1,14 @@
+MODULE PRealFn;
+VAR r : REAL; ExitCode : INTEGER;
+PROCEDURE Half(x : REAL) : REAL;
+BEGIN
+  RETURN x / 2.0
+END Half;
+PROCEDURE AddR(a, b : REAL) : REAL;
+BEGIN
+  RETURN a + b
+END AddR;
+BEGIN
+  r := AddR(1.5, Half(7));
+  IF r = 5.0 THEN ExitCode := 5 ELSE ExitCode := 6 END
+END PRealFn.

+ 20 - 0
tests/p_return.LST

@@ -0,0 +1,20 @@
+Listing:
+
+    1  MODULE PRet;
+    2  VAR ExitCode : INTEGER;
+    3  PROCEDURE FindIt(n : INTEGER) : INTEGER;
+    4  VAR i : INTEGER;
+    5  BEGIN
+    6    i := 0;
+    7    LOOP
+    8      i := i + 1;
+    9      IF i >= n THEN RETURN i * 10 END
+   10    END
+   11  END FindIt;
+   12  BEGIN
+   13    ExitCode := FindIt(4)
+   14  END PRet.
+
+    0 errors
+
+

+ 14 - 0
tests/p_return.mod

@@ -0,0 +1,14 @@
+MODULE PRet;
+VAR ExitCode : INTEGER;
+PROCEDURE FindIt(n : INTEGER) : INTEGER;
+VAR i : INTEGER;
+BEGIN
+  i := 0;
+  LOOP
+    i := i + 1;
+    IF i >= n THEN RETURN i * 10 END
+  END
+END FindIt;
+BEGIN
+  ExitCode := FindIt(4)
+END PRet.

+ 18 - 0
tests/p_varpar.LST

@@ -0,0 +1,18 @@
+Listing:
+
+    1  MODULE PVarPar;
+    2  VAR a, b : INTEGER; ExitCode : INTEGER;
+    3  PROCEDURE Swap(VAR x, y : INTEGER);
+    4  VAR t : INTEGER;
+    5  BEGIN
+    6    t := x; x := y; y := t
+    7  END Swap;
+    8  BEGIN
+    9    a := 12; b := 30;
+   10    Swap(a, b);
+   11    ExitCode := a * 100 + b
+   12  END PVarPar.
+
+    0 errors
+
+

+ 12 - 0
tests/p_varpar.mod

@@ -0,0 +1,12 @@
+MODULE PVarPar;
+VAR a, b : INTEGER; ExitCode : INTEGER;
+PROCEDURE Swap(VAR x, y : INTEGER);
+VAR t : INTEGER;
+BEGIN
+  t := x; x := y; y := t
+END Swap;
+BEGIN
+  a := 12; b := 30;
+  Swap(a, b);
+  ExitCode := a * 100 + b
+END PVarPar.

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است