瀏覽代碼

showcase15: IOChan over files + strict 235 signatures (def/impl pair)

- showcase15lib.def/.mod: classic DEFINITION/IMPLEMENTATION helper
  (Point, InitPoint/SumPoint, AppendInt); showcase15 writes a report
  through a file channel, closes, reopens, reads the lines back and
  prints a summary. Exits 42.
- shim fix: m2freadline no longer clobbers the descriptor's capacity
  header (HIGH of an open ARRAY OF CHAR reads it); it returns the byte
  count instead, so ReadString's HIGH(s)+1 stays the capacity across
  reads. max is declared int (CARDINAL arrives as a 32-bit w).
- tests: Showcase15 -> 131/131. Fixpoint OK (2,103,316 bytes).
Eric Streit 1 周之前
父節點
當前提交
7669454731

+ 10 - 0
compiler/run_tests.sh

@@ -275,6 +275,16 @@ rm -f gen_ssa/_showcase11.txt
 # statements, 1-char strings, qualified type names.
 expect_run_files Showcase12 42 showcase12lib.def showcase12lib.mod showcase12.mod
 expect_run_files_out Showcase13 42 "Even(8)=T Odd(7)=T total=42" ../stdlib/sysio.def ../stdlib/sysio.mod showcase13.mod
+rm -f gen_ssa/_showcase15.txt
+expect_run_files_out Showcase15 42 "read back: point 42" \
+  ../runtime/syslib/SysShim.def ../runtime/syslib/SysShim.mod \
+  ../runtime/syslib/Utf8.def ../runtime/syslib/Utf8.mod \
+  ../stdlib/iochan.def ../stdlib/iochan.mod \
+  ../stdlib/textio.def ../stdlib/textio.mod \
+  ../stdlib/strings.def ../stdlib/strings.mod \
+  ../stdlib/conversions.def ../stdlib/conversions.mod \
+  showcase15lib.def showcase15lib.mod showcase15.mod
+rm -f gen_ssa/_showcase15.txt
 expect_run_files_out Showcase14 42 "showcase14 n=42" \
   ../runtime/syslib/Utf8.def ../runtime/syslib/Utf8.mod \
   ../runtime/syslib/SysShim.def ../runtime/syslib/SysShim.mod \

+ 60 - 0
compiler/tests/showcase15.mod

@@ -0,0 +1,60 @@
+MODULE Showcase15;
+// Session showcase: separate compilation across a DEFINITION /
+// IMPLEMENTATION pair (whose signatures are verified by the new
+// strict 235 check), string concatenation, and IOChan over files.
+//
+// It writes a small report to a file through a channel, closes it,
+// reopens it for reading, reads it back line by line, and prints a
+// summary on stdout. Expected ExitCode: 42.
+IMPORT Showcase15Lib, IOChan, TextIO, Strings;
+
+VAR ExitCode : INTEGER;
+VAR out, inp : IOChan.ChanId;
+VAR name, line : ARRAY [0 .. 63] OF CHAR;
+VAR p : Showcase15Lib.Point;
+VAR total : INTEGER;
+
+PROCEDURE Emit (cid : IOChan.ChanId; s : ARRAY OF CHAR; n : INTEGER);
+(* one "label + number" line on the channel *)
+  VAR buf : ARRAY [0 .. 63] OF CHAR;
+BEGIN
+  Strings.Assign(s, buf);
+  Showcase15Lib.AppendInt(buf, n);
+  IOChan.WriteString(cid, buf);
+  IOChan.WriteLn(cid)
+END Emit;
+
+BEGIN
+  ExitCode := 0;
+  Strings.Assign("gen_ssa/_showcase15.txt", name);
+
+  Showcase15Lib.InitPoint(p, 20, 22);
+  total := Showcase15Lib.SumPoint(p);
+
+  (* write the report through a file channel *)
+  out := IOChan.OpenWrite(name);
+  IF NOT IOChan.IsOpen(out) THEN ExitCode := 90 ELSE
+    IOChan.WriteString(out, "total");
+    IOChan.WriteLn(out);
+    Emit(out, "point ", total);
+    IOChan.Close(out)
+  END;
+
+  (* read it back through another channel, line by line *)
+  inp := IOChan.OpenRead(name);
+  IF NOT IOChan.IsOpen(inp) THEN ExitCode := 91 ELSE
+    IOChan.ReadString(inp, line);          (* "total" *)
+    IF line = "total" THEN ExitCode := ExitCode + 1 ELSE ExitCode := 92 END;
+    IOChan.ReadString(inp, line);          (* "point 42" *)
+    IF line = "point 42" THEN ExitCode := ExitCode + 2 ELSE ExitCode := 93 END;
+    TextIO.WriteString("read back: ");
+    TextIO.WriteString(line);
+    TextIO.WriteLn;
+    IOChan.Close(inp)
+  END;
+
+  IF ExitCode = 3 THEN ExitCode := total END;   (* 42 on success *)
+  TextIO.WriteString("showcase15 total=");
+  TextIO.WriteInt(total);
+  TextIO.WriteLn
+END Showcase15.

+ 18 - 0
compiler/tests/showcase15lib.def

@@ -0,0 +1,18 @@
+DEFINITION MODULE Showcase15Lib;
+(* Showcase-15 helper: a small record + string/IOChan utilities,
+   written as a classic DEFINITION/IMPLEMENTATION pair so the strict
+   235 signature checks apply. *)
+
+TYPE
+  Point = RECORD
+    x, y : INTEGER;
+  END;
+
+PROCEDURE InitPoint (VAR p : Point; x, y : INTEGER);
+PROCEDURE SumPoint (p : Point) : INTEGER;
+
+PROCEDURE AppendInt (VAR s : ARRAY OF CHAR; n : INTEGER);
+(* Appends the decimal form of n to the NUL-terminated string s,
+   truncating to s's capacity. *)
+
+END Showcase15Lib.

+ 33 - 0
compiler/tests/showcase15lib.mod

@@ -0,0 +1,33 @@
+IMPLEMENTATION MODULE Showcase15Lib;
+(* Signatures match the definition exactly (strict 235). *)
+
+IMPORT Conversions;
+
+PROCEDURE InitPoint (VAR p : Point; x, y : INTEGER);
+BEGIN
+  p.x := x;
+  p.y := y
+END InitPoint;
+
+PROCEDURE SumPoint (p : Point) : INTEGER;
+BEGIN
+  RETURN p.x + p.y
+END SumPoint;
+
+PROCEDURE AppendInt (VAR s : ARRAY OF CHAR; n : INTEGER);
+  VAR tmp : ARRAY [0 .. 31] OF CHAR;
+    i, k : CARDINAL;
+BEGIN
+  Conversions.IntToStr(n, tmp);
+  (* find the end of s *)
+  k := 0;
+  WHILE (k <= HIGH(s)) AND (s[k] # CHR(0)) DO INC(k) END;
+  i := 0;
+  WHILE (k < HIGH(s)) AND (tmp[i] # CHR(0)) DO
+    s[k] := tmp[i];
+    INC(k); INC(i)
+  END;
+  IF k <= HIGH(s) THEN s[k] := CHR(0) END
+END AppendInt;
+
+END Showcase15Lib.

+ 2 - 1
docs/features.md

@@ -65,7 +65,8 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
   (any depth), array/record/set/pointer params, open-array `VAR`
   formals.
 - ✅ Separate compilation: `lib.def lib.mod prog.mod` → one image;
-  module init functions; opaque completion.
+  module init functions; opaque completion; strict 235 signature
+  checks between a definition and its implementation.
 - ⏸ Procedure-type variables / indirect calls, per-file object
   emission, import cycles.
 

+ 62 - 0
docs/summary_showcase15.md

@@ -0,0 +1,62 @@
+# Showcase 15 — IOChan over files + strict signature checks
+
+Tags `v3-showcase15`, `v3-showcase15-summary` (this doc).
+Suite **131/131**; fixpoint **OK** (2,103,316 bytes).
+
+## What it runs
+
+`compiler/tests/showcase15.mod` (`Showcase15`, exit 42) links a
+DEFINITION/IMPLEMENTATION helper plus four stdlib modules:
+
+```
+IMPORT Showcase15Lib, IOChan, TextIO, Strings;
+```
+
+1. **Separate compilation + strict 235** — `Showcase15Lib` is a
+   classic `DEFINITION`/`IMPLEMENTATION` pair; its signatures are
+   verified by the new check (`InitPoint`, `SumPoint`,
+   `AppendInt`).
+2. **Records and VAR formals** — `Point`, `InitPoint(VAR p …)`.
+3. **String concatenation/assignment** — builds the report line
+   `"point " + <int>` via `Strings.Assign` + `AppendInt`.
+4. **IOChan over files** — `OpenWrite` a channel, write `"total"`,
+   `"point <n>"`, `Close`, then `OpenRead` and `ReadString` the lines
+   back, checking both.
+
+Output:
+
+```
+read back: point 42
+showcase15 total=42
+```
+
+## Bug found and fixed (shim)
+
+`m2freadline` originally set the descriptor's **count header** to the
+number of bytes read. But `HIGH(s)` of an open `ARRAY OF CHAR` formal
+reads that same header, so `HIGH(s) + 1` (used by `IOChan.ReadString`
+as the capacity) collapsed to the previous line's length after the
+first read — the second line was truncated (`"point"` instead of
+`"point 42"`).
+
+`m2freadline` now **leaves the capacity header intact** and returns the
+byte count instead, so a caller may keep using `HIGH`. (`ReadString`
+only needs the NUL-terminated content.)
+
+Also, `max` is now declared `int` in C: Modula-2 `CARDINAL` arguments
+arrive in a 32-bit register (QBE `w`), so only the low 32 bits of the
+C `long` parameter are defined.
+
+## Registration
+
+`rm -f gen_ssa/_showcase15.txt` before and after;
+`expect_run_files_out Showcase15 42 "read back: point 42" …` with the
+helper and the `SysShim`/`Utf8`/`iochan`/`textio`/`strings`/
+`conversions` modules.
+
+## Files
+
+`compiler/tests/showcase15.mod`,
+`compiler/tests/showcase15lib.def`/`.mod`,
+`compiler/run_tests.sh`, `runtime/syslib/shim.c`,
+`docs/summary_showcase15.md`.

+ 7 - 5
runtime/syslib/shim.c

@@ -151,15 +151,18 @@ long m2fwriteln(long h)
 }
 
 /* Read one line (up to and including '\n', or EOF) into the
-   descriptor's data area, dropping the newline.  The descriptor's
-   header is set to the number of bytes stored; NUL-terminated. */
-long m2freadline(long h, long *desc, long max)
+   descriptor's data area, dropping the newline; NUL-terminated.
+   The descriptor's capacity header is left intact (so a caller may
+   keep using HIGH), and the number of bytes stored is returned.
+   `max` is declared `int`: Modula-2 CARDINAL arguments arrive in a
+   32-bit register (QBE `w`), so only the low 32 bits are defined. */
+long m2freadline(long h, long *desc, int max)
 {
     char *dst = (char *)(desc + 1);
     long n = 0;
     int c;
     if (max < 0) max = 0;
-    if (h == 0) { if (max > 0) dst[0] = 0; desc[0] = 0; return 0; }
+    if (h == 0) { if (max > 0) dst[0] = 0; return 0; }
     while (n < max) {
         c = fgetc((FILE *)h);
         if (c == EOF) break;
@@ -168,7 +171,6 @@ long m2freadline(long h, long *desc, long max)
         dst[n++] = (char)c;
     }
     dst[n] = 0;
-    desc[0] = n;
     return n;
 }