shithub: MicroHs

Download patch

ref: ffda7053c7418a374fe16ed1c7da8a024cfe7523
parent: f1136c9acb3b74f87a3bef4ca931564394a917f7
parent: 8fdde6a43838f7811ccfbd33635747e57b0d6eae
author: Lennart Augustsson <lennart@augustsson.net>
date: Mon Dec 23 09:24:30 EST 2024

Merge branch 'master' into patsyn3

--- a/Makefile
+++ b/Makefile
@@ -213,8 +213,8 @@
 
 ######
 
-VERSION=0.10.6.0
-HVERSION=0,10,6,0
+VERSION=0.10.7.0
+HVERSION=0,10,7,0
 MCABAL=$(HOME)/.mcabal
 MCABALMHS=$(MCABAL)/mhs-$(VERSION)
 MDATA=$(MCABALMHS)/packages/mhs-$(VERSION)/data
--- a/MicroHs.cabal
+++ b/MicroHs.cabal
@@ -1,6 +1,6 @@
 cabal-version:       3.0
 name:                MicroHs
-version:             0.10.6.0
+version:             0.10.7.0
                      -- *** When changing the version number, also
                      -- *** run 'sh updateversion.sh'
                      -- *** Yeah, it stinks.
@@ -42,8 +42,8 @@
       src/runtime/*.c
       src/runtime/*.h
 -- I would like to have these two only for ghc, but I can't figure out how.
-      lib/**/*.hs           --NOT_MHS
-      lib/**/*.hs-boot      --NOT_MHS
+      lib/**/*.hs
+      lib/**/*.hs-boot
 
 -- These files must not be installed for mhs!
 -- Cabal doesn't seem to understand this, so it never copies
--- a/lib/libs.cabal
+++ b/lib/libs.cabal
@@ -1,6 +1,6 @@
 cabal-version:  3.0
 name:           libs
-version:        0.10.6.0
+version:        0.10.7.0
 license:        Apache
 license-file:   LICENSE
 maintainer:     lennart@augustsson.net
--- a/paths/Paths_MicroHs.hs
+++ b/paths/Paths_MicroHs.hs
@@ -10,4 +10,4 @@
 getDataDir = return "."
 
 version :: Version
-version = makeVersion [0,10,6,0]
+version = makeVersion [0,10,7,0]
--- a/src/MicroHs/Abstract.hs
+++ b/src/MicroHs/Abstract.hs
@@ -67,6 +67,15 @@
 isP :: Exp -> Bool
 isP = isPrim "P"
 
+isZ :: Exp -> Bool
+isZ = isPrim "Z"
+
+isK2 :: Exp -> Bool
+isK2 = isPrim "K2"
+
+isK3 :: Exp -> Bool
+isK3 = isPrim "K3"
+
 cId :: Exp
 cId = Lit (LPrim "I")
 
@@ -272,6 +281,12 @@
           Lit (LPrim "R")
         else if isCC ff && isB aa then
           Lit (LPrim "C'B")
+        else if isZ ff && isK aa then
+          Lit (LPrim "K2")
+        else if isZ ff && isK2 aa then
+          Lit (LPrim "K3")
+        else if isZ ff && isK3 aa then
+          Lit (LPrim "K4")
         else
           let
             def =
--- a/src/MicroHs/List.hs
+++ b/src/MicroHs/List.hs
@@ -12,12 +12,31 @@
 elemBy :: (a -> a -> Bool) -> a -> [a] -> Bool
 elemBy eq a = any (eq a)
 
--- A simple "quicksort" for now.
+-- A simple merge sort for now.
 sortLE :: forall a . (a -> a -> Bool) -> [a] -> [a]
-sortLE _  [] = []
-sortLE le (x:xs) = sortLE le lt ++ (x : sortLE le ge)
-  where (ge, lt) = partition (le x) xs
+sortLE le = mergeAll . splatter
+  where
+    splatter [] = []
+    splatter [a] = [[a]]
+    splatter (a1 : a2 : as)
+      | a1 `le` a2 = [a1, a2] : splatter as
+      | otherwise  = [a2, a1] : splatter as
 
+    mergeAll [] = []
+    mergeAll [xs] = xs
+    mergeAll xss = mergeAll (mergePairs xss)
+
+    mergePairs [] = []
+    mergePairs [xs] = [xs]
+    mergePairs (xs1 : xs2 : xss) = merge xs1 xs2 : mergePairs xss
+
+    merge [] ys = ys
+    merge xs [] = xs
+    merge axs@(x : xs) ays@(y : ys)
+      | x `le` y  = x : merge xs ays
+      | otherwise = y : merge axs ys
+
+
 showListS :: (a -> String) -> [a] -> String
 showListS sa arg =
   let
@@ -36,6 +55,14 @@
 anySameBy :: (a -> a -> Bool) -> [a] -> Bool
 anySameBy _ [] = False
 anySameBy eq (x:xs) = elemBy eq x xs || anySameBy eq xs
+
+anySameByLE :: (a -> a -> Bool) -> [a] -> Bool
+anySameByLE le = anySameAdj . sortLE le
+  where
+    anySameAdj (x1 : xs@(x2 : _))
+      | x2 `le` x1 = True
+      | otherwise = anySameAdj xs
+    anySameAdj _ = False
 
 deleteAllBy :: forall a . (a -> a -> Bool) -> a -> [a] -> [a]
 deleteAllBy _ _ [] = []
--- a/src/MicroHs/Parse.hs
+++ b/src/MicroHs/Parse.hs
@@ -320,6 +320,8 @@
   <|< mkPattern    <$> (pKeyword "pattern"  *> pPatSyn)
   <|< PatternSign  <$> (pKeyword "pattern"  *> (esepBy1 pUIdentSym (pSpec ',')) <* dcolon) <*> pType
   <|< Deriving     <$> (pKeyword "deriving" *> pKeyword "instance" *> pType)
+  <|< noop         <$  (pKeyword "type"     <* pKeyword "role" <* pTypeIdentSym <*
+                                               (pKeyword "nominal" <|> pKeyword "phantom" <|> pKeyword "representational"))
   where
     pAssoc = (AssocLeft <$ pKeyword "infixl") <|< (AssocRight <$ pKeyword "infixr") <|< (AssocNone <$ pKeyword "infix")
     dig (TInt _ ii) | 0 <= i && i <= 9 = Just i  where i = fromInteger ii
@@ -333,6 +335,7 @@
     clsSym = do s <- pUIdentSym; guard (unIdent s /= "()"); return s
 
     mkPattern (lhs, pat, meqn) = Pattern lhs pat meqn
+    noop = Infix (AssocLeft, 0) []        -- harmless definition
 
 pPatSyn :: P (LHS, EPat, Maybe [Eqn])
 pPatSyn = do
@@ -405,7 +408,10 @@
 pContext :: P [EConstraint]
 pContext = (pCtx <* pDRArrow) <|< pure []
   where
-    pCtx = ((:[]) <$> pTypeApp)
+    pCtx =     ((:[]) <$> pTypeApp)
+           <|> (eq <$> pTypeArg <*> pTilde <*> pTypeArg)   -- A hack to allow   a~b => ...
+    eq t1 i t2 = [eApp2 (EVar i) t1 t2]
+    pTilde = do i <- pQSymOper; guard (i == mkIdent "~"); return i
 
 pDRArrow :: P ()
 pDRArrow = pSymbol "=>" <|< pSymbol "\x21d2"
--- a/src/MicroHs/TypeCheck.hs
+++ b/src/MicroHs/TypeCheck.hs
@@ -555,14 +555,6 @@
 kTypeTypeTypeS :: EType
 kTypeTypeTypeS = kArrow kType $ kArrow kType kType
 
--- (=>) :: Constraint -> Type -> Type
---kConstraintTypeTypeS :: EType
---kConstraintTypeTypeS = kArrow kConstraint $ kArrow kType kType
-
--- (~) :: Type -> Type -> Constraint
-kTypeTypeConstraintS :: EType
-kTypeTypeConstraintS = kArrow kType (kArrow kType kConstraint)
-
 mkIdentB :: String -> Ident
 mkIdentB = mkIdentSLoc builtinLoc
 
@@ -610,11 +602,16 @@
     kv = EVar k
     kk = IdKind k sKind
     -- Tuples are polykinded since they need to handle both Type and Constraint
+    -- (,) :: forall k . k -> k -> k
+    -- etc.
     tuple n =
       let
         i = tupleConstr builtinLoc n
       in  (i, [entry i $ EForall True [kk] $ foldr kArrow kv (replicate n kv)])
+    -- (=>) :: forall k . Constraint -> k -> k
     kImplies = EForall True [kk] $ kConstraint `kArrow` (kv `kArrow` kv)
+    -- (~) :: forall k . k -> k -> Constraint
+    kTypeEqual = EForall True [kk] $ kv `kArrow` (kv `kArrow` kConstraint)
   in
       [
        -- The function arrow et al are bothersome to define in Primitives, so keep them here.
@@ -621,7 +618,7 @@
        -- But the fixity is defined in Primitives.
        (mkIdentB "->",           [entry identArrow    kTypeTypeTypeS]),
        (mkIdentB "=>",           [entry identImplies  kImplies]),
-       (mkIdentB "~",            [entry identTypeEq   kTypeTypeConstraintS]),
+       (mkIdentB "~",            [entry identTypeEq   kTypeEqual]),
        -- Primitives.hs uses the type [], and it's annoying to fix that.
        -- XXX should not be needed
        (identList,               [entry identList     kTypeTypeS]),
@@ -2332,7 +2329,7 @@
 
 multCheck :: [Ident] -> T ()
 multCheck vs =
-  when (anySame vs) $ do
+  when (anySameByLE (<=) vs) $ do
     let v = head vs
     tcError (getSLoc v) $ "Multiply defined: " ++ showIdent v
 
--- a/src/runtime/eval.c
+++ b/src/runtime/eval.c
@@ -618,14 +618,15 @@
 
 /* Needed during reduction */
 NODEPTR intTable[HIGH_INT - LOW_INT];
-NODEPTR combFalse, combTrue, combUnit, combCons, combPair;
-NODEPTR combCC, combZ, combIOBIND, combIORETURN, combIOCCBIND;
+NODEPTR combK, combTrue, combUnit, combCons, combPair;
+NODEPTR combCC, combZ, combIOBIND, combIORETURN, combIOCCBIND, combB, combC;
 NODEPTR combLT, combEQ, combGT;
-NODEPTR combShowExn, combU, combK2;
+NODEPTR combShowExn, combU, combK2, combK3;
 NODEPTR combBININT1, combBININT2, combUNINT1;
 NODEPTR combBINDBL1, combBINDBL2, combUNDBL1;
 NODEPTR combBINBS1, combBINBS2;
 NODEPTR comb_stdin, comb_stdout, comb_stderr;
+#define combFalse combK
 
 /* One node of each kind for primitives, these are never GCd. */
 /* We use linear search in this, because almost all lookups
@@ -814,15 +815,18 @@
     //MARK(n) = MARKED;
     SETTAG(n, primops[j].tag);
     switch (primops[j].tag) {
-    case T_K: combFalse = n; break;
+    case T_K: combK = n; break;
     case T_A: combTrue = n; break;
     case T_I: combUnit = n; break;
     case T_O: combCons = n; break;
     case T_P: combPair = n; break;
     case T_CC: combCC = n; break;
+    case T_B: combB = n; break;
+    case T_C: combC = n; break;
     case T_Z: combZ = n; break;
     case T_U: combU = n; break;
     case T_K2: combK2 = n; break;
+    case T_K3: combK3 = n; break;
     case T_IO_BIND: combIOBIND = n; break;
     case T_IO_RETURN: combIORETURN = n; break;
     case T_IO_CCBIND: combIOCCBIND = n; break;
@@ -848,15 +852,18 @@
     NODEPTR n = HEAPREF(heap_start++);
     SETTAG(n, t);
     switch (t) {
-    case T_K: combFalse = n; break;
+    case T_K: combK = n; break;
     case T_A: combTrue = n; break;
     case T_I: combUnit = n; break;
     case T_O: combCons = n; break;
     case T_P: combPair = n; break;
     case T_CC: combCC = n; break;
+    case T_B: combB = n; break;
+    case T_C: combC = n; break;
     case T_Z: combZ = n; break;
     case T_U: combU = n; break;
     case T_K2: combK2 = n; break;
+    case T_K3: combK3 = n; break;
     case T_IO_BIND: combIOBIND = n; break;
     case T_IO_RETURN: combIORETURN = n; break;
     case T_IO_CCBIND: combIOCCBIND = n; break;
@@ -928,6 +935,7 @@
 #if GCRED
 int red_a, red_k, red_i, red_int, red_flip;
 #endif
+int red_bb, red_k4, red_k3, red_k2, red_ccb, red_z, red_r;
 
 //counter_t mark_depth;
 //counter_t max_mark_depth = 0;
@@ -2973,7 +2981,8 @@
 /* Reset stack pointer and return. */
 #define RET do { goto ret; } while(0)
 /* Check that there are at least n arguments, return if not. */
-#define CHECK(n) do { if (stack_ptr - stk < (n)) RET; } while(0)
+#define HASNARGS(n) (stack_ptr - stk >= (n))
+#define CHECK(n) do { if (!HASNARGS(n)) RET; } while(0)
 
 #define SETIND(n, x) do { SETTAG((n), T_IND); INDIR((n)) = (x); } while(0)
 #define GOIND(x) do { SETIND(n, (x)); goto ind; } while(0)
@@ -3027,6 +3036,12 @@
   case T_ARR:  RET;
   case T_BADDYN: ERR1("FFI unknown %s", CSTR(n));
     
+  /*
+   * Some of these reductions, (e.g., Z x y = K (x y)) are there to avoid
+   * that increase in arity that some "optimizations" in Abstract.hs
+   * stop reductions from happening.  This can be important for "full laziness".
+   * In practice, these reductions almost never happen, but there they are anyway. :)
+   */
   case T_S:    GCCHECK(2); CHKARG3; GOAP(new_ap(x, z), new_ap(y, z));                     /* S x y z = x z (y z) */
   case T_SS:   GCCHECK(3); CHKARG4; GOAP(new_ap(x, new_ap(y, w)), new_ap(z, w));          /* S' x y z w = x (y w) (z w) */
   case T_K:                CHKARG2; GOIND(x);                                             /* K x y = *x */
@@ -3035,17 +3050,31 @@
   case T_I:                CHKARG1; GOIND(x);                                             /* I x = *x */
   case T_Y:                CHKARG1; GOAP(x, n);                                           /* n@(Y x) = x n */
   case T_B:    GCCHECK(1); CHKARG3; GOAP(x, new_ap(y, z));                                /* B x y z = x (y z) */
-  case T_BB:   GCCHECK(2); CHKARG4; GOAP(new_ap(x, y), new_ap(z, w));                     /* B' x y z w = x y (z w) */
-  case T_Z:                CHKARG3; GOAP(x, y);                                           /* Z x y z = x y */
+  case T_BB:   if (!HASNARGS(4)) {
+               GCCHECK(1); CHKARG2; red_bb++; GOAP(combB, new_ap(x, y)); } else {         /* B' x y = B (x y) */
+               GCCHECK(2); CHKARG4; GOAP(new_ap(x, y), new_ap(z, w)); }                   /* B' x y z w = x y (z w) */
+  case T_Z:    if (!HASNARGS(3)) {
+               GCCHECK(1); CHKARG2; red_z++; GOAP(combK, new_ap(x, y)); } else {          /* Z x y = K (x y) */
+                           CHKARG3; GOAP(x, y); }                                         /* Z x y z = x y */
   case T_C:    GCCHECK(1); CHKARG3; GOAP(new_ap(x, z), y);                                /* C x y z = x z y */
   case T_CC:   GCCHECK(2); CHKARG4; GOAP(new_ap(x, new_ap(y, w)), z);                     /* C' x y z w = x (y w) z */
   case T_P:    GCCHECK(1); CHKARG3; GOAP(new_ap(z, x), y);                                /* P x y z = z x y */
-  case T_R:    GCCHECK(1); CHKARG3; GOAP(new_ap(y, z), x);                                /* R x y z = y z x */
+  case T_R:    if(!HASNARGS(3)) {
+               GCCHECK(1); CHKARG2; red_r++; GOAP(new_ap(combC, y), x); } else {          /* R x y = C y x */
+               GCCHECK(1); CHKARG3; GOAP(new_ap(y, z), x); }                              /* R x y z = y z x */
   case T_O:    GCCHECK(1); CHKARG4; GOAP(new_ap(w, x), y);                                /* O x y z w = w x y */
-  case T_K2:               CHKARG3; GOIND(x);                                             /* K2 x y z = *x */
-  case T_K3:               CHKARG4; GOIND(x);                                             /* K3 x y z w = *x */
-  case T_K4:               CHECK(5); POP(5); n = TOP(-1); x = ARG(TOP(-5)); GOIND(x);     /* K4 x y z w v = *x */
-  case T_CCB:  GCCHECK(2); CHKARG4; GOAP(new_ap(x, z), new_ap(y, w));                     /* C'B x y z w = x z (y w) */
+  case T_K2:   if (!HASNARGS(3)) {
+                           CHKARG2; red_k2++; GOAP(combK, x); } else {                    /* K2 x y = K x */
+                           CHKARG3; GOIND(x); }                                           /* K2 x y z = *x */
+  case T_K3:   if (!HASNARGS(4)) {
+                           CHKARG2; red_k3++; GOAP(combK2, x); } else {                   /* K3 x y = K2 x */
+                           CHKARG4; GOIND(x); }                                           /* K3 x y z w = *x */
+  case T_K4:   if (!HASNARGS(5)) {
+                           CHKARG2; red_k4++; GOAP(combK3, x); } else {                   /* K4 x y = K3 x */
+                           CHECK(5); POP(5); n = TOP(-1); x = ARG(TOP(-5)); GOIND(x); }   /* K4 x y z w v = *x */
+  case T_CCB:  if (!HASNARGS(4)) {
+               GCCHECK(2); CHKARG3; red_ccb++; GOAP(new_ap(combB, new_ap(x, z)), y); } else {  /* C'B x y z = B (x z) y */
+               GCCHECK(2); CHKARG4; GOAP(new_ap(x, z), new_ap(y, w)); }                   /* C'B x y z w = x z (y w) */
 
     /*
      * Strict primitives require evaluating the arguments before we can proceed.
@@ -4202,6 +4231,7 @@
           (double)gc_scan_time / 1000);
 #if GCRED
     PRINT(" GC reductions A=%d, K=%d, I=%d, int=%d flip=%d\n", red_a, red_k, red_i, red_int, red_flip);
+    PRINT(" special reductions B'=%d K4=%d K3=%d K2=%d C'B=%d, Z=%d, R=%d\n", red_bb, red_k4, red_k3, red_k2, red_ccb, red_z, red_r);
 #endif
   }
 #endif  /* WANT_STDIO */
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -77,6 +77,7 @@
 	$(TMHS) Bang       && $(EVAL) > Bang.out       && diff Bang.ref Bang.out
 	$(TMHS) MString    && $(EVAL) > MString.out    && diff MString.ref MString.out
 	$(TMHS) OrPat      && $(EVAL) > OrPat.out      && diff OrPat.ref OrPat.out
+	$(TMHS) PartRed    && $(EVAL) > PartRed.out    && diff PartRed.ref PartRed.out
 	$(TMHS) PatSyn     && $(EVAL) > PatSyn.out     && diff PatSyn.ref PatSyn.out
 
 errtest:
--- /dev/null
+++ b/tests/PartRed.hs
@@ -1,0 +1,93 @@
+module PartRed where
+import Primitives
+
+k2 :: Int -> Int -> Int -> Int
+k2 x y z = x
+
+f2 :: Int -> Int
+f2 = k2 1 2
+
+k3 :: Int -> Int -> Int -> Int -> Int
+k3 x y z w = x
+
+f3 :: Int -> Int
+f3 = k3 1 2 3
+
+f32 :: Int -> Int -> Int
+f32 = k3 1 2
+
+k4 :: Int -> Int -> Int -> Int -> Int -> Int
+k4 x y z w v = x
+
+f4 :: Int -> Int
+f4 = k4 1 2 3 4
+
+f42 :: Int -> Int -> Int -> Int
+f42 = k4 1 2
+
+f43 :: Int -> Int -> Int
+f43 = k4 1 2 3
+
+
+-----
+
+type S a = (a->a, a->a)
+
+sInt :: S Int
+sInt = (id, primIntNeg)
+
+j :: S a -> (a -> a) -> a -> a
+j d x y = snd d (x y)
+
+k :: (Int -> Int) -> Int -> Int
+k = j sInt
+
+---
+
+c'b :: (Int -> Int -> Int) -> (Int -> Int) -> Int -> Int -> Int
+c'b x y z w = x z (y w)
+
+fc'b :: Int -> Int
+fc'b = c'b primIntAdd primIntNeg 99
+
+---
+
+cZ :: (Int -> Int) -> Int -> Int -> Int
+cZ x y z = x y
+
+fcZ :: Int -> Int
+fcZ = cZ primIntNeg 11
+
+---
+
+r :: Int -> (Int -> Int -> Int) -> Int -> Int
+r x y z = y z x
+
+fr :: Int -> Int
+fr = r 22 primIntAdd
+
+main :: IO ()
+main = do
+  cprint k2
+  cprint f2
+  cprint k3
+  cprint f3
+  cprint f32
+  cprint k4
+  cprint f4
+  cprint f42
+  cprint f43
+  --
+  cprint k
+  --
+  cprint c'b
+  cprint fc'b
+  --
+  cprint cZ
+  cprint fcZ
+  --
+  cprint r
+  cprint fr
+
+
+-- R x y = C y x
--- /dev/null
+++ b/tests/PartRed.ref
@@ -1,0 +1,16 @@
+K2
+(K #1)
+K3
+(K #1)
+(K2 #1)
+K4
+(K #1)
+(K3 #1)
+(K2 #1)
+(B neg)
+C'B
+((B (+ #99)) neg)
+Z
+(K #-11)
+R
+((C +) #22)