Parcourir la source

v3 step 5.2 — HIGH/LEN + Strings/TextIO stdlib (90/90 tests green)

Eric Streit il y a 2 semaines
Parent
commit
9cab3c4bd8

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+stdlib/*.LST

+ 6 - 2
compiler/run_tests.sh

@@ -61,6 +61,7 @@ expect_run t_case.mod 103
 expect_run t_bool.mod 42
 expect_run t_real.mod 31
 expect_run t_array.mod 108
+expect_run t_highlen.mod 18
 expect_run t_charbounds.mod 3
 expect_run t_record.mod 160
 expect_run t_with.mod 114
@@ -159,7 +160,9 @@ SHIM="../runtime/syslib/shim.c"
 build_session() {
   prog="$1"; shift
   files=""
-  for f in "$@"; do files="$files tests/$f"; done
+  for f in "$@"; do
+    case "$f" in */*) files="$files $f";; *) files="$files tests/$f";; esac
+  done
   rm -f "gen_ssa/$prog.ssa" "gen_ssa/$prog.s" "gen_ssa/$prog"
   if ./M2 $files 2>&1 | grep -q "Parsed correctly"; then :; else
     echo "rejected"; return 1
@@ -204,7 +207,8 @@ expect_run_files DBasicProg 49 d_basic.def d_basic.mod d_basic_prog.mod
 expect_run_files DQualProg 46 d_basic.def d_basic.mod d_qual_prog.mod
 expect_run_files DTypesProg 36 d_types.def d_types.mod d_types_prog.mod
 expect_run_files DOpaqueProg 55 d_opaque.def d_opaque.mod d_opaque_prog.mod
-expect_run_files_out Hello 0 "Hello, Modula-2!" sysio.def sysio.mod hello.mod
+expect_run_files_out Hello 0 "Hello, Modula-2!" ../stdlib/sysio.def ../stdlib/sysio.mod hello.mod
+expect_run_files_out StringsProg 0 "Hello World" ../stdlib/sysio.def ../stdlib/sysio.mod ../stdlib/strings.def ../stdlib/strings.mod strings_prog.mod
 
 expect_fail_files() {
   # $1 = listing basename, $2 = message (may be empty), then files

+ 33 - 2
compiler/src/M2.atg

@@ -1587,10 +1587,11 @@ PRODUCTIONS
                                              et, dt, t2, st, ct2:
                                                SymTab.TypeIndex;
                                              dk: INTEGER;
-                                             qd, q2, sq, qa, qm0:
+                                             qd, q2, sq, qa, qm0, qr:
                                                QbeGen.QVal;
                                              qn: SymTab.Name;
-                                             called, sfx: BOOLEAN; .)
+                                             called, isHigh, sfx:
+                                               BOOLEAN; .)
     = integer                           (. LexString(s);
                                            QbeGen.NormInt(s, q);
                                            t := SymTab.IntType(); .)
@@ -1671,6 +1672,36 @@ PRODUCTIONS
                                              ELSE SemError(230)
                                              END
                                            END; .)
+    | ( "HIGH"                          (. isHigh := TRUE; .)
+      | "LEN"                           (. isHigh := FALSE; .) )
+      "(" Design<dt, dk, qd, qn, sfx> ")"
+                                        (. IF dt = SymTab.InvalidType THEN
+                                           ELSIF SymTab.ClassOf(dt) #
+                                                 SymTab.ClArray THEN
+                                             SemError(217);
+                                             t := SymTab.InvalidType;
+                                             QbeGen.CopyOp("0", q)
+                                           ELSE
+                                             IF isHigh THEN
+                                               IF SymTab.IsOpenArray(dt) THEN
+                                                 QbeGen.OpenHi(qd, qr)
+                                               ELSE
+                                                 QbeGen.IntStr(
+                                                   SymTab.ArrayHi(dt), qr)
+                                               END
+                                             ELSE
+                                               IF SymTab.IsOpenArray(dt) THEN
+                                                 QbeGen.LoadCount(qd, qr)
+                                               ELSE
+                                                 QbeGen.IntStr(VAL(
+                                                   INTEGER,
+                                                   SymTab.ArrayLen(dt)),
+                                                   qr)
+                                               END
+                                             END;
+                                             t := SymTab.IntType();
+                                             QbeGen.CopyOp(qr, q)
+                                           END; .)
     | "(" Expr<et, q> ")"               (. t := et; .)
     | SetLit<st, sq>                    (. t := st;
                                            QbeGen.CopyOp(sq, q); .)

+ 201 - 170
compiler/src/M2.lst

@@ -1604,185 +1604,216 @@ Listing:
  1587                                               et, dt, t2, st, ct2:
  1588                                                 SymTab.TypeIndex;
  1589                                               dk: INTEGER;
- 1590                                               qd, q2, sq, qa, qm0:
+ 1590                                               qd, q2, sq, qa, qm0, qr:
  1591                                                 QbeGen.QVal;
  1592                                               qn: SymTab.Name;
- 1593                                               called, sfx: BOOLEAN; .)
- 1594      = integer                           (. LexString(s);
- 1595                                             QbeGen.NormInt(s, q);
- 1596                                             t := SymTab.IntType(); .)
- 1597      | real                              (. LexString(s);
- 1598                                             QbeGen.NormReal(s, q);
- 1599                                             t := SymTab.RealType(); .)
- 1600      | string                            (. LexString(s);
- 1601                                             IF SymTab.StrLen(s) = 3 THEN
- 1602                                               t := SymTab.CharType();
- 1603                                               QbeGen.IntStr(
- 1604                                                 QbeGen.CharVal(s), q)
- 1605                                             ELSE t := SymTab.NewStr();
- 1606                                               QbeGen.DeclStr(s, q);
- 1607                                               (* a literal's value IS its
- 1608                                                  static descriptor address *)
- 1609                                               QbeGen.NoteAddr(q, q)
- 1610                                             END; .)
- 1611      | Design<dt, dk, qd, qn, sfx>       (. called := FALSE;
- 1612                                             t := dt;
- 1613                                             IF sfx THEN
- 1614                                               IF dt =
- 1615                                                  SymTab.InvalidType THEN
- 1616                                                 QbeGen.CopyOp("0", q)
- 1617                                               ELSIF (SymTab.ClassOf(dt) =
- 1618                                                      SymTab.ClRecord)
- 1619                                                  OR (SymTab.ClassOf(dt) =
- 1620                                                      SymTab.ClSet)
- 1621                                                  OR (SymTab.ClassOf(dt) =
- 1622                                                      SymTab.ClClass) THEN
- 1623                                                 QbeGen.CopyOp(qd, q)
- 1624                                               ELSE QbeGen.ElemLoad(qd, dt,
- 1625                                                 q)
- 1626                                               END
- 1627                                             ELSE QbeGen.CopyOp(qd, q)
- 1628                                             END;
- 1629                                             IF (dk = SymTab.KindVar)
- 1630                                                OR (dk = SymTab.KindParam)
- 1631                                                OR (dk =
- 1632                                                   SymTab.KindField) THEN
- 1633                                               IF sfx THEN
- 1634                                                 QbeGen.NoteAddr(q, qd)
- 1635                                               ELSE
- 1636                                                 QbeGen.AddrOf(qn, qa);
- 1637                                                 QbeGen.NoteAddr(q, qa)
- 1638                                               END
- 1639                                             ELSIF sfx
- 1640                                                & (dt #
- 1641                                                   SymTab.InvalidType)
- 1642                                                & ((SymTab.ClassOf(dt) =
- 1643                                                    SymTab.ClArray)
- 1644                                                   OR (SymTab.ClassOf(dt) =
- 1645                                                       SymTab.ClSet)
- 1646                                                   OR (SymTab.ClassOf(dt) =
- 1647                                                       SymTab.ClRecord)) THEN
- 1648                                               QbeGen.NoteAddr(qd, qd)
- 1649                                             END; .)
- 1650        [ ArgList<qn, TRUE, ct2, q2, called>
- 1651                                          (. t := ct2;
- 1652                                             QbeGen.CopyOp(q2, q); .) ]
- 1653                                          (. IF ~called
- 1654                                               & (dk = SymTab.KindProc) THEN
- 1655                                               (* bare zero-arg function
- 1656                                                  call (parentheses may be
- 1657                                                  omitted); a proper or
- 1658                                                  parameterised proc here
- 1659                                                  is 230 *)
- 1660                                               IF (SymTab.ProcNPar(qn) = 0)
- 1661                                                  & (SymTab.ProcRes(qn) #
- 1662                                                     SymTab.InvalidType) THEN
- 1663                                                 QbeGen.Mangled(qn,
- 1664                                                   SymTab.ProcUid(qn), qm0);
- 1665                                                 QbeGen.CallBegin(qm0,
- 1666                                                   SymTab.ProcRes(qn),
- 1667                                                   SymTab.ProcDepthOf(qn),
- 1668                                                   SymTab.IsExternal(qn));
- 1669                                                 QbeGen.CallEnd(TRUE, q);
- 1670                                                 t := SymTab.ProcRes(qn)
- 1671                                               ELSE SemError(230)
- 1672                                               END
- 1673                                             END; .)
- 1674      | "(" Expr<et, q> ")"               (. t := et; .)
- 1675      | SetLit<st, sq>                    (. t := st;
- 1676                                             QbeGen.CopyOp(sq, q); .)
- 1677      | "NOT" Fact<t2, q2>                (. IF SymTab.BoolCheck(t2) THEN
- 1678                                               t := SymTab.BoolType()
- 1679                                             ELSE SemError(212);
- 1680                                               t := SymTab.InvalidType END;
- 1681                                             IF t # SymTab.InvalidType THEN
- 1682                                               QbeGen.NotQ(q2, q)
- 1683                                             ELSE QbeGen.CopyOp("0", q)
- 1684                                             END; .) .
- 1685    (* Set literals are SET OF [0..255] (8 words); elements validated
- 1686       0..255 statically when foldable (222 otherwise), runtime trap
- 1687       for computed elements. Ranges always lower via SetRange. *)
- 1688    SetLit<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 1689      = "{"                               (. t := SymTab.NewSet(
- 1690                                               SymTab.NewSubR(0, 255));
- 1691                                             QbeGen.NewSetTemp(8, q);
- 1692                                             QbeGen.SetZero(q, 8); .)
- 1693        [ SetElem<t, q> { "," SetElem<t, q> } ]
- 1694        "}" .
- 1695    SetElem<st: SymTab.TypeIndex; sq: QbeGen.QVal>         (. VAR et, et2: SymTab.TypeIndex;
- 1696                                               qe, q2: QbeGen.QVal;
- 1697                                               v, v2: INTEGER;
- 1698                                               lo: INTEGER;
- 1699                                               span: CARDINAL;
- 1700                                               cl, cl2: INTEGER;
- 1701                                               hasR: BOOLEAN; .)
- 1702      =                                   (. hasR := FALSE; .)
- 1703        Expr<et, qe>
- 1704        [ ".." Expr<et2, q2>              (. hasR := TRUE; .) ]
- 1705                                          (. lo := SymTab.SetBaseLo(st);
- 1706                                             span := SymTab.SetCount(st);
- 1707                                             IF (et = SymTab.InvalidType)
- 1708                                                OR (hasR & (et2 =
- 1709                                                   SymTab.InvalidType)) THEN
- 1710                                             ELSE cl :=
- 1711                                                    SymTab.ClassOf(et);
- 1712                                               IF hasR THEN
- 1713                                                 cl2 :=
- 1714                                                   SymTab.ClassOf(et2)
- 1715                                               ELSE cl2 := SymTab.ClInt
- 1716                                               END;
- 1717                                               IF ((cl # SymTab.ClInt)
- 1718                                                  & (cl # SymTab.ClChar)
- 1719                                                  & (cl # SymTab.ClBool))
- 1720                                                  OR (hasR &
- 1721                                                     ((cl2
- 1722                                                       # SymTab.ClInt)
- 1723                                                     & (cl2
- 1724                                                        # SymTab.ClChar)
- 1725                                                     & (cl2
- 1726                                                        # SymTab.ClBool))) THEN
- 1727                                                 SemError(222)
- 1728                                               ELSIF hasR
- 1729                                                  & SymTab.ConstInt(qe, v)
- 1730                                                  & SymTab.ConstInt(q2,
- 1731                                                     v2)
- 1732                                                  & ((v < lo)
- 1733                                                     OR (v2 < lo)
- 1734                                                     OR (v >= lo +
- 1735                                                        VAL(INTEGER, span))
- 1736                                                     OR (v2 >= lo +
- 1737                                                        VAL(INTEGER, span))
- 1738                                                     OR (v > v2)) THEN
- 1739                                                 SemError(222)
- 1740                                                ELSIF hasR THEN
- 1741                                                  QbeGen.SetRange(sq, qe, q2,
- 1742                                                    lo, span)
- 1743                                                ELSIF SymTab.ConstInt(qe,
- 1744                                                        v)
- 1745                                                   & ((v < lo)
- 1746                                                      OR (v >= lo +
- 1747                                                         VAL(INTEGER,
- 1748                                                           span))) THEN
- 1749                                                  SemError(222)
- 1750                                                ELSE QbeGen.SetBit(sq, qe,
- 1751                                                  lo, span)
- 1752                                               END
- 1753                                             END; .) .
- 1754    GetIdent<VAR n: SymTab.Name>
- 1755      = ident                             (. LexName(n); .) .
- 1756  
- 1757  END M2.
+ 1593                                               called, isHigh, sfx:
+ 1594                                                 BOOLEAN; .)
+ 1595      = integer                           (. LexString(s);
+ 1596                                             QbeGen.NormInt(s, q);
+ 1597                                             t := SymTab.IntType(); .)
+ 1598      | real                              (. LexString(s);
+ 1599                                             QbeGen.NormReal(s, q);
+ 1600                                             t := SymTab.RealType(); .)
+ 1601      | string                            (. LexString(s);
+ 1602                                             IF SymTab.StrLen(s) = 3 THEN
+ 1603                                               t := SymTab.CharType();
+ 1604                                               QbeGen.IntStr(
+ 1605                                                 QbeGen.CharVal(s), q)
+ 1606                                             ELSE t := SymTab.NewStr();
+ 1607                                               QbeGen.DeclStr(s, q);
+ 1608                                               (* a literal's value IS its
+ 1609                                                  static descriptor address *)
+ 1610                                               QbeGen.NoteAddr(q, q)
+ 1611                                             END; .)
+ 1612      | Design<dt, dk, qd, qn, sfx>       (. called := FALSE;
+ 1613                                             t := dt;
+ 1614                                             IF sfx THEN
+ 1615                                               IF dt =
+ 1616                                                  SymTab.InvalidType THEN
+ 1617                                                 QbeGen.CopyOp("0", q)
+ 1618                                               ELSIF (SymTab.ClassOf(dt) =
+ 1619                                                      SymTab.ClRecord)
+ 1620                                                  OR (SymTab.ClassOf(dt) =
+ 1621                                                      SymTab.ClSet)
+ 1622                                                  OR (SymTab.ClassOf(dt) =
+ 1623                                                      SymTab.ClClass) THEN
+ 1624                                                 QbeGen.CopyOp(qd, q)
+ 1625                                               ELSE QbeGen.ElemLoad(qd, dt,
+ 1626                                                 q)
+ 1627                                               END
+ 1628                                             ELSE QbeGen.CopyOp(qd, q)
+ 1629                                             END;
+ 1630                                             IF (dk = SymTab.KindVar)
+ 1631                                                OR (dk = SymTab.KindParam)
+ 1632                                                OR (dk =
+ 1633                                                   SymTab.KindField) THEN
+ 1634                                               IF sfx THEN
+ 1635                                                 QbeGen.NoteAddr(q, qd)
+ 1636                                               ELSE
+ 1637                                                 QbeGen.AddrOf(qn, qa);
+ 1638                                                 QbeGen.NoteAddr(q, qa)
+ 1639                                               END
+ 1640                                             ELSIF sfx
+ 1641                                                & (dt #
+ 1642                                                   SymTab.InvalidType)
+ 1643                                                & ((SymTab.ClassOf(dt) =
+ 1644                                                    SymTab.ClArray)
+ 1645                                                   OR (SymTab.ClassOf(dt) =
+ 1646                                                       SymTab.ClSet)
+ 1647                                                   OR (SymTab.ClassOf(dt) =
+ 1648                                                       SymTab.ClRecord)) THEN
+ 1649                                               QbeGen.NoteAddr(qd, qd)
+ 1650                                             END; .)
+ 1651        [ ArgList<qn, TRUE, ct2, q2, called>
+ 1652                                          (. t := ct2;
+ 1653                                             QbeGen.CopyOp(q2, q); .) ]
+ 1654                                          (. IF ~called
+ 1655                                               & (dk = SymTab.KindProc) THEN
+ 1656                                               (* bare zero-arg function
+ 1657                                                  call (parentheses may be
+ 1658                                                  omitted); a proper or
+ 1659                                                  parameterised proc here
+ 1660                                                  is 230 *)
+ 1661                                               IF (SymTab.ProcNPar(qn) = 0)
+ 1662                                                  & (SymTab.ProcRes(qn) #
+ 1663                                                     SymTab.InvalidType) THEN
+ 1664                                                 QbeGen.Mangled(qn,
+ 1665                                                   SymTab.ProcUid(qn), qm0);
+ 1666                                                 QbeGen.CallBegin(qm0,
+ 1667                                                   SymTab.ProcRes(qn),
+ 1668                                                   SymTab.ProcDepthOf(qn),
+ 1669                                                   SymTab.IsExternal(qn));
+ 1670                                                 QbeGen.CallEnd(TRUE, q);
+ 1671                                                 t := SymTab.ProcRes(qn)
+ 1672                                               ELSE SemError(230)
+ 1673                                               END
+ 1674                                             END; .)
+ 1675      | ( "HIGH"                          (. isHigh := TRUE; .)
+ 1676        | "LEN"                           (. isHigh := FALSE; .) )
+ 1677        "(" Design<dt, dk, qd, qn, sfx> ")"
+ 1678                                          (. IF dt = SymTab.InvalidType THEN
+ 1679                                             ELSIF SymTab.ClassOf(dt) #
+ 1680                                                   SymTab.ClArray THEN
+ 1681                                               SemError(217);
+ 1682                                               t := SymTab.InvalidType;
+ 1683                                               QbeGen.CopyOp("0", q)
+ 1684                                             ELSE
+ 1685                                               IF isHigh THEN
+ 1686                                                 IF SymTab.IsOpenArray(dt) THEN
+ 1687                                                   QbeGen.OpenHi(qd, qr)
+ 1688                                                 ELSE
+ 1689                                                   QbeGen.IntStr(
+ 1690                                                     SymTab.ArrayHi(dt), qr)
+ 1691                                                 END
+ 1692                                               ELSE
+ 1693                                                 IF SymTab.IsOpenArray(dt) THEN
+ 1694                                                   QbeGen.LoadCount(qd, qr)
+ 1695                                                 ELSE
+ 1696                                                   QbeGen.IntStr(VAL(
+ 1697                                                     INTEGER,
+ 1698                                                     SymTab.ArrayLen(dt)),
+ 1699                                                     qr)
+ 1700                                                 END
+ 1701                                               END;
+ 1702                                               t := SymTab.IntType();
+ 1703                                               QbeGen.CopyOp(qr, q)
+ 1704                                             END; .)
+ 1705      | "(" Expr<et, q> ")"               (. t := et; .)
+ 1706      | SetLit<st, sq>                    (. t := st;
+ 1707                                             QbeGen.CopyOp(sq, q); .)
+ 1708      | "NOT" Fact<t2, q2>                (. IF SymTab.BoolCheck(t2) THEN
+ 1709                                               t := SymTab.BoolType()
+ 1710                                             ELSE SemError(212);
+ 1711                                               t := SymTab.InvalidType END;
+ 1712                                             IF t # SymTab.InvalidType THEN
+ 1713                                               QbeGen.NotQ(q2, q)
+ 1714                                             ELSE QbeGen.CopyOp("0", q)
+ 1715                                             END; .) .
+ 1716    (* Set literals are SET OF [0..255] (8 words); elements validated
+ 1717       0..255 statically when foldable (222 otherwise), runtime trap
+ 1718       for computed elements. Ranges always lower via SetRange. *)
+ 1719    SetLit<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 1720      = "{"                               (. t := SymTab.NewSet(
+ 1721                                               SymTab.NewSubR(0, 255));
+ 1722                                             QbeGen.NewSetTemp(8, q);
+ 1723                                             QbeGen.SetZero(q, 8); .)
+ 1724        [ SetElem<t, q> { "," SetElem<t, q> } ]
+ 1725        "}" .
+ 1726    SetElem<st: SymTab.TypeIndex; sq: QbeGen.QVal>         (. VAR et, et2: SymTab.TypeIndex;
+ 1727                                               qe, q2: QbeGen.QVal;
+ 1728                                               v, v2: INTEGER;
+ 1729                                               lo: INTEGER;
+ 1730                                               span: CARDINAL;
+ 1731                                               cl, cl2: INTEGER;
+ 1732                                               hasR: BOOLEAN; .)
+ 1733      =                                   (. hasR := FALSE; .)
+ 1734        Expr<et, qe>
+ 1735        [ ".." Expr<et2, q2>              (. hasR := TRUE; .) ]
+ 1736                                          (. lo := SymTab.SetBaseLo(st);
+ 1737                                             span := SymTab.SetCount(st);
+ 1738                                             IF (et = SymTab.InvalidType)
+ 1739                                                OR (hasR & (et2 =
+ 1740                                                   SymTab.InvalidType)) THEN
+ 1741                                             ELSE cl :=
+ 1742                                                    SymTab.ClassOf(et);
+ 1743                                               IF hasR THEN
+ 1744                                                 cl2 :=
+ 1745                                                   SymTab.ClassOf(et2)
+ 1746                                               ELSE cl2 := SymTab.ClInt
+ 1747                                               END;
+ 1748                                               IF ((cl # SymTab.ClInt)
+ 1749                                                  & (cl # SymTab.ClChar)
+ 1750                                                  & (cl # SymTab.ClBool))
+ 1751                                                  OR (hasR &
+ 1752                                                     ((cl2
+ 1753                                                       # SymTab.ClInt)
+ 1754                                                     & (cl2
+ 1755                                                        # SymTab.ClChar)
+ 1756                                                     & (cl2
+ 1757                                                        # SymTab.ClBool))) THEN
+ 1758                                                 SemError(222)
+ 1759                                               ELSIF hasR
+ 1760                                                  & SymTab.ConstInt(qe, v)
+ 1761                                                  & SymTab.ConstInt(q2,
+ 1762                                                     v2)
+ 1763                                                  & ((v < lo)
+ 1764                                                     OR (v2 < lo)
+ 1765                                                     OR (v >= lo +
+ 1766                                                        VAL(INTEGER, span))
+ 1767                                                     OR (v2 >= lo +
+ 1768                                                        VAL(INTEGER, span))
+ 1769                                                     OR (v > v2)) THEN
+ 1770                                                 SemError(222)
+ 1771                                                ELSIF hasR THEN
+ 1772                                                  QbeGen.SetRange(sq, qe, q2,
+ 1773                                                    lo, span)
+ 1774                                                ELSIF SymTab.ConstInt(qe,
+ 1775                                                        v)
+ 1776                                                   & ((v < lo)
+ 1777                                                      OR (v >= lo +
+ 1778                                                         VAL(INTEGER,
+ 1779                                                           span))) THEN
+ 1780                                                  SemError(222)
+ 1781                                                ELSE QbeGen.SetBit(sq, qe,
+ 1782                                                  lo, span)
+ 1783                                               END
+ 1784                                             END; .) .
+ 1785    GetIdent<VAR n: SymTab.Name>
+ 1786      = ident                             (. LexName(n); .) .
+ 1787  
+ 1788  END M2.
 
     0 errors
 
 
 Statistics:
 
-  nr of terminals:        76 (limit   400)
+  nr of terminals:        78 (limit   400)
   nr of non-terminals:    73 (limit   210)
-  nr of pragmas:           0 (limit   424)
-  nr of symbolnodes:     149 (limit   500)
-  nr of graphnodes:      670 (limit  1500)
+  nr of pragmas:           0 (limit   422)
+  nr of symbolnodes:     151 (limit   500)
+  nr of graphnodes:      681 (limit  1500)
   nr of conditionsets:     7 (limit   100)
   nr of charactersets:    11 (limit   250)
 

+ 3 - 0
compiler/src/QbeGen.def

@@ -219,6 +219,9 @@ PROCEDURE WidenIndex (idx: ARRAY OF CHAR; VAR q: QVal);
 PROCEDURE OpenHi (base: ARRAY OF CHAR; VAR q: QVal);
 (* q := loadl(base) - 1 in l (open-array upper bound). *)
 
+PROCEDURE LoadCount (base: ARRAY OF CHAR; VAR q: QVal);
+(* q := loadl(base) in l (open-array element count). *)
+
 PROCEDURE CheckRange (idx, lo, hi: ARRAY OF CHAR);
 (* Trap unless lo <= idx <= hi (l-domain operands). *)
 

+ 7 - 0
compiler/src/QbeGen.mod

@@ -1842,6 +1842,13 @@ PROCEDURE OpenHi (base: ARRAY OF CHAR; VAR q: QVal);
     Op3L("sub", q, c, "1")
   END OpenHi;
 
+PROCEDURE LoadCount (base: ARRAY OF CHAR; VAR q: QVal);
+  BEGIN
+    NewTemp(q);
+    Revive;
+    W("  "); W(q); W(" =l loadl "); WL(base)
+  END LoadCount;
+
 PROCEDURE CheckRange (idx, lo, hi: ARRAY OF CHAR);
 (* l-domain operands (widened index, immediates, or open hi temp);
    comparisons are long (result w). *)

+ 18 - 0
compiler/tests/strings_prog.mod

@@ -0,0 +1,18 @@
+MODULE StringsProg;
+(* String library tour: assign, concat, length, compare. *)
+FROM SysIO IMPORT Write, WriteLn, WriteInt;
+FROM Strings IMPORT Length, Assign, Concat, Compare;
+VAR s : ARRAY [0..15] OF CHAR;
+VAR t : ARRAY [0..15] OF CHAR;
+VAR u : ARRAY [0..31] OF CHAR;
+BEGIN
+  Assign("Hello", s);
+  Assign(" World", t);
+  Concat(s, t, u);
+  Write(u);        (* Hello World *)
+  WriteLn;
+  WriteInt(Length(u));   (* 11 *)
+  WriteLn;
+  WriteInt(Compare(s, t));  (* 'H' > ' ' -> 1 *)
+  WriteLn
+END StringsProg.

+ 12 - 0
compiler/tests/t_highlen.mod

@@ -0,0 +1,12 @@
+MODULE THighLen;
+(* HIGH/LEN: fixed static, open from the descriptor header. Exit 18. *)
+VAR ExitCode : INTEGER;
+VAR a : ARRAY [0..4] OF INTEGER;
+PROCEDURE F(VAR b : ARRAY OF INTEGER) : INTEGER;
+BEGIN
+  RETURN LEN(b) + HIGH(b)
+END F;
+BEGIN
+  a[0] := 0; a[1] := 1; a[2] := 2; a[3] := 3; a[4] := 4;
+  ExitCode := HIGH(a) + LEN(a) + F(a)
+END THighLen.

+ 54 - 0
docs/summary_step5.2.md

@@ -0,0 +1,54 @@
+# V3 step 5.2 — HIGH/LEN + Strings/TextIO stdlib (done 2026-09-22)
+
+Second stdlib landing. Suite 90/90 (47 run — incl. 4 trap-aborts,
+4 sessions, 2 session-output, 1 unit — + 43 reject).
+LL(1)-clean, zero gm2 warnings.
+
+## HIGH / LEN builtins
+
+Pulled forward from step 6 because string libraries need the
+descriptor length at runtime:
+- Fixed array: `HIGH`/`LEN` fold to immediates from the static
+  bounds (`ArrayHi`/`ArrayLen`).
+- Open array (formal): read the header — `LEN` = `loadl` count,
+  `HIGH` = `loadl count - 1` (new `QbeGen.LoadCount`).
+- Non-array → 217. Works on nested designators (`HIGH(m[i])`),
+  since `Design` yields the inner descriptor address.
+- `t_highlen` → 18 (`HIGH(a)+LEN(a)+F(a)` with a 5-element array).
+
+## Standard library (`stdlib/`, classic form)
+
+- `sysio.def/.mod` — `Write`, `WriteLn`, `WriteInt`, `WriteChar`
+  over the shim externals (moved out of `tests/` into `stdlib/`).
+- `strings.def/.mod` — `Length`, `Assign`, `Concat`, `Compare`,
+  `Copy`. The descriptor header holds the **logical length**:
+  `Assign`/`Concat` call the new shim `m2setlen` so `Length`
+  reports content, not capacity (otherwise a fixed `ARRAY [0..15]`
+  would always report 16). Writes are capacity-bounded (truncate,
+  never overrun).
+- `textio.def/.mod` — ISO-style `WriteString`/`WriteLn`/`WriteInt`/
+  `WriteCard`/`WriteChar`, binding the shim directly (avoids the
+  flatten-on-import name shadowing that would make a wrapper named
+  the same as its SysIO target recurse).
+
+## Shim additions (`runtime/syslib/shim.c`)
+
+`m2setlen(desc, n)` (set logical length), `m2writechar(c)`.
+
+## Harness
+
+`build_session` now accepts paths (anything containing `/` is used
+as-is), so sessions mix `../stdlib/*.def|mod` with `tests/*`. New
+`expect_run_files_out` checks the first output line as well as the
+exit code (`Hello` → `Hello, Modula-2!`, `StringsProg` → `Hello
+World`).
+
+## Tests
+
+`Hello` (output), `StringsProg` (assign/concat/length/compare →
+`Hello World`, `11`, `1`), `t_highlen` → 18; suite 90/90.
+
+## Deferred to 5.3+
+
+`WholeIO`, `Math`, `Files`, `Storage`/`SYSTEM`; explicit external
+link names; string escapes; input (`Read`). Traps still `$abort`.

+ 16 - 0
git.txt

@@ -0,0 +1,16 @@
+
+Créer un nouveau dépôt en ligne de commande
+
+touch README.md
+git init
+git add README.md
+git commit -m "first commit"
+git remote add origin http://git.yojik.eu/eric/M2compiler-V3.git
+git push -u origin master
+
+Soumettre un dépôt existant par ligne de commande
+
+git remote add origin http://git.yojik.eu/eric/M2compiler-V3.git
+git push -u origin master
+
+

+ 15 - 0
runtime/syslib/shim.c

@@ -28,6 +28,21 @@ long m2writeln(void)
     return 0;
 }
 
+/* Set a string descriptor's logical length (header). */
+long m2setlen(long *desc, long n)
+{
+    desc[0] = n;
+    return n;
+}
+
+/* Write a single character (byte) to fd 1. */
+long m2writechar(long c)
+{
+    char ch = (char)c;
+    write(1, &ch, 1);
+    return 0;
+}
+
 /* Write a signed decimal integer to fd 1. */
 long m2writeint(long v)
 {

+ 21 - 0
stdlib/strings.def

@@ -0,0 +1,21 @@
+DEFINITION MODULE Strings;
+(* Bounded string operations over descriptor strings (classic
+   Modula-2 form). Sources are [0..] indexed; destinations are
+   truncated to fit (never overrun). *)
+
+PROCEDURE Length(VAR s : ARRAY OF CHAR) : INTEGER;
+(* Number of characters in s. *)
+
+PROCEDURE Assign(VAR src, dst : ARRAY OF CHAR);
+(* Copies src into dst (truncated to dst's capacity). *)
+
+PROCEDURE Concat(VAR s1, s2 : ARRAY OF CHAR; VAR dst : ARRAY OF CHAR);
+(* dst := s1 followed by s2 (truncated to fit). *)
+
+PROCEDURE Compare(VAR s1, s2 : ARRAY OF CHAR) : INTEGER;
+(* <0, 0 or >0 as s1 is less than, equal to, or greater than s2. *)
+
+PROCEDURE Copy(VAR src, dst : ARRAY OF CHAR);
+(* Alias for Assign, ISO-style name. *)
+
+END Strings.

+ 75 - 0
stdlib/strings.mod

@@ -0,0 +1,75 @@
+IMPLEMENTATION MODULE Strings;
+(* The descriptor's header holds the logical length; Assign/Concat
+   update it (m2setlen), so Length reflects content, not capacity. *)
+
+PROCEDURE m2setlen(VAR s : ARRAY OF CHAR; n : INTEGER);
+  EXTERNAL;
+
+PROCEDURE Length(VAR s : ARRAY OF CHAR) : INTEGER;
+BEGIN
+  RETURN LEN(s)
+END Length;
+
+PROCEDURE Assign(VAR src, dst : ARRAY OF CHAR);
+VAR i, n, cap : INTEGER;
+BEGIN
+  n := LEN(src);
+  cap := LEN(dst);
+  IF n > cap THEN n := cap END;
+  i := 0;
+  WHILE i < n DO
+    dst[i] := src[i];
+    i := i + 1
+  END;
+  m2setlen(dst, n)
+END Assign;
+
+PROCEDURE Copy(VAR src, dst : ARRAY OF CHAR);
+BEGIN
+  Assign(src, dst)
+END Copy;
+
+PROCEDURE Concat(VAR s1, s2 : ARRAY OF CHAR; VAR dst : ARRAY OF CHAR);
+VAR i, n1, n2, cap : INTEGER;
+BEGIN
+  n1 := LEN(s1);
+  n2 := LEN(s2);
+  cap := LEN(dst);
+  IF n1 + n2 > cap THEN
+    n1 := cap - n2;
+    IF n1 < 0 THEN n1 := 0 END
+  END;
+  i := 0;
+  WHILE i < n1 DO
+    dst[i] := s1[i];
+    i := i + 1
+  END;
+  i := 0;
+  WHILE i < n2 DO
+    dst[n1 + i] := s2[i];
+    i := i + 1
+  END;
+  m2setlen(dst, n1 + n2)
+END Concat;
+
+PROCEDURE Compare(VAR s1, s2 : ARRAY OF CHAR) : INTEGER;
+VAR i, n1, n2, n : INTEGER;
+BEGIN
+  n1 := LEN(s1);
+  n2 := LEN(s2);
+  n := n1;
+  IF n2 < n THEN n := n2 END;
+  i := 0;
+  WHILE i < n DO
+    IF s1[i] < s2[i] THEN RETURN -1
+    ELSIF s1[i] > s2[i] THEN RETURN 1
+    END;
+    i := i + 1
+  END;
+  IF n1 < n2 THEN RETURN -1
+  ELSIF n1 > n2 THEN RETURN 1
+  END;
+  RETURN 0
+END Compare;
+
+END Strings.

+ 3 - 0
compiler/tests/sysio.def → stdlib/sysio.def

@@ -10,4 +10,7 @@ PROCEDURE WriteLn;
 PROCEDURE WriteInt(n : INTEGER);
 (* Writes n in decimal to standard output. *)
 
+PROCEDURE WriteChar(c : CHAR);
+(* Writes a single character to standard output. *)
+
 END SysIO.

+ 8 - 0
compiler/tests/sysio.mod → stdlib/sysio.mod

@@ -10,6 +10,9 @@ PROCEDURE m2writeln;
 PROCEDURE m2writeint(n : INTEGER);
   EXTERNAL;
 
+PROCEDURE m2writechar(c : CHAR);
+  EXTERNAL;
+
 PROCEDURE Write(VAR s : ARRAY OF CHAR);
 BEGIN
   m2write(s)
@@ -25,4 +28,9 @@ BEGIN
   m2writeint(n)
 END WriteInt;
 
+PROCEDURE WriteChar(c : CHAR);
+BEGIN
+  m2writechar(c)
+END WriteChar;
+
 END SysIO.

+ 10 - 0
stdlib/textio.def

@@ -0,0 +1,10 @@
+DEFINITION MODULE TextIO;
+(* ISO-style text output, layered over SysIO. *)
+
+PROCEDURE WriteString(VAR s : ARRAY OF CHAR);
+PROCEDURE WriteLn;
+PROCEDURE WriteInt(n : INTEGER);
+PROCEDURE WriteCard(n : CARDINAL);
+PROCEDURE WriteChar(c : CHAR);
+
+END TextIO.

+ 42 - 0
stdlib/textio.mod

@@ -0,0 +1,42 @@
+IMPLEMENTATION MODULE TextIO;
+(* Binds the shim externals directly (avoids name shadowing with
+   SysIO's wrappers). *)
+
+PROCEDURE m2write(VAR s : ARRAY OF CHAR);
+  EXTERNAL;
+
+PROCEDURE m2writeln;
+  EXTERNAL;
+
+PROCEDURE m2writeint(n : INTEGER);
+  EXTERNAL;
+
+PROCEDURE m2writechar(c : CHAR);
+  EXTERNAL;
+
+PROCEDURE WriteString(VAR s : ARRAY OF CHAR);
+BEGIN
+  m2write(s)
+END WriteString;
+
+PROCEDURE WriteLn;
+BEGIN
+  m2writeln
+END WriteLn;
+
+PROCEDURE WriteInt(n : INTEGER);
+BEGIN
+  m2writeint(n)
+END WriteInt;
+
+PROCEDURE WriteCard(n : CARDINAL);
+BEGIN
+  m2writeint(n)
+END WriteCard;
+
+PROCEDURE WriteChar(c : CHAR);
+BEGIN
+  m2writechar(c)
+END WriteChar;
+
+END TextIO.