浏览代码

stdlib: Conversions + RealIO (+ shim real helpers, 2 tests)

- stdlib/conversions: IntToStr/CardToStr (M2), RealToStr (shim
  %.6E), StrToInt/StrToCard (M2 whole-string parse), StrToReal (shim
  strtod); BOOLEAN results.
- stdlib/realio: WriteReal/WriteRealF (right-justified) / ReadReal.
- shim.c: m2realstr/m2strreal/m2writereal(f)/m2readreal.
- tests: conv_prog (127), realio_prog (stdout 3.500000E+00, reads
  1.25) -> 122/122. Fixpoint OK (2,062,392 bytes).
- docs updated.
Eric Streit 1 周之前
父节点
当前提交
2f804221df

+ 2 - 0
compiler/run_tests.sh

@@ -229,6 +229,7 @@ expect_run_files_out StringsProg 0 "Hello World" ../stdlib/sysio.def ../stdlib/s
 expect_run_files_out Stdlib2Prog 42 "stdlib2 n=42" ../stdlib/sysio.def ../stdlib/sysio.mod ../stdlib/math.def ../stdlib/math.mod ../stdlib/strings.def ../stdlib/strings.mod ../stdlib/charclass.def ../stdlib/charclass.mod stdlib2_prog.mod
 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_out UTextProg 42 "héllo" ../runtime/syslib/Utf8.def ../runtime/syslib/Utf8.mod ../stdlib/textio.def ../stdlib/textio.mod utext_prog.mod
 
 expect_run_files_in() {
@@ -248,6 +249,7 @@ expect_run_files_in() {
 
 expect_run_files_in ReadProg 0 "42" "20 22" ../stdlib/sysio.def ../stdlib/sysio.mod read_prog.mod
 expect_run_files_in WholeIOProg 0 "42" "6 7" ../stdlib/sysio.def ../stdlib/sysio.mod ../stdlib/wholeio.def ../stdlib/wholeio.mod wholeio_prog.mod
+expect_run_files_in RealProg 42 "3.500000E+00" "1.25" ../stdlib/realio.def ../stdlib/realio.mod ../stdlib/sysio.def ../stdlib/sysio.mod realio_prog.mod
 expect_run_files FilesProg 42 ../stdlib/files.def ../stdlib/files.mod files_prog.mod
 expect_run_files TBadFile 42 ../stdlib/files.def ../stdlib/files.mod t_badfile.mod
 rm -f gen_ssa/_files_tmp.txt

+ 28 - 0
compiler/tests/conv_prog.mod

@@ -0,0 +1,28 @@
+MODULE ConvProg;
+(* Conversions: Int/Card/Real <-> string round-trips.  Exit 127. *)
+IMPORT Conversions;
+VAR ExitCode : INTEGER;
+VAR s : ARRAY [0 .. 31] OF CHAR;
+VAR n : INTEGER;
+VAR c : CARDINAL;
+VAR x : REAL;
+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 n = 42 THEN ExitCode := ExitCode + 4 END
+  ELSE ExitCode := 100 END;
+  IF Conversions.StrToInt("-7", n) THEN
+    IF n = -7 THEN ExitCode := ExitCode + 8 END
+  ELSE ExitCode := 100 END;
+  IF NOT Conversions.StrToInt("4x", n) THEN
+    ExitCode := ExitCode + 16
+  ELSE ExitCode := 100 END;
+  IF Conversions.StrToCard("99", c) THEN
+    IF c = 99 THEN ExitCode := ExitCode + 32 END
+  ELSE ExitCode := 100 END;
+  IF Conversions.StrToReal("3.5", x) THEN
+    IF x = 3.5 THEN ExitCode := ExitCode + 64 END
+  ELSE ExitCode := 100 END
+END ConvProg.

+ 14 - 0
compiler/tests/realio_prog.mod

@@ -0,0 +1,14 @@
+MODULE RealProg;
+(* RealIO: WriteReal / WriteRealF output and ReadReal input. *)
+IMPORT RealIO, SysIO;
+VAR ExitCode : INTEGER;
+VAR x : REAL;
+BEGIN
+  RealIO.WriteReal(3.5);
+  SysIO.WriteLn;
+  RealIO.WriteRealF(2.5, 16);
+  SysIO.WriteLn;
+  x := 0.0;
+  RealIO.ReadReal(x);
+  IF x = 1.25 THEN ExitCode := 42 ELSE ExitCode := 7 END
+END RealProg.

+ 14 - 6
docs/features.md

@@ -1,4 +1,4 @@
-# m2compiler-V3 — feature status (at `v3-step4`, 87/87 green)
+# m2compiler-V3 — feature status (at `v3-stdlib-conversions`, 122/122 green)
 
 Pipeline: Coco/R `M2.atg` (1748 lines, 73 productions) → `gm2`-built
 `M2` → QBE `.ssa` → `qbe` → `cc` → run. `SymTab.mod` 1623 lines,
@@ -78,15 +78,23 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
   **233 `invalid call`**, **234 `invalid UTF-8 in U-literal`**, plus
   `file:line:col` console format.
 
+## Library
+- ✅ `runtime/syslib`: `SysShim`, `FileIO`, `Utf8` (UTF-8 codec).
+- ✅ `stdlib`: `SysIO`, `TextIO` (+ UTF-8 `U*`), `WholeIO`, `Math`,
+  `Strings`, `CharClass`, `Files`, `Conversions`, `RealIO`,
+  `Storage`.
+
 ## Not started
 - ⏸ Clarion `CLASS` lowering (declared + checked only); Unicode
-  `UString` whole-string assignment (`=`/`+`). `runtime/syslib/Utf8`
-  and `TextIO.U*` output are in. The TopSpeed legacy grammar
-  (`TopSpeed-V3-M2.atg`) is a separate sidecar, not merged.
+  `UString` whole-string assignment (`=`/`+`); `ProgramArgs` /
+  `IOChan`; ISO `ConvResults` enumerations. 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`.
+Next: more Benjamin stdlib (`ProgramArgs`, `IOChan`) and language
+polish (enum literals in expressions/CASE, strict 231 def/impl
+signature checks). Roadmap: `docs/plan.md`,
+`docs/blaise-phases-map.md`.

+ 2 - 1
docs/plan.md

@@ -37,7 +37,8 @@ m2compiler-V3/
   compiler/tests/    accept / reject (.LST) / run (exit-code) tests
   runtime/syslib/    SYSTEM, Storage, SysIO, SysClock, Trap, Utf8
                      always-linked, self-hostable, NO gm2-only imports
-  stdlib/            Strings, TextIO (+U*), WholeIO, Math, Files
+  stdlib/            Strings, TextIO (+U*), WholeIO, Math, Files,
+                     Conversions, RealIO, CharClass
                      opt-in via IMPORT, classic DEFINITION+OPAQUE,
                      Benjamin-R10 semantics, classic syntax only
   tools/redux-check/ modern-syntax enforcement helper

+ 47 - 0
docs/summary_stdlib-conversions.md

@@ -0,0 +1,47 @@
+# Step: Conversions + RealIO
+
+Tag `v3-stdlib-conversions`. Suite **122/122**; fixpoint **OK**
+(`bootstrap/fixpoint.sh`, 2,062,392 bytes).
+
+## Added
+
+- **`Conversions`** — scalar ↔ string:
+  - `IntToStr(n, VAR s)`, `CardToStr(n, VAR s)` — decimal, sign for
+    negatives, NUL-terminated (written in Modula-2).
+  - `RealToStr(x, VAR s)` — 6 significant digits, scientific
+    (`%.6E`), via the shim.
+  - `StrToInt(s, VAR n):BOOLEAN`, `StrToCard(s, VAR n):BOOLEAN` —
+    whole-string parse (optional `+`/`-`), `FALSE` on stray chars or
+    empty input.
+  - `StrToReal(VAR s, VAR x):BOOLEAN` — `strtod` via the shim;
+    `FALSE` unless the whole string is consumed.
+- **`RealIO`** — `WriteReal(x)` (no field), `WriteRealF(x, width)`
+  (right-justified), `ReadReal(VAR x)` (`scanf("%lf")`).
+- **shim** (`runtime/syslib/shim.c`) — `m2realstr`, `m2strreal`,
+  `m2writereal`, `m2writerealf`, `m2readreal`; all use the existing
+  descriptor convention (`desc[0]` = capacity/length, bytes follow).
+
+## Notes / deviations
+
+- `Conversions` uses `BOOLEAN` results rather than the ISO
+  `ConvResults` enumeration (V3 enumeration values in expressions are
+  still `230`). Signatures are otherwise "classic" and simple.
+- `WriteReal` emits no leading space (consistent with `WriteInt`);
+  `WriteRealF(x, w)` pads to `w`.
+- The C-ABI match for the existing externals is reused (INTEGER
+  formal ↔ C `long`), as in `Files`.
+
+## Tests
+
+- `conv_prog.mod` (exit 127): `IntToStr(-1234)`, `StrToInt("42")`,
+  `StrToInt("-7")`, `StrToInt("4x")` → `FALSE`, `StrToCard("99")`,
+  `StrToReal("3.5")`.
+- `realio_prog.mod` (exit 42): stdout first line `3.500000E+00`,
+  `ReadReal` of `1.25` from stdin.
+
+## Files
+
+`stdlib/conversions.def`/`.mod`, `stdlib/realio.def`/`.mod`,
+`runtime/syslib/shim.c`, `compiler/tests/conv_prog.mod`,
+`compiler/tests/realio_prog.mod`, `compiler/run_tests.sh`,
+`docs/plan.md`, `docs/features.md`.

+ 58 - 0
runtime/syslib/shim.c

@@ -271,3 +271,61 @@ long m2exit(long code)
     exit((int)code);
     return 0;
 }
+
+/* ---------------- reals (Conversions / RealIO) ---------------- */
+
+/* Format a REAL into a descriptor string (6 significant digits,
+   scientific). desc[0] is the capacity; on return it holds the
+   length. Returns the number of characters written. */
+long m2realstr(double x, long *desc)
+{
+    char buf[64];
+    char *dst = (char *)(desc + 1);
+    long cap = desc[0];
+    int n = snprintf(buf, sizeof buf, "%.6E", x);
+    if (n < 0) n = 0;
+    if (cap > 0 && n >= (int)cap) n = (int)cap - 1;
+    if (n > 0) memcpy(dst, buf, (size_t)n);
+    if (cap > 0) { dst[n] = 0; desc[0] = n; }
+    return (long)n;
+}
+
+/* Parse a REAL from a descriptor string; *ok := 1 on success. */
+double m2strreal(long *desc, long *ok)
+{
+    char buf[4096];
+    char *end;
+    double v;
+    m2cstr(desc, buf, sizeof buf);
+    v = strtod(buf, &end);
+    while (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r') end++;
+    *ok = (end != buf && *end == 0) ? 1 : 0;
+    return v;
+}
+
+/* Write a REAL right-justified in a field of width `wid` (0 => one
+   leading space), matching the integer convention. */
+long m2writerealf(double x, long wid)
+{
+    char buf[128];
+    int n;
+    if (wid == 0)
+        n = snprintf(buf, sizeof buf, "%.6E", x);
+    else
+        n = snprintf(buf, sizeof buf, "%*.*E", (int)wid, 6, x);
+    if (n > 0) write(1, buf, (size_t)n);
+    return 0;
+}
+
+long m2writereal(double x)
+{
+    return m2writerealf(x, 0);
+}
+
+/* Read a REAL from stdin. */
+double m2readreal(void)
+{
+    double v = 0.0;
+    if (scanf("%lf", &v) != 1) v = 0.0;
+    return v;
+}

+ 14 - 0
stdlib/conversions.def

@@ -0,0 +1,14 @@
+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. *)
+
+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;
+
+END Conversions.

+ 107 - 0
stdlib/conversions.mod

@@ -0,0 +1,107 @@
+IMPLEMENTATION MODULE Conversions;
+
+PROCEDURE m2realstr(x : REAL; VAR s : ARRAY OF CHAR);
+  EXTERNAL;
+
+PROCEDURE m2strreal(VAR s : ARRAY OF CHAR; VAR ok : INTEGER) : REAL;
+  EXTERNAL;
+
+PROCEDURE IntToStr(n : INTEGER; VAR s : ARRAY OF CHAR);
+  VAR v : INTEGER;
+    i, k : CARDINAL;
+    tmp : ARRAY [0 .. 31] OF CHAR;
+BEGIN
+  IF n = 0 THEN
+    s[0] := "0"; s[1] := CHR(0)
+  ELSE
+    IF n < 0 THEN v := -n ELSE v := n END;
+    i := 0;
+    WHILE (v > 0) AND (i <= HIGH(tmp)) DO
+      tmp[i] := CHR(ORD("0") + (v MOD 10));
+      v := v DIV 10;
+      INC(i)
+    END;
+    k := 0;
+    IF n < 0 THEN s[k] := "-"; INC(k) END;
+    WHILE (i > 0) AND (k <= HIGH(s)) DO
+      DEC(i);
+      s[k] := tmp[i]; INC(k)
+    END;
+    IF k <= HIGH(s) THEN s[k] := CHR(0) END
+  END
+END IntToStr;
+
+PROCEDURE CardToStr(n : CARDINAL; VAR s : ARRAY OF CHAR);
+  VAR v : CARDINAL;
+    i, k : CARDINAL;
+    tmp : ARRAY [0 .. 31] OF CHAR;
+BEGIN
+  IF n = 0 THEN
+    s[0] := "0"; s[1] := CHR(0)
+  ELSE
+    v := n; i := 0;
+    WHILE (v > 0) AND (i <= HIGH(tmp)) DO
+      tmp[i] := CHR(ORD("0") + (v MOD 10));
+      v := v DIV 10;
+      INC(i)
+    END;
+    k := 0;
+    WHILE (i > 0) AND (k <= HIGH(s)) DO
+      DEC(i);
+      s[k] := tmp[i]; INC(k)
+    END;
+    IF k <= HIGH(s) THEN s[k] := CHR(0) END
+  END
+END CardToStr;
+
+PROCEDURE RealToStr(x : REAL; VAR s : ARRAY OF CHAR);
+BEGIN
+  m2realstr(x, s)
+END RealToStr;
+
+PROCEDURE StrToInt(s : ARRAY OF CHAR; VAR n : INTEGER) : BOOLEAN;
+  VAR i : CARDINAL;
+    neg, seen : BOOLEAN;
+    v : 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;
+  IF s[i] = "-" THEN neg := TRUE; INC(i)
+  ELSIF s[i] = "+" THEN INC(i)
+  END;
+  WHILE (i <= HIGH(s)) AND (s[i] >= "0") AND (s[i] <= "9") DO
+    v := v * 10 + (ORD(s[i]) - ORD("0"));
+    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
+END StrToInt;
+
+PROCEDURE StrToCard(s : ARRAY OF CHAR; VAR n : CARDINAL) : BOOLEAN;
+  VAR i : CARDINAL;
+    seen : BOOLEAN;
+    v : CARDINAL;
+BEGIN
+  n := 0; i := 0; seen := FALSE; v := 0;
+  WHILE (i <= HIGH(s)) AND (s[i] >= "0") AND (s[i] <= "9") DO
+    v := v * 10 + (ORD(s[i]) - ORD("0"));
+    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;
+  n := v;
+  RETURN TRUE
+END StrToCard;
+
+PROCEDURE StrToReal(VAR s : ARRAY OF CHAR; VAR x : REAL) : BOOLEAN;
+  VAR ok : INTEGER;
+BEGIN
+  x := m2strreal(s, ok);
+  RETURN ok # 0
+END StrToReal;
+
+END Conversions.

+ 10 - 0
stdlib/realio.def

@@ -0,0 +1,10 @@
+DEFINITION MODULE RealIO;
+(* REAL input/output over the runtime shim.  WriteReal emits 6
+   significant digits in scientific notation with one leading space
+   (ISO convention); WriteRealF right-justifies in a field. *)
+
+PROCEDURE WriteReal(x : REAL);
+PROCEDURE WriteRealF(x : REAL; width : CARDINAL);
+PROCEDURE ReadReal(VAR x : REAL);
+
+END RealIO.

+ 27 - 0
stdlib/realio.mod

@@ -0,0 +1,27 @@
+IMPLEMENTATION MODULE RealIO;
+
+PROCEDURE m2writereal(x : REAL);
+  EXTERNAL;
+
+PROCEDURE m2writerealf(x : REAL; wid : INTEGER);
+  EXTERNAL;
+
+PROCEDURE m2readreal() : REAL;
+  EXTERNAL;
+
+PROCEDURE WriteReal(x : REAL);
+BEGIN
+  m2writereal(x)
+END WriteReal;
+
+PROCEDURE WriteRealF(x : REAL; width : CARDINAL);
+BEGIN
+  m2writerealf(x, width)
+END WriteRealF;
+
+PROCEDURE ReadReal(VAR x : REAL);
+BEGIN
+  x := m2readreal()
+END ReadReal;
+
+END RealIO.