Parcourir la source

stdlib/lang: CHAR-array + coverage + ISO ConvResults

- t_charcat: verifies ARRAY OF CHAR '+' (array+array, array+CHAR,
  CHAR+array, chained) -- already worked via StrCat, now tested (15).
- Conversions: StrToInt/StrToCard/StrToReal return an ISO
  ConvResults (strAllRight/strOutOfRange/strWrongFormat/strEmpty);
  overflow is detected before the 32-bit wrap (V3 has no VAL(LONGINT)
  or long->int narrowing). BOOLEAN shortcuts kept as
  IntVal/CardVal/RealVal; ConvProg + showcase14 updated.
- convresults_prog (127) covers every status; suite 139/139.
- Fixpoint OK (2,156,095 bytes).
Eric Streit il y a 1 semaine
Parent
commit
5e2056f6be

+ 2 - 0
compiler/run_tests.sh

@@ -96,6 +96,7 @@ expect_fail t_opaqueptr.mod ""
 expect_run t_enumdecl.mod 0
 expect_run t_enum.mod 15
 expect_run t_strcat.mod 63
+expect_run t_charcat.mod 15
 expect_run t_variant.mod 31
 expect_run t_ustrassign.mod 63
 expect_run t_ustrformal.mod 3
@@ -236,6 +237,7 @@ expect_run_files_out Stdlib2Prog 42 "stdlib2 n=42" ../stdlib/sysio.def ../stdlib
 expect_run_files MathProg 12 ../stdlib/sysio.def ../stdlib/sysio.mod ../stdlib/math.def ../stdlib/math.mod math_prog.mod
 expect_run_files Utf8Prog 127 ../runtime/syslib/Utf8.def ../runtime/syslib/Utf8.mod utf8_prog.mod
 expect_run_files ConvProg 127 ../stdlib/conversions.def ../stdlib/conversions.mod conv_prog.mod
+expect_run_files ConvResultsProg 127 ../stdlib/conversions.def ../stdlib/conversions.mod convresults_prog.mod
 expect_run_files TStrEq 15 t_streq.mod
 expect_run_files ProgArgsProg 7 ../runtime/syslib/SysShim.def ../runtime/syslib/SysShim.mod ../stdlib/programargs.def ../stdlib/programargs.mod progargs_prog.mod
 expect_run_files_out UTextProg 42 "héllo" ../runtime/syslib/Utf8.def ../runtime/syslib/Utf8.mod ../stdlib/textio.def ../stdlib/textio.mod utext_prog.mod

+ 7 - 6
compiler/tests/conv_prog.mod

@@ -1,5 +1,6 @@
 MODULE ConvProg;
-(* Conversions: Int/Card/Real <-> string round-trips.  Exit 127. *)
+(* Conversions: Int/Card/Real <-> string round-trips (BOOLEAN
+   shortcuts). Exit 127. *)
 IMPORT Conversions;
 VAR ExitCode : INTEGER;
 VAR s : ARRAY [0 .. 31] OF CHAR;
@@ -10,19 +11,19 @@ BEGIN
   Conversions.IntToStr(-1234, s);
   IF s[0] = "-" THEN ExitCode := ExitCode + 1 ELSE ExitCode := 100 END;
   IF s[1] = "1" THEN ExitCode := ExitCode + 2 ELSE ExitCode := 100 END;
-  IF Conversions.StrToInt("42", n) THEN
+  IF Conversions.IntVal("42", n) THEN
     IF n = 42 THEN ExitCode := ExitCode + 4 END
   ELSE ExitCode := 100 END;
-  IF Conversions.StrToInt("-7", n) THEN
+  IF Conversions.IntVal("-7", n) THEN
     IF n = -7 THEN ExitCode := ExitCode + 8 END
   ELSE ExitCode := 100 END;
-  IF NOT Conversions.StrToInt("4x", n) THEN
+  IF NOT Conversions.IntVal("4x", n) THEN
     ExitCode := ExitCode + 16
   ELSE ExitCode := 100 END;
-  IF Conversions.StrToCard("99", c) THEN
+  IF Conversions.CardVal("99", c) THEN
     IF c = 99 THEN ExitCode := ExitCode + 32 END
   ELSE ExitCode := 100 END;
-  IF Conversions.StrToReal("3.5", x) THEN
+  IF Conversions.RealVal("3.5", x) THEN
     IF x = 3.5 THEN ExitCode := ExitCode + 64 END
   ELSE ExitCode := 100 END
 END ConvProg.

+ 29 - 0
compiler/tests/convresults_prog.mod

@@ -0,0 +1,29 @@
+MODULE ConvResultsProg;
+(* ISO-style ConvResults status codes from Conversions. Exit 127. *)
+IMPORT Conversions;
+FROM Conversions IMPORT strAllRight, strOutOfRange, strWrongFormat,
+                            strEmpty;
+VAR ExitCode : INTEGER;
+VAR n : INTEGER;
+VAR c : CARDINAL;
+VAR x : REAL;
+VAR r : Conversions.ConvResults;
+BEGIN
+  r := Conversions.StrToInt("42", n);
+  IF (r = strAllRight) AND (n = 42) THEN ExitCode := ExitCode + 1 END;
+  r := Conversions.StrToInt("-7", n);
+  IF (r = strAllRight) AND (n = -7) THEN ExitCode := ExitCode + 2 END;
+  r := Conversions.StrToInt("4x", n);
+  IF r = strWrongFormat THEN ExitCode := ExitCode + 4 END;
+  r := Conversions.StrToInt("", n);
+  IF r = strEmpty THEN ExitCode := ExitCode + 8 END;
+  r := Conversions.StrToInt("99999999999", n);
+  IF r = strOutOfRange THEN ExitCode := ExitCode + 16 END;
+
+  IF Conversions.StrToCard("99", c) = strAllRight THEN
+    IF c = 99 THEN ExitCode := ExitCode + 32 END
+  END;
+  IF Conversions.StrToReal("3.5", x) = strAllRight THEN
+    IF x = 3.5 THEN ExitCode := ExitCode + 64 END
+  END
+END ConvResultsProg.

+ 2 - 2
compiler/tests/showcase14.mod

@@ -43,8 +43,8 @@ BEGIN
   IF ProgramArgs.ArgCount() = 0 THEN Bump(5) END;
 
   (* Conversions: integer and real round-trips *)
-  IF Conversions.StrToInt("-56", m) AND (m = -56) THEN Bump(3) END;
-  IF Conversions.StrToReal("3.5", x) AND (x = 3.5) THEN Bump(3) END;
+  IF Conversions.IntVal("-56", m) AND (m = -56) THEN Bump(3) END;
+  IF Conversions.RealVal("3.5", x) AND (x = 3.5) THEN Bump(3) END;
 
   (* IOChan demo goes to stderr (stdio buffering would otherwise
      reorder it against the unbuffered shim writes) *)

+ 19 - 0
compiler/tests/t_charcat.mod

@@ -0,0 +1,19 @@
+MODULE TCharCat;
+(* String concatenation `+` for CHAR arrays, mixed with literals and
+   CHAR operands, plus chaining. Exit 7. *)
+VAR ExitCode : INTEGER;
+VAR a, b, c : ARRAY [0 .. 31] OF CHAR;
+BEGIN
+  a := "xy"; b := "zz";
+  c := a + b;
+  IF c = "xyzz" THEN ExitCode := ExitCode + 1 ELSE ExitCode := 100 END;
+
+  c := a + "c";                     (* array + CHAR *)
+  IF c = "xyc" THEN ExitCode := ExitCode + 2 ELSE ExitCode := 101 END;
+
+  c := "x" + a;                     (* CHAR + array *)
+  IF c = "xxy" THEN ExitCode := ExitCode + 4 ELSE ExitCode := 102 END;
+
+  c := a + a + a;                   (* chained *)
+  IF c = "xyxyxy" THEN ExitCode := ExitCode + 8 ELSE ExitCode := 103 END
+END TCharCat.

+ 10 - 9
docs/features.md

@@ -1,4 +1,4 @@
-# m2compiler-V3 — feature status (at `v3-polish-ustr-iochan`, 137/137 green)
+# m2compiler-V3 — feature status (at `v3-charcat-convresults`, 139/139 green)
 
 Pipeline: Coco/R `M2.atg` (1748 lines, 73 productions) → `gm2`-built
 `M2` → QBE `.ssa` → `qbe` → `cc` → run. `SymTab.mod` 1623 lines,
@@ -57,10 +57,10 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
   `AND`/`OR`/`NOT`, `IN`, set ops (`+ - * /` = union/diff/inter/symdiff),
   pointer `=`/`#`, int→real conversion, `^` deref, indexing, field
   access, qualified `L.x`/`L.P`.
-- ✅ String `=`/`#` (content), string `+` (concatenation, incl. a
-  `CHAR` operand), string assignment (content copy, truncated to the
-  array capacity, NUL-terminated), `LEN`/`LENGTH` of arrays and string
-  literals.
+- ✅ String `=`/`#` (content), string `+` (concatenation for literals,
+  `CHAR` arrays and `CHAR` operands), string assignment (content copy,
+  truncated to the array capacity, NUL-terminated), `LEN`/`LENGTH` of
+  arrays and string literals. UString `+`/`LEN` likewise.
 - ✅ `NIL` comparisons/assignment.
 - ✅ `VAL`/`ADR`/`TSIZE`/`SIZE`/`HIGH`/`LEN` work. ⏸ Result suffixes
   (`F()^`).
@@ -96,16 +96,17 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
 - ✅ `runtime/syslib`: `SysShim`, `FileIO`, `Utf8` (UTF-8 codec).
 - ✅ `stdlib`: `SysIO`, `TextIO` (+ UTF-8 `U*`), `WholeIO`, `Math`,
   `Strings`, `CharClass`, `Files` (layered on `IOChan`),
-  `Conversions`, `RealIO`, `ProgramArgs`,
+  `Conversions` (ISO `ConvResults` statuses + BOOLEAN shortcuts),
+  `RealIO`, `ProgramArgs`,
   `IOChan` (streams + files; `Position`/`Seek`/`Rewind`), `Storage`.
 
 ## Not started
 - ⏸ Clarion `CLASS` lowering — declared and checked only (fields,
   methods, single inheritance, `VIRTUAL`); bodies/dispatch need a
   receiver-passing design (see `docs/summary_class-lowering.md`).
-  (ISO `ConvResults` enumerations are now expressible.) The TopSpeed
-  legacy grammar (`TopSpeed-V3-M2.atg`) is a separate sidecar, not
-  merged.
+  ⏸ `VAL(LONGINT|REAL, x)` and long→int narrowing (V3 has no 64→32
+  conversion; `Conversions` works around it). 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

+ 59 - 0
docs/summary_charcat-convresults.md

@@ -0,0 +1,59 @@
+# Step: CHAR-array `+` coverage + ISO ConvResults
+
+Tag `v3-charcat-convresults`. Suite **139/139**; fixpoint **OK**
+(image **2,156,095 bytes**).
+
+## CHAR-array `+` (verified, test added)
+
+String concatenation for `ARRAY OF CHAR` already worked end-to-end
+from the earlier string work (`StrCompat` treats two 1-D `ARRAY OF
+CHAR` as string-compatible; `QbeGen.StrCat` → `m2strcat`, and the
+result is assigned by content copy). It simply had no test:
+
+- `a + b` (both `ARRAY OF CHAR`),
+- `a + "c"` / `"x" + a` (a single-char literal is a `CHAR` operand,
+  via `DeclCharStr`),
+- `a + a + a` (chained).
+
+`t_charcat.mod` (exit 15) locks all four in.
+
+Note: a single-character **string** literal is a `CHAR` in classic
+Modula-2, so `b := "z"` into a `CHAR` array is 210; write `b := "zz"`
+or assign `b[0] := "z"`. Concatenation accepts the `CHAR` form
+directly (`a + "c"`).
+
+## ISO `ConvResults`
+
+`Conversions` now reports an ISO-style status instead of only a
+`BOOLEAN`:
+
+```modula-2
+TYPE ConvResults = (strAllRight, strOutOfRange,
+                    strWrongFormat, strEmpty);
+PROCEDURE StrToInt (s : ARRAY OF CHAR; VAR n : INTEGER) : ConvResults;
+PROCEDURE StrToCard(s : ARRAY OF CHAR; VAR n : CARDINAL): ConvResults;
+PROCEDURE StrToReal(VAR s : ARRAY OF CHAR; VAR x : REAL): ConvResults;
+```
+
+- `strAllRight` / `strOutOfRange` / `strWrongFormat` / `strEmpty`.
+- `strOutOfRange` is detected **before** the 32-bit accumulate wraps
+  (`v > (IntMax - d) DIV 10`), because V3 has no `VAL(LONGINT, int)`
+  or long→int narrowing to use a wider accumulator.
+- The BOOLEAN shortcuts are retained under new names —
+  `IntVal`/`CardVal`/`RealVal` — since the `StrTo*` results changed
+  type. (`ConvProg` and `Showcase14` were updated to them.)
+
+`convresults_prog.mod` (exit 127) covers every status for
+`StrToInt`/`StrToCard`/`StrToReal`.
+
+## Tests
+
+- `t_charcat.mod` (15): CHAR-array `+`.
+- `convresults_prog.mod` (127): all `ConvResults` codes.
+- `conv_prog.mod` / `showcase14.mod`: switched to `IntVal`/`RealVal`.
+
+## Files
+
+`stdlib/conversions.def`/`.mod`, `compiler/tests/{t_charcat,
+convresults_prog,conv_prog,showcase14}.mod`, `compiler/run_tests.sh`,
+`docs/features.md`.

+ 17 - 6
stdlib/conversions.def

@@ -1,14 +1,25 @@
 DEFINITION MODULE Conversions;
-(* Classic scalar <-> string conversions.  Output strings are
-   NUL-terminated; the StrTo* functions return FALSE when the whole
-   string is not a valid literal of the target type. *)
+(* Scalar <-> string conversions.  Output strings are NUL-terminated.
+   The StrTo* procedures report an ISO-style `ConvResults` status:
+     strAllRight    the whole string was a valid literal
+     strOutOfRange  valid literal but not representable in the target
+     strWrongFormat the string is not a literal of the target type
+     strEmpty       the string was empty
+   The BOOLEAN-returning wrappers are retained for convenience. *)
+
+TYPE ConvResults = (strAllRight, strOutOfRange, strWrongFormat, strEmpty);
 
 PROCEDURE IntToStr(n : INTEGER; VAR s : ARRAY OF CHAR);
 PROCEDURE CardToStr(n : CARDINAL; VAR s : ARRAY OF CHAR);
 PROCEDURE RealToStr(x : REAL; VAR s : ARRAY OF CHAR);
 
-PROCEDURE StrToInt(s : ARRAY OF CHAR; VAR n : INTEGER) : BOOLEAN;
-PROCEDURE StrToCard(s : ARRAY OF CHAR; VAR n : CARDINAL) : BOOLEAN;
-PROCEDURE StrToReal(VAR s : ARRAY OF CHAR; VAR x : REAL) : BOOLEAN;
+PROCEDURE StrToInt(s : ARRAY OF CHAR; VAR n : INTEGER) : ConvResults;
+PROCEDURE StrToCard(s : ARRAY OF CHAR; VAR n : CARDINAL) : ConvResults;
+PROCEDURE StrToReal(VAR s : ARRAY OF CHAR; VAR x : REAL) : ConvResults;
+
+(* BOOLEAN shortcuts: TRUE iff the status is strAllRight. *)
+PROCEDURE IntVal(s : ARRAY OF CHAR; VAR n : INTEGER) : BOOLEAN;
+PROCEDURE CardVal(s : ARRAY OF CHAR; VAR n : CARDINAL) : BOOLEAN;
+PROCEDURE RealVal(VAR s : ARRAY OF CHAR; VAR x : REAL) : BOOLEAN;
 
 END Conversions.

+ 84 - 19
stdlib/conversions.mod

@@ -1,4 +1,12 @@
 IMPLEMENTATION MODULE Conversions;
+(* Tolerant parsers report an ISO-style ConvResults code.  INTEGER
+   range is checked with a LONGINT accumulator (V3 INTEGER is 32-bit):
+   a value outside [-2147483648, 2147483647] is strOutOfRange. *)
+
+CONST
+  (* 32-bit INTEGER bounds (V3 INTEGER is 32-bit) *)
+  IntMax = 2147483647;
+  IntMin = -2147483647 - 1;
 
 PROCEDURE m2realstr(x : REAL; VAR s : ARRAY OF CHAR);
   EXTERNAL;
@@ -6,6 +14,8 @@ PROCEDURE m2realstr(x : REAL; VAR s : ARRAY OF CHAR);
 PROCEDURE m2strreal(VAR s : ARRAY OF CHAR; VAR ok : INTEGER) : REAL;
   EXTERNAL;
 
+(* ---------------- output ---------------- *)
+
 PROCEDURE IntToStr(n : INTEGER; VAR s : ARRAY OF CHAR);
   VAR v : INTEGER;
     i, k : CARDINAL;
@@ -59,49 +69,104 @@ BEGIN
   m2realstr(x, s)
 END RealToStr;
 
-PROCEDURE StrToInt(s : ARRAY OF CHAR; VAR n : INTEGER) : BOOLEAN;
+(* ---------------- input ---------------- *)
+
+PROCEDURE StrToInt(s : ARRAY OF CHAR; VAR n : INTEGER) : ConvResults;
+(* signed decimal.  V3 has no VAL(LONGINT, int) or long->int
+   narrowing, so overflow is detected *before* the 32-bit accumulate
+   wraps: reject when v would exceed (IntMax-d) DIV 10.  The whole
+   magnitude accumulates as a non-negative INTEGER, then range is
+   checked per sign. *)
   VAR i : CARDINAL;
-    neg, seen : BOOLEAN;
+    neg, seen, over : BOOLEAN;
     v : INTEGER;
+    d : INTEGER;
+    bound : INTEGER;
 BEGIN
   n := 0; i := 0; neg := FALSE; seen := FALSE; v := 0;
-  IF (i > HIGH(s)) OR (s[i] = CHR(0)) THEN RETURN FALSE END;
+  over := FALSE;
+  IF (i > HIGH(s)) OR (s[i] = CHR(0)) THEN RETURN strEmpty END;
   IF s[i] = "-" THEN neg := TRUE; INC(i)
   ELSIF s[i] = "+" THEN INC(i)
   END;
+  IF (i > HIGH(s)) OR (s[i] < "0") OR (s[i] > "9") THEN
+    RETURN strWrongFormat
+  END;
   WHILE (i <= HIGH(s)) AND (s[i] >= "0") AND (s[i] <= "9") DO
-    v := v * 10 + (ORD(s[i]) - ORD("0"));
+    d := ORD(s[i]) - ORD("0");
+    IF neg THEN
+      (* magnitude may reach 2^31 *)
+      bound := (IntMax - 1 - d) DIV 10
+    ELSE
+      bound := (IntMax - d) DIV 10
+    END;
+    IF v > bound THEN over := TRUE
+    ELSE v := v * 10 + d
+    END;
     seen := TRUE;
     INC(i)
   END;
-  IF NOT seen THEN RETURN FALSE END;
-  IF (i <= HIGH(s)) AND (s[i] # CHR(0)) THEN RETURN FALSE END;
-  IF neg THEN n := -v ELSE n := v END;
-  RETURN TRUE
+  IF (i <= HIGH(s)) AND (s[i] # CHR(0)) THEN RETURN strWrongFormat END;
+  IF NOT seen THEN RETURN strWrongFormat END;
+  IF over THEN RETURN strOutOfRange END;
+  IF neg THEN n := 0 - v ELSE n := v END;
+  RETURN strAllRight
 END StrToInt;
 
-PROCEDURE StrToCard(s : ARRAY OF CHAR; VAR n : CARDINAL) : BOOLEAN;
+PROCEDURE StrToCard(s : ARRAY OF CHAR; VAR n : CARDINAL) : ConvResults;
+(* unsigned decimal; same pre-wrap overflow guard, bounded by the
+   CARDINAL max (2^32-1) held as a non-negative INTEGER magnitude. *)
   VAR i : CARDINAL;
-    seen : BOOLEAN;
-    v : CARDINAL;
+    seen, over : BOOLEAN;
+    v : INTEGER;
+    d, bound : INTEGER;
 BEGIN
-  n := 0; i := 0; seen := FALSE; v := 0;
+  n := 0; i := 0; seen := FALSE; v := 0; over := FALSE;
+  IF (i > HIGH(s)) OR (s[i] = CHR(0)) THEN RETURN strEmpty END;
+  IF (s[i] < "0") OR (s[i] > "9") THEN RETURN strWrongFormat END;
   WHILE (i <= HIGH(s)) AND (s[i] >= "0") AND (s[i] <= "9") DO
-    v := v * 10 + (ORD(s[i]) - ORD("0"));
+    d := ORD(s[i]) - ORD("0");
+    bound := (IntMax - d) DIV 10;
+    IF v > bound THEN over := TRUE
+    ELSE v := v * 10 + d
+    END;
     seen := TRUE;
     INC(i)
   END;
-  IF NOT seen THEN RETURN FALSE END;
-  IF (i <= HIGH(s)) AND (s[i] # CHR(0)) THEN RETURN FALSE END;
+  IF (i <= HIGH(s)) AND (s[i] # CHR(0)) THEN RETURN strWrongFormat END;
+  IF NOT seen THEN RETURN strWrongFormat END;
+  IF over THEN RETURN strOutOfRange END;
   n := v;
-  RETURN TRUE
+  RETURN strAllRight
 END StrToCard;
 
-PROCEDURE StrToReal(VAR s : ARRAY OF CHAR; VAR x : REAL) : BOOLEAN;
-  VAR ok : INTEGER;
+PROCEDURE StrToReal(VAR s : ARRAY OF CHAR; VAR x : REAL) : ConvResults;
+  VAR i : CARDINAL;
+    ok : INTEGER;
 BEGIN
+  x := 0.0;
+  i := 0;
+  IF (i > HIGH(s)) OR (s[i] = CHR(0)) THEN RETURN strEmpty END;
   x := m2strreal(s, ok);
-  RETURN ok # 0
+  IF ok = 0 THEN RETURN strWrongFormat END;
+  RETURN strAllRight
 END StrToReal;
 
+(* ---------------- BOOLEAN shortcuts ---------------- *)
+
+PROCEDURE IntVal(s : ARRAY OF CHAR; VAR n : INTEGER) : BOOLEAN;
+BEGIN
+  RETURN StrToInt(s, n) = strAllRight
+END IntVal;
+
+PROCEDURE CardVal(s : ARRAY OF CHAR; VAR n : CARDINAL) : BOOLEAN;
+BEGIN
+  RETURN StrToCard(s, n) = strAllRight
+END CardVal;
+
+PROCEDURE RealVal(VAR s : ARRAY OF CHAR; VAR x : REAL) : BOOLEAN;
+BEGIN
+  RETURN StrToReal(s, x) = strAllRight
+END RealVal;
+
 END Conversions.