Browse Source

lang: VAL conversions across INTEGER/LONGINT/REAL (+ long->int narrowing)

- QbeGen: NarrowLong (=w copy), ConvRI (dtosi), ConvRL (dtosi),
  ConvLR (sltof); alongside WidenLong/ConvIR.
- M2.atg VAL: INTEGER<->LONGINT (widen/narrow), INTEGER/LONGINT<->REAL
  (truncate/widen) with real codegen (previously only same-class
  conversions were accepted, and nothing was emitted).
- Conversions simplified: StrToInt/StrToCard accumulate in LONGINT and
  narrow with VAL, dropping the pre-wrap overflow guard.
- tests: t_valconv (15) -> 148/148. Fixpoint OK (2,320,770 bytes).
Eric Streit 1 week ago
parent
commit
1c774a72ac

+ 1 - 0
compiler/run_tests.sh

@@ -80,6 +80,7 @@ expect_run t_setrange.mod 55
 expect_run t_classmethod.mod 7
 expect_run t_classinherit.mod 7
 expect_run t_virtual.mod 3
+expect_run t_valconv.mod 15
 expect_run t_classsibling.mod 7
 expect_run t_classinit.mod 42
 expect_run t_class.mod 0

+ 31 - 1
compiler/src/M2.atg

@@ -2514,7 +2514,37 @@ PRODUCTIONS
                                              ELSE
                                                c1 := SymTab.ClassOf(et);
                                                c2 := SymTab.ClassOf(vt);
-                                               IF ((c1 = SymTab.ClInt)
+                                               IF (c1 = SymTab.ClInt)
+  AND (c2 = SymTab.ClLong) THEN
+                                                 QbeGen.WidenLong(q, qa);
+                                                 QbeGen.CopyOp(qa, q);
+                                                 t := vt
+                                               ELSIF (c1 = SymTab.ClLong)
+  AND (c2 = SymTab.ClInt) THEN
+                                                 QbeGen.NarrowLong(q, qa);
+                                                 QbeGen.CopyOp(qa, q);
+                                                 t := vt
+                                               ELSIF (c1 = SymTab.ClInt)
+  AND (c2 = SymTab.ClReal) THEN
+                                                 QbeGen.ConvIR(q, qa);
+                                                 QbeGen.CopyOp(qa, q);
+                                                 t := vt
+                                               ELSIF (c1 = SymTab.ClLong)
+  AND (c2 = SymTab.ClReal) THEN
+                                                 QbeGen.ConvLR(q, qa);
+                                                 QbeGen.CopyOp(qa, q);
+                                                 t := vt
+                                               ELSIF (c1 = SymTab.ClReal)
+  AND (c2 = SymTab.ClInt) THEN
+                                                 QbeGen.ConvRI(q, qa);
+                                                 QbeGen.CopyOp(qa, q);
+                                                 t := vt
+                                               ELSIF (c1 = SymTab.ClReal)
+  AND (c2 = SymTab.ClLong) THEN
+                                                 QbeGen.ConvRL(q, qa);
+                                                 QbeGen.CopyOp(qa, q);
+                                                 t := vt
+                                               ELSIF ((c1 = SymTab.ClInt)
                                                    OR (c1 =
                                                       SymTab.ClChar)
                                                    OR (c1 =

+ 154 - 124
compiler/src/M2.lst

@@ -2531,131 +2531,161 @@ Listing:
  2514                                               ELSE
  2515                                                 c1 := SymTab.ClassOf(et);
  2516                                                 c2 := SymTab.ClassOf(vt);
- 2517                                                 IF ((c1 = SymTab.ClInt)
- 2518                                                     OR (c1 =
- 2519                                                        SymTab.ClChar)
- 2520                                                     OR (c1 =
- 2521                                                        SymTab.ClBool)
- 2522                                                     OR (c1 =
- 2523                                                        SymTab.ClEnum))
- 2524   AND ((c2 = SymTab.ClInt)
- 2525                                                     OR (c2 =
- 2526                                                        SymTab.ClChar)
- 2527                                                     OR (c2 =
- 2528                                                        SymTab.ClBool)
- 2529                                                     OR (c2 =
- 2530                                                        SymTab.ClEnum)) THEN
+ 2517                                                 IF (c1 = SymTab.ClInt)
+ 2518    AND (c2 = SymTab.ClLong) THEN
+ 2519                                                   QbeGen.WidenLong(q, qa);
+ 2520                                                   QbeGen.CopyOp(qa, q);
+ 2521                                                   t := vt
+ 2522                                                 ELSIF (c1 = SymTab.ClLong)
+ 2523    AND (c2 = SymTab.ClInt) THEN
+ 2524                                                   QbeGen.NarrowLong(q, qa);
+ 2525                                                   QbeGen.CopyOp(qa, q);
+ 2526                                                   t := vt
+ 2527                                                 ELSIF (c1 = SymTab.ClInt)
+ 2528    AND (c2 = SymTab.ClReal) THEN
+ 2529                                                   QbeGen.ConvIR(q, qa);
+ 2530                                                   QbeGen.CopyOp(qa, q);
  2531                                                   t := vt
- 2532                                                 ELSIF (c1 = SymTab.ClPtr)
- 2533   AND (c2 = SymTab.ClPtr) THEN
- 2534                                                   t := vt
- 2535                                                 ELSIF (c1 = SymTab.ClReal)
- 2536   AND (c2 = SymTab.ClReal) THEN
- 2537                                                   t := vt
- 2538                                                 ELSE SemError(230);
- 2539                                                   t := SymTab.InvalidType
- 2540                                                 END
- 2541                                               END
- 2542                                             END; .)
- 2543      | "(" Expr<et, q> ")"               (. t := et; .)
- 2544      | SetLit<st, sq>                    (. t := st;
- 2545                                             QbeGen.CopyOp(sq, q); .)
- 2546      | ( "NOT" | "~" ) Fact<t2, q2>      (. IF SymTab.BoolCheck(t2) THEN
- 2547                                               t := SymTab.BoolType()
- 2548                                             ELSE SemError(212);
- 2549                                               t := SymTab.InvalidType END;
- 2550                                             IF t # SymTab.InvalidType THEN
- 2551                                               QbeGen.NotQ(q2, q)
- 2552                                             ELSE QbeGen.CopyOp("0", q)
- 2553                                             END; .) .
- 2554    (* Set literals are SET OF [0..255] (8 words); elements validated
- 2555       0..255 statically when foldable (222 otherwise), runtime trap
- 2556       for computed elements. Ranges always lower via SetRange. *)
- 2557    SetLit<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 2558      = "{"                               (. t := SymTab.NewSet(
- 2559                                               SymTab.NewSubR(0, 255));
- 2560                                             QbeGen.NewSetTemp(8, q);
- 2561                                             QbeGen.SetZero(q, 8); .)
- 2562        [ SetElem<t, q> { "," SetElem<t, q> } ]
- 2563        "}" .
- 2564    (* Typed set constructor: TypeName{ elems } — e.g. BITSET{0},
- 2565       BITSET{}.  The declared type (not SET OF [0..255]) sets the
- 2566       width and element span. *)
- 2567    TypedSetLit<vt: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 2568                                          (. VAR nw: CARDINAL; .)
- 2569      = "{"                               (. IF SymTab.ClassOf(vt) #
- 2570                                                SymTab.ClSet THEN
- 2571                                               SemError(230); nw := 8
- 2572                                             ELSE nw := SymTab.SetWords(vt);
- 2573                                               IF nw = 0 THEN nw := 8 END
- 2574                                             END;
- 2575                                             QbeGen.NewSetTemp(nw, q);
- 2576                                             QbeGen.SetZero(q, nw); .)
- 2577        [ SetElem<vt, q> { "," SetElem<vt, q> } ]
- 2578        "}" .
- 2579    SetElem<st: SymTab.TypeIndex; sq: QbeGen.QVal>         (. VAR et, et2: SymTab.TypeIndex;
- 2580                                               qe, q2: QbeGen.QVal;
- 2581                                               v, v2: INTEGER;
- 2582                                               lo: INTEGER;
- 2583                                               span: CARDINAL;
- 2584                                               cl, cl2: INTEGER;
- 2585                                               hasR: BOOLEAN; .)
- 2586      =                                   (. hasR := FALSE; .)
- 2587        Expr<et, qe>
- 2588        [ ".." Expr<et2, q2>              (. hasR := TRUE; .) ]
- 2589                                          (. lo := SymTab.SetBaseLo(st);
- 2590                                             span := SymTab.SetCount(st);
- 2591                                             IF (et = SymTab.InvalidType)
- 2592                                                OR (hasR AND (et2 =
- 2593                                                   SymTab.InvalidType)) THEN
- 2594                                             ELSE cl :=
- 2595                                                    SymTab.ClassOf(et);
- 2596                                               IF hasR THEN
- 2597                                                 cl2 :=
- 2598                                                   SymTab.ClassOf(et2)
- 2599                                               ELSE cl2 := SymTab.ClInt
- 2600                                               END;
- 2601                                               IF ((cl # SymTab.ClInt)
- 2602   AND (cl # SymTab.ClChar)
- 2603   AND (cl # SymTab.ClBool))
- 2604                                                  OR (hasR AND 
- 2605                                                     ((cl2
- 2606                                                       # SymTab.ClInt)
- 2607   AND (cl2
- 2608                                                        # SymTab.ClChar)
- 2609   AND (cl2
- 2610                                                        # SymTab.ClBool))) THEN
- 2611                                                 SemError(222)
- 2612                                               ELSIF hasR
- 2613   AND SymTab.ConstInt(qe, v)
- 2614   AND SymTab.ConstInt(q2,
- 2615                                                     v2)
- 2616   AND ((v < lo)
- 2617                                                     OR (v2 < lo)
- 2618                                                     OR (v >= lo +
- 2619                                                        VAL(INTEGER, span))
- 2620                                                     OR (v2 >= lo +
- 2621                                                        VAL(INTEGER, span))
- 2622                                                     OR (v > v2)) THEN
- 2623                                                 SemError(222)
- 2624                                                ELSIF hasR THEN
- 2625                                                  QbeGen.SetRange(sq, qe, q2,
- 2626                                                    lo, span)
- 2627                                                ELSIF SymTab.ConstInt(qe,
- 2628                                                        v)
- 2629   AND ((v < lo)
- 2630                                                      OR (v >= lo +
- 2631                                                         VAL(INTEGER,
- 2632                                                           span))) THEN
- 2633                                                  SemError(222)
- 2634                                                ELSE QbeGen.SetBit(sq, qe,
- 2635                                                  lo, span)
- 2636                                               END
- 2637                                             END; .) .
- 2638    GetIdent<VAR n: SymTab.Name>
- 2639      = ident                             (. LexName(n); .) .
- 2640  
- 2641  END M2.
+ 2532                                                 ELSIF (c1 = SymTab.ClLong)
+ 2533    AND (c2 = SymTab.ClReal) THEN
+ 2534                                                   QbeGen.ConvLR(q, qa);
+ 2535                                                   QbeGen.CopyOp(qa, q);
+ 2536                                                   t := vt
+ 2537                                                 ELSIF (c1 = SymTab.ClReal)
+ 2538    AND (c2 = SymTab.ClInt) THEN
+ 2539                                                   QbeGen.ConvRI(q, qa);
+ 2540                                                   QbeGen.CopyOp(qa, q);
+ 2541                                                   t := vt
+ 2542                                                 ELSIF (c1 = SymTab.ClReal)
+ 2543    AND (c2 = SymTab.ClLong) THEN
+ 2544                                                   QbeGen.ConvRL(q, qa);
+ 2545                                                   QbeGen.CopyOp(qa, q);
+ 2546                                                   t := vt
+ 2547                                                 ELSIF ((c1 = SymTab.ClInt)
+ 2548                                                     OR (c1 =
+ 2549                                                        SymTab.ClChar)
+ 2550                                                     OR (c1 =
+ 2551                                                        SymTab.ClBool)
+ 2552                                                     OR (c1 =
+ 2553                                                        SymTab.ClEnum))
+ 2554   AND ((c2 = SymTab.ClInt)
+ 2555                                                     OR (c2 =
+ 2556                                                        SymTab.ClChar)
+ 2557                                                     OR (c2 =
+ 2558                                                        SymTab.ClBool)
+ 2559                                                     OR (c2 =
+ 2560                                                        SymTab.ClEnum)) THEN
+ 2561                                                   t := vt
+ 2562                                                 ELSIF (c1 = SymTab.ClPtr)
+ 2563   AND (c2 = SymTab.ClPtr) THEN
+ 2564                                                   t := vt
+ 2565                                                 ELSIF (c1 = SymTab.ClReal)
+ 2566   AND (c2 = SymTab.ClReal) THEN
+ 2567                                                   t := vt
+ 2568                                                 ELSE SemError(230);
+ 2569                                                   t := SymTab.InvalidType
+ 2570                                                 END
+ 2571                                               END
+ 2572                                             END; .)
+ 2573      | "(" Expr<et, q> ")"               (. t := et; .)
+ 2574      | SetLit<st, sq>                    (. t := st;
+ 2575                                             QbeGen.CopyOp(sq, q); .)
+ 2576      | ( "NOT" | "~" ) Fact<t2, q2>      (. IF SymTab.BoolCheck(t2) THEN
+ 2577                                               t := SymTab.BoolType()
+ 2578                                             ELSE SemError(212);
+ 2579                                               t := SymTab.InvalidType END;
+ 2580                                             IF t # SymTab.InvalidType THEN
+ 2581                                               QbeGen.NotQ(q2, q)
+ 2582                                             ELSE QbeGen.CopyOp("0", q)
+ 2583                                             END; .) .
+ 2584    (* Set literals are SET OF [0..255] (8 words); elements validated
+ 2585       0..255 statically when foldable (222 otherwise), runtime trap
+ 2586       for computed elements. Ranges always lower via SetRange. *)
+ 2587    SetLit<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 2588      = "{"                               (. t := SymTab.NewSet(
+ 2589                                               SymTab.NewSubR(0, 255));
+ 2590                                             QbeGen.NewSetTemp(8, q);
+ 2591                                             QbeGen.SetZero(q, 8); .)
+ 2592        [ SetElem<t, q> { "," SetElem<t, q> } ]
+ 2593        "}" .
+ 2594    (* Typed set constructor: TypeName{ elems } — e.g. BITSET{0},
+ 2595       BITSET{}.  The declared type (not SET OF [0..255]) sets the
+ 2596       width and element span. *)
+ 2597    TypedSetLit<vt: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 2598                                          (. VAR nw: CARDINAL; .)
+ 2599      = "{"                               (. IF SymTab.ClassOf(vt) #
+ 2600                                                SymTab.ClSet THEN
+ 2601                                               SemError(230); nw := 8
+ 2602                                             ELSE nw := SymTab.SetWords(vt);
+ 2603                                               IF nw = 0 THEN nw := 8 END
+ 2604                                             END;
+ 2605                                             QbeGen.NewSetTemp(nw, q);
+ 2606                                             QbeGen.SetZero(q, nw); .)
+ 2607        [ SetElem<vt, q> { "," SetElem<vt, q> } ]
+ 2608        "}" .
+ 2609    SetElem<st: SymTab.TypeIndex; sq: QbeGen.QVal>         (. VAR et, et2: SymTab.TypeIndex;
+ 2610                                               qe, q2: QbeGen.QVal;
+ 2611                                               v, v2: INTEGER;
+ 2612                                               lo: INTEGER;
+ 2613                                               span: CARDINAL;
+ 2614                                               cl, cl2: INTEGER;
+ 2615                                               hasR: BOOLEAN; .)
+ 2616      =                                   (. hasR := FALSE; .)
+ 2617        Expr<et, qe>
+ 2618        [ ".." Expr<et2, q2>              (. hasR := TRUE; .) ]
+ 2619                                          (. lo := SymTab.SetBaseLo(st);
+ 2620                                             span := SymTab.SetCount(st);
+ 2621                                             IF (et = SymTab.InvalidType)
+ 2622                                                OR (hasR AND (et2 =
+ 2623                                                   SymTab.InvalidType)) THEN
+ 2624                                             ELSE cl :=
+ 2625                                                    SymTab.ClassOf(et);
+ 2626                                               IF hasR THEN
+ 2627                                                 cl2 :=
+ 2628                                                   SymTab.ClassOf(et2)
+ 2629                                               ELSE cl2 := SymTab.ClInt
+ 2630                                               END;
+ 2631                                               IF ((cl # SymTab.ClInt)
+ 2632   AND (cl # SymTab.ClChar)
+ 2633   AND (cl # SymTab.ClBool))
+ 2634                                                  OR (hasR AND 
+ 2635                                                     ((cl2
+ 2636                                                       # SymTab.ClInt)
+ 2637   AND (cl2
+ 2638                                                        # SymTab.ClChar)
+ 2639   AND (cl2
+ 2640                                                        # SymTab.ClBool))) THEN
+ 2641                                                 SemError(222)
+ 2642                                               ELSIF hasR
+ 2643   AND SymTab.ConstInt(qe, v)
+ 2644   AND SymTab.ConstInt(q2,
+ 2645                                                     v2)
+ 2646   AND ((v < lo)
+ 2647                                                     OR (v2 < lo)
+ 2648                                                     OR (v >= lo +
+ 2649                                                        VAL(INTEGER, span))
+ 2650                                                     OR (v2 >= lo +
+ 2651                                                        VAL(INTEGER, span))
+ 2652                                                     OR (v > v2)) THEN
+ 2653                                                 SemError(222)
+ 2654                                                ELSIF hasR THEN
+ 2655                                                  QbeGen.SetRange(sq, qe, q2,
+ 2656                                                    lo, span)
+ 2657                                                ELSIF SymTab.ConstInt(qe,
+ 2658                                                        v)
+ 2659   AND ((v < lo)
+ 2660                                                      OR (v >= lo +
+ 2661                                                         VAL(INTEGER,
+ 2662                                                           span))) THEN
+ 2663                                                  SemError(222)
+ 2664                                                ELSE QbeGen.SetBit(sq, qe,
+ 2665                                                  lo, span)
+ 2666                                               END
+ 2667                                             END; .) .
+ 2668    GetIdent<VAR n: SymTab.Name>
+ 2669      = ident                             (. LexName(n); .) .
+ 2670  
+ 2671  END M2.
 
     0 errors
 

+ 12 - 0
compiler/src/QbeGen.def

@@ -212,6 +212,18 @@ PROCEDURE AbsQ (a: ARRAY OF CHAR; VAR q: QVal; isReal: BOOLEAN);
 PROCEDURE NegQ (a: ARRAY OF CHAR; VAR q: QVal; isReal: BOOLEAN);
 (* q := fresh temp holding "-a". a and q must differ. *)
 
+PROCEDURE NarrowLong (a: ARRAY OF CHAR; VAR q: QVal);
+(* LONGINT -> INTEGER (low 32 bits). *)
+
+PROCEDURE ConvRI (a: ARRAY OF CHAR; VAR q: QVal);
+(* REAL -> INTEGER (truncate). *)
+
+PROCEDURE ConvRL (a: ARRAY OF CHAR; VAR q: QVal);
+(* REAL -> LONGINT (truncate). *)
+
+PROCEDURE ConvLR (a: ARRAY OF CHAR; VAR q: QVal);
+(* LONGINT -> REAL. *)
+
 PROCEDURE ConvIR (a: ARRAY OF CHAR; VAR q: QVal);
 (* q := fresh double holding "swtof a" (INTEGER -> REAL widening). *)
 

+ 32 - 0
compiler/src/QbeGen.mod

@@ -1539,6 +1539,38 @@ PROCEDURE ConvIR (a: ARRAY OF CHAR; VAR q: QVal);
     W("  "); W(q); W(" =d swtof "); WL(a)
   END ConvIR;
 
+PROCEDURE NarrowLong (a: ARRAY OF CHAR; VAR q: QVal);
+(* LONGINT (l) -> INTEGER (w): keep the low 32 bits. *)
+  BEGIN
+    NewTemp(q);
+    Revive;
+    W("  "); W(q); W(" =w copy "); WL(a)
+  END NarrowLong;
+
+PROCEDURE ConvRI (a: ARRAY OF CHAR; VAR q: QVal);
+(* REAL -> INTEGER (truncate toward zero). *)
+  BEGIN
+    NewTemp(q);
+    Revive;
+    W("  "); W(q); W(" =w dtosi "); WL(a)
+  END ConvRI;
+
+PROCEDURE ConvRL (a: ARRAY OF CHAR; VAR q: QVal);
+(* REAL -> LONGINT (truncate toward zero). *)
+  BEGIN
+    NewTemp(q);
+    Revive;
+    W("  "); W(q); W(" =l dtosi "); WL(a)
+  END ConvRL;
+
+PROCEDURE ConvLR (a: ARRAY OF CHAR; VAR q: QVal);
+(* LONGINT -> REAL. *)
+  BEGIN
+    NewTemp(q);
+    Revive;
+    W("  "); W(q); W(" =d sltof "); WL(a)
+  END ConvLR;
+
 PROCEDURE NotQ (a: ARRAY OF CHAR; VAR q: QVal);
   BEGIN
     NewTemp(q);

+ 26 - 0
compiler/tests/t_valconv.mod

@@ -0,0 +1,26 @@
+MODULE TValConv;
+(* VAL conversions across INTEGER / LONGINT / REAL. Exit 15. *)
+VAR ExitCode : INTEGER;
+VAR i : INTEGER;
+VAR l : LONGINT;
+VAR r : REAL;
+BEGIN
+  ExitCode := 0;
+
+  (* INTEGER -> LONGINT -> INTEGER (widen, then narrow) *)
+  l := VAL(LONGINT, 1000);
+  IF VAL(INTEGER, l) = 1000 THEN ExitCode := ExitCode + 1 END;
+
+  (* INTEGER -> REAL *)
+  r := VAL(REAL, 3);
+  IF r = 3.0 THEN ExitCode := ExitCode + 2 END;
+
+  (* REAL -> INTEGER (truncate toward zero) *)
+  r := 3.9;
+  IF VAL(INTEGER, r) = 3 THEN ExitCode := ExitCode + 4 END;
+
+  (* LONGINT -> REAL *)
+  l := 7;
+  r := VAL(REAL, l);
+  IF r = 7.0 THEN ExitCode := ExitCode + 8 END
+END TValConv.

+ 5 - 4
docs/features.md

@@ -1,4 +1,4 @@
-# m2compiler-V3 — feature status (at `v3-showcase19`, 147/147 green)
+# m2compiler-V3 — feature status (at `v3-val-conversions`, 148/148 green)
 
 Pipeline: Coco/R `M2.atg` (1748 lines, 73 productions) → `gm2`-built
 `M2` → QBE `.ssa` → `qbe` → `cc` → run. `SymTab.mod` 1623 lines,
@@ -109,9 +109,10 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
   (deliberately single inheritance only). See
   `docs/summary_class-lowering.md`, `docs/summary_virtual-dispatch.md`,
   `docs/summary_class-finish.md`.
-  ⏸ `VAL(LONGINT|REAL, x)` and long→int narrowing (V3 has no 64→32
-  conversion; `Conversions` works around it). The TopSpeed legacy
-  grammar (`TopSpeed-V3-M2.atg`) is a separate sidecar, not merged.
+- ✅ `VAL` conversions across `INTEGER`/`CHAR`/`BOOLEAN`/enum,
+  `INTEGER`↔`LONGINT` (widen/narrow), `INTEGER`/`LONGINT`↔`REAL`
+  (truncate/widen) and pointer↔pointer. 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

+ 42 - 0
docs/summary_val-conversions.md

@@ -0,0 +1,42 @@
+# Step: VAL conversions (LONGINT/REAL) + long→int narrowing
+
+Tag `v3-val-conversions`. Suite **148/148**; fixpoint **OK**
+(image **2,320,770 bytes**).
+
+## What
+
+`VAL` now covers the numeric conversions V3 lacked:
+
+| From → To | Codegen |
+| --- | --- |
+| `INTEGER` → `LONGINT` | `extsw` (sign-extend) |
+| `LONGINT` → `INTEGER` | `copy w` (low 32 bits) |
+| `INTEGER` → `REAL` | `swtof` |
+| `LONGINT` → `REAL` | `sltof` |
+| `REAL` → `INTEGER` | `dtosi` (truncate toward zero) |
+| `REAL` → `LONGINT` | `dtosi` |
+
+(Ordinal↔ordinal, pointer↔pointer and REAL↔REAL were already allowed.)
+
+New `QbeGen` helpers: `NarrowLong`, `ConvRI`, `ConvRL`, `ConvLR`
+(alongside the existing `WidenLong`/`ConvIR`).
+
+## Payoff
+
+`Conversions` is simplified: `StrToInt`/`StrToCard` now accumulate in
+a `LONGINT` and simply range-check, then narrow with `VAL(INTEGER, v)`
+/ `VAL(CARDINAL, v)` — the previous "detect overflow before the 32-bit
+wrap" hand-guard is gone.
+
+## Tests
+
+`t_valconv.mod` (15): int→long→int round-trip, int→real, real→int
+truncation, long→real. `ConvProg`/`ConvResultsProg` still pass with
+the rewritten `Conversions`.
+
+## Files
+
+`compiler/src/QbeGen.def`/`.mod` (`NarrowLong`/`ConvRI`/`ConvRL`/
+`ConvLR`), `compiler/src/M2.atg` (`VAL` action),
+`stdlib/conversions.mod`, `compiler/tests/t_valconv.mod`,
+`compiler/run_tests.sh`, `docs/features.md`.

+ 16 - 32
stdlib/conversions.mod

@@ -72,19 +72,14 @@ END RealToStr;
 (* ---------------- input ---------------- *)
 
 PROCEDURE StrToInt(s : ARRAY OF CHAR; VAR n : INTEGER) : ConvResults;
-(* signed decimal.  V3 has no VAL(LONGINT, int) or long->int
-   narrowing, so overflow is detected *before* the 32-bit accumulate
-   wraps: reject when v would exceed (IntMax-d) DIV 10.  The whole
-   magnitude accumulates as a non-negative INTEGER, then range is
-   checked per sign. *)
+(* signed decimal.  Accumulates in LONGINT (so overflow is simply a
+   range check) then narrows with VAL(INTEGER, v). *)
   VAR i : CARDINAL;
-    neg, seen, over : BOOLEAN;
-    v : INTEGER;
+    neg, seen : BOOLEAN;
+    v : LONGINT;
     d : INTEGER;
-    bound : INTEGER;
 BEGIN
   n := 0; i := 0; neg := FALSE; seen := FALSE; v := 0;
-  over := FALSE;
   IF (i > HIGH(s)) OR (s[i] = CHR(0)) THEN RETURN strEmpty END;
   IF s[i] = "-" THEN neg := TRUE; INC(i)
   ELSIF s[i] = "+" THEN INC(i)
@@ -94,49 +89,38 @@ BEGIN
   END;
   WHILE (i <= HIGH(s)) AND (s[i] >= "0") AND (s[i] <= "9") DO
     d := ORD(s[i]) - ORD("0");
-    IF neg THEN
-      (* magnitude may reach 2^31 *)
-      bound := (IntMax - 1 - d) DIV 10
-    ELSE
-      bound := (IntMax - d) DIV 10
-    END;
-    IF v > bound THEN over := TRUE
-    ELSE v := v * 10 + d
-    END;
+    v := v * 10 + d;
     seen := TRUE;
     INC(i)
   END;
   IF (i <= HIGH(s)) AND (s[i] # CHR(0)) THEN RETURN strWrongFormat END;
   IF NOT seen THEN RETURN strWrongFormat END;
-  IF over THEN RETURN strOutOfRange END;
-  IF neg THEN n := 0 - v ELSE n := v END;
+  IF neg THEN v := 0 - v END;
+  IF (v > IntMax) OR (v < IntMin) THEN RETURN strOutOfRange END;
+  n := VAL(INTEGER, v);
   RETURN strAllRight
 END StrToInt;
 
 PROCEDURE StrToCard(s : ARRAY OF CHAR; VAR n : CARDINAL) : ConvResults;
-(* unsigned decimal; same pre-wrap overflow guard, bounded by the
-   CARDINAL max (2^32-1) held as a non-negative INTEGER magnitude. *)
+(* unsigned decimal; LONGINT accumulation, bounded by CARDINAL max. *)
   VAR i : CARDINAL;
-    seen, over : BOOLEAN;
-    v : INTEGER;
-    d, bound : INTEGER;
+    seen : BOOLEAN;
+    v : LONGINT;
+    d : INTEGER;
 BEGIN
-  n := 0; i := 0; seen := FALSE; v := 0; over := FALSE;
+  n := 0; i := 0; seen := FALSE; v := 0;
   IF (i > HIGH(s)) OR (s[i] = CHR(0)) THEN RETURN strEmpty END;
   IF (s[i] < "0") OR (s[i] > "9") THEN RETURN strWrongFormat END;
   WHILE (i <= HIGH(s)) AND (s[i] >= "0") AND (s[i] <= "9") DO
     d := ORD(s[i]) - ORD("0");
-    bound := (IntMax - d) DIV 10;
-    IF v > bound THEN over := TRUE
-    ELSE v := v * 10 + d
-    END;
+    v := v * 10 + d;
     seen := TRUE;
     INC(i)
   END;
   IF (i <= HIGH(s)) AND (s[i] # CHR(0)) THEN RETURN strWrongFormat END;
   IF NOT seen THEN RETURN strWrongFormat END;
-  IF over THEN RETURN strOutOfRange END;
-  n := v;
+  IF v > IntMax THEN RETURN strOutOfRange END;
+  n := VAL(CARDINAL, v);
   RETURN strAllRight
 END StrToCard;