فهرست منبع

unicode inc.1: UCHAR + U'..'/U".." strict-RFC3629 literals (234), UCHR/CHR8/UORD

- SymTab: UCHAR (ClUChar/FUChar, w, 0..1114111) + UString literal type
  (ClUStr/FUStr); OrdCheck/EqCheck accept UCHAR vs UCHAR and
  LONGCARD vs INTEGER (long-family compare widens the int side).
- QbeGen: DeclUStr strict-RFC3629 decode (overlong/surrogate/>10FFFF/
  truncation -> ok=FALSE); UString descriptor emission
  (data $ustrN = { l count, w cp... }) via FlushUStrings; pool reset.
- M2.atg: ustring token; Fact branch (single cp -> UCHAR immediate,
  else UString); UCHR/CHR8/UORD builtins (CHR8 l-domain 0..255 check).
- compiler.frm: message 234 'invalid UTF-8 in U-literal'.
- tests: t_unicode (31), t_trap_chr8, t_bad_{overlong,surrogate,
  utf8big,truncutf} -> 118/118. Fixpoint OK (2,060,379 bytes).
- docs: summary_unicode, plan.req/features status.
Eric Streit 2 هفته پیش
والد
کامیت
af09dc6ce2

+ 6 - 0
compiler/run_tests.sh

@@ -69,6 +69,7 @@ expect_run t_emptystat.mod 42
 expect_run t_highlen.mod 18
 expect_run t_builtins.mod 42
 expect_run t_long.mod 42
+expect_run t_unicode.mod 31
 expect_run t_charbounds.mod 3
 expect_run t_record.mod 160
 expect_run t_with.mod 114
@@ -112,6 +113,10 @@ expect_fail t_bad_exit.mod "not supported yet"
 expect_fail t_bad_return.mod "bad RETURN"
 expect_fail t_bad_for.mod "FOR needs integer variable and bounds"
 expect_fail t_bad_mixed.mod "incompatible assignment"
+expect_fail t_bad_overlong.mod "invalid UTF-8"
+expect_fail t_bad_surrogate.mod "invalid UTF-8"
+expect_fail t_bad_utf8big.mod "invalid UTF-8"
+expect_fail t_bad_truncutf.mod "invalid UTF-8"
 expect_fail t_bad_arity.mod "invalid call"
 expect_fail t_bad_actual.mod "invalid call"
 expect_fail t_bad_funstmt.mod "invalid call"
@@ -156,6 +161,7 @@ expect_trap t_trap_index.mod
 expect_trap t_trap_count.mod
 expect_trap t_trap_setspan.mod
 expect_trap t_trap_emptystr.mod
+expect_trap t_trap_chr8.mod
 expect_fail t_defunit.mod ""
 expect_fail t_implunit.mod ""
 expect_run t_nested.mod 42

+ 43 - 3
compiler/src/M2.atg

@@ -78,6 +78,7 @@ TOKENS
   string  = "'" { noQuote1 } "'"
           | '"' { noQuote2 } '"' .
   charConst = digit { digit } ( "C" | "c" ) .
+  ustring = ( "U" | "u" ) ( "'" { noQuote1 } "'" | '"' { noQuote2 } '"' ) .
 
 PRODUCTIONS
   M2
@@ -198,6 +199,7 @@ PRODUCTIONS
                                              SemError(201) END; .)
     | ( "TSIZE" | "SIZE" | "ADR" | "HIGH" | "LEN"
       | "CHR" | "ORD" | "ORDL" | "VAL" | "ABS" | "CAP"
+      | "UCHR" | "CHR8" | "UORD"
       | "INC" | "DEC" ) .
   ImpModList                            (. VAR n: SymTab.Name; .)
     = GetIdent<n>
@@ -593,7 +595,9 @@ PRODUCTIONS
  AND (cls # SymTab.ClRecord)
  AND (cls # SymTab.ClPtr)
  AND (cls # SymTab.ClLong)
- AND (cls # SymTab.ClProc) THEN
+ AND (cls # SymTab.ClProc)
+ AND (cls # SymTab.ClUChar)
+ AND (cls # SymTab.ClUStr) THEN
                                              SemError(230) END;
                                            IF QbeGen.LocFull() THEN
                                              SemError(233) END;
@@ -1443,6 +1447,7 @@ PRODUCTIONS
                                                IF (cls = SymTab.ClInt)
                                                   OR (cls = SymTab.ClBool)
                                                   OR (cls = SymTab.ClChar)
+                                                  OR (cls = SymTab.ClUChar)
                                                   OR (cls
                                                       = SymTab.ClReal) THEN
                                                  QbeGen.LoadVar(n,
@@ -1458,6 +1463,8 @@ PRODUCTIONS
                                                       = SymTab.ClSet)
                                                   OR (cls
                                                       = SymTab.ClRecord)
+                                                  OR (cls
+                                                      = SymTab.ClUStr)
                                                   OR (cls
                                                       = SymTab.ClClass) THEN
                                                  QbeGen.AddrOf(n, q)
@@ -1948,8 +1955,9 @@ PRODUCTIONS
                                              qn, vn: SymTab.Name;
                                              vt: SymTab.TypeIndex;
                                              c1, c2: INTEGER;
-                                             called, isHigh, sfx, isCh:
-                                               BOOLEAN; .)
+                                             called, isHigh, sfx, isCh,
+                                             isU, uok: BOOLEAN;
+                                             ucp: INTEGER; .)
     = integer                           (. LexString(s);
                                            QbeGen.NormInt(s, q);
                                            t := SymTab.IntType(); .)
@@ -1970,6 +1978,19 @@ PRODUCTIONS
                                                 static descriptor address *)
                                              QbeGen.NoteAddr(q, q)
                                            END; .)
+    | ustring                           (. LexString(s);
+                                           QbeGen.DeclUStr(s, q, isU, ucp,
+                                             uok);
+                                           IF NOT uok THEN
+                                             SemError(234);
+                                             t := SymTab.InvalidType
+                                           ELSIF isU THEN
+                                             t := SymTab.UCharType();
+                                             QbeGen.IntStr(ucp, q)
+                                           ELSE
+                                             t := SymTab.NewUStr();
+                                             QbeGen.NoteAddr(q, q)
+                                           END; .)
     | Design<dt, dk, qd, qn, sfx>       (. called := FALSE;
                                            t := dt;
                                            IF sfx THEN
@@ -2119,6 +2140,25 @@ PRODUCTIONS
                                         (. QbeGen.CapQ(q, qa);
                                            QbeGen.CopyOp(qa, q);
                                            t := SymTab.CharType(); .)
+    | "UCHR" "(" Expr<et, q> ")"
+                                        (. IF (et # SymTab.InvalidType)
+  AND (SymTab.ClassOf(et) # SymTab.ClChar) THEN
+                                             SemError(211) END;
+                                           t := SymTab.UCharType(); .)
+    | "CHR8" "(" Expr<et, q> ")"
+                                        (. IF (et # SymTab.InvalidType)
+  AND (SymTab.ClassOf(et) # SymTab.ClUChar) THEN
+                                             SemError(211) END;
+                                           QbeGen.WidenLong(q, qa);
+                                           QbeGen.CheckRange(qa, "0", "255");
+                                           t := SymTab.CharType(); .)
+    | "UORD" "(" Expr<et, q> ")"
+                                        (. IF (et # SymTab.InvalidType)
+  AND (SymTab.ClassOf(et) # SymTab.ClUChar) THEN
+                                             SemError(211) END;
+                                           QbeGen.WidenLong(q, qa);
+                                           QbeGen.CopyOp(qa, q);
+                                           t := SymTab.LongType(); .)
     | "ABS" "(" Expr<et, q> ")"
                                         (. IF (et # SymTab.InvalidType)
  AND NOT SymTab.IsIntFamily(et)

+ 2223 - 2183
compiler/src/M2.lst

@@ -95,2209 +95,2249 @@ Listing:
    78    string  = "'" { noQuote1 } "'"
    79            | '"' { noQuote2 } '"' .
    80    charConst = digit { digit } ( "C" | "c" ) .
-   81  
-   82  PRODUCTIONS
-   83    M2
-   84      = Unit "." .
-   85    (* Units: program modules compile fully; DEFINITION and
-   86       IMPLEMENTATION modules parse + check now but lower in step 4
-   87       (each ends with one 230); same for nested local modules. *)
-   88    Unit
-   89      = DefUnit
-   90      | ImplUnit
-   91      | ProgModule .
-   92    (* Step 4.3: one session compiles DEFINITION, its IMPLEMENTATION
-   93       and one program (last) into one image. Units share the symbol
-   94       table; imports materialize exported names. *)
-   95    DefUnit                               (. VAR m1, m2, pn: SymTab.Name; .)
-   96      = "DEFINITION" "MODULE"
-   97        GetIdent<m1>                      (. IF NOT SymTab.BeginDef(m1) THEN
-   98                                               SemError(200) END;
-   99                                             QbeGen.SetModule(m1); .)
-  100        ";"
-  101        { Import }
-  102        { ConstBlock | TypeBlock<TRUE> | VarBlock
-  103        | ProcHeading<pn> ";"             (. SymTab.CloseProc;
-  104                                               QbeGen.AbortFunc; .) }
-  105        "END"
-  106        GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
-  107                                               SemError(202) END;
-  108                                             SymTab.EndUnit; .) .
-  109    ImplUnit                              (. VAR m1, m2: SymTab.Name; .)
-  110      = "IMPLEMENTATION" "MODULE"
-  111        GetIdent<m1>                      (. IF NOT SymTab.BeginImpl(m1) THEN
-  112                                               SemError(201) END;
-  113                                             QbeGen.SetModule(m1); .)
-  114        ";"
-  115        { Import }
-  116        DeclSeq
-  117        [ "BEGIN"                         (. QbeGen.BeginInit(m1); .)
-  118          [ StatSeq ]                       (. QbeGen.EndInit; .) ]
-  119        "END"
-  120        GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
-  121                                               SemError(202) END;
-  122                                             SymTab.EndUnit; .) .
-  123    ProgModule                            (. VAR m1, m2: SymTab.Name; .)
-  124      = "MODULE"
-  125        GetIdent<m1>                      (. IF NOT SymTab.BeginProg(m1) THEN
-  126                                               SemError(200) END;
-  127                                             QbeGen.SetModule(m1); .)
-  128        [ Priority ]
-  129        ";"
-  130        { Import }
-  131        DeclSeq
-  132        [ "BEGIN"                         (. QbeGen.BeginBody; .)
-  133          [ StatSeq ] ]
-  134        "END"
-  135        GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
-  136                                               SemError(202) END;
-  137                                             QbeGen.EndModule(m1);
-  138                                             SymTab.EndUnit; .) .
-  139    DeclSeq
-  140      = { ConstBlock | TypeBlock<FALSE> | VarBlock | ProcDecl ";"
-  141        | NestedModule ";" | ClassItem ";" } .
-  142    (* Local module, Wirth form. Declarations lower like top-level ones
-  143       (same QBE module prefix); a BEGIN body becomes an init function
-  144       that main calls; the EXPORT list is hoisted into the enclosing
-  145       scope at END. *)
-  146    NestedModule                          (. VAR m1, m2: SymTab.Name;
-  147                                               expNames: ARRAY [0 .. 63] OF SymTab.Name;
-  148                                               expCount, k: CARDINAL; .)
-  149      = "MODULE"
-  150        GetIdent<m1>                      (. IF NOT SymTab.Enter(m1,
-  151                                               SymTab.KindModule) THEN
-  152                                               SemError(200) END;
-  153                                             SymTab.PushScope;
-  154                                             expCount := 0; .)
-  155        [ Priority ]
-  156        ";"
-  157        { Import }
-  158        [ "EXPORT" [ "QUALIFIED" ]
-  159          GetIdent<expNames[expCount]>    (. INC(expCount); .)
-  160          { "," GetIdent<expNames[expCount]>
-  161                                          (. INC(expCount); .) }
-  162          ";" ]
-  163        DeclSeq
-  164        [ "BEGIN"                         (. QbeGen.BeginInit(m1); .)
-  165          [ StatSeq ]                     (. QbeGen.EndInit; .) ]
-  166        "END"
-  167        GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
-  168                                               SemError(202) END;
-  169                                             k := 0;
-  170                                             WHILE k < expCount DO
-  171                                               SymTab.ExportUp(expNames[k]);
-  172                                               INC(k)
-  173                                             END;
-  174                                             SymTab.PopScope; .) .
-  175    Priority
-  176      = "[" integer "]"                   (. SemError(230); .) .
-  177    (* Imports (4.3): FROM materializes the names (unqualified use);
-  178       plain IMPORT only demands the module exists — qualified `L.x`
-  179       materializes on first use (Design). *)
-  180    (* Unknown modules stay unchecked stubs (legacy, so hand-written
-  181       import lines don't fail); a known module's missing export is
-  182       201. *)
-  183    Import                                (. VAR n: SymTab.Name; .)
-  184      = "FROM"
-  185        GetIdent<n>
-  186        "IMPORT"
-  187        ImpList<n> ";"
-  188      | "IMPORT"
-  189        ImpModList ";" .
-  190    ImpList<mod: SymTab.Name>             (. VAR n: SymTab.Name; .)
-  191      = ImpName<mod>
-  192        { "," ImpName<mod> } .
-  193    (* Pervasive built-ins imported from SYSTEM (e.g. TSIZE) are
-  194       accepted and ignored: the built-in applies regardless. *)
-  195    ImpName<mod: SymTab.Name>             (. VAR n: SymTab.Name; .)
-  196      = GetIdent<n>                       (. IF SymTab.ModKnown(mod)
-  197                                               AND NOT SymTab.ImportFrom(mod, n) THEN
-  198                                               SemError(201) END; .)
-  199      | ( "TSIZE" | "SIZE" | "ADR" | "HIGH" | "LEN"
-  200        | "CHR" | "ORD" | "ORDL" | "VAL" | "ABS" | "CAP"
-  201        | "INC" | "DEC" ) .
-  202    ImpModList                            (. VAR n: SymTab.Name; .)
-  203      = GetIdent<n>
-  204        { "," GetIdent<n> } .
-  205    (* Opaque TYPE declarations (definition modules). The targetless
-  206       alias resolves to InvalidType until step 4 completes it. *)
-  207    (* Scalar-phase TYPEs: named types, integer subranges, enumerations.
-  208       Opaque "TYPE T;" needs isDef (definition units); elsewhere 231.
-  209       Composite forms (ARRAY/RECORD/SET/POINTER) arrive with step 3. *)
-  210    TypeBlock<isDef: BOOLEAN>
-  211      = "TYPE"                            (. SymTab.BeginTypeBlock; .)
-  212        { TypeItem<isDef> ";" | ClassItem ";" }
-  213                                          (. SymTab.EndTypeBlock; .) .
-  214    TypeItem<isDef: BOOLEAN>              (. VAR n: SymTab.Name;
-  215                                               t, op: SymTab.TypeIndex; .)
-  216      = GetIdent<n>                       (. op := SymTab.OpaqueBase(n);
-  217                                             IF op = SymTab.InvalidType THEN
-  218                                               IF NOT SymTab.Enter(n,
-  219                                                  SymTab.KindType) THEN
-  220                                                 SemError(200) END
-  221                                             END; .)
-  222        ( "=" Type<t, FALSE>              (. IF op # SymTab.InvalidType THEN
-  223                                               SymTab.SetTarget(op, t)
-  224                                             ELSE SymTab.SetSymType(n, t)
-  225                                             END; .)
-  226        |                                 (. IF op # SymTab.InvalidType THEN
-  227                                               (* stays opaque *)
-  228                                             ELSIF NOT isDef THEN
-  229                                               SemError(231)
-  230                                             ELSE SymTab.SetSymType(n,
-  231                                               SymTab.NewAlias()) END; .) ) .
-  232    Type<VAR t: SymTab.TypeIndex; allowOpen: BOOLEAN>
-  233      = TypeIdent<t>
-  234      | Subrange<t>
-  235      | Enum<t>
-  236      | ArrayType<t, allowOpen>
-  237      | SetType<t>
-  238      | RecordType<t>
-  239      | PointerType<t>
-  240      | ProcType<t> .
-  241    PointerType<VAR t: SymTab.TypeIndex>  (. VAR base: SymTab.TypeIndex; .)
-  242      = "POINTER" "TO" Type<base, FALSE>(. t := SymTab.NewPtr(base); .) .
-  243    (* Procedure types (step 8.5): PROCEDURE (params): result. Values
-  244       are code pointers; params are collected into the descriptor. *)
-  245    ProcType<VAR t: SymTab.TypeIndex>     (. VAR res, pt: SymTab.TypeIndex;
-  246                                               isV: BOOLEAN; .)
-  247      = "PROCEDURE"                       (. res := SymTab.InvalidType;
-  248                                             t := SymTab.NewProcType(res); .)
-  249        [ "(" [ ProcTypeSection<t> { ";" ProcTypeSection<t> } ] ")" ]
-  250        [ ":" TypeIdent<res>              (. SymTab.SetProcTypeRes(t, res); .) ] .
-  251    ProcTypeSection<t: SymTab.TypeIndex>  (. VAR pt: SymTab.TypeIndex;
-  252                                               isV: BOOLEAN;
-  253                                               cnt, k: CARDINAL;
-  254                                               names: ARRAY [0 .. 15] OF SymTab.Name; .)
-  255      =                                   (. isV := FALSE; cnt := 0; .)
-  256        [ "VAR"                           (. isV := TRUE; .) ]
-  257        GetIdent<names[cnt]>              (. INC(cnt); .)
-  258        { "," GetIdent<names[cnt]>        (. INC(cnt); .) }
-  259        ( ":" Type<pt, FALSE>             (. k := 0;
-  260                                             WHILE k < cnt DO
-  261                                               SymTab.ProcTypeAdd(t, isV, pt);
-  262                                               INC(k)
-  263                                             END; .)
-  264        |                                 (. (* type-only parameter list:
-  265                                                each name is a type (GNU
-  266                                                shorthand used by the
-  267                                                Coco/R scanner frame) *)
-  268                                             k := 0;
-  269                                             WHILE k < cnt DO
-  270                                               IF SymTab.Lookup(names[k])
-  271                                                  AND ((SymTab.SymKind(names[k]) = SymTab.KindType)
-  272    OR (SymTab.SymKind(names[k]) = SymTab.KindPredef)) THEN
-  273                                                 pt := SymTab.SymType(names[k])
-  274                                               ELSE SemError(201);
-  275                                                 pt := SymTab.InvalidType
-  276                                               END;
-  277                                               SymTab.ProcTypeAdd(t, isV, pt);
-  278                                               INC(k)
-  279                                             END; .) ) .
-  280    (* Arrays: "OF" without bounds is an open formal (allowed only
-  281       where allowOpen); "[lo..hi, ...]" nests bounded levels inside
-  282       out. Bounds are folded literals (int/char); anything else 230.
-  283       Bare-type indices ("ARRAY Color OF") wait for enum ordinals. *)
-  284    ArrayType<VAR t: SymTab.TypeIndex; allowOpen: BOOLEAN>
-  285                                          (. VAR elem: SymTab.TypeIndex;
-  286                                               ok: BOOLEAN;
-  287                                               bnds, bndh: ARRAY [0 .. 7] OF INTEGER;
-  288                                               nb, k: CARDINAL; .)
-  289      = "ARRAY"
-  290        ( "OF" Type<elem, FALSE>          (. IF NOT allowOpen THEN
-  291                                               SemError(230) END;
-  292                                             t := SymTab.NewOpenArray(elem); .)
-  293        | "["                             (. SymTab.BoundBegin; ok := TRUE; .)
-  294          BoundPair<ok>
-  295          { "," BoundPair<ok> }
-  296          "]"                             (. (* snapshot before the element
-  297                                                type, which reuses the bound
-  298                                                buffer for a nested ARRAY *)
-  299                                             nb := SymTab.BoundCount();
-  300                                             k := 0;
-  301                                             WHILE k < nb DO
-  302                                               bnds[k] := SymTab.BoundLo(k);
-  303                                               bndh[k] := SymTab.BoundHi(k);
-  304                                               INC(k)
-  305                                             END; .)
-  306          "OF" Type<elem, FALSE>
-  307                                          (. IF ok THEN
-  308                                               k := nb;
-  309                                               WHILE k > 0 DO
-  310                                                 DEC(k);
-  311                                                 elem := SymTab.NewArrayB(
-  312                                                   elem, bnds[k], bndh[k])
-  313                                               END;
-  314                                               t := elem
-  315                                             ELSE t := SymTab.InvalidType
-  316                                             END; .) ) .
-  317    BoundPair<VAR ok: BOOLEAN>            (. VAR tlo, thi: SymTab.TypeIndex;
-  318                                               qlo, qhi: QbeGen.QVal;
-  319                                               lo, hi: INTEGER;
-  320                                               cl, cl2: INTEGER; .)
-  321      = Expr<tlo, qlo> ".." Expr<thi, qhi>
-  322                                          (. IF (tlo = SymTab.InvalidType)
-  323                                              OR (thi = SymTab.InvalidType) THEN
-  324                                             ok := FALSE
-  325                                           ELSE cl := SymTab.ClassOf(tlo);
-  326                                             cl2 := SymTab.ClassOf(thi);
-  327                                             IF ((cl # SymTab.ClInt)
-  328   AND (cl # SymTab.ClChar))
-  329                                                OR ((cl2 # SymTab.ClInt)
-  330   AND (cl2 # SymTab.ClChar)) THEN
-  331                                               SemError(230); ok := FALSE
-  332                                             ELSIF NOT SymTab.ConstInt(qlo, lo)
-  333                                                OR NOT SymTab.ConstInt(qhi, hi)
-  334                                                OR (lo > hi) THEN
-  335                                               SemError(230); ok := FALSE
-  336                                              ELSIF NOT SymTab.BoundAdd(lo, hi) THEN
-  337                                                SemError(230); ok := FALSE
-  338                                              END;
-  339                                            END; .) .
-  340    (* Sets: multi-word masks over bases ≤ 256 values (bool, char,
-  341       bounded subranges; enums wait for ordinals, INTEGER is
-  342       unbounded). Literals are SET OF [0..255]; assignment and
-  343       comparison across suitable bases are lenient (masks over min
-  344       words + zero-check extras), out-of-span literals are 222. *)
-  345    SetType<VAR t: SymTab.TypeIndex>      (. VAR base: SymTab.TypeIndex;
-  346                                               blo, bhi, bspan: INTEGER;
-  347                                               bcls: INTEGER; .)
-  348      = "SET" "OF" Type<base, FALSE>
-  349                                          (. IF base = SymTab.InvalidType THEN
-  350                                               t := SymTab.InvalidType
-  351                                             ELSE bcls :=
-  352                                                    SymTab.ClassOf(base);
-  353                                               IF bcls = SymTab.ClBool THEN
-  354                                                 blo := 0; bspan := 2
-  355                                               ELSIF bcls = SymTab.ClChar THEN
-  356                                                 blo := 0; bspan := 256
-  357                                               ELSIF SymTab.SubBounds(base,
-  358                                                       blo, bhi) THEN
-  359                                                 bspan := bhi - blo + 1
-  360                                               ELSE bspan := 0 END;
-  361                                               IF (bspan <= 0)
-  362                                                  OR (bspan > 256) THEN
-  363                                                 SemError(230);
-  364                                                 t := SymTab.InvalidType
-  365                                               ELSE t := SymTab.NewSet(base)
-  366                                               END
-  367                                             END; .) .
-  368    (* Records: flat blobs; array fields are pointers to static
-  369       descriptors (locked amendment), nested records inline. Field
-  370       offsets static and declaration-ordered. *)
-  371    RecordType<VAR t: SymTab.TypeIndex>   (. VAR t2: SymTab.TypeIndex; .)
-  372      = "RECORD"                          (. t := SymTab.NewRecord(); .)
-  373        [ RecField<t> { ";" [ RecField<t> ] } ]
-  374        "END" .
-  375    RecField<rec: SymTab.TypeIndex>       (. VAR n: SymTab.Name;
-  376                                               t2: SymTab.TypeIndex; .)
-  377      = RecIdents<rec> ":" Type<t2, FALSE>(. SymTab.FixPendingF(rec, t2); .) .
-  378    RecIdents<rec: SymTab.TypeIndex>      (. VAR n: SymTab.Name; .)
-  379      = GetIdent<n>                       (. IF NOT SymTab.FieldPending(rec,
-  380                                               n) THEN
-  381                                               SemError(200) END; .)
-  382        { "," GetIdent<n>                 (. IF NOT SymTab.FieldPending(rec,
-  383                                               n) THEN
-  384                                               SemError(200) END; .) } .
-  385    TypeIdent<VAR t: SymTab.TypeIndex>    (. VAR n, qid: SymTab.Name;
-  386                                               k: INTEGER;
-  387                                               dotted: BOOLEAN; .)
-  388      = GetIdent<n>                       (. dotted := FALSE;
-  389                                             IF NOT SymTab.Lookup(n) THEN
-  390                                               k := -1;
-  391                                               t := SymTab.ForwardType(n);
-  392                                               IF t = SymTab.InvalidType THEN
-  393                                                 SemError(201)
-  394                                               END
-  395                                             ELSE k := SymTab.SymKind(n);
-  396                                               IF (k = SymTab.KindType)
-  397                                                  OR (k = SymTab.KindPredef) THEN
-  398                                                 t := SymTab.SymType(n)
-  399                                               ELSE t := SymTab.InvalidType
-  400                                               END
-  401                                             END; .)
-  402        [ "." GetIdent<qid>               (. dotted := TRUE;
-  403                                             IF k = SymTab.KindModule THEN
-  404                                               t := SymTab.QualType(n, qid);
-  405                                               IF t = SymTab.InvalidType THEN
-  406                                                 SemError(201)
-  407                                               END
-  408                                             ELSE SemError(221);
-  409                                               t := SymTab.InvalidType
-  410                                             END; .) ]
-  411                                          (. IF NOT dotted THEN
-  412                                               IF (k = SymTab.KindModule)
-  413                                                  OR ((k # SymTab.KindType)
-  414    AND (k # SymTab.KindPredef)
-  415    AND (k # SymTab.KindImport)
-  416    AND (k # -1)) THEN
-  417                                                 SemError(221)
-  418                                               END
-  419                                             END; .) .
-  420    Subrange<VAR t: SymTab.TypeIndex>     (. VAR tlo, thi: SymTab.TypeIndex;
-  421                                               qlo, qhi: QbeGen.QVal;
-  422                                               lo, hi: INTEGER; .)
-  423      = "[" Expr<tlo, qlo> ".." Expr<thi, qhi>
-  424                                          (. IF (tlo = SymTab.InvalidType)
-  425                                              OR (thi = SymTab.InvalidType) THEN
-  426                                             t := SymTab.InvalidType
-  427                                           ELSIF (SymTab.ClassOf(tlo) #
-  428                                                  SymTab.ClInt)
-  429                                              OR (SymTab.ClassOf(thi) #
-  430                                                  SymTab.ClInt) THEN
-  431                                             SemError(230);
-  432                                             t := SymTab.InvalidType
-  433                                           ELSIF NOT SymTab.ConstInt(qlo, lo)
-  434                                              OR NOT SymTab.ConstInt(qhi, hi)
-  435                                              OR (lo > hi) THEN
-  436                                             SemError(230);
-  437                                             t := SymTab.InvalidType
-  438                                           ELSE t := SymTab.NewSubR(lo, hi)
-  439                                           END; .)
-  440        "]" .
-  441    Enum<VAR t: SymTab.TypeIndex>         (. VAR n: SymTab.Name; .)
-  442      = "("                               (. t := SymTab.NewEnum(); .)
-  443        GetIdent<n>                       (. IF NOT SymTab.Enter(n,
-  444                                               SymTab.KindConst) THEN
-  445                                               SemError(200) END;
-  446                                             SymTab.SetSymType(n, t); .)
-  447        { "," GetIdent<n>                 (. IF NOT SymTab.Enter(n,
-  448                                               SymTab.KindConst) THEN
-  449                                               SemError(200) END;
-  450                                             SymTab.SetSymType(n, t); .) }
-  451        ")" .
-  452    (* Clarion-form classes (docs/OOP.txt): declaration + single
-  453       inheritance + IMPLEMENTATION blocks. Scopes and member checks
-  454       now; lowering (vtable, dispatch, THIS) later — one 230 per
-  455       class/impl block. Methods end with ";" per the Table example
-  456       (not "," as in the sketch). No underscores in identifiers. *)
-  457    (* Single CLASS item in both loops: separating declaration from
-  458       IMPLEMENTATION at the loop level needs 2-token lookahead
-  459       (CLASS ident vs CLASS IMPLEMENTATION), which LL(1) cannot do.
-  460       The second token decides after CLASS is consumed. A misplaced
-  461       CLASS IMPLEMENTATION inside TYPE still parses (harmless: the
-  462       whole unit ends 230 until lowering). *)
-  463    ClassItem
-  464      = "CLASS" ( "IMPLEMENTATION" ClassImplRest | ClassRest ) .
-  465    ClassRest                             (. VAR cn, m2, pn: SymTab.Name;
-  466                                               ct: SymTab.TypeIndex; .)
-  467      = GetIdent<cn>                      (. IF NOT SymTab.Enter(cn,
-  468                                               SymTab.KindType) THEN
-  469                                               SemError(200) END;
-  470                                             ct := SymTab.NewClass();
-  471                                             SymTab.SetSymType(cn, ct);
-  472                                             SymTab.PushClassScope(ct); .)
-  473        [ Parents<ct> ]
-  474        ";"
-  475        { ClassField<ct> ";" }
-  476        { MethodHeading<pn> ";"           (. SymTab.CloseProc;
-  477                                               QbeGen.AbortFunc; .) }
-  478        "END"
-  479        GetIdent<m2>                      (. IF NOT SymTab.Equal(cn, m2) THEN
-  480                                               SemError(202) END;
-  481                                             SymTab.PopScope;
-  482                                             SemError(230); .) .
-  483    Parents<ct: SymTab.TypeIndex>         (. VAR p: SymTab.Name; .)
-  484      = "(" Parent1<ct>
-  485        { "," GetIdent<p>                 (. SemError(230); .) }
-  486        ")" .
-  487    Parent1<ct: SymTab.TypeIndex>         (. VAR p: SymTab.Name;
-  488                                               pt: SymTab.TypeIndex; .)
-  489      = GetIdent<p>                       (. IF NOT SymTab.Lookup(p) THEN
-  490                                               SemError(201)
-  491                                             ELSE pt := SymTab.SymType(p);
-  492                                               IF SymTab.ClassOf(pt) #
-  493                                                  SymTab.ClClass THEN
-  494                                                 SemError(230)
-  495                                               ELSE SymTab.SetParent(ct, pt)
-  496                                               END
-  497                                             END; .) .
-  498    ClassField<ct: SymTab.TypeIndex>      (. VAR n, rhs: SymTab.Name;
-  499                                               t: SymTab.TypeIndex; .)
-  500      = GetIdent<n>
-  501        ( "=" GetIdent<rhs>               (. IF NOT SymTab.Enter(n,
-  502                                               SymTab.KindConst) THEN
-  503                                               SemError(200) END;
-  504                                             IF SymTab.Lookup(rhs) THEN
-  505                                               SymTab.SetSymType(n,
-  506                                                 SymTab.SymType(rhs))
-  507                                             END; .)
-  508        |                                 (. IF NOT SymTab.FieldPending(ct,
-  509                                               n) THEN
-  510                                               SemError(200) END; .)
-  511          { "," GetIdent<n>               (. IF NOT SymTab.FieldPending(ct,
-  512                                               n) THEN
-  513                                               SemError(200) END; .) }
-  514          ":" Type<t, FALSE>              (. SymTab.FixPendingF(ct, t); .) ) .
-  515    MethodHeading<VAR pn: SymTab.Name>    (. VAR wantVirt: BOOLEAN; .)
-  516      =                                   (. wantVirt := FALSE; .)
-  517        [ "VIRTUAL"                       (. wantVirt := TRUE; .) ]
-  518        ProcHeading<pn>                   (. IF wantVirt THEN
-  519                                               SymTab.MarkVirtual END; .) .
-  520    ClassImplRest                         (. VAR cn, m2: SymTab.Name;
-  521                                               ct: SymTab.TypeIndex; .)
-  522      = GetIdent<cn>                      (. IF NOT SymTab.Lookup(cn) THEN
-  523                                               SemError(201);
-  524                                               ct := SymTab.InvalidType
-  525                                             ELSE ct := SymTab.SymType(cn);
-  526                                               IF SymTab.ClassOf(ct) #
-  527                                                  SymTab.ClClass THEN
-  528                                                 SemError(230);
-  529                                                 ct := SymTab.InvalidType
-  530                                               END
-  531                                             END;
-  532                                             IF ct #
-  533                                                SymTab.InvalidType THEN
-  534                                               IF NOT SymTab.PushClassMembers(
-  535                                                      ct) THEN
-  536                                                 SemError(230) END
-  537                                             END; .)
-  538        ";" { MethodImpl<ct> ";" }
-  539        [ "BEGIN"
-  540          [ StatSeq ] ]
-  541        "END"
-  542        GetIdent<m2>                      (. IF NOT SymTab.Equal(cn, m2) THEN
-  543                                               SemError(202) END;
-  544                                             SymTab.PopScope;
-  545                                             SemError(230); .) .
-  546    MethodImpl<ct: SymTab.TypeIndex>      (. VAR pn: SymTab.Name; .)
-  547      =                                   (. QbeGen.SetNoEmit(TRUE); .)
-  548        MethodHeading<pn> ";"
-  549                                          (. IF (ct #
-  550                                                SymTab.InvalidType)
-  551   AND NOT SymTab.MethodExists(ct,
-  552                                                    pn) THEN
-  553                                               SemError(201) END; .)
-  554        ( "FORWARD"                       (. SymTab.MarkFwd;
-  555                                             SymTab.CloseProc; .)
-  556        | Block<pn>                       (. SymTab.CloseProc; .) )
-  557                                          (. QbeGen.AbortFunc;
-  558                                             QbeGen.SetNoEmit(FALSE);
-  559                                             SemError(230); .) .
-  560    ConstBlock
-  561      = "CONST" { ConstDecl ";" } .
-  562    ConstDecl                             (. VAR n: SymTab.Name;
-  563                                               t: SymTab.TypeIndex;
-  564                                               qv: QbeGen.QVal;
-  565                                               cls: INTEGER; .)
-  566      = GetIdent<n>                       (. IF NOT SymTab.Enter(n,
-  567                                               SymTab.KindConst) THEN
-  568                                               SemError(200) END; .)
-  569        "="
-  570        Expr<t, qv>                       (. SymTab.SetSymType(n, t);
-  571                                             cls := SymTab.ClassOf(t);
-  572                                             IF cls = SymTab.ClStr THEN
-  573                                               SemError(230)
-  574                                             ELSIF NOT QbeGen.IsImm(qv) THEN
-  575                                               SemError(230) END;
-  576                                             SymTab.SetSymVal(n, qv);
-  577                                             QbeGen.DeclConst(n, qv, t); .) .
-  578    VarBlock
-  579      = "VAR" { VarDecl ";" } .
-  580    VarDecl                               (. VAR nm: SymTab.Name;
-  581                                               t: SymTab.TypeIndex;
-  582                                               i: CARDINAL;
-  583                                               cls: INTEGER; .)
-  584      = VarIdents ":"
-  585        Type<t, FALSE>                    (. cls := SymTab.ClassOf(t);
-  586                                             IF (t # SymTab.InvalidType)
-  587   AND (cls # SymTab.ClInt)
-  588   AND (cls # SymTab.ClBool)
-  589   AND (cls # SymTab.ClChar)
-  590   AND (cls # SymTab.ClReal)
-  591   AND (cls # SymTab.ClArray)
-  592   AND (cls # SymTab.ClSet)
-  593   AND (cls # SymTab.ClRecord)
-  594   AND (cls # SymTab.ClPtr)
-  595   AND (cls # SymTab.ClLong)
-  596   AND (cls # SymTab.ClProc) THEN
-  597                                               SemError(230) END;
-  598                                             IF QbeGen.LocFull() THEN
-  599                                               SemError(233) END;
-  600                                             i := 0;
-  601                                             WHILE i < SymTab.PendCount() DO
-  602                                               SymTab.PendName(i, nm);
-  603                                               QbeGen.DeclVar(nm, t);
-  604                                               INC(i)
-  605                                             END;
-  606                                             SymTab.FixPending(t); .) .
-  607    VarIdents                             (. VAR n: SymTab.Name; .)
-  608      = GetIdent<n>                       (. IF NOT SymTab.EnterPending(n,
-  609                                               SymTab.KindVar) THEN
-  610                                               SemError(200) END; .)
-  611        { ","
-  612          GetIdent<n>                     (. IF NOT SymTab.EnterPending(n,
+   81    ustring = ( "U" | "u" ) ( "'" { noQuote1 } "'" | '"' { noQuote2 } '"' ) .
+   82  
+   83  PRODUCTIONS
+   84    M2
+   85      = Unit "." .
+   86    (* Units: program modules compile fully; DEFINITION and
+   87       IMPLEMENTATION modules parse + check now but lower in step 4
+   88       (each ends with one 230); same for nested local modules. *)
+   89    Unit
+   90      = DefUnit
+   91      | ImplUnit
+   92      | ProgModule .
+   93    (* Step 4.3: one session compiles DEFINITION, its IMPLEMENTATION
+   94       and one program (last) into one image. Units share the symbol
+   95       table; imports materialize exported names. *)
+   96    DefUnit                               (. VAR m1, m2, pn: SymTab.Name; .)
+   97      = "DEFINITION" "MODULE"
+   98        GetIdent<m1>                      (. IF NOT SymTab.BeginDef(m1) THEN
+   99                                               SemError(200) END;
+  100                                             QbeGen.SetModule(m1); .)
+  101        ";"
+  102        { Import }
+  103        { ConstBlock | TypeBlock<TRUE> | VarBlock
+  104        | ProcHeading<pn> ";"             (. SymTab.CloseProc;
+  105                                               QbeGen.AbortFunc; .) }
+  106        "END"
+  107        GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
+  108                                               SemError(202) END;
+  109                                             SymTab.EndUnit; .) .
+  110    ImplUnit                              (. VAR m1, m2: SymTab.Name; .)
+  111      = "IMPLEMENTATION" "MODULE"
+  112        GetIdent<m1>                      (. IF NOT SymTab.BeginImpl(m1) THEN
+  113                                               SemError(201) END;
+  114                                             QbeGen.SetModule(m1); .)
+  115        ";"
+  116        { Import }
+  117        DeclSeq
+  118        [ "BEGIN"                         (. QbeGen.BeginInit(m1); .)
+  119          [ StatSeq ]                       (. QbeGen.EndInit; .) ]
+  120        "END"
+  121        GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
+  122                                               SemError(202) END;
+  123                                             SymTab.EndUnit; .) .
+  124    ProgModule                            (. VAR m1, m2: SymTab.Name; .)
+  125      = "MODULE"
+  126        GetIdent<m1>                      (. IF NOT SymTab.BeginProg(m1) THEN
+  127                                               SemError(200) END;
+  128                                             QbeGen.SetModule(m1); .)
+  129        [ Priority ]
+  130        ";"
+  131        { Import }
+  132        DeclSeq
+  133        [ "BEGIN"                         (. QbeGen.BeginBody; .)
+  134          [ StatSeq ] ]
+  135        "END"
+  136        GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
+  137                                               SemError(202) END;
+  138                                             QbeGen.EndModule(m1);
+  139                                             SymTab.EndUnit; .) .
+  140    DeclSeq
+  141      = { ConstBlock | TypeBlock<FALSE> | VarBlock | ProcDecl ";"
+  142        | NestedModule ";" | ClassItem ";" } .
+  143    (* Local module, Wirth form. Declarations lower like top-level ones
+  144       (same QBE module prefix); a BEGIN body becomes an init function
+  145       that main calls; the EXPORT list is hoisted into the enclosing
+  146       scope at END. *)
+  147    NestedModule                          (. VAR m1, m2: SymTab.Name;
+  148                                               expNames: ARRAY [0 .. 63] OF SymTab.Name;
+  149                                               expCount, k: CARDINAL; .)
+  150      = "MODULE"
+  151        GetIdent<m1>                      (. IF NOT SymTab.Enter(m1,
+  152                                               SymTab.KindModule) THEN
+  153                                               SemError(200) END;
+  154                                             SymTab.PushScope;
+  155                                             expCount := 0; .)
+  156        [ Priority ]
+  157        ";"
+  158        { Import }
+  159        [ "EXPORT" [ "QUALIFIED" ]
+  160          GetIdent<expNames[expCount]>    (. INC(expCount); .)
+  161          { "," GetIdent<expNames[expCount]>
+  162                                          (. INC(expCount); .) }
+  163          ";" ]
+  164        DeclSeq
+  165        [ "BEGIN"                         (. QbeGen.BeginInit(m1); .)
+  166          [ StatSeq ]                     (. QbeGen.EndInit; .) ]
+  167        "END"
+  168        GetIdent<m2>                      (. IF NOT SymTab.Equal(m1, m2) THEN
+  169                                               SemError(202) END;
+  170                                             k := 0;
+  171                                             WHILE k < expCount DO
+  172                                               SymTab.ExportUp(expNames[k]);
+  173                                               INC(k)
+  174                                             END;
+  175                                             SymTab.PopScope; .) .
+  176    Priority
+  177      = "[" integer "]"                   (. SemError(230); .) .
+  178    (* Imports (4.3): FROM materializes the names (unqualified use);
+  179       plain IMPORT only demands the module exists — qualified `L.x`
+  180       materializes on first use (Design). *)
+  181    (* Unknown modules stay unchecked stubs (legacy, so hand-written
+  182       import lines don't fail); a known module's missing export is
+  183       201. *)
+  184    Import                                (. VAR n: SymTab.Name; .)
+  185      = "FROM"
+  186        GetIdent<n>
+  187        "IMPORT"
+  188        ImpList<n> ";"
+  189      | "IMPORT"
+  190        ImpModList ";" .
+  191    ImpList<mod: SymTab.Name>             (. VAR n: SymTab.Name; .)
+  192      = ImpName<mod>
+  193        { "," ImpName<mod> } .
+  194    (* Pervasive built-ins imported from SYSTEM (e.g. TSIZE) are
+  195       accepted and ignored: the built-in applies regardless. *)
+  196    ImpName<mod: SymTab.Name>             (. VAR n: SymTab.Name; .)
+  197      = GetIdent<n>                       (. IF SymTab.ModKnown(mod)
+  198                                               AND NOT SymTab.ImportFrom(mod, n) THEN
+  199                                               SemError(201) END; .)
+  200      | ( "TSIZE" | "SIZE" | "ADR" | "HIGH" | "LEN"
+  201        | "CHR" | "ORD" | "ORDL" | "VAL" | "ABS" | "CAP"
+  202        | "UCHR" | "CHR8" | "UORD"
+  203        | "INC" | "DEC" ) .
+  204    ImpModList                            (. VAR n: SymTab.Name; .)
+  205      = GetIdent<n>
+  206        { "," GetIdent<n> } .
+  207    (* Opaque TYPE declarations (definition modules). The targetless
+  208       alias resolves to InvalidType until step 4 completes it. *)
+  209    (* Scalar-phase TYPEs: named types, integer subranges, enumerations.
+  210       Opaque "TYPE T;" needs isDef (definition units); elsewhere 231.
+  211       Composite forms (ARRAY/RECORD/SET/POINTER) arrive with step 3. *)
+  212    TypeBlock<isDef: BOOLEAN>
+  213      = "TYPE"                            (. SymTab.BeginTypeBlock; .)
+  214        { TypeItem<isDef> ";" | ClassItem ";" }
+  215                                          (. SymTab.EndTypeBlock; .) .
+  216    TypeItem<isDef: BOOLEAN>              (. VAR n: SymTab.Name;
+  217                                               t, op: SymTab.TypeIndex; .)
+  218      = GetIdent<n>                       (. op := SymTab.OpaqueBase(n);
+  219                                             IF op = SymTab.InvalidType THEN
+  220                                               IF NOT SymTab.Enter(n,
+  221                                                  SymTab.KindType) THEN
+  222                                                 SemError(200) END
+  223                                             END; .)
+  224        ( "=" Type<t, FALSE>              (. IF op # SymTab.InvalidType THEN
+  225                                               SymTab.SetTarget(op, t)
+  226                                             ELSE SymTab.SetSymType(n, t)
+  227                                             END; .)
+  228        |                                 (. IF op # SymTab.InvalidType THEN
+  229                                               (* stays opaque *)
+  230                                             ELSIF NOT isDef THEN
+  231                                               SemError(231)
+  232                                             ELSE SymTab.SetSymType(n,
+  233                                               SymTab.NewAlias()) END; .) ) .
+  234    Type<VAR t: SymTab.TypeIndex; allowOpen: BOOLEAN>
+  235      = TypeIdent<t>
+  236      | Subrange<t>
+  237      | Enum<t>
+  238      | ArrayType<t, allowOpen>
+  239      | SetType<t>
+  240      | RecordType<t>
+  241      | PointerType<t>
+  242      | ProcType<t> .
+  243    PointerType<VAR t: SymTab.TypeIndex>  (. VAR base: SymTab.TypeIndex; .)
+  244      = "POINTER" "TO" Type<base, FALSE>(. t := SymTab.NewPtr(base); .) .
+  245    (* Procedure types (step 8.5): PROCEDURE (params): result. Values
+  246       are code pointers; params are collected into the descriptor. *)
+  247    ProcType<VAR t: SymTab.TypeIndex>     (. VAR res, pt: SymTab.TypeIndex;
+  248                                               isV: BOOLEAN; .)
+  249      = "PROCEDURE"                       (. res := SymTab.InvalidType;
+  250                                             t := SymTab.NewProcType(res); .)
+  251        [ "(" [ ProcTypeSection<t> { ";" ProcTypeSection<t> } ] ")" ]
+  252        [ ":" TypeIdent<res>              (. SymTab.SetProcTypeRes(t, res); .) ] .
+  253    ProcTypeSection<t: SymTab.TypeIndex>  (. VAR pt: SymTab.TypeIndex;
+  254                                               isV: BOOLEAN;
+  255                                               cnt, k: CARDINAL;
+  256                                               names: ARRAY [0 .. 15] OF SymTab.Name; .)
+  257      =                                   (. isV := FALSE; cnt := 0; .)
+  258        [ "VAR"                           (. isV := TRUE; .) ]
+  259        GetIdent<names[cnt]>              (. INC(cnt); .)
+  260        { "," GetIdent<names[cnt]>        (. INC(cnt); .) }
+  261        ( ":" Type<pt, FALSE>             (. k := 0;
+  262                                             WHILE k < cnt DO
+  263                                               SymTab.ProcTypeAdd(t, isV, pt);
+  264                                               INC(k)
+  265                                             END; .)
+  266        |                                 (. (* type-only parameter list:
+  267                                                each name is a type (GNU
+  268                                                shorthand used by the
+  269                                                Coco/R scanner frame) *)
+  270                                             k := 0;
+  271                                             WHILE k < cnt DO
+  272                                               IF SymTab.Lookup(names[k])
+  273                                                  AND ((SymTab.SymKind(names[k]) = SymTab.KindType)
+  274    OR (SymTab.SymKind(names[k]) = SymTab.KindPredef)) THEN
+  275                                                 pt := SymTab.SymType(names[k])
+  276                                               ELSE SemError(201);
+  277                                                 pt := SymTab.InvalidType
+  278                                               END;
+  279                                               SymTab.ProcTypeAdd(t, isV, pt);
+  280                                               INC(k)
+  281                                             END; .) ) .
+  282    (* Arrays: "OF" without bounds is an open formal (allowed only
+  283       where allowOpen); "[lo..hi, ...]" nests bounded levels inside
+  284       out. Bounds are folded literals (int/char); anything else 230.
+  285       Bare-type indices ("ARRAY Color OF") wait for enum ordinals. *)
+  286    ArrayType<VAR t: SymTab.TypeIndex; allowOpen: BOOLEAN>
+  287                                          (. VAR elem: SymTab.TypeIndex;
+  288                                               ok: BOOLEAN;
+  289                                               bnds, bndh: ARRAY [0 .. 7] OF INTEGER;
+  290                                               nb, k: CARDINAL; .)
+  291      = "ARRAY"
+  292        ( "OF" Type<elem, FALSE>          (. IF NOT allowOpen THEN
+  293                                               SemError(230) END;
+  294                                             t := SymTab.NewOpenArray(elem); .)
+  295        | "["                             (. SymTab.BoundBegin; ok := TRUE; .)
+  296          BoundPair<ok>
+  297          { "," BoundPair<ok> }
+  298          "]"                             (. (* snapshot before the element
+  299                                                type, which reuses the bound
+  300                                                buffer for a nested ARRAY *)
+  301                                             nb := SymTab.BoundCount();
+  302                                             k := 0;
+  303                                             WHILE k < nb DO
+  304                                               bnds[k] := SymTab.BoundLo(k);
+  305                                               bndh[k] := SymTab.BoundHi(k);
+  306                                               INC(k)
+  307                                             END; .)
+  308          "OF" Type<elem, FALSE>
+  309                                          (. IF ok THEN
+  310                                               k := nb;
+  311                                               WHILE k > 0 DO
+  312                                                 DEC(k);
+  313                                                 elem := SymTab.NewArrayB(
+  314                                                   elem, bnds[k], bndh[k])
+  315                                               END;
+  316                                               t := elem
+  317                                             ELSE t := SymTab.InvalidType
+  318                                             END; .) ) .
+  319    BoundPair<VAR ok: BOOLEAN>            (. VAR tlo, thi: SymTab.TypeIndex;
+  320                                               qlo, qhi: QbeGen.QVal;
+  321                                               lo, hi: INTEGER;
+  322                                               cl, cl2: INTEGER; .)
+  323      = Expr<tlo, qlo> ".." Expr<thi, qhi>
+  324                                          (. IF (tlo = SymTab.InvalidType)
+  325                                              OR (thi = SymTab.InvalidType) THEN
+  326                                             ok := FALSE
+  327                                           ELSE cl := SymTab.ClassOf(tlo);
+  328                                             cl2 := SymTab.ClassOf(thi);
+  329                                             IF ((cl # SymTab.ClInt)
+  330   AND (cl # SymTab.ClChar))
+  331                                                OR ((cl2 # SymTab.ClInt)
+  332   AND (cl2 # SymTab.ClChar)) THEN
+  333                                               SemError(230); ok := FALSE
+  334                                             ELSIF NOT SymTab.ConstInt(qlo, lo)
+  335                                                OR NOT SymTab.ConstInt(qhi, hi)
+  336                                                OR (lo > hi) THEN
+  337                                               SemError(230); ok := FALSE
+  338                                              ELSIF NOT SymTab.BoundAdd(lo, hi) THEN
+  339                                                SemError(230); ok := FALSE
+  340                                              END;
+  341                                            END; .) .
+  342    (* Sets: multi-word masks over bases ≤ 256 values (bool, char,
+  343       bounded subranges; enums wait for ordinals, INTEGER is
+  344       unbounded). Literals are SET OF [0..255]; assignment and
+  345       comparison across suitable bases are lenient (masks over min
+  346       words + zero-check extras), out-of-span literals are 222. *)
+  347    SetType<VAR t: SymTab.TypeIndex>      (. VAR base: SymTab.TypeIndex;
+  348                                               blo, bhi, bspan: INTEGER;
+  349                                               bcls: INTEGER; .)
+  350      = "SET" "OF" Type<base, FALSE>
+  351                                          (. IF base = SymTab.InvalidType THEN
+  352                                               t := SymTab.InvalidType
+  353                                             ELSE bcls :=
+  354                                                    SymTab.ClassOf(base);
+  355                                               IF bcls = SymTab.ClBool THEN
+  356                                                 blo := 0; bspan := 2
+  357                                               ELSIF bcls = SymTab.ClChar THEN
+  358                                                 blo := 0; bspan := 256
+  359                                               ELSIF SymTab.SubBounds(base,
+  360                                                       blo, bhi) THEN
+  361                                                 bspan := bhi - blo + 1
+  362                                               ELSE bspan := 0 END;
+  363                                               IF (bspan <= 0)
+  364                                                  OR (bspan > 256) THEN
+  365                                                 SemError(230);
+  366                                                 t := SymTab.InvalidType
+  367                                               ELSE t := SymTab.NewSet(base)
+  368                                               END
+  369                                             END; .) .
+  370    (* Records: flat blobs; array fields are pointers to static
+  371       descriptors (locked amendment), nested records inline. Field
+  372       offsets static and declaration-ordered. *)
+  373    RecordType<VAR t: SymTab.TypeIndex>   (. VAR t2: SymTab.TypeIndex; .)
+  374      = "RECORD"                          (. t := SymTab.NewRecord(); .)
+  375        [ RecField<t> { ";" [ RecField<t> ] } ]
+  376        "END" .
+  377    RecField<rec: SymTab.TypeIndex>       (. VAR n: SymTab.Name;
+  378                                               t2: SymTab.TypeIndex; .)
+  379      = RecIdents<rec> ":" Type<t2, FALSE>(. SymTab.FixPendingF(rec, t2); .) .
+  380    RecIdents<rec: SymTab.TypeIndex>      (. VAR n: SymTab.Name; .)
+  381      = GetIdent<n>                       (. IF NOT SymTab.FieldPending(rec,
+  382                                               n) THEN
+  383                                               SemError(200) END; .)
+  384        { "," GetIdent<n>                 (. IF NOT SymTab.FieldPending(rec,
+  385                                               n) THEN
+  386                                               SemError(200) END; .) } .
+  387    TypeIdent<VAR t: SymTab.TypeIndex>    (. VAR n, qid: SymTab.Name;
+  388                                               k: INTEGER;
+  389                                               dotted: BOOLEAN; .)
+  390      = GetIdent<n>                       (. dotted := FALSE;
+  391                                             IF NOT SymTab.Lookup(n) THEN
+  392                                               k := -1;
+  393                                               t := SymTab.ForwardType(n);
+  394                                               IF t = SymTab.InvalidType THEN
+  395                                                 SemError(201)
+  396                                               END
+  397                                             ELSE k := SymTab.SymKind(n);
+  398                                               IF (k = SymTab.KindType)
+  399                                                  OR (k = SymTab.KindPredef) THEN
+  400                                                 t := SymTab.SymType(n)
+  401                                               ELSE t := SymTab.InvalidType
+  402                                               END
+  403                                             END; .)
+  404        [ "." GetIdent<qid>               (. dotted := TRUE;
+  405                                             IF k = SymTab.KindModule THEN
+  406                                               t := SymTab.QualType(n, qid);
+  407                                               IF t = SymTab.InvalidType THEN
+  408                                                 SemError(201)
+  409                                               END
+  410                                             ELSE SemError(221);
+  411                                               t := SymTab.InvalidType
+  412                                             END; .) ]
+  413                                          (. IF NOT dotted THEN
+  414                                               IF (k = SymTab.KindModule)
+  415                                                  OR ((k # SymTab.KindType)
+  416    AND (k # SymTab.KindPredef)
+  417    AND (k # SymTab.KindImport)
+  418    AND (k # -1)) THEN
+  419                                                 SemError(221)
+  420                                               END
+  421                                             END; .) .
+  422    Subrange<VAR t: SymTab.TypeIndex>     (. VAR tlo, thi: SymTab.TypeIndex;
+  423                                               qlo, qhi: QbeGen.QVal;
+  424                                               lo, hi: INTEGER; .)
+  425      = "[" Expr<tlo, qlo> ".." Expr<thi, qhi>
+  426                                          (. IF (tlo = SymTab.InvalidType)
+  427                                              OR (thi = SymTab.InvalidType) THEN
+  428                                             t := SymTab.InvalidType
+  429                                           ELSIF (SymTab.ClassOf(tlo) #
+  430                                                  SymTab.ClInt)
+  431                                              OR (SymTab.ClassOf(thi) #
+  432                                                  SymTab.ClInt) THEN
+  433                                             SemError(230);
+  434                                             t := SymTab.InvalidType
+  435                                           ELSIF NOT SymTab.ConstInt(qlo, lo)
+  436                                              OR NOT SymTab.ConstInt(qhi, hi)
+  437                                              OR (lo > hi) THEN
+  438                                             SemError(230);
+  439                                             t := SymTab.InvalidType
+  440                                           ELSE t := SymTab.NewSubR(lo, hi)
+  441                                           END; .)
+  442        "]" .
+  443    Enum<VAR t: SymTab.TypeIndex>         (. VAR n: SymTab.Name; .)
+  444      = "("                               (. t := SymTab.NewEnum(); .)
+  445        GetIdent<n>                       (. IF NOT SymTab.Enter(n,
+  446                                               SymTab.KindConst) THEN
+  447                                               SemError(200) END;
+  448                                             SymTab.SetSymType(n, t); .)
+  449        { "," GetIdent<n>                 (. IF NOT SymTab.Enter(n,
+  450                                               SymTab.KindConst) THEN
+  451                                               SemError(200) END;
+  452                                             SymTab.SetSymType(n, t); .) }
+  453        ")" .
+  454    (* Clarion-form classes (docs/OOP.txt): declaration + single
+  455       inheritance + IMPLEMENTATION blocks. Scopes and member checks
+  456       now; lowering (vtable, dispatch, THIS) later — one 230 per
+  457       class/impl block. Methods end with ";" per the Table example
+  458       (not "," as in the sketch). No underscores in identifiers. *)
+  459    (* Single CLASS item in both loops: separating declaration from
+  460       IMPLEMENTATION at the loop level needs 2-token lookahead
+  461       (CLASS ident vs CLASS IMPLEMENTATION), which LL(1) cannot do.
+  462       The second token decides after CLASS is consumed. A misplaced
+  463       CLASS IMPLEMENTATION inside TYPE still parses (harmless: the
+  464       whole unit ends 230 until lowering). *)
+  465    ClassItem
+  466      = "CLASS" ( "IMPLEMENTATION" ClassImplRest | ClassRest ) .
+  467    ClassRest                             (. VAR cn, m2, pn: SymTab.Name;
+  468                                               ct: SymTab.TypeIndex; .)
+  469      = GetIdent<cn>                      (. IF NOT SymTab.Enter(cn,
+  470                                               SymTab.KindType) THEN
+  471                                               SemError(200) END;
+  472                                             ct := SymTab.NewClass();
+  473                                             SymTab.SetSymType(cn, ct);
+  474                                             SymTab.PushClassScope(ct); .)
+  475        [ Parents<ct> ]
+  476        ";"
+  477        { ClassField<ct> ";" }
+  478        { MethodHeading<pn> ";"           (. SymTab.CloseProc;
+  479                                               QbeGen.AbortFunc; .) }
+  480        "END"
+  481        GetIdent<m2>                      (. IF NOT SymTab.Equal(cn, m2) THEN
+  482                                               SemError(202) END;
+  483                                             SymTab.PopScope;
+  484                                             SemError(230); .) .
+  485    Parents<ct: SymTab.TypeIndex>         (. VAR p: SymTab.Name; .)
+  486      = "(" Parent1<ct>
+  487        { "," GetIdent<p>                 (. SemError(230); .) }
+  488        ")" .
+  489    Parent1<ct: SymTab.TypeIndex>         (. VAR p: SymTab.Name;
+  490                                               pt: SymTab.TypeIndex; .)
+  491      = GetIdent<p>                       (. IF NOT SymTab.Lookup(p) THEN
+  492                                               SemError(201)
+  493                                             ELSE pt := SymTab.SymType(p);
+  494                                               IF SymTab.ClassOf(pt) #
+  495                                                  SymTab.ClClass THEN
+  496                                                 SemError(230)
+  497                                               ELSE SymTab.SetParent(ct, pt)
+  498                                               END
+  499                                             END; .) .
+  500    ClassField<ct: SymTab.TypeIndex>      (. VAR n, rhs: SymTab.Name;
+  501                                               t: SymTab.TypeIndex; .)
+  502      = GetIdent<n>
+  503        ( "=" GetIdent<rhs>               (. IF NOT SymTab.Enter(n,
+  504                                               SymTab.KindConst) THEN
+  505                                               SemError(200) END;
+  506                                             IF SymTab.Lookup(rhs) THEN
+  507                                               SymTab.SetSymType(n,
+  508                                                 SymTab.SymType(rhs))
+  509                                             END; .)
+  510        |                                 (. IF NOT SymTab.FieldPending(ct,
+  511                                               n) THEN
+  512                                               SemError(200) END; .)
+  513          { "," GetIdent<n>               (. IF NOT SymTab.FieldPending(ct,
+  514                                               n) THEN
+  515                                               SemError(200) END; .) }
+  516          ":" Type<t, FALSE>              (. SymTab.FixPendingF(ct, t); .) ) .
+  517    MethodHeading<VAR pn: SymTab.Name>    (. VAR wantVirt: BOOLEAN; .)
+  518      =                                   (. wantVirt := FALSE; .)
+  519        [ "VIRTUAL"                       (. wantVirt := TRUE; .) ]
+  520        ProcHeading<pn>                   (. IF wantVirt THEN
+  521                                               SymTab.MarkVirtual END; .) .
+  522    ClassImplRest                         (. VAR cn, m2: SymTab.Name;
+  523                                               ct: SymTab.TypeIndex; .)
+  524      = GetIdent<cn>                      (. IF NOT SymTab.Lookup(cn) THEN
+  525                                               SemError(201);
+  526                                               ct := SymTab.InvalidType
+  527                                             ELSE ct := SymTab.SymType(cn);
+  528                                               IF SymTab.ClassOf(ct) #
+  529                                                  SymTab.ClClass THEN
+  530                                                 SemError(230);
+  531                                                 ct := SymTab.InvalidType
+  532                                               END
+  533                                             END;
+  534                                             IF ct #
+  535                                                SymTab.InvalidType THEN
+  536                                               IF NOT SymTab.PushClassMembers(
+  537                                                      ct) THEN
+  538                                                 SemError(230) END
+  539                                             END; .)
+  540        ";" { MethodImpl<ct> ";" }
+  541        [ "BEGIN"
+  542          [ StatSeq ] ]
+  543        "END"
+  544        GetIdent<m2>                      (. IF NOT SymTab.Equal(cn, m2) THEN
+  545                                               SemError(202) END;
+  546                                             SymTab.PopScope;
+  547                                             SemError(230); .) .
+  548    MethodImpl<ct: SymTab.TypeIndex>      (. VAR pn: SymTab.Name; .)
+  549      =                                   (. QbeGen.SetNoEmit(TRUE); .)
+  550        MethodHeading<pn> ";"
+  551                                          (. IF (ct #
+  552                                                SymTab.InvalidType)
+  553   AND NOT SymTab.MethodExists(ct,
+  554                                                    pn) THEN
+  555                                               SemError(201) END; .)
+  556        ( "FORWARD"                       (. SymTab.MarkFwd;
+  557                                             SymTab.CloseProc; .)
+  558        | Block<pn>                       (. SymTab.CloseProc; .) )
+  559                                          (. QbeGen.AbortFunc;
+  560                                             QbeGen.SetNoEmit(FALSE);
+  561                                             SemError(230); .) .
+  562    ConstBlock
+  563      = "CONST" { ConstDecl ";" } .
+  564    ConstDecl                             (. VAR n: SymTab.Name;
+  565                                               t: SymTab.TypeIndex;
+  566                                               qv: QbeGen.QVal;
+  567                                               cls: INTEGER; .)
+  568      = GetIdent<n>                       (. IF NOT SymTab.Enter(n,
+  569                                               SymTab.KindConst) THEN
+  570                                               SemError(200) END; .)
+  571        "="
+  572        Expr<t, qv>                       (. SymTab.SetSymType(n, t);
+  573                                             cls := SymTab.ClassOf(t);
+  574                                             IF cls = SymTab.ClStr THEN
+  575                                               SemError(230)
+  576                                             ELSIF NOT QbeGen.IsImm(qv) THEN
+  577                                               SemError(230) END;
+  578                                             SymTab.SetSymVal(n, qv);
+  579                                             QbeGen.DeclConst(n, qv, t); .) .
+  580    VarBlock
+  581      = "VAR" { VarDecl ";" } .
+  582    VarDecl                               (. VAR nm: SymTab.Name;
+  583                                               t: SymTab.TypeIndex;
+  584                                               i: CARDINAL;
+  585                                               cls: INTEGER; .)
+  586      = VarIdents ":"
+  587        Type<t, FALSE>                    (. cls := SymTab.ClassOf(t);
+  588                                             IF (t # SymTab.InvalidType)
+  589   AND (cls # SymTab.ClInt)
+  590   AND (cls # SymTab.ClBool)
+  591   AND (cls # SymTab.ClChar)
+  592   AND (cls # SymTab.ClReal)
+  593   AND (cls # SymTab.ClArray)
+  594   AND (cls # SymTab.ClSet)
+  595   AND (cls # SymTab.ClRecord)
+  596   AND (cls # SymTab.ClPtr)
+  597   AND (cls # SymTab.ClLong)
+  598   AND (cls # SymTab.ClProc)
+  599   AND (cls # SymTab.ClUChar)
+  600   AND (cls # SymTab.ClUStr) THEN
+  601                                               SemError(230) END;
+  602                                             IF QbeGen.LocFull() THEN
+  603                                               SemError(233) END;
+  604                                             i := 0;
+  605                                             WHILE i < SymTab.PendCount() DO
+  606                                               SymTab.PendName(i, nm);
+  607                                               QbeGen.DeclVar(nm, t);
+  608                                               INC(i)
+  609                                             END;
+  610                                             SymTab.FixPending(t); .) .
+  611    VarIdents                             (. VAR n: SymTab.Name; .)
+  612      = GetIdent<n>                       (. IF NOT SymTab.EnterPending(n,
   613                                               SymTab.KindVar) THEN
-  614                                               SemError(200) END; .) } .
-  615    ParIdents<isV: BOOLEAN>               (. VAR n: SymTab.Name; .)
-  616      = GetIdent<n>                       (. IF NOT SymTab.EnterParam(n, isV) THEN
-  617                                               SemError(200) END; .)
-  618        { "," GetIdent<n>                 (. IF NOT SymTab.EnterParam(n, isV) THEN
-  619                                               SemError(200) END; .) } .
-  620    (* Procedure headings enter scopes/params/result and buffer the
-  621       QBE header; bodies lower to functions (4.1, module level only).
-  622       FORWARD marks; the body heading re-enters (signature compare
-  623       deferred). Nested procedures parse + check, lowering = 4.2. *)
-  624    ProcHeading<VAR pn: SymTab.Name>      (. VAR t: SymTab.TypeIndex;
-  625                                               mg: QbeGen.QVal; .)
-  626      = "PROCEDURE"
-  627        GetIdent<pn>                      (. IF NOT SymTab.EnterProc(pn) THEN
-  628                                             IF NOT SymTab.ReenterProc(pn) THEN
-  629                                               IF NOT SymTab.ResumeProc(pn) THEN
-  630                                                 SemError(200) END
-  631                                               END
-  632                                             END;
-  633                                             QbeGen.Mangled(pn,
-  634                                               SymTab.ProcUid(pn), mg);
-  635                                             QbeGen.BeginFunc(mg); .)
-  636        [ FormalParams ]
-  637        [ ":" TypeIdent<t>                (. SymTab.SetProcRes(t);
-  638                                             QbeGen.SetFuncRes(t);
-  639                                             IF (t #
-  640                                                SymTab.InvalidType)
-  641   AND ((SymTab.ClassOf(t)
-  642                                                   = SymTab.ClArray)
-  643                                                OR (SymTab.ClassOf(t)
-  644                                                   = SymTab.ClRecord)
-  645                                                OR (SymTab.ClassOf(t)
-  646                                                   = SymTab.ClSet)
+  614                                               SemError(200) END; .)
+  615        { ","
+  616          GetIdent<n>                     (. IF NOT SymTab.EnterPending(n,
+  617                                               SymTab.KindVar) THEN
+  618                                               SemError(200) END; .) } .
+  619    ParIdents<isV: BOOLEAN>               (. VAR n: SymTab.Name; .)
+  620      = GetIdent<n>                       (. IF NOT SymTab.EnterParam(n, isV) THEN
+  621                                               SemError(200) END; .)
+  622        { "," GetIdent<n>                 (. IF NOT SymTab.EnterParam(n, isV) THEN
+  623                                               SemError(200) END; .) } .
+  624    (* Procedure headings enter scopes/params/result and buffer the
+  625       QBE header; bodies lower to functions (4.1, module level only).
+  626       FORWARD marks; the body heading re-enters (signature compare
+  627       deferred). Nested procedures parse + check, lowering = 4.2. *)
+  628    ProcHeading<VAR pn: SymTab.Name>      (. VAR t: SymTab.TypeIndex;
+  629                                               mg: QbeGen.QVal; .)
+  630      = "PROCEDURE"
+  631        GetIdent<pn>                      (. IF NOT SymTab.EnterProc(pn) THEN
+  632                                             IF NOT SymTab.ReenterProc(pn) THEN
+  633                                               IF NOT SymTab.ResumeProc(pn) THEN
+  634                                                 SemError(200) END
+  635                                               END
+  636                                             END;
+  637                                             QbeGen.Mangled(pn,
+  638                                               SymTab.ProcUid(pn), mg);
+  639                                             QbeGen.BeginFunc(mg); .)
+  640        [ FormalParams ]
+  641        [ ":" TypeIdent<t>                (. SymTab.SetProcRes(t);
+  642                                             QbeGen.SetFuncRes(t);
+  643                                             IF (t #
+  644                                                SymTab.InvalidType)
+  645   AND ((SymTab.ClassOf(t)
+  646                                                   = SymTab.ClArray)
   647                                                OR (SymTab.ClassOf(t)
-  648                                                   = SymTab.ClClass)) THEN
-  649                                               SemError(230) END; .) ] .
-  650    FormalParams
-  651      = "(" [ ParamSection { ";" ParamSection } ] ")" .
-  652    ParamSection                          (. VAR t: SymTab.TypeIndex;
-  653                                               nm: SymTab.Name;
-  654                                               i: CARDINAL;
-  655                                               isV: BOOLEAN; .)
-  656      =                                   (. isV := FALSE; .)
-  657        [ "VAR"                           (. isV := TRUE; .) ]
-  658        ParIdents<isV> ":" Type<t, TRUE>  (. i := 0;
-  659                                             WHILE i < SymTab.PendCount() DO
-  660                                               SymTab.PendName(i, nm);
-  661                                               (* value open arrays are
-  662                                                  passed as descriptor
-  663                                                  addresses (no copy):
-  664                                                  same representation as
-  665                                                  VAR formals *)
-  666                                               IF NOT QbeGen.FuncParam(nm,
-  667                                                    isV
-  668                                                    OR SymTab.IsOpenArray(t),
-  669                                                    t) THEN
-  670                                                 SemError(233) END;
-  671                                               INC(i)
-  672                                             END;
-  673                                             SymTab.FixPending(t); .) .
-  674    (* Nested procedures lower like top-level ones (4.2): the
-  675       static link gives them their parent's frame. Methods keep
-  676       parse-now/230-later. *)
-  677    ProcDecl                              (. VAR pn: SymTab.Name; .)
-  678      = ProcHeading<pn> ";"
-  679        ( "FORWARD"                       (. SymTab.MarkFwd;
-  680                                             SymTab.CloseProc;
-  681                                             QbeGen.AbortFunc; .)
-  682        | "EXTERNAL"                      (. SymTab.MarkExternal("");
-  683                                             SymTab.CloseProc;
-  684                                             QbeGen.AbortFunc; .)
-  685        |                                 (. QbeGen.EndFuncHeader; .)
-  686          Block<pn>                       (. SymTab.CloseProc;
-  687                                             QbeGen.EndFunc(
-  688                                               SymTab.ProcRes(pn)); .) ) .
-  689    Block<pn: SymTab.Name>                (. VAR m2: SymTab.Name; .)
-  690      = DeclSeq
-  691        [ "BEGIN"
-  692          [ StatSeq ] ]
-  693        "END"
-  694        GetIdent<m2>                      (. IF NOT SymTab.Equal(pn, m2) THEN
-  695                                               SemError(202) END; .) .
-  696    StatSeq
-  697      = Statement { ";" [ Statement ] } .
-  698    (* Trailing/empty statements (`a; ; b`, Coco/R's generated `;;`)
-  699       are accepted: the statement after ';' is optional. *)
-  700    Statement                             (. VAR lx: QbeGen.QVal; .)
-  701      = AssOrCall
-  702      | IfStat
-  703      | WhileStat
-  704      | RepeatStat
-  705      | LoopStat
-  706      | ForStat
-  707      | CaseStat
-  708      | WithStat
-  709      | ReturnStat
-  710      | HaltStat
-  711      | NewStat
-  712      | DisposeStat
-  713      | IncDecStat
-  714      | "EXIT"                            (. IF QbeGen.TopLoop(lx) THEN
-  715                                               QbeGen.Jmp(lx)
-  716                                             ELSE SemError(230) END; .) .
-  717    (* INC(v [,step]) / DEC(v [,step]) as builtin statements over an
-  718       integer designator. *)
-  719    IncDecStat                            (. VAR dt, et2: SymTab.TypeIndex;
-  720                                               dk: INTEGER;
-  721                                               qd, qv, qn2, qstep:
-  722                                                 QbeGen.QVal;
-  723                                               qn: SymTab.Name;
-  724                                               sfx, isInc: BOOLEAN; .)
-  725      =                                   (. isInc := TRUE; .)
-  726        ( "INC"                           (. isInc := TRUE; .)
-  727        | "DEC"                           (. isInc := FALSE; .) )
-  728        "("                               (. QbeGen.CopyOp("1", qstep); .)
-  729        Design<dt, dk, qd, qn, sfx>
-  730        [ "," Expr<et2, qstep> ]
-  731        ")"                               (. IF dt = SymTab.InvalidType THEN
-  732                                             ELSIF (dk # SymTab.KindVar)
-  733   AND (dk # SymTab.KindParam)
-  734   AND (dk # SymTab.KindField) THEN
-  735                                               SemError(210)
-  736                                             ELSIF NOT SymTab.IsIntFamily(dt) THEN
-  737                                               SemError(211)
-  738                                             ELSE
-  739                                               IF sfx
-  740                                                  OR (dk = SymTab.KindField) THEN
-  741                                                 QbeGen.ElemLoad(qd, dt, qv)
-  742                                               ELSE QbeGen.LoadVar(qn,
-  743                                                 FALSE, qv)
-  744                                               END;
-  745                                               QbeGen.NewTemp(qn2);
-  746                                               IF isInc THEN
-  747                                                 QbeGen.Op3("add", qn2, qv,
-  748                                                   qstep, FALSE)
-  749                                               ELSE QbeGen.Op3("sub", qn2, qv,
-  750                                                   qstep, FALSE)
-  751                                               END;
-  752                                               IF sfx
-  753                                                  OR (dk = SymTab.KindField) THEN
-  754                                                 QbeGen.ElemStore(qd, qn2,
-  755                                                   dt)
-  756                                               ELSE QbeGen.StoreVar(qn,
-  757                                                 qn2, FALSE)
-  758                                               END
-  759                                             END; .) .
-  760    (* NEW/DISPOSE as builtin statements (no call syntax until step 4).
-  761       Targets are pointer designators; DISPOSE nils afterwards (safer
-  762       than Wirth-undefined; documented). DISPOSE is shallow. *)
-  763    NewStat                               (. VAR dt: SymTab.TypeIndex;
-  764                                               dk: INTEGER;
-  765                                               qd, qm: QbeGen.QVal;
-  766                                               qn: SymTab.Name;
-  767                                               sfx: BOOLEAN;
-  768                                               bt: SymTab.TypeIndex; .)
-  769      = "NEW" "(" Design<dt, dk, qd, qn, sfx> ")"
-  770                                          (. IF dt = SymTab.InvalidType THEN
-  771                                             ELSIF (dk # SymTab.KindVar)
-  772   AND (dk # SymTab.KindParam)
-  773   AND (dk # SymTab.KindField) THEN
-  774                                               SemError(210)
-  775                                             ELSIF SymTab.ClassOf(dt) #
-  776                                                   SymTab.ClPtr THEN
-  777                                               SemError(219)
-  778                                             ELSE bt := SymTab.PtrBase(dt);
-  779                                               IF bt #
-  780                                                  SymTab.InvalidType THEN
-  781                                                 QbeGen.NewHeap(bt, qm);
-  782                                                 QbeGen.InitHeap(qm, bt);
-  783                                                 IF sfx
-  784                                                    OR (dk =
-  785                                                       SymTab.KindField) THEN
-  786                                                   QbeGen.ElemStore(qd, qm,
-  787                                                     dt)
-  788                                                 ELSE QbeGen.StorePtr(qn,
-  789                                                   qm)
-  790                                                 END
-  791                                               END
-  792                                             END; .) .
-  793    DisposeStat                           (. VAR dt: SymTab.TypeIndex;
-  794                                               dk: INTEGER;
-  795                                               qd, qv: QbeGen.QVal;
-  796                                               qn: SymTab.Name;
-  797                                               sfx: BOOLEAN; .)
-  798      = "DISPOSE" "(" Design<dt, dk, qd, qn, sfx> ")"
-  799                                          (. IF dt = SymTab.InvalidType THEN
-  800                                             ELSIF (dk # SymTab.KindVar)
-  801   AND (dk # SymTab.KindParam)
-  802   AND (dk # SymTab.KindField) THEN
-  803                                               SemError(210)
-  804                                             ELSIF SymTab.ClassOf(dt) #
-  805                                                   SymTab.ClPtr THEN
-  806                                               SemError(219)
-  807                                             ELSE
-  808                                               IF sfx
-  809                                                  OR (dk =
-  810                                                     SymTab.KindField) THEN
-  811                                                 QbeGen.ElemLoad(qd, dt,
-  812                                                   qv)
-  813                                               ELSE QbeGen.LoadPtr(qn, qv)
-  814                                               END;
-  815                                               QbeGen.FreeHeap(qv);
-  816                                               IF sfx
-  817                                                  OR (dk =
-  818                                                     SymTab.KindField) THEN
-  819                                                 QbeGen.ElemStore(qd, "0",
-  820                                                   dt)
-  821                                               ELSE QbeGen.StorePtr(qn,
-  822                                                 "0")
-  823                                               END
-  824                                             END; .) .
-  825    (* WITH pushes each record's fields (inner wins) plus its base
-  826       address; field designators resolve through both stacks. *)
-  827    WithStat                              (. VAR nW: CARDINAL; .)
-  828      = "WITH"                            (. nW := 0; .)
-  829        WithItem<nW> { "," WithItem<nW> }
-  830        "DO" [ StatSeq ] "END"
-  831                                          (. WHILE nW > 0 DO
-  832                                               SymTab.PopScope;
-  833                                               QbeGen.PopWith;
-  834                                               DEC(nW)
-  835                                             END; .) .
-  836    WithItem<VAR nW: CARDINAL>            (. VAR dt: SymTab.TypeIndex;
-  837                                               dk: INTEGER;
-  838                                               qd, qe: QbeGen.QVal;
-  839                                               qn: SymTab.Name;
-  840                                               sfx: BOOLEAN; .)
-  841      = Design<dt, dk, qd, qn, sfx>
-  842                                          (. IF dt = SymTab.InvalidType THEN
-  843                                             ELSIF (SymTab.ClassOf(dt) #
-  844                                                    SymTab.ClRecord)
-  845   AND (SymTab.ClassOf(dt) #
-  846                                                   SymTab.ClClass) THEN
-  847                                               SemError(215)
-  848                                             ELSIF SymTab.PushRecord(dt) THEN
-  849                                               QbeGen.PushWith(qd);
-  850                                               INC(nW)
-  851                                             END; .) .
-  852    (* Assignment or procedure-statement call (4.1, module level).
-  853       Bare `P;` is a syntax error; function-as-statement is 233. *)
-  854    AssOrCall                             (. VAR dt, et: SymTab.TypeIndex;
-  855                                               dk: INTEGER;
-  856                                               qd, qe, qt, ql: QbeGen.QVal;
-  857                                               qn: SymTab.Name;
-  858                                               ct2, res0: SymTab.TypeIndex;
-  859                                               q2, mg0: QbeGen.QVal;
-  860                                               isR, conv, wconv: BOOLEAN;
-  861                                               called, sfx: BOOLEAN; .)
-  862      = Design<dt, dk, qd, qn, sfx>
-  863        ( ":="
-  864          Expr<et, qe>                    (. IF (dt # SymTab.InvalidType)
-  865   AND (dk # SymTab.KindVar)
-  866   AND (dk # SymTab.KindParam)
-  867   AND (dk # SymTab.KindField) THEN
-  868                                             SemError(210)
-  869                                           ELSIF NOT SymTab.Assignable(et,
-  870                                                    dt) THEN
-  871                                             SemError(210)
-  872                                           ELSIF (dt # SymTab.InvalidType)
-  873   AND (SymTab.ClassOf(dt) =
-  874                                                 SymTab.ClClass) THEN
-  875                                             SemError(230) END;
-  876                                           isR := (dt #
-  877                                                   SymTab.InvalidType)
-  878   AND (SymTab.ClassOf(dt)
-  879                                                     = SymTab.ClReal);
-  880                                           conv := isR
-  881   AND SymTab.IsIntFamily(et);
-  882                                           wconv := (dt #
-  883                                                     SymTab.InvalidType)
-  884   AND SymTab.IsLongFamily(dt)
+  648                                                   = SymTab.ClRecord)
+  649                                                OR (SymTab.ClassOf(t)
+  650                                                   = SymTab.ClSet)
+  651                                                OR (SymTab.ClassOf(t)
+  652                                                   = SymTab.ClClass)) THEN
+  653                                               SemError(230) END; .) ] .
+  654    FormalParams
+  655      = "(" [ ParamSection { ";" ParamSection } ] ")" .
+  656    ParamSection                          (. VAR t: SymTab.TypeIndex;
+  657                                               nm: SymTab.Name;
+  658                                               i: CARDINAL;
+  659                                               isV: BOOLEAN; .)
+  660      =                                   (. isV := FALSE; .)
+  661        [ "VAR"                           (. isV := TRUE; .) ]
+  662        ParIdents<isV> ":" Type<t, TRUE>  (. i := 0;
+  663                                             WHILE i < SymTab.PendCount() DO
+  664                                               SymTab.PendName(i, nm);
+  665                                               (* value open arrays are
+  666                                                  passed as descriptor
+  667                                                  addresses (no copy):
+  668                                                  same representation as
+  669                                                  VAR formals *)
+  670                                               IF NOT QbeGen.FuncParam(nm,
+  671                                                    isV
+  672                                                    OR SymTab.IsOpenArray(t),
+  673                                                    t) THEN
+  674                                                 SemError(233) END;
+  675                                               INC(i)
+  676                                             END;
+  677                                             SymTab.FixPending(t); .) .
+  678    (* Nested procedures lower like top-level ones (4.2): the
+  679       static link gives them their parent's frame. Methods keep
+  680       parse-now/230-later. *)
+  681    ProcDecl                              (. VAR pn: SymTab.Name; .)
+  682      = ProcHeading<pn> ";"
+  683        ( "FORWARD"                       (. SymTab.MarkFwd;
+  684                                             SymTab.CloseProc;
+  685                                             QbeGen.AbortFunc; .)
+  686        | "EXTERNAL"                      (. SymTab.MarkExternal("");
+  687                                             SymTab.CloseProc;
+  688                                             QbeGen.AbortFunc; .)
+  689        |                                 (. QbeGen.EndFuncHeader; .)
+  690          Block<pn>                       (. SymTab.CloseProc;
+  691                                             QbeGen.EndFunc(
+  692                                               SymTab.ProcRes(pn)); .) ) .
+  693    Block<pn: SymTab.Name>                (. VAR m2: SymTab.Name; .)
+  694      = DeclSeq
+  695        [ "BEGIN"
+  696          [ StatSeq ] ]
+  697        "END"
+  698        GetIdent<m2>                      (. IF NOT SymTab.Equal(pn, m2) THEN
+  699                                               SemError(202) END; .) .
+  700    StatSeq
+  701      = Statement { ";" [ Statement ] } .
+  702    (* Trailing/empty statements (`a; ; b`, Coco/R's generated `;;`)
+  703       are accepted: the statement after ';' is optional. *)
+  704    Statement                             (. VAR lx: QbeGen.QVal; .)
+  705      = AssOrCall
+  706      | IfStat
+  707      | WhileStat
+  708      | RepeatStat
+  709      | LoopStat
+  710      | ForStat
+  711      | CaseStat
+  712      | WithStat
+  713      | ReturnStat
+  714      | HaltStat
+  715      | NewStat
+  716      | DisposeStat
+  717      | IncDecStat
+  718      | "EXIT"                            (. IF QbeGen.TopLoop(lx) THEN
+  719                                               QbeGen.Jmp(lx)
+  720                                             ELSE SemError(230) END; .) .
+  721    (* INC(v [,step]) / DEC(v [,step]) as builtin statements over an
+  722       integer designator. *)
+  723    IncDecStat                            (. VAR dt, et2: SymTab.TypeIndex;
+  724                                               dk: INTEGER;
+  725                                               qd, qv, qn2, qstep:
+  726                                                 QbeGen.QVal;
+  727                                               qn: SymTab.Name;
+  728                                               sfx, isInc: BOOLEAN; .)
+  729      =                                   (. isInc := TRUE; .)
+  730        ( "INC"                           (. isInc := TRUE; .)
+  731        | "DEC"                           (. isInc := FALSE; .) )
+  732        "("                               (. QbeGen.CopyOp("1", qstep); .)
+  733        Design<dt, dk, qd, qn, sfx>
+  734        [ "," Expr<et2, qstep> ]
+  735        ")"                               (. IF dt = SymTab.InvalidType THEN
+  736                                             ELSIF (dk # SymTab.KindVar)
+  737   AND (dk # SymTab.KindParam)
+  738   AND (dk # SymTab.KindField) THEN
+  739                                               SemError(210)
+  740                                             ELSIF NOT SymTab.IsIntFamily(dt) THEN
+  741                                               SemError(211)
+  742                                             ELSE
+  743                                               IF sfx
+  744                                                  OR (dk = SymTab.KindField) THEN
+  745                                                 QbeGen.ElemLoad(qd, dt, qv)
+  746                                               ELSE QbeGen.LoadVar(qn,
+  747                                                 FALSE, qv)
+  748                                               END;
+  749                                               QbeGen.NewTemp(qn2);
+  750                                               IF isInc THEN
+  751                                                 QbeGen.Op3("add", qn2, qv,
+  752                                                   qstep, FALSE)
+  753                                               ELSE QbeGen.Op3("sub", qn2, qv,
+  754                                                   qstep, FALSE)
+  755                                               END;
+  756                                               IF sfx
+  757                                                  OR (dk = SymTab.KindField) THEN
+  758                                                 QbeGen.ElemStore(qd, qn2,
+  759                                                   dt)
+  760                                               ELSE QbeGen.StoreVar(qn,
+  761                                                 qn2, FALSE)
+  762                                               END
+  763                                             END; .) .
+  764    (* NEW/DISPOSE as builtin statements (no call syntax until step 4).
+  765       Targets are pointer designators; DISPOSE nils afterwards (safer
+  766       than Wirth-undefined; documented). DISPOSE is shallow. *)
+  767    NewStat                               (. VAR dt: SymTab.TypeIndex;
+  768                                               dk: INTEGER;
+  769                                               qd, qm: QbeGen.QVal;
+  770                                               qn: SymTab.Name;
+  771                                               sfx: BOOLEAN;
+  772                                               bt: SymTab.TypeIndex; .)
+  773      = "NEW" "(" Design<dt, dk, qd, qn, sfx> ")"
+  774                                          (. IF dt = SymTab.InvalidType THEN
+  775                                             ELSIF (dk # SymTab.KindVar)
+  776   AND (dk # SymTab.KindParam)
+  777   AND (dk # SymTab.KindField) THEN
+  778                                               SemError(210)
+  779                                             ELSIF SymTab.ClassOf(dt) #
+  780                                                   SymTab.ClPtr THEN
+  781                                               SemError(219)
+  782                                             ELSE bt := SymTab.PtrBase(dt);
+  783                                               IF bt #
+  784                                                  SymTab.InvalidType THEN
+  785                                                 QbeGen.NewHeap(bt, qm);
+  786                                                 QbeGen.InitHeap(qm, bt);
+  787                                                 IF sfx
+  788                                                    OR (dk =
+  789                                                       SymTab.KindField) THEN
+  790                                                   QbeGen.ElemStore(qd, qm,
+  791                                                     dt)
+  792                                                 ELSE QbeGen.StorePtr(qn,
+  793                                                   qm)
+  794                                                 END
+  795                                               END
+  796                                             END; .) .
+  797    DisposeStat                           (. VAR dt: SymTab.TypeIndex;
+  798                                               dk: INTEGER;
+  799                                               qd, qv: QbeGen.QVal;
+  800                                               qn: SymTab.Name;
+  801                                               sfx: BOOLEAN; .)
+  802      = "DISPOSE" "(" Design<dt, dk, qd, qn, sfx> ")"
+  803                                          (. IF dt = SymTab.InvalidType THEN
+  804                                             ELSIF (dk # SymTab.KindVar)
+  805   AND (dk # SymTab.KindParam)
+  806   AND (dk # SymTab.KindField) THEN
+  807                                               SemError(210)
+  808                                             ELSIF SymTab.ClassOf(dt) #
+  809                                                   SymTab.ClPtr THEN
+  810                                               SemError(219)
+  811                                             ELSE
+  812                                               IF sfx
+  813                                                  OR (dk =
+  814                                                     SymTab.KindField) THEN
+  815                                                 QbeGen.ElemLoad(qd, dt,
+  816                                                   qv)
+  817                                               ELSE QbeGen.LoadPtr(qn, qv)
+  818                                               END;
+  819                                               QbeGen.FreeHeap(qv);
+  820                                               IF sfx
+  821                                                  OR (dk =
+  822                                                     SymTab.KindField) THEN
+  823                                                 QbeGen.ElemStore(qd, "0",
+  824                                                   dt)
+  825                                               ELSE QbeGen.StorePtr(qn,
+  826                                                 "0")
+  827                                               END
+  828                                             END; .) .
+  829    (* WITH pushes each record's fields (inner wins) plus its base
+  830       address; field designators resolve through both stacks. *)
+  831    WithStat                              (. VAR nW: CARDINAL; .)
+  832      = "WITH"                            (. nW := 0; .)
+  833        WithItem<nW> { "," WithItem<nW> }
+  834        "DO" [ StatSeq ] "END"
+  835                                          (. WHILE nW > 0 DO
+  836                                               SymTab.PopScope;
+  837                                               QbeGen.PopWith;
+  838                                               DEC(nW)
+  839                                             END; .) .
+  840    WithItem<VAR nW: CARDINAL>            (. VAR dt: SymTab.TypeIndex;
+  841                                               dk: INTEGER;
+  842                                               qd, qe: QbeGen.QVal;
+  843                                               qn: SymTab.Name;
+  844                                               sfx: BOOLEAN; .)
+  845      = Design<dt, dk, qd, qn, sfx>
+  846                                          (. IF dt = SymTab.InvalidType THEN
+  847                                             ELSIF (SymTab.ClassOf(dt) #
+  848                                                    SymTab.ClRecord)
+  849   AND (SymTab.ClassOf(dt) #
+  850                                                   SymTab.ClClass) THEN
+  851                                               SemError(215)
+  852                                             ELSIF SymTab.PushRecord(dt) THEN
+  853                                               QbeGen.PushWith(qd);
+  854                                               INC(nW)
+  855                                             END; .) .
+  856    (* Assignment or procedure-statement call (4.1, module level).
+  857       Bare `P;` is a syntax error; function-as-statement is 233. *)
+  858    AssOrCall                             (. VAR dt, et: SymTab.TypeIndex;
+  859                                               dk: INTEGER;
+  860                                               qd, qe, qt, ql: QbeGen.QVal;
+  861                                               qn: SymTab.Name;
+  862                                               ct2, res0: SymTab.TypeIndex;
+  863                                               q2, mg0: QbeGen.QVal;
+  864                                               isR, conv, wconv: BOOLEAN;
+  865                                               called, sfx: BOOLEAN; .)
+  866      = Design<dt, dk, qd, qn, sfx>
+  867        ( ":="
+  868          Expr<et, qe>                    (. IF (dt # SymTab.InvalidType)
+  869   AND (dk # SymTab.KindVar)
+  870   AND (dk # SymTab.KindParam)
+  871   AND (dk # SymTab.KindField) THEN
+  872                                             SemError(210)
+  873                                           ELSIF NOT SymTab.Assignable(et,
+  874                                                    dt) THEN
+  875                                             SemError(210)
+  876                                           ELSIF (dt # SymTab.InvalidType)
+  877   AND (SymTab.ClassOf(dt) =
+  878                                                 SymTab.ClClass) THEN
+  879                                             SemError(230) END;
+  880                                           isR := (dt #
+  881                                                   SymTab.InvalidType)
+  882   AND (SymTab.ClassOf(dt)
+  883                                                     = SymTab.ClReal);
+  884                                           conv := isR
   885   AND SymTab.IsIntFamily(et);
-  886                                           IF ((dk = SymTab.KindVar)
-  887                                               OR (dk = SymTab.KindParam)
-  888                                               OR (dk = SymTab.KindField))
-  889   AND (dt # SymTab.InvalidType)
-  890   AND (et # SymTab.InvalidType)
-  891   AND (SymTab.ClassOf(dt) #
-  892                                                 SymTab.ClClass) THEN
-  893                                             IF sfx
-  894                                                OR (dk = SymTab.KindField) THEN
-  895                                               IF SymTab.ClassOf(dt) =
-  896                                                  SymTab.ClArray THEN
-  897                                                 QbeGen.CopyArray(qd, qe,
-  898                                                   dt)
-  899                                               ELSIF SymTab.ClassOf(dt) =
-  900                                                   SymTab.ClSet THEN
-  901                                                 QbeGen.CopySet(qd, qe,
-  902                                                   SymTab.SetWords(dt),
-  903                                                   SymTab.SetWords(et))
-  904                                               ELSIF SymTab.ClassOf(dt) =
-  905                                                   SymTab.ClRecord THEN
-  906                                                 QbeGen.CopyRecord(qd, qe,
-  907                                                   dt)
-  908                                               ELSIF SymTab.IsLongFamily(dt) THEN
-  909                                                 IF wconv THEN
-  910                                                   QbeGen.WidenLong(qe, ql);
-  911                                                   QbeGen.ElemStore(qd, ql,
-  912                                                     dt)
-  913                                                 ELSE QbeGen.ElemStore(qd, qe,
-  914                                                   dt)
-  915                                                 END
-  916                                               ELSIF conv THEN
-  917                                                 QbeGen.ConvIR(qe, qt);
-  918                                                 QbeGen.ElemStore(qd, qt,
-  919                                                   dt)
-  920                                               ELSE QbeGen.ElemStore(qd, qe,
-  921                                                 dt)
-  922                                               END
-  923                                             ELSIF SymTab.ClassOf(dt) =
-  924                                                   SymTab.ClArray THEN
-  925                                               QbeGen.CopyArray(qd, qe, dt)
-  926                                             ELSIF SymTab.ClassOf(dt) =
-  927                                                   SymTab.ClSet THEN
-  928                                               QbeGen.CopySet(qd, qe,
-  929                                                 SymTab.SetWords(dt),
-  930                                                 SymTab.SetWords(et))
-  931                                             ELSIF SymTab.ClassOf(dt) =
-  932                                                   SymTab.ClRecord THEN
-  933                                               QbeGen.CopyRecord(qd, qe, dt)
-  934                                             ELSIF (SymTab.ClassOf(dt) =
-  935                                                   SymTab.ClPtr)
-  936                                                OR (SymTab.ClassOf(dt) =
-  937                                                   SymTab.ClProc) THEN
-  938                                               QbeGen.StorePtr(qn, qe)
-  939                                             ELSIF SymTab.IsLongFamily(dt) THEN
-  940                                               IF wconv THEN
-  941                                                 QbeGen.WidenLong(qe, ql);
-  942                                                 QbeGen.StoreLong(qn, ql)
-  943                                               ELSE QbeGen.StoreLong(qn, qe)
-  944                                               END
-  945                                             ELSIF conv THEN
-  946                                               QbeGen.ConvIR(qe, qt);
-  947                                               QbeGen.StoreVar(qn, qt, TRUE)
-  948                                             ELSE
-  949                                               QbeGen.StoreVar(qn, qe, isR)
-  950                                             END
-  951                                           END; .)
-  952        | ArgList<qn, dt, qd, FALSE, ct2, q2, called>
-  953        |                                 (* bare `P;`: proper parameterless
-  954                                              procedure call; anything else
-  955                                              here is 233 (was a bare syntax
-  956                                              error before 4.2) *)
-  957                                          (. IF (dk = SymTab.KindProc)
-  958   AND NOT sfx THEN
-  959                                               res0 := SymTab.ProcRes(qn);
-  960                                               IF res0 #
-  961                                                  SymTab.InvalidType THEN
-  962                                                 SemError(233)
-  963                                               ELSIF SymTab.ProcNPar(qn) #
-  964                                                       0 THEN
-  965                                                 SemError(233)
-  966                                               ELSE QbeGen.Mangled(qn,
-  967                                                 SymTab.ProcUid(qn), mg0);
-  968                                                 QbeGen.CallBegin(mg0,
-  969                                                   res0,
-  970                                                   SymTab.ProcDepthOf(qn),
-  971                                                   SymTab.IsExternal(qn));
-  972                                                 QbeGen.CallEnd(FALSE, q2)
-  973                                               END
-  974                                             ELSE SemError(233)
-  975                                             END; .) ) .
-  976    (* Actual-parameter list shared by statement and expression calls.
-  977       want selects CallEnd's result handling; t/q carry the call
-  978       value (statement calls discard). Arity/type failures are 233;
-  979       evaluation code still emits so the .ssa stays assembleable. *)
-  980    ArgList<pn: SymTab.Name; pt: SymTab.TypeIndex; callee: QbeGen.QVal;
-  981            want: BOOLEAN;
-  982            VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal;
-  983            VAR called: BOOLEAN>          (. VAR i: CARDINAL;
-  984                                               res: SymTab.TypeIndex;
-  985                                               mg: QbeGen.QVal;
-  986                                               ok, ind: BOOLEAN; .)
-  987      = "("                               (. called := TRUE;
-  988                                             ok := TRUE;
-  989                                             ind := FALSE;
-  990                                             IF SymTab.SymKind(pn) =
-  991                                                SymTab.KindProc THEN
-  992                                               res := SymTab.ProcRes(pn);
-  993                                               QbeGen.Mangled(pn,
-  994                                                 SymTab.ProcUid(pn), mg);
-  995                                               QbeGen.CallBegin(mg, res,
-  996                                                 SymTab.ProcDepthOf(pn),
-  997                                                 SymTab.IsExternal(pn))
-  998                                             ELSIF (pt # SymTab.InvalidType)
-  999    AND (SymTab.ClassOf(pt) = SymTab.ClProc) THEN
- 1000                                               ind := TRUE;
- 1001                                               res :=
- 1002                                                 SymTab.ProcTypeRes(pt);
- 1003                                               QbeGen.CallBeginInd(callee,
- 1004                                                 res, FALSE)
- 1005                                             ELSE SemError(233);
- 1006                                               ok := FALSE;
- 1007                                               res := SymTab.InvalidType
- 1008                                             END;
- 1009                                             i := 0; .)
- 1010        [ ActParam<pn, pt, ind, i>        (. INC(i); .)
- 1011          { "," ActParam<pn, pt, ind, i>  (. INC(i); .) } ]
- 1012        ")"                               (. IF ok THEN
- 1013                                               IF ind THEN
- 1014                                                 IF i #
- 1015                                                    SymTab.ProcTypeNPar(pt) THEN
- 1016                                                   SemError(233); ok := FALSE
- 1017                                                 END
- 1018                                               ELSIF i #
- 1019                                                  SymTab.ProcNPar(pn) THEN
- 1020                                                 SemError(233); ok := FALSE
- 1021                                               END
- 1022                                             END;
- 1023                                             IF NOT ok THEN
- 1024                                               t := SymTab.InvalidType;
- 1025                                               QbeGen.CopyOp("0", q)
- 1026                                             ELSIF want THEN
- 1027                                               IF res =
- 1028                                                  SymTab.InvalidType THEN
- 1029                                                 SemError(233);
- 1030                                                 t := SymTab.InvalidType;
- 1031                                                 QbeGen.CopyOp("0", q)
- 1032                                               ELSE t := res;
- 1033                                                 QbeGen.CallEnd(TRUE, q)
- 1034                                               END
- 1035                                             ELSE
- 1036                                               IF res #
- 1037                                                  SymTab.InvalidType THEN
- 1038                                                 SemError(233)
- 1039                                               END;
- 1040                                               t := SymTab.InvalidType;
- 1041                                               QbeGen.CopyOp("0", q);
- 1042                                               QbeGen.CallEnd(FALSE, q)
- 1043                                             END; .) .
- 1044    (* One actual: VAR formals take recorded designator addresses
- 1045       (233 otherwise); value formals take converted expressions. *)
- 1046    ActParam<pn: SymTab.Name; pt: SymTab.TypeIndex; ind: BOOLEAN;
- 1047             i: CARDINAL>                 (. VAR at, ft: SymTab.TypeIndex;
- 1048                                               qe, qa, qt: QbeGen.QVal;
- 1049                                               isV, conv: BOOLEAN; .)
- 1050      = Expr<at, qe>                      (. IF ind THEN
- 1051                                               ft :=
- 1052                                                 SymTab.ProcTypeParamType(pt,
- 1053                                                   i);
- 1054                                               isV :=
- 1055                                                 SymTab.ProcTypeParamIsVar(pt,
- 1056                                                   i)
- 1057                                             ELSE
- 1058                                               ft := SymTab.ParamType(pn, i);
- 1059                                               isV := SymTab.ParamIsVar(pn, i)
- 1060                                             END;
- 1061                                             IF (at = SymTab.InvalidType)
- 1062                                                OR (ft =
- 1063                                                   SymTab.InvalidType) THEN
- 1064                                             ELSIF isV THEN
- 1065                                               IF (SymTab.ClassOf(at)
- 1066                                                  = SymTab.ClChar)
- 1067    AND (SymTab.ClassOf(ft) = SymTab.ClArray)
- 1068    AND (SymTab.ClassOf(SymTab.ArrayElem(ft))
- 1069         = SymTab.ClChar)
- 1070    AND QbeGen.IsImm(qe) THEN
- 1071                                                 (* 1-char string
- 1072                                                    literal passed to
- 1073                                                    a VAR ARRAY OF CHAR *)
- 1074                                                 QbeGen.DeclCharStr(qe,
- 1075                                                      qa);
- 1076                                                 IF NOT QbeGen.CallArg(qa,
- 1077                                                    "l") THEN
- 1078                                                   SemError(233)
- 1079                                                 END
- 1080                                               ELSIF NOT QbeGen.AddrOfVal(qe,
- 1081                                                    qa) THEN
- 1082                                                 SemError(233)
- 1083                                               ELSIF NOT SymTab.VarParamOk(at,
- 1084                                                        ft) THEN
- 1085                                                 SemError(233)
- 1086                                               ELSIF NOT QbeGen.CallArg(qa,
- 1087                                                        "l") THEN
- 1088                                                 SemError(233)
- 1089                                               END
- 1090                                             ELSE
- 1091                                               IF (SymTab.ClassOf(at)
- 1092                                                  = SymTab.ClChar)
- 1093    AND (SymTab.ClassOf(ft) = SymTab.ClArray)
- 1094    AND (SymTab.ClassOf(SymTab.ArrayElem(ft))
- 1095         = SymTab.ClChar)
- 1096    AND QbeGen.IsImm(qe) THEN
- 1097                                                 (* 1-char string
- 1098                                                    literal passed to
- 1099                                                    ARRAY OF CHAR *)
- 1100                                                 QbeGen.DeclCharStr(qe,
- 1101                                                      qa);
- 1102                                                 IF NOT QbeGen.CallArg(qa,
- 1103                                                    "l") THEN
- 1104                                                   SemError(233)
- 1105                                                 END
- 1106                                               ELSIF NOT SymTab.Assignable(at,
- 1107                                                    ft) THEN
- 1108                                                 SemError(233)
- 1109                                               ELSE
- 1110                                                 conv := (SymTab.ClassOf(
- 1111                                                   ft) = SymTab.ClReal)
- 1112   AND SymTab.IsIntFamily(at);
- 1113                                                 IF conv THEN
- 1114                                                   QbeGen.ConvIR(qe, qt);
- 1115                                                   IF NOT QbeGen.CallArg(qt,
- 1116                                                      "d") THEN
- 1117                                                     SemError(233)
- 1118                                                   END
- 1119                                                 ELSIF NOT QbeGen.CallArg(qe,
- 1120                                                   QbeGen.ArgClass(ft)) THEN
- 1121                                                   SemError(233)
- 1122                                                 END
- 1123                                               END
- 1124                                             END; .) .
- 1125    IfStat                                (. VAR t: SymTab.TypeIndex;
- 1126                                               q, lThen, lElse, lEnd:
- 1127                                                 QbeGen.QVal;
- 1128                                               hasElse: BOOLEAN; .)
- 1129      = "IF"                              (. hasElse := FALSE; .)
- 1130        Expr<t, q>                        (. IF NOT SymTab.BoolCheck(t) THEN
- 1131                                               SemError(214) END;
- 1132                                             QbeGen.NewLabel(lThen);
- 1133                                             QbeGen.NewLabel(lElse);
- 1134                                             QbeGen.NewLabel(lEnd);
- 1135                                             QbeGen.Jnz(q, lThen, lElse);
- 1136                                             QbeGen.EmitLabel(lThen); .)
- 1137        "THEN" [ StatSeq ]                (. QbeGen.Jmp(lEnd); .)
- 1138        { "ELSIF"                         (. QbeGen.EmitLabel(lElse);
- 1139                                             QbeGen.NewLabel(lElse); .)
- 1140          Expr<t, q>                      (. IF NOT SymTab.BoolCheck(t) THEN
- 1141                                               SemError(214) END;
- 1142                                             QbeGen.NewLabel(lThen);
- 1143                                             QbeGen.Jnz(q, lThen, lElse);
- 1144                                             QbeGen.EmitLabel(lThen); .)
- 1145          "THEN" [ StatSeq ]              (. QbeGen.Jmp(lEnd); .) }
- 1146        [ "ELSE"                          (. QbeGen.EmitLabel(lElse);
- 1147                                             hasElse := TRUE; .)
- 1148          [ StatSeq ] ]
- 1149        "END"                             (. IF hasElse THEN
- 1150                                               QbeGen.EmitLabel(lEnd)
- 1151                                             ELSE QbeGen.EmitLabel(lElse);
- 1152                                               QbeGen.EmitLabel(lEnd)
- 1153                                             END; .) .
- 1154    WhileStat                             (. VAR t: SymTab.TypeIndex;
- 1155                                               q, lTop, lBody, lEnd:
- 1156                                                 QbeGen.QVal; .)
- 1157      = "WHILE"                           (. QbeGen.NewLabel(lTop);
- 1158                                             QbeGen.NewLabel(lBody);
- 1159                                             QbeGen.NewLabel(lEnd);
- 1160                                             QbeGen.EmitLabel(lTop); .)
- 1161        Expr<t, q>                        (. IF NOT SymTab.BoolCheck(t) THEN
- 1162                                               SemError(214) END;
- 1163                                             QbeGen.Jnz(q, lBody, lEnd);
- 1164                                             QbeGen.EmitLabel(lBody); .)
- 1165        "DO" [ StatSeq ]                      (. QbeGen.Jmp(lTop); .)
- 1166        "END"                             (. QbeGen.EmitLabel(lEnd); .) .
- 1167    RepeatStat                            (. VAR t: SymTab.TypeIndex;
- 1168                                               q, lTop, lEnd: QbeGen.QVal; .)
- 1169      = "REPEAT"                          (. QbeGen.NewLabel(lTop);
- 1170                                             QbeGen.NewLabel(lEnd);
- 1171                                             QbeGen.EmitLabel(lTop); .)
- 1172        [ StatSeq ]
- 1173        "UNTIL" Expr<t, q>                (. IF NOT SymTab.BoolCheck(t) THEN
- 1174                                               SemError(214) END;
- 1175                                             QbeGen.Jnz(q, lEnd, lTop);
- 1176                                             QbeGen.EmitLabel(lEnd); .) .
- 1177    LoopStat                              (. VAR lTop, lEnd: QbeGen.QVal; .)
- 1178      = "LOOP"                            (. QbeGen.NewLabel(lTop);
- 1179                                             QbeGen.NewLabel(lEnd);
- 1180                                             QbeGen.PushLoop(lEnd);
- 1181                                             QbeGen.EmitLabel(lTop); .)
- 1182        [ StatSeq ]
- 1183        "END"                             (. QbeGen.Jmp(lTop);
- 1184                                             QbeGen.PopLoop;
- 1185                                             QbeGen.EmitLabel(lEnd); .) .
- 1186    (* FOR with static-sign BY (literal, non-zero; 220 otherwise).
- 1187       Runtime direction would need a compare-select; the literal
- 1188       sign picks cslew/csegew at "DO" time. *)
- 1189    ForStat                               (. VAR lv: SymTab.Name;
- 1190                                               tlo, thi, tby:
- 1191                                                 SymTab.TypeIndex;
- 1192                                               qlo, qhi, qby, qt, qk, qb:
- 1193                                                 QbeGen.QVal;
- 1194                                               lTop, lBody, lEnd:
- 1195                                                 QbeGen.QVal;
- 1196                                               by: INTEGER;
- 1197                                               ok: BOOLEAN; .)
- 1198      = "FOR"                             (. by := 1; .)
- 1199        GetIdent<lv>                      (. ok := SymTab.Lookup(lv);
- 1200                                             IF NOT ok THEN
- 1201                                               SemError(201)
- 1202                                             ELSIF (SymTab.SymKind(lv) #
- 1203                                                    SymTab.KindVar)
- 1204   AND (SymTab.SymKind(lv) #
- 1205                                                   SymTab.KindParam) THEN
- 1206                                               SemError(220); ok := FALSE
- 1207                                             ELSIF NOT SymTab.IsIntFamily(
- 1208                                                     SymTab.SymType(lv)) THEN
- 1209                                               SemError(220); ok := FALSE
- 1210                                             END; .)
- 1211        ":=" Expr<tlo, qlo>               (. IF NOT SymTab.IsIntFamily(tlo) THEN
- 1212                                               SemError(220); ok := FALSE
- 1213                                             END; .)
- 1214        "TO" Expr<thi, qhi>               (. IF NOT SymTab.IsIntFamily(thi) THEN
- 1215                                               SemError(220); ok := FALSE
- 1216                                             END; .)
- 1217        [ "BY" Expr<tby, qby>             (. IF (tby #
- 1218                                               SymTab.InvalidType)
- 1219   AND NOT SymTab.IsIntFamily(tby) THEN
- 1220                                               SemError(220); ok := FALSE
- 1221                                             END;
- 1222                                             IF NOT SymTab.ConstInt(qby, by) THEN
- 1223                                               SemError(230); by := 1
- 1224                                             ELSIF by = 0 THEN
- 1225                                               SemError(220); by := 1
- 1226                                             END; .) ]
- 1227        "DO"                              (. IF ok THEN
- 1228                                               QbeGen.StoreVar(lv, qlo,
- 1229                                                 FALSE) END;
- 1230                                             QbeGen.NewLabel(lTop);
- 1231                                             QbeGen.NewLabel(lBody);
- 1232                                             QbeGen.NewLabel(lEnd);
- 1233                                             QbeGen.EmitLabel(lTop);
- 1234                                             QbeGen.LoadVar(lv, FALSE, qt);
- 1235                                             QbeGen.NewTemp(qk);
- 1236                                             IF by > 0 THEN
- 1237                                               QbeGen.Op3("cslew", qk,
- 1238                                                 qt, qhi, FALSE)
- 1239                                             ELSE QbeGen.Op3("csgew", qk,
- 1240                                               qt, qhi, FALSE)
- 1241                                             END;
- 1242                                             QbeGen.Jnz(qk, lBody, lEnd);
- 1243                                             QbeGen.EmitLabel(lBody); .)
- 1244        [ StatSeq ]
- 1245        "END"                             (. IF ok THEN
- 1246                                               QbeGen.LoadVar(lv, FALSE,
- 1247                                                 qt);
- 1248                                               QbeGen.IntStr(by, qb);
- 1249                                               QbeGen.NewTemp(qk);
- 1250                                               QbeGen.Op3("add", qk,
- 1251                                                 qt, qb, FALSE);
- 1252                                               QbeGen.StoreVar(lv, qk,
- 1253                                                 FALSE) END;
- 1254                                             QbeGen.Jmp(lTop);
- 1255                                             QbeGen.EmitLabel(lEnd); .) .
- 1256    CaseStat                              (. VAR tsel: SymTab.TypeIndex;
- 1257                                               qsel, lEnd: QbeGen.QVal; .)
- 1258      = "CASE" Expr<tsel, qsel>           (. QbeGen.NewLabel(lEnd); .)
- 1259        "OF" CaseAlt<tsel, qsel, lEnd>
- 1260        { "|" CaseAlt<tsel, qsel, lEnd> }
- 1261        [ "ELSE" [ StatSeq ] ]
- 1262        "END"                             (. QbeGen.EmitLabel(lEnd); .) .
- 1263    (* Compare-chain lowering: each alternative ends its match-tests
- 1264       with "jmp lAfter", so the no-match fallthrough skips the body:
- 1265       "cmp; jnz(lBody,lF); lF: ... ; jmp lAfter; lBody: S; jmp lEnd;
- 1266       lAfter:". Falls into the next alternative, ELSE, or END. *)
- 1267    CaseAlt<tsel: SymTab.TypeIndex; qsel: QbeGen.QVal;
- 1268            lEnd: QbeGen.QVal>            (. VAR lBody, lAfter: QbeGen.QVal; .)
- 1269      =                                   (. QbeGen.NewLabel(lBody);
- 1270                                             QbeGen.NewLabel(lAfter); .)
- 1271        CaseLabel<tsel, qsel, lBody>
- 1272        { "," CaseLabel<tsel, qsel, lBody> }
- 1273        ":"                               (. QbeGen.Jmp(lAfter);
- 1274                                             QbeGen.EmitLabel(lBody); .)
- 1275        [ StatSeq ]                       (. QbeGen.Jmp(lEnd);
- 1276                                             QbeGen.EmitLabel(lAfter); .) .
- 1277    CaseLabel<tsel: SymTab.TypeIndex; qsel: QbeGen.QVal;
- 1278              lBody: QbeGen.QVal>         (. VAR t2, t3: SymTab.TypeIndex;
- 1279                                               q2, q3, qc, qd, qe:
- 1280                                                 QbeGen.QVal;
- 1281                                               lNext: QbeGen.QVal; .)
- 1282      = Expr<t2, q2>                      (. IF (t2 #
- 1283                                               SymTab.InvalidType)
- 1284   AND (tsel #
- 1285                                                 SymTab.InvalidType)
- 1286   AND ((SymTab.ClassOf(t2) =
- 1287                                                  SymTab.ClSet)
- 1288                                                 OR (SymTab.ClassOf(tsel) =
- 1289                                                     SymTab.ClSet)) THEN
- 1290                                               SemError(230)
- 1291                                             ELSIF (t2 #
- 1292                                               SymTab.InvalidType)
- 1293   AND (tsel #
- 1294                                                 SymTab.InvalidType)
- 1295   AND NOT SymTab.EqCheck(t2,
- 1296                                                   tsel) THEN
- 1297                                               SemError(213) END;
- 1298                                             IF NOT QbeGen.IsImm(q2) THEN
- 1299                                               SemError(230);
- 1300                                               QbeGen.CopyOp("0", q2)
- 1301                                             END;
- 1302                                             QbeGen.NewLabel(lNext);
- 1303                                             QbeGen.Cmp(SymTab.OpEq,
- 1304                                               qsel, q2, qc, FALSE);
- 1305                                             QbeGen.Jnz(qc, lBody, lNext);
- 1306                                             QbeGen.EmitLabel(lNext); .)
- 1307        [ ".." Expr<t3, q3>               (. IF (t3 #
- 1308                                               SymTab.InvalidType)
- 1309   AND (tsel #
- 1310                                                 SymTab.InvalidType)
- 1311   AND NOT SymTab.EqCheck(t3,
- 1312                                                   tsel) THEN
- 1313                                               SemError(213) END;
- 1314                                             IF NOT QbeGen.IsImm(q3) THEN
- 1315                                               SemError(230);
- 1316                                               QbeGen.CopyOp("0", q3)
- 1317                                             END;
- 1318                                             QbeGen.Cmp(SymTab.OpGe,
- 1319                                               qsel, q2, qc, FALSE);
- 1320                                             QbeGen.Cmp(SymTab.OpLe,
- 1321                                               qsel, q3, qd, FALSE);
- 1322                                             QbeGen.NewTemp(qe);
- 1323                                             QbeGen.Op3("and", qe, qc, qd,
- 1324                                               FALSE);
- 1325                                             QbeGen.NewLabel(lNext);
- 1326                                             QbeGen.Jnz(qe, lBody, lNext);
- 1327                                             QbeGen.EmitLabel(lNext); .) ] .
- 1328    ReturnStat                            (. VAR t: SymTab.TypeIndex;
- 1329                                               q, qt: QbeGen.QVal;
- 1330                                               res: SymTab.TypeIndex;
- 1331                                               hadE, conv: BOOLEAN; .)
- 1332      = "RETURN"                          (. hadE := FALSE; .)
- 1333        [ Expr<t, q>                      (. hadE := TRUE; .) ]
- 1334                                          (. conv := FALSE;
- 1335                                             IF NOT SymTab.InProc() THEN
- 1336                                               SemError(232)
- 1337                                             ELSE res := SymTab.CurRes();
- 1338                                               IF NOT hadE THEN
- 1339                                                 IF res #
- 1340                                                    SymTab.InvalidType THEN
- 1341                                                   SemError(232)
- 1342                                                 ELSE QbeGen.EmitRet(q,
- 1343                                                   FALSE)
- 1344                                                 END
- 1345                                               ELSIF (res =
- 1346                                                      SymTab.InvalidType)
- 1347                                                  OR (t #
- 1348                                                      SymTab.InvalidType)
- 1349   AND NOT SymTab.Assignable(t,
- 1350                                                       res) THEN
- 1351                                                 SemError(232)
- 1352                                               ELSE
- 1353                                                 conv := (SymTab.ClassOf(
- 1354                                                   res) = SymTab.ClReal)
- 1355   AND SymTab.IsIntFamily(t);
- 1356                                                 IF conv THEN
- 1357                                                   QbeGen.ConvIR(q, qt);
- 1358                                                   QbeGen.EmitRet(qt, TRUE)
- 1359                                                 ELSE QbeGen.EmitRet(q, TRUE)
- 1360                                                 END
- 1361                                               END
- 1362                                             END; .) .
- 1363    HaltStat                              (. VAR t: SymTab.TypeIndex;
- 1364                                               q: QbeGen.QVal; .)
- 1365      = "HALT" [ "(" Expr<t, q> ")" ]     (. QbeGen.HaltQ; .) .
- 1366    (* Designator: scalar loads, array addresses, and index suffixes.
- 1367       Each index descends one level (bounds-checked, trap on breach);
- 1368       nested levels reload the inner descriptor address. q ends as the
- 1369       value (scalars), the descriptor address (plain arrays), or the
- 1370       element address (indexed); sfx marks the indexed form. *)
- 1371    Design<VAR t: SymTab.TypeIndex; VAR k: INTEGER;
- 1372           VAR q: QbeGen.QVal; VAR qn: SymTab.Name; VAR sfx: BOOLEAN>
- 1373                                          (. VAR n, fn, mal: SymTab.Name;
- 1374                                               cls: INTEGER;
- 1375                                               curT, it, eT, bt:
- 1376                                                 SymTab.TypeIndex;
- 1377                                               iq, ql, qlo, qhi, qe:
- 1378                                                 QbeGen.QVal;
- 1379                                               lo, hi: INTEGER;
- 1380                                               fo: INTEGER;
- 1381                                               isOpen: BOOLEAN;
- 1382                                               qb, cv: QbeGen.QVal; .)
- 1383      = GetIdent<n>                       (. QbeGen.CopyOp(n, qn);
- 1384                                             sfx := FALSE;
- 1385                                             IF NOT SymTab.Lookup(n) THEN
- 1386                                               SemError(201);
- 1387                                               t := SymTab.InvalidType;
- 1388                                               k := -1;
- 1389                                               QbeGen.CopyOp("0", q)
- 1390                                             ELSE
- 1391                                               t := SymTab.SymType(n);
- 1392                                               k := SymTab.SymKind(n);
- 1393                                               IF k = SymTab.KindConst THEN
- 1394                                                 IF SymTab.Equal(n,
- 1395                                                    "TRUE") THEN
- 1396                                                   t := SymTab.BoolType();
- 1397                                                   QbeGen.CopyOp("1", q)
- 1398                                                 ELSIF SymTab.Equal(n,
- 1399                                                    "FALSE") THEN
+  886                                           wconv := (dt #
+  887                                                     SymTab.InvalidType)
+  888   AND SymTab.IsLongFamily(dt)
+  889   AND SymTab.IsIntFamily(et);
+  890                                           IF ((dk = SymTab.KindVar)
+  891                                               OR (dk = SymTab.KindParam)
+  892                                               OR (dk = SymTab.KindField))
+  893   AND (dt # SymTab.InvalidType)
+  894   AND (et # SymTab.InvalidType)
+  895   AND (SymTab.ClassOf(dt) #
+  896                                                 SymTab.ClClass) THEN
+  897                                             IF sfx
+  898                                                OR (dk = SymTab.KindField) THEN
+  899                                               IF SymTab.ClassOf(dt) =
+  900                                                  SymTab.ClArray THEN
+  901                                                 QbeGen.CopyArray(qd, qe,
+  902                                                   dt)
+  903                                               ELSIF SymTab.ClassOf(dt) =
+  904                                                   SymTab.ClSet THEN
+  905                                                 QbeGen.CopySet(qd, qe,
+  906                                                   SymTab.SetWords(dt),
+  907                                                   SymTab.SetWords(et))
+  908                                               ELSIF SymTab.ClassOf(dt) =
+  909                                                   SymTab.ClRecord THEN
+  910                                                 QbeGen.CopyRecord(qd, qe,
+  911                                                   dt)
+  912                                               ELSIF SymTab.IsLongFamily(dt) THEN
+  913                                                 IF wconv THEN
+  914                                                   QbeGen.WidenLong(qe, ql);
+  915                                                   QbeGen.ElemStore(qd, ql,
+  916                                                     dt)
+  917                                                 ELSE QbeGen.ElemStore(qd, qe,
+  918                                                   dt)
+  919                                                 END
+  920                                               ELSIF conv THEN
+  921                                                 QbeGen.ConvIR(qe, qt);
+  922                                                 QbeGen.ElemStore(qd, qt,
+  923                                                   dt)
+  924                                               ELSE QbeGen.ElemStore(qd, qe,
+  925                                                 dt)
+  926                                               END
+  927                                             ELSIF SymTab.ClassOf(dt) =
+  928                                                   SymTab.ClArray THEN
+  929                                               QbeGen.CopyArray(qd, qe, dt)
+  930                                             ELSIF SymTab.ClassOf(dt) =
+  931                                                   SymTab.ClSet THEN
+  932                                               QbeGen.CopySet(qd, qe,
+  933                                                 SymTab.SetWords(dt),
+  934                                                 SymTab.SetWords(et))
+  935                                             ELSIF SymTab.ClassOf(dt) =
+  936                                                   SymTab.ClRecord THEN
+  937                                               QbeGen.CopyRecord(qd, qe, dt)
+  938                                             ELSIF (SymTab.ClassOf(dt) =
+  939                                                   SymTab.ClPtr)
+  940                                                OR (SymTab.ClassOf(dt) =
+  941                                                   SymTab.ClProc) THEN
+  942                                               QbeGen.StorePtr(qn, qe)
+  943                                             ELSIF SymTab.IsLongFamily(dt) THEN
+  944                                               IF wconv THEN
+  945                                                 QbeGen.WidenLong(qe, ql);
+  946                                                 QbeGen.StoreLong(qn, ql)
+  947                                               ELSE QbeGen.StoreLong(qn, qe)
+  948                                               END
+  949                                             ELSIF conv THEN
+  950                                               QbeGen.ConvIR(qe, qt);
+  951                                               QbeGen.StoreVar(qn, qt, TRUE)
+  952                                             ELSE
+  953                                               QbeGen.StoreVar(qn, qe, isR)
+  954                                             END
+  955                                           END; .)
+  956        | ArgList<qn, dt, qd, FALSE, ct2, q2, called>
+  957        |                                 (* bare `P;`: proper parameterless
+  958                                              procedure call; anything else
+  959                                              here is 233 (was a bare syntax
+  960                                              error before 4.2) *)
+  961                                          (. IF (dk = SymTab.KindProc)
+  962   AND NOT sfx THEN
+  963                                               res0 := SymTab.ProcRes(qn);
+  964                                               IF res0 #
+  965                                                  SymTab.InvalidType THEN
+  966                                                 SemError(233)
+  967                                               ELSIF SymTab.ProcNPar(qn) #
+  968                                                       0 THEN
+  969                                                 SemError(233)
+  970                                               ELSE QbeGen.Mangled(qn,
+  971                                                 SymTab.ProcUid(qn), mg0);
+  972                                                 QbeGen.CallBegin(mg0,
+  973                                                   res0,
+  974                                                   SymTab.ProcDepthOf(qn),
+  975                                                   SymTab.IsExternal(qn));
+  976                                                 QbeGen.CallEnd(FALSE, q2)
+  977                                               END
+  978                                             ELSE SemError(233)
+  979                                             END; .) ) .
+  980    (* Actual-parameter list shared by statement and expression calls.
+  981       want selects CallEnd's result handling; t/q carry the call
+  982       value (statement calls discard). Arity/type failures are 233;
+  983       evaluation code still emits so the .ssa stays assembleable. *)
+  984    ArgList<pn: SymTab.Name; pt: SymTab.TypeIndex; callee: QbeGen.QVal;
+  985            want: BOOLEAN;
+  986            VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal;
+  987            VAR called: BOOLEAN>          (. VAR i: CARDINAL;
+  988                                               res: SymTab.TypeIndex;
+  989                                               mg: QbeGen.QVal;
+  990                                               ok, ind: BOOLEAN; .)
+  991      = "("                               (. called := TRUE;
+  992                                             ok := TRUE;
+  993                                             ind := FALSE;
+  994                                             IF SymTab.SymKind(pn) =
+  995                                                SymTab.KindProc THEN
+  996                                               res := SymTab.ProcRes(pn);
+  997                                               QbeGen.Mangled(pn,
+  998                                                 SymTab.ProcUid(pn), mg);
+  999                                               QbeGen.CallBegin(mg, res,
+ 1000                                                 SymTab.ProcDepthOf(pn),
+ 1001                                                 SymTab.IsExternal(pn))
+ 1002                                             ELSIF (pt # SymTab.InvalidType)
+ 1003    AND (SymTab.ClassOf(pt) = SymTab.ClProc) THEN
+ 1004                                               ind := TRUE;
+ 1005                                               res :=
+ 1006                                                 SymTab.ProcTypeRes(pt);
+ 1007                                               QbeGen.CallBeginInd(callee,
+ 1008                                                 res, FALSE)
+ 1009                                             ELSE SemError(233);
+ 1010                                               ok := FALSE;
+ 1011                                               res := SymTab.InvalidType
+ 1012                                             END;
+ 1013                                             i := 0; .)
+ 1014        [ ActParam<pn, pt, ind, i>        (. INC(i); .)
+ 1015          { "," ActParam<pn, pt, ind, i>  (. INC(i); .) } ]
+ 1016        ")"                               (. IF ok THEN
+ 1017                                               IF ind THEN
+ 1018                                                 IF i #
+ 1019                                                    SymTab.ProcTypeNPar(pt) THEN
+ 1020                                                   SemError(233); ok := FALSE
+ 1021                                                 END
+ 1022                                               ELSIF i #
+ 1023                                                  SymTab.ProcNPar(pn) THEN
+ 1024                                                 SemError(233); ok := FALSE
+ 1025                                               END
+ 1026                                             END;
+ 1027                                             IF NOT ok THEN
+ 1028                                               t := SymTab.InvalidType;
+ 1029                                               QbeGen.CopyOp("0", q)
+ 1030                                             ELSIF want THEN
+ 1031                                               IF res =
+ 1032                                                  SymTab.InvalidType THEN
+ 1033                                                 SemError(233);
+ 1034                                                 t := SymTab.InvalidType;
+ 1035                                                 QbeGen.CopyOp("0", q)
+ 1036                                               ELSE t := res;
+ 1037                                                 QbeGen.CallEnd(TRUE, q)
+ 1038                                               END
+ 1039                                             ELSE
+ 1040                                               IF res #
+ 1041                                                  SymTab.InvalidType THEN
+ 1042                                                 SemError(233)
+ 1043                                               END;
+ 1044                                               t := SymTab.InvalidType;
+ 1045                                               QbeGen.CopyOp("0", q);
+ 1046                                               QbeGen.CallEnd(FALSE, q)
+ 1047                                             END; .) .
+ 1048    (* One actual: VAR formals take recorded designator addresses
+ 1049       (233 otherwise); value formals take converted expressions. *)
+ 1050    ActParam<pn: SymTab.Name; pt: SymTab.TypeIndex; ind: BOOLEAN;
+ 1051             i: CARDINAL>                 (. VAR at, ft: SymTab.TypeIndex;
+ 1052                                               qe, qa, qt: QbeGen.QVal;
+ 1053                                               isV, conv: BOOLEAN; .)
+ 1054      = Expr<at, qe>                      (. IF ind THEN
+ 1055                                               ft :=
+ 1056                                                 SymTab.ProcTypeParamType(pt,
+ 1057                                                   i);
+ 1058                                               isV :=
+ 1059                                                 SymTab.ProcTypeParamIsVar(pt,
+ 1060                                                   i)
+ 1061                                             ELSE
+ 1062                                               ft := SymTab.ParamType(pn, i);
+ 1063                                               isV := SymTab.ParamIsVar(pn, i)
+ 1064                                             END;
+ 1065                                             IF (at = SymTab.InvalidType)
+ 1066                                                OR (ft =
+ 1067                                                   SymTab.InvalidType) THEN
+ 1068                                             ELSIF isV THEN
+ 1069                                               IF (SymTab.ClassOf(at)
+ 1070                                                  = SymTab.ClChar)
+ 1071    AND (SymTab.ClassOf(ft) = SymTab.ClArray)
+ 1072    AND (SymTab.ClassOf(SymTab.ArrayElem(ft))
+ 1073         = SymTab.ClChar)
+ 1074    AND QbeGen.IsImm(qe) THEN
+ 1075                                                 (* 1-char string
+ 1076                                                    literal passed to
+ 1077                                                    a VAR ARRAY OF CHAR *)
+ 1078                                                 QbeGen.DeclCharStr(qe,
+ 1079                                                      qa);
+ 1080                                                 IF NOT QbeGen.CallArg(qa,
+ 1081                                                    "l") THEN
+ 1082                                                   SemError(233)
+ 1083                                                 END
+ 1084                                               ELSIF NOT QbeGen.AddrOfVal(qe,
+ 1085                                                    qa) THEN
+ 1086                                                 SemError(233)
+ 1087                                               ELSIF NOT SymTab.VarParamOk(at,
+ 1088                                                        ft) THEN
+ 1089                                                 SemError(233)
+ 1090                                               ELSIF NOT QbeGen.CallArg(qa,
+ 1091                                                        "l") THEN
+ 1092                                                 SemError(233)
+ 1093                                               END
+ 1094                                             ELSE
+ 1095                                               IF (SymTab.ClassOf(at)
+ 1096                                                  = SymTab.ClChar)
+ 1097    AND (SymTab.ClassOf(ft) = SymTab.ClArray)
+ 1098    AND (SymTab.ClassOf(SymTab.ArrayElem(ft))
+ 1099         = SymTab.ClChar)
+ 1100    AND QbeGen.IsImm(qe) THEN
+ 1101                                                 (* 1-char string
+ 1102                                                    literal passed to
+ 1103                                                    ARRAY OF CHAR *)
+ 1104                                                 QbeGen.DeclCharStr(qe,
+ 1105                                                      qa);
+ 1106                                                 IF NOT QbeGen.CallArg(qa,
+ 1107                                                    "l") THEN
+ 1108                                                   SemError(233)
+ 1109                                                 END
+ 1110                                               ELSIF NOT SymTab.Assignable(at,
+ 1111                                                    ft) THEN
+ 1112                                                 SemError(233)
+ 1113                                               ELSE
+ 1114                                                 conv := (SymTab.ClassOf(
+ 1115                                                   ft) = SymTab.ClReal)
+ 1116   AND SymTab.IsIntFamily(at);
+ 1117                                                 IF conv THEN
+ 1118                                                   QbeGen.ConvIR(qe, qt);
+ 1119                                                   IF NOT QbeGen.CallArg(qt,
+ 1120                                                      "d") THEN
+ 1121                                                     SemError(233)
+ 1122                                                   END
+ 1123                                                 ELSIF NOT QbeGen.CallArg(qe,
+ 1124                                                   QbeGen.ArgClass(ft)) THEN
+ 1125                                                   SemError(233)
+ 1126                                                 END
+ 1127                                               END
+ 1128                                             END; .) .
+ 1129    IfStat                                (. VAR t: SymTab.TypeIndex;
+ 1130                                               q, lThen, lElse, lEnd:
+ 1131                                                 QbeGen.QVal;
+ 1132                                               hasElse: BOOLEAN; .)
+ 1133      = "IF"                              (. hasElse := FALSE; .)
+ 1134        Expr<t, q>                        (. IF NOT SymTab.BoolCheck(t) THEN
+ 1135                                               SemError(214) END;
+ 1136                                             QbeGen.NewLabel(lThen);
+ 1137                                             QbeGen.NewLabel(lElse);
+ 1138                                             QbeGen.NewLabel(lEnd);
+ 1139                                             QbeGen.Jnz(q, lThen, lElse);
+ 1140                                             QbeGen.EmitLabel(lThen); .)
+ 1141        "THEN" [ StatSeq ]                (. QbeGen.Jmp(lEnd); .)
+ 1142        { "ELSIF"                         (. QbeGen.EmitLabel(lElse);
+ 1143                                             QbeGen.NewLabel(lElse); .)
+ 1144          Expr<t, q>                      (. IF NOT SymTab.BoolCheck(t) THEN
+ 1145                                               SemError(214) END;
+ 1146                                             QbeGen.NewLabel(lThen);
+ 1147                                             QbeGen.Jnz(q, lThen, lElse);
+ 1148                                             QbeGen.EmitLabel(lThen); .)
+ 1149          "THEN" [ StatSeq ]              (. QbeGen.Jmp(lEnd); .) }
+ 1150        [ "ELSE"                          (. QbeGen.EmitLabel(lElse);
+ 1151                                             hasElse := TRUE; .)
+ 1152          [ StatSeq ] ]
+ 1153        "END"                             (. IF hasElse THEN
+ 1154                                               QbeGen.EmitLabel(lEnd)
+ 1155                                             ELSE QbeGen.EmitLabel(lElse);
+ 1156                                               QbeGen.EmitLabel(lEnd)
+ 1157                                             END; .) .
+ 1158    WhileStat                             (. VAR t: SymTab.TypeIndex;
+ 1159                                               q, lTop, lBody, lEnd:
+ 1160                                                 QbeGen.QVal; .)
+ 1161      = "WHILE"                           (. QbeGen.NewLabel(lTop);
+ 1162                                             QbeGen.NewLabel(lBody);
+ 1163                                             QbeGen.NewLabel(lEnd);
+ 1164                                             QbeGen.EmitLabel(lTop); .)
+ 1165        Expr<t, q>                        (. IF NOT SymTab.BoolCheck(t) THEN
+ 1166                                               SemError(214) END;
+ 1167                                             QbeGen.Jnz(q, lBody, lEnd);
+ 1168                                             QbeGen.EmitLabel(lBody); .)
+ 1169        "DO" [ StatSeq ]                      (. QbeGen.Jmp(lTop); .)
+ 1170        "END"                             (. QbeGen.EmitLabel(lEnd); .) .
+ 1171    RepeatStat                            (. VAR t: SymTab.TypeIndex;
+ 1172                                               q, lTop, lEnd: QbeGen.QVal; .)
+ 1173      = "REPEAT"                          (. QbeGen.NewLabel(lTop);
+ 1174                                             QbeGen.NewLabel(lEnd);
+ 1175                                             QbeGen.EmitLabel(lTop); .)
+ 1176        [ StatSeq ]
+ 1177        "UNTIL" Expr<t, q>                (. IF NOT SymTab.BoolCheck(t) THEN
+ 1178                                               SemError(214) END;
+ 1179                                             QbeGen.Jnz(q, lEnd, lTop);
+ 1180                                             QbeGen.EmitLabel(lEnd); .) .
+ 1181    LoopStat                              (. VAR lTop, lEnd: QbeGen.QVal; .)
+ 1182      = "LOOP"                            (. QbeGen.NewLabel(lTop);
+ 1183                                             QbeGen.NewLabel(lEnd);
+ 1184                                             QbeGen.PushLoop(lEnd);
+ 1185                                             QbeGen.EmitLabel(lTop); .)
+ 1186        [ StatSeq ]
+ 1187        "END"                             (. QbeGen.Jmp(lTop);
+ 1188                                             QbeGen.PopLoop;
+ 1189                                             QbeGen.EmitLabel(lEnd); .) .
+ 1190    (* FOR with static-sign BY (literal, non-zero; 220 otherwise).
+ 1191       Runtime direction would need a compare-select; the literal
+ 1192       sign picks cslew/csegew at "DO" time. *)
+ 1193    ForStat                               (. VAR lv: SymTab.Name;
+ 1194                                               tlo, thi, tby:
+ 1195                                                 SymTab.TypeIndex;
+ 1196                                               qlo, qhi, qby, qt, qk, qb:
+ 1197                                                 QbeGen.QVal;
+ 1198                                               lTop, lBody, lEnd:
+ 1199                                                 QbeGen.QVal;
+ 1200                                               by: INTEGER;
+ 1201                                               ok: BOOLEAN; .)
+ 1202      = "FOR"                             (. by := 1; .)
+ 1203        GetIdent<lv>                      (. ok := SymTab.Lookup(lv);
+ 1204                                             IF NOT ok THEN
+ 1205                                               SemError(201)
+ 1206                                             ELSIF (SymTab.SymKind(lv) #
+ 1207                                                    SymTab.KindVar)
+ 1208   AND (SymTab.SymKind(lv) #
+ 1209                                                   SymTab.KindParam) THEN
+ 1210                                               SemError(220); ok := FALSE
+ 1211                                             ELSIF NOT SymTab.IsIntFamily(
+ 1212                                                     SymTab.SymType(lv)) THEN
+ 1213                                               SemError(220); ok := FALSE
+ 1214                                             END; .)
+ 1215        ":=" Expr<tlo, qlo>               (. IF NOT SymTab.IsIntFamily(tlo) THEN
+ 1216                                               SemError(220); ok := FALSE
+ 1217                                             END; .)
+ 1218        "TO" Expr<thi, qhi>               (. IF NOT SymTab.IsIntFamily(thi) THEN
+ 1219                                               SemError(220); ok := FALSE
+ 1220                                             END; .)
+ 1221        [ "BY" Expr<tby, qby>             (. IF (tby #
+ 1222                                               SymTab.InvalidType)
+ 1223   AND NOT SymTab.IsIntFamily(tby) THEN
+ 1224                                               SemError(220); ok := FALSE
+ 1225                                             END;
+ 1226                                             IF NOT SymTab.ConstInt(qby, by) THEN
+ 1227                                               SemError(230); by := 1
+ 1228                                             ELSIF by = 0 THEN
+ 1229                                               SemError(220); by := 1
+ 1230                                             END; .) ]
+ 1231        "DO"                              (. IF ok THEN
+ 1232                                               QbeGen.StoreVar(lv, qlo,
+ 1233                                                 FALSE) END;
+ 1234                                             QbeGen.NewLabel(lTop);
+ 1235                                             QbeGen.NewLabel(lBody);
+ 1236                                             QbeGen.NewLabel(lEnd);
+ 1237                                             QbeGen.EmitLabel(lTop);
+ 1238                                             QbeGen.LoadVar(lv, FALSE, qt);
+ 1239                                             QbeGen.NewTemp(qk);
+ 1240                                             IF by > 0 THEN
+ 1241                                               QbeGen.Op3("cslew", qk,
+ 1242                                                 qt, qhi, FALSE)
+ 1243                                             ELSE QbeGen.Op3("csgew", qk,
+ 1244                                               qt, qhi, FALSE)
+ 1245                                             END;
+ 1246                                             QbeGen.Jnz(qk, lBody, lEnd);
+ 1247                                             QbeGen.EmitLabel(lBody); .)
+ 1248        [ StatSeq ]
+ 1249        "END"                             (. IF ok THEN
+ 1250                                               QbeGen.LoadVar(lv, FALSE,
+ 1251                                                 qt);
+ 1252                                               QbeGen.IntStr(by, qb);
+ 1253                                               QbeGen.NewTemp(qk);
+ 1254                                               QbeGen.Op3("add", qk,
+ 1255                                                 qt, qb, FALSE);
+ 1256                                               QbeGen.StoreVar(lv, qk,
+ 1257                                                 FALSE) END;
+ 1258                                             QbeGen.Jmp(lTop);
+ 1259                                             QbeGen.EmitLabel(lEnd); .) .
+ 1260    CaseStat                              (. VAR tsel: SymTab.TypeIndex;
+ 1261                                               qsel, lEnd: QbeGen.QVal; .)
+ 1262      = "CASE" Expr<tsel, qsel>           (. QbeGen.NewLabel(lEnd); .)
+ 1263        "OF" CaseAlt<tsel, qsel, lEnd>
+ 1264        { "|" CaseAlt<tsel, qsel, lEnd> }
+ 1265        [ "ELSE" [ StatSeq ] ]
+ 1266        "END"                             (. QbeGen.EmitLabel(lEnd); .) .
+ 1267    (* Compare-chain lowering: each alternative ends its match-tests
+ 1268       with "jmp lAfter", so the no-match fallthrough skips the body:
+ 1269       "cmp; jnz(lBody,lF); lF: ... ; jmp lAfter; lBody: S; jmp lEnd;
+ 1270       lAfter:". Falls into the next alternative, ELSE, or END. *)
+ 1271    CaseAlt<tsel: SymTab.TypeIndex; qsel: QbeGen.QVal;
+ 1272            lEnd: QbeGen.QVal>            (. VAR lBody, lAfter: QbeGen.QVal; .)
+ 1273      =                                   (. QbeGen.NewLabel(lBody);
+ 1274                                             QbeGen.NewLabel(lAfter); .)
+ 1275        CaseLabel<tsel, qsel, lBody>
+ 1276        { "," CaseLabel<tsel, qsel, lBody> }
+ 1277        ":"                               (. QbeGen.Jmp(lAfter);
+ 1278                                             QbeGen.EmitLabel(lBody); .)
+ 1279        [ StatSeq ]                       (. QbeGen.Jmp(lEnd);
+ 1280                                             QbeGen.EmitLabel(lAfter); .) .
+ 1281    CaseLabel<tsel: SymTab.TypeIndex; qsel: QbeGen.QVal;
+ 1282              lBody: QbeGen.QVal>         (. VAR t2, t3: SymTab.TypeIndex;
+ 1283                                               q2, q3, qc, qd, qe:
+ 1284                                                 QbeGen.QVal;
+ 1285                                               lNext: QbeGen.QVal; .)
+ 1286      = Expr<t2, q2>                      (. IF (t2 #
+ 1287                                               SymTab.InvalidType)
+ 1288   AND (tsel #
+ 1289                                                 SymTab.InvalidType)
+ 1290   AND ((SymTab.ClassOf(t2) =
+ 1291                                                  SymTab.ClSet)
+ 1292                                                 OR (SymTab.ClassOf(tsel) =
+ 1293                                                     SymTab.ClSet)) THEN
+ 1294                                               SemError(230)
+ 1295                                             ELSIF (t2 #
+ 1296                                               SymTab.InvalidType)
+ 1297   AND (tsel #
+ 1298                                                 SymTab.InvalidType)
+ 1299   AND NOT SymTab.EqCheck(t2,
+ 1300                                                   tsel) THEN
+ 1301                                               SemError(213) END;
+ 1302                                             IF NOT QbeGen.IsImm(q2) THEN
+ 1303                                               SemError(230);
+ 1304                                               QbeGen.CopyOp("0", q2)
+ 1305                                             END;
+ 1306                                             QbeGen.NewLabel(lNext);
+ 1307                                             QbeGen.Cmp(SymTab.OpEq,
+ 1308                                               qsel, q2, qc, FALSE);
+ 1309                                             QbeGen.Jnz(qc, lBody, lNext);
+ 1310                                             QbeGen.EmitLabel(lNext); .)
+ 1311        [ ".." Expr<t3, q3>               (. IF (t3 #
+ 1312                                               SymTab.InvalidType)
+ 1313   AND (tsel #
+ 1314                                                 SymTab.InvalidType)
+ 1315   AND NOT SymTab.EqCheck(t3,
+ 1316                                                   tsel) THEN
+ 1317                                               SemError(213) END;
+ 1318                                             IF NOT QbeGen.IsImm(q3) THEN
+ 1319                                               SemError(230);
+ 1320                                               QbeGen.CopyOp("0", q3)
+ 1321                                             END;
+ 1322                                             QbeGen.Cmp(SymTab.OpGe,
+ 1323                                               qsel, q2, qc, FALSE);
+ 1324                                             QbeGen.Cmp(SymTab.OpLe,
+ 1325                                               qsel, q3, qd, FALSE);
+ 1326                                             QbeGen.NewTemp(qe);
+ 1327                                             QbeGen.Op3("and", qe, qc, qd,
+ 1328                                               FALSE);
+ 1329                                             QbeGen.NewLabel(lNext);
+ 1330                                             QbeGen.Jnz(qe, lBody, lNext);
+ 1331                                             QbeGen.EmitLabel(lNext); .) ] .
+ 1332    ReturnStat                            (. VAR t: SymTab.TypeIndex;
+ 1333                                               q, qt: QbeGen.QVal;
+ 1334                                               res: SymTab.TypeIndex;
+ 1335                                               hadE, conv: BOOLEAN; .)
+ 1336      = "RETURN"                          (. hadE := FALSE; .)
+ 1337        [ Expr<t, q>                      (. hadE := TRUE; .) ]
+ 1338                                          (. conv := FALSE;
+ 1339                                             IF NOT SymTab.InProc() THEN
+ 1340                                               SemError(232)
+ 1341                                             ELSE res := SymTab.CurRes();
+ 1342                                               IF NOT hadE THEN
+ 1343                                                 IF res #
+ 1344                                                    SymTab.InvalidType THEN
+ 1345                                                   SemError(232)
+ 1346                                                 ELSE QbeGen.EmitRet(q,
+ 1347                                                   FALSE)
+ 1348                                                 END
+ 1349                                               ELSIF (res =
+ 1350                                                      SymTab.InvalidType)
+ 1351                                                  OR (t #
+ 1352                                                      SymTab.InvalidType)
+ 1353   AND NOT SymTab.Assignable(t,
+ 1354                                                       res) THEN
+ 1355                                                 SemError(232)
+ 1356                                               ELSE
+ 1357                                                 conv := (SymTab.ClassOf(
+ 1358                                                   res) = SymTab.ClReal)
+ 1359   AND SymTab.IsIntFamily(t);
+ 1360                                                 IF conv THEN
+ 1361                                                   QbeGen.ConvIR(q, qt);
+ 1362                                                   QbeGen.EmitRet(qt, TRUE)
+ 1363                                                 ELSE QbeGen.EmitRet(q, TRUE)
+ 1364                                                 END
+ 1365                                               END
+ 1366                                             END; .) .
+ 1367    HaltStat                              (. VAR t: SymTab.TypeIndex;
+ 1368                                               q: QbeGen.QVal; .)
+ 1369      = "HALT" [ "(" Expr<t, q> ")" ]     (. QbeGen.HaltQ; .) .
+ 1370    (* Designator: scalar loads, array addresses, and index suffixes.
+ 1371       Each index descends one level (bounds-checked, trap on breach);
+ 1372       nested levels reload the inner descriptor address. q ends as the
+ 1373       value (scalars), the descriptor address (plain arrays), or the
+ 1374       element address (indexed); sfx marks the indexed form. *)
+ 1375    Design<VAR t: SymTab.TypeIndex; VAR k: INTEGER;
+ 1376           VAR q: QbeGen.QVal; VAR qn: SymTab.Name; VAR sfx: BOOLEAN>
+ 1377                                          (. VAR n, fn, mal: SymTab.Name;
+ 1378                                               cls: INTEGER;
+ 1379                                               curT, it, eT, bt:
+ 1380                                                 SymTab.TypeIndex;
+ 1381                                               iq, ql, qlo, qhi, qe:
+ 1382                                                 QbeGen.QVal;
+ 1383                                               lo, hi: INTEGER;
+ 1384                                               fo: INTEGER;
+ 1385                                               isOpen: BOOLEAN;
+ 1386                                               qb, cv: QbeGen.QVal; .)
+ 1387      = GetIdent<n>                       (. QbeGen.CopyOp(n, qn);
+ 1388                                             sfx := FALSE;
+ 1389                                             IF NOT SymTab.Lookup(n) THEN
+ 1390                                               SemError(201);
+ 1391                                               t := SymTab.InvalidType;
+ 1392                                               k := -1;
+ 1393                                               QbeGen.CopyOp("0", q)
+ 1394                                             ELSE
+ 1395                                               t := SymTab.SymType(n);
+ 1396                                               k := SymTab.SymKind(n);
+ 1397                                               IF k = SymTab.KindConst THEN
+ 1398                                                 IF SymTab.Equal(n,
+ 1399                                                    "TRUE") THEN
  1400                                                   t := SymTab.BoolType();
- 1401                                                   QbeGen.CopyOp("0", q)
+ 1401                                                   QbeGen.CopyOp("1", q)
  1402                                                 ELSIF SymTab.Equal(n,
- 1403                                                    "NIL") THEN
- 1404                                                   QbeGen.CopyOp("0", q)
- 1405                                                 ELSE
- 1406                                                   cls :=
- 1407                                                     SymTab.ClassOf(t);
- 1408                                                   IF (t #
- 1409                                                       SymTab.InvalidType)
- 1410   AND ((cls = SymTab.ClInt)
- 1411                                                      OR (cls
- 1412                                                          = SymTab.ClChar)
- 1413                                                      OR (cls
- 1414                                                          = SymTab.ClReal)
+ 1403                                                    "FALSE") THEN
+ 1404                                                   t := SymTab.BoolType();
+ 1405                                                   QbeGen.CopyOp("0", q)
+ 1406                                                 ELSIF SymTab.Equal(n,
+ 1407                                                    "NIL") THEN
+ 1408                                                   QbeGen.CopyOp("0", q)
+ 1409                                                 ELSE
+ 1410                                                   cls :=
+ 1411                                                     SymTab.ClassOf(t);
+ 1412                                                   IF (t #
+ 1413                                                       SymTab.InvalidType)
+ 1414   AND ((cls = SymTab.ClInt)
  1415                                                      OR (cls
- 1416                                                          = SymTab.ClNil)) THEN
- 1417                                                     IF cls = SymTab.ClNil THEN
- 1418                                                       QbeGen.CopyOp("0", q)
- 1419                                                     ELSIF ((cls
- 1420                                                         = SymTab.ClInt)
- 1421                                                       OR (cls
- 1422                                                         = SymTab.ClChar))
- 1423    AND SymTab.GetSymVal(n, cv)
- 1424    AND QbeGen.IsImm(cv) THEN
- 1425                                                       QbeGen.CopyOp(cv, q)
- 1426                                                     ELSE
- 1427                                                       QbeGen.LoadVar(n,
- 1428                                                         cls = SymTab.ClReal,
- 1429                                                         q)
- 1430                                                     END
- 1431                                                   ELSE
- 1432                                                     IF t #
- 1433                                                        SymTab.InvalidType THEN
- 1434                                                       SemError(230)
- 1435                                                     END;
- 1436                                                     QbeGen.CopyOp("0", q)
- 1437                                                   END
- 1438                                                 END
- 1439                                               ELSIF (k = SymTab.KindVar)
- 1440                                                  OR (k = SymTab.KindParam) THEN
- 1441                                                 cls :=
- 1442                                                   SymTab.ClassOf(t);
- 1443                                                 IF (cls = SymTab.ClInt)
- 1444                                                    OR (cls = SymTab.ClBool)
- 1445                                                    OR (cls = SymTab.ClChar)
- 1446                                                    OR (cls
- 1447                                                        = SymTab.ClReal) THEN
- 1448                                                   QbeGen.LoadVar(n,
- 1449                                                     cls = SymTab.ClReal, q)
- 1450                                                 ELSIF (cls = SymTab.ClPtr)
- 1451                                                    OR (cls = SymTab.ClProc) THEN
- 1452                                                   QbeGen.LoadPtr(n, q)
- 1453                                                 ELSIF cls = SymTab.ClLong THEN
- 1454                                                   QbeGen.LoadLong(n, q)
- 1455                                                 ELSIF (cls
- 1456                                                         = SymTab.ClArray)
- 1457                                                    OR (cls
- 1458                                                        = SymTab.ClSet)
- 1459                                                    OR (cls
- 1460                                                        = SymTab.ClRecord)
- 1461                                                    OR (cls
- 1462                                                        = SymTab.ClClass) THEN
- 1463                                                   QbeGen.AddrOf(n, q)
- 1464                                                 ELSE SemError(230);
- 1465                                                   QbeGen.CopyOp("0", q)
- 1466                                                 END
- 1467                                               ELSE QbeGen.CopyOp("0", q);
- 1468                                                 IF k = SymTab.KindImport THEN
- 1469                                                   SemError(230)
- 1470                                                 ELSIF k =
- 1471                                                    SymTab.KindProc THEN
- 1472                                                   (* bare procedure name:
- 1473                                                      a following ArgList
- 1474                                                      makes it a call;
- 1475                                                      otherwise Fact
- 1476                                                      reports 230 *)
- 1477                                                 ELSE
- 1478                                                   IF k = SymTab.KindField THEN
- 1479                                                     IF QbeGen.TopWith(qb) THEN
- 1480                                                       fo :=
- 1481                                                         SymTab.FieldOffset(
- 1482                                                         SymTab.FieldOwner(n),
- 1483                                                         n);
- 1484                                                       QbeGen.FieldAddr(qb,
- 1485                                                         fo, q);
- 1486                                                       sfx := TRUE
- 1487                                                     ELSE SemError(230);
- 1488                                                       QbeGen.CopyOp("0", q)
- 1489                                                     END
- 1490                                                   END
- 1491                                                 END
- 1492                                               END
- 1493                                             END; .)
- 1494        { "[" Expr<it, iq>
- 1495                                          (. IF t = SymTab.InvalidType THEN
- 1496                                             ELSIF SymTab.ClassOf(t) #
- 1497                                                   SymTab.ClArray THEN
- 1498                                               SemError(217);
- 1499                                               t := SymTab.InvalidType
- 1500                                             ELSIF NOT SymTab.IsIntFamily(it)
- 1501   AND (SymTab.ClassOf(it) #
- 1502                                                   SymTab.ClChar) THEN
- 1503                                               SemError(218);
- 1504                                               t := SymTab.InvalidType
- 1505                                             ELSE
- 1506                                               QbeGen.WidenIndex(iq, ql);
- 1507                                               isOpen :=
- 1508                                                 SymTab.IsOpenArray(t);
- 1509                                               IF isOpen THEN
- 1510                                                 QbeGen.CopyOp("0", qlo);
- 1511                                                 IF SymTab.IsCharArray(t) THEN
- 1512                                                   QbeGen.OpenHiChar(q, qhi)
- 1513                                                 ELSE QbeGen.OpenHi(q, qhi)
- 1514                                                 END
- 1515                                               ELSE
- 1516                                                 lo := SymTab.ArrayLo(t);
- 1517                                                 hi := SymTab.ArrayHi(t);
+ 1416                                                          = SymTab.ClChar)
+ 1417                                                      OR (cls
+ 1418                                                          = SymTab.ClReal)
+ 1419                                                      OR (cls
+ 1420                                                          = SymTab.ClNil)) THEN
+ 1421                                                     IF cls = SymTab.ClNil THEN
+ 1422                                                       QbeGen.CopyOp("0", q)
+ 1423                                                     ELSIF ((cls
+ 1424                                                         = SymTab.ClInt)
+ 1425                                                       OR (cls
+ 1426                                                         = SymTab.ClChar))
+ 1427    AND SymTab.GetSymVal(n, cv)
+ 1428    AND QbeGen.IsImm(cv) THEN
+ 1429                                                       QbeGen.CopyOp(cv, q)
+ 1430                                                     ELSE
+ 1431                                                       QbeGen.LoadVar(n,
+ 1432                                                         cls = SymTab.ClReal,
+ 1433                                                         q)
+ 1434                                                     END
+ 1435                                                   ELSE
+ 1436                                                     IF t #
+ 1437                                                        SymTab.InvalidType THEN
+ 1438                                                       SemError(230)
+ 1439                                                     END;
+ 1440                                                     QbeGen.CopyOp("0", q)
+ 1441                                                   END
+ 1442                                                 END
+ 1443                                               ELSIF (k = SymTab.KindVar)
+ 1444                                                  OR (k = SymTab.KindParam) THEN
+ 1445                                                 cls :=
+ 1446                                                   SymTab.ClassOf(t);
+ 1447                                                 IF (cls = SymTab.ClInt)
+ 1448                                                    OR (cls = SymTab.ClBool)
+ 1449                                                    OR (cls = SymTab.ClChar)
+ 1450                                                    OR (cls = SymTab.ClUChar)
+ 1451                                                    OR (cls
+ 1452                                                        = SymTab.ClReal) THEN
+ 1453                                                   QbeGen.LoadVar(n,
+ 1454                                                     cls = SymTab.ClReal, q)
+ 1455                                                 ELSIF (cls = SymTab.ClPtr)
+ 1456                                                    OR (cls = SymTab.ClProc) THEN
+ 1457                                                   QbeGen.LoadPtr(n, q)
+ 1458                                                 ELSIF cls = SymTab.ClLong THEN
+ 1459                                                   QbeGen.LoadLong(n, q)
+ 1460                                                 ELSIF (cls
+ 1461                                                         = SymTab.ClArray)
+ 1462                                                    OR (cls
+ 1463                                                        = SymTab.ClSet)
+ 1464                                                    OR (cls
+ 1465                                                        = SymTab.ClRecord)
+ 1466                                                    OR (cls
+ 1467                                                        = SymTab.ClUStr)
+ 1468                                                    OR (cls
+ 1469                                                        = SymTab.ClClass) THEN
+ 1470                                                   QbeGen.AddrOf(n, q)
+ 1471                                                 ELSE SemError(230);
+ 1472                                                   QbeGen.CopyOp("0", q)
+ 1473                                                 END
+ 1474                                               ELSE QbeGen.CopyOp("0", q);
+ 1475                                                 IF k = SymTab.KindImport THEN
+ 1476                                                   SemError(230)
+ 1477                                                 ELSIF k =
+ 1478                                                    SymTab.KindProc THEN
+ 1479                                                   (* bare procedure name:
+ 1480                                                      a following ArgList
+ 1481                                                      makes it a call;
+ 1482                                                      otherwise Fact
+ 1483                                                      reports 230 *)
+ 1484                                                 ELSE
+ 1485                                                   IF k = SymTab.KindField THEN
+ 1486                                                     IF QbeGen.TopWith(qb) THEN
+ 1487                                                       fo :=
+ 1488                                                         SymTab.FieldOffset(
+ 1489                                                         SymTab.FieldOwner(n),
+ 1490                                                         n);
+ 1491                                                       QbeGen.FieldAddr(qb,
+ 1492                                                         fo, q);
+ 1493                                                       sfx := TRUE
+ 1494                                                     ELSE SemError(230);
+ 1495                                                       QbeGen.CopyOp("0", q)
+ 1496                                                     END
+ 1497                                                   END
+ 1498                                                 END
+ 1499                                               END
+ 1500                                             END; .)
+ 1501        { "[" Expr<it, iq>
+ 1502                                          (. IF t = SymTab.InvalidType THEN
+ 1503                                             ELSIF SymTab.ClassOf(t) #
+ 1504                                                   SymTab.ClArray THEN
+ 1505                                               SemError(217);
+ 1506                                               t := SymTab.InvalidType
+ 1507                                             ELSIF NOT SymTab.IsIntFamily(it)
+ 1508   AND (SymTab.ClassOf(it) #
+ 1509                                                   SymTab.ClChar) THEN
+ 1510                                               SemError(218);
+ 1511                                               t := SymTab.InvalidType
+ 1512                                             ELSE
+ 1513                                               QbeGen.WidenIndex(iq, ql);
+ 1514                                               isOpen :=
+ 1515                                                 SymTab.IsOpenArray(t);
+ 1516                                               IF isOpen THEN
+ 1517                                                 QbeGen.CopyOp("0", qlo);
  1518                                                 IF SymTab.IsCharArray(t) THEN
- 1519                                                   hi := hi + 1
- 1520                                                 END;
- 1521                                                 QbeGen.IntStr(lo, qlo);
- 1522                                                 QbeGen.IntStr(hi, qhi)
- 1523                                               END;
- 1524                                               QbeGen.CheckRange(ql, qlo,
- 1525                                                 qhi);
- 1526                                               eT := SymTab.ArrayElem(t);
- 1527                                               QbeGen.ElemAddr(q, ql, qlo,
- 1528                                                 t, qe);
- 1529                                               IF SymTab.ClassOf(eT) =
- 1530                                                  SymTab.ClArray THEN
- 1531                                                 QbeGen.ElemLoad(qe, eT, q)
- 1532                                               ELSE QbeGen.CopyOp(qe, q)
- 1533                                               END;
- 1534                                               t := eT; sfx := TRUE
- 1535                                             END; .)
- 1536          { "," Expr<it, iq>
- 1537                                          (. IF t = SymTab.InvalidType THEN
- 1538                                             ELSIF SymTab.ClassOf(t) #
- 1539                                                   SymTab.ClArray THEN
- 1540                                               SemError(217);
- 1541                                               t := SymTab.InvalidType
- 1542                                             ELSIF NOT SymTab.IsIntFamily(it)
- 1543   AND (SymTab.ClassOf(it) #
- 1544                                                   SymTab.ClChar) THEN
- 1545                                               SemError(218);
- 1546                                               t := SymTab.InvalidType
- 1547                                             ELSE
- 1548                                               QbeGen.WidenIndex(iq, ql);
- 1549                                               isOpen :=
- 1550                                                 SymTab.IsOpenArray(t);
- 1551                                               IF isOpen THEN
- 1552                                                 QbeGen.CopyOp("0", qlo);
- 1553                                                 IF SymTab.IsCharArray(t) THEN
- 1554                                                   QbeGen.OpenHiChar(q, qhi)
- 1555                                                 ELSE QbeGen.OpenHi(q, qhi)
- 1556                                                 END
- 1557                                               ELSE
- 1558                                                 lo := SymTab.ArrayLo(t);
- 1559                                                 hi := SymTab.ArrayHi(t);
+ 1519                                                   QbeGen.OpenHiChar(q, qhi)
+ 1520                                                 ELSE QbeGen.OpenHi(q, qhi)
+ 1521                                                 END
+ 1522                                               ELSE
+ 1523                                                 lo := SymTab.ArrayLo(t);
+ 1524                                                 hi := SymTab.ArrayHi(t);
+ 1525                                                 IF SymTab.IsCharArray(t) THEN
+ 1526                                                   hi := hi + 1
+ 1527                                                 END;
+ 1528                                                 QbeGen.IntStr(lo, qlo);
+ 1529                                                 QbeGen.IntStr(hi, qhi)
+ 1530                                               END;
+ 1531                                               QbeGen.CheckRange(ql, qlo,
+ 1532                                                 qhi);
+ 1533                                               eT := SymTab.ArrayElem(t);
+ 1534                                               QbeGen.ElemAddr(q, ql, qlo,
+ 1535                                                 t, qe);
+ 1536                                               IF SymTab.ClassOf(eT) =
+ 1537                                                  SymTab.ClArray THEN
+ 1538                                                 QbeGen.ElemLoad(qe, eT, q)
+ 1539                                               ELSE QbeGen.CopyOp(qe, q)
+ 1540                                               END;
+ 1541                                               t := eT; sfx := TRUE
+ 1542                                             END; .)
+ 1543          { "," Expr<it, iq>
+ 1544                                          (. IF t = SymTab.InvalidType THEN
+ 1545                                             ELSIF SymTab.ClassOf(t) #
+ 1546                                                   SymTab.ClArray THEN
+ 1547                                               SemError(217);
+ 1548                                               t := SymTab.InvalidType
+ 1549                                             ELSIF NOT SymTab.IsIntFamily(it)
+ 1550   AND (SymTab.ClassOf(it) #
+ 1551                                                   SymTab.ClChar) THEN
+ 1552                                               SemError(218);
+ 1553                                               t := SymTab.InvalidType
+ 1554                                             ELSE
+ 1555                                               QbeGen.WidenIndex(iq, ql);
+ 1556                                               isOpen :=
+ 1557                                                 SymTab.IsOpenArray(t);
+ 1558                                               IF isOpen THEN
+ 1559                                                 QbeGen.CopyOp("0", qlo);
  1560                                                 IF SymTab.IsCharArray(t) THEN
- 1561                                                   hi := hi + 1
- 1562                                                 END;
- 1563                                                 QbeGen.IntStr(lo, qlo);
- 1564                                                 QbeGen.IntStr(hi, qhi)
- 1565                                               END;
- 1566                                               QbeGen.CheckRange(ql, qlo,
- 1567                                                 qhi);
- 1568                                               eT := SymTab.ArrayElem(t);
- 1569                                               QbeGen.ElemAddr(q, ql, qlo,
- 1570                                                 t, qe);
- 1571                                               IF SymTab.ClassOf(eT) =
- 1572                                                  SymTab.ClArray THEN
- 1573                                                 QbeGen.ElemLoad(qe, eT, q)
- 1574                                               ELSE QbeGen.CopyOp(qe, q)
- 1575                                               END;
- 1576                                               t := eT; sfx := TRUE
- 1577                                             END; .) }
- 1578          "]"
- 1579        | "." GetIdent<fn>
- 1580                                          (. IF k = SymTab.KindModule THEN
- 1581                                               (* qualified L.x: materialize
- 1582                                                  the export, then load it *)
- 1583                                               IF NOT SymTab.MaterializeAlias(n,
- 1584                                                    fn, mal) THEN
- 1585                                                 SemError(201);
- 1586                                                 t := SymTab.InvalidType;
- 1587                                                 QbeGen.CopyOp("0", q)
- 1588                                               ELSE
- 1589                                                 QbeGen.CopyOp(mal, qn);
- 1590                                                 t := SymTab.SymType(mal);
- 1591                                                 k := SymTab.SymKind(mal);
- 1592                                                 sfx := FALSE;
- 1593                                                 IF k = SymTab.KindProc THEN
- 1594                                                   (* call: ArgList supplies
- 1595                                                      the value *)
- 1596                                                   QbeGen.CopyOp("0", q)
- 1597                                                 ELSIF NOT QbeGen.LoadDesignator(
- 1598                                                      mal, t, k, q) THEN
- 1599                                                   SemError(230);
- 1600                                                   QbeGen.CopyOp("0", q)
- 1601                                                 END
- 1602                                               END
- 1603                                             ELSIF t = SymTab.InvalidType THEN
- 1604                                             ELSIF (SymTab.ClassOf(t) #
- 1605                                                    SymTab.ClRecord)
- 1606   AND (SymTab.ClassOf(t) #
- 1607                                                   SymTab.ClClass) THEN
- 1608                                               SemError(215);
- 1609                                               t := SymTab.InvalidType
- 1610                                             ELSIF NOT SymTab.FieldExists(t,
- 1611                                                      fn) THEN
- 1612                                               SemError(216);
- 1613                                               t := SymTab.InvalidType
- 1614                                             ELSE
- 1615                                               fo := SymTab.FieldOffset(t,
- 1616                                                 fn);
- 1617                                               t := SymTab.FieldType(t, fn);
- 1618                                               QbeGen.FieldAddr(q, fo, qe);
- 1619                                               (* array fields are inline:
- 1620                                                  the field address is the
- 1621                                                  descriptor, like records *)
- 1622                                               QbeGen.CopyOp(qe, q);
- 1623                                               sfx := TRUE
- 1624                                             END; .)
- 1625        | "^"
- 1626                                          (. IF t = SymTab.InvalidType THEN
- 1627                                             ELSIF SymTab.ClassOf(t) #
- 1628                                                   SymTab.ClPtr THEN
- 1629                                               SemError(219);
- 1630                                               t := SymTab.InvalidType
- 1631                                             ELSE
- 1632                                               bt := SymTab.PtrBase(t);
- 1633                                               IF bt = SymTab.InvalidType THEN
- 1634                                               ELSE
- 1635                                                 IF sfx THEN
- 1636                                                   QbeGen.ElemLoad(q, t,
- 1637                                                     qb);
- 1638                                                   QbeGen.CopyOp(qb, q)
- 1639                                                 END;
- 1640                                                 t := bt;
- 1641                                                 (* q holds the pointee
- 1642                                                    address: Fact loads
- 1643                                                    scalars/pointers and uses
- 1644                                                    the address for
- 1645                                                    aggregates; the VAR-actual
- 1646                                                    note is q itself. *)
- 1647                                                 sfx := TRUE
- 1648                                               END
- 1649                                             END; .) } .
- 1650    Expr<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 1651                                          (. VAR t2: SymTab.TypeIndex;
- 1652                                               op: INTEGER;
- 1653                                               q2, qt, wl: QbeGen.QVal;
- 1654                                               isR: BOOLEAN; .)
- 1655      = SimExpr<t, q>
- 1656        [ Rel<op> SimExpr<t2, q2>
- 1657          (. IF op = SymTab.OpIn THEN
- 1658               IF SymTab.InCheck(t, t2) THEN
- 1659                 IF (t = SymTab.InvalidType)
- 1660                    OR (t2 = SymTab.InvalidType) THEN
- 1661                   t := SymTab.BoolType(); QbeGen.CopyOp("0", q)
- 1662                 ELSE
- 1663                   QbeGen.InSet(q, q2, SymTab.SetBaseLo(t2),
- 1664                     SymTab.SetCount(t2), qt);
- 1665                   t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
- 1666                 END
- 1667               ELSE SemError(222); t := SymTab.InvalidType;
- 1668                 QbeGen.CopyOp("0", q)
- 1669               END
- 1670             ELSIF SymTab.RelCheck(t, t2, op) THEN
- 1671               IF (t = SymTab.InvalidType)
- 1672                  OR (t2 = SymTab.InvalidType) THEN
- 1673                 t := SymTab.BoolType(); QbeGen.CopyOp("0", q)
- 1674               ELSIF (SymTab.ClassOf(t) = SymTab.ClPtr)
- 1675                  OR (SymTab.ClassOf(t2) = SymTab.ClPtr) THEN
- 1676                 IF (op # SymTab.OpEq) AND (op # SymTab.OpNeq1)
- 1677   AND (op # SymTab.OpNeq2) THEN
- 1678                   SemError(213); t := SymTab.InvalidType;
- 1679                   QbeGen.CopyOp("0", q)
- 1680                 ELSE
- 1681                   QbeGen.CmpL(op, q, q2, qt);
- 1682                   t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
- 1683                 END
- 1684               ELSIF (SymTab.ClassOf(t) = SymTab.ClSet)
- 1685                  OR (SymTab.ClassOf(t2) = SymTab.ClSet) THEN
- 1686                 QbeGen.CmpSet(op, q, q2,
- 1687                   SymTab.SetWords(t), SymTab.SetWords(t2), qt);
- 1688                 t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
- 1689               ELSIF SymTab.IsLongFamily(t)
- 1690                  OR SymTab.IsLongFamily(t2) THEN
- 1691                 IF SymTab.IsIntFamily(t) THEN
- 1692                   QbeGen.WidenLong(q, wl); QbeGen.CopyOp(wl, q)
- 1693                 END;
- 1694                 IF SymTab.IsIntFamily(t2) THEN
- 1695                   QbeGen.WidenLong(q2, wl); QbeGen.CopyOp(wl, q2)
- 1696                 END;
- 1697                 QbeGen.CmpLong(op, q, q2, qt);
- 1698                 t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
- 1699               ELSE
- 1700                 isR := SymTab.ClassOf(t) = SymTab.ClReal;
- 1701                 t := SymTab.BoolType();
- 1702                 QbeGen.Cmp(op, q, q2, qt, isR);
- 1703                 QbeGen.CopyOp(qt, q)
- 1704               END
- 1705             ELSE SemError(213); t := SymTab.InvalidType;
- 1706               QbeGen.CopyOp("0", q)
- 1707             END; .) ] .
- 1708    Rel<VAR op: INTEGER>
- 1709      = "="                               (. op := SymTab.OpEq; .)
- 1710      | "#"                               (. op := SymTab.OpNeq1; .)
- 1711      | "<"                               (. op := SymTab.OpLt; .)
- 1712      | "<="                              (. op := SymTab.OpLe; .)
- 1713      | ">"                               (. op := SymTab.OpGt; .)
- 1714      | ">="                              (. op := SymTab.OpGe; .)
- 1715      | "IN"                              (. op := SymTab.OpIn; .) .
- 1716    SimExpr<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 1717                                          (. VAR t2, res2, lt, rt:
- 1718                                                 SymTab.TypeIndex;
- 1719                                               op: INTEGER;
- 1720                                               q2, qt, wq, qf:
- 1721                                                 QbeGen.QVal;
- 1722                                               neg, isR, isL, folded:
- 1723                                                 BOOLEAN;
- 1724                                               lw, rw, mw: CARDINAL;
- 1725                                               lTrue, lNext, lDone, qr, qs:
- 1726                                                 QbeGen.QVal; .)
- 1727      =                                   (. neg := FALSE; .)
- 1728        [ "+" | "-"                       (. neg := TRUE; .) ]
- 1729        Term<t, q>                        (. IF neg THEN
- 1730                                             IF QbeGen.IsImm(q) THEN
- 1731                                               QbeGen.NegFold(q, q)
- 1732                                             ELSE QbeGen.NewTemp(qt);
- 1733                                               QbeGen.NegQ(q, qt,
- 1734                                                 SymTab.ClassOf(t)
- 1735                                                 = SymTab.ClReal);
- 1736                                               QbeGen.CopyOp(qt, q)
- 1737                                             END
- 1738                                           END; .)
- 1739        { AddOp<op>                       (. IF op = SymTab.OpOr THEN
- 1740                                               QbeGen.DelayBegin END; .)
- 1741          Term<t2, q2>                     (. IF op = SymTab.OpOr THEN
- 1742                                               QbeGen.DelayEnd END; .)
- 1743          (. IF op = SymTab.OpOr THEN
- 1744               (* short-circuit: if q is true the RHS is skipped *)
- 1745               IF SymTab.BoolCheck(t) AND SymTab.BoolCheck(t2) THEN
- 1746                 t := SymTab.BoolType()
- 1747               ELSE SemError(212); t := SymTab.InvalidType END;
- 1748               IF t # SymTab.InvalidType THEN
- 1749                 QbeGen.Slot4(qs);
- 1750                 QbeGen.NewLabel(lTrue);
- 1751                 QbeGen.NewLabel(lNext);
- 1752                 QbeGen.NewLabel(lDone);
- 1753                 QbeGen.Jnz(q, lTrue, lNext);
- 1754                 QbeGen.EmitLabel(lTrue);
- 1755                 QbeGen.StoreW(qs, "1");
- 1756                 QbeGen.Jmp(lDone);
- 1757                 QbeGen.EmitLabel(lNext);
- 1758                 QbeGen.DelayFlush;
- 1759                 QbeGen.StoreW(qs, q2);
- 1760                 QbeGen.Jmp(lDone);
- 1761                 QbeGen.EmitLabel(lDone);
- 1762                 QbeGen.LoadW(qs, qr);
- 1763                 QbeGen.CopyOp(qr, q)
- 1764               ELSE QbeGen.DelayFlush; QbeGen.CopyOp("0", q)
- 1765               END
- 1766             ELSIF (t # SymTab.InvalidType) AND (t2 # SymTab.InvalidType)
- 1767   AND (SymTab.ClassOf(t) = SymTab.ClSet)
- 1768   AND (SymTab.ClassOf(t2) = SymTab.ClSet) THEN
- 1769               lw := SymTab.SetWords(t); rw := SymTab.SetWords(t2);
- 1770               mw := lw;
- 1771               IF rw > mw THEN mw := rw END;
- 1772               IF op = SymTab.OpAdd THEN
- 1773                 QbeGen.SetBinOp(0, q, q2, lw, rw, qt)
- 1774               ELSE
- 1775                 QbeGen.SetBinOp(2, q, q2, lw, rw, qt)
- 1776               END;
- 1777               t := SymTab.NewSet(
- 1778                      SymTab.NewSubR(0,
- 1779                        VAL(INTEGER, mw) * 32 - 1));
- 1780               QbeGen.CopyOp(qt, q)
- 1781             ELSE
- 1782               IF SymTab.ArithCheck(t, t2, FALSE, res2) THEN
- 1783                 lt := t; rt := t2; t := res2
- 1784               ELSE SemError(211); t := SymTab.InvalidType END;
- 1785               IF t # SymTab.InvalidType THEN
- 1786                 isL := SymTab.IsLongFamily(t);
- 1787                 isR := SymTab.ClassOf(t) = SymTab.ClReal;
- 1788                 folded := FALSE;
- 1789                 IF (NOT isL) AND (NOT isR)
- 1790    AND QbeGen.IsImm(q) AND QbeGen.IsImm(q2) THEN
- 1791                   IF op = SymTab.OpAdd THEN
- 1792                     folded := QbeGen.Fold2(0, q, q2, qf)
- 1793                   ELSE
- 1794                     folded := QbeGen.Fold2(1, q, q2, qf)
- 1795                   END
- 1796                 END;
- 1797                 IF folded THEN QbeGen.CopyOp(qf, q)
- 1798                 ELSE
- 1799                 IF isL THEN
- 1800                   IF SymTab.IsIntFamily(lt) THEN
- 1801                     QbeGen.WidenLong(q, wq); QbeGen.CopyOp(wq, q)
- 1802                   END;
- 1803                   IF SymTab.IsIntFamily(rt) THEN
- 1804                     QbeGen.WidenLong(q2, wq); QbeGen.CopyOp(wq, q2)
- 1805                   END;
- 1806                   QbeGen.NewTemp(qt);
- 1807                   IF op = SymTab.OpAdd THEN
- 1808                     QbeGen.Op3L("add", qt, q, q2)
- 1809                   ELSE
- 1810                     QbeGen.Op3L("sub", qt, q, q2)
- 1811                   END
- 1812                 ELSE
+ 1561                                                   QbeGen.OpenHiChar(q, qhi)
+ 1562                                                 ELSE QbeGen.OpenHi(q, qhi)
+ 1563                                                 END
+ 1564                                               ELSE
+ 1565                                                 lo := SymTab.ArrayLo(t);
+ 1566                                                 hi := SymTab.ArrayHi(t);
+ 1567                                                 IF SymTab.IsCharArray(t) THEN
+ 1568                                                   hi := hi + 1
+ 1569                                                 END;
+ 1570                                                 QbeGen.IntStr(lo, qlo);
+ 1571                                                 QbeGen.IntStr(hi, qhi)
+ 1572                                               END;
+ 1573                                               QbeGen.CheckRange(ql, qlo,
+ 1574                                                 qhi);
+ 1575                                               eT := SymTab.ArrayElem(t);
+ 1576                                               QbeGen.ElemAddr(q, ql, qlo,
+ 1577                                                 t, qe);
+ 1578                                               IF SymTab.ClassOf(eT) =
+ 1579                                                  SymTab.ClArray THEN
+ 1580                                                 QbeGen.ElemLoad(qe, eT, q)
+ 1581                                               ELSE QbeGen.CopyOp(qe, q)
+ 1582                                               END;
+ 1583                                               t := eT; sfx := TRUE
+ 1584                                             END; .) }
+ 1585          "]"
+ 1586        | "." GetIdent<fn>
+ 1587                                          (. IF k = SymTab.KindModule THEN
+ 1588                                               (* qualified L.x: materialize
+ 1589                                                  the export, then load it *)
+ 1590                                               IF NOT SymTab.MaterializeAlias(n,
+ 1591                                                    fn, mal) THEN
+ 1592                                                 SemError(201);
+ 1593                                                 t := SymTab.InvalidType;
+ 1594                                                 QbeGen.CopyOp("0", q)
+ 1595                                               ELSE
+ 1596                                                 QbeGen.CopyOp(mal, qn);
+ 1597                                                 t := SymTab.SymType(mal);
+ 1598                                                 k := SymTab.SymKind(mal);
+ 1599                                                 sfx := FALSE;
+ 1600                                                 IF k = SymTab.KindProc THEN
+ 1601                                                   (* call: ArgList supplies
+ 1602                                                      the value *)
+ 1603                                                   QbeGen.CopyOp("0", q)
+ 1604                                                 ELSIF NOT QbeGen.LoadDesignator(
+ 1605                                                      mal, t, k, q) THEN
+ 1606                                                   SemError(230);
+ 1607                                                   QbeGen.CopyOp("0", q)
+ 1608                                                 END
+ 1609                                               END
+ 1610                                             ELSIF t = SymTab.InvalidType THEN
+ 1611                                             ELSIF (SymTab.ClassOf(t) #
+ 1612                                                    SymTab.ClRecord)
+ 1613   AND (SymTab.ClassOf(t) #
+ 1614                                                   SymTab.ClClass) THEN
+ 1615                                               SemError(215);
+ 1616                                               t := SymTab.InvalidType
+ 1617                                             ELSIF NOT SymTab.FieldExists(t,
+ 1618                                                      fn) THEN
+ 1619                                               SemError(216);
+ 1620                                               t := SymTab.InvalidType
+ 1621                                             ELSE
+ 1622                                               fo := SymTab.FieldOffset(t,
+ 1623                                                 fn);
+ 1624                                               t := SymTab.FieldType(t, fn);
+ 1625                                               QbeGen.FieldAddr(q, fo, qe);
+ 1626                                               (* array fields are inline:
+ 1627                                                  the field address is the
+ 1628                                                  descriptor, like records *)
+ 1629                                               QbeGen.CopyOp(qe, q);
+ 1630                                               sfx := TRUE
+ 1631                                             END; .)
+ 1632        | "^"
+ 1633                                          (. IF t = SymTab.InvalidType THEN
+ 1634                                             ELSIF SymTab.ClassOf(t) #
+ 1635                                                   SymTab.ClPtr THEN
+ 1636                                               SemError(219);
+ 1637                                               t := SymTab.InvalidType
+ 1638                                             ELSE
+ 1639                                               bt := SymTab.PtrBase(t);
+ 1640                                               IF bt = SymTab.InvalidType THEN
+ 1641                                               ELSE
+ 1642                                                 IF sfx THEN
+ 1643                                                   QbeGen.ElemLoad(q, t,
+ 1644                                                     qb);
+ 1645                                                   QbeGen.CopyOp(qb, q)
+ 1646                                                 END;
+ 1647                                                 t := bt;
+ 1648                                                 (* q holds the pointee
+ 1649                                                    address: Fact loads
+ 1650                                                    scalars/pointers and uses
+ 1651                                                    the address for
+ 1652                                                    aggregates; the VAR-actual
+ 1653                                                    note is q itself. *)
+ 1654                                                 sfx := TRUE
+ 1655                                               END
+ 1656                                             END; .) } .
+ 1657    Expr<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 1658                                          (. VAR t2: SymTab.TypeIndex;
+ 1659                                               op: INTEGER;
+ 1660                                               q2, qt, wl: QbeGen.QVal;
+ 1661                                               isR: BOOLEAN; .)
+ 1662      = SimExpr<t, q>
+ 1663        [ Rel<op> SimExpr<t2, q2>
+ 1664          (. IF op = SymTab.OpIn THEN
+ 1665               IF SymTab.InCheck(t, t2) THEN
+ 1666                 IF (t = SymTab.InvalidType)
+ 1667                    OR (t2 = SymTab.InvalidType) THEN
+ 1668                   t := SymTab.BoolType(); QbeGen.CopyOp("0", q)
+ 1669                 ELSE
+ 1670                   QbeGen.InSet(q, q2, SymTab.SetBaseLo(t2),
+ 1671                     SymTab.SetCount(t2), qt);
+ 1672                   t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
+ 1673                 END
+ 1674               ELSE SemError(222); t := SymTab.InvalidType;
+ 1675                 QbeGen.CopyOp("0", q)
+ 1676               END
+ 1677             ELSIF SymTab.RelCheck(t, t2, op) THEN
+ 1678               IF (t = SymTab.InvalidType)
+ 1679                  OR (t2 = SymTab.InvalidType) THEN
+ 1680                 t := SymTab.BoolType(); QbeGen.CopyOp("0", q)
+ 1681               ELSIF (SymTab.ClassOf(t) = SymTab.ClPtr)
+ 1682                  OR (SymTab.ClassOf(t2) = SymTab.ClPtr) THEN
+ 1683                 IF (op # SymTab.OpEq) AND (op # SymTab.OpNeq1)
+ 1684   AND (op # SymTab.OpNeq2) THEN
+ 1685                   SemError(213); t := SymTab.InvalidType;
+ 1686                   QbeGen.CopyOp("0", q)
+ 1687                 ELSE
+ 1688                   QbeGen.CmpL(op, q, q2, qt);
+ 1689                   t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
+ 1690                 END
+ 1691               ELSIF (SymTab.ClassOf(t) = SymTab.ClSet)
+ 1692                  OR (SymTab.ClassOf(t2) = SymTab.ClSet) THEN
+ 1693                 QbeGen.CmpSet(op, q, q2,
+ 1694                   SymTab.SetWords(t), SymTab.SetWords(t2), qt);
+ 1695                 t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
+ 1696               ELSIF SymTab.IsLongFamily(t)
+ 1697                  OR SymTab.IsLongFamily(t2) THEN
+ 1698                 IF SymTab.IsIntFamily(t) THEN
+ 1699                   QbeGen.WidenLong(q, wl); QbeGen.CopyOp(wl, q)
+ 1700                 END;
+ 1701                 IF SymTab.IsIntFamily(t2) THEN
+ 1702                   QbeGen.WidenLong(q2, wl); QbeGen.CopyOp(wl, q2)
+ 1703                 END;
+ 1704                 QbeGen.CmpLong(op, q, q2, qt);
+ 1705                 t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
+ 1706               ELSE
+ 1707                 isR := SymTab.ClassOf(t) = SymTab.ClReal;
+ 1708                 t := SymTab.BoolType();
+ 1709                 QbeGen.Cmp(op, q, q2, qt, isR);
+ 1710                 QbeGen.CopyOp(qt, q)
+ 1711               END
+ 1712             ELSE SemError(213); t := SymTab.InvalidType;
+ 1713               QbeGen.CopyOp("0", q)
+ 1714             END; .) ] .
+ 1715    Rel<VAR op: INTEGER>
+ 1716      = "="                               (. op := SymTab.OpEq; .)
+ 1717      | "#"                               (. op := SymTab.OpNeq1; .)
+ 1718      | "<"                               (. op := SymTab.OpLt; .)
+ 1719      | "<="                              (. op := SymTab.OpLe; .)
+ 1720      | ">"                               (. op := SymTab.OpGt; .)
+ 1721      | ">="                              (. op := SymTab.OpGe; .)
+ 1722      | "IN"                              (. op := SymTab.OpIn; .) .
+ 1723    SimExpr<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 1724                                          (. VAR t2, res2, lt, rt:
+ 1725                                                 SymTab.TypeIndex;
+ 1726                                               op: INTEGER;
+ 1727                                               q2, qt, wq, qf:
+ 1728                                                 QbeGen.QVal;
+ 1729                                               neg, isR, isL, folded:
+ 1730                                                 BOOLEAN;
+ 1731                                               lw, rw, mw: CARDINAL;
+ 1732                                               lTrue, lNext, lDone, qr, qs:
+ 1733                                                 QbeGen.QVal; .)
+ 1734      =                                   (. neg := FALSE; .)
+ 1735        [ "+" | "-"                       (. neg := TRUE; .) ]
+ 1736        Term<t, q>                        (. IF neg THEN
+ 1737                                             IF QbeGen.IsImm(q) THEN
+ 1738                                               QbeGen.NegFold(q, q)
+ 1739                                             ELSE QbeGen.NewTemp(qt);
+ 1740                                               QbeGen.NegQ(q, qt,
+ 1741                                                 SymTab.ClassOf(t)
+ 1742                                                 = SymTab.ClReal);
+ 1743                                               QbeGen.CopyOp(qt, q)
+ 1744                                             END
+ 1745                                           END; .)
+ 1746        { AddOp<op>                       (. IF op = SymTab.OpOr THEN
+ 1747                                               QbeGen.DelayBegin END; .)
+ 1748          Term<t2, q2>                     (. IF op = SymTab.OpOr THEN
+ 1749                                               QbeGen.DelayEnd END; .)
+ 1750          (. IF op = SymTab.OpOr THEN
+ 1751               (* short-circuit: if q is true the RHS is skipped *)
+ 1752               IF SymTab.BoolCheck(t) AND SymTab.BoolCheck(t2) THEN
+ 1753                 t := SymTab.BoolType()
+ 1754               ELSE SemError(212); t := SymTab.InvalidType END;
+ 1755               IF t # SymTab.InvalidType THEN
+ 1756                 QbeGen.Slot4(qs);
+ 1757                 QbeGen.NewLabel(lTrue);
+ 1758                 QbeGen.NewLabel(lNext);
+ 1759                 QbeGen.NewLabel(lDone);
+ 1760                 QbeGen.Jnz(q, lTrue, lNext);
+ 1761                 QbeGen.EmitLabel(lTrue);
+ 1762                 QbeGen.StoreW(qs, "1");
+ 1763                 QbeGen.Jmp(lDone);
+ 1764                 QbeGen.EmitLabel(lNext);
+ 1765                 QbeGen.DelayFlush;
+ 1766                 QbeGen.StoreW(qs, q2);
+ 1767                 QbeGen.Jmp(lDone);
+ 1768                 QbeGen.EmitLabel(lDone);
+ 1769                 QbeGen.LoadW(qs, qr);
+ 1770                 QbeGen.CopyOp(qr, q)
+ 1771               ELSE QbeGen.DelayFlush; QbeGen.CopyOp("0", q)
+ 1772               END
+ 1773             ELSIF (t # SymTab.InvalidType) AND (t2 # SymTab.InvalidType)
+ 1774   AND (SymTab.ClassOf(t) = SymTab.ClSet)
+ 1775   AND (SymTab.ClassOf(t2) = SymTab.ClSet) THEN
+ 1776               lw := SymTab.SetWords(t); rw := SymTab.SetWords(t2);
+ 1777               mw := lw;
+ 1778               IF rw > mw THEN mw := rw END;
+ 1779               IF op = SymTab.OpAdd THEN
+ 1780                 QbeGen.SetBinOp(0, q, q2, lw, rw, qt)
+ 1781               ELSE
+ 1782                 QbeGen.SetBinOp(2, q, q2, lw, rw, qt)
+ 1783               END;
+ 1784               t := SymTab.NewSet(
+ 1785                      SymTab.NewSubR(0,
+ 1786                        VAL(INTEGER, mw) * 32 - 1));
+ 1787               QbeGen.CopyOp(qt, q)
+ 1788             ELSE
+ 1789               IF SymTab.ArithCheck(t, t2, FALSE, res2) THEN
+ 1790                 lt := t; rt := t2; t := res2
+ 1791               ELSE SemError(211); t := SymTab.InvalidType END;
+ 1792               IF t # SymTab.InvalidType THEN
+ 1793                 isL := SymTab.IsLongFamily(t);
+ 1794                 isR := SymTab.ClassOf(t) = SymTab.ClReal;
+ 1795                 folded := FALSE;
+ 1796                 IF (NOT isL) AND (NOT isR)
+ 1797    AND QbeGen.IsImm(q) AND QbeGen.IsImm(q2) THEN
+ 1798                   IF op = SymTab.OpAdd THEN
+ 1799                     folded := QbeGen.Fold2(0, q, q2, qf)
+ 1800                   ELSE
+ 1801                     folded := QbeGen.Fold2(1, q, q2, qf)
+ 1802                   END
+ 1803                 END;
+ 1804                 IF folded THEN QbeGen.CopyOp(qf, q)
+ 1805                 ELSE
+ 1806                 IF isL THEN
+ 1807                   IF SymTab.IsIntFamily(lt) THEN
+ 1808                     QbeGen.WidenLong(q, wq); QbeGen.CopyOp(wq, q)
+ 1809                   END;
+ 1810                   IF SymTab.IsIntFamily(rt) THEN
+ 1811                     QbeGen.WidenLong(q2, wq); QbeGen.CopyOp(wq, q2)
+ 1812                   END;
  1813                   QbeGen.NewTemp(qt);
  1814                   IF op = SymTab.OpAdd THEN
- 1815                     QbeGen.Op3("add", qt, q, q2, isR)
+ 1815                     QbeGen.Op3L("add", qt, q, q2)
  1816                   ELSE
- 1817                     QbeGen.Op3("sub", qt, q, q2, isR)
+ 1817                     QbeGen.Op3L("sub", qt, q, q2)
  1818                   END
- 1819                 END;
- 1820                 QbeGen.CopyOp(qt, q)
- 1821                 END
- 1822               ELSE QbeGen.CopyOp("0", q)
- 1823               END
- 1824             END; .) } .
- 1825    AddOp<VAR op: INTEGER>
- 1826      = "+"                               (. op := SymTab.OpAdd; .)
- 1827      | "-"                               (. op := SymTab.OpSub; .)
- 1828      | "OR"                              (. op := SymTab.OpOr; .) .
- 1829    Term<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 1830                                          (. VAR t2, res2, lt, rt:
- 1831                                                 SymTab.TypeIndex;
- 1832                                               op: INTEGER;
- 1833                                               q2, qt, wq, qf:
- 1834                                                 QbeGen.QVal;
- 1835                                               isR, isL, folded: BOOLEAN;
- 1836                                               lw, rw, mw: CARDINAL;
- 1837                                               lNext, lFalse, lDone, qr, qs:
- 1838                                                 QbeGen.QVal; .)
- 1839      = Fact<t, q> { MulOp<op>            (. IF op = SymTab.OpAnd THEN
- 1840                                               QbeGen.DelayBegin END; .)
- 1841          Fact<t2, q2>                    (. IF op = SymTab.OpAnd THEN
- 1842                                               QbeGen.DelayEnd END; .)
- 1843        (. IF op = SymTab.OpAnd THEN
- 1844             (* short-circuit: if q is false the RHS is skipped *)
- 1845             IF SymTab.BoolCheck(t) AND SymTab.BoolCheck(t2) THEN
- 1846               t := SymTab.BoolType()
- 1847             ELSE SemError(212); t := SymTab.InvalidType END;
- 1848             IF t # SymTab.InvalidType THEN
- 1849               QbeGen.Slot4(qs);
- 1850               QbeGen.NewLabel(lNext);
- 1851               QbeGen.NewLabel(lFalse);
- 1852               QbeGen.NewLabel(lDone);
- 1853               QbeGen.Jnz(q, lNext, lFalse);
- 1854               QbeGen.EmitLabel(lNext);
- 1855               QbeGen.DelayFlush;
- 1856               QbeGen.StoreW(qs, q2);
- 1857               QbeGen.Jmp(lDone);
- 1858               QbeGen.EmitLabel(lFalse);
- 1859               QbeGen.StoreW(qs, "0");
- 1860               QbeGen.Jmp(lDone);
- 1861               QbeGen.EmitLabel(lDone);
- 1862               QbeGen.LoadW(qs, qr);
- 1863               QbeGen.CopyOp(qr, q)
- 1864             ELSE QbeGen.DelayFlush; QbeGen.CopyOp("0", q)
- 1865             END
- 1866           ELSIF (t # SymTab.InvalidType) AND (t2 # SymTab.InvalidType)
- 1867   AND (SymTab.ClassOf(t) = SymTab.ClSet)
- 1868   AND (SymTab.ClassOf(t2) = SymTab.ClSet) THEN
- 1869             lw := SymTab.SetWords(t); rw := SymTab.SetWords(t2);
- 1870             mw := lw;
- 1871             IF rw > mw THEN mw := rw END;
- 1872             IF op = SymTab.OpTimes THEN
- 1873               QbeGen.SetBinOp(1, q, q2, lw, rw, qt)
- 1874             ELSE
- 1875               QbeGen.SetBinOp(3, q, q2, lw, rw, qt)
- 1876             END;
- 1877             t := SymTab.NewSet(
- 1878                    SymTab.NewSubR(0,
- 1879                      VAL(INTEGER, mw) * 32 - 1));
- 1880             QbeGen.CopyOp(qt, q)
- 1881           ELSE
- 1882             IF SymTab.ArithCheck(t, t2,
- 1883                  (op = SymTab.OpDiv) OR (op = SymTab.OpMod),
- 1884                  res2) THEN
- 1885               lt := t; rt := t2; t := res2
- 1886             ELSE SemError(211); t := SymTab.InvalidType END;
- 1887             IF t # SymTab.InvalidType THEN
- 1888               isL := SymTab.IsLongFamily(t);
- 1889               isR := SymTab.ClassOf(t) = SymTab.ClReal;
- 1890               folded := FALSE;
- 1891               IF (NOT isL) AND (NOT isR)
- 1892    AND QbeGen.IsImm(q) AND QbeGen.IsImm(q2) THEN
- 1893                 IF op = SymTab.OpTimes THEN
- 1894                   folded := QbeGen.Fold2(2, q, q2, qf)
- 1895                 ELSIF op = SymTab.OpDiv THEN
- 1896                   folded := QbeGen.Fold2(3, q, q2, qf)
- 1897                 ELSIF op = SymTab.OpMod THEN
- 1898                   folded := QbeGen.Fold2(4, q, q2, qf)
- 1899                 END
- 1900               END;
- 1901               IF folded THEN QbeGen.CopyOp(qf, q)
- 1902               ELSE
- 1903               IF isL THEN
- 1904                 IF SymTab.IsIntFamily(lt) THEN
- 1905                   QbeGen.WidenLong(q, wq); QbeGen.CopyOp(wq, q)
- 1906                 END;
- 1907                 IF SymTab.IsIntFamily(rt) THEN
- 1908                   QbeGen.WidenLong(q2, wq); QbeGen.CopyOp(wq, q2)
- 1909                 END;
- 1910                 QbeGen.NewTemp(qt);
- 1911                 IF op = SymTab.OpTimes THEN
- 1912                   QbeGen.Op3L("mul", qt, q, q2)
- 1913                 ELSIF (op = SymTab.OpDiv)
- 1914                    OR (op = SymTab.OpSlash) THEN
- 1915                   QbeGen.Op3L("div", qt, q, q2)
- 1916                 ELSE
- 1917                   QbeGen.Op3L("rem", qt, q, q2)
- 1918                 END
- 1919               ELSE
- 1920                 QbeGen.NewTemp(qt);
- 1921                 IF op = SymTab.OpTimes THEN
- 1922                   QbeGen.Op3("mul", qt, q, q2, isR)
- 1923                 ELSIF (op = SymTab.OpDiv)
- 1924                    OR (op = SymTab.OpSlash) THEN
- 1925                   QbeGen.Op3("div", qt, q, q2, isR)
- 1926                 ELSE
- 1927                   QbeGen.Op3("rem", qt, q, q2, isR)
- 1928                 END
- 1929               END;
- 1930               QbeGen.CopyOp(qt, q)
- 1931               END
- 1932             ELSE QbeGen.CopyOp("0", q)
- 1933             END
- 1934           END; .) } .
- 1935    MulOp<VAR op: INTEGER>
- 1936      = "*"                               (. op := SymTab.OpTimes; .)
- 1937      | "/"                               (. op := SymTab.OpSlash; .)
- 1938      | "DIV"                             (. op := SymTab.OpDiv; .)
- 1939      | "MOD"                             (. op := SymTab.OpMod; .)
- 1940      | ( "AND" | "&" )                   (. op := SymTab.OpAnd; .) .
- 1941    Fact<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 1942                                          (. VAR s: ARRAY [0 .. 255] OF CHAR;
- 1943                                               et, dt, t2, st, ct2:
- 1944                                                 SymTab.TypeIndex;
- 1945                                               dk: INTEGER;
- 1946                                               qd, q2, sq, qa, qm0, qr:
- 1947                                                 QbeGen.QVal;
- 1948                                               qn, vn: SymTab.Name;
- 1949                                               vt: SymTab.TypeIndex;
- 1950                                               c1, c2: INTEGER;
- 1951                                               called, isHigh, sfx, isCh:
- 1952                                                 BOOLEAN; .)
- 1953      = integer                           (. LexString(s);
- 1954                                             QbeGen.NormInt(s, q);
- 1955                                             t := SymTab.IntType(); .)
- 1956      | charConst                         (. LexString(s);
- 1957                                             QbeGen.NormLit(s, q, isCh);
- 1958                                             t := SymTab.CharType(); .)
- 1959      | real                              (. LexString(s);
- 1960                                             QbeGen.NormReal(s, q);
- 1961                                             t := SymTab.RealType(); .)
- 1962      | string                            (. LexString(s);
- 1963                                             IF SymTab.StrLen(s) = 3 THEN
- 1964                                               t := SymTab.CharType();
- 1965                                               QbeGen.IntStr(
- 1966                                                 QbeGen.CharVal(s), q)
- 1967                                             ELSE t := SymTab.NewStr();
- 1968                                               QbeGen.DeclStr(s, q);
- 1969                                               (* a literal's value IS its
- 1970                                                  static descriptor address *)
- 1971                                               QbeGen.NoteAddr(q, q)
- 1972                                             END; .)
- 1973      | Design<dt, dk, qd, qn, sfx>       (. called := FALSE;
- 1974                                             t := dt;
- 1975                                             IF sfx THEN
- 1976                                               IF dt =
- 1977                                                  SymTab.InvalidType THEN
- 1978                                                 QbeGen.CopyOp("0", q)
- 1979                                               ELSIF (SymTab.ClassOf(dt) =
- 1980                                                      SymTab.ClRecord)
- 1981                                                  OR (SymTab.ClassOf(dt) =
- 1982                                                      SymTab.ClSet)
- 1983                                                  OR (SymTab.ClassOf(dt) =
- 1984                                                      SymTab.ClArray)
- 1985                                                  OR (SymTab.ClassOf(dt) =
- 1986                                                      SymTab.ClClass) THEN
- 1987                                                 QbeGen.CopyOp(qd, q)
- 1988                                               ELSE QbeGen.ElemLoad(qd, dt,
- 1989                                                 q)
- 1990                                               END
- 1991                                             ELSE QbeGen.CopyOp(qd, q)
- 1992                                             END;
- 1993                                             IF (dk = SymTab.KindVar)
- 1994                                                OR (dk = SymTab.KindParam)
- 1995                                                OR (dk =
- 1996                                                   SymTab.KindField) THEN
- 1997                                               IF sfx THEN
- 1998                                                 QbeGen.NoteAddr(q, qd)
- 1999                                               ELSE
- 2000                                                 QbeGen.AddrOf(qn, qa);
- 2001                                                 QbeGen.NoteAddr(q, qa)
- 2002                                               END
- 2003                                             ELSIF sfx
- 2004   AND (dt #
- 2005                                                   SymTab.InvalidType)
- 2006   AND ((SymTab.ClassOf(dt) =
- 2007                                                    SymTab.ClArray)
- 2008                                                   OR (SymTab.ClassOf(dt) =
- 2009                                                       SymTab.ClSet)
- 2010                                                   OR (SymTab.ClassOf(dt) =
- 2011                                                       SymTab.ClRecord)) THEN
- 2012                                               QbeGen.NoteAddr(qd, qd)
- 2013                                             END; .)
- 2014        [ TypedSetLit<dt, q>              (. t := dt; .) ]
- 2015        [ ArgList<qn, dt, qd, TRUE, ct2, q2, called>
- 2016                                          (. t := ct2;
- 2017                                             QbeGen.CopyOp(q2, q); .) ]
- 2018                                          (. IF NOT called
- 2019   AND (dk = SymTab.KindProc) THEN
- 2020                                               (* bare zero-arg function
- 2021                                                  call (parentheses may be
- 2022                                                  omitted); a proper or
- 2023                                                  parameterised proc here
- 2024                                                  is 230 *)
- 2025                                               IF (SymTab.ProcNPar(qn) = 0)
- 2026   AND (SymTab.ProcRes(qn) #
- 2027                                                     SymTab.InvalidType) THEN
- 2028                                                 QbeGen.Mangled(qn,
- 2029                                                   SymTab.ProcUid(qn), qm0);
- 2030                                                 QbeGen.CallBegin(qm0,
- 2031                                                   SymTab.ProcRes(qn),
- 2032                                                   SymTab.ProcDepthOf(qn),
- 2033                                                   SymTab.IsExternal(qn));
- 2034                                                 QbeGen.CallEnd(TRUE, q);
- 2035                                                 t := SymTab.ProcRes(qn)
- 2036                                               ELSE
- 2037                                                 (* procedure used as a
- 2038                                                    value (assign to a
- 2039                                                    procedure variable):
- 2040                                                    its code address *)
- 2041                                                 t := SymTab.ProcTypeOf(qn);
- 2042                                                 QbeGen.Mangled(qn,
- 2043                                                   SymTab.ProcUid(qn), qm0);
- 2044                                                 QbeGen.ProcAddr(qm0, q)
- 2045                                               END
- 2046                                             END; .)
- 2047      | ( "HIGH"                          (. isHigh := TRUE; .)
- 2048        | ( "LEN" | "LENGTH" )            (. isHigh := FALSE; .) )
- 2049        "(" Design<dt, dk, qd, qn, sfx> ")"
- 2050                                          (. IF dt = SymTab.InvalidType THEN
- 2051                                             ELSIF SymTab.ClassOf(dt) #
- 2052                                                   SymTab.ClArray THEN
- 2053                                               SemError(217);
- 2054                                               t := SymTab.InvalidType;
- 2055                                               QbeGen.CopyOp("0", q)
- 2056                                             ELSE
- 2057                                               IF isHigh THEN
- 2058                                                 IF SymTab.IsOpenArray(dt) THEN
- 2059                                                   QbeGen.OpenHi(qd, qr)
- 2060                                                 ELSE
- 2061                                                   QbeGen.IntStr(
- 2062                                                     SymTab.ArrayHi(dt), qr)
- 2063                                                 END
- 2064                                               ELSE
- 2065                                                 IF SymTab.IsOpenArray(dt) THEN
- 2066                                                   QbeGen.LoadCount(qd, qr)
- 2067                                                 ELSE
- 2068                                                   QbeGen.IntStr(VAL(
- 2069                                                     INTEGER,
- 2070                                                     SymTab.ArrayLen(dt)),
- 2071                                                     qr)
- 2072                                                 END
- 2073                                               END;
- 2074                                                t := SymTab.IntType();
- 2075                                                QbeGen.CopyOp(qr, q)
- 2076                                              END; .)
- 2077      | ( "SIZE" | "TSIZE" ) "(" Design<dt, dk, qd, qn, sfx> ")"
- 2078                                          (. IF dt = SymTab.InvalidType THEN
- 2079                                               t := SymTab.InvalidType;
- 2080                                               QbeGen.CopyOp("0", q)
- 2081                                             ELSE
- 2082                                               QbeGen.IntStr(VAL(INTEGER,
- 2083                                                 SymTab.ObjectSize(dt)), q);
- 2084                                               t := SymTab.IntType()
- 2085                                             END; .)
- 2086      | "ADR" "(" Design<dt, dk, qd, qn, sfx> ")"
- 2087                                          (. IF dt = SymTab.InvalidType THEN
- 2088                                               t := SymTab.InvalidType;
- 2089                                               QbeGen.CopyOp("0", q)
- 2090                                             ELSE
- 2091                                               IF sfx THEN
- 2092                                                 QbeGen.CopyOp(qd, q)
- 2093                                               ELSIF (dk = SymTab.KindVar)
- 2094                                                  OR (dk = SymTab.KindParam) THEN
- 2095                                                 QbeGen.AddrOf(qn, q)
- 2096                                               ELSE SemError(230);
- 2097                                                 QbeGen.CopyOp("0", q)
- 2098                                               END;
- 2099                                               t := SymTab.AddrType()
- 2100                                             END; .)
- 2101      | "CHR" "(" Expr<et, q> ")"
- 2102                                          (. IF (et # SymTab.InvalidType)
- 2103   AND NOT SymTab.IsIntFamily(et) THEN
- 2104                                               SemError(211) END;
- 2105                                             t := SymTab.CharType(); .)
- 2106      | ( "ORD" | "ORDL" ) "(" Expr<et, q> ")"
- 2107                                          (. IF et # SymTab.InvalidType THEN
- 2108                                               IF (SymTab.ClassOf(et) #
- 2109                                                   SymTab.ClChar)
- 2110   AND (SymTab.ClassOf(et) #
- 2111                                                     SymTab.ClBool)
- 2112   AND (SymTab.ClassOf(et) #
- 2113                                                     SymTab.ClEnum)
- 2114   AND NOT SymTab.IsIntFamily(et) THEN
- 2115                                                 SemError(211) END
- 2116                                             END;
- 2117                                             t := SymTab.IntType(); .)
- 2118      | "CAP" "(" Expr<et, q> ")"
- 2119                                          (. QbeGen.CapQ(q, qa);
- 2120                                             QbeGen.CopyOp(qa, q);
- 2121                                             t := SymTab.CharType(); .)
- 2122      | "ABS" "(" Expr<et, q> ")"
+ 1819                 ELSE
+ 1820                   QbeGen.NewTemp(qt);
+ 1821                   IF op = SymTab.OpAdd THEN
+ 1822                     QbeGen.Op3("add", qt, q, q2, isR)
+ 1823                   ELSE
+ 1824                     QbeGen.Op3("sub", qt, q, q2, isR)
+ 1825                   END
+ 1826                 END;
+ 1827                 QbeGen.CopyOp(qt, q)
+ 1828                 END
+ 1829               ELSE QbeGen.CopyOp("0", q)
+ 1830               END
+ 1831             END; .) } .
+ 1832    AddOp<VAR op: INTEGER>
+ 1833      = "+"                               (. op := SymTab.OpAdd; .)
+ 1834      | "-"                               (. op := SymTab.OpSub; .)
+ 1835      | "OR"                              (. op := SymTab.OpOr; .) .
+ 1836    Term<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 1837                                          (. VAR t2, res2, lt, rt:
+ 1838                                                 SymTab.TypeIndex;
+ 1839                                               op: INTEGER;
+ 1840                                               q2, qt, wq, qf:
+ 1841                                                 QbeGen.QVal;
+ 1842                                               isR, isL, folded: BOOLEAN;
+ 1843                                               lw, rw, mw: CARDINAL;
+ 1844                                               lNext, lFalse, lDone, qr, qs:
+ 1845                                                 QbeGen.QVal; .)
+ 1846      = Fact<t, q> { MulOp<op>            (. IF op = SymTab.OpAnd THEN
+ 1847                                               QbeGen.DelayBegin END; .)
+ 1848          Fact<t2, q2>                    (. IF op = SymTab.OpAnd THEN
+ 1849                                               QbeGen.DelayEnd END; .)
+ 1850        (. IF op = SymTab.OpAnd THEN
+ 1851             (* short-circuit: if q is false the RHS is skipped *)
+ 1852             IF SymTab.BoolCheck(t) AND SymTab.BoolCheck(t2) THEN
+ 1853               t := SymTab.BoolType()
+ 1854             ELSE SemError(212); t := SymTab.InvalidType END;
+ 1855             IF t # SymTab.InvalidType THEN
+ 1856               QbeGen.Slot4(qs);
+ 1857               QbeGen.NewLabel(lNext);
+ 1858               QbeGen.NewLabel(lFalse);
+ 1859               QbeGen.NewLabel(lDone);
+ 1860               QbeGen.Jnz(q, lNext, lFalse);
+ 1861               QbeGen.EmitLabel(lNext);
+ 1862               QbeGen.DelayFlush;
+ 1863               QbeGen.StoreW(qs, q2);
+ 1864               QbeGen.Jmp(lDone);
+ 1865               QbeGen.EmitLabel(lFalse);
+ 1866               QbeGen.StoreW(qs, "0");
+ 1867               QbeGen.Jmp(lDone);
+ 1868               QbeGen.EmitLabel(lDone);
+ 1869               QbeGen.LoadW(qs, qr);
+ 1870               QbeGen.CopyOp(qr, q)
+ 1871             ELSE QbeGen.DelayFlush; QbeGen.CopyOp("0", q)
+ 1872             END
+ 1873           ELSIF (t # SymTab.InvalidType) AND (t2 # SymTab.InvalidType)
+ 1874   AND (SymTab.ClassOf(t) = SymTab.ClSet)
+ 1875   AND (SymTab.ClassOf(t2) = SymTab.ClSet) THEN
+ 1876             lw := SymTab.SetWords(t); rw := SymTab.SetWords(t2);
+ 1877             mw := lw;
+ 1878             IF rw > mw THEN mw := rw END;
+ 1879             IF op = SymTab.OpTimes THEN
+ 1880               QbeGen.SetBinOp(1, q, q2, lw, rw, qt)
+ 1881             ELSE
+ 1882               QbeGen.SetBinOp(3, q, q2, lw, rw, qt)
+ 1883             END;
+ 1884             t := SymTab.NewSet(
+ 1885                    SymTab.NewSubR(0,
+ 1886                      VAL(INTEGER, mw) * 32 - 1));
+ 1887             QbeGen.CopyOp(qt, q)
+ 1888           ELSE
+ 1889             IF SymTab.ArithCheck(t, t2,
+ 1890                  (op = SymTab.OpDiv) OR (op = SymTab.OpMod),
+ 1891                  res2) THEN
+ 1892               lt := t; rt := t2; t := res2
+ 1893             ELSE SemError(211); t := SymTab.InvalidType END;
+ 1894             IF t # SymTab.InvalidType THEN
+ 1895               isL := SymTab.IsLongFamily(t);
+ 1896               isR := SymTab.ClassOf(t) = SymTab.ClReal;
+ 1897               folded := FALSE;
+ 1898               IF (NOT isL) AND (NOT isR)
+ 1899    AND QbeGen.IsImm(q) AND QbeGen.IsImm(q2) THEN
+ 1900                 IF op = SymTab.OpTimes THEN
+ 1901                   folded := QbeGen.Fold2(2, q, q2, qf)
+ 1902                 ELSIF op = SymTab.OpDiv THEN
+ 1903                   folded := QbeGen.Fold2(3, q, q2, qf)
+ 1904                 ELSIF op = SymTab.OpMod THEN
+ 1905                   folded := QbeGen.Fold2(4, q, q2, qf)
+ 1906                 END
+ 1907               END;
+ 1908               IF folded THEN QbeGen.CopyOp(qf, q)
+ 1909               ELSE
+ 1910               IF isL THEN
+ 1911                 IF SymTab.IsIntFamily(lt) THEN
+ 1912                   QbeGen.WidenLong(q, wq); QbeGen.CopyOp(wq, q)
+ 1913                 END;
+ 1914                 IF SymTab.IsIntFamily(rt) THEN
+ 1915                   QbeGen.WidenLong(q2, wq); QbeGen.CopyOp(wq, q2)
+ 1916                 END;
+ 1917                 QbeGen.NewTemp(qt);
+ 1918                 IF op = SymTab.OpTimes THEN
+ 1919                   QbeGen.Op3L("mul", qt, q, q2)
+ 1920                 ELSIF (op = SymTab.OpDiv)
+ 1921                    OR (op = SymTab.OpSlash) THEN
+ 1922                   QbeGen.Op3L("div", qt, q, q2)
+ 1923                 ELSE
+ 1924                   QbeGen.Op3L("rem", qt, q, q2)
+ 1925                 END
+ 1926               ELSE
+ 1927                 QbeGen.NewTemp(qt);
+ 1928                 IF op = SymTab.OpTimes THEN
+ 1929                   QbeGen.Op3("mul", qt, q, q2, isR)
+ 1930                 ELSIF (op = SymTab.OpDiv)
+ 1931                    OR (op = SymTab.OpSlash) THEN
+ 1932                   QbeGen.Op3("div", qt, q, q2, isR)
+ 1933                 ELSE
+ 1934                   QbeGen.Op3("rem", qt, q, q2, isR)
+ 1935                 END
+ 1936               END;
+ 1937               QbeGen.CopyOp(qt, q)
+ 1938               END
+ 1939             ELSE QbeGen.CopyOp("0", q)
+ 1940             END
+ 1941           END; .) } .
+ 1942    MulOp<VAR op: INTEGER>
+ 1943      = "*"                               (. op := SymTab.OpTimes; .)
+ 1944      | "/"                               (. op := SymTab.OpSlash; .)
+ 1945      | "DIV"                             (. op := SymTab.OpDiv; .)
+ 1946      | "MOD"                             (. op := SymTab.OpMod; .)
+ 1947      | ( "AND" | "&" )                   (. op := SymTab.OpAnd; .) .
+ 1948    Fact<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 1949                                          (. VAR s: ARRAY [0 .. 255] OF CHAR;
+ 1950                                               et, dt, t2, st, ct2:
+ 1951                                                 SymTab.TypeIndex;
+ 1952                                               dk: INTEGER;
+ 1953                                               qd, q2, sq, qa, qm0, qr:
+ 1954                                                 QbeGen.QVal;
+ 1955                                               qn, vn: SymTab.Name;
+ 1956                                               vt: SymTab.TypeIndex;
+ 1957                                               c1, c2: INTEGER;
+ 1958                                               called, isHigh, sfx, isCh,
+ 1959                                               isU, uok: BOOLEAN;
+ 1960                                               ucp: INTEGER; .)
+ 1961      = integer                           (. LexString(s);
+ 1962                                             QbeGen.NormInt(s, q);
+ 1963                                             t := SymTab.IntType(); .)
+ 1964      | charConst                         (. LexString(s);
+ 1965                                             QbeGen.NormLit(s, q, isCh);
+ 1966                                             t := SymTab.CharType(); .)
+ 1967      | real                              (. LexString(s);
+ 1968                                             QbeGen.NormReal(s, q);
+ 1969                                             t := SymTab.RealType(); .)
+ 1970      | string                            (. LexString(s);
+ 1971                                             IF SymTab.StrLen(s) = 3 THEN
+ 1972                                               t := SymTab.CharType();
+ 1973                                               QbeGen.IntStr(
+ 1974                                                 QbeGen.CharVal(s), q)
+ 1975                                             ELSE t := SymTab.NewStr();
+ 1976                                               QbeGen.DeclStr(s, q);
+ 1977                                               (* a literal's value IS its
+ 1978                                                  static descriptor address *)
+ 1979                                               QbeGen.NoteAddr(q, q)
+ 1980                                             END; .)
+ 1981      | ustring                           (. LexString(s);
+ 1982                                             QbeGen.DeclUStr(s, q, isU, ucp,
+ 1983                                               uok);
+ 1984                                             IF NOT uok THEN
+ 1985                                               SemError(234);
+ 1986                                               t := SymTab.InvalidType
+ 1987                                             ELSIF isU THEN
+ 1988                                               t := SymTab.UCharType();
+ 1989                                               QbeGen.IntStr(ucp, q)
+ 1990                                             ELSE
+ 1991                                               t := SymTab.NewUStr();
+ 1992                                               QbeGen.NoteAddr(q, q)
+ 1993                                             END; .)
+ 1994      | Design<dt, dk, qd, qn, sfx>       (. called := FALSE;
+ 1995                                             t := dt;
+ 1996                                             IF sfx THEN
+ 1997                                               IF dt =
+ 1998                                                  SymTab.InvalidType THEN
+ 1999                                                 QbeGen.CopyOp("0", q)
+ 2000                                               ELSIF (SymTab.ClassOf(dt) =
+ 2001                                                      SymTab.ClRecord)
+ 2002                                                  OR (SymTab.ClassOf(dt) =
+ 2003                                                      SymTab.ClSet)
+ 2004                                                  OR (SymTab.ClassOf(dt) =
+ 2005                                                      SymTab.ClArray)
+ 2006                                                  OR (SymTab.ClassOf(dt) =
+ 2007                                                      SymTab.ClClass) THEN
+ 2008                                                 QbeGen.CopyOp(qd, q)
+ 2009                                               ELSE QbeGen.ElemLoad(qd, dt,
+ 2010                                                 q)
+ 2011                                               END
+ 2012                                             ELSE QbeGen.CopyOp(qd, q)
+ 2013                                             END;
+ 2014                                             IF (dk = SymTab.KindVar)
+ 2015                                                OR (dk = SymTab.KindParam)
+ 2016                                                OR (dk =
+ 2017                                                   SymTab.KindField) THEN
+ 2018                                               IF sfx THEN
+ 2019                                                 QbeGen.NoteAddr(q, qd)
+ 2020                                               ELSE
+ 2021                                                 QbeGen.AddrOf(qn, qa);
+ 2022                                                 QbeGen.NoteAddr(q, qa)
+ 2023                                               END
+ 2024                                             ELSIF sfx
+ 2025   AND (dt #
+ 2026                                                   SymTab.InvalidType)
+ 2027   AND ((SymTab.ClassOf(dt) =
+ 2028                                                    SymTab.ClArray)
+ 2029                                                   OR (SymTab.ClassOf(dt) =
+ 2030                                                       SymTab.ClSet)
+ 2031                                                   OR (SymTab.ClassOf(dt) =
+ 2032                                                       SymTab.ClRecord)) THEN
+ 2033                                               QbeGen.NoteAddr(qd, qd)
+ 2034                                             END; .)
+ 2035        [ TypedSetLit<dt, q>              (. t := dt; .) ]
+ 2036        [ ArgList<qn, dt, qd, TRUE, ct2, q2, called>
+ 2037                                          (. t := ct2;
+ 2038                                             QbeGen.CopyOp(q2, q); .) ]
+ 2039                                          (. IF NOT called
+ 2040   AND (dk = SymTab.KindProc) THEN
+ 2041                                               (* bare zero-arg function
+ 2042                                                  call (parentheses may be
+ 2043                                                  omitted); a proper or
+ 2044                                                  parameterised proc here
+ 2045                                                  is 230 *)
+ 2046                                               IF (SymTab.ProcNPar(qn) = 0)
+ 2047   AND (SymTab.ProcRes(qn) #
+ 2048                                                     SymTab.InvalidType) THEN
+ 2049                                                 QbeGen.Mangled(qn,
+ 2050                                                   SymTab.ProcUid(qn), qm0);
+ 2051                                                 QbeGen.CallBegin(qm0,
+ 2052                                                   SymTab.ProcRes(qn),
+ 2053                                                   SymTab.ProcDepthOf(qn),
+ 2054                                                   SymTab.IsExternal(qn));
+ 2055                                                 QbeGen.CallEnd(TRUE, q);
+ 2056                                                 t := SymTab.ProcRes(qn)
+ 2057                                               ELSE
+ 2058                                                 (* procedure used as a
+ 2059                                                    value (assign to a
+ 2060                                                    procedure variable):
+ 2061                                                    its code address *)
+ 2062                                                 t := SymTab.ProcTypeOf(qn);
+ 2063                                                 QbeGen.Mangled(qn,
+ 2064                                                   SymTab.ProcUid(qn), qm0);
+ 2065                                                 QbeGen.ProcAddr(qm0, q)
+ 2066                                               END
+ 2067                                             END; .)
+ 2068      | ( "HIGH"                          (. isHigh := TRUE; .)
+ 2069        | ( "LEN" | "LENGTH" )            (. isHigh := FALSE; .) )
+ 2070        "(" Design<dt, dk, qd, qn, sfx> ")"
+ 2071                                          (. IF dt = SymTab.InvalidType THEN
+ 2072                                             ELSIF SymTab.ClassOf(dt) #
+ 2073                                                   SymTab.ClArray THEN
+ 2074                                               SemError(217);
+ 2075                                               t := SymTab.InvalidType;
+ 2076                                               QbeGen.CopyOp("0", q)
+ 2077                                             ELSE
+ 2078                                               IF isHigh THEN
+ 2079                                                 IF SymTab.IsOpenArray(dt) THEN
+ 2080                                                   QbeGen.OpenHi(qd, qr)
+ 2081                                                 ELSE
+ 2082                                                   QbeGen.IntStr(
+ 2083                                                     SymTab.ArrayHi(dt), qr)
+ 2084                                                 END
+ 2085                                               ELSE
+ 2086                                                 IF SymTab.IsOpenArray(dt) THEN
+ 2087                                                   QbeGen.LoadCount(qd, qr)
+ 2088                                                 ELSE
+ 2089                                                   QbeGen.IntStr(VAL(
+ 2090                                                     INTEGER,
+ 2091                                                     SymTab.ArrayLen(dt)),
+ 2092                                                     qr)
+ 2093                                                 END
+ 2094                                               END;
+ 2095                                                t := SymTab.IntType();
+ 2096                                                QbeGen.CopyOp(qr, q)
+ 2097                                              END; .)
+ 2098      | ( "SIZE" | "TSIZE" ) "(" Design<dt, dk, qd, qn, sfx> ")"
+ 2099                                          (. IF dt = SymTab.InvalidType THEN
+ 2100                                               t := SymTab.InvalidType;
+ 2101                                               QbeGen.CopyOp("0", q)
+ 2102                                             ELSE
+ 2103                                               QbeGen.IntStr(VAL(INTEGER,
+ 2104                                                 SymTab.ObjectSize(dt)), q);
+ 2105                                               t := SymTab.IntType()
+ 2106                                             END; .)
+ 2107      | "ADR" "(" Design<dt, dk, qd, qn, sfx> ")"
+ 2108                                          (. IF dt = SymTab.InvalidType THEN
+ 2109                                               t := SymTab.InvalidType;
+ 2110                                               QbeGen.CopyOp("0", q)
+ 2111                                             ELSE
+ 2112                                               IF sfx THEN
+ 2113                                                 QbeGen.CopyOp(qd, q)
+ 2114                                               ELSIF (dk = SymTab.KindVar)
+ 2115                                                  OR (dk = SymTab.KindParam) THEN
+ 2116                                                 QbeGen.AddrOf(qn, q)
+ 2117                                               ELSE SemError(230);
+ 2118                                                 QbeGen.CopyOp("0", q)
+ 2119                                               END;
+ 2120                                               t := SymTab.AddrType()
+ 2121                                             END; .)
+ 2122      | "CHR" "(" Expr<et, q> ")"
  2123                                          (. IF (et # SymTab.InvalidType)
- 2124   AND NOT SymTab.IsIntFamily(et)
- 2125   AND (SymTab.ClassOf(et) #
- 2126                                                  SymTab.ClReal) THEN
- 2127                                               SemError(211)
- 2128                                             ELSE QbeGen.AbsQ(q, qa,
- 2129                                                    SymTab.ClassOf(et) =
- 2130                                                      SymTab.ClReal);
- 2131                                               QbeGen.CopyOp(qa, q)
- 2132                                             END;
- 2133                                             t := et; .)
- 2134      | "VAL" "(" GetIdent<vn> "," Expr<et, q> ")"
- 2135                                          (. IF NOT SymTab.Lookup(vn) THEN
- 2136                                               SemError(201);
- 2137                                               t := SymTab.InvalidType
- 2138                                             ELSE vt := SymTab.SymType(vn);
- 2139                                               IF vt = SymTab.InvalidType THEN
- 2140                                                 t := SymTab.InvalidType
- 2141                                               ELSIF et =
- 2142                                                  SymTab.InvalidType THEN
- 2143                                                 t := vt
- 2144                                               ELSE
- 2145                                                 c1 := SymTab.ClassOf(et);
- 2146                                                 c2 := SymTab.ClassOf(vt);
- 2147                                                 IF ((c1 = SymTab.ClInt)
- 2148                                                     OR (c1 =
- 2149                                                        SymTab.ClChar)
- 2150                                                     OR (c1 =
- 2151                                                        SymTab.ClBool)
- 2152                                                     OR (c1 =
- 2153                                                        SymTab.ClEnum))
- 2154   AND ((c2 = SymTab.ClInt)
- 2155                                                     OR (c2 =
- 2156                                                        SymTab.ClChar)
- 2157                                                     OR (c2 =
- 2158                                                        SymTab.ClBool)
- 2159                                                     OR (c2 =
- 2160                                                        SymTab.ClEnum)) THEN
- 2161                                                   t := vt
- 2162                                                 ELSIF (c1 = SymTab.ClPtr)
- 2163   AND (c2 = SymTab.ClPtr) THEN
- 2164                                                   t := vt
- 2165                                                 ELSIF (c1 = SymTab.ClReal)
- 2166   AND (c2 = SymTab.ClReal) THEN
- 2167                                                   t := vt
- 2168                                                 ELSE SemError(230);
- 2169                                                   t := SymTab.InvalidType
- 2170                                                 END
- 2171                                               END
- 2172                                             END; .)
- 2173      | "(" Expr<et, q> ")"               (. t := et; .)
- 2174      | SetLit<st, sq>                    (. t := st;
- 2175                                             QbeGen.CopyOp(sq, q); .)
- 2176      | ( "NOT" | "~" ) Fact<t2, q2>      (. IF SymTab.BoolCheck(t2) THEN
- 2177                                               t := SymTab.BoolType()
- 2178                                             ELSE SemError(212);
- 2179                                               t := SymTab.InvalidType END;
- 2180                                             IF t # SymTab.InvalidType THEN
- 2181                                               QbeGen.NotQ(q2, q)
- 2182                                             ELSE QbeGen.CopyOp("0", q)
- 2183                                             END; .) .
- 2184    (* Set literals are SET OF [0..255] (8 words); elements validated
- 2185       0..255 statically when foldable (222 otherwise), runtime trap
- 2186       for computed elements. Ranges always lower via SetRange. *)
- 2187    SetLit<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 2188      = "{"                               (. t := SymTab.NewSet(
- 2189                                               SymTab.NewSubR(0, 255));
- 2190                                             QbeGen.NewSetTemp(8, q);
- 2191                                             QbeGen.SetZero(q, 8); .)
- 2192        [ SetElem<t, q> { "," SetElem<t, q> } ]
- 2193        "}" .
- 2194    (* Typed set constructor: TypeName{ elems } — e.g. BITSET{0},
- 2195       BITSET{}.  The declared type (not SET OF [0..255]) sets the
- 2196       width and element span. *)
- 2197    TypedSetLit<vt: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 2198                                          (. VAR nw: CARDINAL; .)
- 2199      = "{"                               (. IF SymTab.ClassOf(vt) #
- 2200                                                SymTab.ClSet THEN
- 2201                                               SemError(230); nw := 8
- 2202                                             ELSE nw := SymTab.SetWords(vt);
- 2203                                               IF nw = 0 THEN nw := 8 END
- 2204                                             END;
- 2205                                             QbeGen.NewSetTemp(nw, q);
- 2206                                             QbeGen.SetZero(q, nw); .)
- 2207        [ SetElem<vt, q> { "," SetElem<vt, q> } ]
- 2208        "}" .
- 2209    SetElem<st: SymTab.TypeIndex; sq: QbeGen.QVal>         (. VAR et, et2: SymTab.TypeIndex;
- 2210                                               qe, q2: QbeGen.QVal;
- 2211                                               v, v2: INTEGER;
- 2212                                               lo: INTEGER;
- 2213                                               span: CARDINAL;
- 2214                                               cl, cl2: INTEGER;
- 2215                                               hasR: BOOLEAN; .)
- 2216      =                                   (. hasR := FALSE; .)
- 2217        Expr<et, qe>
- 2218        [ ".." Expr<et2, q2>              (. hasR := TRUE; .) ]
- 2219                                          (. lo := SymTab.SetBaseLo(st);
- 2220                                             span := SymTab.SetCount(st);
- 2221                                             IF (et = SymTab.InvalidType)
- 2222                                                OR (hasR AND (et2 =
- 2223                                                   SymTab.InvalidType)) THEN
- 2224                                             ELSE cl :=
- 2225                                                    SymTab.ClassOf(et);
- 2226                                               IF hasR THEN
- 2227                                                 cl2 :=
- 2228                                                   SymTab.ClassOf(et2)
- 2229                                               ELSE cl2 := SymTab.ClInt
- 2230                                               END;
- 2231                                               IF ((cl # SymTab.ClInt)
- 2232   AND (cl # SymTab.ClChar)
- 2233   AND (cl # SymTab.ClBool))
- 2234                                                  OR (hasR AND 
- 2235                                                     ((cl2
- 2236                                                       # SymTab.ClInt)
- 2237   AND (cl2
- 2238                                                        # SymTab.ClChar)
- 2239   AND (cl2
- 2240                                                        # SymTab.ClBool))) THEN
- 2241                                                 SemError(222)
- 2242                                               ELSIF hasR
- 2243   AND SymTab.ConstInt(qe, v)
- 2244   AND SymTab.ConstInt(q2,
- 2245                                                     v2)
- 2246   AND ((v < lo)
- 2247                                                     OR (v2 < lo)
- 2248                                                     OR (v >= lo +
- 2249                                                        VAL(INTEGER, span))
- 2250                                                     OR (v2 >= lo +
- 2251                                                        VAL(INTEGER, span))
- 2252                                                     OR (v > v2)) THEN
- 2253                                                 SemError(222)
- 2254                                                ELSIF hasR THEN
- 2255                                                  QbeGen.SetRange(sq, qe, q2,
- 2256                                                    lo, span)
- 2257                                                ELSIF SymTab.ConstInt(qe,
- 2258                                                        v)
- 2259   AND ((v < lo)
- 2260                                                      OR (v >= lo +
- 2261                                                         VAL(INTEGER,
- 2262                                                           span))) THEN
- 2263                                                  SemError(222)
- 2264                                                ELSE QbeGen.SetBit(sq, qe,
- 2265                                                  lo, span)
- 2266                                               END
- 2267                                             END; .) .
- 2268    GetIdent<VAR n: SymTab.Name>
- 2269      = ident                             (. LexName(n); .) .
- 2270  
- 2271  END M2.
+ 2124   AND NOT SymTab.IsIntFamily(et) THEN
+ 2125                                               SemError(211) END;
+ 2126                                             t := SymTab.CharType(); .)
+ 2127      | ( "ORD" | "ORDL" ) "(" Expr<et, q> ")"
+ 2128                                          (. IF et # SymTab.InvalidType THEN
+ 2129                                               IF (SymTab.ClassOf(et) #
+ 2130                                                   SymTab.ClChar)
+ 2131   AND (SymTab.ClassOf(et) #
+ 2132                                                     SymTab.ClBool)
+ 2133   AND (SymTab.ClassOf(et) #
+ 2134                                                     SymTab.ClEnum)
+ 2135   AND NOT SymTab.IsIntFamily(et) THEN
+ 2136                                                 SemError(211) END
+ 2137                                             END;
+ 2138                                             t := SymTab.IntType(); .)
+ 2139      | "CAP" "(" Expr<et, q> ")"
+ 2140                                          (. QbeGen.CapQ(q, qa);
+ 2141                                             QbeGen.CopyOp(qa, q);
+ 2142                                             t := SymTab.CharType(); .)
+ 2143      | "UCHR" "(" Expr<et, q> ")"
+ 2144                                          (. IF (et # SymTab.InvalidType)
+ 2145    AND (SymTab.ClassOf(et) # SymTab.ClChar) THEN
+ 2146                                               SemError(211) END;
+ 2147                                             t := SymTab.UCharType(); .)
+ 2148      | "CHR8" "(" Expr<et, q> ")"
+ 2149                                          (. IF (et # SymTab.InvalidType)
+ 2150    AND (SymTab.ClassOf(et) # SymTab.ClUChar) THEN
+ 2151                                               SemError(211) END;
+ 2152                                             QbeGen.WidenLong(q, qa);
+ 2153                                             QbeGen.CheckRange(qa, "0", "255");
+ 2154                                             t := SymTab.CharType(); .)
+ 2155      | "UORD" "(" Expr<et, q> ")"
+ 2156                                          (. IF (et # SymTab.InvalidType)
+ 2157    AND (SymTab.ClassOf(et) # SymTab.ClUChar) THEN
+ 2158                                               SemError(211) END;
+ 2159                                             QbeGen.WidenLong(q, qa);
+ 2160                                             QbeGen.CopyOp(qa, q);
+ 2161                                             t := SymTab.LongType(); .)
+ 2162      | "ABS" "(" Expr<et, q> ")"
+ 2163                                          (. IF (et # SymTab.InvalidType)
+ 2164   AND NOT SymTab.IsIntFamily(et)
+ 2165   AND (SymTab.ClassOf(et) #
+ 2166                                                  SymTab.ClReal) THEN
+ 2167                                               SemError(211)
+ 2168                                             ELSE QbeGen.AbsQ(q, qa,
+ 2169                                                    SymTab.ClassOf(et) =
+ 2170                                                      SymTab.ClReal);
+ 2171                                               QbeGen.CopyOp(qa, q)
+ 2172                                             END;
+ 2173                                             t := et; .)
+ 2174      | "VAL" "(" GetIdent<vn> "," Expr<et, q> ")"
+ 2175                                          (. IF NOT SymTab.Lookup(vn) THEN
+ 2176                                               SemError(201);
+ 2177                                               t := SymTab.InvalidType
+ 2178                                             ELSE vt := SymTab.SymType(vn);
+ 2179                                               IF vt = SymTab.InvalidType THEN
+ 2180                                                 t := SymTab.InvalidType
+ 2181                                               ELSIF et =
+ 2182                                                  SymTab.InvalidType THEN
+ 2183                                                 t := vt
+ 2184                                               ELSE
+ 2185                                                 c1 := SymTab.ClassOf(et);
+ 2186                                                 c2 := SymTab.ClassOf(vt);
+ 2187                                                 IF ((c1 = SymTab.ClInt)
+ 2188                                                     OR (c1 =
+ 2189                                                        SymTab.ClChar)
+ 2190                                                     OR (c1 =
+ 2191                                                        SymTab.ClBool)
+ 2192                                                     OR (c1 =
+ 2193                                                        SymTab.ClEnum))
+ 2194   AND ((c2 = SymTab.ClInt)
+ 2195                                                     OR (c2 =
+ 2196                                                        SymTab.ClChar)
+ 2197                                                     OR (c2 =
+ 2198                                                        SymTab.ClBool)
+ 2199                                                     OR (c2 =
+ 2200                                                        SymTab.ClEnum)) THEN
+ 2201                                                   t := vt
+ 2202                                                 ELSIF (c1 = SymTab.ClPtr)
+ 2203   AND (c2 = SymTab.ClPtr) THEN
+ 2204                                                   t := vt
+ 2205                                                 ELSIF (c1 = SymTab.ClReal)
+ 2206   AND (c2 = SymTab.ClReal) THEN
+ 2207                                                   t := vt
+ 2208                                                 ELSE SemError(230);
+ 2209                                                   t := SymTab.InvalidType
+ 2210                                                 END
+ 2211                                               END
+ 2212                                             END; .)
+ 2213      | "(" Expr<et, q> ")"               (. t := et; .)
+ 2214      | SetLit<st, sq>                    (. t := st;
+ 2215                                             QbeGen.CopyOp(sq, q); .)
+ 2216      | ( "NOT" | "~" ) Fact<t2, q2>      (. IF SymTab.BoolCheck(t2) THEN
+ 2217                                               t := SymTab.BoolType()
+ 2218                                             ELSE SemError(212);
+ 2219                                               t := SymTab.InvalidType END;
+ 2220                                             IF t # SymTab.InvalidType THEN
+ 2221                                               QbeGen.NotQ(q2, q)
+ 2222                                             ELSE QbeGen.CopyOp("0", q)
+ 2223                                             END; .) .
+ 2224    (* Set literals are SET OF [0..255] (8 words); elements validated
+ 2225       0..255 statically when foldable (222 otherwise), runtime trap
+ 2226       for computed elements. Ranges always lower via SetRange. *)
+ 2227    SetLit<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 2228      = "{"                               (. t := SymTab.NewSet(
+ 2229                                               SymTab.NewSubR(0, 255));
+ 2230                                             QbeGen.NewSetTemp(8, q);
+ 2231                                             QbeGen.SetZero(q, 8); .)
+ 2232        [ SetElem<t, q> { "," SetElem<t, q> } ]
+ 2233        "}" .
+ 2234    (* Typed set constructor: TypeName{ elems } — e.g. BITSET{0},
+ 2235       BITSET{}.  The declared type (not SET OF [0..255]) sets the
+ 2236       width and element span. *)
+ 2237    TypedSetLit<vt: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 2238                                          (. VAR nw: CARDINAL; .)
+ 2239      = "{"                               (. IF SymTab.ClassOf(vt) #
+ 2240                                                SymTab.ClSet THEN
+ 2241                                               SemError(230); nw := 8
+ 2242                                             ELSE nw := SymTab.SetWords(vt);
+ 2243                                               IF nw = 0 THEN nw := 8 END
+ 2244                                             END;
+ 2245                                             QbeGen.NewSetTemp(nw, q);
+ 2246                                             QbeGen.SetZero(q, nw); .)
+ 2247        [ SetElem<vt, q> { "," SetElem<vt, q> } ]
+ 2248        "}" .
+ 2249    SetElem<st: SymTab.TypeIndex; sq: QbeGen.QVal>         (. VAR et, et2: SymTab.TypeIndex;
+ 2250                                               qe, q2: QbeGen.QVal;
+ 2251                                               v, v2: INTEGER;
+ 2252                                               lo: INTEGER;
+ 2253                                               span: CARDINAL;
+ 2254                                               cl, cl2: INTEGER;
+ 2255                                               hasR: BOOLEAN; .)
+ 2256      =                                   (. hasR := FALSE; .)
+ 2257        Expr<et, qe>
+ 2258        [ ".." Expr<et2, q2>              (. hasR := TRUE; .) ]
+ 2259                                          (. lo := SymTab.SetBaseLo(st);
+ 2260                                             span := SymTab.SetCount(st);
+ 2261                                             IF (et = SymTab.InvalidType)
+ 2262                                                OR (hasR AND (et2 =
+ 2263                                                   SymTab.InvalidType)) THEN
+ 2264                                             ELSE cl :=
+ 2265                                                    SymTab.ClassOf(et);
+ 2266                                               IF hasR THEN
+ 2267                                                 cl2 :=
+ 2268                                                   SymTab.ClassOf(et2)
+ 2269                                               ELSE cl2 := SymTab.ClInt
+ 2270                                               END;
+ 2271                                               IF ((cl # SymTab.ClInt)
+ 2272   AND (cl # SymTab.ClChar)
+ 2273   AND (cl # SymTab.ClBool))
+ 2274                                                  OR (hasR AND 
+ 2275                                                     ((cl2
+ 2276                                                       # SymTab.ClInt)
+ 2277   AND (cl2
+ 2278                                                        # SymTab.ClChar)
+ 2279   AND (cl2
+ 2280                                                        # SymTab.ClBool))) THEN
+ 2281                                                 SemError(222)
+ 2282                                               ELSIF hasR
+ 2283   AND SymTab.ConstInt(qe, v)
+ 2284   AND SymTab.ConstInt(q2,
+ 2285                                                     v2)
+ 2286   AND ((v < lo)
+ 2287                                                     OR (v2 < lo)
+ 2288                                                     OR (v >= lo +
+ 2289                                                        VAL(INTEGER, span))
+ 2290                                                     OR (v2 >= lo +
+ 2291                                                        VAL(INTEGER, span))
+ 2292                                                     OR (v > v2)) THEN
+ 2293                                                 SemError(222)
+ 2294                                                ELSIF hasR THEN
+ 2295                                                  QbeGen.SetRange(sq, qe, q2,
+ 2296                                                    lo, span)
+ 2297                                                ELSIF SymTab.ConstInt(qe,
+ 2298                                                        v)
+ 2299   AND ((v < lo)
+ 2300                                                      OR (v >= lo +
+ 2301                                                         VAL(INTEGER,
+ 2302                                                           span))) THEN
+ 2303                                                  SemError(222)
+ 2304                                                ELSE QbeGen.SetBit(sq, qe,
+ 2305                                                  lo, span)
+ 2306                                               END
+ 2307                                             END; .) .
+ 2308    GetIdent<VAR n: SymTab.Name>
+ 2309      = ident                             (. LexName(n); .) .
+ 2310  
+ 2311  END M2.
 
     0 errors
 
 
 Statistics:
 
-  nr of terminals:        94 (limit   400)
+  nr of terminals:        98 (limit   400)
   nr of non-terminals:    78 (limit   210)
-  nr of pragmas:           0 (limit   406)
-  nr of symbolnodes:     172 (limit   500)
-  nr of graphnodes:      866 (limit  1500)
+  nr of pragmas:           0 (limit   402)
+  nr of symbolnodes:     176 (limit   500)
+  nr of graphnodes:      893 (limit  1500)
   nr of conditionsets:     9 (limit   100)
-  nr of charactersets:    12 (limit   250)
+  nr of charactersets:    15 (limit   250)
 
 

+ 10 - 0
compiler/src/QbeGen.def

@@ -265,6 +265,13 @@ PROCEDURE DeclStr (text: ARRAY OF CHAR; VAR q: QVal);
 (* Emits `data $strN = { l len, b ... }` for a quoted literal and
    returns its address operand. Deterministic counter. *)
 
+PROCEDURE DeclUStr (text: ARRAY OF CHAR; VAR q: QVal;
+                    VAR single: BOOLEAN; VAR cp: INTEGER;
+                    VAR ok: BOOLEAN);
+(* Strict RFC3629-decode a `U'...'`/`U"..."` lexeme. one codepoint ->
+   single=TRUE, cp=it; otherwise a UString descriptor ($ustrN) in q.
+   ok=FALSE on invalid UTF-8 (caller reports 234). *)
+
 PROCEDURE DeclCharStr (ch: ARRAY OF CHAR; VAR q: QVal);
 (* ch is the decimal ordinal of a CHAR literal; emits the equivalent
    1-character string descriptor and returns its address. Lets a
@@ -389,6 +396,9 @@ PROCEDURE HApp (s: ARRAY OF CHAR);
 PROCEDURE UpAddrOf (flat: INTEGER; levels: CARDINAL; VAR q: QVal);
 PROCEDURE Revive;
 PROCEDURE FlushStrings;
+
+PROCEDURE FlushUStrings;
+(* Emit the recorded UString literal descriptors as top-level data. *)
 PROCEDURE ArrData (name: ARRAY OF CHAR; t: INTEGER);
 PROCEDURE HeapSize (t: INTEGER): CARDINAL;
 

+ 96 - 0
compiler/src/QbeGen.mod

@@ -20,6 +20,13 @@ VAR
   nStr   : CARDINAL;  (* string-literal data counter *)
   strNams : ARRAY [0 .. 4095] OF QVal;
   strTexts : ARRAY [0 .. 4095] OF ARRAY [0 .. 255] OF CHAR;
+  (* UString literals: decoded codepoints, recorded for EndModule. *)
+  ustrN : CARDINAL;
+  ustrNams : ARRAY [0 .. 255] OF QVal;
+  ustrStart : ARRAY [0 .. 255] OF CARDINAL;
+  ustrCount : ARRAY [0 .. 255] OF CARDINAL;
+  ustrPool : ARRAY [0 .. 8191] OF INTEGER;
+  ustrUsed : CARDINAL;
   withTop : CARDINAL;
   withSt  : ARRAY [0 .. 7] OF QVal;
   noEmit : BOOLEAN;  (* TRUE while parsing nested procedures in 4.1:
@@ -508,6 +515,7 @@ PROCEDURE OpenModule (name: ARRAY OF CHAR);
     outSel := 0;
     sessUsed := 0; sessBuf[0] := CHR(0);
     delayTop := 0;
+    ustrN := 0; ustrUsed := 0;
     i := 0;
     WHILE i <= HIGH(nestBufs) DO
       nestBufs[i][0] := CHR(0); nestUsed[i] := 0; INC(i)
@@ -692,6 +700,7 @@ PROCEDURE EndModule (name: ARRAY OF CHAR);
       INC(i)
     END;
     FlushStrings;
+    FlushUStrings;
     (* the image is named after the program module *)
     fname[0] := CHR(0);
     App(fname, "gen_ssa/");
@@ -2362,6 +2371,93 @@ PROCEDURE DeclStr (text: ARRAY OF CHAR; VAR q: QVal);
     App(q, nm)
   END DeclStr;
 
+PROCEDURE DeclUStr (text: ARRAY OF CHAR; VAR q: QVal;
+                    VAR single: BOOLEAN; VAR cp: INTEGER;
+                    VAR ok: BOOLEAN);
+(* text is the raw lexeme U'...' / U"..."  Strict RFC3629-decode the
+   bytes between the quotes. Exactly one codepoint -> single:=TRUE,
+   cp:=it.  Otherwise record a UString descriptor and q := "$ustrN".
+   ok:=FALSE for invalid UTF-8 (overlong / surrogate / >10FFFF /
+   truncation). *)
+  VAR L, i, cnt, base: CARDINAL;
+    b0, b1, b2, b3, ch: INTEGER;
+    nm: QVal;
+
+  PROCEDURE Cont (VAR b: INTEGER): BOOLEAN;
+  (* consume a continuation byte, or fail *)
+    BEGIN
+      IF i >= L - 1 THEN RETURN FALSE END;
+      b := ORD(text[i]); INC(i);
+      RETURN (b >= 128) AND (b <= 191)
+    END Cont;
+
+  BEGIN
+    single := FALSE; cp := 0; ok := TRUE;
+    q[0] := "0"; q[1] := CHR(0);
+    L := Len(text);
+    IF (L < 3) OR (text[0] # "U") THEN ok := FALSE; RETURN END;
+    i := 2; cnt := 0; base := ustrUsed;
+    WHILE i < L - 1 DO
+      b0 := ORD(text[i]); INC(i);
+      ch := -1;
+      IF b0 <= 127 THEN
+        ch := b0
+      ELSIF (b0 >= 194) AND (b0 <= 223) THEN
+        IF Cont(b1) THEN ch := ((b0 - 192) * 64) + (b1 - 128) END
+      ELSIF (b0 >= 224) AND (b0 <= 239) THEN
+        IF Cont(b1) AND Cont(b2) THEN
+          ch := ((b0 - 224) * 4096) + ((b1 - 128) * 64) + (b2 - 128);
+          IF ch < 2048 THEN ch := -1 END
+        END
+      ELSIF (b0 >= 240) AND (b0 <= 244) THEN
+        IF Cont(b1) AND Cont(b2) AND Cont(b3) THEN
+          ch := ((b0 - 240) * 262144) + ((b1 - 128) * 4096)
+                + ((b2 - 128) * 64) + (b3 - 128);
+          IF ch < 65536 THEN ch := -1 END
+        END
+      ELSE ok := FALSE
+      END;
+      IF ok AND (ch < 0) THEN ok := FALSE END;
+      IF ok AND (ch >= 55296) AND (ch <= 57343) THEN ok := FALSE END;
+      IF ok AND (ch > 1114111) THEN ok := FALSE END;
+      IF NOT ok THEN RETURN END;
+      IF ustrUsed <= HIGH(ustrPool) THEN
+        ustrPool[ustrUsed] := ch; INC(ustrUsed)
+      END;
+      INC(cnt)
+    END;
+    IF cnt = 1 THEN
+      single := TRUE; cp := ustrPool[base]
+    ELSE
+      Cpy(nm, "ustr"); AppNum(nm, ustrN);
+      IF ustrN <= HIGH(ustrNams) THEN
+        Cpy(ustrNams[ustrN], nm);
+        ustrStart[ustrN] := base; ustrCount[ustrN] := cnt; INC(ustrN)
+      END;
+      Cpy(q, "$"); App(q, nm)
+    END
+  END DeclUStr;
+
+PROCEDURE FlushUStrings;
+  VAR k, i: CARDINAL;
+    bv: QVal;
+  BEGIN
+    IF NOT opened THEN RETURN END;
+    k := 0;
+    WHILE k < ustrN DO
+      W("data $"); W(ustrNams[k]); W(" = { l ");
+      IntStr(VAL(INTEGER, ustrCount[k]), bv); W(bv);
+      i := 0;
+      WHILE i < ustrCount[k] DO
+        W(", w ");
+        IntStr(ustrPool[ustrStart[k] + i], bv); W(bv);
+        INC(i)
+      END;
+      WL(" }");
+      INC(k)
+    END
+  END FlushUStrings;
+
 PROCEDURE DeclCharStr (ch: ARRAY OF CHAR; VAR q: QVal);
   VAR v: INTEGER;
     txt: ARRAY [0 .. 3] OF CHAR;

+ 6 - 0
compiler/src/SymTab.def

@@ -45,6 +45,8 @@ CONST
   ClNil     = 12;
   ClLong    = 13;
   ClProc    = 14;
+  ClUChar   = 15;
+  ClUStr    = 16;
 
   (* operator codes for RelCheck *)
   OpEq = 0; OpNeq1 = 1; OpNeq2 = 2;
@@ -329,6 +331,8 @@ PROCEDURE NewRecord (): TypeIndex;
 PROCEDURE NewSet (base: TypeIndex): TypeIndex;
 PROCEDURE NewPtr (base: TypeIndex): TypeIndex;
 PROCEDURE NewStr (): TypeIndex;
+PROCEDURE NewUStr (): TypeIndex;
+(* UString literal type: LONGCARD count + count x UCHAR codepoints. *)
 PROCEDURE NewClass (): TypeIndex;
 (* Fresh class descriptor (holds fields like a record; methods live
    in the class scope). Parent and scope via SetParent/PushClassScope. *)
@@ -349,6 +353,8 @@ PROCEDURE ForwardType (name: ARRAY OF CHAR): TypeIndex;
    already known or forward references are not permitted. *)
 
 PROCEDURE IntType (): TypeIndex;
+PROCEDURE UCharType (): TypeIndex;
+(* UCHAR: 32-bit Unicode codepoint (QBE w). *)
 PROCEDURE RealType (): TypeIndex;
 PROCEDURE CharType (): TypeIndex;
 PROCEDURE BoolType (): TypeIndex;

+ 26 - 2
compiler/src/SymTab.mod

@@ -15,6 +15,7 @@ CONST
   FRecord = 5; FSet = 6; FPtr = 7; FStr = 8;
   FInt = 9; FReal = 10; FChar = 11; FBool = 12;
   FClass = 13; FOpenArr = 14; FNil = 15; FLong = 16; FProc = 17;
+  FUChar = 18; FUStr = 19;
 
 TYPE
   SymPtr = POINTER TO SymNode;
@@ -86,6 +87,7 @@ VAR
   nProc : CARDINAL;
   nextUid : CARDINAL;
   dInt, dCard, dReal, dChar, dBool, dNil, dAddr, dLong : TypeIndex;
+  dUChar : TypeIndex;    (* UCHAR: 32-bit codepoint *)
   dBitset : TypeIndex;   (* BITSET = SET OF [0..15] *)
   globScope : ScopePtr;  (* the global scope (module names live here) *)
   curMod : INTEGER;      (* current registry slot, -1 between units *)
@@ -508,6 +510,11 @@ PROCEDURE NewStr (): TypeIndex;
     RETURN NewDesc(FStr, InvalidType)
   END NewStr;
 
+PROCEDURE NewUStr (): TypeIndex;
+  BEGIN
+    RETURN NewDesc(FUStr, InvalidType)
+  END NewUStr;
+
 PROCEDURE NewClass (): TypeIndex;
   VAR t: TypeIndex;
   BEGIN
@@ -558,6 +565,9 @@ PROCEDURE SetTarget (t, base: TypeIndex);
 PROCEDURE IntType (): TypeIndex;
   BEGIN RETURN dInt END IntType;
 
+PROCEDURE UCharType (): TypeIndex;
+  BEGIN RETURN dUChar END UCharType;
+
 PROCEDURE AddrType (): TypeIndex;
   BEGIN RETURN dAddr END AddrType;
 
@@ -590,6 +600,8 @@ PROCEDURE ClassOf (t: TypeIndex): INTEGER;
     | FStr : RETURN ClStr
     | FClass : RETURN ClClass
     | FProc : RETURN ClProc
+    | FUChar : RETURN ClUChar
+    | FUStr : RETURN ClUStr
     | FNil : RETURN ClNil
     | FSub : IF tref[r] = InvalidType THEN RETURN ClInt
              ELSE RETURN ClassOf(tref[r]) END
@@ -889,12 +901,12 @@ PROCEDURE TypeSizeD (t: TypeIndex; depth: CARDINAL): CARDINAL;
     r := Resolve(t);
     IF r = InvalidType THEN RETURN 0 END;
     CASE tform[r] OF
-      FInt, FBool, FChar : RETURN 4
+      FInt, FBool, FChar, FUChar : RETURN 4
     | FLong : RETURN 8
     | FReal : RETURN 8
     | FEnum, FSub : RETURN 4
     | FSet : RETURN SetWords(t) * 4
-    | FPtr, FOpenArr, FArray, FProc : RETURN 8
+    | FPtr, FOpenArr, FArray, FProc, FUStr : RETURN 8
     | FRecord, FClass :
         n := 0;
         f := fields;
@@ -1887,12 +1899,17 @@ PROCEDURE EqCheck (l, r: TypeIndex): BOOLEAN;
     IF (ClassOf(l) = ClLong) AND (ClassOf(r) = ClLong) THEN
       RETURN TRUE
     END;
+    IF (ClassOf(l) = ClLong) AND IsIntFamily(r) THEN RETURN TRUE END;
+    IF IsIntFamily(l) AND (ClassOf(r) = ClLong) THEN RETURN TRUE END;
     IF (ClassOf(l) = ClReal) AND (ClassOf(r) = ClReal) THEN
       RETURN TRUE
     END;
     IF (ClassOf(l) = ClChar) AND (ClassOf(r) = ClChar) THEN
       RETURN TRUE
     END;
+    IF (ClassOf(l) = ClUChar) AND (ClassOf(r) = ClUChar) THEN
+      RETURN TRUE
+    END;
     IF (ClassOf(l) = ClBool) AND (ClassOf(r) = ClBool) THEN
       RETURN TRUE
     END;
@@ -1911,12 +1928,17 @@ PROCEDURE OrdCheck (l, r: TypeIndex): BOOLEAN;
     IF (ClassOf(l) = ClLong) AND (ClassOf(r) = ClLong) THEN
       RETURN TRUE
     END;
+    IF (ClassOf(l) = ClLong) AND IsIntFamily(r) THEN RETURN TRUE END;
+    IF IsIntFamily(l) AND (ClassOf(r) = ClLong) THEN RETURN TRUE END;
     IF (ClassOf(l) = ClReal) AND (ClassOf(r) = ClReal) THEN
       RETURN TRUE
     END;
     IF (ClassOf(l) = ClChar) AND (ClassOf(r) = ClChar) THEN
       RETURN TRUE
     END;
+    IF (ClassOf(l) = ClUChar) AND (ClassOf(r) = ClUChar) THEN
+      RETURN TRUE
+    END;
     IF (ClassOf(l) = ClEnum) AND SameType(l, r) THEN RETURN TRUE END;
     RETURN FALSE
   END OrdCheck;
@@ -1995,6 +2017,7 @@ PROCEDURE Init;
     dNil := NewDesc(FNil, InvalidType);
     dAddr := NewDesc(FPtr, InvalidType);   (* ADDRESS: pointer-sized *)
     dLong := NewDesc(FLong, InvalidType);  (* LONGINT/LONGCARD: 64-bit *)
+    dUChar := NewDesc(FUChar, InvalidType);
     dBitset := NewSet(NewSubR(0, 15));     (* BITSET *)
     Predef("INTEGER", KindPredef, dInt);
     Predef("CARDINAL", KindPredef, dCard);
@@ -2008,6 +2031,7 @@ PROCEDURE Init;
     Predef("TRUE", KindConst, dBool);
     Predef("FALSE", KindConst, dBool);
     Predef("ADDRESS", KindPredef, dAddr);
+    Predef("UCHAR", KindPredef, dUChar);
     Predef("BITSET", KindPredef, dBitset);
     Predef("NIL", KindConst, dNil)
   END Init;

+ 1 - 0
compiler/src/compiler.frm

@@ -106,6 +106,7 @@ MODULE -->Grammar;
         | 231: Msg("opaque type outside definition")
         | 232: Msg("bad RETURN")
         | 233: Msg("invalid call")
+        | 234: Msg("invalid UTF-8 in U-literal")
         ELSE         Msg("Error: "); WriteInt(f, nr, 0);
         END
       END ErrText;

+ 7 - 0
compiler/tests/t_bad_overlong.mod

@@ -0,0 +1,7 @@
+MODULE TBadOverlong;
+VAR ExitCode : INTEGER;
+VAR u : UCHAR;
+BEGIN
+  u := U'À€';
+  ExitCode := 0
+END TBadOverlong.

+ 7 - 0
compiler/tests/t_bad_surrogate.mod

@@ -0,0 +1,7 @@
+MODULE TBadSurrogate;
+VAR ExitCode : INTEGER;
+VAR u : UCHAR;
+BEGIN
+  u := U'í €';
+  ExitCode := 0
+END TBadSurrogate.

+ 7 - 0
compiler/tests/t_bad_truncutf.mod

@@ -0,0 +1,7 @@
+MODULE TBadTruncUtf;
+VAR ExitCode : INTEGER;
+VAR u : UCHAR;
+BEGIN
+  u := U'â‚';
+  ExitCode := 0
+END TBadTruncUtf.

+ 7 - 0
compiler/tests/t_bad_utf8big.mod

@@ -0,0 +1,7 @@
+MODULE TBadUtf8Big;
+VAR ExitCode : INTEGER;
+VAR u : UCHAR;
+BEGIN
+  u := U'�';
+  ExitCode := 0
+END TBadUtf8Big.

+ 8 - 0
compiler/tests/t_trap_chr8.mod

@@ -0,0 +1,8 @@
+MODULE TTrapChr8;
+(* CHR8(U'😀') = 128512 > 255 -> range trap. *)
+VAR ExitCode : INTEGER;
+VAR c : CHAR;
+BEGIN
+  c := CHR8(U'😀');
+  ExitCode := 0
+END TTrapChr8.

+ 14 - 0
compiler/tests/t_unicode.mod

@@ -0,0 +1,14 @@
+MODULE TUnicode;
+(* Unicode: UCHAR, U-literal decoding, UCHR/CHR8/UORD.  Exit 31. *)
+VAR ExitCode : INTEGER;
+VAR u : UCHAR;
+VAR c : CHAR;
+BEGIN
+  u := U'é';                          (* U+00E9 = 233 *)
+  c := "A";
+  IF UORD(u) = 233 THEN ExitCode := ExitCode + 1 ELSE ExitCode := 100 END;
+  IF UORD(U'😀') = 128512 THEN ExitCode := ExitCode + 2 ELSE ExitCode := 100 END;
+  IF UORD(U'€') = 8364 THEN ExitCode := ExitCode + 4 ELSE ExitCode := 100 END;
+  IF U'é' > UCHR(c) THEN ExitCode := ExitCode + 8 ELSE ExitCode := 100 END;
+  IF CHR8(UCHR(c)) = c THEN ExitCode := ExitCode + 16 ELSE ExitCode := 100 END
+END TUnicode.

+ 12 - 6
docs/features.md

@@ -23,6 +23,8 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
 - ✅ `RECORD` (flat blobs, nested inline, array fields as pointers).
 - ✅ `SET OF` bool/char/subrange (multi-word masks).
 - ✅ `POINTER TO`, `NIL` as a real type.
+- ✅ `UCHAR` (32-bit codepoint) + `U'a'`/`U"…"` literals (strict
+  RFC3629 decode → 234 on bad bytes) and `UCHR`/`CHR8`/`UORD`.
 - ✅ Opaque `TYPE T;` + completion in the implementation.
 - ⏸ Enums in expressions/CASE labels, variant records, procedure
   types, `LONGINT` quads.
@@ -73,14 +75,18 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
 
 ## Error codes
 - ✅ 200/201/202, 210–222, 223 (declared), 230, 231, 232,
-  **233 `invalid call`**, plus `file:line:col` console format.
+  **233 `invalid call`**, **234 `invalid UTF-8 in U-literal`**, plus
+  `file:line:col` console format.
 
 ## Not started
-- ⏸ Clarion `CLASS` lowering (declared + checked only), Unicode
-  `UCHAR`/`UString` (locked design, no lexer/backing), `stdlib` /
-  `runtime/syslib` libraries, self-hosting fixpoint. The TopSpeed
-  legacy grammar (`TopSpeed-V3-M2.atg`) is a separate sidecar, not
-  merged.
+- ⏸ Clarion `CLASS` lowering (declared + checked only); Unicode
+  `UString` backing/assignment, `runtime/syslib/Utf8`, `TextIO.U*`
+  output. The TopSpeed legacy grammar (`TopSpeed-V3-M2.atg`) is a
+  separate sidecar, not merged.
+
+(Self-hosting is reached: `bootstrap/fixpoint.sh` builds a
+byte-identical compiler image; `stdlib/` + `runtime/syslib` are in
+use. See `docs/summary_selfhosting.md`.)
 
 Next: step 5 — Benjamin stdlib (first real use of separate
 compilation). Roadmap: `docs/plan.md`, `docs/blaise-phases-map.md`.

+ 16 - 9
docs/plan.md

@@ -104,25 +104,32 @@ self-hosting is the last phase. Hygiene from step 1: deterministic
 
 ## 6. Unicode — UCHAR / UString (LOCKED)
 
+Status: **increment 1 landed** (see `docs/summary_unicode.md`).
+Remaining: `UString` backing/assignment, `runtime/syslib/Utf8`,
+`TextIO.U*` output.
+
 - `UCHAR`: 32-bit codepoint `0..1114111` (QBE `w`). `CHAR` unchanged
-  (8-bit, `0..255`). Distinct types, no implicit mixing.
+  (8-bit, `0..255`). Distinct types, no implicit mixing. ✅
 - UString = `LONGCARD count (codepoints)` + `count × l codepoints`
-  (prior header rule, element = UCHAR, 4 bytes each).
+  (prior header rule, element = UCHAR, 4 bytes each). 🔄 (literals
+  emit the descriptor; assignment/backing pending)
 - Source files are UTF-8. Identifiers stay ASCII. Coco/R captures
   literal bytes; `LexUString` action strict-RFC3629-decodes
-  (no overlongs/surrogates/`>10FFFF`/truncation).
+  (no overlongs/surrogates/`>10FFFF`/truncation). ✅
 - Literals: `U'..'` / `U".."`; single codepoint `U'a'` → UCHAR,
-  longer/empty → UString. `'a'` ≠ `U'a'`.
-- Invalid UTF-8 in literal → hard lex error `234`, in `.LST`, no `.ssa`.
+  longer/empty → UString. `'a'` ≠ `U'a'`. ✅
+- Invalid UTF-8 in literal → hard lex error `234`. ✅ (reported in
+  `.LST` + console; note the image is still written, gate pending)
 - Conversions: `UCHR(c:CHAR):UCHAR` safe; `CHR8(u:UCHAR):CHAR` traps
-  unless `u ≤ 255`; `ORD` 8-bit; new `UORD(u):LONGCARD`.
+  unless `u ≤ 255`; `ORD` 8-bit; new `UORD(u):LONGCARD`. ✅
 - `runtime/syslib/Utf8` (`decode/Encode`, `MaxUTF8Length=4`,
   `MaxCodePoint=1114111`, cf. Benjamin `Utf8.def`): shared by the
-  lexer (decode) and output libs (encode).
+  lexer (decode) and output libs (encode). ⏸ (decode lives in
+  `QbeGen.DeclUStr` for now)
 - `TextIO.UWriteChar/UWriteString/UWriteLn`: encode to UTF-8, emit via
-  `SysIO` byte descriptors. Raw `WriteChar` stays byte-oriented.
+  `SysIO` byte descriptors. Raw `WriteChar` stays byte-oriented. ⏸
 
-## 7. Interactive ATG order (done through §6; UCHAR/UString pending)
+## 7. Interactive ATG order (done through §6, increment 1)
 
 1. `COMPILER M2` header + `CHARACTERS/IGNORE/COMMENTS/TOKENS` ✅
    (`0x`, `E`-exponents, `//` decided interactively; `ustring`

+ 54 - 0
docs/summary_unicode.md

@@ -0,0 +1,54 @@
+# Step: Unicode — increment 1 (UCHAR + U-literals)
+
+Tags `v3-unicode-1`. Suite **118/118**; self-hosting fixpoint still
+**OK** (`bootstrap/fixpoint.sh`, image 2,060,379 bytes).
+
+## Landed
+
+- **`UCHAR`** — 32-bit codepoint (`0..1114111`), QBE `w`, distinct
+  from `CHAR`/`INTEGER` (class `ClUChar`, form `FUChar`). Usable in
+  `VAR`/array declarations and designators (`ARRAY OF UCHAR`).
+- **`U'..'` / `U".."` literals** — Coco/R `ustring` token; the parser
+  action strict-RFC3629-decodes the bytes between the quotes
+  (`QbeGen.DeclUStr`). One codepoint → `UCHAR` immediate; otherwise a
+  **UString** descriptor `data $ustrN = { l count, w cp0, … }`
+  (`QbeGen.FlushUStrings`, emitted at `EndModule`).
+- **Strict decoding** — rejects overlongs, surrogates (`D800..DFFF`),
+  `>10FFFF`, and truncated sequences → semantic error
+  **234 `invalid UTF-8 in U-literal`** (new message in
+  `compiler.frm`).
+- **Conversions** — `UCHR(c:CHAR):UCHAR` (widening, identity);
+  `CHR8(u:UCHAR):CHAR` with an `l`-domain `0..255` range check
+  (traps via `$abort`, rc 134); `UORD(u):LONGCARD`.
+- **Comparisons** — `OrdCheck`/`EqCheck` accept `UCHAR` vs `UCHAR`,
+  and (generally useful) `LONGCARD` vs `INTEGER`; the existing
+  long-family comparison path widens the int side.
+
+## Tests (`compiler/run_tests.sh`)
+
+- `t_unicode.mod` (exit 31): `U'é'`, `U'😀'`, `U'€'`, `UCHR`,
+  `CHR8`, `UORD`.
+- `t_trap_chr8.mod`: `CHR8(U'😀')` → abort (rc 134).
+- `t_bad_overlong.mod`, `t_bad_surrogate.mod`, `t_bad_utf8big.mod`,
+  `t_bad_truncutf.mod`: expect `invalid UTF-8 in U-literal` (234).
+  These carry raw invalid bytes; `.LST` output is git-ignored.
+
+## Deferred to increment 2
+
+- `UString` backing/assignment (array-of-UCHAR view, whole-string
+  assignment, index/`HIGH`).
+- `runtime/syslib/Utf8` (`Decode`/`Encode`, `MaxUTF8Length=4`,
+  `MaxCodePoint=1114111`), shared by lexer and output libs.
+- `TextIO.UWriteChar/UWriteString/UWriteLn` (UTF-8 output via
+  `SysIO`); raw `WriteChar` stays byte-oriented.
+- Suppressing `.ssa` on error 234 (the image is still written).
+
+## Files
+
+`compiler/src/{SymTab.def,SymTab.mod}` (UCHAR type, ordinal/equality
+rules), `compiler/src/{QbeGen.def,QbeGen.mod}`
+(`DeclUStr`/`FlushUStrings`, pool + descriptor emission),
+`compiler/src/M2.atg` (`ustring` token + `Fact` branch +
+`UCHR`/`CHR8`/`UORD`), `compiler/src/compiler.frm` (message 234),
+`compiler/run_tests.sh`, `compiler/tests/t_unicode.mod`,
+`compiler/tests/t_trap_chr8.mod`, `compiler/tests/t_bad_*.mod`.