瀏覽代碼

stdlib: Strings Slice/Insert/Replace/Capitalize, SysClock, more Math

- Strings: Slice (Python-style s[start:stop], negative indices,
  stop exclusive, clamped), Insert, Replace, Capitalize.
- SysClock (new): Time() (epoch seconds), Clock() (CPU ticks,
  ClocksPerSec=1000000); shim gains m2time/m2clock (+ <time.h>).
- Math: log10, atan2, sinh, cosh, tanh, hypot, floor, ceil, trunc,
  round, fmod.
- tests: strings_slice_prog (255), math2_prog (31), sysclock_prog (3)
  -> 151/151. Fixpoint OK (2,337,882 bytes).
Eric Streit 1 周之前
父節點
當前提交
6c04ef7454

+ 3 - 0
compiler/run_tests.sh

@@ -239,8 +239,11 @@ expect_run_files StorageProg 42 ../stdlib/storage.def ../stdlib/storage.mod stor
 expect_run_files ClashProg 60 d_clash_a.def d_clash_a.mod d_clash_b.def d_clash_b.mod d_clash_prog.mod
 expect_run_files_out Hello 0 "Hello, Modula-2!" ../stdlib/sysio.def ../stdlib/sysio.mod hello.mod
 expect_run_files_out StringsProg 0 "Hello World" ../stdlib/sysio.def ../stdlib/sysio.mod ../stdlib/strings.def ../stdlib/strings.mod strings_prog.mod
+expect_run_files StringsSliceProg 255 ../stdlib/strings.def ../stdlib/strings.mod strings_slice_prog.mod
 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 Math2Prog 31 ../stdlib/math.def ../stdlib/math.mod math2_prog.mod
+expect_run_files SysClockProg 3 ../stdlib/sysclock.def ../stdlib/sysclock.mod sysclock_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

+ 12 - 0
compiler/tests/math2_prog.mod

@@ -0,0 +1,12 @@
+MODULE Math2Prog;
+(* Math additions: hypot/floor/ceil/trunc/log10. Exit 31. *)
+IMPORT Math;
+VAR ExitCode : INTEGER;
+BEGIN
+  ExitCode := 0;
+  IF Math.hypot(3.0, 4.0) = 5.0 THEN ExitCode := ExitCode + 1 END;
+  IF Math.floor(3.7) = 3.0 THEN ExitCode := ExitCode + 2 END;
+  IF Math.ceil(3.2) = 4.0 THEN ExitCode := ExitCode + 4 END;
+  IF Math.trunc(-3.7) = -3.0 THEN ExitCode := ExitCode + 8 END;
+  IF Math.log10(1000.0) = 3.0 THEN ExitCode := ExitCode + 16 END
+END Math2Prog.

+ 36 - 0
compiler/tests/strings_slice_prog.mod

@@ -0,0 +1,36 @@
+MODULE StringsSliceProg;
+(* Strings.Slice (Python-style) + Insert/Replace/Capitalize. Exit 255. *)
+IMPORT Strings;
+VAR ExitCode : INTEGER;
+VAR s, d : ARRAY [0 .. 31] OF CHAR;
+BEGIN
+  ExitCode := 0;
+  Strings.Assign("Hello, World", s);
+
+  Strings.Slice(s, 0, 5, d);       (* "Hello" *)
+  IF Strings.Equal(d, "Hello") THEN ExitCode := ExitCode + 1 END;
+
+  Strings.Slice(s, 7, 12, d);      (* "World" *)
+  IF Strings.Equal(d, "World") THEN ExitCode := ExitCode + 2 END;
+
+  Strings.Slice(s, -5, 12, d);     (* "World" (negative start) *)
+  IF Strings.Equal(d, "World") THEN ExitCode := ExitCode + 4 END;
+
+  Strings.Slice(s, 0, -8, d);      (* "Hell"  (negative stop: 12-8=4) *)
+  IF Strings.Equal(d, "Hell") THEN ExitCode := ExitCode + 8 END;
+
+  Strings.Slice(s, 3, 3, d);       (* "" *)
+  IF Strings.Equal(d, "") THEN ExitCode := ExitCode + 16 END;
+
+  Strings.Assign("abc", d);
+  Strings.Insert("XY", d, 1);      (* "aXYbc" *)
+  IF Strings.Equal(d, "aXYbc") THEN ExitCode := ExitCode + 32 END;
+
+  Strings.Assign("hello", d);
+  Strings.Replace("XYZ", d, 2);    (* "heXYZ" *)
+  IF Strings.Equal(d, "heXYZ") THEN ExitCode := ExitCode + 64 END;
+
+  Strings.Assign("hELLO wORLD", d);
+  Strings.Capitalize(d);           (* "Hello world" *)
+  IF Strings.Equal(d, "Hello world") THEN ExitCode := ExitCode + 128 END
+END StringsSliceProg.

+ 11 - 0
compiler/tests/sysclock_prog.mod

@@ -0,0 +1,11 @@
+MODULE SysClockProg;
+(* SysClock: wall-clock and processor time. Exit 3. *)
+IMPORT SysClock;
+VAR ExitCode : INTEGER;
+VAR t : LONGINT;
+BEGIN
+  ExitCode := 0;
+  t := SysClock.Time();
+  IF t > 1600000000 THEN ExitCode := ExitCode + 1 END;   (* after 2020 *)
+  IF SysClock.Clock() >= 0 THEN ExitCode := ExitCode + 2 END
+END SysClockProg.

+ 7 - 4
docs/features.md

@@ -1,4 +1,4 @@
-# m2compiler-V3 — feature status (at `v3-val-conversions`, 148/148 green)
+# m2compiler-V3 — feature status (at `v3-stdlib-more`, 151/151 green)
 
 Pipeline: Coco/R `M2.atg` (1748 lines, 73 productions) → `gm2`-built
 `M2` → QBE `.ssa` → `qbe` → `cc` → run. `SymTab.mod` 1623 lines,
@@ -94,10 +94,13 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
 
 ## Library
 - ✅ `runtime/syslib`: `SysShim`, `FileIO`, `Utf8` (UTF-8 codec).
-- ✅ `stdlib`: `SysIO`, `TextIO` (+ UTF-8 `U*`), `WholeIO`, `Math`,
-  `Strings`, `CharClass`, `Files` (layered on `IOChan`),
+- ✅ `stdlib`: `SysIO`, `TextIO` (+ UTF-8 `U*`), `WholeIO`,
+  `Math` (incl. `log10`/`atan2`/`sinh`/`cosh`/`tanh`/`hypot`/
+  `floor`/`ceil`/`trunc`/`round`/`fmod`), `Strings` (incl. `Slice`
+  (Python-style), `Insert`, `Replace`, `Capitalize`), `CharClass`,
+  `Files` (layered on `IOChan`),
   `Conversions` (ISO `ConvResults` statuses + BOOLEAN shortcuts),
-  `RealIO`, `ProgramArgs`,
+  `RealIO`, `ProgramArgs`, `SysClock`,
   `IOChan` (streams + files; `Position`/`Seek`/`Rewind`), `Storage`.
 
 ## Not started

+ 44 - 0
docs/summary_stdlib-more.md

@@ -0,0 +1,44 @@
+# Step: stdlib — Strings additions, SysClock, more Math
+
+Tag `v3-stdlib-more`. Suite **151/151**; fixpoint **OK**
+(image **2,337,882 bytes**).
+
+## `Strings`
+
+- **`Slice(s, start, stop, dst)`** — Python-style `s[start:stop]`:
+  0-based, negative indices count from the end (`-1` = last),
+  `stop` is exclusive, both clamp to `[0, Length]`; the result is
+  truncated to `dst`'s capacity.  (`"Hello, World"[0:5]` → `Hello`,
+  `[-5:12]` → `World`, `[0:-8]` → `Hell`, `[3:3]` → `""`.)
+- **`Insert(src, dst, pos)`** — inserts `src` at 0-based `pos`
+  (clamped), truncated to `dst`'s capacity.
+- **`Replace(src, dst, pos)`** — overwrites `dst` from `pos` with
+  `src`, extending (and re-terminating) if it runs past the old end.
+- **`Capitalize(s)`** — first character upper, the rest lower (ASCII).
+
+## `SysClock` (new)
+
+- `Time() : LONGINT` — seconds since the Unix epoch.
+- `Clock() : LONGINT` — processor time in ticks,
+  `ClocksPerSec = 1000000`.
+- Bound to shim `m2time`/`m2clock` (libc `time`/`clock`); the shim
+  gained `#include <time.h>` and the two functions.
+
+## `Math`
+
+Added: `log10`, `atan2`, `sinh`, `cosh`, `tanh`, `hypot`, `floor`,
+`ceil`, `trunc`, `round`, `fmod` (all bound to libm).
+
+## Tests
+
+- `strings_slice_prog.mod` (255): `Slice` (incl. negatives and empty),
+  `Insert`, `Replace`, `Capitalize`.
+- `math2_prog.mod` (31): `hypot`/`floor`/`ceil`/`trunc`/`log10`.
+- `sysclock_prog.mod` (3): `Time` after 2020, `Clock` >= 0.
+
+## Files
+
+`stdlib/strings.def`/`.mod`, `stdlib/sysclock.def`/`.mod`,
+`stdlib/math.def`/`.mod`, `runtime/syslib/shim.c`,
+`compiler/tests/{strings_slice_prog,math2_prog,sysclock_prog}.mod`,
+`compiler/run_tests.sh`, `docs/features.md`.

+ 16 - 0
runtime/syslib/shim.c

@@ -12,6 +12,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <time.h>
 
 /* Write the contents of a string descriptor to fd 1. Returns the
    number of bytes written. */
@@ -314,6 +315,21 @@ long m2exit(long code)
     return 0;
 }
 
+/* ---------------- clock (SysClock) ---------------- */
+
+/* Wall-clock time in seconds since the Unix epoch. */
+long m2time(void)
+{
+    return (long)time(NULL);
+}
+
+/* Processor time used so far, in clock ticks (CLOCKS_PER_SEC per
+   second).  The scaling is exposed as a constant by SysClock. */
+long m2clock(void)
+{
+    return (long)clock();
+}
+
 /* String content equality: 1 when the NUL-terminated contents of the
    two descriptors match. */
 long m2streq(long *a, long *b)

+ 12 - 0
stdlib/math.def

@@ -16,4 +16,16 @@ PROCEDURE arctan(x : REAL) : REAL;
 PROCEDURE power(x, y : REAL) : REAL;
 PROCEDURE abs(x : REAL) : REAL;
 
+PROCEDURE log10(x : REAL) : REAL;
+PROCEDURE atan2(y, x : REAL) : REAL;
+PROCEDURE sinh(x : REAL) : REAL;
+PROCEDURE cosh(x : REAL) : REAL;
+PROCEDURE tanh(x : REAL) : REAL;
+PROCEDURE hypot(x, y : REAL) : REAL;
+PROCEDURE floor(x : REAL) : REAL;
+PROCEDURE ceil(x : REAL) : REAL;
+PROCEDURE trunc(x : REAL) : REAL;
+PROCEDURE round(x : REAL) : REAL;
+PROCEDURE fmod(x, y : REAL) : REAL;
+
 END Math.

+ 33 - 0
stdlib/math.mod

@@ -35,6 +35,39 @@ PROCEDURE pow(x, y : REAL) : REAL;
 PROCEDURE fabs(x : REAL) : REAL;
   EXTERNAL;
 
+PROCEDURE log10(x : REAL) : REAL;
+  EXTERNAL;
+
+PROCEDURE atan2(y, x : REAL) : REAL;
+  EXTERNAL;
+
+PROCEDURE sinh(x : REAL) : REAL;
+  EXTERNAL;
+
+PROCEDURE cosh(x : REAL) : REAL;
+  EXTERNAL;
+
+PROCEDURE tanh(x : REAL) : REAL;
+  EXTERNAL;
+
+PROCEDURE hypot(x, y : REAL) : REAL;
+  EXTERNAL;
+
+PROCEDURE floor(x : REAL) : REAL;
+  EXTERNAL;
+
+PROCEDURE ceil(x : REAL) : REAL;
+  EXTERNAL;
+
+PROCEDURE trunc(x : REAL) : REAL;
+  EXTERNAL;
+
+PROCEDURE round(x : REAL) : REAL;
+  EXTERNAL;
+
+PROCEDURE fmod(x, y : REAL) : REAL;
+  EXTERNAL;
+
 PROCEDURE arcsin(x : REAL) : REAL;
 BEGIN
   RETURN asin(x)

+ 18 - 0
stdlib/strings.def

@@ -32,4 +32,22 @@ PROCEDURE Equal(VAR s1, s2 : ARRAY OF CHAR) : BOOLEAN;
 PROCEDURE Pos(VAR pattern, s : ARRAY OF CHAR) : INTEGER;
 (* 1-based index of the leftmost occurrence of pattern in s, or 0. *)
 
+PROCEDURE Slice(VAR s : ARRAY OF CHAR; start, stop : INTEGER;
+                VAR dst : ARRAY OF CHAR);
+(* dst := s[start:stop], Python-style: start/stop are 0-based and may
+   be negative (counting from the end, -1 = last character); stop is
+   exclusive; both clamp to [0, Length(s)]. The result is truncated to
+   dst's capacity. *)
+
+PROCEDURE Insert(VAR src, dst : ARRAY OF CHAR; pos : INTEGER);
+(* Inserts src into dst at 0-based position pos (clamped), truncating
+   to dst's capacity. *)
+
+PROCEDURE Replace(VAR src, dst : ARRAY OF CHAR; pos : INTEGER);
+(* Overwrites dst from 0-based position pos with src (extending if
+   src runs past dst's current end). *)
+
+PROCEDURE Capitalize(VAR s : ARRAY OF CHAR);
+(* First character upper case, the rest lower case (ASCII). *)
+
 END Strings.

+ 88 - 0
stdlib/strings.mod

@@ -122,6 +122,94 @@ BEGIN
   RETURN 0
 END Pos;
 
+PROCEDURE Slice (VAR s : ARRAY OF CHAR; start, stop : INTEGER;
+                 VAR dst : ARRAY OF CHAR);
+VAR n, i, k, cap : INTEGER;
+BEGIN
+  n := Length(s);
+  IF start < 0 THEN start := n + start END;
+  IF stop < 0 THEN stop := n + stop END;
+  IF start < 0 THEN start := 0 END;
+  IF start > n THEN start := n END;
+  IF stop < 0 THEN stop := 0 END;
+  IF stop > n THEN stop := n END;
+  IF stop < start THEN stop := start END;
+  cap := HIGH(dst);
+  i := start; k := 0;
+  WHILE (i < stop) AND (k < cap) DO
+    dst[k] := s[i];
+    INC(i); INC(k)
+  END;
+  dst[k] := CHR(0)
+END Slice;
+
+PROCEDURE Insert (VAR src, dst : ARRAY OF CHAR; pos : INTEGER);
+VAR n, m, cap, nl, i : INTEGER;
+BEGIN
+  n := Length(dst);
+  m := Length(src);
+  cap := HIGH(dst);
+  IF pos < 0 THEN pos := 0 END;
+  IF pos > n THEN pos := n END;
+  IF m < 0 THEN m := 0 END;
+  IF n + m > cap THEN m := cap - n END;   (* only what fits *)
+  IF m < 0 THEN m := 0 END;
+  (* shift the tail right by m, from the end backwards *)
+  i := n;
+  WHILE i > pos DO
+    DEC(i);
+    IF i + m <= cap THEN dst[i + m] := dst[i] END
+  END;
+  i := 0;
+  WHILE i < m DO
+    dst[pos + i] := src[i];
+    INC(i)
+  END;
+  nl := n + m;
+  IF nl > cap THEN nl := cap END;
+  dst[nl] := CHR(0)
+END Insert;
+
+PROCEDURE Replace (VAR src, dst : ARRAY OF CHAR; pos : INTEGER);
+VAR n, m, cap, nl, i : INTEGER;
+BEGIN
+  n := Length(dst);
+  m := Length(src);
+  cap := HIGH(dst);
+  IF pos < 0 THEN pos := 0 END;
+  IF pos > n THEN pos := n END;
+  i := 0;
+  WHILE (i < m) AND (pos + i < cap) DO
+    dst[pos + i] := src[i];
+    INC(i)
+  END;
+  nl := pos + m;
+  IF nl > cap THEN nl := cap END;
+  IF nl > n THEN dst[nl] := CHR(0) END
+END Replace;
+
+PROCEDURE Capitalize (VAR s : ARRAY OF CHAR);
+VAR i, n : INTEGER;
+    ch : CHAR;
+BEGIN
+  n := Length(s);
+  i := 0;
+  WHILE i < n DO
+    ch := s[i];
+    IF i = 0 THEN
+      IF (ch >= "a") AND (ch <= "z") THEN
+        ch := CHR(ORD(ch) - 32)
+      END
+    ELSE
+      IF (ch >= "A") AND (ch <= "Z") THEN
+        ch := CHR(ORD(ch) + 32)
+      END
+    END;
+    s[i] := ch;
+    INC(i)
+  END
+END Capitalize;
+
 PROCEDURE Compare (VAR s1, s2 : ARRAY OF CHAR) : INTEGER;
 VAR i, n1, n2, n : INTEGER;
 BEGIN

+ 16 - 0
stdlib/sysclock.def

@@ -0,0 +1,16 @@
+DEFINITION MODULE SysClock;
+(* Wall-clock and processor time, bound to the runtime shim (libc
+   time/clock). *)
+
+CONST ClocksPerSec = 1000000;
+(* Processor-time ticks per second on the supported host (libc
+   CLOCKS_PER_SEC is 1,000,000). *)
+
+PROCEDURE Time() : LONGINT;
+(* Seconds since the Unix epoch (1 January 1970, 00:00 UTC). *)
+
+PROCEDURE Clock() : LONGINT;
+(* Processor time used by the program so far, in ticks of
+   ClocksPerSec per second. *)
+
+END SysClock.

+ 20 - 0
stdlib/sysclock.mod

@@ -0,0 +1,20 @@
+IMPLEMENTATION MODULE SysClock;
+(* Binds the shim time routines directly. *)
+
+PROCEDURE m2time() : LONGINT;
+  EXTERNAL;
+
+PROCEDURE m2clock() : LONGINT;
+  EXTERNAL;
+
+PROCEDURE Time() : LONGINT;
+BEGIN
+  RETURN m2time()
+END Time;
+
+PROCEDURE Clock() : LONGINT;
+BEGIN
+  RETURN m2clock()
+END Clock;
+
+END SysClock.