Просмотр исходного кода

stdlib: IOChan over files (OpenRead/OpenWrite/Close/IsOpen, ReadString/ReadInt)

- iochan: ChanId now also wraps a file; OpenRead/OpenWrite return a
  closed channel on failure, IsOpen queries, Close takes ChanId by VAR
  and clears it. ReadString reads one line (newline dropped),
  ReadInt reads a signed decimal. Streams and ops otherwise unchanged.
- SysShim: freadint, freadline; shim.c: m2freadline (descriptor line
  reader). m2freadint already existed.
- tests: iochanfile_prog writes/reads a temp file through channels
  -> 130/130. Fixpoint OK (2,103,316 bytes).
- docs updated.
Eric Streit 2 недель назад
Родитель
Сommit
8d25c078b9

+ 3 - 0
compiler/run_tests.sh

@@ -255,6 +255,9 @@ expect_run_files_in ReadProg 0 "42" "20 22" ../stdlib/sysio.def ../stdlib/sysio.
 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_in IOChanProg 0 "hello 42" "Z" ../runtime/syslib/SysShim.def ../runtime/syslib/SysShim.mod ../stdlib/iochan.def ../stdlib/iochan.mod iochan_prog.mod
+rm -f gen_ssa/_iochan_tmp.txt
+expect_run_files IOChanFileProg 3 ../runtime/syslib/SysShim.def ../runtime/syslib/SysShim.mod ../stdlib/strings.def ../stdlib/strings.mod ../stdlib/iochan.def ../stdlib/iochan.mod iochanfile_prog.mod
+rm -f gen_ssa/_iochan_tmp.txt
 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

+ 31 - 0
compiler/tests/iochanfile_prog.mod

@@ -0,0 +1,31 @@
+MODULE IOChanFileProg;
+(* IOChan over files: write a channel to a file, close, reopen for
+   reading, read the line and integer back.  Exit 3. *)
+IMPORT IOChan, Strings;
+VAR ExitCode : INTEGER;
+VAR f : IOChan.ChanId;
+VAR name, line : ARRAY [0 .. 63] OF CHAR;
+BEGIN
+  Strings.Assign("gen_ssa/_iochan_tmp.txt", name);
+
+  f := IOChan.OpenWrite(name);
+  IF IOChan.IsOpen(f) THEN
+    IOChan.WriteString(f, "line one");
+    IOChan.WriteLn(f);
+    IOChan.WriteInt(f, -7);
+    IOChan.WriteLn(f);
+    IOChan.Close(f)
+  END;
+
+  f := IOChan.OpenRead(name);
+  IF IOChan.IsOpen(f) THEN
+    IOChan.ReadString(f, line);
+    IF line = "line one" THEN
+      ExitCode := ExitCode + 1
+    ELSE ExitCode := 90 END;
+    IF IOChan.ReadInt(f) = -7 THEN
+      ExitCode := ExitCode + 2
+    ELSE ExitCode := 91 END;
+    IOChan.Close(f)
+  END
+END IOChanFileProg.

+ 7 - 7
docs/features.md

@@ -1,4 +1,4 @@
-# m2compiler-V3 — feature status (at `v3-sigcheck`, 129/129 green)
+# m2compiler-V3 — feature status (at `v3-iochan-files`, 130/130 green)
 
 Pipeline: Coco/R `M2.atg` (1748 lines, 73 productions) → `gm2`-built
 `M2` → QBE `.ssa` → `qbe` → `cc` → run. `SymTab.mod` 1623 lines,
@@ -89,18 +89,18 @@ 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`, `Conversions`, `RealIO`,
-  `ProgramArgs`, `IOChan`, `Storage`.
+  `ProgramArgs`, `IOChan` (streams + files), `Storage`.
 
 ## Not started
 - ⏸ Clarion `CLASS` lowering (declared + checked only); Unicode
-  `UString` whole-string assignment; `IOChan` beyond the standard
-  streams; variant records. (ISO `ConvResults` enumerations are now
-  expressible.) The TopSpeed legacy grammar (`TopSpeed-V3-M2.atg`) is
-  a separate sidecar, not merged.
+  `UString` whole-string assignment; variant records; `Files` layered
+  on `IOChan`. (ISO `ConvResults` enumerations are now expressible.)
+  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: variant records, `IOChan` over files, `UString` assignment.
+Next: variant records, `UString` assignment, `Files` on `IOChan`.
 Roadmap: `docs/plan.md`, `docs/blaise-phases-map.md`.

+ 52 - 0
docs/summary_iochan-files.md

@@ -0,0 +1,52 @@
+# Step: IOChan over files
+
+Tag `v3-iochan-files`. Suite **130/130**; fixpoint **OK**
+(`bootstrap/fixpoint.sh`, image **2,103,316 bytes**).
+
+## What changed
+
+`IOChan` now covers files as well as the standard streams, so a
+program can treat a file and stdin/stdout uniformly through one
+`ChanId`:
+
+- `OpenRead(name)`, `OpenWrite(name)` → `ChanId` (a failed open yields
+  a closed channel: `IsOpen` = FALSE).
+- `Close(VAR cid)` — closes and clears the handle.
+- `IsOpen(cid)`.
+- `ReadString(cid, VAR s)` — reads one line (the newline is dropped)
+  into the NUL-terminated string; `ReadInt(cid)` reads a signed
+  decimal integer (skipping leading blanks).
+
+The existing standard channels and operations (`StdIn/Out/ErrChan`,
+`ReadChar`, `WriteChar`, `WriteString`, `WriteLn`, `WriteInt`) are
+unchanged. Operations on a closed/failed channel stay safe no-ops
+(`ReadChar` returns `0C`).
+
+## Bridge additions
+
+- `runtime/syslib/SysShim.def`/`.mod`: `freadint`, `freadline`.
+- `runtime/syslib/shim.c`: `m2freadline` (line read into a descriptor,
+  newline dropped, header set to the length). `m2freadint` already
+  existed (used by `Files`).
+
+## Test
+
+`compiler/tests/iochanfile_prog.mod` (`IOChanFileProg`, exit 3):
+writes `"line one\n-7\n"` through a channel to
+`gen_ssa/_iochan_tmp.txt`, closes, reopens for reading, and checks the
+line (`ReadString` → `"line one"`) and the integer (`ReadInt` → `-7`).
+The harness removes the temporary file before and after.
+
+## Notes
+
+- `Close` takes `cid` by reference so it can set it to `NIL`; this
+  mirrors the ISO-ish "release the channel" shape.
+- `ReadString` truncates to the destination capacity.
+- `Files` keeps its own direct shim binding for now; `IOChan` could
+  become its base layer in a later step.
+
+## Files
+
+`stdlib/iochan.def`/`.mod`, `runtime/syslib/SysShim.def`/`.mod`,
+`runtime/syslib/shim.c`, `compiler/tests/iochanfile_prog.mod`,
+`compiler/run_tests.sh`, `docs/features.md`.

+ 7 - 0
runtime/syslib/SysShim.def

@@ -16,6 +16,11 @@ PROCEDURE fread (h : ADDRESS; VAR buf : ARRAY OF CHAR;
                  max : CARDINAL) : CARDINAL;
 (* Reads up to max bytes into buf; buf's length is set to the
    number read, which is also returned. *)
+
+PROCEDURE freadline (h : ADDRESS; VAR buf : ARRAY OF CHAR;
+                     max : CARDINAL) : CARDINAL;
+(* Reads one line (the newline is dropped) into buf; buf's length is
+   set to the number of bytes stored, which is also returned. *)
 PROCEDURE fputc (h : ADDRESS; c : CHAR);
 PROCEDURE fputs (h : ADDRESS; VAR s : ARRAY OF CHAR);
 PROCEDURE fwriteln (h : ADDRESS);
@@ -27,6 +32,8 @@ PROCEDURE stderr () : ADDRESS;
 
 PROCEDURE fgetc (h : ADDRESS) : CHAR;
 (* Reads one byte from h; 0C at end of file. *)
+PROCEDURE freadint (h : ADDRESS) : INTEGER;
+(* Reads a signed decimal integer from h, skipping leading blanks. *)
 PROCEDURE fwriteint (h : ADDRESS; v : INTEGER);
 (* Writes v in decimal to h (no field width). *)
 

+ 16 - 0
runtime/syslib/SysShim.mod

@@ -12,6 +12,9 @@ PROCEDURE m2fclose (h : ADDRESS);
 PROCEDURE m2fread (h : ADDRESS; VAR buf : ARRAY OF CHAR;
                    max : CARDINAL) : CARDINAL;
   EXTERNAL;
+PROCEDURE m2freadline (h : ADDRESS; VAR buf : ARRAY OF CHAR;
+                       max : CARDINAL) : CARDINAL;
+  EXTERNAL;
 PROCEDURE m2fputc (h : ADDRESS; c : CHAR);
   EXTERNAL;
 PROCEDURE m2fputs (h : ADDRESS; VAR s : ARRAY OF CHAR);
@@ -28,6 +31,8 @@ PROCEDURE m2stdin () : ADDRESS;
   EXTERNAL;
 PROCEDURE m2fgetc (h : ADDRESS) : CHAR;
   EXTERNAL;
+PROCEDURE m2freadint (h : ADDRESS) : INTEGER;
+  EXTERNAL;
 PROCEDURE m2fwriteint (h : ADDRESS; v : INTEGER);
   EXTERNAL;
 PROCEDURE m2argc () : CARDINAL;
@@ -57,6 +62,12 @@ BEGIN
   RETURN m2fread(h, buf, max)
 END fread;
 
+PROCEDURE freadline (h : ADDRESS; VAR buf : ARRAY OF CHAR;
+                     max : CARDINAL) : CARDINAL;
+BEGIN
+  RETURN m2freadline(h, buf, max)
+END freadline;
+
 PROCEDURE fputc (h : ADDRESS; c : CHAR);
 BEGIN
   m2fputc(h, c)
@@ -97,6 +108,11 @@ BEGIN
   RETURN m2fgetc(h)
 END fgetc;
 
+PROCEDURE freadint (h : ADDRESS) : INTEGER;
+BEGIN
+  RETURN m2freadint(h)
+END freadint;
+
 PROCEDURE fwriteint (h : ADDRESS; v : INTEGER);
 BEGIN
   m2fwriteint(h, v)

+ 22 - 0
runtime/syslib/shim.c

@@ -150,6 +150,28 @@ long m2fwriteln(long h)
     return 0;
 }
 
+/* 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)
+{
+    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; }
+    while (n < max) {
+        c = fgetc((FILE *)h);
+        if (c == EOF) break;
+        if (c == '\n') break;
+        if (c == '\r') continue;
+        dst[n++] = (char)c;
+    }
+    dst[n] = 0;
+    desc[0] = n;
+    return n;
+}
+
 /* Bulk read: up to max bytes into the descriptor's data area; the
    descriptor's header is set to the number actually read. */
 long m2fread(long h, long *desc, long max)

+ 17 - 4
stdlib/iochan.def

@@ -1,18 +1,31 @@
 DEFINITION MODULE IOChan;
-(* A minimal channel abstraction over the runtime streams (an
-   ISO-style ChanId).  ChanId is opaque; the standard channels are
-   the standard input/output/error streams. *)
+(* A channel abstraction over the runtime streams and files (an
+   ISO-style ChanId).  ChanId is opaque: the standard channels wrap
+   stdio streams, and OpenRead/OpenWrite wrap a file.  A failed open
+   returns a closed channel (IsOpen = FALSE); operations on a closed
+   channel are safe no-ops (ReadChar returns 0C). *)
 
 TYPE ChanId;
 
+(* --- standard channels --- *)
 PROCEDURE StdInChan() : ChanId;
 PROCEDURE StdOutChan() : ChanId;
 PROCEDURE StdErrChan() : ChanId;
 
+(* --- file channels --- *)
+PROCEDURE OpenRead(VAR name : ARRAY OF CHAR) : ChanId;
+PROCEDURE OpenWrite(VAR name : ARRAY OF CHAR) : ChanId;
+PROCEDURE Close(VAR cid : ChanId);
+PROCEDURE IsOpen(cid : ChanId) : BOOLEAN;
+
+(* --- operations --- *)
 PROCEDURE ReadChar(cid : ChanId) : CHAR;
+PROCEDURE ReadString(cid : ChanId; VAR s : ARRAY OF CHAR);  (* one line *)
+PROCEDURE ReadInt(cid : ChanId) : INTEGER;
+
 PROCEDURE WriteChar(cid : ChanId; c : CHAR);
 PROCEDURE WriteString(cid : ChanId; VAR s : ARRAY OF CHAR);
-PROCEDURE WriteLn(cid : ChanId);
 PROCEDURE WriteInt(cid : ChanId; n : INTEGER);
+PROCEDURE WriteLn(cid : ChanId);
 
 END IOChan.

+ 37 - 5
stdlib/iochan.mod

@@ -21,11 +21,43 @@ BEGIN
   RETURN SysShim.stderr()
 END StdErrChan;
 
+PROCEDURE OpenRead(VAR name : ARRAY OF CHAR) : ChanId;
+BEGIN
+  RETURN SysShim.fopenread(name)
+END OpenRead;
+
+PROCEDURE OpenWrite(VAR name : ARRAY OF CHAR) : ChanId;
+BEGIN
+  RETURN SysShim.fopenwrite(name)
+END OpenWrite;
+
+PROCEDURE Close(VAR cid : ChanId);
+BEGIN
+  SysShim.fclose(cid);
+  cid := NIL
+END Close;
+
+PROCEDURE IsOpen(cid : ChanId) : BOOLEAN;
+BEGIN
+  RETURN cid # NIL
+END IsOpen;
+
 PROCEDURE ReadChar(cid : ChanId) : CHAR;
 BEGIN
   RETURN SysShim.fgetc(cid)
 END ReadChar;
 
+PROCEDURE ReadString(cid : ChanId; VAR s : ARRAY OF CHAR);
+BEGIN
+  IF SysShim.freadline(cid, s, HIGH(s) + 1) < 0 THEN
+  END
+END ReadString;
+
+PROCEDURE ReadInt(cid : ChanId) : INTEGER;
+BEGIN
+  RETURN SysShim.freadint(cid)
+END ReadInt;
+
 PROCEDURE WriteChar(cid : ChanId; c : CHAR);
 BEGIN
   SysShim.fputc(cid, c)
@@ -36,14 +68,14 @@ BEGIN
   SysShim.fputs(cid, s)
 END WriteString;
 
-PROCEDURE WriteLn(cid : ChanId);
-BEGIN
-  SysShim.fwriteln(cid)
-END WriteLn;
-
 PROCEDURE WriteInt(cid : ChanId; n : INTEGER);
 BEGIN
   SysShim.fwriteint(cid, n)
 END WriteInt;
 
+PROCEDURE WriteLn(cid : ChanId);
+BEGIN
+  SysShim.fwriteln(cid)
+END WriteLn;
+
 END IOChan.