Переглянути джерело

gaps 1-5: libc, enum/char sets, MIN/MAX+ sized types, set builtins, forward types

- FROM libc IMPORT printf/exit/...: intrinsic permissive external C
  procedures (varargs, no result) bound to the C symbols; string and
  CHAR-array actuals pass as descriptor+8 (SymTab.DeclareCProc/Varargs,
  QbeGen.CArg/CArgAdd, ArgList/ActParam varargs paths).
- SET OF enum and SET OF <char-range> (plus enum/BOOLEAN subranges);
  set span limit 256 -> 65536 (SymTab.SetSpan, SetType, Subrange,
  InCheck, SetElemClassOk).
- MIN/MAX for REAL/LONGREAL/LONGINT; gm2 sized names LONGCARD,
  SHORTREAL, INTEGER8/16/32/64, CARDINAL8/16/32/64.
- INCL/EXCL statements and SHIFT/ROTATE expressions over sets
  (QbeGen.SetClearBit/SetShift; bit-by-bit over a runtime amount).
- Module-level forward type references: a global may name a type
  declared further down; emission deferred to FlushPend at module end
  (SymTab.IsUnresolved + relaxed ForwardType).
- Tests t_libc/t_setops/t_minmax2/t_fwdtype; docs/analysis_gaps.md.
- Suite 165/165; fixpoint OK (2,745,278 bytes).  gm2 testsuite corpus
  301 -> 376 compile-OK (0 CRASH); gm2 stdlib 115 -> 124.
Eric Streit 2 тижнів тому
батько
коміт
619bff9a99

+ 4 - 0
compiler/run_tests.sh

@@ -70,6 +70,10 @@ expect_run t_ctor5.mod 0
 expect_run t_ctor6.mod 0
 expect_run t_ctor6.mod 0
 expect_run t_ctor7.mod 42
 expect_run t_ctor7.mod 42
 expect_run t_minmax.mod 42
 expect_run t_minmax.mod 42
+expect_run t_minmax2.mod 42
+expect_run t_setops.mod 42
+expect_run t_fwdtype.mod 42
+expect_run t_libc.mod 42
 expect_run t_nestidx.mod 42
 expect_run t_nestidx.mod 42
 expect_run t_proctype.mod 42
 expect_run t_proctype.mod 42
 expect_run t_compat.mod 42
 expect_run t_compat.mod 42

+ 170 - 48
compiler/src/M2.atg

@@ -61,6 +61,24 @@ VAR
   (* Class of the type of the innermost `TypeName{...}` brace
   (* Class of the type of the innermost `TypeName{...}` brace
      constructor (ClSet or ClArray); dispatches BraceElem. *)
      constructor (ClSet or ClArray); dispatches BraceElem. *)
   braceCls: INTEGER;
   braceCls: INTEGER;
+  (* Module-level VAR declarations whose type was still an unresolved
+     forward alias at declaration time; emitted once the TYPE block
+     completes (module scope only). *)
+  nPendVar: CARDINAL;
+  pendVarName: ARRAY [0 .. 255] OF SymTab.Name;
+  pendVarT: ARRAY [0 .. 255] OF SymTab.TypeIndex;
+
+PROCEDURE FlushPend;
+(* Emit module-level globals whose forward type is now resolved. *)
+  VAR k: CARDINAL;
+  BEGIN
+    k := 0;
+    WHILE k < nPendVar DO
+      QbeGen.DeclVar(pendVarName[k], pendVarT[k]);
+      INC(k)
+    END;
+    nPendVar := 0
+  END FlushPend;
 
 
 CHARACTERS
 CHARACTERS
   eol      = CHR(13) .
   eol      = CHR(13) .
@@ -122,33 +140,41 @@ PRODUCTIONS
       GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
       GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
                                              SemError(202) END;
                                              SemError(202) END;
                                            SymTab.EndUnit; .) .
                                            SymTab.EndUnit; .) .
-  ImplUnit                              (. VAR m1, m2: SymTab.Name; .)
+  ImplUnit                              (. VAR m1, m2: SymTab.Name;
+                                             k: CARDINAL; .)
     = "IMPLEMENTATION" "MODULE"
     = "IMPLEMENTATION" "MODULE"
       GetIdent<m1>                      (. IF NOT SymTab.BeginImpl(m1) THEN
       GetIdent<m1>                      (. IF NOT SymTab.BeginImpl(m1) THEN
                                              SemError(201) END;
                                              SemError(201) END;
+                                           nPendVar := 0;
                                            QbeGen.SetModule(m1); .)
                                            QbeGen.SetModule(m1); .)
       ";"
       ";"
       { Import }
       { Import }
       DeclSeq
       DeclSeq
-      [ "BEGIN"                         (. QbeGen.BeginInit(m1); .)
+      [ "BEGIN"                         (. FlushPend;
+                                           QbeGen.BeginInit(m1); .)
         [ StatSeq ]                       (. QbeGen.EndInit; .) ]
         [ StatSeq ]                       (. QbeGen.EndInit; .) ]
       "END"
       "END"
-      GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
+      GetIdent<m2>                      (. FlushPend;
+                                           IF NOT SymTab.Equal(m1, m2) THEN
                                              SemError(202) END;
                                              SemError(202) END;
                                            SymTab.EndUnit; .) .
                                            SymTab.EndUnit; .) .
-  ProgModule                            (. VAR m1, m2: SymTab.Name; .)
+  ProgModule                            (. VAR m1, m2: SymTab.Name;
+                                             k: CARDINAL; .)
     = "MODULE"
     = "MODULE"
       GetIdent<m1>                      (. IF NOT SymTab.BeginProg(m1) THEN
       GetIdent<m1>                      (. IF NOT SymTab.BeginProg(m1) THEN
                                              SemError(200) END;
                                              SemError(200) END;
+                                           nPendVar := 0;
                                            QbeGen.SetModule(m1); .)
                                            QbeGen.SetModule(m1); .)
       [ Priority ]
       [ Priority ]
       ";"
       ";"
       { Import }
       { Import }
       DeclSeq
       DeclSeq
-      [ "BEGIN"                         (. QbeGen.BeginBody; .)
+      [ "BEGIN"                         (. FlushPend;
+                                           QbeGen.BeginBody; .)
         [ StatSeq ] ]
         [ StatSeq ] ]
       "END"
       "END"
-      GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
+      GetIdent<m2>                      (. FlushPend;
+                                           IF NOT SymTab.Equal(m1, m2) THEN
                                              SemError(202) END;
                                              SemError(202) END;
                                            QbeGen.EndModule(m1);
                                            QbeGen.EndModule(m1);
                                            SymTab.EndUnit; .) .
                                            SymTab.EndUnit; .) .
@@ -209,7 +235,12 @@ PRODUCTIONS
   (* Pervasive built-ins imported from SYSTEM (e.g. TSIZE) are
   (* Pervasive built-ins imported from SYSTEM (e.g. TSIZE) are
      accepted and ignored: the built-in applies regardless. *)
      accepted and ignored: the built-in applies regardless. *)
   ImpName<mod: SymTab.Name>             (. VAR n: SymTab.Name; .)
   ImpName<mod: SymTab.Name>             (. VAR n: SymTab.Name; .)
-    = GetIdent<n>                       (. IF SymTab.ModKnown(mod)
+    = GetIdent<n>                       (. IF SymTab.Equal(mod, "libc") THEN
+                                             (* intrinsic C library:
+                                                permissive external *)
+                                             IF NOT SymTab.DeclareCProc(n) THEN
+                                               SemError(201) END
+                                           ELSIF SymTab.ModKnown(mod)
                                              AND NOT SymTab.ImportFrom(mod, n) THEN
                                              AND NOT SymTab.ImportFrom(mod, n) THEN
                                              SemError(201) END; .)
                                              SemError(201) END; .)
     | ( "TSIZE" | "SIZE" | "ADR" | "HIGH" | "LEN"
     | ( "TSIZE" | "SIZE" | "ADR" | "HIGH" | "LEN"
@@ -387,12 +418,16 @@ PRODUCTIONS
                                                blo := 0; bspan := 2
                                                blo := 0; bspan := 2
                                              ELSIF bcls = SymTab.ClChar THEN
                                              ELSIF bcls = SymTab.ClChar THEN
                                                blo := 0; bspan := 256
                                                blo := 0; bspan := 256
+                                             ELSIF bcls = SymTab.ClEnum THEN
+                                               blo := 0;
+                                               bspan := VAL(INTEGER,
+                                                 SymTab.EnumCount(base))
                                              ELSIF SymTab.SubBounds(base,
                                              ELSIF SymTab.SubBounds(base,
                                                      blo, bhi) THEN
                                                      blo, bhi) THEN
                                                bspan := bhi - blo + 1
                                                bspan := bhi - blo + 1
                                              ELSE bspan := 0 END;
                                              ELSE bspan := 0 END;
                                              IF (bspan <= 0)
                                              IF (bspan <= 0)
-                                                OR (bspan > 256) THEN
+                                                OR (bspan > 65536) THEN
                                                SemError(230);
                                                SemError(230);
                                                t := SymTab.InvalidType
                                                t := SymTab.InvalidType
                                              ELSE t := SymTab.NewSet(base)
                                              ELSE t := SymTab.NewSet(base)
@@ -501,10 +536,8 @@ PRODUCTIONS
                                         (. IF (tlo = SymTab.InvalidType)
                                         (. IF (tlo = SymTab.InvalidType)
                                             OR (thi = SymTab.InvalidType) THEN
                                             OR (thi = SymTab.InvalidType) THEN
                                            t := SymTab.InvalidType
                                            t := SymTab.InvalidType
-                                         ELSIF (SymTab.ClassOf(tlo) #
-                                                SymTab.ClInt)
-                                            OR (SymTab.ClassOf(thi) #
-                                                SymTab.ClInt) THEN
+                                         ELSIF NOT SymTab.IsOrdinal(tlo)
+                                            OR NOT SymTab.IsOrdinal(thi) THEN
                                            SemError(230);
                                            SemError(230);
                                            t := SymTab.InvalidType
                                            t := SymTab.InvalidType
                                          ELSIF NOT SymTab.ConstInt(qlo, lo)
                                          ELSIF NOT SymTab.ConstInt(qlo, lo)
@@ -692,6 +725,7 @@ PRODUCTIONS
     = VarIdents ":"
     = VarIdents ":"
       Type<t, FALSE>                    (. cls := SymTab.ClassOf(t);
       Type<t, FALSE>                    (. cls := SymTab.ClassOf(t);
                                            IF (t # SymTab.InvalidType)
                                            IF (t # SymTab.InvalidType)
+ AND NOT SymTab.IsUnresolved(t)
  AND (cls # SymTab.ClInt)
  AND (cls # SymTab.ClInt)
  AND (cls # SymTab.ClBool)
  AND (cls # SymTab.ClBool)
  AND (cls # SymTab.ClChar)
  AND (cls # SymTab.ClChar)
@@ -710,10 +744,27 @@ PRODUCTIONS
                                            IF QbeGen.LocFull() THEN
                                            IF QbeGen.LocFull() THEN
                                              SemError(233) END;
                                              SemError(233) END;
                                            i := 0;
                                            i := 0;
-                                           WHILE i < SymTab.PendCount() DO
-                                             SymTab.PendName(i, nm);
-                                             QbeGen.DeclVar(nm, t);
-                                             INC(i)
+                                           IF SymTab.IsUnresolved(t)
+                                              AND NOT SymTab.InProc() THEN
+                                             (* a forward-typed global:
+                                                defer emission until the
+                                                TYPE block completes *)
+                                             WHILE i < SymTab.PendCount() DO
+                                               SymTab.PendName(i, nm);
+                                               IF nPendVar <=
+                                                  HIGH(pendVarName) THEN
+                                                 pendVarName[nPendVar] := nm;
+                                                 pendVarT[nPendVar] := t;
+                                                 INC(nPendVar)
+                                               END;
+                                               INC(i)
+                                             END
+                                           ELSE
+                                             WHILE i < SymTab.PendCount() DO
+                                               SymTab.PendName(i, nm);
+                                               QbeGen.DeclVar(nm, t);
+                                               INC(i)
+                                             END
                                            END;
                                            END;
                                            (* a plain VAR list, not a
                                            (* a plain VAR list, not a
                                               heading: the signature
                                               heading: the signature
@@ -858,9 +909,35 @@ PRODUCTIONS
     | NewStat
     | NewStat
     | DisposeStat
     | DisposeStat
     | IncDecStat
     | IncDecStat
+    | InclExclStat
     | "EXIT"                            (. IF QbeGen.TopLoop(lx) THEN
     | "EXIT"                            (. IF QbeGen.TopLoop(lx) THEN
                                              QbeGen.Jmp(lx)
                                              QbeGen.Jmp(lx)
                                            ELSE SemError(230) END; .) .
                                            ELSE SemError(230) END; .) .
+  (* INCL(set, elem) / EXCL(set, elem): PIM set-element builtins. *)
+  InclExclStat                          (. VAR at, et2: SymTab.TypeIndex;
+                                             dk: INTEGER;
+                                             qd, qe: QbeGen.QVal;
+                                             qn: SymTab.Name;
+                                             sfx, isInc: BOOLEAN; .)
+    = ( "INCL"                          (. isInc := TRUE; .)
+      | "EXCL"                          (. isInc := FALSE; .) )
+      "(" Design<at, dk, qd, qn, sfx> ","
+      Expr<et2, qe> ")"
+                                        (. IF at = SymTab.InvalidType THEN
+                                           ELSIF SymTab.ClassOf(at) #
+                                                 SymTab.ClSet THEN
+                                             SemError(222)
+                                           ELSE
+                                             IF isInc THEN
+                                               QbeGen.SetBit(qd, qe,
+                                                 SymTab.SetBaseLo(at),
+                                                 SymTab.SetCount(at))
+                                             ELSE
+                                               QbeGen.SetClearBit(qd, qe,
+                                                 SymTab.SetBaseLo(at),
+                                                 SymTab.SetCount(at))
+                                             END
+                                           END; .) .
   (* INC(v [,step]) / DEC(v [,step]) as builtin statements over an
   (* INC(v [,step]) / DEC(v [,step]) as builtin statements over an
      integer designator. *)
      integer designator. *)
   IncDecStat                            (. VAR dt, et2: SymTab.TypeIndex;
   IncDecStat                            (. VAR dt, et2: SymTab.TypeIndex;
@@ -1151,7 +1228,7 @@ PRODUCTIONS
                                              vs: INTEGER;
                                              vs: INTEGER;
                                              res: SymTab.TypeIndex;
                                              res: SymTab.TypeIndex;
                                              mg: QbeGen.QVal;
                                              mg: QbeGen.QVal;
-                                             ok, ind, isMeth: BOOLEAN; .)
+                                             ok, ind, isMeth, va: BOOLEAN; .)
     = "("                               (. called := TRUE;
     = "("                               (. called := TRUE;
                                            ok := TRUE;
                                            ok := TRUE;
                                            ind := FALSE;
                                            ind := FALSE;
@@ -1208,7 +1285,11 @@ PRODUCTIONS
                                                np := SymTab.ProcTypeNPar(pt)
                                                np := SymTab.ProcTypeNPar(pt)
                                              ELSE np := SymTab.ProcNPar(pn)
                                              ELSE np := SymTab.ProcNPar(pn)
                                              END;
                                              END;
-                                             IF i # np THEN
+                                             va := (NOT isMeth) AND (NOT ind)
+                                               AND (SymTab.SymKind(pn) =
+                                                    SymTab.KindProc)
+                                               AND SymTab.Varargs(pn);
+                                             IF (i # np) AND NOT va THEN
                                                SemError(233); ok := FALSE
                                                SemError(233); ok := FALSE
                                              END
                                              END
                                            END;
                                            END;
@@ -1239,8 +1320,15 @@ PRODUCTIONS
            methCls: SymTab.TypeIndex; i: CARDINAL>
            methCls: SymTab.TypeIndex; i: CARDINAL>
                                         (. VAR at, ft: SymTab.TypeIndex;
                                         (. VAR at, ft: SymTab.TypeIndex;
                                              qe, qa, qt: QbeGen.QVal;
                                              qe, qa, qt: QbeGen.QVal;
-                                             isV, conv: BOOLEAN; .)
-    = Expr<at, qe>                      (. IF ind THEN
+                                             isV, conv, va: BOOLEAN;
+                                             cl: CHAR; .)
+    = Expr<at, qe>                      (. va := (NOT ind)
+                                             AND (methCls =
+                                                SymTab.InvalidType)
+                                             AND (SymTab.SymKind(pn) =
+                                                SymTab.KindProc)
+                                             AND SymTab.Varargs(pn);
+                                           IF ind THEN
                                              ft :=
                                              ft :=
                                                SymTab.ProcTypeParamType(pt,
                                                SymTab.ProcTypeParamType(pt,
                                                  i);
                                                  i);
@@ -1259,9 +1347,11 @@ PRODUCTIONS
                                              ft := SymTab.ParamType(pn, i);
                                              ft := SymTab.ParamType(pn, i);
                                              isV := SymTab.ParamIsVar(pn, i)
                                              isV := SymTab.ParamIsVar(pn, i)
                                            END;
                                            END;
-                                           IF (at = SymTab.InvalidType)
-                                              OR (ft =
-                                                 SymTab.InvalidType) THEN
+                                           IF (at = SymTab.InvalidType) THEN
+                                           ELSIF ft = SymTab.InvalidType THEN
+                                             IF va THEN
+                                               QbeGen.CArgAdd(qe, at, qa, cl)
+                                             END
                                            ELSIF isV THEN
                                            ELSIF isV THEN
                                              IF (SymTab.ClassOf(at)
                                              IF (SymTab.ClassOf(at)
                                                 = SymTab.ClChar)
                                                 = SymTab.ClChar)
@@ -1634,6 +1724,8 @@ PRODUCTIONS
                                                         = SymTab.ClEnum)
                                                         = SymTab.ClEnum)
                                                     OR (cls
                                                     OR (cls
                                                         = SymTab.ClReal)
                                                         = SymTab.ClReal)
+                                                    OR (cls
+                                                        = SymTab.ClLong)
                                                     OR (cls
                                                     OR (cls
                                                         = SymTab.ClNil)) THEN
                                                         = SymTab.ClNil)) THEN
                                                    IF cls = SymTab.ClNil THEN
                                                    IF cls = SymTab.ClNil THEN
@@ -1643,7 +1735,9 @@ PRODUCTIONS
                                                      OR (cls
                                                      OR (cls
                                                        = SymTab.ClChar)
                                                        = SymTab.ClChar)
                                                      OR (cls
                                                      OR (cls
-                                                       = SymTab.ClEnum))
+                                                       = SymTab.ClEnum)
+                                                     OR (cls
+                                                       = SymTab.ClLong))
   AND SymTab.GetSymVal(n, cv)
   AND SymTab.GetSymVal(n, cv)
   AND QbeGen.IsImm(cv) THEN
   AND QbeGen.IsImm(cv) THEN
                                                      QbeGen.CopyOp(cv, q)
                                                      QbeGen.CopyOp(cv, q)
@@ -2257,10 +2351,10 @@ PRODUCTIONS
     | ( "AND" | "&" )                   (. op := SymTab.OpAnd; .) .
     | ( "AND" | "&" )                   (. op := SymTab.OpAnd; .) .
   Fact<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
   Fact<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
                                         (. VAR s: ARRAY [0 .. 255] OF CHAR;
                                         (. VAR s: ARRAY [0 .. 255] OF CHAR;
-                                             et, dt, t2, st, ct2:
+                                             et, dt, t2, st, ct2, et2:
                                                SymTab.TypeIndex;
                                                SymTab.TypeIndex;
                                              dk: INTEGER;
                                              dk: INTEGER;
-                                             qd, q2, sq, qa, qm0, qr:
+                                             qd, q2, sq, qa, qm0, qr, qt:
                                                QbeGen.QVal;
                                                QbeGen.QVal;
                                              qn, vn: SymTab.Name;
                                              qn, vn: SymTab.Name;
                                              vt: SymTab.TypeIndex;
                                              vt: SymTab.TypeIndex;
@@ -2489,11 +2583,51 @@ PRODUCTIONS
                                                SymTab.ObjectSize(dt)), q);
                                                SymTab.ObjectSize(dt)), q);
                                              t := SymTab.IntType()
                                              t := SymTab.IntType()
                                            END; .)
                                            END; .)
+    | ( "SHIFT"                         (. isMax := FALSE; .)
+      | "ROTATE"                        (. isMax := TRUE; .) )
+      "(" Expr<et, q> "," Expr<et2, q2> ")"
+                                        (. (* set shift/rotate: isMax
+                                              doubles as "rotate" *)
+                                           IF (et # SymTab.InvalidType)
+                                              AND (SymTab.ClassOf(et) =
+                                                 SymTab.ClSet) THEN
+                                             QbeGen.SetShift(q, q2,
+                                               SymTab.SetWords(et),
+                                               SymTab.SetCount(et), isMax,
+                                               qt);
+                                             t := et;
+                                             QbeGen.CopyOp(qt, q)
+                                           ELSE SemError(230);
+                                             t := SymTab.InvalidType;
+                                             QbeGen.CopyOp("0", q)
+                                           END; .)
     | ( "MIN"                           (. isMax := FALSE; .)
     | ( "MIN"                           (. isMax := FALSE; .)
       | "MAX"                           (. isMax := TRUE; .) )
       | "MAX"                           (. isMax := TRUE; .) )
       "(" Design<dt, dk, qd, qn, sfx> ")"
       "(" Design<dt, dk, qd, qn, sfx> ")"
-                                        (. IF SymTab.TypeBounds(dt, lo, hi)
-                                           THEN
+                                        (. IF (dt # SymTab.InvalidType)
+ AND (SymTab.ClassOf(dt) = SymTab.ClReal) THEN
+                                             (* REAL/LONGREAL: the
+                                                implementation bounds *)
+                                             IF isMax THEN
+                                               QbeGen.NormReal(
+                                                 "3.402823e38", q)
+                                             ELSE QbeGen.NormReal(
+                                                 "-3.402823e38", q)
+                                             END;
+                                             t := SymTab.RealType()
+                                           ELSIF (dt #
+                                              SymTab.InvalidType)
+ AND (SymTab.ClassOf(dt) =
+                                                 SymTab.ClLong) THEN
+                                             IF isMax THEN
+                                               QbeGen.CopyOp(
+                                                 "9223372036854775807", q)
+                                             ELSE QbeGen.CopyOp(
+                                                 "-9223372036854775808", q)
+                                             END;
+                                             t := SymTab.LongType()
+                                           ELSIF SymTab.TypeBounds(dt, lo,
+                                                    hi) THEN
                                              IF isMax THEN
                                              IF isMax THEN
                                                QbeGen.IntStr(hi, q)
                                                QbeGen.IntStr(hi, q)
                                              ELSE QbeGen.IntStr(lo, q)
                                              ELSE QbeGen.IntStr(lo, q)
@@ -2736,16 +2870,10 @@ PRODUCTIONS
                                                    SymTab.ClassOf(et2)
                                                    SymTab.ClassOf(et2)
                                                ELSE cl2 := SymTab.ClInt
                                                ELSE cl2 := SymTab.ClInt
                                                END;
                                                END;
-                                               IF ((cl # SymTab.ClInt)
- AND (cl # SymTab.ClChar)
- AND (cl # SymTab.ClBool))
-                                                  OR (hasR AND 
-                                                     ((cl2
-                                                       # SymTab.ClInt)
- AND (cl2
-                                                        # SymTab.ClChar)
- AND (cl2
-                                                        # SymTab.ClBool))) THEN
+                                               IF NOT SymTab.SetElemClassOk(cl)
+                                                  OR (hasR AND NOT
+                                                     SymTab.SetElemClassOk(cl2))
+                                               THEN
                                                  SemError(222)
                                                  SemError(222)
                                                ELSIF hasR
                                                ELSIF hasR
  AND SymTab.ConstInt(qe, v)
  AND SymTab.ConstInt(qe, v)
@@ -2815,16 +2943,10 @@ PRODUCTIONS
                                                  SymTab.ClassOf(et2)
                                                  SymTab.ClassOf(et2)
                                              ELSE cl2 := SymTab.ClInt
                                              ELSE cl2 := SymTab.ClInt
                                              END;
                                              END;
-                                             IF ((cl # SymTab.ClInt)
- AND (cl # SymTab.ClChar)
- AND (cl # SymTab.ClBool))
-                                                OR (hasR AND 
-                                                   ((cl2
-                                                     # SymTab.ClInt)
- AND (cl2
-                                                      # SymTab.ClChar)
- AND (cl2
-                                                      # SymTab.ClBool))) THEN
+                                             IF NOT SymTab.SetElemClassOk(cl)
+                                                OR (hasR AND NOT
+                                                   SymTab.SetElemClassOk(cl2))
+                                             THEN
                                                SemError(222)
                                                SemError(222)
                                              ELSIF hasR
                                              ELSIF hasR
  AND SymTab.ConstInt(qe, v)
  AND SymTab.ConstInt(qe, v)

+ 2917 - 2795
compiler/src/M2.lst

@@ -79,2812 +79,2934 @@ Listing:
    61    (* Class of the type of the innermost `TypeName{...}` brace
    61    (* Class of the type of the innermost `TypeName{...}` brace
    62       constructor (ClSet or ClArray); dispatches BraceElem. *)
    62       constructor (ClSet or ClArray); dispatches BraceElem. *)
    63    braceCls: INTEGER;
    63    braceCls: INTEGER;
-   64  
-   65  CHARACTERS
-   66    eol      = CHR(13) .
-   67    lf       = CHR(10) .
-   68    letter   = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" .
-   69    digit    = "0123456789" .
-   70    hexDigit = digit + "ABCDEFabcdef" .
-   71    noQuote1 = ANY - "'" - eol .
-   72    noQuote2 = ANY - '"' - eol .
-   73  
-   74  IGNORE CHR(9) .. CHR(13)
-   75  
-   76  COMMENTS FROM "(*" TO "*)" NESTED
-   77  COMMENTS FROM "//" TO lf
-   78  
-   79  TOKENS
-   80    ident   = letter { letter | digit } .
-   81    integer = digit { digit }
-   82            | digit { digit } CONTEXT("..")
-   83            | "0x" hexDigit { hexDigit }
-   84            | "0X" hexDigit { hexDigit } .
-   85    real    = digit { digit } "." { digit }
-   86              [ ( "E" | "e" ) [ "+" | "-" ] digit { digit } ] .
-   87    string  = "'" { noQuote1 } "'"
-   88            | '"' { noQuote2 } '"' .
-   89    charConst = digit { digit } ( "C" | "c" ) .
-   90    ustring = ( "U" | "u" ) ( "'" { noQuote1 } "'" | '"' { noQuote2 } '"' ) .
+   64    (* Module-level VAR declarations whose type was still an unresolved
+   65       forward alias at declaration time; emitted once the TYPE block
+   66       completes (module scope only). *)
+   67    nPendVar: CARDINAL;
+   68    pendVarName: ARRAY [0 .. 255] OF SymTab.Name;
+   69    pendVarT: ARRAY [0 .. 255] OF SymTab.TypeIndex;
+   70  
+   71  PROCEDURE FlushPend;
+   72  (* Emit module-level globals whose forward type is now resolved. *)
+   73    VAR k: CARDINAL;
+   74    BEGIN
+   75      k := 0;
+   76      WHILE k < nPendVar DO
+   77        QbeGen.DeclVar(pendVarName[k], pendVarT[k]);
+   78        INC(k)
+   79      END;
+   80      nPendVar := 0
+   81    END FlushPend;
+   82  
+   83  CHARACTERS
+   84    eol      = CHR(13) .
+   85    lf       = CHR(10) .
+   86    letter   = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" .
+   87    digit    = "0123456789" .
+   88    hexDigit = digit + "ABCDEFabcdef" .
+   89    noQuote1 = ANY - "'" - eol .
+   90    noQuote2 = ANY - '"' - eol .
    91  
    91  
-   92  PRODUCTIONS
-   93    M2
-   94      = Unit "." .
-   95    (* Units: program modules compile fully; DEFINITION and
-   96       IMPLEMENTATION modules parse + check now but lower in step 4
-   97       (each ends with one 230); same for nested local modules. *)
-   98    Unit
-   99      = DefUnit
-  100      | ImplUnit
-  101      | ProgModule .
-  102    (* Step 4.3: one session compiles DEFINITION, its IMPLEMENTATION
-  103       and one program (last) into one image. Units share the symbol
-  104       table; imports materialize exported names. *)
-  105    DefUnit                               (. VAR m1, m2, pn: SymTab.Name; .)
-  106      = "DEFINITION" "MODULE"
-  107        GetIdent<m1>                      (. IF NOT SymTab.BeginDef(m1) THEN
-  108                                               SemError(200) END;
-  109                                             QbeGen.SetModule(m1); .)
-  110        ";"
-  111        { Import }
-  112        [ "EXPORT" [ "QUALIFIED" ]        (. (* definition-module export
-  113                                                list: parsed, and the
-  114                                                names are already exported
-  115                                                by the module scope *) .)
-  116          GetIdent<pn> { "," GetIdent<pn> } ";" ]
-  117        { ConstBlock | TypeBlock<TRUE> | VarBlock
-  118        | ProcHeading<pn, SymTab.InvalidType> ";"
-  119                                          (. SymTab.CloseProc;
-  120                                               QbeGen.AbortFunc; .) }
-  121        "END"
-  122        GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
-  123                                               SemError(202) END;
-  124                                             SymTab.EndUnit; .) .
-  125    ImplUnit                              (. VAR m1, m2: SymTab.Name; .)
-  126      = "IMPLEMENTATION" "MODULE"
-  127        GetIdent<m1>                      (. IF NOT SymTab.BeginImpl(m1) THEN
-  128                                               SemError(201) END;
-  129                                             QbeGen.SetModule(m1); .)
-  130        ";"
-  131        { Import }
-  132        DeclSeq
-  133        [ "BEGIN"                         (. QbeGen.BeginInit(m1); .)
-  134          [ StatSeq ]                       (. QbeGen.EndInit; .) ]
-  135        "END"
-  136        GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
-  137                                               SemError(202) END;
-  138                                             SymTab.EndUnit; .) .
-  139    ProgModule                            (. VAR m1, m2: SymTab.Name; .)
-  140      = "MODULE"
-  141        GetIdent<m1>                      (. IF NOT SymTab.BeginProg(m1) THEN
-  142                                               SemError(200) END;
-  143                                             QbeGen.SetModule(m1); .)
-  144        [ Priority ]
-  145        ";"
-  146        { Import }
-  147        DeclSeq
-  148        [ "BEGIN"                         (. QbeGen.BeginBody; .)
-  149          [ StatSeq ] ]
-  150        "END"
-  151        GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
-  152                                               SemError(202) END;
-  153                                             QbeGen.EndModule(m1);
-  154                                             SymTab.EndUnit; .) .
-  155    DeclSeq
-  156      = { ConstBlock | TypeBlock<FALSE> | VarBlock | ProcDecl ";"
-  157        | NestedModule ";" | ClassItem ";" } .
-  158    (* Local module, Wirth form. Declarations lower like top-level ones
-  159       (same QBE module prefix); a BEGIN body becomes an init function
-  160       that main calls; the EXPORT list is hoisted into the enclosing
-  161       scope at END. *)
-  162    NestedModule                          (. VAR m1, m2: SymTab.Name;
-  163                                               expNames: ARRAY [0 .. 63] OF SymTab.Name;
-  164                                               expCount, k: CARDINAL; .)
-  165      = "MODULE"
-  166        GetIdent<m1>                      (. IF NOT SymTab.Enter(m1,
-  167                                               SymTab.KindModule) THEN
-  168                                               SemError(200) END;
-  169                                             SymTab.PushScope;
-  170                                             expCount := 0; .)
-  171        [ Priority ]
-  172        ";"
-  173        { Import }
-  174        [ "EXPORT" [ "QUALIFIED" ]
-  175          GetIdent<expNames[expCount]>    (. INC(expCount); .)
-  176          { "," GetIdent<expNames[expCount]>
-  177                                          (. INC(expCount); .) }
-  178          ";" ]
-  179        DeclSeq
-  180        [ "BEGIN"                         (. QbeGen.BeginInit(m1); .)
-  181          [ StatSeq ]                     (. QbeGen.EndInit; .) ]
-  182        "END"
-  183        GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
-  184                                               SemError(202) END;
-  185                                             k := 0;
-  186                                             WHILE k < expCount DO
-  187                                               SymTab.ExportUp(expNames[k]);
-  188                                               INC(k)
-  189                                             END;
-  190                                             SymTab.PopScope; .) .
-  191    Priority
-  192      = "[" integer "]"                   (. SemError(230); .) .
-  193    (* Imports (4.3): FROM materializes the names (unqualified use);
-  194       plain IMPORT only demands the module exists — qualified `L.x`
-  195       materializes on first use (Design). *)
-  196    (* Unknown modules stay unchecked stubs (legacy, so hand-written
-  197       import lines don't fail); a known module's missing export is
-  198       201. *)
-  199    Import                                (. VAR n: SymTab.Name; .)
-  200      = "FROM"
-  201        GetIdent<n>
-  202        "IMPORT"
-  203        ImpList<n> ";"
-  204      | "IMPORT"
-  205        ImpModList ";" .
-  206    ImpList<mod: SymTab.Name>             (. VAR n: SymTab.Name; .)
-  207      = ImpName<mod>
-  208        { "," ImpName<mod> } .
-  209    (* Pervasive built-ins imported from SYSTEM (e.g. TSIZE) are
-  210       accepted and ignored: the built-in applies regardless. *)
-  211    ImpName<mod: SymTab.Name>             (. VAR n: SymTab.Name; .)
-  212      = GetIdent<n>                       (. IF SymTab.ModKnown(mod)
-  213                                               AND NOT SymTab.ImportFrom(mod, n) THEN
-  214                                               SemError(201) END; .)
-  215      | ( "TSIZE" | "SIZE" | "ADR" | "HIGH" | "LEN"
-  216        | "CHR" | "ORD" | "ORDL" | "VAL" | "ABS" | "CAP"
-  217        | "UCHR" | "CHR8" | "UORD"
-  218        | "INC" | "DEC" ) .
-  219    ImpModList                            (. VAR n: SymTab.Name; .)
-  220      = GetIdent<n>
-  221        { "," GetIdent<n> } .
-  222    (* Opaque TYPE declarations (definition modules). The targetless
-  223       alias resolves to InvalidType until step 4 completes it. *)
-  224    (* Scalar-phase TYPEs: named types, integer subranges, enumerations.
-  225       Opaque "TYPE T;" needs isDef (definition units); elsewhere 231.
-  226       Composite forms (ARRAY/RECORD/SET/POINTER) arrive with step 3. *)
-  227    TypeBlock<isDef: BOOLEAN>
-  228      = "TYPE"                            (. SymTab.BeginTypeBlock; .)
-  229        { TypeItem<isDef> ";" | ClassItem ";" }
-  230                                          (. SymTab.EndTypeBlock; .) .
-  231    TypeItem<isDef: BOOLEAN>              (. VAR n: SymTab.Name;
-  232                                               t, op: SymTab.TypeIndex; .)
-  233      = GetIdent<n>                       (. op := SymTab.OpaqueBase(n);
-  234                                             IF op = SymTab.InvalidType THEN
-  235                                               IF NOT SymTab.Enter(n,
-  236                                                  SymTab.KindType) THEN
-  237                                                 SemError(200) END
-  238                                             END; .)
-  239        ( "=" Type<t, FALSE>              (. IF op # SymTab.InvalidType THEN
-  240                                               SymTab.SetTarget(op, t)
-  241                                             ELSE SymTab.SetSymType(n, t)
-  242                                             END; .)
-  243        |                                 (. IF op # SymTab.InvalidType THEN
-  244                                               (* stays opaque *)
-  245                                             ELSIF NOT isDef THEN
-  246                                               SemError(231)
-  247                                             ELSE SymTab.SetSymType(n,
-  248                                               SymTab.NewAlias()) END; .) ) .
-  249    Type<VAR t: SymTab.TypeIndex; allowOpen: BOOLEAN>
-  250      = TypeIdent<t> [ Subrange<t> ]     (* anchored subrange: T[lo..hi] *)
-  251      | Subrange<t>
-  252      | Enum<t>
-  253      | ArrayType<t, allowOpen>
-  254      | SetType<t>
-  255      | RecordType<t>
-  256      | PointerType<t>
-  257      | ProcType<t> .
-  258    PointerType<VAR t: SymTab.TypeIndex>  (. VAR base: SymTab.TypeIndex; .)
-  259      = "POINTER" "TO" Type<base, FALSE>(. t := SymTab.NewPtr(base); .) .
-  260    (* Procedure types (step 8.5): PROCEDURE (params): result. Values
-  261       are code pointers; params are collected into the descriptor. *)
-  262    ProcType<VAR t: SymTab.TypeIndex>     (. VAR res, pt: SymTab.TypeIndex;
-  263                                               isV: BOOLEAN; .)
-  264      = "PROCEDURE"                       (. res := SymTab.InvalidType;
-  265                                             t := SymTab.NewProcType(res); .)
-  266        [ "(" [ ProcTypeSection<t> { ";" ProcTypeSection<t> } ] ")" ]
-  267        [ ":" TypeIdent<res>              (. SymTab.SetProcTypeRes(t, res); .) ] .
-  268    ProcTypeSection<t: SymTab.TypeIndex>  (. VAR pt: SymTab.TypeIndex;
-  269                                               isV: BOOLEAN;
-  270                                               cnt, k: CARDINAL;
-  271                                               names: ARRAY [0 .. 15] OF SymTab.Name; .)
-  272      =                                   (. isV := FALSE; cnt := 0; .)
-  273        [ "VAR"                           (. isV := TRUE; .) ]
-  274        ( GetIdent<names[cnt]>            (. INC(cnt); .)
-  275          { "," GetIdent<names[cnt]>      (. INC(cnt); .) }
-  276          ( ":" Type<pt, TRUE>           (. k := 0;
-  277                                             WHILE k < cnt DO
-  278                                               SymTab.ProcTypeAdd(t, isV, pt);
-  279                                               INC(k)
-  280                                             END; .)
-  281          |                               (. (* type-only parameter list:
-  282                                                each name is a type (GNU
-  283                                                shorthand used by the
-  284                                                Coco/R scanner frame) *)
-  285                                             k := 0;
-  286                                             WHILE k < cnt DO
-  287                                               IF SymTab.Lookup(names[k])
-  288                                                  AND ((SymTab.SymKind(names[k]) = SymTab.KindType)
-  289    OR (SymTab.SymKind(names[k]) = SymTab.KindPredef)) THEN
-  290                                                 pt := SymTab.SymType(names[k])
-  291                                               ELSE SemError(201);
-  292                                                 pt := SymTab.InvalidType
-  293                                               END;
-  294                                               SymTab.ProcTypeAdd(t, isV, pt);
-  295                                               INC(k)
-  296                                             END; .) )
-  297        | Type<pt, TRUE>                 (. (* unnamed parameter (PIM):
-  298                                                e.g. PROCEDURE (VAR ARRAY OF REAL) *)
-  299                                             SymTab.ProcTypeAdd(t, isV, pt); .) ) .
-  300    (* Arrays: "OF" without bounds is an open formal (allowed only
-  301       where allowOpen); "[lo..hi, ...]" nests bounded levels inside
-  302       out. Bounds are folded literals (int/char); anything else 230.
-  303       Bare-type indices ("ARRAY Color OF") wait for enum ordinals. *)
-  304    ArrayType<VAR t: SymTab.TypeIndex; allowOpen: BOOLEAN>
-  305                                          (. VAR elem: SymTab.TypeIndex;
-  306                                               ok: BOOLEAN;
-  307                                               bnds, bndh: ARRAY [0 .. 7] OF INTEGER;
-  308                                               nb, k: CARDINAL;
-  309                                               idx: SymTab.TypeIndex;
-  310                                               ilo, ihi: INTEGER; .)
-  311      = "ARRAY"
-  312        ( "OF" Type<elem, FALSE>          (. IF NOT allowOpen THEN
-  313                                               SemError(230) END;
-  314                                             t := SymTab.NewOpenArray(elem); .)
-  315        | TypeIdent<idx> [ Subrange<idx> ]
-  316          "OF" Type<elem, FALSE>          (. (* index-type array: the index
-  317                                                type's ordinal bounds give the
-  318                                                [lo..hi] pair *)
-  319                                             IF SymTab.TypeBounds(idx, ilo,
-  320                                                ihi)
-  321                                             THEN t := SymTab.NewArrayB(elem,
-  322                                                    ilo, ihi)
-  323                                             ELSE SemError(230);
-  324                                               t := SymTab.InvalidType
-  325                                             END; .)
-  326        | "["                             (. SymTab.BoundBegin; ok := TRUE; .)
-  327          BoundPair<ok>
-  328          { "," BoundPair<ok> }
-  329          "]"                             (. (* snapshot before the element
-  330                                                type, which reuses the bound
-  331                                                buffer for a nested ARRAY *)
-  332                                             nb := SymTab.BoundCount();
-  333                                             k := 0;
-  334                                             WHILE k < nb DO
-  335                                               bnds[k] := SymTab.BoundLo(k);
-  336                                               bndh[k] := SymTab.BoundHi(k);
-  337                                               INC(k)
-  338                                             END; .)
-  339          "OF" Type<elem, FALSE>
-  340                                          (. IF ok THEN
-  341                                               k := nb;
-  342                                               WHILE k > 0 DO
-  343                                                 DEC(k);
-  344                                                 elem := SymTab.NewArrayB(
-  345                                                   elem, bnds[k], bndh[k])
-  346                                               END;
-  347                                               t := elem
-  348                                             ELSE t := SymTab.InvalidType
-  349                                             END; .) ) .
-  350    BoundPair<VAR ok: BOOLEAN>            (. VAR tlo, thi: SymTab.TypeIndex;
-  351                                               qlo, qhi: QbeGen.QVal;
-  352                                               lo, hi: INTEGER;
-  353                                               cl, cl2: INTEGER; .)
-  354      = Expr<tlo, qlo> ".." Expr<thi, qhi>
-  355                                          (. IF (tlo = SymTab.InvalidType)
-  356                                              OR (thi = SymTab.InvalidType) THEN
-  357                                             ok := FALSE
-  358                                           ELSE cl := SymTab.ClassOf(tlo);
-  359                                             cl2 := SymTab.ClassOf(thi);
-  360                                             IF ((cl # SymTab.ClInt)
-  361   AND (cl # SymTab.ClChar))
-  362                                                OR ((cl2 # SymTab.ClInt)
-  363   AND (cl2 # SymTab.ClChar)) THEN
-  364                                               SemError(230); ok := FALSE
-  365                                             ELSIF NOT SymTab.ConstInt(qlo, lo)
-  366                                                OR NOT SymTab.ConstInt(qhi, hi)
-  367                                                OR (lo > hi) THEN
-  368                                               SemError(230); ok := FALSE
-  369                                              ELSIF NOT SymTab.BoundAdd(lo, hi) THEN
-  370                                                SemError(230); ok := FALSE
-  371                                              END;
-  372                                            END; .) .
-  373    (* Sets: multi-word masks over bases ≤ 256 values (bool, char,
-  374       bounded subranges; enums wait for ordinals, INTEGER is
-  375       unbounded). Literals are SET OF [0..255]; assignment and
-  376       comparison across suitable bases are lenient (masks over min
-  377       words + zero-check extras), out-of-span literals are 222. *)
-  378    SetType<VAR t: SymTab.TypeIndex>      (. VAR base: SymTab.TypeIndex;
-  379                                               blo, bhi, bspan: INTEGER;
-  380                                               bcls: INTEGER; .)
-  381      = "SET" "OF" Type<base, FALSE>
-  382                                          (. IF base = SymTab.InvalidType THEN
-  383                                               t := SymTab.InvalidType
-  384                                             ELSE bcls :=
-  385                                                    SymTab.ClassOf(base);
-  386                                               IF bcls = SymTab.ClBool THEN
-  387                                                 blo := 0; bspan := 2
-  388                                               ELSIF bcls = SymTab.ClChar THEN
-  389                                                 blo := 0; bspan := 256
-  390                                               ELSIF SymTab.SubBounds(base,
-  391                                                       blo, bhi) THEN
-  392                                                 bspan := bhi - blo + 1
-  393                                               ELSE bspan := 0 END;
-  394                                               IF (bspan <= 0)
-  395                                                  OR (bspan > 256) THEN
-  396                                                 SemError(230);
-  397                                                 t := SymTab.InvalidType
-  398                                               ELSE t := SymTab.NewSet(base)
-  399                                               END
-  400                                             END; .) .
-  401    (* Records: flat blobs; array fields are pointers to static
-  402       descriptors (locked amendment), nested records inline. Field
-  403       offsets static and declaration-ordered. *)
-  404    (* Field list: plain fields and (optionally) one variant part.  The
-  405       variant part is a `CASE ... END` item; because it starts with the
-  406       CASE keyword it is unambiguously distinguishable from a field
-  407       (which starts with an identifier). *)
-  408    RecordType<VAR t: SymTab.TypeIndex>   (. VAR t2: SymTab.TypeIndex;
-  409                                               tagOk: BOOLEAN; .)
-  410      = "RECORD"                          (. t := SymTab.NewRecord(); .)
-  411        [ RecItem<t> { ";" [ RecItem<t> ] } ]
-  412        "END" .
-  413    RecItem<rec: SymTab.TypeIndex>        (. VAR tt: SymTab.TypeIndex; .)
-  414      = RecField<rec>
-  415      | CaseField<rec>                    (. SymTab.MarkVariant(rec); .) .
-  416    (* A variant part: CASE tag : Type OF variants.  The layout overlays
-  417       every branch from the tag slot (see SymTab.ComputeOffsets). *)
-  418    CaseField<rec: SymTab.TypeIndex>      (. VAR tagT: SymTab.TypeIndex;
-  419                                               tagN: SymTab.Name; .)
-  420      = "CASE"                            (. SymTab.ResetFields(rec); .)
-  421        GetIdent<tagN>                    (. IF NOT SymTab.FieldPending(rec,
-  422                                               tagN) THEN
-  423                                               SemError(200) END; .)
-  424        ":" Type<tagT, FALSE>             (. IF (tagT # SymTab.InvalidType)
-  425    AND NOT SymTab.IsOrdinal(tagT) THEN
-  426                                               SemError(224)
-  427                                             END;
-  428                                             SymTab.FixPendingF(rec, tagT);
-  429                                             SymTab.SetVariantTag(rec, tagN); .)
-  430        "OF"
-  431        RecFieldList<rec>
-  432        { "|"                             (. SymTab.ResetFields(rec); .)
-  433          RecFieldList<rec> }
-  434        "END" .
-  435    RecFieldList<rec: SymTab.TypeIndex>   (. VAR lt, lq: SymTab.TypeIndex;
-  436                                               lv1, lv2: QbeGen.QVal; .)
-  437      = VarLabel<lt, lv1> [ ".." VarLabel<lq, lv2> ] ":"
-  438        RecField<rec> { ";" [ RecField<rec> ] } .
-  439    (* Variant case label: a constant (or a constant range).  Values are
-  440       not interpreted (the layout overlays regardless). *)
-  441    VarLabel<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
-  442                                          (. VAR lname: SymTab.Name; .)
-  443      = ( ident                         (. LexName(lname);
-  444                                             QbeGen.CopyOp("0", q);
-  445                                             t := SymTab.IntType(); .)
-  446        | integer                       (. LexString(lname);
-  447                                             QbeGen.CopyOp("0", q);
-  448                                             t := SymTab.IntType(); .)
-  449        | charConst                     (. LexString(lname);
-  450                                             QbeGen.CopyOp("0", q);
-  451                                             t := SymTab.CharType(); .) ) .
-  452    RecField<rec: SymTab.TypeIndex>       (. VAR n: SymTab.Name;
-  453                                               t2: SymTab.TypeIndex; .)
-  454      = RecIdents<rec> ":" Type<t2, FALSE>(. SymTab.FixPendingF(rec, t2); .) .
-  455    RecIdents<rec: SymTab.TypeIndex>      (. VAR n: SymTab.Name; .)
-  456      = GetIdent<n>                       (. IF NOT SymTab.FieldPending(rec,
-  457                                               n) THEN
+   92  IGNORE CHR(9) .. CHR(13)
+   93  
+   94  COMMENTS FROM "(*" TO "*)" NESTED
+   95  COMMENTS FROM "//" TO lf
+   96  
+   97  TOKENS
+   98    ident   = letter { letter | digit } .
+   99    integer = digit { digit }
+  100            | digit { digit } CONTEXT("..")
+  101            | "0x" hexDigit { hexDigit }
+  102            | "0X" hexDigit { hexDigit } .
+  103    real    = digit { digit } "." { digit }
+  104              [ ( "E" | "e" ) [ "+" | "-" ] digit { digit } ] .
+  105    string  = "'" { noQuote1 } "'"
+  106            | '"' { noQuote2 } '"' .
+  107    charConst = digit { digit } ( "C" | "c" ) .
+  108    ustring = ( "U" | "u" ) ( "'" { noQuote1 } "'" | '"' { noQuote2 } '"' ) .
+  109  
+  110  PRODUCTIONS
+  111    M2
+  112      = Unit "." .
+  113    (* Units: program modules compile fully; DEFINITION and
+  114       IMPLEMENTATION modules parse + check now but lower in step 4
+  115       (each ends with one 230); same for nested local modules. *)
+  116    Unit
+  117      = DefUnit
+  118      | ImplUnit
+  119      | ProgModule .
+  120    (* Step 4.3: one session compiles DEFINITION, its IMPLEMENTATION
+  121       and one program (last) into one image. Units share the symbol
+  122       table; imports materialize exported names. *)
+  123    DefUnit                               (. VAR m1, m2, pn: SymTab.Name; .)
+  124      = "DEFINITION" "MODULE"
+  125        GetIdent<m1>                      (. IF NOT SymTab.BeginDef(m1) THEN
+  126                                               SemError(200) END;
+  127                                             QbeGen.SetModule(m1); .)
+  128        ";"
+  129        { Import }
+  130        [ "EXPORT" [ "QUALIFIED" ]        (. (* definition-module export
+  131                                                list: parsed, and the
+  132                                                names are already exported
+  133                                                by the module scope *) .)
+  134          GetIdent<pn> { "," GetIdent<pn> } ";" ]
+  135        { ConstBlock | TypeBlock<TRUE> | VarBlock
+  136        | ProcHeading<pn, SymTab.InvalidType> ";"
+  137                                          (. SymTab.CloseProc;
+  138                                               QbeGen.AbortFunc; .) }
+  139        "END"
+  140        GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
+  141                                               SemError(202) END;
+  142                                             SymTab.EndUnit; .) .
+  143    ImplUnit                              (. VAR m1, m2: SymTab.Name;
+  144                                               k: CARDINAL; .)
+  145      = "IMPLEMENTATION" "MODULE"
+  146        GetIdent<m1>                      (. IF NOT SymTab.BeginImpl(m1) THEN
+  147                                               SemError(201) END;
+  148                                             nPendVar := 0;
+  149                                             QbeGen.SetModule(m1); .)
+  150        ";"
+  151        { Import }
+  152        DeclSeq
+  153        [ "BEGIN"                         (. FlushPend;
+  154                                             QbeGen.BeginInit(m1); .)
+  155          [ StatSeq ]                       (. QbeGen.EndInit; .) ]
+  156        "END"
+  157        GetIdent<m2>                      (. FlushPend;
+  158                                             IF NOT SymTab.Equal(m1, m2) THEN
+  159                                               SemError(202) END;
+  160                                             SymTab.EndUnit; .) .
+  161    ProgModule                            (. VAR m1, m2: SymTab.Name;
+  162                                               k: CARDINAL; .)
+  163      = "MODULE"
+  164        GetIdent<m1>                      (. IF NOT SymTab.BeginProg(m1) THEN
+  165                                               SemError(200) END;
+  166                                             nPendVar := 0;
+  167                                             QbeGen.SetModule(m1); .)
+  168        [ Priority ]
+  169        ";"
+  170        { Import }
+  171        DeclSeq
+  172        [ "BEGIN"                         (. FlushPend;
+  173                                             QbeGen.BeginBody; .)
+  174          [ StatSeq ] ]
+  175        "END"
+  176        GetIdent<m2>                      (. FlushPend;
+  177                                             IF NOT SymTab.Equal(m1, m2) THEN
+  178                                               SemError(202) END;
+  179                                             QbeGen.EndModule(m1);
+  180                                             SymTab.EndUnit; .) .
+  181    DeclSeq
+  182      = { ConstBlock | TypeBlock<FALSE> | VarBlock | ProcDecl ";"
+  183        | NestedModule ";" | ClassItem ";" } .
+  184    (* Local module, Wirth form. Declarations lower like top-level ones
+  185       (same QBE module prefix); a BEGIN body becomes an init function
+  186       that main calls; the EXPORT list is hoisted into the enclosing
+  187       scope at END. *)
+  188    NestedModule                          (. VAR m1, m2: SymTab.Name;
+  189                                               expNames: ARRAY [0 .. 63] OF SymTab.Name;
+  190                                               expCount, k: CARDINAL; .)
+  191      = "MODULE"
+  192        GetIdent<m1>                      (. IF NOT SymTab.Enter(m1,
+  193                                               SymTab.KindModule) THEN
+  194                                               SemError(200) END;
+  195                                             SymTab.PushScope;
+  196                                             expCount := 0; .)
+  197        [ Priority ]
+  198        ";"
+  199        { Import }
+  200        [ "EXPORT" [ "QUALIFIED" ]
+  201          GetIdent<expNames[expCount]>    (. INC(expCount); .)
+  202          { "," GetIdent<expNames[expCount]>
+  203                                          (. INC(expCount); .) }
+  204          ";" ]
+  205        DeclSeq
+  206        [ "BEGIN"                         (. QbeGen.BeginInit(m1); .)
+  207          [ StatSeq ]                     (. QbeGen.EndInit; .) ]
+  208        "END"
+  209        GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
+  210                                               SemError(202) END;
+  211                                             k := 0;
+  212                                             WHILE k < expCount DO
+  213                                               SymTab.ExportUp(expNames[k]);
+  214                                               INC(k)
+  215                                             END;
+  216                                             SymTab.PopScope; .) .
+  217    Priority
+  218      = "[" integer "]"                   (. SemError(230); .) .
+  219    (* Imports (4.3): FROM materializes the names (unqualified use);
+  220       plain IMPORT only demands the module exists — qualified `L.x`
+  221       materializes on first use (Design). *)
+  222    (* Unknown modules stay unchecked stubs (legacy, so hand-written
+  223       import lines don't fail); a known module's missing export is
+  224       201. *)
+  225    Import                                (. VAR n: SymTab.Name; .)
+  226      = "FROM"
+  227        GetIdent<n>
+  228        "IMPORT"
+  229        ImpList<n> ";"
+  230      | "IMPORT"
+  231        ImpModList ";" .
+  232    ImpList<mod: SymTab.Name>             (. VAR n: SymTab.Name; .)
+  233      = ImpName<mod>
+  234        { "," ImpName<mod> } .
+  235    (* Pervasive built-ins imported from SYSTEM (e.g. TSIZE) are
+  236       accepted and ignored: the built-in applies regardless. *)
+  237    ImpName<mod: SymTab.Name>             (. VAR n: SymTab.Name; .)
+  238      = GetIdent<n>                       (. IF SymTab.Equal(mod, "libc") THEN
+  239                                               (* intrinsic C library:
+  240                                                  permissive external *)
+  241                                               IF NOT SymTab.DeclareCProc(n) THEN
+  242                                                 SemError(201) END
+  243                                             ELSIF SymTab.ModKnown(mod)
+  244                                               AND NOT SymTab.ImportFrom(mod, n) THEN
+  245                                               SemError(201) END; .)
+  246      | ( "TSIZE" | "SIZE" | "ADR" | "HIGH" | "LEN"
+  247        | "CHR" | "ORD" | "ORDL" | "VAL" | "ABS" | "CAP"
+  248        | "UCHR" | "CHR8" | "UORD"
+  249        | "INC" | "DEC" ) .
+  250    ImpModList                            (. VAR n: SymTab.Name; .)
+  251      = GetIdent<n>
+  252        { "," GetIdent<n> } .
+  253    (* Opaque TYPE declarations (definition modules). The targetless
+  254       alias resolves to InvalidType until step 4 completes it. *)
+  255    (* Scalar-phase TYPEs: named types, integer subranges, enumerations.
+  256       Opaque "TYPE T;" needs isDef (definition units); elsewhere 231.
+  257       Composite forms (ARRAY/RECORD/SET/POINTER) arrive with step 3. *)
+  258    TypeBlock<isDef: BOOLEAN>
+  259      = "TYPE"                            (. SymTab.BeginTypeBlock; .)
+  260        { TypeItem<isDef> ";" | ClassItem ";" }
+  261                                          (. SymTab.EndTypeBlock; .) .
+  262    TypeItem<isDef: BOOLEAN>              (. VAR n: SymTab.Name;
+  263                                               t, op: SymTab.TypeIndex; .)
+  264      = GetIdent<n>                       (. op := SymTab.OpaqueBase(n);
+  265                                             IF op = SymTab.InvalidType THEN
+  266                                               IF NOT SymTab.Enter(n,
+  267                                                  SymTab.KindType) THEN
+  268                                                 SemError(200) END
+  269                                             END; .)
+  270        ( "=" Type<t, FALSE>              (. IF op # SymTab.InvalidType THEN
+  271                                               SymTab.SetTarget(op, t)
+  272                                             ELSE SymTab.SetSymType(n, t)
+  273                                             END; .)
+  274        |                                 (. IF op # SymTab.InvalidType THEN
+  275                                               (* stays opaque *)
+  276                                             ELSIF NOT isDef THEN
+  277                                               SemError(231)
+  278                                             ELSE SymTab.SetSymType(n,
+  279                                               SymTab.NewAlias()) END; .) ) .
+  280    Type<VAR t: SymTab.TypeIndex; allowOpen: BOOLEAN>
+  281      = TypeIdent<t> [ Subrange<t> ]     (* anchored subrange: T[lo..hi] *)
+  282      | Subrange<t>
+  283      | Enum<t>
+  284      | ArrayType<t, allowOpen>
+  285      | SetType<t>
+  286      | RecordType<t>
+  287      | PointerType<t>
+  288      | ProcType<t> .
+  289    PointerType<VAR t: SymTab.TypeIndex>  (. VAR base: SymTab.TypeIndex; .)
+  290      = "POINTER" "TO" Type<base, FALSE>(. t := SymTab.NewPtr(base); .) .
+  291    (* Procedure types (step 8.5): PROCEDURE (params): result. Values
+  292       are code pointers; params are collected into the descriptor. *)
+  293    ProcType<VAR t: SymTab.TypeIndex>     (. VAR res, pt: SymTab.TypeIndex;
+  294                                               isV: BOOLEAN; .)
+  295      = "PROCEDURE"                       (. res := SymTab.InvalidType;
+  296                                             t := SymTab.NewProcType(res); .)
+  297        [ "(" [ ProcTypeSection<t> { ";" ProcTypeSection<t> } ] ")" ]
+  298        [ ":" TypeIdent<res>              (. SymTab.SetProcTypeRes(t, res); .) ] .
+  299    ProcTypeSection<t: SymTab.TypeIndex>  (. VAR pt: SymTab.TypeIndex;
+  300                                               isV: BOOLEAN;
+  301                                               cnt, k: CARDINAL;
+  302                                               names: ARRAY [0 .. 15] OF SymTab.Name; .)
+  303      =                                   (. isV := FALSE; cnt := 0; .)
+  304        [ "VAR"                           (. isV := TRUE; .) ]
+  305        ( GetIdent<names[cnt]>            (. INC(cnt); .)
+  306          { "," GetIdent<names[cnt]>      (. INC(cnt); .) }
+  307          ( ":" Type<pt, TRUE>           (. k := 0;
+  308                                             WHILE k < cnt DO
+  309                                               SymTab.ProcTypeAdd(t, isV, pt);
+  310                                               INC(k)
+  311                                             END; .)
+  312          |                               (. (* type-only parameter list:
+  313                                                each name is a type (GNU
+  314                                                shorthand used by the
+  315                                                Coco/R scanner frame) *)
+  316                                             k := 0;
+  317                                             WHILE k < cnt DO
+  318                                               IF SymTab.Lookup(names[k])
+  319                                                  AND ((SymTab.SymKind(names[k]) = SymTab.KindType)
+  320    OR (SymTab.SymKind(names[k]) = SymTab.KindPredef)) THEN
+  321                                                 pt := SymTab.SymType(names[k])
+  322                                               ELSE SemError(201);
+  323                                                 pt := SymTab.InvalidType
+  324                                               END;
+  325                                               SymTab.ProcTypeAdd(t, isV, pt);
+  326                                               INC(k)
+  327                                             END; .) )
+  328        | Type<pt, TRUE>                 (. (* unnamed parameter (PIM):
+  329                                                e.g. PROCEDURE (VAR ARRAY OF REAL) *)
+  330                                             SymTab.ProcTypeAdd(t, isV, pt); .) ) .
+  331    (* Arrays: "OF" without bounds is an open formal (allowed only
+  332       where allowOpen); "[lo..hi, ...]" nests bounded levels inside
+  333       out. Bounds are folded literals (int/char); anything else 230.
+  334       Bare-type indices ("ARRAY Color OF") wait for enum ordinals. *)
+  335    ArrayType<VAR t: SymTab.TypeIndex; allowOpen: BOOLEAN>
+  336                                          (. VAR elem: SymTab.TypeIndex;
+  337                                               ok: BOOLEAN;
+  338                                               bnds, bndh: ARRAY [0 .. 7] OF INTEGER;
+  339                                               nb, k: CARDINAL;
+  340                                               idx: SymTab.TypeIndex;
+  341                                               ilo, ihi: INTEGER; .)
+  342      = "ARRAY"
+  343        ( "OF" Type<elem, FALSE>          (. IF NOT allowOpen THEN
+  344                                               SemError(230) END;
+  345                                             t := SymTab.NewOpenArray(elem); .)
+  346        | TypeIdent<idx> [ Subrange<idx> ]
+  347          "OF" Type<elem, FALSE>          (. (* index-type array: the index
+  348                                                type's ordinal bounds give the
+  349                                                [lo..hi] pair *)
+  350                                             IF SymTab.TypeBounds(idx, ilo,
+  351                                                ihi)
+  352                                             THEN t := SymTab.NewArrayB(elem,
+  353                                                    ilo, ihi)
+  354                                             ELSE SemError(230);
+  355                                               t := SymTab.InvalidType
+  356                                             END; .)
+  357        | "["                             (. SymTab.BoundBegin; ok := TRUE; .)
+  358          BoundPair<ok>
+  359          { "," BoundPair<ok> }
+  360          "]"                             (. (* snapshot before the element
+  361                                                type, which reuses the bound
+  362                                                buffer for a nested ARRAY *)
+  363                                             nb := SymTab.BoundCount();
+  364                                             k := 0;
+  365                                             WHILE k < nb DO
+  366                                               bnds[k] := SymTab.BoundLo(k);
+  367                                               bndh[k] := SymTab.BoundHi(k);
+  368                                               INC(k)
+  369                                             END; .)
+  370          "OF" Type<elem, FALSE>
+  371                                          (. IF ok THEN
+  372                                               k := nb;
+  373                                               WHILE k > 0 DO
+  374                                                 DEC(k);
+  375                                                 elem := SymTab.NewArrayB(
+  376                                                   elem, bnds[k], bndh[k])
+  377                                               END;
+  378                                               t := elem
+  379                                             ELSE t := SymTab.InvalidType
+  380                                             END; .) ) .
+  381    BoundPair<VAR ok: BOOLEAN>            (. VAR tlo, thi: SymTab.TypeIndex;
+  382                                               qlo, qhi: QbeGen.QVal;
+  383                                               lo, hi: INTEGER;
+  384                                               cl, cl2: INTEGER; .)
+  385      = Expr<tlo, qlo> ".." Expr<thi, qhi>
+  386                                          (. IF (tlo = SymTab.InvalidType)
+  387                                              OR (thi = SymTab.InvalidType) THEN
+  388                                             ok := FALSE
+  389                                           ELSE cl := SymTab.ClassOf(tlo);
+  390                                             cl2 := SymTab.ClassOf(thi);
+  391                                             IF ((cl # SymTab.ClInt)
+  392   AND (cl # SymTab.ClChar))
+  393                                                OR ((cl2 # SymTab.ClInt)
+  394   AND (cl2 # SymTab.ClChar)) THEN
+  395                                               SemError(230); ok := FALSE
+  396                                             ELSIF NOT SymTab.ConstInt(qlo, lo)
+  397                                                OR NOT SymTab.ConstInt(qhi, hi)
+  398                                                OR (lo > hi) THEN
+  399                                               SemError(230); ok := FALSE
+  400                                              ELSIF NOT SymTab.BoundAdd(lo, hi) THEN
+  401                                                SemError(230); ok := FALSE
+  402                                              END;
+  403                                            END; .) .
+  404    (* Sets: multi-word masks over bases ≤ 256 values (bool, char,
+  405       bounded subranges; enums wait for ordinals, INTEGER is
+  406       unbounded). Literals are SET OF [0..255]; assignment and
+  407       comparison across suitable bases are lenient (masks over min
+  408       words + zero-check extras), out-of-span literals are 222. *)
+  409    SetType<VAR t: SymTab.TypeIndex>      (. VAR base: SymTab.TypeIndex;
+  410                                               blo, bhi, bspan: INTEGER;
+  411                                               bcls: INTEGER; .)
+  412      = "SET" "OF" Type<base, FALSE>
+  413                                          (. IF base = SymTab.InvalidType THEN
+  414                                               t := SymTab.InvalidType
+  415                                             ELSE bcls :=
+  416                                                    SymTab.ClassOf(base);
+  417                                               IF bcls = SymTab.ClBool THEN
+  418                                                 blo := 0; bspan := 2
+  419                                               ELSIF bcls = SymTab.ClChar THEN
+  420                                                 blo := 0; bspan := 256
+  421                                               ELSIF bcls = SymTab.ClEnum THEN
+  422                                                 blo := 0;
+  423                                                 bspan := VAL(INTEGER,
+  424                                                   SymTab.EnumCount(base))
+  425                                               ELSIF SymTab.SubBounds(base,
+  426                                                       blo, bhi) THEN
+  427                                                 bspan := bhi - blo + 1
+  428                                               ELSE bspan := 0 END;
+  429                                               IF (bspan <= 0)
+  430                                                  OR (bspan > 65536) THEN
+  431                                                 SemError(230);
+  432                                                 t := SymTab.InvalidType
+  433                                               ELSE t := SymTab.NewSet(base)
+  434                                               END
+  435                                             END; .) .
+  436    (* Records: flat blobs; array fields are pointers to static
+  437       descriptors (locked amendment), nested records inline. Field
+  438       offsets static and declaration-ordered. *)
+  439    (* Field list: plain fields and (optionally) one variant part.  The
+  440       variant part is a `CASE ... END` item; because it starts with the
+  441       CASE keyword it is unambiguously distinguishable from a field
+  442       (which starts with an identifier). *)
+  443    RecordType<VAR t: SymTab.TypeIndex>   (. VAR t2: SymTab.TypeIndex;
+  444                                               tagOk: BOOLEAN; .)
+  445      = "RECORD"                          (. t := SymTab.NewRecord(); .)
+  446        [ RecItem<t> { ";" [ RecItem<t> ] } ]
+  447        "END" .
+  448    RecItem<rec: SymTab.TypeIndex>        (. VAR tt: SymTab.TypeIndex; .)
+  449      = RecField<rec>
+  450      | CaseField<rec>                    (. SymTab.MarkVariant(rec); .) .
+  451    (* A variant part: CASE tag : Type OF variants.  The layout overlays
+  452       every branch from the tag slot (see SymTab.ComputeOffsets). *)
+  453    CaseField<rec: SymTab.TypeIndex>      (. VAR tagT: SymTab.TypeIndex;
+  454                                               tagN: SymTab.Name; .)
+  455      = "CASE"                            (. SymTab.ResetFields(rec); .)
+  456        GetIdent<tagN>                    (. IF NOT SymTab.FieldPending(rec,
+  457                                               tagN) THEN
   458                                               SemError(200) END; .)
   458                                               SemError(200) END; .)
-  459        { "," GetIdent<n>                 (. IF NOT SymTab.FieldPending(rec,
-  460                                               n) THEN
-  461                                               SemError(200) END; .) } .
-  462    TypeIdent<VAR t: SymTab.TypeIndex>    (. VAR n, qid: SymTab.Name;
-  463                                               k: INTEGER;
-  464                                               dotted: BOOLEAN; .)
-  465      = GetIdent<n>                       (. dotted := FALSE;
-  466                                             IF NOT SymTab.Lookup(n) THEN
-  467                                               k := -1;
-  468                                               t := SymTab.ForwardType(n);
-  469                                               IF t = SymTab.InvalidType THEN
-  470                                                 SemError(201)
-  471                                               END
-  472                                             ELSE k := SymTab.SymKind(n);
-  473                                               IF (k = SymTab.KindType)
-  474                                                  OR (k = SymTab.KindPredef) THEN
-  475                                                 t := SymTab.SymType(n)
-  476                                               ELSE t := SymTab.InvalidType
-  477                                               END
-  478                                             END; .)
-  479        [ "." GetIdent<qid>               (. dotted := TRUE;
-  480                                             IF k = SymTab.KindModule THEN
-  481                                               t := SymTab.QualType(n, qid);
-  482                                               IF t = SymTab.InvalidType THEN
-  483                                                 SemError(201)
-  484                                               END
-  485                                             ELSE SemError(221);
-  486                                               t := SymTab.InvalidType
-  487                                             END; .) ]
-  488                                          (. IF NOT dotted THEN
-  489                                               IF (k = SymTab.KindModule)
-  490                                                  OR ((k # SymTab.KindType)
-  491    AND (k # SymTab.KindPredef)
-  492    AND (k # SymTab.KindImport)
-  493    AND (k # -1)) THEN
-  494                                                 SemError(221)
-  495                                               END
-  496                                             END; .) .
-  497    Subrange<VAR t: SymTab.TypeIndex>     (. VAR tlo, thi: SymTab.TypeIndex;
-  498                                               qlo, qhi: QbeGen.QVal;
-  499                                               lo, hi: INTEGER; .)
-  500      = "[" Expr<tlo, qlo> ".." Expr<thi, qhi>
-  501                                          (. IF (tlo = SymTab.InvalidType)
-  502                                              OR (thi = SymTab.InvalidType) THEN
-  503                                             t := SymTab.InvalidType
-  504                                           ELSIF (SymTab.ClassOf(tlo) #
-  505                                                  SymTab.ClInt)
-  506                                              OR (SymTab.ClassOf(thi) #
-  507                                                  SymTab.ClInt) THEN
-  508                                             SemError(230);
-  509                                             t := SymTab.InvalidType
-  510                                           ELSIF NOT SymTab.ConstInt(qlo, lo)
-  511                                              OR NOT SymTab.ConstInt(qhi, hi)
-  512                                              OR (lo > hi) THEN
-  513                                             SemError(230);
-  514                                             t := SymTab.InvalidType
-  515                                           ELSE t := SymTab.NewSubR(lo, hi)
-  516                                           END; .)
-  517        "]" .
-  518    Enum<VAR t: SymTab.TypeIndex>         (. VAR n: SymTab.Name;
-  519                                               ord: INTEGER;
-  520                                               qv: QbeGen.QVal; .)
-  521      = "("                               (. t := SymTab.NewEnum();
-  522                                             ord := 0; .)
-  523        GetIdent<n>                       (. IF NOT SymTab.Enter(n,
-  524                                               SymTab.KindConst) THEN
-  525                                               SemError(200) END;
-  526                                             SymTab.SetSymType(n, t);
-  527                                             QbeGen.IntStr(ord, qv);
-  528                                             SymTab.SetSymVal(n, qv);
-  529                                             INC(ord); .)
-  530        { "," GetIdent<n>                 (. IF NOT SymTab.Enter(n,
-  531                                               SymTab.KindConst) THEN
-  532                                               SemError(200) END;
-  533                                             SymTab.SetSymType(n, t);
-  534                                             QbeGen.IntStr(ord, qv);
-  535                                             SymTab.SetSymVal(n, qv);
-  536                                             INC(ord); .) }
-  537        ")"                               (. SymTab.SetEnumCount(t,
-  538                                               VAL(CARDINAL, ord)); .) .
-  539    (* Clarion-form classes (docs/OOP.txt): declaration + single
-  540       inheritance + IMPLEMENTATION blocks. Scopes and member checks
-  541       now; lowering (vtable, dispatch, THIS) later — one 230 per
-  542       class/impl block. Methods end with ";" per the Table example
-  543       (not "," as in the sketch). No underscores in identifiers. *)
-  544    (* Single CLASS item in both loops: separating declaration from
-  545       IMPLEMENTATION at the loop level needs 2-token lookahead
-  546       (CLASS ident vs CLASS IMPLEMENTATION), which LL(1) cannot do.
-  547       The second token decides after CLASS is consumed. A misplaced
-  548       CLASS IMPLEMENTATION inside TYPE still parses (harmless: the
-  549       whole unit ends 230 until lowering). *)
-  550    ClassItem
-  551      = "CLASS" ( "IMPLEMENTATION" ClassImplRest | ClassRest ) .
-  552    ClassRest                             (. VAR cn, m2, pn: SymTab.Name;
-  553                                               ct: SymTab.TypeIndex; .)
-  554      = GetIdent<cn>                      (. IF NOT SymTab.Enter(cn,
-  555                                               SymTab.KindType) THEN
-  556                                               SemError(200) END;
-  557                                             ct := SymTab.NewClass();
-  558                                             SymTab.SetSymType(cn, ct);
-  559                                             SymTab.PushClassScope(ct); .)
-  560        [ Parents<ct> ]
-  561        ";"
-  562        { ClassField<ct> ";" }
-  563        { MethodHeading<pn, SymTab.InvalidType> ";"
-  564                                          (. SymTab.CloseProc;
-  565                                             QbeGen.AbortFunc; .) }
-  566        "END"
-  567        GetIdent<m2>                      (. IF NOT SymTab.Equal(cn, m2) THEN
-  568                                               SemError(202) END;
-  569                                             SymTab.LayoutClass(ct);
-  570                                             SymTab.PopScope; .) .
-  571    Parents<ct: SymTab.TypeIndex>         (. VAR p: SymTab.Name; .)
-  572      = "(" Parent1<ct>
-  573        { "," GetIdent<p>                 (. SemError(230); .) }
-  574        ")" .
-  575    Parent1<ct: SymTab.TypeIndex>         (. VAR p: SymTab.Name;
-  576                                               pt: SymTab.TypeIndex; .)
-  577      = GetIdent<p>                       (. IF NOT SymTab.Lookup(p) THEN
-  578                                               SemError(201)
-  579                                             ELSE pt := SymTab.SymType(p);
-  580                                               IF SymTab.ClassOf(pt) #
-  581                                                  SymTab.ClClass THEN
-  582                                                 SemError(230)
-  583                                               ELSE SymTab.SetParent(ct, pt)
-  584                                               END
-  585                                             END; .) .
-  586    ClassField<ct: SymTab.TypeIndex>      (. VAR n, rhs: SymTab.Name;
-  587                                               t: SymTab.TypeIndex; .)
-  588      = GetIdent<n>
-  589        ( "=" GetIdent<rhs>               (. IF NOT SymTab.Enter(n,
-  590                                               SymTab.KindConst) THEN
-  591                                               SemError(200) END;
-  592                                             IF SymTab.Lookup(rhs) THEN
-  593                                               SymTab.SetSymType(n,
-  594                                                 SymTab.SymType(rhs))
-  595                                             END; .)
-  596        |                                 (. IF NOT SymTab.FieldPending(ct,
-  597                                               n) THEN
-  598                                               SemError(200) END; .)
-  599          { "," GetIdent<n>               (. IF NOT SymTab.FieldPending(ct,
-  600                                               n) THEN
-  601                                               SemError(200) END; .) }
-  602          ":" Type<t, FALSE>              (. SymTab.FixPendingF(ct, t); .) ) .
-  603    MethodHeading<VAR pn: SymTab.Name; ct: SymTab.TypeIndex>
-  604                                          (. VAR wantVirt: BOOLEAN; .)
-  605      =                                   (. wantVirt := FALSE; .)
-  606        [ "VIRTUAL"                       (. wantVirt := TRUE; .) ]
-  607        ProcHeading<pn, ct>               (. IF wantVirt THEN
-  608                                               SymTab.MarkVirtual END; .) .
-  609    ClassImplRest                         (. VAR cn, m2: SymTab.Name;
-  610                                               ct: SymTab.TypeIndex; .)
-  611      = GetIdent<cn>                      (. IF NOT SymTab.Lookup(cn) THEN
-  612                                               SemError(201);
-  613                                               ct := SymTab.InvalidType
-  614                                             ELSE ct := SymTab.SymType(cn);
-  615                                               IF SymTab.ClassOf(ct) #
-  616                                                  SymTab.ClClass THEN
-  617                                                 SemError(230);
-  618                                                 ct := SymTab.InvalidType
-  619                                               END
-  620                                             END;
-  621                                             IF ct #
-  622                                                SymTab.InvalidType THEN
-  623                                               IF NOT SymTab.PushClassMembers(
-  624                                                      ct) THEN
-  625                                                 SemError(230) END;
-  626                                               SymTab.PushImplClass(ct)
-  627                                             END; .)
-  628        ";" { MethodImpl<ct> ";" }
-  629        [ "BEGIN"                         (. QbeGen.BeginInit(cn); .)
-  630          [ StatSeq ]                     (. QbeGen.EndInit; .) ]
-  631        "END"
-  632        GetIdent<m2>                      (. IF NOT SymTab.Equal(cn, m2) THEN
-  633                                               SemError(202) END;
-  634                                             SymTab.PopImplClass;
-  635                                             SymTab.PopScope; .) .
-  636    MethodImpl<ct: SymTab.TypeIndex>      (. VAR pn: SymTab.Name;
-  637                                               thisQ: QbeGen.QVal;
-  638                                               methRes: SymTab.TypeIndex; .)
-  639      = MethodHeading<pn, ct> ";"
-  640                                          (. IF (ct #
-  641                                                SymTab.InvalidType)
-  642   AND NOT SymTab.MethodExists(ct,
-  643                                                    pn) THEN
-  644                                               SemError(201) END; .)
-  645        ( "FORWARD"                       (. SymTab.MarkFwd;
-  646                                             QbeGen.AbortFunc;
-  647                                             SymTab.CloseProc; .)
-  648        |                                 (. QbeGen.EndFuncHeader;
-  649                                             (* bind the receiver: bare
-  650                                                field names resolve
-  651                                                against THIS *)
-  652                                             QbeGen.ThisBase(thisQ);
-  653                                             QbeGen.PushWith(thisQ); .)
-  654          Block<pn>                       (. QbeGen.PopWith;
-  655                                             methRes := SymTab.CurRes();
-  656                                             SymTab.CloseProc;
-  657                                             QbeGen.EndFunc(methRes); .) ) .
-  658    ConstBlock
-  659      = "CONST" { ConstDecl ";" } .
-  660    ConstDecl                             (. VAR n: SymTab.Name;
-  661                                               t: SymTab.TypeIndex;
-  662                                               qv: QbeGen.QVal;
-  663                                               cls: INTEGER; .)
-  664      = GetIdent<n>                       (. IF NOT SymTab.Enter(n,
-  665                                               SymTab.KindConst) THEN
-  666                                               SemError(200) END; .)
-  667        "="
-  668        Expr<t, qv>                       (. SymTab.SetSymType(n, t);
-  669                                             cls := SymTab.ClassOf(t);
-  670                                             IF (cls = SymTab.ClArray)
-  671                                                OR (cls = SymTab.ClRecord)
-  672                                                OR (cls = SymTab.ClClass)
-  673                                                OR (cls = SymTab.ClStr)
-  674                                                OR (cls = SymTab.ClUStr) THEN
-  675                                               (* an aggregate/string
-  676                                                  constant: qv is its
-  677                                                  descriptor address; no
-  678                                                  scalar data *)
-  679                                               SymTab.SetSymVal(n, qv)
-  680                                             ELSIF NOT QbeGen.IsImm(qv) THEN
-  681                                               SemError(230)
-  682                                             ELSE
-  683                                               SymTab.SetSymVal(n, qv);
-  684                                               QbeGen.DeclConst(n, qv, t)
-  685                                             END; .) .
-  686    VarBlock
-  687      = "VAR" { VarDecl ";" } .
-  688    VarDecl                               (. VAR nm: SymTab.Name;
-  689                                               t: SymTab.TypeIndex;
-  690                                               i: CARDINAL;
-  691                                               cls: INTEGER; .)
-  692      = VarIdents ":"
-  693        Type<t, FALSE>                    (. cls := SymTab.ClassOf(t);
-  694                                             IF (t # SymTab.InvalidType)
-  695   AND (cls # SymTab.ClInt)
-  696   AND (cls # SymTab.ClBool)
-  697   AND (cls # SymTab.ClChar)
-  698   AND (cls # SymTab.ClReal)
-  699   AND (cls # SymTab.ClArray)
-  700   AND (cls # SymTab.ClSet)
-  701   AND (cls # SymTab.ClRecord)
-  702   AND (cls # SymTab.ClPtr)
-  703   AND (cls # SymTab.ClLong)
-  704   AND (cls # SymTab.ClProc)
-  705   AND (cls # SymTab.ClUChar)
-  706   AND (cls # SymTab.ClUStr)
-  707   AND (cls # SymTab.ClEnum)
-  708   AND (cls # SymTab.ClClass) THEN
-  709                                               SemError(230) END;
-  710                                             IF QbeGen.LocFull() THEN
-  711                                               SemError(233) END;
-  712                                             i := 0;
-  713                                             WHILE i < SymTab.PendCount() DO
-  714                                               SymTab.PendName(i, nm);
-  715                                               QbeGen.DeclVar(nm, t);
-  716                                               INC(i)
-  717                                             END;
-  718                                             (* a plain VAR list, not a
-  719                                                heading: the signature
-  720                                                result is discarded *)
-  721                                             IF NOT SymTab.FixPending(t) THEN
-  722                                             END; .) .
-  723    VarIdents                             (. VAR n: SymTab.Name; .)
-  724      = GetIdent<n>                       (. IF NOT SymTab.EnterPending(n,
-  725                                               SymTab.KindVar) THEN
-  726                                               SemError(200) END; .)
-  727        { ","
-  728          GetIdent<n>                     (. IF NOT SymTab.EnterPending(n,
-  729                                               SymTab.KindVar) THEN
-  730                                               SemError(200) END; .) } .
-  731    ParIdents<isV: BOOLEAN>               (. VAR n: SymTab.Name; .)
-  732      = GetIdent<n>                       (. IF NOT SymTab.EnterParam(n, isV) THEN
-  733                                               SemError(200) END; .)
-  734        { "," GetIdent<n>                 (. IF NOT SymTab.EnterParam(n, isV) THEN
-  735                                               SemError(200) END; .) } .
-  736    (* Procedure headings enter scopes/params/result and buffer the
-  737       QBE header; bodies lower to functions (4.1, module level only).
-  738       FORWARD marks; the body heading re-enters (signature compare
-  739       deferred). Nested procedures parse + check, lowering = 4.2. *)
-  740    ProcHeading<VAR pn: SymTab.Name; methCls: SymTab.TypeIndex>
-  741                                          (. VAR t: SymTab.TypeIndex;
-  742                                               mg: QbeGen.QVal; .)
-  743      = "PROCEDURE"
-  744        GetIdent<pn>                      (. IF methCls #
-  745                                                SymTab.InvalidType THEN
-  746                                             (* a method: resume the
-  747                                                declared symbol (reuse
-  748                                                its uid) *)
-  749                                             IF NOT SymTab.ResumeMethod(
-  750                                                  methCls, pn) THEN
-  751                                               SemError(200) END
-  752                                             ELSIF NOT SymTab.EnterProc(pn) THEN
-  753                                             IF NOT SymTab.ReenterProc(pn) THEN
-  754                                               IF NOT SymTab.ResumeProc(pn) THEN
-  755                                                 SemError(200) END
-  756                                               END
-  757                                             END;
-  758                                             IF methCls #
-  759                                                SymTab.InvalidType THEN
-  760                                               QbeGen.Mangled(pn,
-  761                                                 SymTab.MethUid(), mg)
+  459        ":" Type<tagT, FALSE>             (. IF (tagT # SymTab.InvalidType)
+  460    AND NOT SymTab.IsOrdinal(tagT) THEN
+  461                                               SemError(224)
+  462                                             END;
+  463                                             SymTab.FixPendingF(rec, tagT);
+  464                                             SymTab.SetVariantTag(rec, tagN); .)
+  465        "OF"
+  466        RecFieldList<rec>
+  467        { "|"                             (. SymTab.ResetFields(rec); .)
+  468          RecFieldList<rec> }
+  469        "END" .
+  470    RecFieldList<rec: SymTab.TypeIndex>   (. VAR lt, lq: SymTab.TypeIndex;
+  471                                               lv1, lv2: QbeGen.QVal; .)
+  472      = VarLabel<lt, lv1> [ ".." VarLabel<lq, lv2> ] ":"
+  473        RecField<rec> { ";" [ RecField<rec> ] } .
+  474    (* Variant case label: a constant (or a constant range).  Values are
+  475       not interpreted (the layout overlays regardless). *)
+  476    VarLabel<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+  477                                          (. VAR lname: SymTab.Name; .)
+  478      = ( ident                         (. LexName(lname);
+  479                                             QbeGen.CopyOp("0", q);
+  480                                             t := SymTab.IntType(); .)
+  481        | integer                       (. LexString(lname);
+  482                                             QbeGen.CopyOp("0", q);
+  483                                             t := SymTab.IntType(); .)
+  484        | charConst                     (. LexString(lname);
+  485                                             QbeGen.CopyOp("0", q);
+  486                                             t := SymTab.CharType(); .) ) .
+  487    RecField<rec: SymTab.TypeIndex>       (. VAR n: SymTab.Name;
+  488                                               t2: SymTab.TypeIndex; .)
+  489      = RecIdents<rec> ":" Type<t2, FALSE>(. SymTab.FixPendingF(rec, t2); .) .
+  490    RecIdents<rec: SymTab.TypeIndex>      (. VAR n: SymTab.Name; .)
+  491      = GetIdent<n>                       (. IF NOT SymTab.FieldPending(rec,
+  492                                               n) THEN
+  493                                               SemError(200) END; .)
+  494        { "," GetIdent<n>                 (. IF NOT SymTab.FieldPending(rec,
+  495                                               n) THEN
+  496                                               SemError(200) END; .) } .
+  497    TypeIdent<VAR t: SymTab.TypeIndex>    (. VAR n, qid: SymTab.Name;
+  498                                               k: INTEGER;
+  499                                               dotted: BOOLEAN; .)
+  500      = GetIdent<n>                       (. dotted := FALSE;
+  501                                             IF NOT SymTab.Lookup(n) THEN
+  502                                               k := -1;
+  503                                               t := SymTab.ForwardType(n);
+  504                                               IF t = SymTab.InvalidType THEN
+  505                                                 SemError(201)
+  506                                               END
+  507                                             ELSE k := SymTab.SymKind(n);
+  508                                               IF (k = SymTab.KindType)
+  509                                                  OR (k = SymTab.KindPredef) THEN
+  510                                                 t := SymTab.SymType(n)
+  511                                               ELSE t := SymTab.InvalidType
+  512                                               END
+  513                                             END; .)
+  514        [ "." GetIdent<qid>               (. dotted := TRUE;
+  515                                             IF k = SymTab.KindModule THEN
+  516                                               t := SymTab.QualType(n, qid);
+  517                                               IF t = SymTab.InvalidType THEN
+  518                                                 SemError(201)
+  519                                               END
+  520                                             ELSE SemError(221);
+  521                                               t := SymTab.InvalidType
+  522                                             END; .) ]
+  523                                          (. IF NOT dotted THEN
+  524                                               IF (k = SymTab.KindModule)
+  525                                                  OR ((k # SymTab.KindType)
+  526    AND (k # SymTab.KindPredef)
+  527    AND (k # SymTab.KindImport)
+  528    AND (k # -1)) THEN
+  529                                                 SemError(221)
+  530                                               END
+  531                                             END; .) .
+  532    Subrange<VAR t: SymTab.TypeIndex>     (. VAR tlo, thi: SymTab.TypeIndex;
+  533                                               qlo, qhi: QbeGen.QVal;
+  534                                               lo, hi: INTEGER; .)
+  535      = "[" Expr<tlo, qlo> ".." Expr<thi, qhi>
+  536                                          (. IF (tlo = SymTab.InvalidType)
+  537                                              OR (thi = SymTab.InvalidType) THEN
+  538                                             t := SymTab.InvalidType
+  539                                           ELSIF NOT SymTab.IsOrdinal(tlo)
+  540                                              OR NOT SymTab.IsOrdinal(thi) THEN
+  541                                             SemError(230);
+  542                                             t := SymTab.InvalidType
+  543                                           ELSIF NOT SymTab.ConstInt(qlo, lo)
+  544                                              OR NOT SymTab.ConstInt(qhi, hi)
+  545                                              OR (lo > hi) THEN
+  546                                             SemError(230);
+  547                                             t := SymTab.InvalidType
+  548                                           ELSE t := SymTab.NewSubR(lo, hi)
+  549                                           END; .)
+  550        "]" .
+  551    Enum<VAR t: SymTab.TypeIndex>         (. VAR n: SymTab.Name;
+  552                                               ord: INTEGER;
+  553                                               qv: QbeGen.QVal; .)
+  554      = "("                               (. t := SymTab.NewEnum();
+  555                                             ord := 0; .)
+  556        GetIdent<n>                       (. IF NOT SymTab.Enter(n,
+  557                                               SymTab.KindConst) THEN
+  558                                               SemError(200) END;
+  559                                             SymTab.SetSymType(n, t);
+  560                                             QbeGen.IntStr(ord, qv);
+  561                                             SymTab.SetSymVal(n, qv);
+  562                                             INC(ord); .)
+  563        { "," GetIdent<n>                 (. IF NOT SymTab.Enter(n,
+  564                                               SymTab.KindConst) THEN
+  565                                               SemError(200) END;
+  566                                             SymTab.SetSymType(n, t);
+  567                                             QbeGen.IntStr(ord, qv);
+  568                                             SymTab.SetSymVal(n, qv);
+  569                                             INC(ord); .) }
+  570        ")"                               (. SymTab.SetEnumCount(t,
+  571                                               VAL(CARDINAL, ord)); .) .
+  572    (* Clarion-form classes (docs/OOP.txt): declaration + single
+  573       inheritance + IMPLEMENTATION blocks. Scopes and member checks
+  574       now; lowering (vtable, dispatch, THIS) later — one 230 per
+  575       class/impl block. Methods end with ";" per the Table example
+  576       (not "," as in the sketch). No underscores in identifiers. *)
+  577    (* Single CLASS item in both loops: separating declaration from
+  578       IMPLEMENTATION at the loop level needs 2-token lookahead
+  579       (CLASS ident vs CLASS IMPLEMENTATION), which LL(1) cannot do.
+  580       The second token decides after CLASS is consumed. A misplaced
+  581       CLASS IMPLEMENTATION inside TYPE still parses (harmless: the
+  582       whole unit ends 230 until lowering). *)
+  583    ClassItem
+  584      = "CLASS" ( "IMPLEMENTATION" ClassImplRest | ClassRest ) .
+  585    ClassRest                             (. VAR cn, m2, pn: SymTab.Name;
+  586                                               ct: SymTab.TypeIndex; .)
+  587      = GetIdent<cn>                      (. IF NOT SymTab.Enter(cn,
+  588                                               SymTab.KindType) THEN
+  589                                               SemError(200) END;
+  590                                             ct := SymTab.NewClass();
+  591                                             SymTab.SetSymType(cn, ct);
+  592                                             SymTab.PushClassScope(ct); .)
+  593        [ Parents<ct> ]
+  594        ";"
+  595        { ClassField<ct> ";" }
+  596        { MethodHeading<pn, SymTab.InvalidType> ";"
+  597                                          (. SymTab.CloseProc;
+  598                                             QbeGen.AbortFunc; .) }
+  599        "END"
+  600        GetIdent<m2>                      (. IF NOT SymTab.Equal(cn, m2) THEN
+  601                                               SemError(202) END;
+  602                                             SymTab.LayoutClass(ct);
+  603                                             SymTab.PopScope; .) .
+  604    Parents<ct: SymTab.TypeIndex>         (. VAR p: SymTab.Name; .)
+  605      = "(" Parent1<ct>
+  606        { "," GetIdent<p>                 (. SemError(230); .) }
+  607        ")" .
+  608    Parent1<ct: SymTab.TypeIndex>         (. VAR p: SymTab.Name;
+  609                                               pt: SymTab.TypeIndex; .)
+  610      = GetIdent<p>                       (. IF NOT SymTab.Lookup(p) THEN
+  611                                               SemError(201)
+  612                                             ELSE pt := SymTab.SymType(p);
+  613                                               IF SymTab.ClassOf(pt) #
+  614                                                  SymTab.ClClass THEN
+  615                                                 SemError(230)
+  616                                               ELSE SymTab.SetParent(ct, pt)
+  617                                               END
+  618                                             END; .) .
+  619    ClassField<ct: SymTab.TypeIndex>      (. VAR n, rhs: SymTab.Name;
+  620                                               t: SymTab.TypeIndex; .)
+  621      = GetIdent<n>
+  622        ( "=" GetIdent<rhs>               (. IF NOT SymTab.Enter(n,
+  623                                               SymTab.KindConst) THEN
+  624                                               SemError(200) END;
+  625                                             IF SymTab.Lookup(rhs) THEN
+  626                                               SymTab.SetSymType(n,
+  627                                                 SymTab.SymType(rhs))
+  628                                             END; .)
+  629        |                                 (. IF NOT SymTab.FieldPending(ct,
+  630                                               n) THEN
+  631                                               SemError(200) END; .)
+  632          { "," GetIdent<n>               (. IF NOT SymTab.FieldPending(ct,
+  633                                               n) THEN
+  634                                               SemError(200) END; .) }
+  635          ":" Type<t, FALSE>              (. SymTab.FixPendingF(ct, t); .) ) .
+  636    MethodHeading<VAR pn: SymTab.Name; ct: SymTab.TypeIndex>
+  637                                          (. VAR wantVirt: BOOLEAN; .)
+  638      =                                   (. wantVirt := FALSE; .)
+  639        [ "VIRTUAL"                       (. wantVirt := TRUE; .) ]
+  640        ProcHeading<pn, ct>               (. IF wantVirt THEN
+  641                                               SymTab.MarkVirtual END; .) .
+  642    ClassImplRest                         (. VAR cn, m2: SymTab.Name;
+  643                                               ct: SymTab.TypeIndex; .)
+  644      = GetIdent<cn>                      (. IF NOT SymTab.Lookup(cn) THEN
+  645                                               SemError(201);
+  646                                               ct := SymTab.InvalidType
+  647                                             ELSE ct := SymTab.SymType(cn);
+  648                                               IF SymTab.ClassOf(ct) #
+  649                                                  SymTab.ClClass THEN
+  650                                                 SemError(230);
+  651                                                 ct := SymTab.InvalidType
+  652                                               END
+  653                                             END;
+  654                                             IF ct #
+  655                                                SymTab.InvalidType THEN
+  656                                               IF NOT SymTab.PushClassMembers(
+  657                                                      ct) THEN
+  658                                                 SemError(230) END;
+  659                                               SymTab.PushImplClass(ct)
+  660                                             END; .)
+  661        ";" { MethodImpl<ct> ";" }
+  662        [ "BEGIN"                         (. QbeGen.BeginInit(cn); .)
+  663          [ StatSeq ]                     (. QbeGen.EndInit; .) ]
+  664        "END"
+  665        GetIdent<m2>                      (. IF NOT SymTab.Equal(cn, m2) THEN
+  666                                               SemError(202) END;
+  667                                             SymTab.PopImplClass;
+  668                                             SymTab.PopScope; .) .
+  669    MethodImpl<ct: SymTab.TypeIndex>      (. VAR pn: SymTab.Name;
+  670                                               thisQ: QbeGen.QVal;
+  671                                               methRes: SymTab.TypeIndex; .)
+  672      = MethodHeading<pn, ct> ";"
+  673                                          (. IF (ct #
+  674                                                SymTab.InvalidType)
+  675   AND NOT SymTab.MethodExists(ct,
+  676                                                    pn) THEN
+  677                                               SemError(201) END; .)
+  678        ( "FORWARD"                       (. SymTab.MarkFwd;
+  679                                             QbeGen.AbortFunc;
+  680                                             SymTab.CloseProc; .)
+  681        |                                 (. QbeGen.EndFuncHeader;
+  682                                             (* bind the receiver: bare
+  683                                                field names resolve
+  684                                                against THIS *)
+  685                                             QbeGen.ThisBase(thisQ);
+  686                                             QbeGen.PushWith(thisQ); .)
+  687          Block<pn>                       (. QbeGen.PopWith;
+  688                                             methRes := SymTab.CurRes();
+  689                                             SymTab.CloseProc;
+  690                                             QbeGen.EndFunc(methRes); .) ) .
+  691    ConstBlock
+  692      = "CONST" { ConstDecl ";" } .
+  693    ConstDecl                             (. VAR n: SymTab.Name;
+  694                                               t: SymTab.TypeIndex;
+  695                                               qv: QbeGen.QVal;
+  696                                               cls: INTEGER; .)
+  697      = GetIdent<n>                       (. IF NOT SymTab.Enter(n,
+  698                                               SymTab.KindConst) THEN
+  699                                               SemError(200) END; .)
+  700        "="
+  701        Expr<t, qv>                       (. SymTab.SetSymType(n, t);
+  702                                             cls := SymTab.ClassOf(t);
+  703                                             IF (cls = SymTab.ClArray)
+  704                                                OR (cls = SymTab.ClRecord)
+  705                                                OR (cls = SymTab.ClClass)
+  706                                                OR (cls = SymTab.ClStr)
+  707                                                OR (cls = SymTab.ClUStr) THEN
+  708                                               (* an aggregate/string
+  709                                                  constant: qv is its
+  710                                                  descriptor address; no
+  711                                                  scalar data *)
+  712                                               SymTab.SetSymVal(n, qv)
+  713                                             ELSIF NOT QbeGen.IsImm(qv) THEN
+  714                                               SemError(230)
+  715                                             ELSE
+  716                                               SymTab.SetSymVal(n, qv);
+  717                                               QbeGen.DeclConst(n, qv, t)
+  718                                             END; .) .
+  719    VarBlock
+  720      = "VAR" { VarDecl ";" } .
+  721    VarDecl                               (. VAR nm: SymTab.Name;
+  722                                               t: SymTab.TypeIndex;
+  723                                               i: CARDINAL;
+  724                                               cls: INTEGER; .)
+  725      = VarIdents ":"
+  726        Type<t, FALSE>                    (. cls := SymTab.ClassOf(t);
+  727                                             IF (t # SymTab.InvalidType)
+  728   AND NOT SymTab.IsUnresolved(t)
+  729   AND (cls # SymTab.ClInt)
+  730   AND (cls # SymTab.ClBool)
+  731   AND (cls # SymTab.ClChar)
+  732   AND (cls # SymTab.ClReal)
+  733   AND (cls # SymTab.ClArray)
+  734   AND (cls # SymTab.ClSet)
+  735   AND (cls # SymTab.ClRecord)
+  736   AND (cls # SymTab.ClPtr)
+  737   AND (cls # SymTab.ClLong)
+  738   AND (cls # SymTab.ClProc)
+  739   AND (cls # SymTab.ClUChar)
+  740   AND (cls # SymTab.ClUStr)
+  741   AND (cls # SymTab.ClEnum)
+  742   AND (cls # SymTab.ClClass) THEN
+  743                                               SemError(230) END;
+  744                                             IF QbeGen.LocFull() THEN
+  745                                               SemError(233) END;
+  746                                             i := 0;
+  747                                             IF SymTab.IsUnresolved(t)
+  748                                                AND NOT SymTab.InProc() THEN
+  749                                               (* a forward-typed global:
+  750                                                  defer emission until the
+  751                                                  TYPE block completes *)
+  752                                               WHILE i < SymTab.PendCount() DO
+  753                                                 SymTab.PendName(i, nm);
+  754                                                 IF nPendVar <=
+  755                                                    HIGH(pendVarName) THEN
+  756                                                   pendVarName[nPendVar] := nm;
+  757                                                   pendVarT[nPendVar] := t;
+  758                                                   INC(nPendVar)
+  759                                                 END;
+  760                                                 INC(i)
+  761                                               END
   762                                             ELSE
   762                                             ELSE
-  763                                               QbeGen.Mangled(pn,
-  764                                                 SymTab.ProcUid(pn), mg)
-  765                                             END;
-  766                                             QbeGen.BeginFunc(mg);
-  767                                             IF methCls #
-  768                                                SymTab.InvalidType THEN
-  769                                               (* hidden THIS receiver:
-  770                                                  a VAR param of the
-  771                                                  class type, pushed as
-  772                                                  the WITH base *)
-  773                                               IF NOT SymTab.EnterThisParam(
-  774                                                    methCls) THEN
-  775                                                 SemError(200) END;
-  776                                               IF NOT QbeGen.FuncParam(
-  777                                                    "THIS", TRUE,
-  778                                                    methCls) THEN
-  779                                                 SemError(233) END
-  780                                             END; .)
-  781        [ FormalParams ]
-  782        [ ":" TypeIdent<t>                (. IF NOT SymTab.SetProcRes(t) THEN
-  783                                               SemError(235) END;
-  784                                             QbeGen.SetFuncRes(t);
-  785                                             IF (t #
-  786                                                SymTab.InvalidType)
-  787   AND ((SymTab.ClassOf(t)
-  788                                                   = SymTab.ClArray)
-  789                                                OR (SymTab.ClassOf(t)
-  790                                                   = SymTab.ClRecord)
-  791                                                OR (SymTab.ClassOf(t)
-  792                                                   = SymTab.ClSet)
-  793                                                OR (SymTab.ClassOf(t)
-  794                                                   = SymTab.ClClass)) THEN
-  795                                               SemError(230) END; .) ] .
-  796    FormalParams
-  797      = "(" [ ParamSection { ";" ParamSection } ] ")" .
-  798    ParamSection                          (. VAR t: SymTab.TypeIndex;
-  799                                               nm: SymTab.Name;
-  800                                               i: CARDINAL;
-  801                                               isV: BOOLEAN; .)
-  802      =                                   (. isV := FALSE; .)
-  803        [ "VAR"                           (. isV := TRUE; .) ]
-  804        ParIdents<isV> ":" Type<t, TRUE>  (. i := 0;
-  805                                             WHILE i < SymTab.PendCount() DO
-  806                                               SymTab.PendName(i, nm);
-  807                                               (* value open arrays are
-  808                                                  passed as descriptor
-  809                                                  addresses (no copy):
-  810                                                  same representation as
-  811                                                  VAR formals *)
-  812                                               IF NOT QbeGen.FuncParam(nm,
-  813                                                    isV
-  814                                                    OR SymTab.IsOpenArray(t),
-  815                                                    t) THEN
-  816                                                 SemError(233) END;
-  817                                               INC(i)
-  818                                             END;
-  819                                             IF NOT SymTab.FixPending(t) THEN
-  820                                               SemError(235) END; .) .
-  821    (* Nested procedures lower like top-level ones (4.2): the
-  822       static link gives them their parent's frame. Methods keep
-  823       parse-now/230-later. *)
-  824    ProcDecl                              (. VAR pn: SymTab.Name; .)
-  825      = ProcHeading<pn, SymTab.InvalidType> ";"
-  826        ( "FORWARD"                       (. SymTab.MarkFwd;
-  827                                             SymTab.CloseProc;
-  828                                             QbeGen.AbortFunc; .)
-  829        | "EXTERNAL"                      (. SymTab.MarkExternal("");
-  830                                             SymTab.CloseProc;
-  831                                             QbeGen.AbortFunc; .)
-  832        |                                 (. QbeGen.EndFuncHeader; .)
-  833          Block<pn>                       (. SymTab.CloseProc;
-  834                                             QbeGen.EndFunc(
-  835                                               SymTab.ProcRes(pn)); .) ) .
-  836    Block<pn: SymTab.Name>                (. VAR m2: SymTab.Name; .)
-  837      = DeclSeq
-  838        [ "BEGIN"
-  839          [ StatSeq ] ]
-  840        "END"
-  841        GetIdent<m2>                      (. IF NOT SymTab.Equal(pn, m2) THEN
-  842                                               SemError(202) END; .) .
-  843    StatSeq
-  844      = Statement { ";" [ Statement ] } .
-  845    (* Trailing/empty statements (`a; ; b`, Coco/R's generated `;;`)
-  846       are accepted: the statement after ';' is optional. *)
-  847    Statement                             (. VAR lx: QbeGen.QVal; .)
-  848      = AssOrCall
-  849      | IfStat
-  850      | WhileStat
-  851      | RepeatStat
-  852      | LoopStat
-  853      | ForStat
-  854      | CaseStat
-  855      | WithStat
-  856      | ReturnStat
-  857      | HaltStat
-  858      | NewStat
-  859      | DisposeStat
-  860      | IncDecStat
-  861      | "EXIT"                            (. IF QbeGen.TopLoop(lx) THEN
-  862                                               QbeGen.Jmp(lx)
-  863                                             ELSE SemError(230) END; .) .
-  864    (* INC(v [,step]) / DEC(v [,step]) as builtin statements over an
-  865       integer designator. *)
-  866    IncDecStat                            (. VAR dt, et2: SymTab.TypeIndex;
-  867                                               dk: INTEGER;
-  868                                               qd, qv, qn2, qstep:
-  869                                                 QbeGen.QVal;
-  870                                               qn: SymTab.Name;
-  871                                               sfx, isInc: BOOLEAN; .)
-  872      =                                   (. isInc := TRUE; .)
-  873        ( "INC"                           (. isInc := TRUE; .)
-  874        | "DEC"                           (. isInc := FALSE; .) )
-  875        "("                               (. QbeGen.CopyOp("1", qstep); .)
-  876        Design<dt, dk, qd, qn, sfx>
-  877        [ "," Expr<et2, qstep> ]
-  878        ")"                               (. IF dt = SymTab.InvalidType THEN
-  879                                             ELSIF (dk # SymTab.KindVar)
-  880   AND (dk # SymTab.KindParam)
-  881   AND (dk # SymTab.KindField) THEN
-  882                                               SemError(210)
-  883                                             ELSIF NOT SymTab.IsIntFamily(dt) THEN
-  884                                               SemError(211)
-  885                                             ELSE
-  886                                               IF sfx
-  887                                                  OR (dk = SymTab.KindField) THEN
-  888                                                 QbeGen.ElemLoad(qd, dt, qv)
-  889                                               ELSE QbeGen.LoadVar(qn,
-  890                                                 FALSE, qv)
-  891                                               END;
-  892                                               QbeGen.NewTemp(qn2);
-  893                                               IF isInc THEN
-  894                                                 QbeGen.Op3("add", qn2, qv,
-  895                                                   qstep, FALSE)
-  896                                               ELSE QbeGen.Op3("sub", qn2, qv,
-  897                                                   qstep, FALSE)
-  898                                               END;
-  899                                               IF sfx
-  900                                                  OR (dk = SymTab.KindField) THEN
-  901                                                 QbeGen.ElemStore(qd, qn2,
-  902                                                   dt)
-  903                                               ELSE QbeGen.StoreVar(qn,
-  904                                                 qn2, FALSE)
-  905                                               END
-  906                                             END; .) .
-  907    (* NEW/DISPOSE as builtin statements (no call syntax until step 4).
-  908       Targets are pointer designators; DISPOSE nils afterwards (safer
-  909       than Wirth-undefined; documented). DISPOSE is shallow. *)
-  910    NewStat                               (. VAR dt: SymTab.TypeIndex;
-  911                                               dk: INTEGER;
-  912                                               qd, qm: QbeGen.QVal;
-  913                                               qn: SymTab.Name;
-  914                                               sfx: BOOLEAN;
-  915                                               bt: SymTab.TypeIndex; .)
-  916      = "NEW" "(" Design<dt, dk, qd, qn, sfx> ")"
-  917                                          (. IF dt = SymTab.InvalidType THEN
-  918                                             ELSIF (dk # SymTab.KindVar)
-  919   AND (dk # SymTab.KindParam)
-  920   AND (dk # SymTab.KindField) THEN
-  921                                               SemError(210)
-  922                                             ELSIF SymTab.ClassOf(dt) #
-  923                                                   SymTab.ClPtr THEN
-  924                                               SemError(219)
-  925                                             ELSE bt := SymTab.PtrBase(dt);
-  926                                               IF bt #
-  927                                                  SymTab.InvalidType THEN
-  928                                                 QbeGen.NewHeap(bt, qm);
-  929                                                 QbeGen.InitHeap(qm, bt);
-  930                                                 IF sfx
-  931                                                    OR (dk =
-  932                                                       SymTab.KindField) THEN
-  933                                                   QbeGen.ElemStore(qd, qm,
-  934                                                     dt)
-  935                                                 ELSE QbeGen.StorePtr(qn,
-  936                                                   qm)
-  937                                                 END
-  938                                               END
-  939                                             END; .) .
-  940    DisposeStat                           (. VAR dt: SymTab.TypeIndex;
-  941                                               dk: INTEGER;
-  942                                               qd, qv: QbeGen.QVal;
-  943                                               qn: SymTab.Name;
-  944                                               sfx: BOOLEAN; .)
-  945      = "DISPOSE" "(" Design<dt, dk, qd, qn, sfx> ")"
-  946                                          (. IF dt = SymTab.InvalidType THEN
-  947                                             ELSIF (dk # SymTab.KindVar)
-  948   AND (dk # SymTab.KindParam)
-  949   AND (dk # SymTab.KindField) THEN
-  950                                               SemError(210)
-  951                                             ELSIF SymTab.ClassOf(dt) #
-  952                                                   SymTab.ClPtr THEN
-  953                                               SemError(219)
-  954                                             ELSE
-  955                                               IF sfx
-  956                                                  OR (dk =
-  957                                                     SymTab.KindField) THEN
-  958                                                 QbeGen.ElemLoad(qd, dt,
-  959                                                   qv)
-  960                                               ELSE QbeGen.LoadPtr(qn, qv)
-  961                                               END;
-  962                                               QbeGen.FreeHeap(qv);
+  763                                               WHILE i < SymTab.PendCount() DO
+  764                                                 SymTab.PendName(i, nm);
+  765                                                 QbeGen.DeclVar(nm, t);
+  766                                                 INC(i)
+  767                                               END
+  768                                             END;
+  769                                             (* a plain VAR list, not a
+  770                                                heading: the signature
+  771                                                result is discarded *)
+  772                                             IF NOT SymTab.FixPending(t) THEN
+  773                                             END; .) .
+  774    VarIdents                             (. VAR n: SymTab.Name; .)
+  775      = GetIdent<n>                       (. IF NOT SymTab.EnterPending(n,
+  776                                               SymTab.KindVar) THEN
+  777                                               SemError(200) END; .)
+  778        { ","
+  779          GetIdent<n>                     (. IF NOT SymTab.EnterPending(n,
+  780                                               SymTab.KindVar) THEN
+  781                                               SemError(200) END; .) } .
+  782    ParIdents<isV: BOOLEAN>               (. VAR n: SymTab.Name; .)
+  783      = GetIdent<n>                       (. IF NOT SymTab.EnterParam(n, isV) THEN
+  784                                               SemError(200) END; .)
+  785        { "," GetIdent<n>                 (. IF NOT SymTab.EnterParam(n, isV) THEN
+  786                                               SemError(200) END; .) } .
+  787    (* Procedure headings enter scopes/params/result and buffer the
+  788       QBE header; bodies lower to functions (4.1, module level only).
+  789       FORWARD marks; the body heading re-enters (signature compare
+  790       deferred). Nested procedures parse + check, lowering = 4.2. *)
+  791    ProcHeading<VAR pn: SymTab.Name; methCls: SymTab.TypeIndex>
+  792                                          (. VAR t: SymTab.TypeIndex;
+  793                                               mg: QbeGen.QVal; .)
+  794      = "PROCEDURE"
+  795        GetIdent<pn>                      (. IF methCls #
+  796                                                SymTab.InvalidType THEN
+  797                                             (* a method: resume the
+  798                                                declared symbol (reuse
+  799                                                its uid) *)
+  800                                             IF NOT SymTab.ResumeMethod(
+  801                                                  methCls, pn) THEN
+  802                                               SemError(200) END
+  803                                             ELSIF NOT SymTab.EnterProc(pn) THEN
+  804                                             IF NOT SymTab.ReenterProc(pn) THEN
+  805                                               IF NOT SymTab.ResumeProc(pn) THEN
+  806                                                 SemError(200) END
+  807                                               END
+  808                                             END;
+  809                                             IF methCls #
+  810                                                SymTab.InvalidType THEN
+  811                                               QbeGen.Mangled(pn,
+  812                                                 SymTab.MethUid(), mg)
+  813                                             ELSE
+  814                                               QbeGen.Mangled(pn,
+  815                                                 SymTab.ProcUid(pn), mg)
+  816                                             END;
+  817                                             QbeGen.BeginFunc(mg);
+  818                                             IF methCls #
+  819                                                SymTab.InvalidType THEN
+  820                                               (* hidden THIS receiver:
+  821                                                  a VAR param of the
+  822                                                  class type, pushed as
+  823                                                  the WITH base *)
+  824                                               IF NOT SymTab.EnterThisParam(
+  825                                                    methCls) THEN
+  826                                                 SemError(200) END;
+  827                                               IF NOT QbeGen.FuncParam(
+  828                                                    "THIS", TRUE,
+  829                                                    methCls) THEN
+  830                                                 SemError(233) END
+  831                                             END; .)
+  832        [ FormalParams ]
+  833        [ ":" TypeIdent<t>                (. IF NOT SymTab.SetProcRes(t) THEN
+  834                                               SemError(235) END;
+  835                                             QbeGen.SetFuncRes(t);
+  836                                             IF (t #
+  837                                                SymTab.InvalidType)
+  838   AND ((SymTab.ClassOf(t)
+  839                                                   = SymTab.ClArray)
+  840                                                OR (SymTab.ClassOf(t)
+  841                                                   = SymTab.ClRecord)
+  842                                                OR (SymTab.ClassOf(t)
+  843                                                   = SymTab.ClSet)
+  844                                                OR (SymTab.ClassOf(t)
+  845                                                   = SymTab.ClClass)) THEN
+  846                                               SemError(230) END; .) ] .
+  847    FormalParams
+  848      = "(" [ ParamSection { ";" ParamSection } ] ")" .
+  849    ParamSection                          (. VAR t: SymTab.TypeIndex;
+  850                                               nm: SymTab.Name;
+  851                                               i: CARDINAL;
+  852                                               isV: BOOLEAN; .)
+  853      =                                   (. isV := FALSE; .)
+  854        [ "VAR"                           (. isV := TRUE; .) ]
+  855        ParIdents<isV> ":" Type<t, TRUE>  (. i := 0;
+  856                                             WHILE i < SymTab.PendCount() DO
+  857                                               SymTab.PendName(i, nm);
+  858                                               (* value open arrays are
+  859                                                  passed as descriptor
+  860                                                  addresses (no copy):
+  861                                                  same representation as
+  862                                                  VAR formals *)
+  863                                               IF NOT QbeGen.FuncParam(nm,
+  864                                                    isV
+  865                                                    OR SymTab.IsOpenArray(t),
+  866                                                    t) THEN
+  867                                                 SemError(233) END;
+  868                                               INC(i)
+  869                                             END;
+  870                                             IF NOT SymTab.FixPending(t) THEN
+  871                                               SemError(235) END; .) .
+  872    (* Nested procedures lower like top-level ones (4.2): the
+  873       static link gives them their parent's frame. Methods keep
+  874       parse-now/230-later. *)
+  875    ProcDecl                              (. VAR pn: SymTab.Name; .)
+  876      = ProcHeading<pn, SymTab.InvalidType> ";"
+  877        ( "FORWARD"                       (. SymTab.MarkFwd;
+  878                                             SymTab.CloseProc;
+  879                                             QbeGen.AbortFunc; .)
+  880        | "EXTERNAL"                      (. SymTab.MarkExternal("");
+  881                                             SymTab.CloseProc;
+  882                                             QbeGen.AbortFunc; .)
+  883        |                                 (. QbeGen.EndFuncHeader; .)
+  884          Block<pn>                       (. SymTab.CloseProc;
+  885                                             QbeGen.EndFunc(
+  886                                               SymTab.ProcRes(pn)); .) ) .
+  887    Block<pn: SymTab.Name>                (. VAR m2: SymTab.Name; .)
+  888      = DeclSeq
+  889        [ "BEGIN"
+  890          [ StatSeq ] ]
+  891        "END"
+  892        GetIdent<m2>                      (. IF NOT SymTab.Equal(pn, m2) THEN
+  893                                               SemError(202) END; .) .
+  894    StatSeq
+  895      = Statement { ";" [ Statement ] } .
+  896    (* Trailing/empty statements (`a; ; b`, Coco/R's generated `;;`)
+  897       are accepted: the statement after ';' is optional. *)
+  898    Statement                             (. VAR lx: QbeGen.QVal; .)
+  899      = AssOrCall
+  900      | IfStat
+  901      | WhileStat
+  902      | RepeatStat
+  903      | LoopStat
+  904      | ForStat
+  905      | CaseStat
+  906      | WithStat
+  907      | ReturnStat
+  908      | HaltStat
+  909      | NewStat
+  910      | DisposeStat
+  911      | IncDecStat
+  912      | InclExclStat
+  913      | "EXIT"                            (. IF QbeGen.TopLoop(lx) THEN
+  914                                               QbeGen.Jmp(lx)
+  915                                             ELSE SemError(230) END; .) .
+  916    (* INCL(set, elem) / EXCL(set, elem): PIM set-element builtins. *)
+  917    InclExclStat                          (. VAR at, et2: SymTab.TypeIndex;
+  918                                               dk: INTEGER;
+  919                                               qd, qe: QbeGen.QVal;
+  920                                               qn: SymTab.Name;
+  921                                               sfx, isInc: BOOLEAN; .)
+  922      = ( "INCL"                          (. isInc := TRUE; .)
+  923        | "EXCL"                          (. isInc := FALSE; .) )
+  924        "(" Design<at, dk, qd, qn, sfx> ","
+  925        Expr<et2, qe> ")"
+  926                                          (. IF at = SymTab.InvalidType THEN
+  927                                             ELSIF SymTab.ClassOf(at) #
+  928                                                   SymTab.ClSet THEN
+  929                                               SemError(222)
+  930                                             ELSE
+  931                                               IF isInc THEN
+  932                                                 QbeGen.SetBit(qd, qe,
+  933                                                   SymTab.SetBaseLo(at),
+  934                                                   SymTab.SetCount(at))
+  935                                               ELSE
+  936                                                 QbeGen.SetClearBit(qd, qe,
+  937                                                   SymTab.SetBaseLo(at),
+  938                                                   SymTab.SetCount(at))
+  939                                               END
+  940                                             END; .) .
+  941    (* INC(v [,step]) / DEC(v [,step]) as builtin statements over an
+  942       integer designator. *)
+  943    IncDecStat                            (. VAR dt, et2: SymTab.TypeIndex;
+  944                                               dk: INTEGER;
+  945                                               qd, qv, qn2, qstep:
+  946                                                 QbeGen.QVal;
+  947                                               qn: SymTab.Name;
+  948                                               sfx, isInc: BOOLEAN; .)
+  949      =                                   (. isInc := TRUE; .)
+  950        ( "INC"                           (. isInc := TRUE; .)
+  951        | "DEC"                           (. isInc := FALSE; .) )
+  952        "("                               (. QbeGen.CopyOp("1", qstep); .)
+  953        Design<dt, dk, qd, qn, sfx>
+  954        [ "," Expr<et2, qstep> ]
+  955        ")"                               (. IF dt = SymTab.InvalidType THEN
+  956                                             ELSIF (dk # SymTab.KindVar)
+  957   AND (dk # SymTab.KindParam)
+  958   AND (dk # SymTab.KindField) THEN
+  959                                               SemError(210)
+  960                                             ELSIF NOT SymTab.IsIntFamily(dt) THEN
+  961                                               SemError(211)
+  962                                             ELSE
   963                                               IF sfx
   963                                               IF sfx
-  964                                                  OR (dk =
-  965                                                     SymTab.KindField) THEN
-  966                                                 QbeGen.ElemStore(qd, "0",
-  967                                                   dt)
-  968                                               ELSE QbeGen.StorePtr(qn,
-  969                                                 "0")
-  970                                               END
-  971                                             END; .) .
-  972    (* WITH pushes each record's fields (inner wins) plus its base
-  973       address; field designators resolve through both stacks. *)
-  974    WithStat                              (. VAR nW: CARDINAL; .)
-  975      = "WITH"                            (. nW := 0; .)
-  976        WithItem<nW> { "," WithItem<nW> }
-  977        "DO" [ StatSeq ] "END"
-  978                                          (. WHILE nW > 0 DO
-  979                                               SymTab.PopScope;
-  980                                               QbeGen.PopWith;
-  981                                               DEC(nW)
-  982                                             END; .) .
-  983    WithItem<VAR nW: CARDINAL>            (. VAR dt: SymTab.TypeIndex;
-  984                                               dk: INTEGER;
-  985                                               qd, qe: QbeGen.QVal;
-  986                                               qn: SymTab.Name;
-  987                                               sfx: BOOLEAN; .)
-  988      = Design<dt, dk, qd, qn, sfx>
-  989                                          (. IF dt = SymTab.InvalidType THEN
-  990                                             ELSIF (SymTab.ClassOf(dt) #
-  991                                                    SymTab.ClRecord)
-  992   AND (SymTab.ClassOf(dt) #
-  993                                                   SymTab.ClClass) THEN
-  994                                               SemError(215)
-  995                                             ELSIF SymTab.PushRecord(dt) THEN
-  996                                               QbeGen.PushWith(qd);
-  997                                               INC(nW)
-  998                                             END; .) .
-  999    (* Assignment or procedure-statement call (4.1, module level).
- 1000       Bare `P;` is a syntax error; function-as-statement is 233. *)
- 1001    AssOrCall                             (. VAR dt, et: SymTab.TypeIndex;
- 1002                                               dk: INTEGER;
- 1003                                               qd, qe, qt, ql: QbeGen.QVal;
- 1004                                               qn: SymTab.Name;
- 1005                                               ct2, res0: SymTab.TypeIndex;
- 1006                                               q2, mg0: QbeGen.QVal;
- 1007                                               isR, conv, wconv: BOOLEAN;
- 1008                                               called, sfx: BOOLEAN; .)
- 1009      = Design<dt, dk, qd, qn, sfx>
- 1010        ( ":="
- 1011          Expr<et, qe>                    (. IF (dt # SymTab.InvalidType)
- 1012   AND (dk # SymTab.KindVar)
- 1013   AND (dk # SymTab.KindParam)
- 1014   AND (dk # SymTab.KindField) THEN
- 1015                                             SemError(210)
- 1016                                           ELSIF NOT SymTab.Assignable(et,
- 1017                                                    dt) THEN
- 1018                                             SemError(210)
- 1019                                           ELSIF (dt # SymTab.InvalidType)
- 1020   AND (SymTab.ClassOf(dt) =
- 1021                                                 SymTab.ClClass) THEN
- 1022                                             SemError(230) END;
- 1023                                           isR := (dt #
- 1024                                                   SymTab.InvalidType)
- 1025   AND (SymTab.ClassOf(dt)
- 1026                                                     = SymTab.ClReal);
- 1027                                           conv := isR
- 1028   AND SymTab.IsIntFamily(et);
- 1029                                           wconv := (dt #
- 1030                                                     SymTab.InvalidType)
- 1031   AND SymTab.IsLongFamily(dt)
- 1032   AND SymTab.IsIntFamily(et);
- 1033                                           IF ((dk = SymTab.KindVar)
- 1034                                               OR (dk = SymTab.KindParam)
- 1035                                               OR (dk = SymTab.KindField))
- 1036   AND (dt # SymTab.InvalidType)
- 1037   AND (et # SymTab.InvalidType)
- 1038   AND (SymTab.ClassOf(dt) #
- 1039                                                 SymTab.ClClass) THEN
- 1040                                             IF sfx
- 1041                                                OR (dk = SymTab.KindField) THEN
- 1042                                               IF SymTab.ClassOf(dt) =
- 1043                                                  SymTab.ClArray THEN
- 1044                                                 IF (et # SymTab.InvalidType)
- 1045    AND (SymTab.ClassOf(et) = SymTab.ClUStr)
- 1046    AND SymTab.IsUCharArray(dt) THEN
- 1047                                                   QbeGen.UAssign(qd, qe)
- 1048                                                 ELSIF SymTab.StrCompat(dt,
- 1049                                                      et) THEN
- 1050                                                   QbeGen.StrAssign(qd,
- 1051                                                     qe)
- 1052                                                 ELSE
- 1053                                                   QbeGen.CopyArray(qd,
- 1054                                                     qe, dt)
- 1055                                                 END
- 1056                                               ELSIF SymTab.ClassOf(dt) =
- 1057                                                   SymTab.ClSet THEN
- 1058                                                 QbeGen.CopySet(qd, qe,
- 1059                                                   SymTab.SetWords(dt),
- 1060                                                   SymTab.SetWords(et))
- 1061                                               ELSIF SymTab.ClassOf(dt) =
- 1062                                                   SymTab.ClRecord THEN
- 1063                                                 QbeGen.CopyRecord(qd, qe,
- 1064                                                   dt)
- 1065                                               ELSIF SymTab.IsLongFamily(dt) THEN
- 1066                                                 IF wconv THEN
- 1067                                                   QbeGen.WidenLong(qe, ql);
- 1068                                                   QbeGen.ElemStore(qd, ql,
- 1069                                                     dt)
- 1070                                                 ELSE QbeGen.ElemStore(qd, qe,
- 1071                                                   dt)
- 1072                                                 END
- 1073                                               ELSIF conv THEN
- 1074                                                 QbeGen.ConvIR(qe, qt);
- 1075                                                 QbeGen.ElemStore(qd, qt,
- 1076                                                   dt)
- 1077                                               ELSE QbeGen.ElemStore(qd, qe,
- 1078                                                 dt)
- 1079                                               END
- 1080                                             ELSIF SymTab.ClassOf(dt) =
- 1081                                                   SymTab.ClArray THEN
- 1082                                               IF (et # SymTab.InvalidType)
- 1083    AND (SymTab.ClassOf(et) = SymTab.ClUStr)
- 1084    AND SymTab.IsUCharArray(dt) THEN
- 1085                                                 QbeGen.UAssign(qd, qe)
- 1086                                               ELSIF SymTab.StrCompat(dt,
- 1087                                                    et) THEN
- 1088                                                 QbeGen.StrAssign(qd, qe)
- 1089                                               ELSE
- 1090                                                 QbeGen.CopyArray(qd, qe,
- 1091                                                   dt)
- 1092                                               END
- 1093                                             ELSIF SymTab.ClassOf(dt) =
- 1094                                                   SymTab.ClSet THEN
- 1095                                               QbeGen.CopySet(qd, qe,
- 1096                                                 SymTab.SetWords(dt),
- 1097                                                 SymTab.SetWords(et))
- 1098                                             ELSIF SymTab.ClassOf(dt) =
- 1099                                                   SymTab.ClRecord THEN
- 1100                                               QbeGen.CopyRecord(qd, qe, dt)
- 1101                                             ELSIF (SymTab.ClassOf(dt) =
- 1102                                                   SymTab.ClPtr)
- 1103                                                OR (SymTab.ClassOf(dt) =
- 1104                                                   SymTab.ClProc) THEN
- 1105                                               QbeGen.StorePtr(qn, qe)
- 1106                                             ELSIF SymTab.IsLongFamily(dt) THEN
- 1107                                               IF wconv THEN
- 1108                                                 QbeGen.WidenLong(qe, ql);
- 1109                                                 QbeGen.StoreLong(qn, ql)
- 1110                                               ELSE QbeGen.StoreLong(qn, qe)
- 1111                                               END
- 1112                                             ELSIF conv THEN
- 1113                                               QbeGen.ConvIR(qe, qt);
- 1114                                               QbeGen.StoreVar(qn, qt, TRUE)
- 1115                                             ELSE
- 1116                                               QbeGen.StoreVar(qn, qe, isR)
- 1117                                             END
- 1118                                           END; .)
- 1119        | ArgList<qn, dt, qd, FALSE, methCls, ct2, q2, called>
- 1120        |                                 (* bare `P;`: proper parameterless
- 1121                                              procedure call; anything else
- 1122                                              here is 233 (was a bare syntax
- 1123                                              error before 4.2) *)
- 1124                                          (. IF (dk = SymTab.KindProc)
- 1125   AND NOT sfx THEN
- 1126                                               res0 := SymTab.ProcRes(qn);
- 1127                                               IF res0 #
- 1128                                                  SymTab.InvalidType THEN
- 1129                                                 SemError(233)
- 1130                                               ELSIF SymTab.ProcNPar(qn) #
- 1131                                                       0 THEN
- 1132                                                 SemError(233)
- 1133                                               ELSE QbeGen.Mangled(qn,
- 1134                                                 SymTab.ProcUid(qn), mg0);
- 1135                                                 QbeGen.CallBegin(mg0,
- 1136                                                   res0,
- 1137                                                   SymTab.ProcDepthOf(qn),
- 1138                                                   SymTab.IsExternal(qn));
- 1139                                                 QbeGen.CallEnd(FALSE, q2)
- 1140                                               END
- 1141                                             ELSE SemError(233)
- 1142                                             END; .) ) .
- 1143    (* Actual-parameter list shared by statement and expression calls.
- 1144       want selects CallEnd's result handling; t/q carry the call
- 1145       value (statement calls discard). Arity/type failures are 233;
- 1146       evaluation code still emits so the .ssa stays assembleable. *)
- 1147    ArgList<pn: SymTab.Name; pt: SymTab.TypeIndex; callee: QbeGen.QVal;
- 1148            want: BOOLEAN; methCls: SymTab.TypeIndex;
- 1149            VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal;
- 1150            VAR called: BOOLEAN>          (. VAR i, np: CARDINAL;
- 1151                                               vs: INTEGER;
- 1152                                               res: SymTab.TypeIndex;
- 1153                                               mg: QbeGen.QVal;
- 1154                                               ok, ind, isMeth: BOOLEAN; .)
- 1155      = "("                               (. called := TRUE;
- 1156                                             ok := TRUE;
- 1157                                             ind := FALSE;
- 1158                                             isMeth := methCls #
- 1159                                               SymTab.InvalidType;
- 1160                                             IF isMeth THEN
- 1161                                               (* class method: the
- 1162                                                  receiver is armed.
- 1163                                                  Virtual -> dispatch
- 1164                                                  through the vtable;
- 1165                                                  otherwise a static
- 1166                                                  call. *)
- 1167                                               res := SymTab.ClassMethodRes(
- 1168                                                 methCls, pn);
- 1169                                               vs := SymTab.VirtSlot(
- 1170                                                 methCls, pn);
- 1171                                               IF vs >= 0 THEN
- 1172                                                 QbeGen.VirtCallBegin(
- 1173                                                   callee, vs, res)
- 1174                                               ELSE
- 1175                                                 QbeGen.Mangled(pn,
- 1176                                                   SymTab.ClassMethodUid(
- 1177                                                   methCls, pn), mg);
- 1178                                                 QbeGen.CallBegin(mg, res, 0,
- 1179                                                   FALSE)
- 1180                                               END
- 1181                                             ELSIF SymTab.SymKind(pn) =
- 1182                                                SymTab.KindProc THEN
- 1183                                               res := SymTab.ProcRes(pn);
- 1184                                               QbeGen.Mangled(pn,
- 1185                                                 SymTab.ProcUid(pn), mg);
- 1186                                               QbeGen.CallBegin(mg, res,
- 1187                                                 SymTab.ProcDepthOf(pn),
- 1188                                                 SymTab.IsExternal(pn))
- 1189                                             ELSIF (pt # SymTab.InvalidType)
- 1190    AND (SymTab.ClassOf(pt) = SymTab.ClProc) THEN
- 1191                                               ind := TRUE;
- 1192                                               res :=
- 1193                                                 SymTab.ProcTypeRes(pt);
- 1194                                               QbeGen.CallBeginInd(callee,
- 1195                                                 res, FALSE)
- 1196                                             ELSE SemError(233);
- 1197                                               ok := FALSE;
- 1198                                               res := SymTab.InvalidType
- 1199                                             END;
- 1200                                             i := 0; .)
- 1201        [ ActParam<pn, pt, ind, methCls, i>        (. INC(i); .)
- 1202          { "," ActParam<pn, pt, ind, methCls, i>  (. INC(i); .) } ]
- 1203        ")"                               (. IF ok THEN
- 1204                                               IF isMeth THEN
- 1205                                                 np := SymTab.ClassMethodNPar(
- 1206                                                   methCls, pn)
- 1207                                               ELSIF ind THEN
- 1208                                                 np := SymTab.ProcTypeNPar(pt)
- 1209                                               ELSE np := SymTab.ProcNPar(pn)
- 1210                                               END;
- 1211                                               IF i # np THEN
- 1212                                                 SemError(233); ok := FALSE
- 1213                                               END
- 1214                                             END;
- 1215                                             IF NOT ok THEN
- 1216                                               t := SymTab.InvalidType;
- 1217                                               QbeGen.CopyOp("0", q)
- 1218                                             ELSIF want THEN
- 1219                                               IF res =
- 1220                                                  SymTab.InvalidType THEN
- 1221                                                 SemError(233);
- 1222                                                 t := SymTab.InvalidType;
- 1223                                                 QbeGen.CopyOp("0", q)
- 1224                                               ELSE t := res;
- 1225                                                 QbeGen.CallEnd(TRUE, q)
- 1226                                               END
- 1227                                             ELSE
- 1228                                               IF res #
- 1229                                                  SymTab.InvalidType THEN
- 1230                                                 SemError(233)
- 1231                                               END;
- 1232                                               t := SymTab.InvalidType;
- 1233                                               QbeGen.CopyOp("0", q);
- 1234                                               QbeGen.CallEnd(FALSE, q)
- 1235                                             END; .) .
- 1236    (* One actual: VAR formals take recorded designator addresses
- 1237       (233 otherwise); value formals take converted expressions. *)
- 1238    ActParam<pn: SymTab.Name; pt: SymTab.TypeIndex; ind: BOOLEAN;
- 1239             methCls: SymTab.TypeIndex; i: CARDINAL>
- 1240                                          (. VAR at, ft: SymTab.TypeIndex;
- 1241                                               qe, qa, qt: QbeGen.QVal;
- 1242                                               isV, conv: BOOLEAN; .)
- 1243      = Expr<at, qe>                      (. IF ind THEN
- 1244                                               ft :=
- 1245                                                 SymTab.ProcTypeParamType(pt,
- 1246                                                   i);
- 1247                                               isV :=
- 1248                                                 SymTab.ProcTypeParamIsVar(pt,
- 1249                                                   i)
- 1250                                             ELSIF methCls #
- 1251                                                SymTab.InvalidType THEN
- 1252                                               ft :=
- 1253                                                 SymTab.ClassMethodParamType(
- 1254                                                 methCls, pn, i);
- 1255                                               isV :=
- 1256                                                 SymTab.ClassMethodParamIsVar(
- 1257                                                 methCls, pn, i)
- 1258                                             ELSE
- 1259                                               ft := SymTab.ParamType(pn, i);
- 1260                                               isV := SymTab.ParamIsVar(pn, i)
- 1261                                             END;
- 1262                                             IF (at = SymTab.InvalidType)
- 1263                                                OR (ft =
- 1264                                                   SymTab.InvalidType) THEN
- 1265                                             ELSIF isV THEN
- 1266                                               IF (SymTab.ClassOf(at)
- 1267                                                  = SymTab.ClChar)
- 1268    AND (SymTab.ClassOf(ft) = SymTab.ClArray)
- 1269    AND (SymTab.ClassOf(SymTab.ArrayElem(ft))
- 1270         = SymTab.ClChar)
- 1271    AND QbeGen.IsImm(qe) THEN
- 1272                                                 (* 1-char string
- 1273                                                    literal passed to
- 1274                                                    a VAR ARRAY OF CHAR *)
- 1275                                                 QbeGen.DeclCharStr(qe,
- 1276                                                      qa);
- 1277                                                 IF NOT QbeGen.CallArg(qa,
- 1278                                                    "l") THEN
- 1279                                                   SemError(233)
- 1280                                                 END
- 1281                                               ELSIF NOT QbeGen.AddrOfVal(qe,
- 1282                                                    qa) THEN
- 1283                                                 SemError(233)
- 1284                                               ELSIF NOT SymTab.VarParamOk(at,
- 1285                                                        ft) THEN
- 1286                                                 SemError(233)
- 1287                                               ELSIF NOT QbeGen.CallArg(qa,
- 1288                                                        "l") THEN
- 1289                                                 SemError(233)
- 1290                                               END
- 1291                                             ELSE
- 1292                                               IF (SymTab.ClassOf(at)
- 1293                                                  = SymTab.ClChar)
- 1294    AND (SymTab.ClassOf(ft) = SymTab.ClArray)
- 1295    AND (SymTab.ClassOf(SymTab.ArrayElem(ft))
- 1296         = SymTab.ClChar)
- 1297    AND QbeGen.IsImm(qe) THEN
- 1298                                                 (* 1-char string
- 1299                                                    literal passed to
- 1300                                                    ARRAY OF CHAR *)
- 1301                                                 QbeGen.DeclCharStr(qe,
- 1302                                                      qa);
- 1303                                                 IF NOT QbeGen.CallArg(qa,
- 1304                                                    "l") THEN
- 1305                                                   SemError(233)
- 1306                                                 END
- 1307                                               ELSIF NOT SymTab.Assignable(at,
- 1308                                                    ft) THEN
- 1309                                                 SemError(233)
- 1310                                               ELSE
- 1311                                                 conv := (SymTab.ClassOf(
- 1312                                                   ft) = SymTab.ClReal)
- 1313   AND SymTab.IsIntFamily(at);
- 1314                                                 IF conv THEN
- 1315                                                   QbeGen.ConvIR(qe, qt);
- 1316                                                   IF NOT QbeGen.CallArg(qt,
- 1317                                                      "d") THEN
- 1318                                                     SemError(233)
- 1319                                                   END
- 1320                                                 ELSIF NOT QbeGen.CallArg(qe,
- 1321                                                   QbeGen.ArgClass(ft)) THEN
- 1322                                                   SemError(233)
- 1323                                                 END
- 1324                                               END
- 1325                                             END; .) .
- 1326    IfStat                                (. VAR t: SymTab.TypeIndex;
- 1327                                               q, lThen, lElse, lEnd:
- 1328                                                 QbeGen.QVal;
- 1329                                               hasElse: BOOLEAN; .)
- 1330      = "IF"                              (. hasElse := FALSE; .)
- 1331        Expr<t, q>                        (. IF NOT SymTab.BoolCheck(t) THEN
- 1332                                               SemError(214) END;
- 1333                                             QbeGen.NewLabel(lThen);
- 1334                                             QbeGen.NewLabel(lElse);
- 1335                                             QbeGen.NewLabel(lEnd);
- 1336                                             QbeGen.Jnz(q, lThen, lElse);
- 1337                                             QbeGen.EmitLabel(lThen); .)
- 1338        "THEN" [ StatSeq ]                (. QbeGen.Jmp(lEnd); .)
- 1339        { "ELSIF"                         (. QbeGen.EmitLabel(lElse);
- 1340                                             QbeGen.NewLabel(lElse); .)
- 1341          Expr<t, q>                      (. IF NOT SymTab.BoolCheck(t) THEN
- 1342                                               SemError(214) END;
- 1343                                             QbeGen.NewLabel(lThen);
- 1344                                             QbeGen.Jnz(q, lThen, lElse);
- 1345                                             QbeGen.EmitLabel(lThen); .)
- 1346          "THEN" [ StatSeq ]              (. QbeGen.Jmp(lEnd); .) }
- 1347        [ "ELSE"                          (. QbeGen.EmitLabel(lElse);
- 1348                                             hasElse := TRUE; .)
- 1349          [ StatSeq ] ]
- 1350        "END"                             (. IF hasElse THEN
- 1351                                               QbeGen.EmitLabel(lEnd)
- 1352                                             ELSE QbeGen.EmitLabel(lElse);
- 1353                                               QbeGen.EmitLabel(lEnd)
- 1354                                             END; .) .
- 1355    WhileStat                             (. VAR t: SymTab.TypeIndex;
- 1356                                               q, lTop, lBody, lEnd:
- 1357                                                 QbeGen.QVal; .)
- 1358      = "WHILE"                           (. QbeGen.NewLabel(lTop);
- 1359                                             QbeGen.NewLabel(lBody);
- 1360                                             QbeGen.NewLabel(lEnd);
- 1361                                             QbeGen.EmitLabel(lTop); .)
- 1362        Expr<t, q>                        (. IF NOT SymTab.BoolCheck(t) THEN
- 1363                                               SemError(214) END;
- 1364                                             QbeGen.Jnz(q, lBody, lEnd);
- 1365                                             QbeGen.EmitLabel(lBody); .)
- 1366        "DO" [ StatSeq ]                      (. QbeGen.Jmp(lTop); .)
- 1367        "END"                             (. QbeGen.EmitLabel(lEnd); .) .
- 1368    RepeatStat                            (. VAR t: SymTab.TypeIndex;
- 1369                                               q, lTop, lEnd: QbeGen.QVal; .)
- 1370      = "REPEAT"                          (. QbeGen.NewLabel(lTop);
- 1371                                             QbeGen.NewLabel(lEnd);
- 1372                                             QbeGen.EmitLabel(lTop); .)
- 1373        [ StatSeq ]
- 1374        "UNTIL" Expr<t, q>                (. IF NOT SymTab.BoolCheck(t) THEN
- 1375                                               SemError(214) END;
- 1376                                             QbeGen.Jnz(q, lEnd, lTop);
- 1377                                             QbeGen.EmitLabel(lEnd); .) .
- 1378    LoopStat                              (. VAR lTop, lEnd: QbeGen.QVal; .)
- 1379      = "LOOP"                            (. QbeGen.NewLabel(lTop);
- 1380                                             QbeGen.NewLabel(lEnd);
- 1381                                             QbeGen.PushLoop(lEnd);
- 1382                                             QbeGen.EmitLabel(lTop); .)
- 1383        [ StatSeq ]
- 1384        "END"                             (. QbeGen.Jmp(lTop);
- 1385                                             QbeGen.PopLoop;
- 1386                                             QbeGen.EmitLabel(lEnd); .) .
- 1387    (* FOR with static-sign BY (literal, non-zero; 220 otherwise).
- 1388       Runtime direction would need a compare-select; the literal
- 1389       sign picks cslew/csegew at "DO" time. *)
- 1390    ForStat                               (. VAR lv: SymTab.Name;
- 1391                                               tlo, thi, tby:
- 1392                                                 SymTab.TypeIndex;
- 1393                                               qlo, qhi, qby, qt, qk, qb:
- 1394                                                 QbeGen.QVal;
- 1395                                               lTop, lBody, lEnd:
- 1396                                                 QbeGen.QVal;
- 1397                                               by: INTEGER;
- 1398                                               ok: BOOLEAN; .)
- 1399      = "FOR"                             (. by := 1; .)
- 1400        GetIdent<lv>                      (. ok := SymTab.Lookup(lv);
- 1401                                             IF NOT ok THEN
- 1402                                               SemError(201)
- 1403                                             ELSIF (SymTab.SymKind(lv) #
- 1404                                                    SymTab.KindVar)
- 1405   AND (SymTab.SymKind(lv) #
- 1406                                                   SymTab.KindParam) THEN
- 1407                                               SemError(220); ok := FALSE
- 1408                                             ELSIF NOT SymTab.IsIntFamily(
- 1409                                                     SymTab.SymType(lv)) THEN
- 1410                                               SemError(220); ok := FALSE
- 1411                                             END; .)
- 1412        ":=" Expr<tlo, qlo>               (. IF NOT SymTab.IsIntFamily(tlo) THEN
- 1413                                               SemError(220); ok := FALSE
- 1414                                             END; .)
- 1415        "TO" Expr<thi, qhi>               (. IF NOT SymTab.IsIntFamily(thi) THEN
- 1416                                               SemError(220); ok := FALSE
- 1417                                             END; .)
- 1418        [ "BY" Expr<tby, qby>             (. IF (tby #
- 1419                                               SymTab.InvalidType)
- 1420   AND NOT SymTab.IsIntFamily(tby) THEN
- 1421                                               SemError(220); ok := FALSE
- 1422                                             END;
- 1423                                             IF NOT SymTab.ConstInt(qby, by) THEN
- 1424                                               SemError(230); by := 1
- 1425                                             ELSIF by = 0 THEN
- 1426                                               SemError(220); by := 1
- 1427                                             END; .) ]
- 1428        "DO"                              (. IF ok THEN
- 1429                                               QbeGen.StoreVar(lv, qlo,
- 1430                                                 FALSE) END;
- 1431                                             QbeGen.NewLabel(lTop);
- 1432                                             QbeGen.NewLabel(lBody);
- 1433                                             QbeGen.NewLabel(lEnd);
- 1434                                             QbeGen.EmitLabel(lTop);
- 1435                                             QbeGen.LoadVar(lv, FALSE, qt);
- 1436                                             QbeGen.NewTemp(qk);
- 1437                                             IF by > 0 THEN
- 1438                                               QbeGen.Op3("cslew", qk,
- 1439                                                 qt, qhi, FALSE)
- 1440                                             ELSE QbeGen.Op3("csgew", qk,
- 1441                                               qt, qhi, FALSE)
- 1442                                             END;
- 1443                                             QbeGen.Jnz(qk, lBody, lEnd);
- 1444                                             QbeGen.EmitLabel(lBody); .)
- 1445        [ StatSeq ]
- 1446        "END"                             (. IF ok THEN
- 1447                                               QbeGen.LoadVar(lv, FALSE,
- 1448                                                 qt);
- 1449                                               QbeGen.IntStr(by, qb);
- 1450                                               QbeGen.NewTemp(qk);
- 1451                                               QbeGen.Op3("add", qk,
- 1452                                                 qt, qb, FALSE);
- 1453                                               QbeGen.StoreVar(lv, qk,
- 1454                                                 FALSE) END;
- 1455                                             QbeGen.Jmp(lTop);
- 1456                                             QbeGen.EmitLabel(lEnd); .) .
- 1457    CaseStat                              (. VAR tsel: SymTab.TypeIndex;
- 1458                                               qsel, lEnd: QbeGen.QVal; .)
- 1459      = "CASE" Expr<tsel, qsel>           (. QbeGen.NewLabel(lEnd); .)
- 1460        "OF" CaseAlt<tsel, qsel, lEnd>
- 1461        { "|" CaseAlt<tsel, qsel, lEnd> }
- 1462        [ "ELSE" [ StatSeq ] ]
- 1463        "END"                             (. QbeGen.EmitLabel(lEnd); .) .
- 1464    (* Compare-chain lowering: each alternative ends its match-tests
- 1465       with "jmp lAfter", so the no-match fallthrough skips the body:
- 1466       "cmp; jnz(lBody,lF); lF: ... ; jmp lAfter; lBody: S; jmp lEnd;
- 1467       lAfter:". Falls into the next alternative, ELSE, or END. *)
- 1468    CaseAlt<tsel: SymTab.TypeIndex; qsel: QbeGen.QVal;
- 1469            lEnd: QbeGen.QVal>            (. VAR lBody, lAfter: QbeGen.QVal; .)
- 1470      =                                   (. QbeGen.NewLabel(lBody);
- 1471                                             QbeGen.NewLabel(lAfter); .)
- 1472        CaseLabel<tsel, qsel, lBody>
- 1473        { "," CaseLabel<tsel, qsel, lBody> }
- 1474        ":"                               (. QbeGen.Jmp(lAfter);
- 1475                                             QbeGen.EmitLabel(lBody); .)
- 1476        [ StatSeq ]                       (. QbeGen.Jmp(lEnd);
- 1477                                             QbeGen.EmitLabel(lAfter); .) .
- 1478    CaseLabel<tsel: SymTab.TypeIndex; qsel: QbeGen.QVal;
- 1479              lBody: QbeGen.QVal>         (. VAR t2, t3: SymTab.TypeIndex;
- 1480                                               q2, q3, qc, qd, qe:
- 1481                                                 QbeGen.QVal;
- 1482                                               lNext: QbeGen.QVal; .)
- 1483      = Expr<t2, q2>                      (. IF (t2 #
- 1484                                               SymTab.InvalidType)
- 1485   AND (tsel #
- 1486                                                 SymTab.InvalidType)
- 1487   AND ((SymTab.ClassOf(t2) =
- 1488                                                  SymTab.ClSet)
- 1489                                                 OR (SymTab.ClassOf(tsel) =
- 1490                                                     SymTab.ClSet)) THEN
- 1491                                               SemError(230)
- 1492                                             ELSIF (t2 #
- 1493                                               SymTab.InvalidType)
- 1494   AND (tsel #
- 1495                                                 SymTab.InvalidType)
- 1496   AND NOT SymTab.EqCheck(t2,
- 1497                                                   tsel) THEN
- 1498                                               SemError(213) END;
- 1499                                             IF NOT QbeGen.IsImm(q2) THEN
- 1500                                               SemError(230);
- 1501                                               QbeGen.CopyOp("0", q2)
- 1502                                             END;
- 1503                                             QbeGen.NewLabel(lNext);
- 1504                                             QbeGen.Cmp(SymTab.OpEq,
- 1505                                               qsel, q2, qc, FALSE);
- 1506                                             QbeGen.Jnz(qc, lBody, lNext);
- 1507                                             QbeGen.EmitLabel(lNext); .)
- 1508        [ ".." Expr<t3, q3>               (. IF (t3 #
+  964                                                  OR (dk = SymTab.KindField) THEN
+  965                                                 QbeGen.ElemLoad(qd, dt, qv)
+  966                                               ELSE QbeGen.LoadVar(qn,
+  967                                                 FALSE, qv)
+  968                                               END;
+  969                                               QbeGen.NewTemp(qn2);
+  970                                               IF isInc THEN
+  971                                                 QbeGen.Op3("add", qn2, qv,
+  972                                                   qstep, FALSE)
+  973                                               ELSE QbeGen.Op3("sub", qn2, qv,
+  974                                                   qstep, FALSE)
+  975                                               END;
+  976                                               IF sfx
+  977                                                  OR (dk = SymTab.KindField) THEN
+  978                                                 QbeGen.ElemStore(qd, qn2,
+  979                                                   dt)
+  980                                               ELSE QbeGen.StoreVar(qn,
+  981                                                 qn2, FALSE)
+  982                                               END
+  983                                             END; .) .
+  984    (* NEW/DISPOSE as builtin statements (no call syntax until step 4).
+  985       Targets are pointer designators; DISPOSE nils afterwards (safer
+  986       than Wirth-undefined; documented). DISPOSE is shallow. *)
+  987    NewStat                               (. VAR dt: SymTab.TypeIndex;
+  988                                               dk: INTEGER;
+  989                                               qd, qm: QbeGen.QVal;
+  990                                               qn: SymTab.Name;
+  991                                               sfx: BOOLEAN;
+  992                                               bt: SymTab.TypeIndex; .)
+  993      = "NEW" "(" Design<dt, dk, qd, qn, sfx> ")"
+  994                                          (. IF dt = SymTab.InvalidType THEN
+  995                                             ELSIF (dk # SymTab.KindVar)
+  996   AND (dk # SymTab.KindParam)
+  997   AND (dk # SymTab.KindField) THEN
+  998                                               SemError(210)
+  999                                             ELSIF SymTab.ClassOf(dt) #
+ 1000                                                   SymTab.ClPtr THEN
+ 1001                                               SemError(219)
+ 1002                                             ELSE bt := SymTab.PtrBase(dt);
+ 1003                                               IF bt #
+ 1004                                                  SymTab.InvalidType THEN
+ 1005                                                 QbeGen.NewHeap(bt, qm);
+ 1006                                                 QbeGen.InitHeap(qm, bt);
+ 1007                                                 IF sfx
+ 1008                                                    OR (dk =
+ 1009                                                       SymTab.KindField) THEN
+ 1010                                                   QbeGen.ElemStore(qd, qm,
+ 1011                                                     dt)
+ 1012                                                 ELSE QbeGen.StorePtr(qn,
+ 1013                                                   qm)
+ 1014                                                 END
+ 1015                                               END
+ 1016                                             END; .) .
+ 1017    DisposeStat                           (. VAR dt: SymTab.TypeIndex;
+ 1018                                               dk: INTEGER;
+ 1019                                               qd, qv: QbeGen.QVal;
+ 1020                                               qn: SymTab.Name;
+ 1021                                               sfx: BOOLEAN; .)
+ 1022      = "DISPOSE" "(" Design<dt, dk, qd, qn, sfx> ")"
+ 1023                                          (. IF dt = SymTab.InvalidType THEN
+ 1024                                             ELSIF (dk # SymTab.KindVar)
+ 1025   AND (dk # SymTab.KindParam)
+ 1026   AND (dk # SymTab.KindField) THEN
+ 1027                                               SemError(210)
+ 1028                                             ELSIF SymTab.ClassOf(dt) #
+ 1029                                                   SymTab.ClPtr THEN
+ 1030                                               SemError(219)
+ 1031                                             ELSE
+ 1032                                               IF sfx
+ 1033                                                  OR (dk =
+ 1034                                                     SymTab.KindField) THEN
+ 1035                                                 QbeGen.ElemLoad(qd, dt,
+ 1036                                                   qv)
+ 1037                                               ELSE QbeGen.LoadPtr(qn, qv)
+ 1038                                               END;
+ 1039                                               QbeGen.FreeHeap(qv);
+ 1040                                               IF sfx
+ 1041                                                  OR (dk =
+ 1042                                                     SymTab.KindField) THEN
+ 1043                                                 QbeGen.ElemStore(qd, "0",
+ 1044                                                   dt)
+ 1045                                               ELSE QbeGen.StorePtr(qn,
+ 1046                                                 "0")
+ 1047                                               END
+ 1048                                             END; .) .
+ 1049    (* WITH pushes each record's fields (inner wins) plus its base
+ 1050       address; field designators resolve through both stacks. *)
+ 1051    WithStat                              (. VAR nW: CARDINAL; .)
+ 1052      = "WITH"                            (. nW := 0; .)
+ 1053        WithItem<nW> { "," WithItem<nW> }
+ 1054        "DO" [ StatSeq ] "END"
+ 1055                                          (. WHILE nW > 0 DO
+ 1056                                               SymTab.PopScope;
+ 1057                                               QbeGen.PopWith;
+ 1058                                               DEC(nW)
+ 1059                                             END; .) .
+ 1060    WithItem<VAR nW: CARDINAL>            (. VAR dt: SymTab.TypeIndex;
+ 1061                                               dk: INTEGER;
+ 1062                                               qd, qe: QbeGen.QVal;
+ 1063                                               qn: SymTab.Name;
+ 1064                                               sfx: BOOLEAN; .)
+ 1065      = Design<dt, dk, qd, qn, sfx>
+ 1066                                          (. IF dt = SymTab.InvalidType THEN
+ 1067                                             ELSIF (SymTab.ClassOf(dt) #
+ 1068                                                    SymTab.ClRecord)
+ 1069   AND (SymTab.ClassOf(dt) #
+ 1070                                                   SymTab.ClClass) THEN
+ 1071                                               SemError(215)
+ 1072                                             ELSIF SymTab.PushRecord(dt) THEN
+ 1073                                               QbeGen.PushWith(qd);
+ 1074                                               INC(nW)
+ 1075                                             END; .) .
+ 1076    (* Assignment or procedure-statement call (4.1, module level).
+ 1077       Bare `P;` is a syntax error; function-as-statement is 233. *)
+ 1078    AssOrCall                             (. VAR dt, et: SymTab.TypeIndex;
+ 1079                                               dk: INTEGER;
+ 1080                                               qd, qe, qt, ql: QbeGen.QVal;
+ 1081                                               qn: SymTab.Name;
+ 1082                                               ct2, res0: SymTab.TypeIndex;
+ 1083                                               q2, mg0: QbeGen.QVal;
+ 1084                                               isR, conv, wconv: BOOLEAN;
+ 1085                                               called, sfx: BOOLEAN; .)
+ 1086      = Design<dt, dk, qd, qn, sfx>
+ 1087        ( ":="
+ 1088          Expr<et, qe>                    (. IF (dt # SymTab.InvalidType)
+ 1089   AND (dk # SymTab.KindVar)
+ 1090   AND (dk # SymTab.KindParam)
+ 1091   AND (dk # SymTab.KindField) THEN
+ 1092                                             SemError(210)
+ 1093                                           ELSIF NOT SymTab.Assignable(et,
+ 1094                                                    dt) THEN
+ 1095                                             SemError(210)
+ 1096                                           ELSIF (dt # SymTab.InvalidType)
+ 1097   AND (SymTab.ClassOf(dt) =
+ 1098                                                 SymTab.ClClass) THEN
+ 1099                                             SemError(230) END;
+ 1100                                           isR := (dt #
+ 1101                                                   SymTab.InvalidType)
+ 1102   AND (SymTab.ClassOf(dt)
+ 1103                                                     = SymTab.ClReal);
+ 1104                                           conv := isR
+ 1105   AND SymTab.IsIntFamily(et);
+ 1106                                           wconv := (dt #
+ 1107                                                     SymTab.InvalidType)
+ 1108   AND SymTab.IsLongFamily(dt)
+ 1109   AND SymTab.IsIntFamily(et);
+ 1110                                           IF ((dk = SymTab.KindVar)
+ 1111                                               OR (dk = SymTab.KindParam)
+ 1112                                               OR (dk = SymTab.KindField))
+ 1113   AND (dt # SymTab.InvalidType)
+ 1114   AND (et # SymTab.InvalidType)
+ 1115   AND (SymTab.ClassOf(dt) #
+ 1116                                                 SymTab.ClClass) THEN
+ 1117                                             IF sfx
+ 1118                                                OR (dk = SymTab.KindField) THEN
+ 1119                                               IF SymTab.ClassOf(dt) =
+ 1120                                                  SymTab.ClArray THEN
+ 1121                                                 IF (et # SymTab.InvalidType)
+ 1122    AND (SymTab.ClassOf(et) = SymTab.ClUStr)
+ 1123    AND SymTab.IsUCharArray(dt) THEN
+ 1124                                                   QbeGen.UAssign(qd, qe)
+ 1125                                                 ELSIF SymTab.StrCompat(dt,
+ 1126                                                      et) THEN
+ 1127                                                   QbeGen.StrAssign(qd,
+ 1128                                                     qe)
+ 1129                                                 ELSE
+ 1130                                                   QbeGen.CopyArray(qd,
+ 1131                                                     qe, dt)
+ 1132                                                 END
+ 1133                                               ELSIF SymTab.ClassOf(dt) =
+ 1134                                                   SymTab.ClSet THEN
+ 1135                                                 QbeGen.CopySet(qd, qe,
+ 1136                                                   SymTab.SetWords(dt),
+ 1137                                                   SymTab.SetWords(et))
+ 1138                                               ELSIF SymTab.ClassOf(dt) =
+ 1139                                                   SymTab.ClRecord THEN
+ 1140                                                 QbeGen.CopyRecord(qd, qe,
+ 1141                                                   dt)
+ 1142                                               ELSIF SymTab.IsLongFamily(dt) THEN
+ 1143                                                 IF wconv THEN
+ 1144                                                   QbeGen.WidenLong(qe, ql);
+ 1145                                                   QbeGen.ElemStore(qd, ql,
+ 1146                                                     dt)
+ 1147                                                 ELSE QbeGen.ElemStore(qd, qe,
+ 1148                                                   dt)
+ 1149                                                 END
+ 1150                                               ELSIF conv THEN
+ 1151                                                 QbeGen.ConvIR(qe, qt);
+ 1152                                                 QbeGen.ElemStore(qd, qt,
+ 1153                                                   dt)
+ 1154                                               ELSE QbeGen.ElemStore(qd, qe,
+ 1155                                                 dt)
+ 1156                                               END
+ 1157                                             ELSIF SymTab.ClassOf(dt) =
+ 1158                                                   SymTab.ClArray THEN
+ 1159                                               IF (et # SymTab.InvalidType)
+ 1160    AND (SymTab.ClassOf(et) = SymTab.ClUStr)
+ 1161    AND SymTab.IsUCharArray(dt) THEN
+ 1162                                                 QbeGen.UAssign(qd, qe)
+ 1163                                               ELSIF SymTab.StrCompat(dt,
+ 1164                                                    et) THEN
+ 1165                                                 QbeGen.StrAssign(qd, qe)
+ 1166                                               ELSE
+ 1167                                                 QbeGen.CopyArray(qd, qe,
+ 1168                                                   dt)
+ 1169                                               END
+ 1170                                             ELSIF SymTab.ClassOf(dt) =
+ 1171                                                   SymTab.ClSet THEN
+ 1172                                               QbeGen.CopySet(qd, qe,
+ 1173                                                 SymTab.SetWords(dt),
+ 1174                                                 SymTab.SetWords(et))
+ 1175                                             ELSIF SymTab.ClassOf(dt) =
+ 1176                                                   SymTab.ClRecord THEN
+ 1177                                               QbeGen.CopyRecord(qd, qe, dt)
+ 1178                                             ELSIF (SymTab.ClassOf(dt) =
+ 1179                                                   SymTab.ClPtr)
+ 1180                                                OR (SymTab.ClassOf(dt) =
+ 1181                                                   SymTab.ClProc) THEN
+ 1182                                               QbeGen.StorePtr(qn, qe)
+ 1183                                             ELSIF SymTab.IsLongFamily(dt) THEN
+ 1184                                               IF wconv THEN
+ 1185                                                 QbeGen.WidenLong(qe, ql);
+ 1186                                                 QbeGen.StoreLong(qn, ql)
+ 1187                                               ELSE QbeGen.StoreLong(qn, qe)
+ 1188                                               END
+ 1189                                             ELSIF conv THEN
+ 1190                                               QbeGen.ConvIR(qe, qt);
+ 1191                                               QbeGen.StoreVar(qn, qt, TRUE)
+ 1192                                             ELSE
+ 1193                                               QbeGen.StoreVar(qn, qe, isR)
+ 1194                                             END
+ 1195                                           END; .)
+ 1196        | ArgList<qn, dt, qd, FALSE, methCls, ct2, q2, called>
+ 1197        |                                 (* bare `P;`: proper parameterless
+ 1198                                              procedure call; anything else
+ 1199                                              here is 233 (was a bare syntax
+ 1200                                              error before 4.2) *)
+ 1201                                          (. IF (dk = SymTab.KindProc)
+ 1202   AND NOT sfx THEN
+ 1203                                               res0 := SymTab.ProcRes(qn);
+ 1204                                               IF res0 #
+ 1205                                                  SymTab.InvalidType THEN
+ 1206                                                 SemError(233)
+ 1207                                               ELSIF SymTab.ProcNPar(qn) #
+ 1208                                                       0 THEN
+ 1209                                                 SemError(233)
+ 1210                                               ELSE QbeGen.Mangled(qn,
+ 1211                                                 SymTab.ProcUid(qn), mg0);
+ 1212                                                 QbeGen.CallBegin(mg0,
+ 1213                                                   res0,
+ 1214                                                   SymTab.ProcDepthOf(qn),
+ 1215                                                   SymTab.IsExternal(qn));
+ 1216                                                 QbeGen.CallEnd(FALSE, q2)
+ 1217                                               END
+ 1218                                             ELSE SemError(233)
+ 1219                                             END; .) ) .
+ 1220    (* Actual-parameter list shared by statement and expression calls.
+ 1221       want selects CallEnd's result handling; t/q carry the call
+ 1222       value (statement calls discard). Arity/type failures are 233;
+ 1223       evaluation code still emits so the .ssa stays assembleable. *)
+ 1224    ArgList<pn: SymTab.Name; pt: SymTab.TypeIndex; callee: QbeGen.QVal;
+ 1225            want: BOOLEAN; methCls: SymTab.TypeIndex;
+ 1226            VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal;
+ 1227            VAR called: BOOLEAN>          (. VAR i, np: CARDINAL;
+ 1228                                               vs: INTEGER;
+ 1229                                               res: SymTab.TypeIndex;
+ 1230                                               mg: QbeGen.QVal;
+ 1231                                               ok, ind, isMeth, va: BOOLEAN; .)
+ 1232      = "("                               (. called := TRUE;
+ 1233                                             ok := TRUE;
+ 1234                                             ind := FALSE;
+ 1235                                             isMeth := methCls #
+ 1236                                               SymTab.InvalidType;
+ 1237                                             IF isMeth THEN
+ 1238                                               (* class method: the
+ 1239                                                  receiver is armed.
+ 1240                                                  Virtual -> dispatch
+ 1241                                                  through the vtable;
+ 1242                                                  otherwise a static
+ 1243                                                  call. *)
+ 1244                                               res := SymTab.ClassMethodRes(
+ 1245                                                 methCls, pn);
+ 1246                                               vs := SymTab.VirtSlot(
+ 1247                                                 methCls, pn);
+ 1248                                               IF vs >= 0 THEN
+ 1249                                                 QbeGen.VirtCallBegin(
+ 1250                                                   callee, vs, res)
+ 1251                                               ELSE
+ 1252                                                 QbeGen.Mangled(pn,
+ 1253                                                   SymTab.ClassMethodUid(
+ 1254                                                   methCls, pn), mg);
+ 1255                                                 QbeGen.CallBegin(mg, res, 0,
+ 1256                                                   FALSE)
+ 1257                                               END
+ 1258                                             ELSIF SymTab.SymKind(pn) =
+ 1259                                                SymTab.KindProc THEN
+ 1260                                               res := SymTab.ProcRes(pn);
+ 1261                                               QbeGen.Mangled(pn,
+ 1262                                                 SymTab.ProcUid(pn), mg);
+ 1263                                               QbeGen.CallBegin(mg, res,
+ 1264                                                 SymTab.ProcDepthOf(pn),
+ 1265                                                 SymTab.IsExternal(pn))
+ 1266                                             ELSIF (pt # SymTab.InvalidType)
+ 1267    AND (SymTab.ClassOf(pt) = SymTab.ClProc) THEN
+ 1268                                               ind := TRUE;
+ 1269                                               res :=
+ 1270                                                 SymTab.ProcTypeRes(pt);
+ 1271                                               QbeGen.CallBeginInd(callee,
+ 1272                                                 res, FALSE)
+ 1273                                             ELSE SemError(233);
+ 1274                                               ok := FALSE;
+ 1275                                               res := SymTab.InvalidType
+ 1276                                             END;
+ 1277                                             i := 0; .)
+ 1278        [ ActParam<pn, pt, ind, methCls, i>        (. INC(i); .)
+ 1279          { "," ActParam<pn, pt, ind, methCls, i>  (. INC(i); .) } ]
+ 1280        ")"                               (. IF ok THEN
+ 1281                                               IF isMeth THEN
+ 1282                                                 np := SymTab.ClassMethodNPar(
+ 1283                                                   methCls, pn)
+ 1284                                               ELSIF ind THEN
+ 1285                                                 np := SymTab.ProcTypeNPar(pt)
+ 1286                                               ELSE np := SymTab.ProcNPar(pn)
+ 1287                                               END;
+ 1288                                               va := (NOT isMeth) AND (NOT ind)
+ 1289                                                 AND (SymTab.SymKind(pn) =
+ 1290                                                      SymTab.KindProc)
+ 1291                                                 AND SymTab.Varargs(pn);
+ 1292                                               IF (i # np) AND NOT va THEN
+ 1293                                                 SemError(233); ok := FALSE
+ 1294                                               END
+ 1295                                             END;
+ 1296                                             IF NOT ok THEN
+ 1297                                               t := SymTab.InvalidType;
+ 1298                                               QbeGen.CopyOp("0", q)
+ 1299                                             ELSIF want THEN
+ 1300                                               IF res =
+ 1301                                                  SymTab.InvalidType THEN
+ 1302                                                 SemError(233);
+ 1303                                                 t := SymTab.InvalidType;
+ 1304                                                 QbeGen.CopyOp("0", q)
+ 1305                                               ELSE t := res;
+ 1306                                                 QbeGen.CallEnd(TRUE, q)
+ 1307                                               END
+ 1308                                             ELSE
+ 1309                                               IF res #
+ 1310                                                  SymTab.InvalidType THEN
+ 1311                                                 SemError(233)
+ 1312                                               END;
+ 1313                                               t := SymTab.InvalidType;
+ 1314                                               QbeGen.CopyOp("0", q);
+ 1315                                               QbeGen.CallEnd(FALSE, q)
+ 1316                                             END; .) .
+ 1317    (* One actual: VAR formals take recorded designator addresses
+ 1318       (233 otherwise); value formals take converted expressions. *)
+ 1319    ActParam<pn: SymTab.Name; pt: SymTab.TypeIndex; ind: BOOLEAN;
+ 1320             methCls: SymTab.TypeIndex; i: CARDINAL>
+ 1321                                          (. VAR at, ft: SymTab.TypeIndex;
+ 1322                                               qe, qa, qt: QbeGen.QVal;
+ 1323                                               isV, conv, va: BOOLEAN;
+ 1324                                               cl: CHAR; .)
+ 1325      = Expr<at, qe>                      (. va := (NOT ind)
+ 1326                                               AND (methCls =
+ 1327                                                  SymTab.InvalidType)
+ 1328                                               AND (SymTab.SymKind(pn) =
+ 1329                                                  SymTab.KindProc)
+ 1330                                               AND SymTab.Varargs(pn);
+ 1331                                             IF ind THEN
+ 1332                                               ft :=
+ 1333                                                 SymTab.ProcTypeParamType(pt,
+ 1334                                                   i);
+ 1335                                               isV :=
+ 1336                                                 SymTab.ProcTypeParamIsVar(pt,
+ 1337                                                   i)
+ 1338                                             ELSIF methCls #
+ 1339                                                SymTab.InvalidType THEN
+ 1340                                               ft :=
+ 1341                                                 SymTab.ClassMethodParamType(
+ 1342                                                 methCls, pn, i);
+ 1343                                               isV :=
+ 1344                                                 SymTab.ClassMethodParamIsVar(
+ 1345                                                 methCls, pn, i)
+ 1346                                             ELSE
+ 1347                                               ft := SymTab.ParamType(pn, i);
+ 1348                                               isV := SymTab.ParamIsVar(pn, i)
+ 1349                                             END;
+ 1350                                             IF (at = SymTab.InvalidType) THEN
+ 1351                                             ELSIF ft = SymTab.InvalidType THEN
+ 1352                                               IF va THEN
+ 1353                                                 QbeGen.CArgAdd(qe, at, qa, cl)
+ 1354                                               END
+ 1355                                             ELSIF isV THEN
+ 1356                                               IF (SymTab.ClassOf(at)
+ 1357                                                  = SymTab.ClChar)
+ 1358    AND (SymTab.ClassOf(ft) = SymTab.ClArray)
+ 1359    AND (SymTab.ClassOf(SymTab.ArrayElem(ft))
+ 1360         = SymTab.ClChar)
+ 1361    AND QbeGen.IsImm(qe) THEN
+ 1362                                                 (* 1-char string
+ 1363                                                    literal passed to
+ 1364                                                    a VAR ARRAY OF CHAR *)
+ 1365                                                 QbeGen.DeclCharStr(qe,
+ 1366                                                      qa);
+ 1367                                                 IF NOT QbeGen.CallArg(qa,
+ 1368                                                    "l") THEN
+ 1369                                                   SemError(233)
+ 1370                                                 END
+ 1371                                               ELSIF NOT QbeGen.AddrOfVal(qe,
+ 1372                                                    qa) THEN
+ 1373                                                 SemError(233)
+ 1374                                               ELSIF NOT SymTab.VarParamOk(at,
+ 1375                                                        ft) THEN
+ 1376                                                 SemError(233)
+ 1377                                               ELSIF NOT QbeGen.CallArg(qa,
+ 1378                                                        "l") THEN
+ 1379                                                 SemError(233)
+ 1380                                               END
+ 1381                                             ELSE
+ 1382                                               IF (SymTab.ClassOf(at)
+ 1383                                                  = SymTab.ClChar)
+ 1384    AND (SymTab.ClassOf(ft) = SymTab.ClArray)
+ 1385    AND (SymTab.ClassOf(SymTab.ArrayElem(ft))
+ 1386         = SymTab.ClChar)
+ 1387    AND QbeGen.IsImm(qe) THEN
+ 1388                                                 (* 1-char string
+ 1389                                                    literal passed to
+ 1390                                                    ARRAY OF CHAR *)
+ 1391                                                 QbeGen.DeclCharStr(qe,
+ 1392                                                      qa);
+ 1393                                                 IF NOT QbeGen.CallArg(qa,
+ 1394                                                    "l") THEN
+ 1395                                                   SemError(233)
+ 1396                                                 END
+ 1397                                               ELSIF NOT SymTab.Assignable(at,
+ 1398                                                    ft) THEN
+ 1399                                                 SemError(233)
+ 1400                                               ELSE
+ 1401                                                 conv := (SymTab.ClassOf(
+ 1402                                                   ft) = SymTab.ClReal)
+ 1403   AND SymTab.IsIntFamily(at);
+ 1404                                                 IF conv THEN
+ 1405                                                   QbeGen.ConvIR(qe, qt);
+ 1406                                                   IF NOT QbeGen.CallArg(qt,
+ 1407                                                      "d") THEN
+ 1408                                                     SemError(233)
+ 1409                                                   END
+ 1410                                                 ELSIF NOT QbeGen.CallArg(qe,
+ 1411                                                   QbeGen.ArgClass(ft)) THEN
+ 1412                                                   SemError(233)
+ 1413                                                 END
+ 1414                                               END
+ 1415                                             END; .) .
+ 1416    IfStat                                (. VAR t: SymTab.TypeIndex;
+ 1417                                               q, lThen, lElse, lEnd:
+ 1418                                                 QbeGen.QVal;
+ 1419                                               hasElse: BOOLEAN; .)
+ 1420      = "IF"                              (. hasElse := FALSE; .)
+ 1421        Expr<t, q>                        (. IF NOT SymTab.BoolCheck(t) THEN
+ 1422                                               SemError(214) END;
+ 1423                                             QbeGen.NewLabel(lThen);
+ 1424                                             QbeGen.NewLabel(lElse);
+ 1425                                             QbeGen.NewLabel(lEnd);
+ 1426                                             QbeGen.Jnz(q, lThen, lElse);
+ 1427                                             QbeGen.EmitLabel(lThen); .)
+ 1428        "THEN" [ StatSeq ]                (. QbeGen.Jmp(lEnd); .)
+ 1429        { "ELSIF"                         (. QbeGen.EmitLabel(lElse);
+ 1430                                             QbeGen.NewLabel(lElse); .)
+ 1431          Expr<t, q>                      (. IF NOT SymTab.BoolCheck(t) THEN
+ 1432                                               SemError(214) END;
+ 1433                                             QbeGen.NewLabel(lThen);
+ 1434                                             QbeGen.Jnz(q, lThen, lElse);
+ 1435                                             QbeGen.EmitLabel(lThen); .)
+ 1436          "THEN" [ StatSeq ]              (. QbeGen.Jmp(lEnd); .) }
+ 1437        [ "ELSE"                          (. QbeGen.EmitLabel(lElse);
+ 1438                                             hasElse := TRUE; .)
+ 1439          [ StatSeq ] ]
+ 1440        "END"                             (. IF hasElse THEN
+ 1441                                               QbeGen.EmitLabel(lEnd)
+ 1442                                             ELSE QbeGen.EmitLabel(lElse);
+ 1443                                               QbeGen.EmitLabel(lEnd)
+ 1444                                             END; .) .
+ 1445    WhileStat                             (. VAR t: SymTab.TypeIndex;
+ 1446                                               q, lTop, lBody, lEnd:
+ 1447                                                 QbeGen.QVal; .)
+ 1448      = "WHILE"                           (. QbeGen.NewLabel(lTop);
+ 1449                                             QbeGen.NewLabel(lBody);
+ 1450                                             QbeGen.NewLabel(lEnd);
+ 1451                                             QbeGen.EmitLabel(lTop); .)
+ 1452        Expr<t, q>                        (. IF NOT SymTab.BoolCheck(t) THEN
+ 1453                                               SemError(214) END;
+ 1454                                             QbeGen.Jnz(q, lBody, lEnd);
+ 1455                                             QbeGen.EmitLabel(lBody); .)
+ 1456        "DO" [ StatSeq ]                      (. QbeGen.Jmp(lTop); .)
+ 1457        "END"                             (. QbeGen.EmitLabel(lEnd); .) .
+ 1458    RepeatStat                            (. VAR t: SymTab.TypeIndex;
+ 1459                                               q, lTop, lEnd: QbeGen.QVal; .)
+ 1460      = "REPEAT"                          (. QbeGen.NewLabel(lTop);
+ 1461                                             QbeGen.NewLabel(lEnd);
+ 1462                                             QbeGen.EmitLabel(lTop); .)
+ 1463        [ StatSeq ]
+ 1464        "UNTIL" Expr<t, q>                (. IF NOT SymTab.BoolCheck(t) THEN
+ 1465                                               SemError(214) END;
+ 1466                                             QbeGen.Jnz(q, lEnd, lTop);
+ 1467                                             QbeGen.EmitLabel(lEnd); .) .
+ 1468    LoopStat                              (. VAR lTop, lEnd: QbeGen.QVal; .)
+ 1469      = "LOOP"                            (. QbeGen.NewLabel(lTop);
+ 1470                                             QbeGen.NewLabel(lEnd);
+ 1471                                             QbeGen.PushLoop(lEnd);
+ 1472                                             QbeGen.EmitLabel(lTop); .)
+ 1473        [ StatSeq ]
+ 1474        "END"                             (. QbeGen.Jmp(lTop);
+ 1475                                             QbeGen.PopLoop;
+ 1476                                             QbeGen.EmitLabel(lEnd); .) .
+ 1477    (* FOR with static-sign BY (literal, non-zero; 220 otherwise).
+ 1478       Runtime direction would need a compare-select; the literal
+ 1479       sign picks cslew/csegew at "DO" time. *)
+ 1480    ForStat                               (. VAR lv: SymTab.Name;
+ 1481                                               tlo, thi, tby:
+ 1482                                                 SymTab.TypeIndex;
+ 1483                                               qlo, qhi, qby, qt, qk, qb:
+ 1484                                                 QbeGen.QVal;
+ 1485                                               lTop, lBody, lEnd:
+ 1486                                                 QbeGen.QVal;
+ 1487                                               by: INTEGER;
+ 1488                                               ok: BOOLEAN; .)
+ 1489      = "FOR"                             (. by := 1; .)
+ 1490        GetIdent<lv>                      (. ok := SymTab.Lookup(lv);
+ 1491                                             IF NOT ok THEN
+ 1492                                               SemError(201)
+ 1493                                             ELSIF (SymTab.SymKind(lv) #
+ 1494                                                    SymTab.KindVar)
+ 1495   AND (SymTab.SymKind(lv) #
+ 1496                                                   SymTab.KindParam) THEN
+ 1497                                               SemError(220); ok := FALSE
+ 1498                                             ELSIF NOT SymTab.IsIntFamily(
+ 1499                                                     SymTab.SymType(lv)) THEN
+ 1500                                               SemError(220); ok := FALSE
+ 1501                                             END; .)
+ 1502        ":=" Expr<tlo, qlo>               (. IF NOT SymTab.IsIntFamily(tlo) THEN
+ 1503                                               SemError(220); ok := FALSE
+ 1504                                             END; .)
+ 1505        "TO" Expr<thi, qhi>               (. IF NOT SymTab.IsIntFamily(thi) THEN
+ 1506                                               SemError(220); ok := FALSE
+ 1507                                             END; .)
+ 1508        [ "BY" Expr<tby, qby>             (. IF (tby #
  1509                                               SymTab.InvalidType)
  1509                                               SymTab.InvalidType)
- 1510   AND (tsel #
- 1511                                                 SymTab.InvalidType)
- 1512   AND NOT SymTab.EqCheck(t3,
- 1513                                                   tsel) THEN
- 1514                                               SemError(213) END;
- 1515                                             IF NOT QbeGen.IsImm(q3) THEN
- 1516                                               SemError(230);
- 1517                                               QbeGen.CopyOp("0", q3)
- 1518                                             END;
- 1519                                             QbeGen.Cmp(SymTab.OpGe,
- 1520                                               qsel, q2, qc, FALSE);
- 1521                                             QbeGen.Cmp(SymTab.OpLe,
- 1522                                               qsel, q3, qd, FALSE);
- 1523                                             QbeGen.NewTemp(qe);
- 1524                                             QbeGen.Op3("and", qe, qc, qd,
- 1525                                               FALSE);
- 1526                                             QbeGen.NewLabel(lNext);
- 1527                                             QbeGen.Jnz(qe, lBody, lNext);
- 1528                                             QbeGen.EmitLabel(lNext); .) ] .
- 1529    ReturnStat                            (. VAR t: SymTab.TypeIndex;
- 1530                                               q, qt: QbeGen.QVal;
- 1531                                               res: SymTab.TypeIndex;
- 1532                                               hadE, conv: BOOLEAN; .)
- 1533      = "RETURN"                          (. hadE := FALSE; .)
- 1534        [ Expr<t, q>                      (. hadE := TRUE; .) ]
- 1535                                          (. conv := FALSE;
- 1536                                             IF NOT SymTab.InProc() THEN
- 1537                                               SemError(232)
- 1538                                             ELSE res := SymTab.CurRes();
- 1539                                               IF NOT hadE THEN
- 1540                                                 IF res #
- 1541                                                    SymTab.InvalidType THEN
- 1542                                                   SemError(232)
- 1543                                                 ELSE QbeGen.EmitRet(q,
- 1544                                                   FALSE)
- 1545                                                 END
- 1546                                               ELSIF (res =
- 1547                                                      SymTab.InvalidType)
- 1548                                                  OR (t #
- 1549                                                      SymTab.InvalidType)
- 1550   AND NOT SymTab.Assignable(t,
- 1551                                                       res) THEN
- 1552                                                 SemError(232)
- 1553                                               ELSE
- 1554                                                 conv := (SymTab.ClassOf(
- 1555                                                   res) = SymTab.ClReal)
- 1556   AND SymTab.IsIntFamily(t);
- 1557                                                 IF conv THEN
- 1558                                                   QbeGen.ConvIR(q, qt);
- 1559                                                   QbeGen.EmitRet(qt, TRUE)
- 1560                                                 ELSE QbeGen.EmitRet(q, TRUE)
- 1561                                                 END
- 1562                                               END
- 1563                                             END; .) .
- 1564    HaltStat                              (. VAR t: SymTab.TypeIndex;
- 1565                                               q: QbeGen.QVal; .)
- 1566      = "HALT" [ "(" Expr<t, q> ")" ]     (. QbeGen.HaltQ; .) .
- 1567    (* Designator: scalar loads, array addresses, and index suffixes.
- 1568       Each index descends one level (bounds-checked, trap on breach);
- 1569       nested levels reload the inner descriptor address. q ends as the
- 1570       value (scalars), the descriptor address (plain arrays), or the
- 1571       element address (indexed); sfx marks the indexed form. *)
- 1572    Design<VAR t: SymTab.TypeIndex; VAR k: INTEGER;
- 1573           VAR q: QbeGen.QVal; VAR qn: SymTab.Name; VAR sfx: BOOLEAN>
- 1574                                          (. VAR n, fn, mal: SymTab.Name;
- 1575                                               cls: INTEGER;
- 1576                                               ic: SymTab.TypeIndex;
- 1577                                               curT, it, eT, bt:
- 1578                                                 SymTab.TypeIndex;
- 1579                                               iq, ql, qlo, qhi, qe:
- 1580                                                 QbeGen.QVal;
- 1581                                               lo, hi: INTEGER;
- 1582                                               fo: INTEGER;
- 1583                                               isOpen: BOOLEAN;
- 1584                                               qb, cv: QbeGen.QVal; .)
- 1585      = GetIdent<n>                       (. methCls := SymTab.InvalidType;
- 1586                                             QbeGen.CopyOp(n, qn);
- 1587                                             sfx := FALSE;
- 1588                                             IF NOT SymTab.Lookup(n) THEN
- 1589                                               (* a bare method name inside
- 1590                                                  a CLASS IMPLEMENTATION
- 1591                                                  is a sibling call on
- 1592                                                  THIS *)
- 1593                                               ic := SymTab.CurImplClass();
- 1594                                               IF (ic #
- 1595                                                  SymTab.InvalidType)
- 1596    AND SymTab.MethodExists(ic, n) THEN
- 1597                                                 sfx := FALSE;
- 1598                                                 QbeGen.ThisBase(q);
- 1599                                                 QbeGen.ArmRecv(q);
- 1600                                                 methCls := ic;
- 1601                                                 k := SymTab.KindProc;
- 1602                                                 t := SymTab.InvalidType
- 1603                                               ELSE
- 1604                                                 SemError(201);
- 1605                                                 t :=
- 1606                                                   SymTab.InvalidType;
- 1607                                                 k := -1;
- 1608                                                 QbeGen.CopyOp("0", q)
- 1609                                               END
- 1610                                             ELSE
- 1611                                               t := SymTab.SymType(n);
- 1612                                               k := SymTab.SymKind(n);
- 1613                                               IF k = SymTab.KindConst THEN
- 1614                                                 IF SymTab.Equal(n,
- 1615                                                    "TRUE") THEN
- 1616                                                   t := SymTab.BoolType();
- 1617                                                   QbeGen.CopyOp("1", q)
- 1618                                                 ELSIF SymTab.Equal(n,
- 1619                                                    "FALSE") THEN
- 1620                                                   t := SymTab.BoolType();
- 1621                                                   QbeGen.CopyOp("0", q)
- 1622                                                 ELSIF SymTab.Equal(n,
- 1623                                                    "NIL") THEN
- 1624                                                   QbeGen.CopyOp("0", q)
- 1625                                                 ELSE
- 1626                                                   cls :=
- 1627                                                     SymTab.ClassOf(t);
- 1628                                                   IF (t #
- 1629                                                       SymTab.InvalidType)
- 1630   AND ((cls = SymTab.ClInt)
- 1631                                                      OR (cls
- 1632                                                          = SymTab.ClChar)
- 1633                                                      OR (cls
- 1634                                                          = SymTab.ClEnum)
- 1635                                                      OR (cls
- 1636                                                          = SymTab.ClReal)
- 1637                                                      OR (cls
- 1638                                                          = SymTab.ClNil)) THEN
- 1639                                                     IF cls = SymTab.ClNil THEN
- 1640                                                       QbeGen.CopyOp("0", q)
- 1641                                                     ELSIF ((cls
- 1642                                                         = SymTab.ClInt)
- 1643                                                       OR (cls
- 1644                                                         = SymTab.ClChar)
- 1645                                                       OR (cls
- 1646                                                         = SymTab.ClEnum))
- 1647    AND SymTab.GetSymVal(n, cv)
- 1648    AND QbeGen.IsImm(cv) THEN
- 1649                                                       QbeGen.CopyOp(cv, q)
- 1650                                                     ELSE
- 1651                                                       QbeGen.LoadVar(n,
- 1652                                                         cls = SymTab.ClReal,
- 1653                                                         q)
- 1654                                                     END
- 1655                                                   ELSIF (cls = SymTab.ClArray)
- 1656                                                      OR (cls = SymTab.ClRecord)
- 1657                                                      OR (cls = SymTab.ClClass)
- 1658                                                      OR (cls = SymTab.ClStr)
- 1659                                                      OR (cls = SymTab.ClUStr) THEN
- 1660                                                     (* aggregate constant:
- 1661                                                        its value IS the
- 1662                                                        descriptor address *)
- 1663                                                     IF SymTab.GetSymVal(n, cv) THEN
- 1664                                                       QbeGen.CopyOp(cv, q)
- 1665                                                     ELSE
- 1666                                                       QbeGen.CopyOp("0", q)
- 1667                                                     END
- 1668                                                   ELSE
- 1669                                                     IF t #
- 1670                                                        SymTab.InvalidType THEN
- 1671                                                       SemError(230)
- 1672                                                     END;
- 1673                                                     QbeGen.CopyOp("0", q)
- 1674                                                   END
- 1675                                                 END
- 1676                                               ELSIF (k = SymTab.KindVar)
- 1677                                                  OR (k = SymTab.KindParam) THEN
- 1678                                                 cls :=
- 1679                                                   SymTab.ClassOf(t);
- 1680                                                 IF (cls = SymTab.ClInt)
- 1681                                                    OR (cls = SymTab.ClBool)
- 1682                                                    OR (cls = SymTab.ClChar)
- 1683                                                    OR (cls = SymTab.ClUChar)
- 1684                                                    OR (cls = SymTab.ClEnum)
- 1685                                                    OR (cls
- 1686                                                        = SymTab.ClReal) THEN
- 1687                                                   QbeGen.LoadVar(n,
- 1688                                                     cls = SymTab.ClReal, q)
- 1689                                                 ELSIF (cls = SymTab.ClPtr)
- 1690                                                    OR (cls = SymTab.ClProc) THEN
- 1691                                                   QbeGen.LoadPtr(n, q)
- 1692                                                 ELSIF cls = SymTab.ClLong THEN
- 1693                                                   QbeGen.LoadLong(n, q)
- 1694                                                 ELSIF (cls
- 1695                                                         = SymTab.ClArray)
- 1696                                                    OR (cls
- 1697                                                        = SymTab.ClSet)
- 1698                                                    OR (cls
- 1699                                                        = SymTab.ClRecord)
- 1700                                                    OR (cls
- 1701                                                        = SymTab.ClUStr)
- 1702                                                    OR (cls
- 1703                                                        = SymTab.ClClass) THEN
- 1704                                                   QbeGen.AddrOf(n, q)
- 1705                                                 ELSE SemError(230);
- 1706                                                   QbeGen.CopyOp("0", q)
- 1707                                                 END
- 1708                                               ELSE QbeGen.CopyOp("0", q);
- 1709                                                 IF k = SymTab.KindImport THEN
- 1710                                                   SemError(230)
- 1711                                                 ELSIF k =
- 1712                                                    SymTab.KindProc THEN
- 1713                                                   (* bare procedure name:
- 1714                                                      a following ArgList
- 1715                                                      makes it a call;
- 1716                                                      otherwise Fact
- 1717                                                      reports 230 *)
- 1718                                                 ELSE
- 1719                                                   IF k = SymTab.KindField THEN
- 1720                                                     IF QbeGen.TopWith(qb) THEN
- 1721                                                       fo :=
- 1722                                                         SymTab.FieldOffset(
- 1723                                                         SymTab.FieldOwner(n),
- 1724                                                         n);
- 1725                                                       QbeGen.FieldAddr(qb,
- 1726                                                         fo, q);
- 1727                                                       sfx := TRUE
- 1728                                                     ELSE SemError(230);
- 1729                                                       QbeGen.CopyOp("0", q)
- 1730                                                     END
- 1731                                                   END
- 1732                                                 END
- 1733                                               END
- 1734                                             END; .)
- 1735        { "[" Expr<it, iq>
- 1736                                          (. IF t = SymTab.InvalidType THEN
- 1737                                             ELSIF SymTab.ClassOf(t) #
- 1738                                                   SymTab.ClArray THEN
- 1739                                               SemError(217);
- 1740                                               t := SymTab.InvalidType
- 1741                                             ELSIF NOT SymTab.IsIntFamily(it)
- 1742   AND (SymTab.ClassOf(it) #
- 1743                                                   SymTab.ClChar)
- 1744   AND (SymTab.ClassOf(it) #
- 1745                                                   SymTab.ClEnum) THEN
- 1746                                               SemError(218);
- 1747                                               t := SymTab.InvalidType
- 1748                                             ELSE
- 1749                                               QbeGen.WidenIndex(iq, ql);
- 1750                                               isOpen :=
- 1751                                                 SymTab.IsOpenArray(t);
- 1752                                               IF isOpen THEN
- 1753                                                 QbeGen.CopyOp("0", qlo);
- 1754                                                 IF SymTab.IsCharArray(t)
- 1755    OR SymTab.IsUCharArray(t) THEN
- 1756                                                   QbeGen.OpenHiChar(q, qhi)
- 1757                                                 ELSE QbeGen.OpenHi(q, qhi)
- 1758                                                 END
- 1759                                               ELSE
- 1760                                                 lo := SymTab.ArrayLo(t);
- 1761                                                 hi := SymTab.ArrayHi(t);
- 1762                                                 IF SymTab.IsCharArray(t)
- 1763    OR SymTab.IsUCharArray(t) THEN
- 1764                                                   hi := hi + 1
- 1765                                                 END;
- 1766                                                 QbeGen.IntStr(lo, qlo);
- 1767                                                 QbeGen.IntStr(hi, qhi)
- 1768                                               END;
- 1769                                               QbeGen.CheckRange(ql, qlo,
- 1770                                                 qhi);
- 1771                                               eT := SymTab.ArrayElem(t);
- 1772                                               QbeGen.ElemAddr(q, ql, qlo,
- 1773                                                 t, qe);
- 1774                                               IF SymTab.ClassOf(eT) =
- 1775                                                  SymTab.ClArray THEN
- 1776                                                 QbeGen.ElemLoad(qe, eT, q)
- 1777                                               ELSE QbeGen.CopyOp(qe, q)
- 1778                                               END;
- 1779                                               t := eT; sfx := TRUE
- 1780                                             END; .)
- 1781          { "," Expr<it, iq>
- 1782                                          (. IF t = SymTab.InvalidType THEN
- 1783                                             ELSIF SymTab.ClassOf(t) #
- 1784                                                   SymTab.ClArray THEN
- 1785                                               SemError(217);
- 1786                                               t := SymTab.InvalidType
- 1787                                             ELSIF NOT SymTab.IsIntFamily(it)
- 1788   AND (SymTab.ClassOf(it) #
- 1789                                                   SymTab.ClChar)
- 1790   AND (SymTab.ClassOf(it) #
- 1791                                                   SymTab.ClEnum) THEN
- 1792                                               SemError(218);
- 1793                                               t := SymTab.InvalidType
- 1794                                             ELSE
- 1795                                               QbeGen.WidenIndex(iq, ql);
- 1796                                               isOpen :=
- 1797                                                 SymTab.IsOpenArray(t);
- 1798                                               IF isOpen THEN
- 1799                                                 QbeGen.CopyOp("0", qlo);
- 1800                                                 IF SymTab.IsCharArray(t)
- 1801    OR SymTab.IsUCharArray(t) THEN
- 1802                                                   QbeGen.OpenHiChar(q, qhi)
- 1803                                                 ELSE QbeGen.OpenHi(q, qhi)
- 1804                                                 END
- 1805                                               ELSE
- 1806                                                 lo := SymTab.ArrayLo(t);
- 1807                                                 hi := SymTab.ArrayHi(t);
- 1808                                                 IF SymTab.IsCharArray(t)
- 1809    OR SymTab.IsUCharArray(t) THEN
- 1810                                                   hi := hi + 1
- 1811                                                 END;
- 1812                                                 QbeGen.IntStr(lo, qlo);
- 1813                                                 QbeGen.IntStr(hi, qhi)
- 1814                                               END;
- 1815                                               QbeGen.CheckRange(ql, qlo,
- 1816                                                 qhi);
- 1817                                               eT := SymTab.ArrayElem(t);
- 1818                                               QbeGen.ElemAddr(q, ql, qlo,
- 1819                                                 t, qe);
- 1820                                               IF SymTab.ClassOf(eT) =
- 1821                                                  SymTab.ClArray THEN
- 1822                                                 QbeGen.ElemLoad(qe, eT, q)
- 1823                                               ELSE QbeGen.CopyOp(qe, q)
- 1824                                               END;
- 1825                                               t := eT; sfx := TRUE
- 1826                                             END; .) }
- 1827          "]"
- 1828        | "." GetIdent<fn>
- 1829                                          (. IF k = SymTab.KindModule THEN
- 1830                                               (* qualified L.x: materialize
- 1831                                                  the export, then load it *)
- 1832                                               IF NOT SymTab.MaterializeAlias(n,
- 1833                                                    fn, mal) THEN
- 1834                                                 SemError(201);
- 1835                                                 t := SymTab.InvalidType;
- 1836                                                 QbeGen.CopyOp("0", q)
- 1837                                               ELSE
- 1838                                                 QbeGen.CopyOp(mal, qn);
- 1839                                                 t := SymTab.SymType(mal);
- 1840                                                 k := SymTab.SymKind(mal);
- 1841                                                 sfx := FALSE;
- 1842                                                 IF k = SymTab.KindProc THEN
- 1843                                                   (* call: ArgList supplies
- 1844                                                      the value *)
- 1845                                                   QbeGen.CopyOp("0", q)
- 1846                                                 ELSIF NOT QbeGen.LoadDesignator(
- 1847                                                      mal, t, k, q) THEN
- 1848                                                   SemError(230);
- 1849                                                   QbeGen.CopyOp("0", q)
- 1850                                                 END
- 1851                                               END
- 1852                                             ELSIF t = SymTab.InvalidType THEN
- 1853                                             ELSIF (SymTab.ClassOf(t) #
- 1854                                                    SymTab.ClRecord)
- 1855   AND (SymTab.ClassOf(t) #
- 1856                                                   SymTab.ClClass) THEN
- 1857                                               SemError(215);
- 1858                                               t := SymTab.InvalidType
- 1859                                             ELSIF (SymTab.ClassOf(t) =
- 1860                                                    SymTab.ClClass)
- 1861    AND SymTab.MethodExists(t, fn) THEN
- 1862                                               (* obj.Method: bind the
- 1863                                                  method and pass obj as
- 1864                                                  the hidden receiver; q
- 1865                                                  already holds the
- 1866                                                  object's address *)
- 1867                                               QbeGen.ArmRecv(q);
- 1868                                               QbeGen.CopyOp(fn, n);
- 1869                                               QbeGen.CopyOp(fn, qn);
- 1870                                               methCls := t;
- 1871                                               k := SymTab.KindProc;
- 1872                                               t := SymTab.InvalidType
- 1873                                             ELSIF NOT SymTab.FieldExists(t,
- 1874                                                      fn) THEN
- 1875                                               SemError(216);
- 1876                                               t := SymTab.InvalidType
- 1877                                             ELSE
- 1878                                               fo := SymTab.FieldOffset(t,
- 1879                                                 fn);
- 1880                                               t := SymTab.FieldType(t, fn);
- 1881                                               QbeGen.FieldAddr(q, fo, qe);
- 1882                                               (* array fields are inline:
- 1883                                                  the field address is the
- 1884                                                  descriptor, like records *)
- 1885                                               QbeGen.CopyOp(qe, q);
- 1886                                               sfx := TRUE
- 1887                                             END; .)
- 1888        | "^"
- 1889                                          (. IF t = SymTab.InvalidType THEN
- 1890                                             ELSIF SymTab.ClassOf(t) #
- 1891                                                   SymTab.ClPtr THEN
- 1892                                               SemError(219);
- 1893                                               t := SymTab.InvalidType
- 1894                                             ELSE
- 1895                                               bt := SymTab.PtrBase(t);
- 1896                                               IF bt = SymTab.InvalidType THEN
- 1897                                               ELSE
- 1898                                                 IF sfx THEN
- 1899                                                   QbeGen.ElemLoad(q, t,
- 1900                                                     qb);
- 1901                                                   QbeGen.CopyOp(qb, q)
- 1902                                                 END;
- 1903                                                 t := bt;
- 1904                                                 (* q holds the pointee
- 1905                                                    address: Fact loads
- 1906                                                    scalars/pointers and uses
- 1907                                                    the address for
- 1908                                                    aggregates; the VAR-actual
- 1909                                                    note is q itself. *)
- 1910                                                 sfx := TRUE
- 1911                                               END
- 1912                                             END; .) } .
- 1913    Expr<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 1914                                          (. VAR t2: SymTab.TypeIndex;
- 1915                                               op: INTEGER;
- 1916                                               q2, qt, wl: QbeGen.QVal;
- 1917                                               isR: BOOLEAN; .)
- 1918      = SimExpr<t, q>
- 1919        [ Rel<op> SimExpr<t2, q2>
- 1920          (. IF op = SymTab.OpIn THEN
- 1921               IF SymTab.InCheck(t, t2) THEN
- 1922                 IF (t = SymTab.InvalidType)
- 1923                    OR (t2 = SymTab.InvalidType) THEN
- 1924                   t := SymTab.BoolType(); QbeGen.CopyOp("0", q)
- 1925                 ELSE
- 1926                   QbeGen.InSet(q, q2, SymTab.SetBaseLo(t2),
- 1927                     SymTab.SetCount(t2), qt);
- 1928                   t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
- 1929                 END
- 1930               ELSE SemError(222); t := SymTab.InvalidType;
- 1931                 QbeGen.CopyOp("0", q)
- 1932               END
- 1933             ELSIF SymTab.RelCheck(t, t2, op) THEN
- 1934               IF (t = SymTab.InvalidType)
- 1935                  OR (t2 = SymTab.InvalidType) THEN
- 1936                 t := SymTab.BoolType(); QbeGen.CopyOp("0", q)
- 1937               ELSIF (SymTab.ClassOf(t) = SymTab.ClPtr)
- 1938                  OR (SymTab.ClassOf(t2) = SymTab.ClPtr) THEN
- 1939                 IF (op # SymTab.OpEq) AND (op # SymTab.OpNeq1)
- 1940   AND (op # SymTab.OpNeq2) THEN
- 1941                   SemError(213); t := SymTab.InvalidType;
- 1942                   QbeGen.CopyOp("0", q)
- 1943                 ELSE
- 1944                   QbeGen.CmpL(op, q, q2, qt);
- 1945                   t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
- 1946                 END
- 1947               ELSIF (SymTab.ClassOf(t) = SymTab.ClSet)
- 1948                  OR (SymTab.ClassOf(t2) = SymTab.ClSet) THEN
- 1949                 QbeGen.CmpSet(op, q, q2,
- 1950                   SymTab.SetWords(t), SymTab.SetWords(t2), qt);
- 1951                 t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
- 1952               ELSIF SymTab.StrCompat(t, t2) THEN
- 1953                 QbeGen.StrEq(op, q, q2, qt);
- 1954                 t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
- 1955               ELSIF SymTab.IsLongFamily(t)
- 1956                  OR SymTab.IsLongFamily(t2) THEN
- 1957                 IF SymTab.IsIntFamily(t) THEN
- 1958                   QbeGen.WidenLong(q, wl); QbeGen.CopyOp(wl, q)
- 1959                 END;
- 1960                 IF SymTab.IsIntFamily(t2) THEN
- 1961                   QbeGen.WidenLong(q2, wl); QbeGen.CopyOp(wl, q2)
- 1962                 END;
- 1963                 QbeGen.CmpLong(op, q, q2, qt);
- 1964                 t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
- 1965               ELSE
- 1966                 isR := SymTab.ClassOf(t) = SymTab.ClReal;
- 1967                 t := SymTab.BoolType();
- 1968                 QbeGen.Cmp(op, q, q2, qt, isR);
- 1969                 QbeGen.CopyOp(qt, q)
- 1970               END
- 1971             ELSE SemError(213); t := SymTab.InvalidType;
- 1972               QbeGen.CopyOp("0", q)
- 1973             END; .) ] .
- 1974    Rel<VAR op: INTEGER>
- 1975      = "="                               (. op := SymTab.OpEq; .)
- 1976      | "#"                               (. op := SymTab.OpNeq1; .)
- 1977      | "<"                               (. op := SymTab.OpLt; .)
- 1978      | "<="                              (. op := SymTab.OpLe; .)
- 1979      | ">"                               (. op := SymTab.OpGt; .)
- 1980      | ">="                              (. op := SymTab.OpGe; .)
- 1981      | "IN"                              (. op := SymTab.OpIn; .) .
- 1982    SimExpr<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 1983                                          (. VAR t2, res2, lt, rt:
- 1984                                                 SymTab.TypeIndex;
- 1985                                               op: INTEGER;
- 1986                                               q2, qt, wq, qf, q2a, q2b:
- 1987                                                 QbeGen.QVal;
- 1988                                               neg, isR, isL, folded:
- 1989                                                 BOOLEAN;
- 1990                                               fok: BOOLEAN;
- 1991                                               lw, rw, mw: CARDINAL;
- 1992                                               lTrue, lNext, lDone, qr, qs:
- 1993                                                 QbeGen.QVal; .)
- 1994      =                                   (. neg := FALSE; .)
- 1995        [ "+" | "-"                       (. neg := TRUE; .) ]
- 1996        Term<t, q>                        (. IF neg THEN
- 1997                                             IF QbeGen.IsImm(q) THEN
- 1998                                               QbeGen.NegFold(q, q)
- 1999                                             ELSE QbeGen.NewTemp(qt);
- 2000                                               QbeGen.NegQ(q, qt,
- 2001                                                 SymTab.ClassOf(t)
- 2002                                                 = SymTab.ClReal);
- 2003                                               QbeGen.CopyOp(qt, q)
- 2004                                             END
- 2005                                           END; .)
- 2006        { AddOp<op>                       (. IF op = SymTab.OpOr THEN
- 2007                                               QbeGen.DelayBegin END; .)
- 2008          Term<t2, q2>                     (. IF op = SymTab.OpOr THEN
- 2009                                               QbeGen.DelayEnd END; .)
- 2010          (. IF op = SymTab.OpOr THEN
- 2011               (* short-circuit: if q is true the RHS is skipped *)
- 2012               IF SymTab.BoolCheck(t) AND SymTab.BoolCheck(t2) THEN
- 2013                 t := SymTab.BoolType()
- 2014               ELSE SemError(212); t := SymTab.InvalidType END;
- 2015               IF t # SymTab.InvalidType THEN
- 2016                 QbeGen.Slot4(qs);
- 2017                 QbeGen.NewLabel(lTrue);
- 2018                 QbeGen.NewLabel(lNext);
- 2019                 QbeGen.NewLabel(lDone);
- 2020                 QbeGen.Jnz(q, lTrue, lNext);
- 2021                 QbeGen.EmitLabel(lTrue);
- 2022                 QbeGen.StoreW(qs, "1");
- 2023                 QbeGen.Jmp(lDone);
- 2024                 QbeGen.EmitLabel(lNext);
- 2025                 QbeGen.DelayFlush;
- 2026                 QbeGen.StoreW(qs, q2);
- 2027                 QbeGen.Jmp(lDone);
- 2028                 QbeGen.EmitLabel(lDone);
- 2029                 QbeGen.LoadW(qs, qr);
- 2030                 QbeGen.CopyOp(qr, q)
- 2031               ELSE QbeGen.DelayFlush; QbeGen.CopyOp("0", q)
- 2032               END
- 2033             ELSIF (op = SymTab.OpAdd)
- 2034               AND (SymTab.UStrCompat(t, t2)
- 2035                 OR ((SymTab.ClassOf(t) = SymTab.ClUChar)
- 2036                   AND (SymTab.ClassOf(t2) = SymTab.ClUStr))
- 2037                 OR ((SymTab.ClassOf(t) = SymTab.ClUStr)
- 2038                   AND (SymTab.ClassOf(t2) = SymTab.ClUChar))
- 2039                 OR ((SymTab.ClassOf(t) = SymTab.ClUChar)
- 2040                   AND (SymTab.ClassOf(t2) = SymTab.ClUChar))) THEN
- 2041               (* UString concatenation: a UCHAR operand becomes a
- 2042                  1-codepoint UString; the result is a descriptor in the
- 2043                  shim's concat buffer.  Work on copies so neither
- 2044                  operand is clobbered. *)
- 2045               IF SymTab.ClassOf(t) = SymTab.ClUStr THEN
- 2046                 QbeGen.CopyOp(q, q2a)
- 2047               ELSE
- 2048                 QbeGen.UStrFrom(q, q2a)
- 2049               END;
- 2050               IF SymTab.ClassOf(t2) = SymTab.ClUStr THEN
- 2051                 QbeGen.UStrCat(q2a, q2, qt)
- 2052               ELSE
- 2053                 QbeGen.UStrFrom(q2, q2b);
- 2054                 QbeGen.UStrCat(q2a, q2b, qt)
- 2055               END;
- 2056               t := SymTab.NewUStr();
- 2057               QbeGen.CopyOp(qt, q)
- 2058             ELSIF (op = SymTab.OpAdd)
- 2059               AND (SymTab.StrCompat(t, t2)
- 2060                 OR (SymTab.IsStrType(t)
- 2061                   AND (SymTab.ClassOf(t2) = SymTab.ClChar))
- 2062                 OR ((SymTab.ClassOf(t) = SymTab.ClChar)
- 2063                   AND SymTab.IsStrType(t2))) THEN
- 2064               (* string concatenation; a CHAR operand becomes a
- 2065                  1-character string literal.  When both operands are
- 2066                  constants, fold to a single string literal so a
- 2067                  constructor element stays compile-time. *)
- 2068               QbeGen.StrFold(q, q2, SymTab.ClassOf(t), SymTab.ClassOf(t2),
- 2069                 qt, fok);
- 2070               IF NOT fok THEN
- 2071                 IF SymTab.StrCompat(t, t2) THEN
- 2072                   QbeGen.StrCat(q, q2, qt)
- 2073                 ELSIF SymTab.IsStrType(t) THEN
- 2074                   QbeGen.DeclCharStr(q2, qs);
- 2075                   QbeGen.StrCat(q, qs, qt)
- 2076                 ELSE
- 2077                   QbeGen.DeclCharStr(q, qs);
- 2078                   QbeGen.StrCat(qs, q2, qt)
- 2079                 END
- 2080               END;
- 2081               t := SymTab.NewStr();
- 2082               QbeGen.CopyOp(qt, q)
- 2083             ELSIF (t # SymTab.InvalidType) AND (t2 # SymTab.InvalidType)
- 2084   AND (SymTab.ClassOf(t) = SymTab.ClSet)
- 2085   AND (SymTab.ClassOf(t2) = SymTab.ClSet) THEN
- 2086               lw := SymTab.SetWords(t); rw := SymTab.SetWords(t2);
- 2087               mw := lw;
- 2088               IF rw > mw THEN mw := rw END;
- 2089               IF op = SymTab.OpAdd THEN
- 2090                 QbeGen.SetBinOp(0, q, q2, lw, rw, qt)
- 2091               ELSE
- 2092                 QbeGen.SetBinOp(2, q, q2, lw, rw, qt)
- 2093               END;
- 2094               t := SymTab.NewSet(
- 2095                      SymTab.NewSubR(0,
- 2096                        VAL(INTEGER, mw) * 32 - 1));
- 2097               QbeGen.CopyOp(qt, q)
- 2098             ELSE
- 2099               IF SymTab.ArithCheck(t, t2, FALSE, res2) THEN
- 2100                 lt := t; rt := t2; t := res2
- 2101               ELSE SemError(211); t := SymTab.InvalidType END;
- 2102               IF t # SymTab.InvalidType THEN
- 2103                 isL := SymTab.IsLongFamily(t);
- 2104                 isR := SymTab.ClassOf(t) = SymTab.ClReal;
- 2105                 folded := FALSE;
- 2106                 IF (NOT isL) AND (NOT isR)
- 2107    AND QbeGen.IsImm(q) AND QbeGen.IsImm(q2) THEN
- 2108                   IF op = SymTab.OpAdd THEN
- 2109                     folded := QbeGen.Fold2(0, q, q2, qf)
- 2110                   ELSE
- 2111                     folded := QbeGen.Fold2(1, q, q2, qf)
- 2112                   END
- 2113                 END;
- 2114                 IF folded THEN QbeGen.CopyOp(qf, q)
- 2115                 ELSE
- 2116                 IF isL THEN
- 2117                   IF SymTab.IsIntFamily(lt) THEN
- 2118                     QbeGen.WidenLong(q, wq); QbeGen.CopyOp(wq, q)
- 2119                   END;
- 2120                   IF SymTab.IsIntFamily(rt) THEN
- 2121                     QbeGen.WidenLong(q2, wq); QbeGen.CopyOp(wq, q2)
- 2122                   END;
- 2123                   QbeGen.NewTemp(qt);
- 2124                   IF op = SymTab.OpAdd THEN
- 2125                     QbeGen.Op3L("add", qt, q, q2)
- 2126                   ELSE
- 2127                     QbeGen.Op3L("sub", qt, q, q2)
- 2128                   END
- 2129                 ELSE
- 2130                   QbeGen.NewTemp(qt);
- 2131                   IF op = SymTab.OpAdd THEN
- 2132                     QbeGen.Op3("add", qt, q, q2, isR)
- 2133                   ELSE
- 2134                     QbeGen.Op3("sub", qt, q, q2, isR)
- 2135                   END
- 2136                 END;
- 2137                 QbeGen.CopyOp(qt, q)
- 2138                 END
- 2139               ELSE QbeGen.CopyOp("0", q)
- 2140               END
- 2141             END; .) } .
- 2142    AddOp<VAR op: INTEGER>
- 2143      = "+"                               (. op := SymTab.OpAdd; .)
- 2144      | "-"                               (. op := SymTab.OpSub; .)
- 2145      | "OR"                              (. op := SymTab.OpOr; .) .
- 2146    Term<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 2147                                          (. VAR t2, res2, lt, rt:
- 2148                                                 SymTab.TypeIndex;
- 2149                                               op: INTEGER;
- 2150                                               q2, qt, wq, qf:
- 2151                                                 QbeGen.QVal;
- 2152                                               isR, isL, folded: BOOLEAN;
- 2153                                               lw, rw, mw: CARDINAL;
- 2154                                               lNext, lFalse, lDone, qr, qs:
- 2155                                                 QbeGen.QVal; .)
- 2156      = Fact<t, q> { MulOp<op>            (. IF op = SymTab.OpAnd THEN
- 2157                                               QbeGen.DelayBegin END; .)
- 2158          Fact<t2, q2>                    (. IF op = SymTab.OpAnd THEN
- 2159                                               QbeGen.DelayEnd END; .)
- 2160        (. IF op = SymTab.OpAnd THEN
- 2161             (* short-circuit: if q is false the RHS is skipped *)
- 2162             IF SymTab.BoolCheck(t) AND SymTab.BoolCheck(t2) THEN
- 2163               t := SymTab.BoolType()
- 2164             ELSE SemError(212); t := SymTab.InvalidType END;
- 2165             IF t # SymTab.InvalidType THEN
- 2166               QbeGen.Slot4(qs);
- 2167               QbeGen.NewLabel(lNext);
- 2168               QbeGen.NewLabel(lFalse);
- 2169               QbeGen.NewLabel(lDone);
- 2170               QbeGen.Jnz(q, lNext, lFalse);
- 2171               QbeGen.EmitLabel(lNext);
- 2172               QbeGen.DelayFlush;
- 2173               QbeGen.StoreW(qs, q2);
- 2174               QbeGen.Jmp(lDone);
- 2175               QbeGen.EmitLabel(lFalse);
- 2176               QbeGen.StoreW(qs, "0");
- 2177               QbeGen.Jmp(lDone);
- 2178               QbeGen.EmitLabel(lDone);
- 2179               QbeGen.LoadW(qs, qr);
- 2180               QbeGen.CopyOp(qr, q)
- 2181             ELSE QbeGen.DelayFlush; QbeGen.CopyOp("0", q)
- 2182             END
- 2183           ELSIF (t # SymTab.InvalidType) AND (t2 # SymTab.InvalidType)
- 2184   AND (SymTab.ClassOf(t) = SymTab.ClSet)
- 2185   AND (SymTab.ClassOf(t2) = SymTab.ClSet) THEN
- 2186             lw := SymTab.SetWords(t); rw := SymTab.SetWords(t2);
- 2187             mw := lw;
- 2188             IF rw > mw THEN mw := rw END;
- 2189             IF op = SymTab.OpTimes THEN
- 2190               QbeGen.SetBinOp(1, q, q2, lw, rw, qt)
- 2191             ELSE
- 2192               QbeGen.SetBinOp(3, q, q2, lw, rw, qt)
- 2193             END;
- 2194             t := SymTab.NewSet(
- 2195                    SymTab.NewSubR(0,
- 2196                      VAL(INTEGER, mw) * 32 - 1));
- 2197             QbeGen.CopyOp(qt, q)
- 2198           ELSE
- 2199             IF SymTab.ArithCheck(t, t2,
- 2200                  (op = SymTab.OpDiv) OR (op = SymTab.OpMod),
- 2201                  res2) THEN
- 2202               lt := t; rt := t2; t := res2
- 2203             ELSE SemError(211); t := SymTab.InvalidType END;
- 2204             IF t # SymTab.InvalidType THEN
- 2205               isL := SymTab.IsLongFamily(t);
- 2206               isR := SymTab.ClassOf(t) = SymTab.ClReal;
- 2207               folded := FALSE;
- 2208               IF (NOT isL) AND (NOT isR)
- 2209    AND QbeGen.IsImm(q) AND QbeGen.IsImm(q2) THEN
- 2210                 IF op = SymTab.OpTimes THEN
- 2211                   folded := QbeGen.Fold2(2, q, q2, qf)
- 2212                 ELSIF op = SymTab.OpDiv THEN
- 2213                   folded := QbeGen.Fold2(3, q, q2, qf)
- 2214                 ELSIF op = SymTab.OpMod THEN
- 2215                   folded := QbeGen.Fold2(4, q, q2, qf)
- 2216                 END
- 2217               END;
- 2218               IF folded THEN QbeGen.CopyOp(qf, q)
- 2219               ELSE
- 2220               IF isL THEN
- 2221                 IF SymTab.IsIntFamily(lt) THEN
- 2222                   QbeGen.WidenLong(q, wq); QbeGen.CopyOp(wq, q)
- 2223                 END;
- 2224                 IF SymTab.IsIntFamily(rt) THEN
- 2225                   QbeGen.WidenLong(q2, wq); QbeGen.CopyOp(wq, q2)
- 2226                 END;
- 2227                 QbeGen.NewTemp(qt);
- 2228                 IF op = SymTab.OpTimes THEN
- 2229                   QbeGen.Op3L("mul", qt, q, q2)
- 2230                 ELSIF (op = SymTab.OpDiv)
- 2231                    OR (op = SymTab.OpSlash) THEN
- 2232                   QbeGen.Op3L("div", qt, q, q2)
- 2233                 ELSE
- 2234                   QbeGen.Op3L("rem", qt, q, q2)
- 2235                 END
- 2236               ELSE
- 2237                 QbeGen.NewTemp(qt);
- 2238                 IF op = SymTab.OpTimes THEN
- 2239                   QbeGen.Op3("mul", qt, q, q2, isR)
- 2240                 ELSIF (op = SymTab.OpDiv)
- 2241                    OR (op = SymTab.OpSlash) THEN
- 2242                   QbeGen.Op3("div", qt, q, q2, isR)
- 2243                 ELSE
- 2244                   QbeGen.Op3("rem", qt, q, q2, isR)
- 2245                 END
- 2246               END;
- 2247               QbeGen.CopyOp(qt, q)
- 2248               END
- 2249             ELSE QbeGen.CopyOp("0", q)
- 2250             END
- 2251           END; .) } .
- 2252    MulOp<VAR op: INTEGER>
- 2253      = "*"                               (. op := SymTab.OpTimes; .)
- 2254      | "/"                               (. op := SymTab.OpSlash; .)
- 2255      | "DIV"                             (. op := SymTab.OpDiv; .)
- 2256      | "MOD"                             (. op := SymTab.OpMod; .)
- 2257      | ( "AND" | "&" )                   (. op := SymTab.OpAnd; .) .
- 2258    Fact<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 2259                                          (. VAR s: ARRAY [0 .. 255] OF CHAR;
- 2260                                               et, dt, t2, st, ct2:
- 2261                                                 SymTab.TypeIndex;
- 2262                                               dk: INTEGER;
- 2263                                               qd, q2, sq, qa, qm0, qr:
- 2264                                                 QbeGen.QVal;
- 2265                                               qn, vn: SymTab.Name;
- 2266                                               vt: SymTab.TypeIndex;
- 2267                                               c1, c2: INTEGER;
- 2268                                               lo, hi: INTEGER;
- 2269                                               isMax: BOOLEAN;
- 2270                                               called, isHigh, sfx, isCh,
- 2271                                               isU, uok, isStr: BOOLEAN;
- 2272                                               ucp: INTEGER; .)
- 2273      = integer                           (. LexString(s);
- 2274                                             QbeGen.NormInt(s, q);
- 2275                                             t := SymTab.IntType(); .)
- 2276      | charConst                         (. LexString(s);
- 2277                                             QbeGen.NormLit(s, q, isCh);
- 2278                                             t := SymTab.CharType(); .)
- 2279      | real                              (. LexString(s);
- 2280                                             QbeGen.NormReal(s, q);
- 2281                                             t := SymTab.RealType(); .)
- 2282      | string                            (. LexString(s);
- 2283                                             IF SymTab.StrLen(s) = 3 THEN
- 2284                                               t := SymTab.CharType();
- 2285                                               QbeGen.IntStr(
- 2286                                                 QbeGen.CharVal(s), q)
- 2287                                             ELSE t := SymTab.NewStr();
- 2288                                               QbeGen.DeclStr(s, q);
- 2289                                               (* a literal's value IS its
- 2290                                                  static descriptor address *)
- 2291                                               QbeGen.NoteAddr(q, q)
- 2292                                             END; .)
- 2293      | ustring                           (. LexString(s);
- 2294                                             QbeGen.DeclUStr(s, q, isU, ucp,
- 2295                                               uok);
- 2296                                             IF NOT uok THEN
- 2297                                               SemError(234);
- 2298                                               t := SymTab.InvalidType
- 2299                                             ELSIF isU THEN
- 2300                                               t := SymTab.UCharType();
- 2301                                               QbeGen.IntStr(ucp, q)
- 2302                                             ELSE
- 2303                                               t := SymTab.NewUStr();
- 2304                                               QbeGen.NoteAddr(q, q)
- 2305                                             END; .)
- 2306      | Design<dt, dk, qd, qn, sfx>       (. called := FALSE;
- 2307                                             t := dt;
- 2308                                             IF sfx THEN
- 2309                                               IF dt =
- 2310                                                  SymTab.InvalidType THEN
- 2311                                                 QbeGen.CopyOp("0", q)
- 2312                                               ELSIF (SymTab.ClassOf(dt) =
- 2313                                                      SymTab.ClRecord)
- 2314                                                  OR (SymTab.ClassOf(dt) =
- 2315                                                      SymTab.ClSet)
- 2316                                                  OR (SymTab.ClassOf(dt) =
- 2317                                                      SymTab.ClArray)
- 2318                                                  OR (SymTab.ClassOf(dt) =
- 2319                                                      SymTab.ClClass) THEN
- 2320                                                 QbeGen.CopyOp(qd, q)
- 2321                                               ELSE QbeGen.ElemLoad(qd, dt,
- 2322                                                 q)
- 2323                                               END
- 2324                                             ELSE QbeGen.CopyOp(qd, q)
- 2325                                             END;
- 2326                                             IF (dk = SymTab.KindVar)
- 2327                                                OR (dk = SymTab.KindParam)
- 2328                                                OR (dk =
- 2329                                                   SymTab.KindField) THEN
- 2330                                               IF sfx THEN
- 2331                                                 QbeGen.NoteAddr(q, qd)
- 2332                                               ELSE
- 2333                                                 QbeGen.AddrOf(qn, qa);
- 2334                                                 QbeGen.NoteAddr(q, qa)
- 2335                                               END
- 2336                                             ELSIF sfx
- 2337   AND (dt #
- 2338                                                   SymTab.InvalidType)
- 2339   AND ((SymTab.ClassOf(dt) =
- 2340                                                    SymTab.ClArray)
- 2341                                                   OR (SymTab.ClassOf(dt) =
- 2342                                                       SymTab.ClSet)
- 2343                                                   OR (SymTab.ClassOf(dt) =
- 2344                                                       SymTab.ClRecord)) THEN
- 2345                                               QbeGen.NoteAddr(qd, qd)
- 2346                                             END; .)
- 2347        [ TypedBraceLit<dt, q>            (. t := dt; .) ]
- 2348        [ ArgList<qn, dt, qd, TRUE, methCls, ct2, q2, called>
- 2349                                          (. t := ct2;
- 2350                                             QbeGen.CopyOp(q2, q); .) ]
- 2351                                          (. IF NOT called
- 2352   AND (dk = SymTab.KindProc) THEN
- 2353                                               (* bare zero-arg function
- 2354                                                  call (parentheses may be
- 2355                                                  omitted); a proper or
- 2356                                                  parameterised proc here
- 2357                                                  is 230 *)
- 2358                                               IF (SymTab.ProcNPar(qn) = 0)
- 2359   AND (SymTab.ProcRes(qn) #
- 2360                                                     SymTab.InvalidType) THEN
- 2361                                                 QbeGen.Mangled(qn,
- 2362                                                   SymTab.ProcUid(qn), qm0);
- 2363                                                 QbeGen.CallBegin(qm0,
- 2364                                                   SymTab.ProcRes(qn),
- 2365                                                   SymTab.ProcDepthOf(qn),
- 2366                                                   SymTab.IsExternal(qn));
- 2367                                                 QbeGen.CallEnd(TRUE, q);
- 2368                                                 t := SymTab.ProcRes(qn)
- 2369                                               ELSE
- 2370                                                 (* procedure used as a
- 2371                                                    value (assign to a
- 2372                                                    procedure variable):
- 2373                                                    its code address *)
- 2374                                                 t := SymTab.ProcTypeOf(qn);
- 2375                                                 QbeGen.Mangled(qn,
- 2376                                                   SymTab.ProcUid(qn), qm0);
- 2377                                                 QbeGen.ProcAddr(qm0, q)
- 2378                                               END
- 2379                                             END; .)
- 2380      | ( "HIGH"                          (. isHigh := TRUE; .)
- 2381        | ( "LEN" | "LENGTH" )            (. isHigh := FALSE; .) )
- 2382        "(" ( Design<dt, dk, qd, qn, sfx> (. isStr := FALSE;
- 2383                                              isU := FALSE; .)
- 2384            | string                       (. LexString(s);
- 2385                                              isStr := TRUE;
- 2386                                              isU := FALSE;
- 2387                                              IF SymTab.StrLen(s) = 3 THEN
- 2388                                                dt := SymTab.CharType();
- 2389                                                QbeGen.IntStr(QbeGen.CharVal(s),
- 2390                                                  qd)
- 2391                                              ELSE
- 2392                                                dt := SymTab.NewStr();
- 2393                                                QbeGen.DeclStr(s, qd);
- 2394                                                QbeGen.NoteAddr(qd, qd)
- 2395                                              END;
- 2396                                              dk := -1;
- 2397                                              qn[0] := CHR(0); .)
- 2398            | ustring                      (. LexString(s);
- 2399                                              QbeGen.DeclUStr(s, qd, isU, ucp,
- 2400                                                uok);
- 2401                                              isStr := FALSE;
- 2402                                              IF NOT uok THEN
- 2403                                                SemError(234);
- 2404                                                dt := SymTab.InvalidType
- 2405                                              ELSIF isU THEN
- 2406                                                (* one codepoint: a UCHAR;
- 2407                                                   LEN is 1, HIGH is 0 *)
- 2408                                                dt := SymTab.UCharType();
- 2409                                                QbeGen.IntStr(ucp, qd)
- 2410                                              ELSE
- 2411                                                dt := SymTab.NewUStr();
- 2412                                                QbeGen.NoteAddr(qd, qd)
- 2413                                              END;
- 2414                                              dk := -1;
- 2415                                              qn[0] := CHR(0); .) )
- 2416        ")"
- 2417                                          (. IF (dt # SymTab.InvalidType)
- 2418    AND (SymTab.ClassOf(dt) = SymTab.ClUStr) THEN
- 2419                                               (* UString: the count is
- 2420                                                  the descriptor header *)
- 2421                                               IF isHigh THEN
- 2422                                                 QbeGen.UStrLen(qd, qr);
- 2423                                                 QbeGen.DecQ(qr)
- 2424                                               ELSE
- 2425                                                 QbeGen.UStrLen(qd, qr)
- 2426                                               END;
- 2427                                               t := SymTab.IntType();
- 2428                                               QbeGen.CopyOp(qr, q)
- 2429                                             ELSIF (dt # SymTab.InvalidType)
- 2430    AND (SymTab.ClassOf(dt) = SymTab.ClUChar) THEN
- 2431                                               (* single UCHAR codepoint *)
- 2432                                               IF isHigh THEN
- 2433                                                 QbeGen.CopyOp("0", qr)
- 2434                                               ELSE
- 2435                                                 QbeGen.CopyOp("1", qr)
- 2436                                               END;
- 2437                                               t := SymTab.IntType();
- 2438                                               QbeGen.CopyOp(qr, q)
- 2439                                             ELSIF isStr THEN
- 2440                                               (* fold: content length at
- 2441                                                  compile time *)
- 2442                                               IF SymTab.StrLen(s) = 3 THEN
- 2443                                                 c1 := 1
- 2444                                               ELSE
- 2445                                                 c1 :=
- 2446                                                   SymTab.StrLen(s) - 2
- 2447                                               END;
- 2448                                               IF isHigh THEN
- 2449                                                 DEC(c1)
- 2450                                               END;
- 2451                                               QbeGen.IntStr(c1, qr);
- 2452                                               t := SymTab.IntType();
- 2453                                               QbeGen.CopyOp(qr, q)
- 2454                                             ELSIF dt = SymTab.InvalidType THEN
- 2455                                               t := SymTab.InvalidType;
- 2456                                               QbeGen.CopyOp("0", q)
- 2457                                             ELSIF SymTab.ClassOf(dt) #
- 2458                                                   SymTab.ClArray THEN
- 2459                                               SemError(217);
- 2460                                               t := SymTab.InvalidType;
- 2461                                               QbeGen.CopyOp("0", q)
- 2462                                             ELSE
- 2463                                               IF isHigh THEN
- 2464                                                 IF SymTab.IsOpenArray(dt) THEN
- 2465                                                   QbeGen.OpenHi(qd, qr)
- 2466                                                 ELSE
- 2467                                                   QbeGen.IntStr(
- 2468                                                     SymTab.ArrayHi(dt), qr)
- 2469                                                 END
- 2470                                               ELSE
- 2471                                                 IF SymTab.IsOpenArray(dt) THEN
- 2472                                                   QbeGen.LoadCount(qd, qr)
- 2473                                                 ELSE
- 2474                                                   QbeGen.IntStr(VAL(
- 2475                                                     INTEGER,
- 2476                                                     SymTab.ArrayLen(dt)),
- 2477                                                     qr)
- 2478                                                 END
- 2479                                               END;
- 2480                                                t := SymTab.IntType();
- 2481                                                QbeGen.CopyOp(qr, q)
- 2482                                              END; .)
- 2483      | ( "SIZE" | "TSIZE" ) "(" Design<dt, dk, qd, qn, sfx> ")"
- 2484                                          (. IF dt = SymTab.InvalidType THEN
- 2485                                               t := SymTab.InvalidType;
- 2486                                               QbeGen.CopyOp("0", q)
- 2487                                             ELSE
- 2488                                               QbeGen.IntStr(VAL(INTEGER,
- 2489                                                 SymTab.ObjectSize(dt)), q);
- 2490                                               t := SymTab.IntType()
- 2491                                             END; .)
- 2492      | ( "MIN"                           (. isMax := FALSE; .)
- 2493        | "MAX"                           (. isMax := TRUE; .) )
- 2494        "(" Design<dt, dk, qd, qn, sfx> ")"
- 2495                                          (. IF SymTab.TypeBounds(dt, lo, hi)
- 2496                                             THEN
- 2497                                               IF isMax THEN
- 2498                                                 QbeGen.IntStr(hi, q)
- 2499                                               ELSE QbeGen.IntStr(lo, q)
- 2500                                               END;
- 2501                                               t := SymTab.IntType()
- 2502                                             ELSE SemError(230);
- 2503                                               t := SymTab.InvalidType;
- 2504                                               QbeGen.CopyOp("0", q)
- 2505                                             END; .)
- 2506      | "ADR" "(" Design<dt, dk, qd, qn, sfx> ")"
- 2507                                          (. IF dt = SymTab.InvalidType THEN
- 2508                                               t := SymTab.InvalidType;
- 2509                                               QbeGen.CopyOp("0", q)
- 2510                                             ELSE
- 2511                                               IF sfx THEN
- 2512                                                 QbeGen.CopyOp(qd, q)
- 2513                                               ELSIF (dk = SymTab.KindVar)
- 2514                                                  OR (dk = SymTab.KindParam) THEN
- 2515                                                 QbeGen.AddrOf(qn, q)
- 2516                                               ELSE SemError(230);
- 2517                                                 QbeGen.CopyOp("0", q)
- 2518                                               END;
- 2519                                               t := SymTab.AddrType()
- 2520                                             END; .)
- 2521      | "CHR" "(" Expr<et, q> ")"
- 2522                                          (. IF (et # SymTab.InvalidType)
- 2523   AND NOT SymTab.IsIntFamily(et) THEN
- 2524                                               SemError(211) END;
- 2525                                             t := SymTab.CharType(); .)
- 2526      | ( "ORD" | "ORDL" ) "(" Expr<et, q> ")"
- 2527                                          (. IF et # SymTab.InvalidType THEN
- 2528                                               IF (SymTab.ClassOf(et) #
- 2529                                                   SymTab.ClChar)
- 2530   AND (SymTab.ClassOf(et) #
- 2531                                                     SymTab.ClBool)
- 2532   AND (SymTab.ClassOf(et) #
- 2533                                                     SymTab.ClEnum)
- 2534   AND NOT SymTab.IsIntFamily(et) THEN
- 2535                                                 SemError(211) END
- 2536                                             END;
- 2537                                             t := SymTab.IntType(); .)
- 2538      | "CAP" "(" Expr<et, q> ")"
- 2539                                          (. QbeGen.CapQ(q, qa);
- 2540                                             QbeGen.CopyOp(qa, q);
- 2541                                             t := SymTab.CharType(); .)
- 2542      | "UCHR" "(" Expr<et, q> ")"
- 2543                                          (. (* UCHR: the UCHAR constructor.
- 2544                                                CHAR -> UCHAR (identity);
- 2545                                                INTEGER familly -> UCHAR
- 2546                                                (codepoint value). *)
- 2547                                             IF (et # SymTab.InvalidType)
- 2548    AND (SymTab.ClassOf(et) # SymTab.ClChar)
- 2549    AND NOT SymTab.IsIntFamily(et) THEN
- 2550                                               SemError(211) END;
- 2551                                             t := SymTab.UCharType(); .)
- 2552      | "CHR8" "(" Expr<et, q> ")"
- 2553                                          (. IF (et # SymTab.InvalidType)
- 2554    AND (SymTab.ClassOf(et) # SymTab.ClUChar) THEN
- 2555                                               SemError(211) END;
- 2556                                             QbeGen.WidenLong(q, qa);
- 2557                                             QbeGen.CheckRange(qa, "0", "255");
- 2558                                             t := SymTab.CharType(); .)
- 2559      | "UORD" "(" Expr<et, q> ")"
- 2560                                          (. (* UORD(u): the codepoint as a
- 2561                                                32-bit ordinal (INTEGER),
- 2562                                                cf. ORD for CHAR. *)
- 2563                                             IF (et # SymTab.InvalidType)
- 2564    AND (SymTab.ClassOf(et) # SymTab.ClUChar) THEN
- 2565                                               SemError(211) END;
- 2566                                             t := SymTab.IntType(); .)
- 2567      | "ABS" "(" Expr<et, q> ")"
- 2568                                          (. IF (et # SymTab.InvalidType)
- 2569   AND NOT SymTab.IsIntFamily(et)
- 2570   AND (SymTab.ClassOf(et) #
- 2571                                                  SymTab.ClReal) THEN
- 2572                                               SemError(211)
- 2573                                             ELSE QbeGen.AbsQ(q, qa,
- 2574                                                    SymTab.ClassOf(et) =
- 2575                                                      SymTab.ClReal);
- 2576                                               QbeGen.CopyOp(qa, q)
- 2577                                             END;
- 2578                                             t := et; .)
- 2579      | "VAL" "(" GetIdent<vn> "," Expr<et, q> ")"
- 2580                                          (. IF NOT SymTab.Lookup(vn) THEN
- 2581                                               SemError(201);
- 2582                                               t := SymTab.InvalidType
- 2583                                             ELSE vt := SymTab.SymType(vn);
- 2584                                               IF vt = SymTab.InvalidType THEN
- 2585                                                 t := SymTab.InvalidType
- 2586                                               ELSIF et =
- 2587                                                  SymTab.InvalidType THEN
- 2588                                                 t := vt
- 2589                                               ELSE
- 2590                                                 c1 := SymTab.ClassOf(et);
- 2591                                                 c2 := SymTab.ClassOf(vt);
- 2592                                                 IF (c1 = SymTab.ClInt)
- 2593    AND (c2 = SymTab.ClLong) THEN
- 2594                                                   QbeGen.WidenLong(q, qa);
- 2595                                                   QbeGen.CopyOp(qa, q);
- 2596                                                   t := vt
- 2597                                                 ELSIF (c1 = SymTab.ClLong)
- 2598    AND (c2 = SymTab.ClInt) THEN
- 2599                                                   QbeGen.NarrowLong(q, qa);
- 2600                                                   QbeGen.CopyOp(qa, q);
- 2601                                                   t := vt
- 2602                                                 ELSIF (c1 = SymTab.ClInt)
- 2603    AND (c2 = SymTab.ClReal) THEN
- 2604                                                   QbeGen.ConvIR(q, qa);
- 2605                                                   QbeGen.CopyOp(qa, q);
- 2606                                                   t := vt
- 2607                                                 ELSIF (c1 = SymTab.ClLong)
- 2608    AND (c2 = SymTab.ClReal) THEN
- 2609                                                   QbeGen.ConvLR(q, qa);
- 2610                                                   QbeGen.CopyOp(qa, q);
- 2611                                                   t := vt
- 2612                                                 ELSIF (c1 = SymTab.ClReal)
- 2613    AND (c2 = SymTab.ClInt) THEN
- 2614                                                   QbeGen.ConvRI(q, qa);
- 2615                                                   QbeGen.CopyOp(qa, q);
- 2616                                                   t := vt
- 2617                                                 ELSIF (c1 = SymTab.ClReal)
- 2618    AND (c2 = SymTab.ClLong) THEN
- 2619                                                   QbeGen.ConvRL(q, qa);
- 2620                                                   QbeGen.CopyOp(qa, q);
- 2621                                                   t := vt
- 2622                                                 ELSIF ((c1 = SymTab.ClInt)
- 2623                                                     OR (c1 =
- 2624                                                        SymTab.ClChar)
- 2625                                                     OR (c1 =
- 2626                                                        SymTab.ClBool)
- 2627                                                     OR (c1 =
- 2628                                                        SymTab.ClEnum))
- 2629   AND ((c2 = SymTab.ClInt)
- 2630                                                     OR (c2 =
- 2631                                                        SymTab.ClChar)
- 2632                                                     OR (c2 =
- 2633                                                        SymTab.ClBool)
- 2634                                                     OR (c2 =
- 2635                                                        SymTab.ClEnum)) THEN
- 2636                                                   t := vt
- 2637                                                 ELSIF (c1 = SymTab.ClPtr)
- 2638   AND (c2 = SymTab.ClPtr) THEN
- 2639                                                   t := vt
- 2640                                                 ELSIF (c1 = SymTab.ClReal)
- 2641   AND (c2 = SymTab.ClReal) THEN
- 2642                                                   t := vt
- 2643                                                 ELSE SemError(230);
- 2644                                                   t := SymTab.InvalidType
- 2645                                                 END
- 2646                                               END
- 2647                                             END; .)
- 2648      | "(" Expr<et, q> ")"               (. t := et; .)
- 2649      | SetLit<st, sq>                    (. t := st;
- 2650                                             QbeGen.CopyOp(sq, q); .)
- 2651      | ( "NOT" | "~" ) Fact<t2, q2>      (. IF SymTab.BoolCheck(t2) THEN
- 2652                                               t := SymTab.BoolType()
- 2653                                             ELSE SemError(212);
- 2654                                               t := SymTab.InvalidType END;
- 2655                                             IF t # SymTab.InvalidType THEN
- 2656                                               QbeGen.NotQ(q2, q)
- 2657                                             ELSE QbeGen.CopyOp("0", q)
- 2658                                             END; .) .
- 2659    (* Set literals are SET OF [0..255] (8 words); elements validated
- 2660       0..255 statically when foldable (222 otherwise), runtime trap
- 2661       for computed elements. Ranges always lower via SetRange. *)
- 2662    SetLit<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 2663      = "{"                               (. t := SymTab.NewSet(
- 2664                                               SymTab.NewSubR(0, 255));
- 2665                                             QbeGen.NewSetTemp(8, q);
- 2666                                             QbeGen.SetZero(q, 8); .)
- 2667        [ SetElem<t, q> { "," SetElem<t, q> } ]
- 2668        "}" .
- 2669    (* Typed brace constructor: TypeName{ elems } — BITSET{0} (a set)
- 2670       or ArrayName{...} (an array constructor, GNU Modula-2).  The
- 2671       declared type sets the width (set) or element type (array). *)
- 2672    TypedBraceLit<vt: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 2673                                          (. VAR nw: CARDINAL;
- 2674                                               savedCls: INTEGER; .)
- 2675      = "{"                               (. savedCls := braceCls;
- 2676                                             IF vt = SymTab.InvalidType THEN
- 2677                                               braceCls := -1
- 2678                                             ELSE braceCls :=
- 2679                                                    SymTab.ClassOf(vt)
- 2680                                             END;
- 2681                                             IF braceCls = SymTab.ClSet THEN
- 2682                                               IF vt = SymTab.InvalidType THEN
- 2683                                                 nw := 8
- 2684                                               ELSE nw := SymTab.SetWords(vt);
- 2685                                                 IF nw = 0 THEN nw := 8 END
- 2686                                               END;
- 2687                                               QbeGen.NewSetTemp(nw, q);
- 2688                                               QbeGen.SetZero(q, nw)
- 2689                                             ELSIF (braceCls =
- 2690                                                    SymTab.ClArray)
- 2691                                                OR (braceCls =
- 2692                                                    SymTab.ClRecord)
- 2693                                                OR (braceCls =
- 2694                                                    SymTab.ClClass) THEN
- 2695                                               QbeGen.CtorBegin(vt)
- 2696                                             ELSE
- 2697                                               IF vt # SymTab.InvalidType THEN
- 2698                                                 SemError(230) END;
- 2699                                               braceCls := -1
- 2700                                             END; .)
- 2701        [ BraceElem<vt, q> { "," BraceElem<vt, q> } ]
- 2702        "}"                               (. IF (braceCls = SymTab.ClArray)
- 2703                                                OR (braceCls =
- 2704                                                   SymTab.ClRecord)
- 2705                                                OR (braceCls =
- 2706                                                   SymTab.ClClass) THEN
- 2707                                               QbeGen.CtorEnd(q)
- 2708                                             ELSIF braceCls # SymTab.ClSet THEN
- 2709                                               QbeGen.CopyOp("0", q)
- 2710                                             END;
- 2711                                             braceCls := savedCls; .) .
- 2712    BraceElem<vt: SymTab.TypeIndex; VAR sq: QbeGen.QVal>
- 2713                                          (. VAR et, et2: SymTab.TypeIndex;
- 2714                                               qe, q2: QbeGen.QVal;
- 2715                                               v, v2, reps, k: INTEGER;
- 2716                                               elem: SymTab.TypeIndex;
- 2717                                               lo: INTEGER;
- 2718                                               span: CARDINAL;
- 2719                                               cl, cl2: INTEGER;
- 2720                                               hasR, hasB: BOOLEAN; .)
- 2721      =                                   (. hasR := FALSE; hasB := FALSE; .)
- 2722        Expr<et, qe>
- 2723        [ ".." Expr<et2, q2>              (. hasR := TRUE; .) ]
- 2724        [ "BY" Expr<et2, q2>              (. hasB := TRUE; .) ]
- 2725                                          (. IF braceCls = SymTab.ClSet THEN
- 2726                                               IF hasB THEN SemError(230) END;
- 2727                                               lo := SymTab.SetBaseLo(vt);
- 2728                                               span := SymTab.SetCount(vt);
- 2729                                               IF (et = SymTab.InvalidType)
- 2730                                                  OR (hasR AND (et2 =
- 2731                                                     SymTab.InvalidType)) THEN
- 2732                                               ELSE cl :=
- 2733                                                      SymTab.ClassOf(et);
- 2734                                                 IF hasR THEN
- 2735                                                   cl2 :=
- 2736                                                     SymTab.ClassOf(et2)
- 2737                                                 ELSE cl2 := SymTab.ClInt
- 2738                                                 END;
- 2739                                                 IF ((cl # SymTab.ClInt)
- 2740   AND (cl # SymTab.ClChar)
- 2741   AND (cl # SymTab.ClBool))
- 2742                                                    OR (hasR AND 
- 2743                                                       ((cl2
- 2744                                                         # SymTab.ClInt)
- 2745   AND (cl2
- 2746                                                          # SymTab.ClChar)
- 2747   AND (cl2
- 2748                                                          # SymTab.ClBool))) THEN
- 2749                                                   SemError(222)
- 2750                                                 ELSIF hasR
- 2751   AND SymTab.ConstInt(qe, v)
- 2752   AND SymTab.ConstInt(q2,
- 2753                                                       v2)
- 2754   AND ((v < lo)
- 2755                                                       OR (v2 < lo)
- 2756                                                       OR (v >= lo +
- 2757                                                          VAL(INTEGER, span))
- 2758                                                       OR (v2 >= lo +
- 2759                                                          VAL(INTEGER, span))
- 2760                                                       OR (v > v2)) THEN
- 2761                                                   SemError(222)
- 2762                                                  ELSIF hasR THEN
- 2763                                                    QbeGen.SetRange(sq, qe, q2,
- 2764                                                      lo, span)
- 2765                                                  ELSIF SymTab.ConstInt(qe,
- 2766                                                          v)
- 2767   AND ((v < lo)
- 2768                                                        OR (v >= lo +
- 2769                                                           VAL(INTEGER,
- 2770                                                             span))) THEN
- 2771                                                    SemError(222)
- 2772                                                  ELSE QbeGen.SetBit(sq, qe,
- 2773                                                    lo, span)
- 2774                                                 END
- 2775                                               END
- 2776                                             ELSIF (braceCls = SymTab.ClArray)
- 2777                                                OR (braceCls =
- 2778                                                   SymTab.ClRecord)
- 2779                                                OR (braceCls =
- 2780                                                   SymTab.ClClass) THEN
- 2781                                               IF hasR THEN SemError(230) END;
- 2782                                               reps := 1;
- 2783                                               IF hasB THEN
- 2784                                                 IF SymTab.ConstInt(q2, v2)
- 2785   AND (v2 >= 1) THEN
- 2786                                                   reps := v2
- 2787                                                 ELSE SemError(230)
- 2788                                                 END
- 2789                                               END;
- 2790                                               k := 0;
- 2791                                               WHILE k < reps DO
- 2792                                                 QbeGen.CtorElem(qe);
- 2793                                                 INC(k)
- 2794                                               END
- 2795                                             END; .) .
- 2796    SetElem<st: SymTab.TypeIndex; sq: QbeGen.QVal>         (. VAR et, et2: SymTab.TypeIndex;
- 2797                                               qe, q2: QbeGen.QVal;
- 2798                                               v, v2: INTEGER;
- 2799                                               lo: INTEGER;
- 2800                                               span: CARDINAL;
- 2801                                               cl, cl2: INTEGER;
- 2802                                               hasR: BOOLEAN; .)
- 2803      =                                   (. hasR := FALSE; .)
- 2804        Expr<et, qe>
- 2805        [ ".." Expr<et2, q2>              (. hasR := TRUE; .) ]
- 2806                                          (. lo := SymTab.SetBaseLo(st);
- 2807                                             span := SymTab.SetCount(st);
- 2808                                             IF (et = SymTab.InvalidType)
- 2809                                                OR (hasR AND (et2 =
- 2810                                                   SymTab.InvalidType)) THEN
- 2811                                             ELSE cl :=
- 2812                                                    SymTab.ClassOf(et);
- 2813                                               IF hasR THEN
- 2814                                                 cl2 :=
- 2815                                                   SymTab.ClassOf(et2)
- 2816                                               ELSE cl2 := SymTab.ClInt
- 2817                                               END;
- 2818                                               IF ((cl # SymTab.ClInt)
- 2819   AND (cl # SymTab.ClChar)
- 2820   AND (cl # SymTab.ClBool))
- 2821                                                  OR (hasR AND 
- 2822                                                     ((cl2
- 2823                                                       # SymTab.ClInt)
- 2824   AND (cl2
- 2825                                                        # SymTab.ClChar)
- 2826   AND (cl2
- 2827                                                        # SymTab.ClBool))) THEN
- 2828                                                 SemError(222)
- 2829                                               ELSIF hasR
- 2830   AND SymTab.ConstInt(qe, v)
- 2831   AND SymTab.ConstInt(q2,
- 2832                                                     v2)
- 2833   AND ((v < lo)
- 2834                                                     OR (v2 < lo)
- 2835                                                     OR (v >= lo +
- 2836                                                        VAL(INTEGER, span))
- 2837                                                     OR (v2 >= lo +
- 2838                                                        VAL(INTEGER, span))
- 2839                                                     OR (v > v2)) THEN
- 2840                                                 SemError(222)
- 2841                                                ELSIF hasR THEN
- 2842                                                  QbeGen.SetRange(sq, qe, q2,
- 2843                                                    lo, span)
- 2844                                                ELSIF SymTab.ConstInt(qe,
- 2845                                                        v)
- 2846   AND ((v < lo)
- 2847                                                      OR (v >= lo +
- 2848                                                         VAL(INTEGER,
- 2849                                                           span))) THEN
- 2850                                                  SemError(222)
- 2851                                                ELSE QbeGen.SetBit(sq, qe,
- 2852                                                  lo, span)
- 2853                                               END
- 2854                                             END; .) .
- 2855    GetIdent<VAR n: SymTab.Name>
- 2856      = ident                             (. LexName(n); .) .
- 2857  
- 2858  END M2.
+ 1510   AND NOT SymTab.IsIntFamily(tby) THEN
+ 1511                                               SemError(220); ok := FALSE
+ 1512                                             END;
+ 1513                                             IF NOT SymTab.ConstInt(qby, by) THEN
+ 1514                                               SemError(230); by := 1
+ 1515                                             ELSIF by = 0 THEN
+ 1516                                               SemError(220); by := 1
+ 1517                                             END; .) ]
+ 1518        "DO"                              (. IF ok THEN
+ 1519                                               QbeGen.StoreVar(lv, qlo,
+ 1520                                                 FALSE) END;
+ 1521                                             QbeGen.NewLabel(lTop);
+ 1522                                             QbeGen.NewLabel(lBody);
+ 1523                                             QbeGen.NewLabel(lEnd);
+ 1524                                             QbeGen.EmitLabel(lTop);
+ 1525                                             QbeGen.LoadVar(lv, FALSE, qt);
+ 1526                                             QbeGen.NewTemp(qk);
+ 1527                                             IF by > 0 THEN
+ 1528                                               QbeGen.Op3("cslew", qk,
+ 1529                                                 qt, qhi, FALSE)
+ 1530                                             ELSE QbeGen.Op3("csgew", qk,
+ 1531                                               qt, qhi, FALSE)
+ 1532                                             END;
+ 1533                                             QbeGen.Jnz(qk, lBody, lEnd);
+ 1534                                             QbeGen.EmitLabel(lBody); .)
+ 1535        [ StatSeq ]
+ 1536        "END"                             (. IF ok THEN
+ 1537                                               QbeGen.LoadVar(lv, FALSE,
+ 1538                                                 qt);
+ 1539                                               QbeGen.IntStr(by, qb);
+ 1540                                               QbeGen.NewTemp(qk);
+ 1541                                               QbeGen.Op3("add", qk,
+ 1542                                                 qt, qb, FALSE);
+ 1543                                               QbeGen.StoreVar(lv, qk,
+ 1544                                                 FALSE) END;
+ 1545                                             QbeGen.Jmp(lTop);
+ 1546                                             QbeGen.EmitLabel(lEnd); .) .
+ 1547    CaseStat                              (. VAR tsel: SymTab.TypeIndex;
+ 1548                                               qsel, lEnd: QbeGen.QVal; .)
+ 1549      = "CASE" Expr<tsel, qsel>           (. QbeGen.NewLabel(lEnd); .)
+ 1550        "OF" CaseAlt<tsel, qsel, lEnd>
+ 1551        { "|" CaseAlt<tsel, qsel, lEnd> }
+ 1552        [ "ELSE" [ StatSeq ] ]
+ 1553        "END"                             (. QbeGen.EmitLabel(lEnd); .) .
+ 1554    (* Compare-chain lowering: each alternative ends its match-tests
+ 1555       with "jmp lAfter", so the no-match fallthrough skips the body:
+ 1556       "cmp; jnz(lBody,lF); lF: ... ; jmp lAfter; lBody: S; jmp lEnd;
+ 1557       lAfter:". Falls into the next alternative, ELSE, or END. *)
+ 1558    CaseAlt<tsel: SymTab.TypeIndex; qsel: QbeGen.QVal;
+ 1559            lEnd: QbeGen.QVal>            (. VAR lBody, lAfter: QbeGen.QVal; .)
+ 1560      =                                   (. QbeGen.NewLabel(lBody);
+ 1561                                             QbeGen.NewLabel(lAfter); .)
+ 1562        CaseLabel<tsel, qsel, lBody>
+ 1563        { "," CaseLabel<tsel, qsel, lBody> }
+ 1564        ":"                               (. QbeGen.Jmp(lAfter);
+ 1565                                             QbeGen.EmitLabel(lBody); .)
+ 1566        [ StatSeq ]                       (. QbeGen.Jmp(lEnd);
+ 1567                                             QbeGen.EmitLabel(lAfter); .) .
+ 1568    CaseLabel<tsel: SymTab.TypeIndex; qsel: QbeGen.QVal;
+ 1569              lBody: QbeGen.QVal>         (. VAR t2, t3: SymTab.TypeIndex;
+ 1570                                               q2, q3, qc, qd, qe:
+ 1571                                                 QbeGen.QVal;
+ 1572                                               lNext: QbeGen.QVal; .)
+ 1573      = Expr<t2, q2>                      (. IF (t2 #
+ 1574                                               SymTab.InvalidType)
+ 1575   AND (tsel #
+ 1576                                                 SymTab.InvalidType)
+ 1577   AND ((SymTab.ClassOf(t2) =
+ 1578                                                  SymTab.ClSet)
+ 1579                                                 OR (SymTab.ClassOf(tsel) =
+ 1580                                                     SymTab.ClSet)) THEN
+ 1581                                               SemError(230)
+ 1582                                             ELSIF (t2 #
+ 1583                                               SymTab.InvalidType)
+ 1584   AND (tsel #
+ 1585                                                 SymTab.InvalidType)
+ 1586   AND NOT SymTab.EqCheck(t2,
+ 1587                                                   tsel) THEN
+ 1588                                               SemError(213) END;
+ 1589                                             IF NOT QbeGen.IsImm(q2) THEN
+ 1590                                               SemError(230);
+ 1591                                               QbeGen.CopyOp("0", q2)
+ 1592                                             END;
+ 1593                                             QbeGen.NewLabel(lNext);
+ 1594                                             QbeGen.Cmp(SymTab.OpEq,
+ 1595                                               qsel, q2, qc, FALSE);
+ 1596                                             QbeGen.Jnz(qc, lBody, lNext);
+ 1597                                             QbeGen.EmitLabel(lNext); .)
+ 1598        [ ".." Expr<t3, q3>               (. IF (t3 #
+ 1599                                               SymTab.InvalidType)
+ 1600   AND (tsel #
+ 1601                                                 SymTab.InvalidType)
+ 1602   AND NOT SymTab.EqCheck(t3,
+ 1603                                                   tsel) THEN
+ 1604                                               SemError(213) END;
+ 1605                                             IF NOT QbeGen.IsImm(q3) THEN
+ 1606                                               SemError(230);
+ 1607                                               QbeGen.CopyOp("0", q3)
+ 1608                                             END;
+ 1609                                             QbeGen.Cmp(SymTab.OpGe,
+ 1610                                               qsel, q2, qc, FALSE);
+ 1611                                             QbeGen.Cmp(SymTab.OpLe,
+ 1612                                               qsel, q3, qd, FALSE);
+ 1613                                             QbeGen.NewTemp(qe);
+ 1614                                             QbeGen.Op3("and", qe, qc, qd,
+ 1615                                               FALSE);
+ 1616                                             QbeGen.NewLabel(lNext);
+ 1617                                             QbeGen.Jnz(qe, lBody, lNext);
+ 1618                                             QbeGen.EmitLabel(lNext); .) ] .
+ 1619    ReturnStat                            (. VAR t: SymTab.TypeIndex;
+ 1620                                               q, qt: QbeGen.QVal;
+ 1621                                               res: SymTab.TypeIndex;
+ 1622                                               hadE, conv: BOOLEAN; .)
+ 1623      = "RETURN"                          (. hadE := FALSE; .)
+ 1624        [ Expr<t, q>                      (. hadE := TRUE; .) ]
+ 1625                                          (. conv := FALSE;
+ 1626                                             IF NOT SymTab.InProc() THEN
+ 1627                                               SemError(232)
+ 1628                                             ELSE res := SymTab.CurRes();
+ 1629                                               IF NOT hadE THEN
+ 1630                                                 IF res #
+ 1631                                                    SymTab.InvalidType THEN
+ 1632                                                   SemError(232)
+ 1633                                                 ELSE QbeGen.EmitRet(q,
+ 1634                                                   FALSE)
+ 1635                                                 END
+ 1636                                               ELSIF (res =
+ 1637                                                      SymTab.InvalidType)
+ 1638                                                  OR (t #
+ 1639                                                      SymTab.InvalidType)
+ 1640   AND NOT SymTab.Assignable(t,
+ 1641                                                       res) THEN
+ 1642                                                 SemError(232)
+ 1643                                               ELSE
+ 1644                                                 conv := (SymTab.ClassOf(
+ 1645                                                   res) = SymTab.ClReal)
+ 1646   AND SymTab.IsIntFamily(t);
+ 1647                                                 IF conv THEN
+ 1648                                                   QbeGen.ConvIR(q, qt);
+ 1649                                                   QbeGen.EmitRet(qt, TRUE)
+ 1650                                                 ELSE QbeGen.EmitRet(q, TRUE)
+ 1651                                                 END
+ 1652                                               END
+ 1653                                             END; .) .
+ 1654    HaltStat                              (. VAR t: SymTab.TypeIndex;
+ 1655                                               q: QbeGen.QVal; .)
+ 1656      = "HALT" [ "(" Expr<t, q> ")" ]     (. QbeGen.HaltQ; .) .
+ 1657    (* Designator: scalar loads, array addresses, and index suffixes.
+ 1658       Each index descends one level (bounds-checked, trap on breach);
+ 1659       nested levels reload the inner descriptor address. q ends as the
+ 1660       value (scalars), the descriptor address (plain arrays), or the
+ 1661       element address (indexed); sfx marks the indexed form. *)
+ 1662    Design<VAR t: SymTab.TypeIndex; VAR k: INTEGER;
+ 1663           VAR q: QbeGen.QVal; VAR qn: SymTab.Name; VAR sfx: BOOLEAN>
+ 1664                                          (. VAR n, fn, mal: SymTab.Name;
+ 1665                                               cls: INTEGER;
+ 1666                                               ic: SymTab.TypeIndex;
+ 1667                                               curT, it, eT, bt:
+ 1668                                                 SymTab.TypeIndex;
+ 1669                                               iq, ql, qlo, qhi, qe:
+ 1670                                                 QbeGen.QVal;
+ 1671                                               lo, hi: INTEGER;
+ 1672                                               fo: INTEGER;
+ 1673                                               isOpen: BOOLEAN;
+ 1674                                               qb, cv: QbeGen.QVal; .)
+ 1675      = GetIdent<n>                       (. methCls := SymTab.InvalidType;
+ 1676                                             QbeGen.CopyOp(n, qn);
+ 1677                                             sfx := FALSE;
+ 1678                                             IF NOT SymTab.Lookup(n) THEN
+ 1679                                               (* a bare method name inside
+ 1680                                                  a CLASS IMPLEMENTATION
+ 1681                                                  is a sibling call on
+ 1682                                                  THIS *)
+ 1683                                               ic := SymTab.CurImplClass();
+ 1684                                               IF (ic #
+ 1685                                                  SymTab.InvalidType)
+ 1686    AND SymTab.MethodExists(ic, n) THEN
+ 1687                                                 sfx := FALSE;
+ 1688                                                 QbeGen.ThisBase(q);
+ 1689                                                 QbeGen.ArmRecv(q);
+ 1690                                                 methCls := ic;
+ 1691                                                 k := SymTab.KindProc;
+ 1692                                                 t := SymTab.InvalidType
+ 1693                                               ELSE
+ 1694                                                 SemError(201);
+ 1695                                                 t :=
+ 1696                                                   SymTab.InvalidType;
+ 1697                                                 k := -1;
+ 1698                                                 QbeGen.CopyOp("0", q)
+ 1699                                               END
+ 1700                                             ELSE
+ 1701                                               t := SymTab.SymType(n);
+ 1702                                               k := SymTab.SymKind(n);
+ 1703                                               IF k = SymTab.KindConst THEN
+ 1704                                                 IF SymTab.Equal(n,
+ 1705                                                    "TRUE") THEN
+ 1706                                                   t := SymTab.BoolType();
+ 1707                                                   QbeGen.CopyOp("1", q)
+ 1708                                                 ELSIF SymTab.Equal(n,
+ 1709                                                    "FALSE") THEN
+ 1710                                                   t := SymTab.BoolType();
+ 1711                                                   QbeGen.CopyOp("0", q)
+ 1712                                                 ELSIF SymTab.Equal(n,
+ 1713                                                    "NIL") THEN
+ 1714                                                   QbeGen.CopyOp("0", q)
+ 1715                                                 ELSE
+ 1716                                                   cls :=
+ 1717                                                     SymTab.ClassOf(t);
+ 1718                                                   IF (t #
+ 1719                                                       SymTab.InvalidType)
+ 1720   AND ((cls = SymTab.ClInt)
+ 1721                                                      OR (cls
+ 1722                                                          = SymTab.ClChar)
+ 1723                                                      OR (cls
+ 1724                                                          = SymTab.ClEnum)
+ 1725                                                      OR (cls
+ 1726                                                          = SymTab.ClReal)
+ 1727                                                      OR (cls
+ 1728                                                          = SymTab.ClLong)
+ 1729                                                      OR (cls
+ 1730                                                          = SymTab.ClNil)) THEN
+ 1731                                                     IF cls = SymTab.ClNil THEN
+ 1732                                                       QbeGen.CopyOp("0", q)
+ 1733                                                     ELSIF ((cls
+ 1734                                                         = SymTab.ClInt)
+ 1735                                                       OR (cls
+ 1736                                                         = SymTab.ClChar)
+ 1737                                                       OR (cls
+ 1738                                                         = SymTab.ClEnum)
+ 1739                                                       OR (cls
+ 1740                                                         = SymTab.ClLong))
+ 1741    AND SymTab.GetSymVal(n, cv)
+ 1742    AND QbeGen.IsImm(cv) THEN
+ 1743                                                       QbeGen.CopyOp(cv, q)
+ 1744                                                     ELSE
+ 1745                                                       QbeGen.LoadVar(n,
+ 1746                                                         cls = SymTab.ClReal,
+ 1747                                                         q)
+ 1748                                                     END
+ 1749                                                   ELSIF (cls = SymTab.ClArray)
+ 1750                                                      OR (cls = SymTab.ClRecord)
+ 1751                                                      OR (cls = SymTab.ClClass)
+ 1752                                                      OR (cls = SymTab.ClStr)
+ 1753                                                      OR (cls = SymTab.ClUStr) THEN
+ 1754                                                     (* aggregate constant:
+ 1755                                                        its value IS the
+ 1756                                                        descriptor address *)
+ 1757                                                     IF SymTab.GetSymVal(n, cv) THEN
+ 1758                                                       QbeGen.CopyOp(cv, q)
+ 1759                                                     ELSE
+ 1760                                                       QbeGen.CopyOp("0", q)
+ 1761                                                     END
+ 1762                                                   ELSE
+ 1763                                                     IF t #
+ 1764                                                        SymTab.InvalidType THEN
+ 1765                                                       SemError(230)
+ 1766                                                     END;
+ 1767                                                     QbeGen.CopyOp("0", q)
+ 1768                                                   END
+ 1769                                                 END
+ 1770                                               ELSIF (k = SymTab.KindVar)
+ 1771                                                  OR (k = SymTab.KindParam) THEN
+ 1772                                                 cls :=
+ 1773                                                   SymTab.ClassOf(t);
+ 1774                                                 IF (cls = SymTab.ClInt)
+ 1775                                                    OR (cls = SymTab.ClBool)
+ 1776                                                    OR (cls = SymTab.ClChar)
+ 1777                                                    OR (cls = SymTab.ClUChar)
+ 1778                                                    OR (cls = SymTab.ClEnum)
+ 1779                                                    OR (cls
+ 1780                                                        = SymTab.ClReal) THEN
+ 1781                                                   QbeGen.LoadVar(n,
+ 1782                                                     cls = SymTab.ClReal, q)
+ 1783                                                 ELSIF (cls = SymTab.ClPtr)
+ 1784                                                    OR (cls = SymTab.ClProc) THEN
+ 1785                                                   QbeGen.LoadPtr(n, q)
+ 1786                                                 ELSIF cls = SymTab.ClLong THEN
+ 1787                                                   QbeGen.LoadLong(n, q)
+ 1788                                                 ELSIF (cls
+ 1789                                                         = SymTab.ClArray)
+ 1790                                                    OR (cls
+ 1791                                                        = SymTab.ClSet)
+ 1792                                                    OR (cls
+ 1793                                                        = SymTab.ClRecord)
+ 1794                                                    OR (cls
+ 1795                                                        = SymTab.ClUStr)
+ 1796                                                    OR (cls
+ 1797                                                        = SymTab.ClClass) THEN
+ 1798                                                   QbeGen.AddrOf(n, q)
+ 1799                                                 ELSE SemError(230);
+ 1800                                                   QbeGen.CopyOp("0", q)
+ 1801                                                 END
+ 1802                                               ELSE QbeGen.CopyOp("0", q);
+ 1803                                                 IF k = SymTab.KindImport THEN
+ 1804                                                   SemError(230)
+ 1805                                                 ELSIF k =
+ 1806                                                    SymTab.KindProc THEN
+ 1807                                                   (* bare procedure name:
+ 1808                                                      a following ArgList
+ 1809                                                      makes it a call;
+ 1810                                                      otherwise Fact
+ 1811                                                      reports 230 *)
+ 1812                                                 ELSE
+ 1813                                                   IF k = SymTab.KindField THEN
+ 1814                                                     IF QbeGen.TopWith(qb) THEN
+ 1815                                                       fo :=
+ 1816                                                         SymTab.FieldOffset(
+ 1817                                                         SymTab.FieldOwner(n),
+ 1818                                                         n);
+ 1819                                                       QbeGen.FieldAddr(qb,
+ 1820                                                         fo, q);
+ 1821                                                       sfx := TRUE
+ 1822                                                     ELSE SemError(230);
+ 1823                                                       QbeGen.CopyOp("0", q)
+ 1824                                                     END
+ 1825                                                   END
+ 1826                                                 END
+ 1827                                               END
+ 1828                                             END; .)
+ 1829        { "[" Expr<it, iq>
+ 1830                                          (. IF t = SymTab.InvalidType THEN
+ 1831                                             ELSIF SymTab.ClassOf(t) #
+ 1832                                                   SymTab.ClArray THEN
+ 1833                                               SemError(217);
+ 1834                                               t := SymTab.InvalidType
+ 1835                                             ELSIF NOT SymTab.IsIntFamily(it)
+ 1836   AND (SymTab.ClassOf(it) #
+ 1837                                                   SymTab.ClChar)
+ 1838   AND (SymTab.ClassOf(it) #
+ 1839                                                   SymTab.ClEnum) THEN
+ 1840                                               SemError(218);
+ 1841                                               t := SymTab.InvalidType
+ 1842                                             ELSE
+ 1843                                               QbeGen.WidenIndex(iq, ql);
+ 1844                                               isOpen :=
+ 1845                                                 SymTab.IsOpenArray(t);
+ 1846                                               IF isOpen THEN
+ 1847                                                 QbeGen.CopyOp("0", qlo);
+ 1848                                                 IF SymTab.IsCharArray(t)
+ 1849    OR SymTab.IsUCharArray(t) THEN
+ 1850                                                   QbeGen.OpenHiChar(q, qhi)
+ 1851                                                 ELSE QbeGen.OpenHi(q, qhi)
+ 1852                                                 END
+ 1853                                               ELSE
+ 1854                                                 lo := SymTab.ArrayLo(t);
+ 1855                                                 hi := SymTab.ArrayHi(t);
+ 1856                                                 IF SymTab.IsCharArray(t)
+ 1857    OR SymTab.IsUCharArray(t) THEN
+ 1858                                                   hi := hi + 1
+ 1859                                                 END;
+ 1860                                                 QbeGen.IntStr(lo, qlo);
+ 1861                                                 QbeGen.IntStr(hi, qhi)
+ 1862                                               END;
+ 1863                                               QbeGen.CheckRange(ql, qlo,
+ 1864                                                 qhi);
+ 1865                                               eT := SymTab.ArrayElem(t);
+ 1866                                               QbeGen.ElemAddr(q, ql, qlo,
+ 1867                                                 t, qe);
+ 1868                                               IF SymTab.ClassOf(eT) =
+ 1869                                                  SymTab.ClArray THEN
+ 1870                                                 QbeGen.ElemLoad(qe, eT, q)
+ 1871                                               ELSE QbeGen.CopyOp(qe, q)
+ 1872                                               END;
+ 1873                                               t := eT; sfx := TRUE
+ 1874                                             END; .)
+ 1875          { "," Expr<it, iq>
+ 1876                                          (. IF t = SymTab.InvalidType THEN
+ 1877                                             ELSIF SymTab.ClassOf(t) #
+ 1878                                                   SymTab.ClArray THEN
+ 1879                                               SemError(217);
+ 1880                                               t := SymTab.InvalidType
+ 1881                                             ELSIF NOT SymTab.IsIntFamily(it)
+ 1882   AND (SymTab.ClassOf(it) #
+ 1883                                                   SymTab.ClChar)
+ 1884   AND (SymTab.ClassOf(it) #
+ 1885                                                   SymTab.ClEnum) THEN
+ 1886                                               SemError(218);
+ 1887                                               t := SymTab.InvalidType
+ 1888                                             ELSE
+ 1889                                               QbeGen.WidenIndex(iq, ql);
+ 1890                                               isOpen :=
+ 1891                                                 SymTab.IsOpenArray(t);
+ 1892                                               IF isOpen THEN
+ 1893                                                 QbeGen.CopyOp("0", qlo);
+ 1894                                                 IF SymTab.IsCharArray(t)
+ 1895    OR SymTab.IsUCharArray(t) THEN
+ 1896                                                   QbeGen.OpenHiChar(q, qhi)
+ 1897                                                 ELSE QbeGen.OpenHi(q, qhi)
+ 1898                                                 END
+ 1899                                               ELSE
+ 1900                                                 lo := SymTab.ArrayLo(t);
+ 1901                                                 hi := SymTab.ArrayHi(t);
+ 1902                                                 IF SymTab.IsCharArray(t)
+ 1903    OR SymTab.IsUCharArray(t) THEN
+ 1904                                                   hi := hi + 1
+ 1905                                                 END;
+ 1906                                                 QbeGen.IntStr(lo, qlo);
+ 1907                                                 QbeGen.IntStr(hi, qhi)
+ 1908                                               END;
+ 1909                                               QbeGen.CheckRange(ql, qlo,
+ 1910                                                 qhi);
+ 1911                                               eT := SymTab.ArrayElem(t);
+ 1912                                               QbeGen.ElemAddr(q, ql, qlo,
+ 1913                                                 t, qe);
+ 1914                                               IF SymTab.ClassOf(eT) =
+ 1915                                                  SymTab.ClArray THEN
+ 1916                                                 QbeGen.ElemLoad(qe, eT, q)
+ 1917                                               ELSE QbeGen.CopyOp(qe, q)
+ 1918                                               END;
+ 1919                                               t := eT; sfx := TRUE
+ 1920                                             END; .) }
+ 1921          "]"
+ 1922        | "." GetIdent<fn>
+ 1923                                          (. IF k = SymTab.KindModule THEN
+ 1924                                               (* qualified L.x: materialize
+ 1925                                                  the export, then load it *)
+ 1926                                               IF NOT SymTab.MaterializeAlias(n,
+ 1927                                                    fn, mal) THEN
+ 1928                                                 SemError(201);
+ 1929                                                 t := SymTab.InvalidType;
+ 1930                                                 QbeGen.CopyOp("0", q)
+ 1931                                               ELSE
+ 1932                                                 QbeGen.CopyOp(mal, qn);
+ 1933                                                 t := SymTab.SymType(mal);
+ 1934                                                 k := SymTab.SymKind(mal);
+ 1935                                                 sfx := FALSE;
+ 1936                                                 IF k = SymTab.KindProc THEN
+ 1937                                                   (* call: ArgList supplies
+ 1938                                                      the value *)
+ 1939                                                   QbeGen.CopyOp("0", q)
+ 1940                                                 ELSIF NOT QbeGen.LoadDesignator(
+ 1941                                                      mal, t, k, q) THEN
+ 1942                                                   SemError(230);
+ 1943                                                   QbeGen.CopyOp("0", q)
+ 1944                                                 END
+ 1945                                               END
+ 1946                                             ELSIF t = SymTab.InvalidType THEN
+ 1947                                             ELSIF (SymTab.ClassOf(t) #
+ 1948                                                    SymTab.ClRecord)
+ 1949   AND (SymTab.ClassOf(t) #
+ 1950                                                   SymTab.ClClass) THEN
+ 1951                                               SemError(215);
+ 1952                                               t := SymTab.InvalidType
+ 1953                                             ELSIF (SymTab.ClassOf(t) =
+ 1954                                                    SymTab.ClClass)
+ 1955    AND SymTab.MethodExists(t, fn) THEN
+ 1956                                               (* obj.Method: bind the
+ 1957                                                  method and pass obj as
+ 1958                                                  the hidden receiver; q
+ 1959                                                  already holds the
+ 1960                                                  object's address *)
+ 1961                                               QbeGen.ArmRecv(q);
+ 1962                                               QbeGen.CopyOp(fn, n);
+ 1963                                               QbeGen.CopyOp(fn, qn);
+ 1964                                               methCls := t;
+ 1965                                               k := SymTab.KindProc;
+ 1966                                               t := SymTab.InvalidType
+ 1967                                             ELSIF NOT SymTab.FieldExists(t,
+ 1968                                                      fn) THEN
+ 1969                                               SemError(216);
+ 1970                                               t := SymTab.InvalidType
+ 1971                                             ELSE
+ 1972                                               fo := SymTab.FieldOffset(t,
+ 1973                                                 fn);
+ 1974                                               t := SymTab.FieldType(t, fn);
+ 1975                                               QbeGen.FieldAddr(q, fo, qe);
+ 1976                                               (* array fields are inline:
+ 1977                                                  the field address is the
+ 1978                                                  descriptor, like records *)
+ 1979                                               QbeGen.CopyOp(qe, q);
+ 1980                                               sfx := TRUE
+ 1981                                             END; .)
+ 1982        | "^"
+ 1983                                          (. IF t = SymTab.InvalidType THEN
+ 1984                                             ELSIF SymTab.ClassOf(t) #
+ 1985                                                   SymTab.ClPtr THEN
+ 1986                                               SemError(219);
+ 1987                                               t := SymTab.InvalidType
+ 1988                                             ELSE
+ 1989                                               bt := SymTab.PtrBase(t);
+ 1990                                               IF bt = SymTab.InvalidType THEN
+ 1991                                               ELSE
+ 1992                                                 IF sfx THEN
+ 1993                                                   QbeGen.ElemLoad(q, t,
+ 1994                                                     qb);
+ 1995                                                   QbeGen.CopyOp(qb, q)
+ 1996                                                 END;
+ 1997                                                 t := bt;
+ 1998                                                 (* q holds the pointee
+ 1999                                                    address: Fact loads
+ 2000                                                    scalars/pointers and uses
+ 2001                                                    the address for
+ 2002                                                    aggregates; the VAR-actual
+ 2003                                                    note is q itself. *)
+ 2004                                                 sfx := TRUE
+ 2005                                               END
+ 2006                                             END; .) } .
+ 2007    Expr<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 2008                                          (. VAR t2: SymTab.TypeIndex;
+ 2009                                               op: INTEGER;
+ 2010                                               q2, qt, wl: QbeGen.QVal;
+ 2011                                               isR: BOOLEAN; .)
+ 2012      = SimExpr<t, q>
+ 2013        [ Rel<op> SimExpr<t2, q2>
+ 2014          (. IF op = SymTab.OpIn THEN
+ 2015               IF SymTab.InCheck(t, t2) THEN
+ 2016                 IF (t = SymTab.InvalidType)
+ 2017                    OR (t2 = SymTab.InvalidType) THEN
+ 2018                   t := SymTab.BoolType(); QbeGen.CopyOp("0", q)
+ 2019                 ELSE
+ 2020                   QbeGen.InSet(q, q2, SymTab.SetBaseLo(t2),
+ 2021                     SymTab.SetCount(t2), qt);
+ 2022                   t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
+ 2023                 END
+ 2024               ELSE SemError(222); t := SymTab.InvalidType;
+ 2025                 QbeGen.CopyOp("0", q)
+ 2026               END
+ 2027             ELSIF SymTab.RelCheck(t, t2, op) THEN
+ 2028               IF (t = SymTab.InvalidType)
+ 2029                  OR (t2 = SymTab.InvalidType) THEN
+ 2030                 t := SymTab.BoolType(); QbeGen.CopyOp("0", q)
+ 2031               ELSIF (SymTab.ClassOf(t) = SymTab.ClPtr)
+ 2032                  OR (SymTab.ClassOf(t2) = SymTab.ClPtr) THEN
+ 2033                 IF (op # SymTab.OpEq) AND (op # SymTab.OpNeq1)
+ 2034   AND (op # SymTab.OpNeq2) THEN
+ 2035                   SemError(213); t := SymTab.InvalidType;
+ 2036                   QbeGen.CopyOp("0", q)
+ 2037                 ELSE
+ 2038                   QbeGen.CmpL(op, q, q2, qt);
+ 2039                   t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
+ 2040                 END
+ 2041               ELSIF (SymTab.ClassOf(t) = SymTab.ClSet)
+ 2042                  OR (SymTab.ClassOf(t2) = SymTab.ClSet) THEN
+ 2043                 QbeGen.CmpSet(op, q, q2,
+ 2044                   SymTab.SetWords(t), SymTab.SetWords(t2), qt);
+ 2045                 t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
+ 2046               ELSIF SymTab.StrCompat(t, t2) THEN
+ 2047                 QbeGen.StrEq(op, q, q2, qt);
+ 2048                 t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
+ 2049               ELSIF SymTab.IsLongFamily(t)
+ 2050                  OR SymTab.IsLongFamily(t2) THEN
+ 2051                 IF SymTab.IsIntFamily(t) THEN
+ 2052                   QbeGen.WidenLong(q, wl); QbeGen.CopyOp(wl, q)
+ 2053                 END;
+ 2054                 IF SymTab.IsIntFamily(t2) THEN
+ 2055                   QbeGen.WidenLong(q2, wl); QbeGen.CopyOp(wl, q2)
+ 2056                 END;
+ 2057                 QbeGen.CmpLong(op, q, q2, qt);
+ 2058                 t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
+ 2059               ELSE
+ 2060                 isR := SymTab.ClassOf(t) = SymTab.ClReal;
+ 2061                 t := SymTab.BoolType();
+ 2062                 QbeGen.Cmp(op, q, q2, qt, isR);
+ 2063                 QbeGen.CopyOp(qt, q)
+ 2064               END
+ 2065             ELSE SemError(213); t := SymTab.InvalidType;
+ 2066               QbeGen.CopyOp("0", q)
+ 2067             END; .) ] .
+ 2068    Rel<VAR op: INTEGER>
+ 2069      = "="                               (. op := SymTab.OpEq; .)
+ 2070      | "#"                               (. op := SymTab.OpNeq1; .)
+ 2071      | "<"                               (. op := SymTab.OpLt; .)
+ 2072      | "<="                              (. op := SymTab.OpLe; .)
+ 2073      | ">"                               (. op := SymTab.OpGt; .)
+ 2074      | ">="                              (. op := SymTab.OpGe; .)
+ 2075      | "IN"                              (. op := SymTab.OpIn; .) .
+ 2076    SimExpr<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 2077                                          (. VAR t2, res2, lt, rt:
+ 2078                                                 SymTab.TypeIndex;
+ 2079                                               op: INTEGER;
+ 2080                                               q2, qt, wq, qf, q2a, q2b:
+ 2081                                                 QbeGen.QVal;
+ 2082                                               neg, isR, isL, folded:
+ 2083                                                 BOOLEAN;
+ 2084                                               fok: BOOLEAN;
+ 2085                                               lw, rw, mw: CARDINAL;
+ 2086                                               lTrue, lNext, lDone, qr, qs:
+ 2087                                                 QbeGen.QVal; .)
+ 2088      =                                   (. neg := FALSE; .)
+ 2089        [ "+" | "-"                       (. neg := TRUE; .) ]
+ 2090        Term<t, q>                        (. IF neg THEN
+ 2091                                             IF QbeGen.IsImm(q) THEN
+ 2092                                               QbeGen.NegFold(q, q)
+ 2093                                             ELSE QbeGen.NewTemp(qt);
+ 2094                                               QbeGen.NegQ(q, qt,
+ 2095                                                 SymTab.ClassOf(t)
+ 2096                                                 = SymTab.ClReal);
+ 2097                                               QbeGen.CopyOp(qt, q)
+ 2098                                             END
+ 2099                                           END; .)
+ 2100        { AddOp<op>                       (. IF op = SymTab.OpOr THEN
+ 2101                                               QbeGen.DelayBegin END; .)
+ 2102          Term<t2, q2>                     (. IF op = SymTab.OpOr THEN
+ 2103                                               QbeGen.DelayEnd END; .)
+ 2104          (. IF op = SymTab.OpOr THEN
+ 2105               (* short-circuit: if q is true the RHS is skipped *)
+ 2106               IF SymTab.BoolCheck(t) AND SymTab.BoolCheck(t2) THEN
+ 2107                 t := SymTab.BoolType()
+ 2108               ELSE SemError(212); t := SymTab.InvalidType END;
+ 2109               IF t # SymTab.InvalidType THEN
+ 2110                 QbeGen.Slot4(qs);
+ 2111                 QbeGen.NewLabel(lTrue);
+ 2112                 QbeGen.NewLabel(lNext);
+ 2113                 QbeGen.NewLabel(lDone);
+ 2114                 QbeGen.Jnz(q, lTrue, lNext);
+ 2115                 QbeGen.EmitLabel(lTrue);
+ 2116                 QbeGen.StoreW(qs, "1");
+ 2117                 QbeGen.Jmp(lDone);
+ 2118                 QbeGen.EmitLabel(lNext);
+ 2119                 QbeGen.DelayFlush;
+ 2120                 QbeGen.StoreW(qs, q2);
+ 2121                 QbeGen.Jmp(lDone);
+ 2122                 QbeGen.EmitLabel(lDone);
+ 2123                 QbeGen.LoadW(qs, qr);
+ 2124                 QbeGen.CopyOp(qr, q)
+ 2125               ELSE QbeGen.DelayFlush; QbeGen.CopyOp("0", q)
+ 2126               END
+ 2127             ELSIF (op = SymTab.OpAdd)
+ 2128               AND (SymTab.UStrCompat(t, t2)
+ 2129                 OR ((SymTab.ClassOf(t) = SymTab.ClUChar)
+ 2130                   AND (SymTab.ClassOf(t2) = SymTab.ClUStr))
+ 2131                 OR ((SymTab.ClassOf(t) = SymTab.ClUStr)
+ 2132                   AND (SymTab.ClassOf(t2) = SymTab.ClUChar))
+ 2133                 OR ((SymTab.ClassOf(t) = SymTab.ClUChar)
+ 2134                   AND (SymTab.ClassOf(t2) = SymTab.ClUChar))) THEN
+ 2135               (* UString concatenation: a UCHAR operand becomes a
+ 2136                  1-codepoint UString; the result is a descriptor in the
+ 2137                  shim's concat buffer.  Work on copies so neither
+ 2138                  operand is clobbered. *)
+ 2139               IF SymTab.ClassOf(t) = SymTab.ClUStr THEN
+ 2140                 QbeGen.CopyOp(q, q2a)
+ 2141               ELSE
+ 2142                 QbeGen.UStrFrom(q, q2a)
+ 2143               END;
+ 2144               IF SymTab.ClassOf(t2) = SymTab.ClUStr THEN
+ 2145                 QbeGen.UStrCat(q2a, q2, qt)
+ 2146               ELSE
+ 2147                 QbeGen.UStrFrom(q2, q2b);
+ 2148                 QbeGen.UStrCat(q2a, q2b, qt)
+ 2149               END;
+ 2150               t := SymTab.NewUStr();
+ 2151               QbeGen.CopyOp(qt, q)
+ 2152             ELSIF (op = SymTab.OpAdd)
+ 2153               AND (SymTab.StrCompat(t, t2)
+ 2154                 OR (SymTab.IsStrType(t)
+ 2155                   AND (SymTab.ClassOf(t2) = SymTab.ClChar))
+ 2156                 OR ((SymTab.ClassOf(t) = SymTab.ClChar)
+ 2157                   AND SymTab.IsStrType(t2))) THEN
+ 2158               (* string concatenation; a CHAR operand becomes a
+ 2159                  1-character string literal.  When both operands are
+ 2160                  constants, fold to a single string literal so a
+ 2161                  constructor element stays compile-time. *)
+ 2162               QbeGen.StrFold(q, q2, SymTab.ClassOf(t), SymTab.ClassOf(t2),
+ 2163                 qt, fok);
+ 2164               IF NOT fok THEN
+ 2165                 IF SymTab.StrCompat(t, t2) THEN
+ 2166                   QbeGen.StrCat(q, q2, qt)
+ 2167                 ELSIF SymTab.IsStrType(t) THEN
+ 2168                   QbeGen.DeclCharStr(q2, qs);
+ 2169                   QbeGen.StrCat(q, qs, qt)
+ 2170                 ELSE
+ 2171                   QbeGen.DeclCharStr(q, qs);
+ 2172                   QbeGen.StrCat(qs, q2, qt)
+ 2173                 END
+ 2174               END;
+ 2175               t := SymTab.NewStr();
+ 2176               QbeGen.CopyOp(qt, q)
+ 2177             ELSIF (t # SymTab.InvalidType) AND (t2 # SymTab.InvalidType)
+ 2178   AND (SymTab.ClassOf(t) = SymTab.ClSet)
+ 2179   AND (SymTab.ClassOf(t2) = SymTab.ClSet) THEN
+ 2180               lw := SymTab.SetWords(t); rw := SymTab.SetWords(t2);
+ 2181               mw := lw;
+ 2182               IF rw > mw THEN mw := rw END;
+ 2183               IF op = SymTab.OpAdd THEN
+ 2184                 QbeGen.SetBinOp(0, q, q2, lw, rw, qt)
+ 2185               ELSE
+ 2186                 QbeGen.SetBinOp(2, q, q2, lw, rw, qt)
+ 2187               END;
+ 2188               t := SymTab.NewSet(
+ 2189                      SymTab.NewSubR(0,
+ 2190                        VAL(INTEGER, mw) * 32 - 1));
+ 2191               QbeGen.CopyOp(qt, q)
+ 2192             ELSE
+ 2193               IF SymTab.ArithCheck(t, t2, FALSE, res2) THEN
+ 2194                 lt := t; rt := t2; t := res2
+ 2195               ELSE SemError(211); t := SymTab.InvalidType END;
+ 2196               IF t # SymTab.InvalidType THEN
+ 2197                 isL := SymTab.IsLongFamily(t);
+ 2198                 isR := SymTab.ClassOf(t) = SymTab.ClReal;
+ 2199                 folded := FALSE;
+ 2200                 IF (NOT isL) AND (NOT isR)
+ 2201    AND QbeGen.IsImm(q) AND QbeGen.IsImm(q2) THEN
+ 2202                   IF op = SymTab.OpAdd THEN
+ 2203                     folded := QbeGen.Fold2(0, q, q2, qf)
+ 2204                   ELSE
+ 2205                     folded := QbeGen.Fold2(1, q, q2, qf)
+ 2206                   END
+ 2207                 END;
+ 2208                 IF folded THEN QbeGen.CopyOp(qf, q)
+ 2209                 ELSE
+ 2210                 IF isL THEN
+ 2211                   IF SymTab.IsIntFamily(lt) THEN
+ 2212                     QbeGen.WidenLong(q, wq); QbeGen.CopyOp(wq, q)
+ 2213                   END;
+ 2214                   IF SymTab.IsIntFamily(rt) THEN
+ 2215                     QbeGen.WidenLong(q2, wq); QbeGen.CopyOp(wq, q2)
+ 2216                   END;
+ 2217                   QbeGen.NewTemp(qt);
+ 2218                   IF op = SymTab.OpAdd THEN
+ 2219                     QbeGen.Op3L("add", qt, q, q2)
+ 2220                   ELSE
+ 2221                     QbeGen.Op3L("sub", qt, q, q2)
+ 2222                   END
+ 2223                 ELSE
+ 2224                   QbeGen.NewTemp(qt);
+ 2225                   IF op = SymTab.OpAdd THEN
+ 2226                     QbeGen.Op3("add", qt, q, q2, isR)
+ 2227                   ELSE
+ 2228                     QbeGen.Op3("sub", qt, q, q2, isR)
+ 2229                   END
+ 2230                 END;
+ 2231                 QbeGen.CopyOp(qt, q)
+ 2232                 END
+ 2233               ELSE QbeGen.CopyOp("0", q)
+ 2234               END
+ 2235             END; .) } .
+ 2236    AddOp<VAR op: INTEGER>
+ 2237      = "+"                               (. op := SymTab.OpAdd; .)
+ 2238      | "-"                               (. op := SymTab.OpSub; .)
+ 2239      | "OR"                              (. op := SymTab.OpOr; .) .
+ 2240    Term<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 2241                                          (. VAR t2, res2, lt, rt:
+ 2242                                                 SymTab.TypeIndex;
+ 2243                                               op: INTEGER;
+ 2244                                               q2, qt, wq, qf:
+ 2245                                                 QbeGen.QVal;
+ 2246                                               isR, isL, folded: BOOLEAN;
+ 2247                                               lw, rw, mw: CARDINAL;
+ 2248                                               lNext, lFalse, lDone, qr, qs:
+ 2249                                                 QbeGen.QVal; .)
+ 2250      = Fact<t, q> { MulOp<op>            (. IF op = SymTab.OpAnd THEN
+ 2251                                               QbeGen.DelayBegin END; .)
+ 2252          Fact<t2, q2>                    (. IF op = SymTab.OpAnd THEN
+ 2253                                               QbeGen.DelayEnd END; .)
+ 2254        (. IF op = SymTab.OpAnd THEN
+ 2255             (* short-circuit: if q is false the RHS is skipped *)
+ 2256             IF SymTab.BoolCheck(t) AND SymTab.BoolCheck(t2) THEN
+ 2257               t := SymTab.BoolType()
+ 2258             ELSE SemError(212); t := SymTab.InvalidType END;
+ 2259             IF t # SymTab.InvalidType THEN
+ 2260               QbeGen.Slot4(qs);
+ 2261               QbeGen.NewLabel(lNext);
+ 2262               QbeGen.NewLabel(lFalse);
+ 2263               QbeGen.NewLabel(lDone);
+ 2264               QbeGen.Jnz(q, lNext, lFalse);
+ 2265               QbeGen.EmitLabel(lNext);
+ 2266               QbeGen.DelayFlush;
+ 2267               QbeGen.StoreW(qs, q2);
+ 2268               QbeGen.Jmp(lDone);
+ 2269               QbeGen.EmitLabel(lFalse);
+ 2270               QbeGen.StoreW(qs, "0");
+ 2271               QbeGen.Jmp(lDone);
+ 2272               QbeGen.EmitLabel(lDone);
+ 2273               QbeGen.LoadW(qs, qr);
+ 2274               QbeGen.CopyOp(qr, q)
+ 2275             ELSE QbeGen.DelayFlush; QbeGen.CopyOp("0", q)
+ 2276             END
+ 2277           ELSIF (t # SymTab.InvalidType) AND (t2 # SymTab.InvalidType)
+ 2278   AND (SymTab.ClassOf(t) = SymTab.ClSet)
+ 2279   AND (SymTab.ClassOf(t2) = SymTab.ClSet) THEN
+ 2280             lw := SymTab.SetWords(t); rw := SymTab.SetWords(t2);
+ 2281             mw := lw;
+ 2282             IF rw > mw THEN mw := rw END;
+ 2283             IF op = SymTab.OpTimes THEN
+ 2284               QbeGen.SetBinOp(1, q, q2, lw, rw, qt)
+ 2285             ELSE
+ 2286               QbeGen.SetBinOp(3, q, q2, lw, rw, qt)
+ 2287             END;
+ 2288             t := SymTab.NewSet(
+ 2289                    SymTab.NewSubR(0,
+ 2290                      VAL(INTEGER, mw) * 32 - 1));
+ 2291             QbeGen.CopyOp(qt, q)
+ 2292           ELSE
+ 2293             IF SymTab.ArithCheck(t, t2,
+ 2294                  (op = SymTab.OpDiv) OR (op = SymTab.OpMod),
+ 2295                  res2) THEN
+ 2296               lt := t; rt := t2; t := res2
+ 2297             ELSE SemError(211); t := SymTab.InvalidType END;
+ 2298             IF t # SymTab.InvalidType THEN
+ 2299               isL := SymTab.IsLongFamily(t);
+ 2300               isR := SymTab.ClassOf(t) = SymTab.ClReal;
+ 2301               folded := FALSE;
+ 2302               IF (NOT isL) AND (NOT isR)
+ 2303    AND QbeGen.IsImm(q) AND QbeGen.IsImm(q2) THEN
+ 2304                 IF op = SymTab.OpTimes THEN
+ 2305                   folded := QbeGen.Fold2(2, q, q2, qf)
+ 2306                 ELSIF op = SymTab.OpDiv THEN
+ 2307                   folded := QbeGen.Fold2(3, q, q2, qf)
+ 2308                 ELSIF op = SymTab.OpMod THEN
+ 2309                   folded := QbeGen.Fold2(4, q, q2, qf)
+ 2310                 END
+ 2311               END;
+ 2312               IF folded THEN QbeGen.CopyOp(qf, q)
+ 2313               ELSE
+ 2314               IF isL THEN
+ 2315                 IF SymTab.IsIntFamily(lt) THEN
+ 2316                   QbeGen.WidenLong(q, wq); QbeGen.CopyOp(wq, q)
+ 2317                 END;
+ 2318                 IF SymTab.IsIntFamily(rt) THEN
+ 2319                   QbeGen.WidenLong(q2, wq); QbeGen.CopyOp(wq, q2)
+ 2320                 END;
+ 2321                 QbeGen.NewTemp(qt);
+ 2322                 IF op = SymTab.OpTimes THEN
+ 2323                   QbeGen.Op3L("mul", qt, q, q2)
+ 2324                 ELSIF (op = SymTab.OpDiv)
+ 2325                    OR (op = SymTab.OpSlash) THEN
+ 2326                   QbeGen.Op3L("div", qt, q, q2)
+ 2327                 ELSE
+ 2328                   QbeGen.Op3L("rem", qt, q, q2)
+ 2329                 END
+ 2330               ELSE
+ 2331                 QbeGen.NewTemp(qt);
+ 2332                 IF op = SymTab.OpTimes THEN
+ 2333                   QbeGen.Op3("mul", qt, q, q2, isR)
+ 2334                 ELSIF (op = SymTab.OpDiv)
+ 2335                    OR (op = SymTab.OpSlash) THEN
+ 2336                   QbeGen.Op3("div", qt, q, q2, isR)
+ 2337                 ELSE
+ 2338                   QbeGen.Op3("rem", qt, q, q2, isR)
+ 2339                 END
+ 2340               END;
+ 2341               QbeGen.CopyOp(qt, q)
+ 2342               END
+ 2343             ELSE QbeGen.CopyOp("0", q)
+ 2344             END
+ 2345           END; .) } .
+ 2346    MulOp<VAR op: INTEGER>
+ 2347      = "*"                               (. op := SymTab.OpTimes; .)
+ 2348      | "/"                               (. op := SymTab.OpSlash; .)
+ 2349      | "DIV"                             (. op := SymTab.OpDiv; .)
+ 2350      | "MOD"                             (. op := SymTab.OpMod; .)
+ 2351      | ( "AND" | "&" )                   (. op := SymTab.OpAnd; .) .
+ 2352    Fact<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 2353                                          (. VAR s: ARRAY [0 .. 255] OF CHAR;
+ 2354                                               et, dt, t2, st, ct2, et2:
+ 2355                                                 SymTab.TypeIndex;
+ 2356                                               dk: INTEGER;
+ 2357                                               qd, q2, sq, qa, qm0, qr, qt:
+ 2358                                                 QbeGen.QVal;
+ 2359                                               qn, vn: SymTab.Name;
+ 2360                                               vt: SymTab.TypeIndex;
+ 2361                                               c1, c2: INTEGER;
+ 2362                                               lo, hi: INTEGER;
+ 2363                                               isMax: BOOLEAN;
+ 2364                                               called, isHigh, sfx, isCh,
+ 2365                                               isU, uok, isStr: BOOLEAN;
+ 2366                                               ucp: INTEGER; .)
+ 2367      = integer                           (. LexString(s);
+ 2368                                             QbeGen.NormInt(s, q);
+ 2369                                             t := SymTab.IntType(); .)
+ 2370      | charConst                         (. LexString(s);
+ 2371                                             QbeGen.NormLit(s, q, isCh);
+ 2372                                             t := SymTab.CharType(); .)
+ 2373      | real                              (. LexString(s);
+ 2374                                             QbeGen.NormReal(s, q);
+ 2375                                             t := SymTab.RealType(); .)
+ 2376      | string                            (. LexString(s);
+ 2377                                             IF SymTab.StrLen(s) = 3 THEN
+ 2378                                               t := SymTab.CharType();
+ 2379                                               QbeGen.IntStr(
+ 2380                                                 QbeGen.CharVal(s), q)
+ 2381                                             ELSE t := SymTab.NewStr();
+ 2382                                               QbeGen.DeclStr(s, q);
+ 2383                                               (* a literal's value IS its
+ 2384                                                  static descriptor address *)
+ 2385                                               QbeGen.NoteAddr(q, q)
+ 2386                                             END; .)
+ 2387      | ustring                           (. LexString(s);
+ 2388                                             QbeGen.DeclUStr(s, q, isU, ucp,
+ 2389                                               uok);
+ 2390                                             IF NOT uok THEN
+ 2391                                               SemError(234);
+ 2392                                               t := SymTab.InvalidType
+ 2393                                             ELSIF isU THEN
+ 2394                                               t := SymTab.UCharType();
+ 2395                                               QbeGen.IntStr(ucp, q)
+ 2396                                             ELSE
+ 2397                                               t := SymTab.NewUStr();
+ 2398                                               QbeGen.NoteAddr(q, q)
+ 2399                                             END; .)
+ 2400      | Design<dt, dk, qd, qn, sfx>       (. called := FALSE;
+ 2401                                             t := dt;
+ 2402                                             IF sfx THEN
+ 2403                                               IF dt =
+ 2404                                                  SymTab.InvalidType THEN
+ 2405                                                 QbeGen.CopyOp("0", q)
+ 2406                                               ELSIF (SymTab.ClassOf(dt) =
+ 2407                                                      SymTab.ClRecord)
+ 2408                                                  OR (SymTab.ClassOf(dt) =
+ 2409                                                      SymTab.ClSet)
+ 2410                                                  OR (SymTab.ClassOf(dt) =
+ 2411                                                      SymTab.ClArray)
+ 2412                                                  OR (SymTab.ClassOf(dt) =
+ 2413                                                      SymTab.ClClass) THEN
+ 2414                                                 QbeGen.CopyOp(qd, q)
+ 2415                                               ELSE QbeGen.ElemLoad(qd, dt,
+ 2416                                                 q)
+ 2417                                               END
+ 2418                                             ELSE QbeGen.CopyOp(qd, q)
+ 2419                                             END;
+ 2420                                             IF (dk = SymTab.KindVar)
+ 2421                                                OR (dk = SymTab.KindParam)
+ 2422                                                OR (dk =
+ 2423                                                   SymTab.KindField) THEN
+ 2424                                               IF sfx THEN
+ 2425                                                 QbeGen.NoteAddr(q, qd)
+ 2426                                               ELSE
+ 2427                                                 QbeGen.AddrOf(qn, qa);
+ 2428                                                 QbeGen.NoteAddr(q, qa)
+ 2429                                               END
+ 2430                                             ELSIF sfx
+ 2431   AND (dt #
+ 2432                                                   SymTab.InvalidType)
+ 2433   AND ((SymTab.ClassOf(dt) =
+ 2434                                                    SymTab.ClArray)
+ 2435                                                   OR (SymTab.ClassOf(dt) =
+ 2436                                                       SymTab.ClSet)
+ 2437                                                   OR (SymTab.ClassOf(dt) =
+ 2438                                                       SymTab.ClRecord)) THEN
+ 2439                                               QbeGen.NoteAddr(qd, qd)
+ 2440                                             END; .)
+ 2441        [ TypedBraceLit<dt, q>            (. t := dt; .) ]
+ 2442        [ ArgList<qn, dt, qd, TRUE, methCls, ct2, q2, called>
+ 2443                                          (. t := ct2;
+ 2444                                             QbeGen.CopyOp(q2, q); .) ]
+ 2445                                          (. IF NOT called
+ 2446   AND (dk = SymTab.KindProc) THEN
+ 2447                                               (* bare zero-arg function
+ 2448                                                  call (parentheses may be
+ 2449                                                  omitted); a proper or
+ 2450                                                  parameterised proc here
+ 2451                                                  is 230 *)
+ 2452                                               IF (SymTab.ProcNPar(qn) = 0)
+ 2453   AND (SymTab.ProcRes(qn) #
+ 2454                                                     SymTab.InvalidType) THEN
+ 2455                                                 QbeGen.Mangled(qn,
+ 2456                                                   SymTab.ProcUid(qn), qm0);
+ 2457                                                 QbeGen.CallBegin(qm0,
+ 2458                                                   SymTab.ProcRes(qn),
+ 2459                                                   SymTab.ProcDepthOf(qn),
+ 2460                                                   SymTab.IsExternal(qn));
+ 2461                                                 QbeGen.CallEnd(TRUE, q);
+ 2462                                                 t := SymTab.ProcRes(qn)
+ 2463                                               ELSE
+ 2464                                                 (* procedure used as a
+ 2465                                                    value (assign to a
+ 2466                                                    procedure variable):
+ 2467                                                    its code address *)
+ 2468                                                 t := SymTab.ProcTypeOf(qn);
+ 2469                                                 QbeGen.Mangled(qn,
+ 2470                                                   SymTab.ProcUid(qn), qm0);
+ 2471                                                 QbeGen.ProcAddr(qm0, q)
+ 2472                                               END
+ 2473                                             END; .)
+ 2474      | ( "HIGH"                          (. isHigh := TRUE; .)
+ 2475        | ( "LEN" | "LENGTH" )            (. isHigh := FALSE; .) )
+ 2476        "(" ( Design<dt, dk, qd, qn, sfx> (. isStr := FALSE;
+ 2477                                              isU := FALSE; .)
+ 2478            | string                       (. LexString(s);
+ 2479                                              isStr := TRUE;
+ 2480                                              isU := FALSE;
+ 2481                                              IF SymTab.StrLen(s) = 3 THEN
+ 2482                                                dt := SymTab.CharType();
+ 2483                                                QbeGen.IntStr(QbeGen.CharVal(s),
+ 2484                                                  qd)
+ 2485                                              ELSE
+ 2486                                                dt := SymTab.NewStr();
+ 2487                                                QbeGen.DeclStr(s, qd);
+ 2488                                                QbeGen.NoteAddr(qd, qd)
+ 2489                                              END;
+ 2490                                              dk := -1;
+ 2491                                              qn[0] := CHR(0); .)
+ 2492            | ustring                      (. LexString(s);
+ 2493                                              QbeGen.DeclUStr(s, qd, isU, ucp,
+ 2494                                                uok);
+ 2495                                              isStr := FALSE;
+ 2496                                              IF NOT uok THEN
+ 2497                                                SemError(234);
+ 2498                                                dt := SymTab.InvalidType
+ 2499                                              ELSIF isU THEN
+ 2500                                                (* one codepoint: a UCHAR;
+ 2501                                                   LEN is 1, HIGH is 0 *)
+ 2502                                                dt := SymTab.UCharType();
+ 2503                                                QbeGen.IntStr(ucp, qd)
+ 2504                                              ELSE
+ 2505                                                dt := SymTab.NewUStr();
+ 2506                                                QbeGen.NoteAddr(qd, qd)
+ 2507                                              END;
+ 2508                                              dk := -1;
+ 2509                                              qn[0] := CHR(0); .) )
+ 2510        ")"
+ 2511                                          (. IF (dt # SymTab.InvalidType)
+ 2512    AND (SymTab.ClassOf(dt) = SymTab.ClUStr) THEN
+ 2513                                               (* UString: the count is
+ 2514                                                  the descriptor header *)
+ 2515                                               IF isHigh THEN
+ 2516                                                 QbeGen.UStrLen(qd, qr);
+ 2517                                                 QbeGen.DecQ(qr)
+ 2518                                               ELSE
+ 2519                                                 QbeGen.UStrLen(qd, qr)
+ 2520                                               END;
+ 2521                                               t := SymTab.IntType();
+ 2522                                               QbeGen.CopyOp(qr, q)
+ 2523                                             ELSIF (dt # SymTab.InvalidType)
+ 2524    AND (SymTab.ClassOf(dt) = SymTab.ClUChar) THEN
+ 2525                                               (* single UCHAR codepoint *)
+ 2526                                               IF isHigh THEN
+ 2527                                                 QbeGen.CopyOp("0", qr)
+ 2528                                               ELSE
+ 2529                                                 QbeGen.CopyOp("1", qr)
+ 2530                                               END;
+ 2531                                               t := SymTab.IntType();
+ 2532                                               QbeGen.CopyOp(qr, q)
+ 2533                                             ELSIF isStr THEN
+ 2534                                               (* fold: content length at
+ 2535                                                  compile time *)
+ 2536                                               IF SymTab.StrLen(s) = 3 THEN
+ 2537                                                 c1 := 1
+ 2538                                               ELSE
+ 2539                                                 c1 :=
+ 2540                                                   SymTab.StrLen(s) - 2
+ 2541                                               END;
+ 2542                                               IF isHigh THEN
+ 2543                                                 DEC(c1)
+ 2544                                               END;
+ 2545                                               QbeGen.IntStr(c1, qr);
+ 2546                                               t := SymTab.IntType();
+ 2547                                               QbeGen.CopyOp(qr, q)
+ 2548                                             ELSIF dt = SymTab.InvalidType THEN
+ 2549                                               t := SymTab.InvalidType;
+ 2550                                               QbeGen.CopyOp("0", q)
+ 2551                                             ELSIF SymTab.ClassOf(dt) #
+ 2552                                                   SymTab.ClArray THEN
+ 2553                                               SemError(217);
+ 2554                                               t := SymTab.InvalidType;
+ 2555                                               QbeGen.CopyOp("0", q)
+ 2556                                             ELSE
+ 2557                                               IF isHigh THEN
+ 2558                                                 IF SymTab.IsOpenArray(dt) THEN
+ 2559                                                   QbeGen.OpenHi(qd, qr)
+ 2560                                                 ELSE
+ 2561                                                   QbeGen.IntStr(
+ 2562                                                     SymTab.ArrayHi(dt), qr)
+ 2563                                                 END
+ 2564                                               ELSE
+ 2565                                                 IF SymTab.IsOpenArray(dt) THEN
+ 2566                                                   QbeGen.LoadCount(qd, qr)
+ 2567                                                 ELSE
+ 2568                                                   QbeGen.IntStr(VAL(
+ 2569                                                     INTEGER,
+ 2570                                                     SymTab.ArrayLen(dt)),
+ 2571                                                     qr)
+ 2572                                                 END
+ 2573                                               END;
+ 2574                                                t := SymTab.IntType();
+ 2575                                                QbeGen.CopyOp(qr, q)
+ 2576                                              END; .)
+ 2577      | ( "SIZE" | "TSIZE" ) "(" Design<dt, dk, qd, qn, sfx> ")"
+ 2578                                          (. IF dt = SymTab.InvalidType THEN
+ 2579                                               t := SymTab.InvalidType;
+ 2580                                               QbeGen.CopyOp("0", q)
+ 2581                                             ELSE
+ 2582                                               QbeGen.IntStr(VAL(INTEGER,
+ 2583                                                 SymTab.ObjectSize(dt)), q);
+ 2584                                               t := SymTab.IntType()
+ 2585                                             END; .)
+ 2586      | ( "SHIFT"                         (. isMax := FALSE; .)
+ 2587        | "ROTATE"                        (. isMax := TRUE; .) )
+ 2588        "(" Expr<et, q> "," Expr<et2, q2> ")"
+ 2589                                          (. (* set shift/rotate: isMax
+ 2590                                                doubles as "rotate" *)
+ 2591                                             IF (et # SymTab.InvalidType)
+ 2592                                                AND (SymTab.ClassOf(et) =
+ 2593                                                   SymTab.ClSet) THEN
+ 2594                                               QbeGen.SetShift(q, q2,
+ 2595                                                 SymTab.SetWords(et),
+ 2596                                                 SymTab.SetCount(et), isMax,
+ 2597                                                 qt);
+ 2598                                               t := et;
+ 2599                                               QbeGen.CopyOp(qt, q)
+ 2600                                             ELSE SemError(230);
+ 2601                                               t := SymTab.InvalidType;
+ 2602                                               QbeGen.CopyOp("0", q)
+ 2603                                             END; .)
+ 2604      | ( "MIN"                           (. isMax := FALSE; .)
+ 2605        | "MAX"                           (. isMax := TRUE; .) )
+ 2606        "(" Design<dt, dk, qd, qn, sfx> ")"
+ 2607                                          (. IF (dt # SymTab.InvalidType)
+ 2608   AND (SymTab.ClassOf(dt) = SymTab.ClReal) THEN
+ 2609                                               (* REAL/LONGREAL: the
+ 2610                                                  implementation bounds *)
+ 2611                                               IF isMax THEN
+ 2612                                                 QbeGen.NormReal(
+ 2613                                                   "3.402823e38", q)
+ 2614                                               ELSE QbeGen.NormReal(
+ 2615                                                   "-3.402823e38", q)
+ 2616                                               END;
+ 2617                                               t := SymTab.RealType()
+ 2618                                             ELSIF (dt #
+ 2619                                                SymTab.InvalidType)
+ 2620   AND (SymTab.ClassOf(dt) =
+ 2621                                                   SymTab.ClLong) THEN
+ 2622                                               IF isMax THEN
+ 2623                                                 QbeGen.CopyOp(
+ 2624                                                   "9223372036854775807", q)
+ 2625                                               ELSE QbeGen.CopyOp(
+ 2626                                                   "-9223372036854775808", q)
+ 2627                                               END;
+ 2628                                               t := SymTab.LongType()
+ 2629                                             ELSIF SymTab.TypeBounds(dt, lo,
+ 2630                                                      hi) THEN
+ 2631                                               IF isMax THEN
+ 2632                                                 QbeGen.IntStr(hi, q)
+ 2633                                               ELSE QbeGen.IntStr(lo, q)
+ 2634                                               END;
+ 2635                                               t := SymTab.IntType()
+ 2636                                             ELSE SemError(230);
+ 2637                                               t := SymTab.InvalidType;
+ 2638                                               QbeGen.CopyOp("0", q)
+ 2639                                             END; .)
+ 2640      | "ADR" "(" Design<dt, dk, qd, qn, sfx> ")"
+ 2641                                          (. IF dt = SymTab.InvalidType THEN
+ 2642                                               t := SymTab.InvalidType;
+ 2643                                               QbeGen.CopyOp("0", q)
+ 2644                                             ELSE
+ 2645                                               IF sfx THEN
+ 2646                                                 QbeGen.CopyOp(qd, q)
+ 2647                                               ELSIF (dk = SymTab.KindVar)
+ 2648                                                  OR (dk = SymTab.KindParam) THEN
+ 2649                                                 QbeGen.AddrOf(qn, q)
+ 2650                                               ELSE SemError(230);
+ 2651                                                 QbeGen.CopyOp("0", q)
+ 2652                                               END;
+ 2653                                               t := SymTab.AddrType()
+ 2654                                             END; .)
+ 2655      | "CHR" "(" Expr<et, q> ")"
+ 2656                                          (. IF (et # SymTab.InvalidType)
+ 2657   AND NOT SymTab.IsIntFamily(et) THEN
+ 2658                                               SemError(211) END;
+ 2659                                             t := SymTab.CharType(); .)
+ 2660      | ( "ORD" | "ORDL" ) "(" Expr<et, q> ")"
+ 2661                                          (. IF et # SymTab.InvalidType THEN
+ 2662                                               IF (SymTab.ClassOf(et) #
+ 2663                                                   SymTab.ClChar)
+ 2664   AND (SymTab.ClassOf(et) #
+ 2665                                                     SymTab.ClBool)
+ 2666   AND (SymTab.ClassOf(et) #
+ 2667                                                     SymTab.ClEnum)
+ 2668   AND NOT SymTab.IsIntFamily(et) THEN
+ 2669                                                 SemError(211) END
+ 2670                                             END;
+ 2671                                             t := SymTab.IntType(); .)
+ 2672      | "CAP" "(" Expr<et, q> ")"
+ 2673                                          (. QbeGen.CapQ(q, qa);
+ 2674                                             QbeGen.CopyOp(qa, q);
+ 2675                                             t := SymTab.CharType(); .)
+ 2676      | "UCHR" "(" Expr<et, q> ")"
+ 2677                                          (. (* UCHR: the UCHAR constructor.
+ 2678                                                CHAR -> UCHAR (identity);
+ 2679                                                INTEGER familly -> UCHAR
+ 2680                                                (codepoint value). *)
+ 2681                                             IF (et # SymTab.InvalidType)
+ 2682    AND (SymTab.ClassOf(et) # SymTab.ClChar)
+ 2683    AND NOT SymTab.IsIntFamily(et) THEN
+ 2684                                               SemError(211) END;
+ 2685                                             t := SymTab.UCharType(); .)
+ 2686      | "CHR8" "(" Expr<et, q> ")"
+ 2687                                          (. IF (et # SymTab.InvalidType)
+ 2688    AND (SymTab.ClassOf(et) # SymTab.ClUChar) THEN
+ 2689                                               SemError(211) END;
+ 2690                                             QbeGen.WidenLong(q, qa);
+ 2691                                             QbeGen.CheckRange(qa, "0", "255");
+ 2692                                             t := SymTab.CharType(); .)
+ 2693      | "UORD" "(" Expr<et, q> ")"
+ 2694                                          (. (* UORD(u): the codepoint as a
+ 2695                                                32-bit ordinal (INTEGER),
+ 2696                                                cf. ORD for CHAR. *)
+ 2697                                             IF (et # SymTab.InvalidType)
+ 2698    AND (SymTab.ClassOf(et) # SymTab.ClUChar) THEN
+ 2699                                               SemError(211) END;
+ 2700                                             t := SymTab.IntType(); .)
+ 2701      | "ABS" "(" Expr<et, q> ")"
+ 2702                                          (. IF (et # SymTab.InvalidType)
+ 2703   AND NOT SymTab.IsIntFamily(et)
+ 2704   AND (SymTab.ClassOf(et) #
+ 2705                                                  SymTab.ClReal) THEN
+ 2706                                               SemError(211)
+ 2707                                             ELSE QbeGen.AbsQ(q, qa,
+ 2708                                                    SymTab.ClassOf(et) =
+ 2709                                                      SymTab.ClReal);
+ 2710                                               QbeGen.CopyOp(qa, q)
+ 2711                                             END;
+ 2712                                             t := et; .)
+ 2713      | "VAL" "(" GetIdent<vn> "," Expr<et, q> ")"
+ 2714                                          (. IF NOT SymTab.Lookup(vn) THEN
+ 2715                                               SemError(201);
+ 2716                                               t := SymTab.InvalidType
+ 2717                                             ELSE vt := SymTab.SymType(vn);
+ 2718                                               IF vt = SymTab.InvalidType THEN
+ 2719                                                 t := SymTab.InvalidType
+ 2720                                               ELSIF et =
+ 2721                                                  SymTab.InvalidType THEN
+ 2722                                                 t := vt
+ 2723                                               ELSE
+ 2724                                                 c1 := SymTab.ClassOf(et);
+ 2725                                                 c2 := SymTab.ClassOf(vt);
+ 2726                                                 IF (c1 = SymTab.ClInt)
+ 2727    AND (c2 = SymTab.ClLong) THEN
+ 2728                                                   QbeGen.WidenLong(q, qa);
+ 2729                                                   QbeGen.CopyOp(qa, q);
+ 2730                                                   t := vt
+ 2731                                                 ELSIF (c1 = SymTab.ClLong)
+ 2732    AND (c2 = SymTab.ClInt) THEN
+ 2733                                                   QbeGen.NarrowLong(q, qa);
+ 2734                                                   QbeGen.CopyOp(qa, q);
+ 2735                                                   t := vt
+ 2736                                                 ELSIF (c1 = SymTab.ClInt)
+ 2737    AND (c2 = SymTab.ClReal) THEN
+ 2738                                                   QbeGen.ConvIR(q, qa);
+ 2739                                                   QbeGen.CopyOp(qa, q);
+ 2740                                                   t := vt
+ 2741                                                 ELSIF (c1 = SymTab.ClLong)
+ 2742    AND (c2 = SymTab.ClReal) THEN
+ 2743                                                   QbeGen.ConvLR(q, qa);
+ 2744                                                   QbeGen.CopyOp(qa, q);
+ 2745                                                   t := vt
+ 2746                                                 ELSIF (c1 = SymTab.ClReal)
+ 2747    AND (c2 = SymTab.ClInt) THEN
+ 2748                                                   QbeGen.ConvRI(q, qa);
+ 2749                                                   QbeGen.CopyOp(qa, q);
+ 2750                                                   t := vt
+ 2751                                                 ELSIF (c1 = SymTab.ClReal)
+ 2752    AND (c2 = SymTab.ClLong) THEN
+ 2753                                                   QbeGen.ConvRL(q, qa);
+ 2754                                                   QbeGen.CopyOp(qa, q);
+ 2755                                                   t := vt
+ 2756                                                 ELSIF ((c1 = SymTab.ClInt)
+ 2757                                                     OR (c1 =
+ 2758                                                        SymTab.ClChar)
+ 2759                                                     OR (c1 =
+ 2760                                                        SymTab.ClBool)
+ 2761                                                     OR (c1 =
+ 2762                                                        SymTab.ClEnum))
+ 2763   AND ((c2 = SymTab.ClInt)
+ 2764                                                     OR (c2 =
+ 2765                                                        SymTab.ClChar)
+ 2766                                                     OR (c2 =
+ 2767                                                        SymTab.ClBool)
+ 2768                                                     OR (c2 =
+ 2769                                                        SymTab.ClEnum)) THEN
+ 2770                                                   t := vt
+ 2771                                                 ELSIF (c1 = SymTab.ClPtr)
+ 2772   AND (c2 = SymTab.ClPtr) THEN
+ 2773                                                   t := vt
+ 2774                                                 ELSIF (c1 = SymTab.ClReal)
+ 2775   AND (c2 = SymTab.ClReal) THEN
+ 2776                                                   t := vt
+ 2777                                                 ELSE SemError(230);
+ 2778                                                   t := SymTab.InvalidType
+ 2779                                                 END
+ 2780                                               END
+ 2781                                             END; .)
+ 2782      | "(" Expr<et, q> ")"               (. t := et; .)
+ 2783      | SetLit<st, sq>                    (. t := st;
+ 2784                                             QbeGen.CopyOp(sq, q); .)
+ 2785      | ( "NOT" | "~" ) Fact<t2, q2>      (. IF SymTab.BoolCheck(t2) THEN
+ 2786                                               t := SymTab.BoolType()
+ 2787                                             ELSE SemError(212);
+ 2788                                               t := SymTab.InvalidType END;
+ 2789                                             IF t # SymTab.InvalidType THEN
+ 2790                                               QbeGen.NotQ(q2, q)
+ 2791                                             ELSE QbeGen.CopyOp("0", q)
+ 2792                                             END; .) .
+ 2793    (* Set literals are SET OF [0..255] (8 words); elements validated
+ 2794       0..255 statically when foldable (222 otherwise), runtime trap
+ 2795       for computed elements. Ranges always lower via SetRange. *)
+ 2796    SetLit<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 2797      = "{"                               (. t := SymTab.NewSet(
+ 2798                                               SymTab.NewSubR(0, 255));
+ 2799                                             QbeGen.NewSetTemp(8, q);
+ 2800                                             QbeGen.SetZero(q, 8); .)
+ 2801        [ SetElem<t, q> { "," SetElem<t, q> } ]
+ 2802        "}" .
+ 2803    (* Typed brace constructor: TypeName{ elems } — BITSET{0} (a set)
+ 2804       or ArrayName{...} (an array constructor, GNU Modula-2).  The
+ 2805       declared type sets the width (set) or element type (array). *)
+ 2806    TypedBraceLit<vt: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 2807                                          (. VAR nw: CARDINAL;
+ 2808                                               savedCls: INTEGER; .)
+ 2809      = "{"                               (. savedCls := braceCls;
+ 2810                                             IF vt = SymTab.InvalidType THEN
+ 2811                                               braceCls := -1
+ 2812                                             ELSE braceCls :=
+ 2813                                                    SymTab.ClassOf(vt)
+ 2814                                             END;
+ 2815                                             IF braceCls = SymTab.ClSet THEN
+ 2816                                               IF vt = SymTab.InvalidType THEN
+ 2817                                                 nw := 8
+ 2818                                               ELSE nw := SymTab.SetWords(vt);
+ 2819                                                 IF nw = 0 THEN nw := 8 END
+ 2820                                               END;
+ 2821                                               QbeGen.NewSetTemp(nw, q);
+ 2822                                               QbeGen.SetZero(q, nw)
+ 2823                                             ELSIF (braceCls =
+ 2824                                                    SymTab.ClArray)
+ 2825                                                OR (braceCls =
+ 2826                                                    SymTab.ClRecord)
+ 2827                                                OR (braceCls =
+ 2828                                                    SymTab.ClClass) THEN
+ 2829                                               QbeGen.CtorBegin(vt)
+ 2830                                             ELSE
+ 2831                                               IF vt # SymTab.InvalidType THEN
+ 2832                                                 SemError(230) END;
+ 2833                                               braceCls := -1
+ 2834                                             END; .)
+ 2835        [ BraceElem<vt, q> { "," BraceElem<vt, q> } ]
+ 2836        "}"                               (. IF (braceCls = SymTab.ClArray)
+ 2837                                                OR (braceCls =
+ 2838                                                   SymTab.ClRecord)
+ 2839                                                OR (braceCls =
+ 2840                                                   SymTab.ClClass) THEN
+ 2841                                               QbeGen.CtorEnd(q)
+ 2842                                             ELSIF braceCls # SymTab.ClSet THEN
+ 2843                                               QbeGen.CopyOp("0", q)
+ 2844                                             END;
+ 2845                                             braceCls := savedCls; .) .
+ 2846    BraceElem<vt: SymTab.TypeIndex; VAR sq: QbeGen.QVal>
+ 2847                                          (. VAR et, et2: SymTab.TypeIndex;
+ 2848                                               qe, q2: QbeGen.QVal;
+ 2849                                               v, v2, reps, k: INTEGER;
+ 2850                                               elem: SymTab.TypeIndex;
+ 2851                                               lo: INTEGER;
+ 2852                                               span: CARDINAL;
+ 2853                                               cl, cl2: INTEGER;
+ 2854                                               hasR, hasB: BOOLEAN; .)
+ 2855      =                                   (. hasR := FALSE; hasB := FALSE; .)
+ 2856        Expr<et, qe>
+ 2857        [ ".." Expr<et2, q2>              (. hasR := TRUE; .) ]
+ 2858        [ "BY" Expr<et2, q2>              (. hasB := TRUE; .) ]
+ 2859                                          (. IF braceCls = SymTab.ClSet THEN
+ 2860                                               IF hasB THEN SemError(230) END;
+ 2861                                               lo := SymTab.SetBaseLo(vt);
+ 2862                                               span := SymTab.SetCount(vt);
+ 2863                                               IF (et = SymTab.InvalidType)
+ 2864                                                  OR (hasR AND (et2 =
+ 2865                                                     SymTab.InvalidType)) THEN
+ 2866                                               ELSE cl :=
+ 2867                                                      SymTab.ClassOf(et);
+ 2868                                                 IF hasR THEN
+ 2869                                                   cl2 :=
+ 2870                                                     SymTab.ClassOf(et2)
+ 2871                                                 ELSE cl2 := SymTab.ClInt
+ 2872                                                 END;
+ 2873                                                 IF NOT SymTab.SetElemClassOk(cl)
+ 2874                                                    OR (hasR AND NOT
+ 2875                                                       SymTab.SetElemClassOk(cl2))
+ 2876                                                 THEN
+ 2877                                                   SemError(222)
+ 2878                                                 ELSIF hasR
+ 2879   AND SymTab.ConstInt(qe, v)
+ 2880   AND SymTab.ConstInt(q2,
+ 2881                                                       v2)
+ 2882   AND ((v < lo)
+ 2883                                                       OR (v2 < lo)
+ 2884                                                       OR (v >= lo +
+ 2885                                                          VAL(INTEGER, span))
+ 2886                                                       OR (v2 >= lo +
+ 2887                                                          VAL(INTEGER, span))
+ 2888                                                       OR (v > v2)) THEN
+ 2889                                                   SemError(222)
+ 2890                                                  ELSIF hasR THEN
+ 2891                                                    QbeGen.SetRange(sq, qe, q2,
+ 2892                                                      lo, span)
+ 2893                                                  ELSIF SymTab.ConstInt(qe,
+ 2894                                                          v)
+ 2895   AND ((v < lo)
+ 2896                                                        OR (v >= lo +
+ 2897                                                           VAL(INTEGER,
+ 2898                                                             span))) THEN
+ 2899                                                    SemError(222)
+ 2900                                                  ELSE QbeGen.SetBit(sq, qe,
+ 2901                                                    lo, span)
+ 2902                                                 END
+ 2903                                               END
+ 2904                                             ELSIF (braceCls = SymTab.ClArray)
+ 2905                                                OR (braceCls =
+ 2906                                                   SymTab.ClRecord)
+ 2907                                                OR (braceCls =
+ 2908                                                   SymTab.ClClass) THEN
+ 2909                                               IF hasR THEN SemError(230) END;
+ 2910                                               reps := 1;
+ 2911                                               IF hasB THEN
+ 2912                                                 IF SymTab.ConstInt(q2, v2)
+ 2913   AND (v2 >= 1) THEN
+ 2914                                                   reps := v2
+ 2915                                                 ELSE SemError(230)
+ 2916                                                 END
+ 2917                                               END;
+ 2918                                               k := 0;
+ 2919                                               WHILE k < reps DO
+ 2920                                                 QbeGen.CtorElem(qe);
+ 2921                                                 INC(k)
+ 2922                                               END
+ 2923                                             END; .) .
+ 2924    SetElem<st: SymTab.TypeIndex; sq: QbeGen.QVal>         (. VAR et, et2: SymTab.TypeIndex;
+ 2925                                               qe, q2: QbeGen.QVal;
+ 2926                                               v, v2: INTEGER;
+ 2927                                               lo: INTEGER;
+ 2928                                               span: CARDINAL;
+ 2929                                               cl, cl2: INTEGER;
+ 2930                                               hasR: BOOLEAN; .)
+ 2931      =                                   (. hasR := FALSE; .)
+ 2932        Expr<et, qe>
+ 2933        [ ".." Expr<et2, q2>              (. hasR := TRUE; .) ]
+ 2934                                          (. lo := SymTab.SetBaseLo(st);
+ 2935                                             span := SymTab.SetCount(st);
+ 2936                                             IF (et = SymTab.InvalidType)
+ 2937                                                OR (hasR AND (et2 =
+ 2938                                                   SymTab.InvalidType)) THEN
+ 2939                                             ELSE cl :=
+ 2940                                                    SymTab.ClassOf(et);
+ 2941                                               IF hasR THEN
+ 2942                                                 cl2 :=
+ 2943                                                   SymTab.ClassOf(et2)
+ 2944                                               ELSE cl2 := SymTab.ClInt
+ 2945                                               END;
+ 2946                                               IF NOT SymTab.SetElemClassOk(cl)
+ 2947                                                  OR (hasR AND NOT
+ 2948                                                     SymTab.SetElemClassOk(cl2))
+ 2949                                               THEN
+ 2950                                                 SemError(222)
+ 2951                                               ELSIF hasR
+ 2952   AND SymTab.ConstInt(qe, v)
+ 2953   AND SymTab.ConstInt(q2,
+ 2954                                                     v2)
+ 2955   AND ((v < lo)
+ 2956                                                     OR (v2 < lo)
+ 2957                                                     OR (v >= lo +
+ 2958                                                        VAL(INTEGER, span))
+ 2959                                                     OR (v2 >= lo +
+ 2960                                                        VAL(INTEGER, span))
+ 2961                                                     OR (v > v2)) THEN
+ 2962                                                 SemError(222)
+ 2963                                                ELSIF hasR THEN
+ 2964                                                  QbeGen.SetRange(sq, qe, q2,
+ 2965                                                    lo, span)
+ 2966                                                ELSIF SymTab.ConstInt(qe,
+ 2967                                                        v)
+ 2968   AND ((v < lo)
+ 2969                                                      OR (v >= lo +
+ 2970                                                         VAL(INTEGER,
+ 2971                                                           span))) THEN
+ 2972                                                  SemError(222)
+ 2973                                                ELSE QbeGen.SetBit(sq, qe,
+ 2974                                                  lo, span)
+ 2975                                               END
+ 2976                                             END; .) .
+ 2977    GetIdent<VAR n: SymTab.Name>
+ 2978      = ident                             (. LexName(n); .) .
+ 2979  
+ 2980  END M2.
 
 
     0 errors
     0 errors
 
 
 
 
 Statistics:
 Statistics:
 
 
-  nr of terminals:       100 (limit   400)
-  nr of non-terminals:    83 (limit   210)
-  nr of pragmas:           0 (limit   400)
-  nr of symbolnodes:     183 (limit   500)
-  nr of graphnodes:      987 (limit  1500)
+  nr of terminals:       104 (limit   400)
+  nr of non-terminals:    84 (limit   210)
+  nr of pragmas:           0 (limit   396)
+  nr of symbolnodes:     188 (limit   500)
+  nr of graphnodes:     1014 (limit  1500)
   nr of conditionsets:    11 (limit   100)
   nr of conditionsets:    11 (limit   100)
   nr of charactersets:    15 (limit   250)
   nr of charactersets:    15 (limit   250)
 
 

+ 18 - 0
compiler/src/QbeGen.def

@@ -129,6 +129,15 @@ PROCEDURE InitStack (addr: ARRAY OF CHAR; t: INTEGER);
 PROCEDURE ArgClass (t: INTEGER): CHAR;
 PROCEDURE ArgClass (t: INTEGER): CHAR;
 (* Formal/actual class for call emission. *)
 (* Formal/actual class for call emission. *)
 
 
+PROCEDURE CArg (q: ARRAY OF CHAR; t: INTEGER; VAR out: QVal;
+                VAR cls: CHAR);
+(* Prepare actual q (of type t) for a C varargs callee: a string or
+   CHAR array becomes descriptor+8 (a C char pointer); otherwise q
+   passes through by its natural class. *)
+PROCEDURE CArgAdd (q: ARRAY OF CHAR; t: INTEGER; VAR out: QVal;
+                   VAR cls: CHAR);
+(* Like CArg but appends the actual to the pending call. *)
+
 PROCEDURE LocFull (): BOOLEAN;
 PROCEDURE LocFull (): BOOLEAN;
 (* TRUE past 64 locals in the current scope (→ 233). *)
 (* TRUE past 64 locals in the current scope (→ 233). *)
 
 
@@ -366,6 +375,15 @@ PROCEDURE SetZero (addr: ARRAY OF CHAR; words: CARDINAL);
 PROCEDURE SetBit (addr: ARRAY OF CHAR; val: ARRAY OF CHAR;
 PROCEDURE SetBit (addr: ARRAY OF CHAR; val: ARRAY OF CHAR;
                    lo: INTEGER; span: CARDINAL);
                    lo: INTEGER; span: CARDINAL);
 (* ORs one element in: bit = val - lo, trapped outside [0, span). *)
 (* ORs one element in: bit = val - lo, trapped outside [0, span). *)
+PROCEDURE SetClearBit (addr: ARRAY OF CHAR; val: ARRAY OF CHAR;
+                       lo: INTEGER; span: CARDINAL);
+(* ANDs one element out (EXCL): bit = val - lo, trapped outside
+   [0, span). *)
+PROCEDURE SetShift (src: ARRAY OF CHAR; n: ARRAY OF CHAR;
+                    words: CARDINAL; span: CARDINAL; isRot: BOOLEAN;
+                    VAR q: QVal);
+(* q := src shifted (SHIFT) or rotated (ROTATE) by n bit positions
+   (a new set temp). *)
 
 
 PROCEDURE SetRange (addr: ARRAY OF CHAR; a: ARRAY OF CHAR;
 PROCEDURE SetRange (addr: ARRAY OF CHAR; a: ARRAY OF CHAR;
                      b: ARRAY OF CHAR; lo: INTEGER; span: CARDINAL);
                      b: ARRAY OF CHAR; lo: INTEGER; span: CARDINAL);

+ 122 - 1
compiler/src/QbeGen.mod

@@ -623,7 +623,9 @@ PROCEDURE DeclConst (name: ARRAY OF CHAR; val: ARRAY OF CHAR; t: INTEGER);
     IF (cls = SymTab.ClStr) OR NOT IsImm(val) THEN
     IF (cls = SymTab.ClStr) OR NOT IsImm(val) THEN
       DataLine(mang, FALSE, "0"); RETURN
       DataLine(mang, FALSE, "0"); RETURN
     END;
     END;
-    IF cls = SymTab.ClReal THEN
+    IF cls = SymTab.ClLong THEN
+      DataLineL(mang, val)
+    ELSIF cls = SymTab.ClReal THEN
       (* This qbe accepts only integer/zero literals in "data":
       (* This qbe accepts only integer/zero literals in "data":
          non-zero REALs flush at BeginBody. *)
          non-zero REALs flush at BeginBody. *)
       DataLine(mang, TRUE, "0");
       DataLine(mang, TRUE, "0");
@@ -1248,6 +1250,26 @@ PROCEDURE ArgClass (t: INTEGER): CHAR;
     RETURN ResClass(t)
     RETURN ResClass(t)
   END ArgClass;
   END ArgClass;
 
 
+PROCEDURE CArg (q: ARRAY OF CHAR; t: INTEGER; VAR out: QVal;
+                VAR cls: CHAR);
+  BEGIN
+    cls := ArgClass(t);
+    IF SymTab.ClassOf(t) = SymTab.ClStr THEN
+      NewTemp(out); Op3L("add", out, q, "8"); cls := "l"
+    ELSIF (SymTab.ClassOf(t) = SymTab.ClArray)
+       AND SymTab.IsCharArray(t) THEN
+      NewTemp(out); Op3L("add", out, q, "8"); cls := "l"
+    ELSE Cpy(out, q)
+    END
+  END CArg;
+
+PROCEDURE CArgAdd (q: ARRAY OF CHAR; t: INTEGER; VAR out: QVal;
+                   VAR cls: CHAR);
+  BEGIN
+    CArg(q, t, out, cls);
+    IF NOT CallArg(out, cls) THEN END
+  END CArgAdd;
+
 (* ---------------- temporaries and operators ---------------- *)
 (* ---------------- temporaries and operators ---------------- *)
 
 
 PROCEDURE NewTemp (VAR t: QVal);
 PROCEDURE NewTemp (VAR t: QVal);
@@ -2168,6 +2190,105 @@ PROCEDURE SetBit (addr: ARRAY OF CHAR; val: ARRAY OF CHAR;
     W("  storew "); W(wn); W(", "); WL(wa)
     W("  storew "); W(wn); W(", "); WL(wa)
   END SetBit;
   END SetBit;
 
 
+PROCEDURE SetClearBit (addr: ARRAY OF CHAR; val: ARRAY OF CHAR;
+                       lo: INTEGER; span: CARDINAL);
+(* ANDs one element out: off = val - lo trapped in [0, span). *)
+  VAR off, offL, hiS, loS: QVal;
+    wi, bi, wil, off4, wa, wcur, m, nm, wn: QVal;
+  BEGIN
+    NewTemp(off);
+    IntStr(lo, loS);
+    Op3("sub", off, val, loS, FALSE);
+    WidenIndex(off, offL);
+    IntStr(VAL(INTEGER, span) - 1, hiS);
+    CheckRange(offL, "0", hiS);
+    NewTemp(wi);
+    Op3("shr", wi, off, "5", FALSE);
+    NewTemp(bi);
+    Op3("and", bi, off, "31", FALSE);
+    WidenIndex(wi, wil);
+    NewTemp(off4);
+    Op3L("mul", off4, wil, "4");
+    NewTemp(wa);
+    Op3L("add", wa, addr, off4);
+    NewTemp(m);
+    Op3("shl", m, "1", bi, FALSE);
+    NewTemp(nm);
+    Op3("xor", nm, m, "-1", FALSE);
+    NewTemp(wcur);
+    W("  "); W(wcur); W(" =w loadw "); WL(wa);
+    NewTemp(wn);
+    Op3("and", wn, wcur, nm, FALSE);
+    Revive;
+    W("  storew "); W(wn); W(", "); WL(wa)
+  END SetClearBit;
+
+PROCEDURE SetShift (src: ARRAY OF CHAR; n: ARRAY OF CHAR;
+                    words: CARDINAL; span: CARDINAL; isRot: BOOLEAN;
+                    VAR q: QVal);
+(* Bit-by-bit shift/rotate by a runtime amount n over [0, span);
+   bits that leave the span are dropped (SHIFT) or wrapped (ROTATE). *)
+  VAR out, nn, sp, hi, i: QVal;
+    wi, bi, wil, off4, wa, m, t, bit, nzb: QVal;
+    j, c, cs, jw: QVal;
+    ok1, ok2, ok, wj, bj, wjl, o2, wb2, mm, mm2, mm3: QVal;
+    wcur, wn, ip1: QVal;
+    ltop, lbody, lend: QVal;
+  BEGIN
+    NewSetTemp(words, out);
+    SetZero(out, words);
+    Cpy(nn, n);
+    IntStr(VAL(INTEGER, span), sp);
+    IntStr(VAL(INTEGER, span) - 1, hi);
+    IF isRot THEN
+      NewTemp(j);   Op3("rem", j, n, sp, FALSE);
+      NewTemp(ok1); Op3("add", ok1, j, sp, FALSE);
+      NewTemp(cs);  Op3("rem", cs, ok1, sp, FALSE);
+      Cpy(nn, cs)
+    END;
+    NewTemp(i);   Op3("add", i, "0", "0", FALSE);
+    NewLabel(ltop); NewLabel(lbody); NewLabel(lend);
+    EmitLabel(ltop);
+    NewTemp(c); Op3("cslew", c, i, hi, FALSE);
+    Jnz(c, lbody, lend);
+    EmitLabel(lbody);
+    NewTemp(wi);   Op3("shr", wi, i, "5", FALSE);
+    NewTemp(bi);   Op3("and", bi, i, "31", FALSE);
+    WidenIndex(wi, wil);
+    NewTemp(off4); Op3L("mul", off4, wil, "4");
+    NewTemp(wa);   Op3L("add", wa, src, off4);
+    NewTemp(m);    Op3("shl", m, "1", bi, FALSE);
+    NewTemp(t);    W("  "); W(t); W(" =w loadw "); WL(wa);
+    NewTemp(bit);  Op3("and", bit, t, m, FALSE);
+    NewTemp(nzb);  Op3("cnew", nzb, bit, "0", FALSE);
+    NewTemp(j);    Op3("add", j, i, nn, FALSE);
+    IF isRot THEN
+      NewTemp(c);  Op3("csgew", c, j, sp, FALSE);
+      NewTemp(cs); Op3("mul", cs, c, sp, FALSE);
+      NewTemp(jw); Op3("sub", jw, j, cs, FALSE);
+      Cpy(j, jw)
+    END;
+    NewTemp(ok1);  Op3("csgew", ok1, j, "0", FALSE);
+    NewTemp(ok2);  Op3("cslew", ok2, j, hi, FALSE);
+    NewTemp(ok);   Op3("and", ok, ok1, ok2, FALSE);
+    NewTemp(wj);   Op3("shr", wj, j, "5", FALSE);
+    NewTemp(bj);   Op3("and", bj, j, "31", FALSE);
+    WidenIndex(wj, wjl);
+    NewTemp(o2);   Op3L("mul", o2, wjl, "4");
+    NewTemp(wb2);  Op3L("add", wb2, out, o2);
+    NewTemp(mm);   Op3("shl", mm, "1", bj, FALSE);
+    NewTemp(mm2);  Op3("mul", mm2, mm, nzb, FALSE);
+    NewTemp(mm3);  Op3("mul", mm3, mm2, ok, FALSE);
+    NewTemp(wcur); W("  "); W(wcur); W(" =w loadw "); WL(wb2);
+    NewTemp(wn);   Op3("or", wn, wcur, mm3, FALSE);
+    Revive;
+    W("  storew "); W(wn); W(", "); WL(wb2);
+    Op3("add", i, i, "1", FALSE);
+    Jmp(ltop);
+    EmitLabel(lend);
+    Cpy(q, out)
+  END SetShift;
+
 PROCEDURE SetRange (addr: ARRAY OF CHAR; a: ARRAY OF CHAR;
 PROCEDURE SetRange (addr: ARRAY OF CHAR; a: ARRAY OF CHAR;
                      b: ARRAY OF CHAR; lo: INTEGER; span: CARDINAL);
                      b: ARRAY OF CHAR; lo: INTEGER; span: CARDINAL);
   VAR al, bl, hiS: QVal;
   VAR al, bl, hiS: QVal;

+ 12 - 0
compiler/src/SymTab.def

@@ -173,6 +173,12 @@ PROCEDURE MarkExternal (link: ARRAY OF CHAR);
    to the C symbol `link`, or to the procedure's own name if ''). *)
    to the C symbol `link`, or to the procedure's own name if ''). *)
 
 
 PROCEDURE IsExternal (name: ARRAY OF CHAR): BOOLEAN;
 PROCEDURE IsExternal (name: ARRAY OF CHAR): BOOLEAN;
+(* TRUE for an EXTERNAL procedure. *)
+PROCEDURE DeclareCProc (name: ARRAY OF CHAR): BOOLEAN;
+(* Enter name as an intrinsic external C procedure (varargs, no
+   result): FROM libc IMPORT. *)
+PROCEDURE Varargs (name: ARRAY OF CHAR): BOOLEAN;
+(* TRUE when the named procedure is a C-varargs intrinsic. *)
 PROCEDURE ProcLink (name: ARRAY OF CHAR; VAR link: Name): BOOLEAN;
 PROCEDURE ProcLink (name: ARRAY OF CHAR; VAR link: Name): BOOLEAN;
 (* External-procedure query/link lookup (link name of the named
 (* External-procedure query/link lookup (link name of the named
    visible procedure). *)
    visible procedure). *)
@@ -424,6 +430,9 @@ PROCEDURE ForwardType (name: ARRAY OF CHAR): TypeIndex;
    the later TypeItem via SetTarget). InvalidType when the name is
    the later TypeItem via SetTarget). InvalidType when the name is
    already known or forward references are not permitted. *)
    already known or forward references are not permitted. *)
 
 
+PROCEDURE IsUnresolved (t: TypeIndex): BOOLEAN;
+(* TRUE for a forward placeholder whose target is not yet declared. *)
+
 PROCEDURE IntType (): TypeIndex;
 PROCEDURE IntType (): TypeIndex;
 PROCEDURE UCharType (): TypeIndex;
 PROCEDURE UCharType (): TypeIndex;
 (* UCHAR: 32-bit Unicode codepoint (QBE w). *)
 (* UCHAR: 32-bit Unicode codepoint (QBE w). *)
@@ -562,6 +571,9 @@ PROCEDURE EqCheck (l, r: TypeIndex): BOOLEAN;
 (* = # compatibility, also reused for CASE label matching. *)
 (* = # compatibility, also reused for CASE label matching. *)
 
 
 PROCEDURE InCheck (l, set: TypeIndex): BOOLEAN;
 PROCEDURE InCheck (l, set: TypeIndex): BOOLEAN;
+(* Membership `l IN set`. *)
+PROCEDURE SetElemClassOk (c: INTEGER): BOOLEAN;
+(* TRUE for an element class acceptable in a set constructor. *)
 PROCEDURE SetElemCheck (first, elem: TypeIndex): BOOLEAN;
 PROCEDURE SetElemCheck (first, elem: TypeIndex): BOOLEAN;
 PROCEDURE SetFor (elem: TypeIndex): TypeIndex;
 PROCEDURE SetFor (elem: TypeIndex): TypeIndex;
 (* Fresh SET OF elem descriptor for set literals. *)
 (* Fresh SET OF elem descriptor for set literals. *)

+ 77 - 4
compiler/src/SymTab.mod

@@ -49,6 +49,7 @@ TYPE
     hasBody : BOOLEAN;  (* KindProc: a body was resumed/emitted *)
     hasBody : BOOLEAN;  (* KindProc: a body was resumed/emitted *)
     ext   : BOOLEAN;    (* KindProc: EXTERNAL (no body emitted) *)
     ext   : BOOLEAN;    (* KindProc: EXTERNAL (no body emitted) *)
     link  : Name;       (* KindProc: external link (C) name *)
     link  : Name;       (* KindProc: external link (C) name *)
+    varargs : BOOLEAN;  (* KindProc: C varargs (FROM libc IMPORT) *)
     sym   : Name;       (* base symbol name (differs from name for
     sym   : Name;       (* base symbol name (differs from name for
                            qualified aliases) *)
                            qualified aliases) *)
     mod   : Name;       (* defining module (for QBE global names) *)
     mod   : Name;       (* defining module (for QBE global names) *)
@@ -288,6 +289,8 @@ PROCEDURE RawEnter (name: ARRAY OF CHAR; kind: INTEGER): SymPtr;
     node^.right := NIL;
     node^.right := NIL;
     node^.val[0] := CHR(0);
     node^.val[0] := CHR(0);
     node^.fsnap := FALSE;   (* no DEFINITION signature snapshotted *)
     node^.fsnap := FALSE;   (* no DEFINITION signature snapshotted *)
+    node^.ext := FALSE;
+    node^.varargs := FALSE;
     IF NOT TreeInsert(curScope, node) THEN RETURN NIL END;
     IF NOT TreeInsert(curScope, node) THEN RETURN NIL END;
     RETURN node
     RETURN node
   END RawEnter;
   END RawEnter;
@@ -981,9 +984,13 @@ PROCEDURE SetSpan (t: TypeIndex; VAR lo, n: INTEGER): BOOLEAN;
     IF r = InvalidType THEN RETURN FALSE END;
     IF r = InvalidType THEN RETURN FALSE END;
     IF tform[r] = FBool THEN lo := 0; n := 2; RETURN TRUE END;
     IF tform[r] = FBool THEN lo := 0; n := 2; RETURN TRUE END;
     IF tform[r] = FChar THEN lo := 0; n := 256; RETURN TRUE END;
     IF tform[r] = FChar THEN lo := 0; n := 256; RETURN TRUE END;
+    IF tform[r] = FEnum THEN
+      IF tenum[r] = 0 THEN RETURN FALSE END;
+      lo := 0; n := VAL(INTEGER, tenum[r]); RETURN TRUE
+    END;
     IF (tform[r] = FSub) AND (tref[r] = InvalidType)
     IF (tform[r] = FSub) AND (tref[r] = InvalidType)
  AND SubBounds(b, blo, bhi) AND (bhi >= blo)
  AND SubBounds(b, blo, bhi) AND (bhi >= blo)
- AND (bhi - blo < 256) THEN
+ AND (bhi - blo < 65536) THEN
       lo := blo; n := bhi - blo + 1; RETURN TRUE
       lo := blo; n := bhi - blo + 1; RETURN TRUE
     END;
     END;
     RETURN FALSE
     RETURN FALSE
@@ -1885,6 +1892,39 @@ PROCEDURE EnterProc (name: ARRAY OF CHAR): BOOLEAN;
     RETURN TRUE
     RETURN TRUE
   END EnterProc;
   END EnterProc;
 
 
+PROCEDURE DeclareCProc (name: ARRAY OF CHAR): BOOLEAN;
+(* Enter name in the current scope as an external C procedure with a
+   permissive (varargs, no-result) signature.  Used for intrinsic
+   `FROM libc IMPORT ...`; not pushed as the current proc. *)
+  VAR node: SymPtr;
+  BEGIN
+    node := RawEnter(name, KindProc);
+    IF node = NIL THEN RETURN FALSE END;
+    node^.rslt := InvalidType;
+    node^.plink := NIL;
+    node^.isVar := FALSE;
+    node^.fwd := FALSE;
+    node^.virt := FALSE;
+    node^.ext := TRUE;
+    node^.varargs := TRUE;
+    node^.fsnap := FALSE;
+    Assign(node^.link, name);
+    node^.uid := nextUid; INC(nextUid);
+    node^.fdep := nProc;
+    node^.vslot := -1;
+    node^.hasBody := TRUE;
+    RETURN TRUE
+  END DeclareCProc;
+
+PROCEDURE Varargs (name: ARRAY OF CHAR): BOOLEAN;
+(* TRUE for a C-varargs procedure (its calls skip arity/type checks). *)
+  VAR node: SymPtr;
+  BEGIN
+    node := Find(name);
+    IF node = NIL THEN RETURN FALSE END;
+    RETURN node^.varargs
+  END Varargs;
+
 PROCEDURE ResumeProc (name: ARRAY OF CHAR): BOOLEAN;
 PROCEDURE ResumeProc (name: ARRAY OF CHAR): BOOLEAN;
 (* Resumes a DEFINITION procedure heading for its IMPLEMENTATION
 (* Resumes a DEFINITION procedure heading for its IMPLEMENTATION
    body.  The definition's signature (result type, formals in order,
    body.  The definition's signature (result type, formals in order,
@@ -2292,11 +2332,25 @@ PROCEDURE EndTypeBlock;
     IF typeBlockDepth > 0 THEN DEC(typeBlockDepth) END
     IF typeBlockDepth > 0 THEN DEC(typeBlockDepth) END
   END EndTypeBlock;
   END EndTypeBlock;
 
 
+PROCEDURE IsUnresolved (t: TypeIndex): BOOLEAN;
+  VAR r: TypeIndex;
+  BEGIN
+    IF (t < 0) OR (t >= VAL(INTEGER, nTypes)) THEN RETURN FALSE END;
+    r := t;
+    RETURN (tform[r] = FAlias) AND (tref[r] = InvalidType)
+  END IsUnresolved;
+
 PROCEDURE ForwardType (name: ARRAY OF CHAR): TypeIndex;
 PROCEDURE ForwardType (name: ARRAY OF CHAR): TypeIndex;
+(* Registers an undeclared type name as an opaque placeholder that a
+   later TYPE declaration completes.  Active inside a TYPE block, and
+   at module declaration level (so a global may reference a type
+   declared further down). *)
   VAR node: SymPtr;
   VAR node: SymPtr;
     t: TypeIndex;
     t: TypeIndex;
   BEGIN
   BEGIN
-    IF typeBlockDepth = 0 THEN RETURN InvalidType END;
+    IF (typeBlockDepth = 0) AND (nProc > 0) THEN
+      RETURN InvalidType
+    END;
     node := Find(name);
     node := Find(name);
     IF node # NIL THEN
     IF node # NIL THEN
       IF node^.kind = KindType THEN RETURN node^.typ END;
       IF node^.kind = KindType THEN RETURN node^.typ END;
@@ -2781,6 +2835,13 @@ PROCEDURE OrdCheck (l, r: TypeIndex): BOOLEAN;
     RETURN FALSE
     RETURN FALSE
   END OrdCheck;
   END OrdCheck;
 
 
+PROCEDURE SetElemClassOk (c: INTEGER): BOOLEAN;
+(* TRUE for an element class acceptable in a set constructor. *)
+  BEGIN
+    RETURN (c = ClInt) OR (c = ClChar) OR (c = ClBool)
+        OR (c = ClEnum) OR (c = ClUChar)
+  END SetElemClassOk;
+
 PROCEDURE InCheck (l, set: TypeIndex): BOOLEAN;
 PROCEDURE InCheck (l, set: TypeIndex): BOOLEAN;
   VAR rs, b: TypeIndex;
   VAR rs, b: TypeIndex;
   BEGIN
   BEGIN
@@ -2793,10 +2854,11 @@ PROCEDURE InCheck (l, set: TypeIndex): BOOLEAN;
     IF (ClassOf(l) = ClChar) AND (ClassOf(b) = ClChar) THEN
     IF (ClassOf(l) = ClChar) AND (ClassOf(b) = ClChar) THEN
       RETURN TRUE
       RETURN TRUE
     END;
     END;
-    (* lenient: small int/char/bool tested against suitable sets;
+    (* lenient: small int/char/bool/enum tested against suitable sets;
        the span trap decides out-of-range at runtime *)
        the span trap decides out-of-range at runtime *)
     IF ((ClassOf(l) = ClInt) OR (ClassOf(l) = ClChar)
     IF ((ClassOf(l) = ClInt) OR (ClassOf(l) = ClChar)
-        OR (ClassOf(l) = ClBool)) AND (SetWords(set) > 0) THEN
+        OR (ClassOf(l) = ClBool) OR (ClassOf(l) = ClEnum))
+       AND (SetWords(set) > 0) THEN
       RETURN TRUE
       RETURN TRUE
     END;
     END;
     RETURN FALSE
     RETURN FALSE
@@ -2885,6 +2947,17 @@ PROCEDURE Init;
     Predef("LONGADDR", KindPredef, dAddr);
     Predef("LONGADDR", KindPredef, dAddr);
     Predef("PROC", KindPredef, dAddr);
     Predef("PROC", KindPredef, dAddr);
     Predef("LOC", KindPredef, dAddr);
     Predef("LOC", KindPredef, dAddr);
+    (* gm2 sized/scalar names (mapped onto V3's families) *)
+    Predef("LONGCARD", KindPredef, dLong);
+    Predef("SHORTREAL", KindPredef, dReal);
+    Predef("INTEGER8", KindPredef, dInt);
+    Predef("INTEGER16", KindPredef, dInt);
+    Predef("INTEGER32", KindPredef, dInt);
+    Predef("INTEGER64", KindPredef, dLong);
+    Predef("CARDINAL8", KindPredef, dCard);
+    Predef("CARDINAL16", KindPredef, dCard);
+    Predef("CARDINAL32", KindPredef, dCard);
+    Predef("CARDINAL64", KindPredef, dLong);
     Predef("NIL", KindConst, dNil)
     Predef("NIL", KindConst, dNil)
   END Init;
   END Init;
 
 

+ 10 - 0
compiler/tests/t_fwdtype.mod

@@ -0,0 +1,10 @@
+MODULE tfwdtype;
+// Module-level forward type reference: a global whose type is declared
+// further down (emission deferred until the TYPE completes).
+VAR g : Later;
+TYPE Later = RECORD x, y : INTEGER END;
+VAR ExitCode : INTEGER;
+BEGIN
+  g.x := 40; g.y := 2;
+  ExitCode := g.x + g.y
+END tfwdtype.

+ 8 - 0
compiler/tests/t_libc.mod

@@ -0,0 +1,8 @@
+MODULE tlibc;
+// FROM libc: intrinsic permissive external (printf/exit bind to C).
+FROM libc IMPORT printf, exit;
+VAR ExitCode : INTEGER;
+BEGIN
+  printf("tlibc %d\n", 42);
+  exit(42)
+END tlibc.

+ 9 - 0
compiler/tests/t_minmax2.mod

@@ -0,0 +1,9 @@
+MODULE tminmax2;
+// MIN/MAX over REAL/LONGINT and gm2 sized type names.
+VAR r : REAL; l : LONGINT; a : LONGCARD; k : INTEGER32;
+VAR ExitCode : INTEGER;
+BEGIN
+  r := MAX(REAL); l := MAX(LONGINT); a := 5; k := 7;
+  IF (r > 0.0) AND (l > 0) AND (a = 5) AND (k = 7) THEN ExitCode := 42
+  ELSE ExitCode := 1 END
+END tminmax2.

+ 15 - 0
compiler/tests/t_setops.mod

@@ -0,0 +1,15 @@
+MODULE tsetops;
+// SET OF enum + SET OF char-range, INCL/EXCL, SHIFT/ROTATE.
+TYPE Colour = (Blue, Red, Green, White);
+     CS = SET OF Colour;
+     Chars = SET OF ['A'..'z'];
+VAR x : CS; c : Chars; r : CS;
+VAR ExitCode : INTEGER;
+BEGIN
+  x := CS {};
+  INCL(x, Red); INCL(x, Blue); EXCL(x, Red);
+  c := Chars {'A'};
+  r := SHIFT(CS {Blue}, 1);          (* {Blue}(bit 0) -> {Red}(bit 1) *)
+  IF (Blue IN x) AND NOT (Red IN x) AND ('A' IN c) AND (Red IN r)
+  THEN ExitCode := 42 ELSE ExitCode := 1 END
+END tsetops.

+ 107 - 0
docs/analysis_gaps.md

@@ -0,0 +1,107 @@
+# m2compiler-V3 — gap analysis (2026-09-30)
+
+Suite **165/165**, self-hosting fixpoint OK (2,745,278 bytes).
+Corpus at this point (with `$V3_LIBS`):
+
+| corpus | compile-OK | CRASH | LANG |
+| --- | --- | --- | --- |
+| gm2 testsuite (612) | 376 | 0 | 227 |
+| gm2 stdlib (336) | 124 | 0 | 212 |
+
+Failure taxonomy (testsuite, per file): `undeclared identifier` 65,
+`not supported yet` 43, `invalid call` 25, `incompatible assignment`
+25, `ident expected` 8, `invalid ImpName` 7.
+
+## 6. Fixed today (items 1–5)
+
+1. **`FROM libc` intrinsic** — `printf`/`exit`/… import as permissive
+   external C procedures (varargs, no result) and bind to the C
+   symbols; string/CHAR-array actuals pass as `descriptor+8`.  This
+   removed the dominant undeclared bucket (136 → 65 files).
+2. **`SET OF enum` and `SET OF <char-range>`** (plus enum subranges
+   `[a..d]`, `ARRAY [FALSE..TRUE]`), and the set span limit lifted
+   256 → 65536.
+3. **`MIN`/`MAX` for REAL/LONGREAL/LONGINT** and gm2 sized names
+   (`LONGCARD`, `SHORTREAL`, `INTEGER8/16/32/64`, `CARDINAL8/…`).
+4. **SYSTEM/PIM set builtins** — `INCL`/`EXCL` (statements) and
+   `SHIFT`/`ROTATE` (expressions), bit-by-bit over a runtime amount.
+5. **Module-level forward type references** — a global may name a type
+   declared further down; its emission is deferred until the TYPE
+   block completes (TYPE-block forwards already worked).
+
+
+## 1. Language features genuinely missing
+
+Ranked by corpus impact (each verified by a failing file or a probe):
+
+1. **Sets over enumerations and CHAR ranges** — `SET OF <enum>` and
+   `SET OF ['A'..'z']` are `230`.  Only `bool`/`char`/integer subrange
+   bases are accepted.  (`goodifin.mod`, `charset2.mod`, `enum3.mod`.)
+2. **`MIN`/`MAX` for REAL/LONGREAL/LONGINT** — only ordinal types,
+   arrays and sets are handled; `MAX(REAL)` / `MIN(LONGINT)` are `230`.
+   (`maxreal.mod`, `minmaxreal2.mod`, `longmm.mod`, `constcast.mod`.)
+3. **gm2 sized types** — `LONGCARD`, `SHORTREAL`, `INTEGER8/16/32/64`,
+   `CARDINAL8/16/32/64`, `LONGWORD` undeclared.
+4. **SYSTEM builtins** — `SHIFT`, `INCL`, `EXCL`, `ROTATE`, `LOC`,
+   sized-bit names (`INCL`/`EXCL`/`SHIFT` recur).
+5. **Enum subranges / BOOLEAN bounds** — `[a..d]`,
+   `ARRAY [FALSE..TRUE] …` are `230`.
+6. **`String`** (gm2 magic type) — used throughout the stdlib corpus.
+7. **`GOTO` + labels** — parse error.
+8. **Whole-variant-record assignment/equality** — deliberately `236`.
+9. **Nested local `MODULE`** — parsed, not lowered.
+10. **Result suffixes** `F()^`.
+11. **Generics** — not started (`docs/analysis_generics.md`).
+12. **Coroutines / processes** (`NEWPROCESS`/`TRANSFER`/`IOTRANSFER`).
+13. **Multiple inheritance** — deliberately out of scope (`docs/OOP.txt`).
+14. **Recursive `DISPOSE`** (shallow today) and `runtime/syslib/Trap`
+    (traps are `$abort`, rc 134).
+
+Procedure types and indirect calls do work (`t_proctype` passes);
+`docs/features.md` still lists them as ⏸ — stale.
+
+## 2. Library
+
+- **gm2-only modules V3 lacks**: `String`, `StrIO`, `RealStr`/`WholeStr`,
+  `LongIO`/`LongMath`/`LongStr`, the `InOut`/`StdIO`/`STextIO`/
+  `SWholeIO`/`SIOResult` family, `IOConsts`, `Termination`,
+  `StdChans`/`StreamFile`/`FIO`, `SysStorage`, `wrapclock`/`time`,
+  `ChanConsts`/`GenDevIF`/`DeviceTablePtr` (the gm2 device/IO framework).
+- **`FROM libc IMPORT printf, exit`** — no intrinsic `libc` module;
+  this is the largest `undeclared identifier` bucket (163 + 141
+  occurrences) and is not a real language gap.
+
+## 3. Architecture / robustness
+
+- **No AST, single pass.**  Declaration-before-use is enforced, so
+  forward references in `CONST`/expressions fail (e.g. `arraycons3.mod`
+  declares `TYPE` after the `CONST` constructor and is rejected).  Only
+  `FORWARD` procedures and `ForwardType` are tolerated.
+- **Single image only** — no per-module object emission, no import
+  cycles.
+- **Driver robustness** — `M2` exits 0 even with errors (harnesses parse
+  the `.LST` verdict) and aborts on a missing input file.
+- **Fixed-size tables** — 64 formals/locals (233 past that), 64 call
+  args, 256 VAR-actual note entries, 4096 string literals, 256 pooled
+  constructors, 8 constructor levels, 4 MiB image buffer.
+- **Weak error recovery** — one bad token cascades into `ident
+  expected` / `'END' expected`.
+
+## 4. Deliberate dialect choices (not gaps)
+
+Classic identifiers (no `_`), `#` not `<>`, no octal `B`/`C`, `0x`-only
+hex, `NOT`/`AND`/`OR` only, TopSpeed legacy isolated in the sidecar.
+
+## 5. Biggest wins (all five landed today — see §6)
+
+1. ~~**`FROM libc` intrinsic**~~ ✅
+2. ~~**`SET OF enum` + `SET OF char-range`**~~ ✅
+3. ~~**`MIN`/`MAX` for REAL/LONGINT** + **gm2 sized types**~~ ✅
+4. ~~**SYSTEM `INCL`/`EXCL`/`SHIFT`/`ROTATE`**~~ ✅
+5. ~~**Forward type references**~~ ✅ (module-level globals; TYPE-block
+   forwards already worked)
+
+Remaining after today: `String`, `GOTO`/labels, variant-record
+assignment, nested-`MODULE` lowering, result suffixes, generics,
+coroutines, multiple inheritance (deliberate); `ADDRESS`↔pointer
+assignment; and the library holes in §2.

+ 12 - 2
docs/features.md

@@ -1,4 +1,4 @@
-# m2compiler-V3 — feature status (at `v3-ctor-gaps`, 161/161 green)
+# m2compiler-V3 — feature status (at `v3-gaps-1-5`, 165/165 green)
 
 
 Pipeline: Coco/R `M2.atg` (1748 lines, 73 productions) → `gm2`-built
 Pipeline: Coco/R `M2.atg` (1748 lines, 73 productions) → `gm2`-built
 `M2` → QBE `.ssa` → `qbe` → `cc` → run. `SymTab.mod` 1623 lines,
 `M2` → QBE `.ssa` → `qbe` → `cc` → run. `SymTab.mod` 1623 lines,
@@ -34,7 +34,17 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
   string concatenation folded into a `CHAR`-array element, included.
   string concatenation folded into a `CHAR`-array element, included.
 - ✅ Index-type arrays `ARRAY <ordinalType> OF T`; enum values usable
 - ✅ Index-type arrays `ARRAY <ordinalType> OF T`; enum values usable
   as array indices.
   as array indices.
-- ✅ `MIN`/`MAX` over any ordinal type, array or set.
+- ✅ `MIN`/`MAX` over any ordinal type, array or set; REAL/LONGREAL
+  and LONGINT bounds too.
+- ✅ gm2 sized/scalar names: `LONGCARD`, `SHORTREAL`,
+  `INTEGER8/16/32/64`, `CARDINAL8/16/32/64`.
+- ✅ `FROM libc IMPORT printf, exit, …`: intrinsic permissive external
+  C procedures (varargs, no result); string/CHAR-array actuals pass as
+  `descriptor+8`.
+- ✅ Set builtins `INCL`/`EXCL` and `SHIFT`/`ROTATE`; `SET OF <enum>`
+  and `SET OF <char-range>`; set spans up to 65536.
+- ✅ Module-level forward type references (a global may name a type
+  declared further down; emission deferred).
 - ✅ `RECORD` (flat blobs, nested inline, array fields as pointers),
 - ✅ `RECORD` (flat blobs, nested inline, array fields as pointers),
   including variant records (`RECORD … ; CASE tag : T OF … END`): plain
   including variant records (`RECORD … ; CASE tag : T OF … END`): plain
   fields pack before the tag, each branch's fields overlay after it.
   fields pack before the tag, each branch's fields overlay after it.

+ 19 - 5
docs/summary_v3-corpus.md

@@ -1,6 +1,6 @@
 # Step: V3 compiler corpus runs
 # Step: V3 compiler corpus runs
 
 
-Tag `v3-corpus-run`. V3 suite **161/161**, fixpoint OK.
+Tag `v3-corpus-run`. V3 suite **165/165**, fixpoint OK.
 
 
 ## Why
 ## Why
 
 
@@ -22,19 +22,19 @@ gap).
 
 
 | | no libs | with `$V3_LIBS` |
 | | no libs | with `$V3_LIBS` |
 | --- | --- | --- |
 | --- | --- | --- |
-| **compile-OK** | 242 | **301** |
+| **compile-OK** | 242 | **376** |
 | CRASH | 0 | 0 |
 | CRASH | 0 | 0 |
 | LIB | 185 | **9** |
 | LIB | 185 | **9** |
-| LANG | 185 | **302** |
+| LANG | 185 | **227** |
 
 
 **2. gm2 standard libraries** (`m2pim`/`m2iso`/`m2log`/`m2cor`/`m2min`,
 **2. gm2 standard libraries** (`m2pim`/`m2iso`/`m2log`/`m2cor`/`m2min`,
 336 files):
 336 files):
 
 
 | | no libs | with `$V3_LIBS` |
 | | no libs | with `$V3_LIBS` |
 | --- | --- | --- |
 | --- | --- | --- |
-| **compile-OK** | 78 | **115** |
+| **compile-OK** | 78 | **124** |
 | LIB | 176 | **0** |
 | LIB | 176 | **0** |
-| LANG | 82 | **221** |
+| LANG | 82 | **212** |
 
 
 (With the import path set, LIB noise is gone and the true LANG counts
 (With the import path set, LIB noise is gone and the true LANG counts
 are visible.)
 are visible.)
@@ -90,6 +90,20 @@ are visible.)
 12. **`MIN`/`MAX`** builtins over any ordinal type, array, or set
 12. **`MIN`/`MAX`** builtins over any ordinal type, array, or set
     (`MIN(Colour)`, `MAX(large)`, `ARRAY [MIN(c)..MAX(c)] OF T`);
     (`MIN(Colour)`, `MAX(large)`, `ARRAY [MIN(c)..MAX(c)] OF T`);
     `SymTab.TypeBounds` + enum literal counts.
     `SymTab.TypeBounds` + enum literal counts.
+13. **`FROM libc` intrinsic** — `printf`/`exit`/… import as permissive
+    external C procedures (varargs, no result) and bind to the C
+    symbols; string/CHAR-array actuals pass as `descriptor+8`.
+14. **Sets over enums and CHAR ranges** — `SET OF <enum>`,
+    `SET OF ['A'..'z']`, enum/`BOOLEAN` subranges; span limit
+    256 → 65536.
+15. **`MIN`/`MAX` for REAL/LONGINT** and **gm2 sized names**
+    (`LONGCARD`, `SHORTREAL`, `INTEGER8/16/32/64`,
+    `CARDINAL8/16/32/64`).
+16. **Set builtins** `INCL`/`EXCL` (statements) and `SHIFT`/`ROTATE`
+    (expressions, bit-by-bit over a runtime amount).
+17. **Module-level forward type references** — a global may name a type
+    declared further down; emission is deferred until the TYPE block
+    completes (TYPE-block forwards already worked).
 
 
 ## Remaining LANG gaps
 ## Remaining LANG gaps
 
 

+ 1 - 1
tools/v3-corpus/corpus.sh

@@ -18,7 +18,7 @@ cd "$(dirname "$0")"
 ROOT="$(cd ../.. && pwd)"
 ROOT="$(cd ../.. && pwd)"
 M2="$ROOT/compiler/M2"
 M2="$ROOT/compiler/M2"
 WORK=/tmp/opencode/v3corpus
 WORK=/tmp/opencode/v3corpus
-KNOWN="SYSTEM SysIO TextIO Strings Math Conversions RealIO ProgramArgs IOChan Files Storage WholeIO CharClass SysClock"
+KNOWN="SYSTEM libc SysIO TextIO Strings Math Conversions RealIO ProgramArgs IOChan Files Storage WholeIO CharClass SysClock"
 [ -x "$M2" ] || { echo "compiler/M2 missing -- run compiler/build.sh"; exit 1; }
 [ -x "$M2" ] || { echo "compiler/M2 missing -- run compiler/build.sh"; exit 1; }
 [ "$#" -gt 0 ] || set -- \
 [ "$#" -gt 0 ] || set -- \
   "$HOME/Projets/Projets-Modula2/MyWork/gcc-git/gcc/testsuite/gm2/pim/pass" \
   "$HOME/Projets/Projets-Modula2/MyWork/gcc-git/gcc/testsuite/gm2/pim/pass" \