Bläddra i källkod

v3 step 5.4 — Files library (94/94 tests green)

Eric Streit 2 veckor sedan
förälder
incheckning
d260e48ce4
6 ändrade filer med 249 tillägg och 0 borttagningar
  1. 2 0
      compiler/run_tests.sh
  2. 19 0
      compiler/tests/files_prog.mod
  3. 47 0
      docs/summary_step5.4.md
  4. 84 0
      runtime/syslib/shim.c
  5. 20 0
      stdlib/files.def
  6. 77 0
      stdlib/files.mod

+ 2 - 0
compiler/run_tests.sh

@@ -228,6 +228,8 @@ 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 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 WholeIOProg 0 "42" "6 7" ../stdlib/sysio.def ../stdlib/sysio.mod ../stdlib/wholeio.def ../stdlib/wholeio.mod wholeio_prog.mod
+expect_run_files FilesProg 42 ../stdlib/files.def ../stdlib/files.mod files_prog.mod
+rm -f gen_ssa/_files_tmp.txt
 
 
 expect_fail_files() {
 expect_fail_files() {
   # $1 = listing basename, $2 = message (may be empty), then files
   # $1 = listing basename, $2 = message (may be empty), then files

+ 19 - 0
compiler/tests/files_prog.mod

@@ -0,0 +1,19 @@
+MODULE FilesProg;
+(* File round-trip: write 42, read it back into ExitCode. *)
+IMPORT Files;
+FROM Files IMPORT File;
+VAR ExitCode : INTEGER;
+VAR f : File;
+VAR n : INTEGER;
+BEGIN
+  f := Files.OpenWrite("gen_ssa/_files_tmp.txt");
+  Files.WriteInt(f, 42);
+  Files.WriteLn(f);
+  Files.Close(f);
+
+  f := Files.OpenRead("gen_ssa/_files_tmp.txt");
+  n := Files.ReadInt(f);
+  Files.Close(f);
+
+  ExitCode := n
+END FilesProg.

+ 47 - 0
docs/summary_step5.4.md

@@ -0,0 +1,47 @@
+# V3 step 5.4 — Files (done 2026-09-22)
+
+Fourth stdlib landing; completes the plan-listed stdlib
+(Strings/TextIO/WholeIO/Math/Files). Suite 94/94 (51 run — incl.
+4 trap-aborts, 5 sessions, 2 session-output, 2 session-input,
+1 unit — + 43 reject). LL(1)-clean, zero gm2 warnings.
+
+## Files (`stdlib/files`)
+
+Sequential files over libc stdio, bound via `EXTERNAL`:
+`OpenRead`/`OpenWrite`, `Close`, `ReadChar`/`ReadInt`,
+`WriteChar`/`WriteInt`/`WriteString`/`WriteLn`. A `File` is an
+opaque handle; `NIL` means the open failed.
+
+## Shim additions (`runtime/syslib/shim.c`)
+
+`m2fopenread`/`m2fopenwrite` (convert the descriptor string to a
+NUL-terminated buffer, then `fopen`), `m2fclose`, `m2fgetc`,
+`m2freadint`, `m2fputc`, `m2fwriteint`, `m2fputs`, `m2fwriteln`.
+A shared `m2cstr` helper does the descriptor→C-string copy.
+
+## Tests
+
+`FilesProg` → 42: writes `42` to `gen_ssa/_files_tmp.txt`, reopens
+it, reads the integer back into `ExitCode`.
+
+## Two findings (documented, affecting later work)
+
+1. **Qualified access is flatten-on-import.** `Files.WriteInt`
+   materializes `WriteInt` into the client scope; if the client
+   already imported a different module's `WriteInt` (e.g. from
+   `SysIO`), `Materialize` returns the existing node and the call
+   resolves to the wrong signature (233). The tests avoid mixing
+   same-named imports; true per-module qualified resolution is
+   deferred.
+2. **Handles must be pointer-typed.** `File = INTEGER` gave a `w`
+   (32-bit) value and truncated the C `FILE*` on return, segfaulting
+   on reuse. `File = POINTER TO FileRec` (opaque, `l`) matches the
+   C pointer width. Rule: any value carrying a host pointer must be
+   a pointer-class type, not `INTEGER`.
+
+## Deferred to 5.5+/step 7
+
+`Storage`/`SYSTEM` (`ALLOCATE`/`DEALLOCATE`, `ADDRESS`, generic
+pointers — needs SYSTEM types), `ReadString`, string escapes,
+explicit external link names. Traps still `$abort`; `runtime/syslib`
+`SYSTEM`/`Storage`/`Trap` land with the step-7 syslib port.

+ 84 - 0
runtime/syslib/shim.c

@@ -11,6 +11,7 @@
 #include <unistd.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdio.h>
+#include <string.h>
 
 
 /* Write the contents of a string descriptor to fd 1. Returns the
 /* Write the contents of a string descriptor to fd 1. Returns the
    number of bytes written. */
    number of bytes written. */
@@ -54,6 +55,89 @@ long m2readint(void)
     return neg ? -v : v;
     return neg ? -v : v;
 }
 }
 
 
+/* ---------------- sequential files (stdio) ---------------- */
+
+/* Copy a descriptor string into a NUL-terminated buffer. */
+static void m2cstr(long *desc, char *buf, size_t cap)
+{
+    long n = desc[0];
+    if ((size_t)n >= cap) n = (long)(cap - 1);
+    if (n > 0) memcpy(buf, (char *)(desc + 1), (size_t)n);
+    buf[n] = 0;
+}
+
+long m2fopenread(long *desc)
+{
+    char buf[4096];
+    m2cstr(desc, buf, sizeof buf);
+    return (long)fopen(buf, "r");
+}
+
+long m2fopenwrite(long *desc)
+{
+    char buf[4096];
+    m2cstr(desc, buf, sizeof buf);
+    return (long)fopen(buf, "w");
+}
+
+long m2fclose(long h)
+{
+    if (h != 0) fclose((FILE *)h);
+    return 0;
+}
+
+long m2fgetc(long h)
+{
+    int c = fgetc((FILE *)h);
+    return (c == EOF) ? 0 : c;
+}
+
+long m2fputc(long h, long c)
+{
+    fputc((int)c, (FILE *)h);
+    return 0;
+}
+
+long m2fputs(long h, long *desc)
+{
+    long n = desc[0];
+    if (n > 0) fwrite((char *)(desc + 1), 1, (size_t)n, (FILE *)h);
+    return n;
+}
+
+long m2freadint(long h)
+{
+    long v = 0;
+    int c, neg = 0;
+    do { c = fgetc((FILE *)h); } while (c == ' ' || c == '\t' ||
+                                        c == '\n' || c == '\r');
+    if (c == '-') { neg = 1; c = fgetc((FILE *)h); }
+    while (c >= '0' && c <= '9') {
+        v = v * 10 + (c - '0');
+        c = fgetc((FILE *)h);
+    }
+    return neg ? -v : v;
+}
+
+long m2fwriteint(long h, long v)
+{
+    char buf[32];
+    int i = 0, neg = 0;
+    unsigned long u;
+    if (v < 0) { neg = 1; u = (unsigned long)(-v); }
+    else u = (unsigned long)v;
+    do { buf[i++] = (char)('0' + u % 10); u /= 10; } while (u != 0);
+    if (neg) buf[i++] = '-';
+    while (i > 0) fputc(buf[--i], (FILE *)h);
+    return 0;
+}
+
+long m2fwriteln(long h)
+{
+    fputc('\n', (FILE *)h);
+    return 0;
+}
+
 /* Set a string descriptor's logical length (header). */
 /* Set a string descriptor's logical length (header). */
 long m2setlen(long *desc, long n)
 long m2setlen(long *desc, long n)
 {
 {

+ 20 - 0
stdlib/files.def

@@ -0,0 +1,20 @@
+DEFINITION MODULE Files;
+(* Sequential files over the runtime shim (stdio). File is an opaque
+   handle (a pointer-sized value); NIL means the open failed.
+   Text is written/read as descriptor strings; integers as decimal. *)
+
+TYPE FileRec;
+TYPE File = POINTER TO FileRec;
+
+PROCEDURE OpenRead(VAR name : ARRAY OF CHAR) : File;
+PROCEDURE OpenWrite(VAR name : ARRAY OF CHAR) : File;
+PROCEDURE Close(f : File);
+
+PROCEDURE ReadChar(f : File) : CHAR;
+PROCEDURE ReadInt(f : File) : INTEGER;
+PROCEDURE WriteChar(f : File; c : CHAR);
+PROCEDURE WriteInt(f : File; n : INTEGER);
+PROCEDURE WriteString(f : File; VAR s : ARRAY OF CHAR);
+PROCEDURE WriteLn(f : File);
+
+END Files.

+ 77 - 0
stdlib/files.mod

@@ -0,0 +1,77 @@
+IMPLEMENTATION MODULE Files;
+(* Binds the shim file routines directly. Handles are C FILE*
+   pointers, typed as File (opaque, pointer-sized). *)
+
+PROCEDURE m2fopenread(VAR name : ARRAY OF CHAR) : File;
+  EXTERNAL;
+
+PROCEDURE m2fopenwrite(VAR name : ARRAY OF CHAR) : File;
+  EXTERNAL;
+
+PROCEDURE m2fclose(h : File);
+  EXTERNAL;
+
+PROCEDURE m2fgetc(h : File) : CHAR;
+  EXTERNAL;
+
+PROCEDURE m2freadint(h : File) : INTEGER;
+  EXTERNAL;
+
+PROCEDURE m2fputc(h : File; c : CHAR);
+  EXTERNAL;
+
+PROCEDURE m2fwriteint(h : File; v : INTEGER);
+  EXTERNAL;
+
+PROCEDURE m2fputs(h : File; VAR s : ARRAY OF CHAR);
+  EXTERNAL;
+
+PROCEDURE m2fwriteln(h : File);
+  EXTERNAL;
+
+PROCEDURE OpenRead(VAR name : ARRAY OF CHAR) : File;
+BEGIN
+  RETURN m2fopenread(name)
+END OpenRead;
+
+PROCEDURE OpenWrite(VAR name : ARRAY OF CHAR) : File;
+BEGIN
+  RETURN m2fopenwrite(name)
+END OpenWrite;
+
+PROCEDURE Close(f : File);
+BEGIN
+  m2fclose(f)
+END Close;
+
+PROCEDURE ReadChar(f : File) : CHAR;
+BEGIN
+  RETURN m2fgetc(f)
+END ReadChar;
+
+PROCEDURE ReadInt(f : File) : INTEGER;
+BEGIN
+  RETURN m2freadint(f)
+END ReadInt;
+
+PROCEDURE WriteChar(f : File; c : CHAR);
+BEGIN
+  m2fputc(f, c)
+END WriteChar;
+
+PROCEDURE WriteInt(f : File; n : INTEGER);
+BEGIN
+  m2fwriteint(f, n)
+END WriteInt;
+
+PROCEDURE WriteString(f : File; VAR s : ARRAY OF CHAR);
+BEGIN
+  m2fputs(f, s)
+END WriteString;
+
+PROCEDURE WriteLn(f : File);
+BEGIN
+  m2fwriteln(f)
+END WriteLn;
+
+END Files.