aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/Reducer
diff options
context:
space:
mode:
authorMarvin Borner2024-02-24 18:43:49 +0100
committerMarvin Borner2024-02-24 18:43:49 +0100
commit8b446184aa45d142fe2c4b78b79112b658499ba0 (patch)
treeb834c5f29e47cffbc8893b39e691dd5589c9345e /src/Reducer
parent02d531a2dff18aed6ec8085c10113ac0a46b05b9 (diff)
Initial import of the ION reducer
The full commit history can be found on AIT/nf.hs since this is mainly a translation of Tromp's nf.c
Diffstat (limited to 'src/Reducer')
-rw-r--r--src/Reducer/ION.hs398
1 files changed, 398 insertions, 0 deletions
diff --git a/src/Reducer/ION.hs b/src/Reducer/ION.hs
index 1a27605..7f29e12 100644
--- a/src/Reducer/ION.hs
+++ b/src/Reducer/ION.hs
@@ -1,9 +1,407 @@
-- MIT License, Copyright (c) 2024 Marvin Borner
+-- Based on Benn Lynn's ION machine and John Tromp's nf.c
+-- TODO: clean everything up after tests are working
+-- iter -> fold/etc, application infix vm abstraction,
+-- monadic VM, map -> array?
+-- (fairly literate translation from C code)
module Reducer.ION
( reduce
) where
+import Data.Bits ( (.|.) )
+import Data.Char ( chr
+ , ord
+ )
+import Data.List ( intercalate )
+import qualified Data.Map as M
+import Data.Map ( Map )
+import Debug.Trace
import Helper
+import System.IO
+
+ncomb :: Int
+ncomb = 128
+
+spTop :: Int
+spTop = maxBound
+
+data VM = VM
+ { sp :: Int
+ , hp :: Int
+ , nvar :: Int
+ , mem :: Map Int Int
+ }
+ deriving Show
+
+dumpMem :: VM -> String
+dumpMem VM { mem } =
+ "--- mem ---\n"
+ ++ intercalate
+ "\n"
+ ( (\(a, b) -> show a ++ ": " ++ show b)
+ <$> filter (\(a, b) -> a <= 255 && b /= 0) (M.toList mem)
+ )
+ ++ "\n--- /mem ---"
+
+dumpStack :: VM -> String
+dumpStack VM { mem, sp } =
+ "--- stack ---\n"
+ ++ intercalate
+ "\n"
+ ( (\(a, b) ->
+ show (a - sp) ++ ": " ++ show b ++ if a == sp then " <-" else ""
+ )
+ <$> filter (\(a, b) -> a > 255 && b /= 0) (M.toList mem)
+ )
+ ++ "\n--- /stack ---"
+
+isComb :: Int -> Bool
+isComb n = n < ncomb
+
+new :: VM
+new = VM spTop ncomb 0 mempty
+
+load :: Int -> VM -> Int
+load k vm = M.findWithDefault 0 k (mem vm)
+
+store :: Int -> Int -> VM -> VM
+store k v vm = vm { mem = M.insert k v $ mem vm }
+
+app :: Int -> Int -> VM -> (Int, VM)
+app x y vm@VM { hp } = (hp, store hp x $ store (hp + 1) y $ vm { hp = hp + 2 })
+
+-- push :: Int -> VM -> VM
+-- push n vm@VM { sp } = store (sp - 1) n $ vm { sp = sp - 1 }
+
+arg' :: Int -> VM -> Int
+arg' n vm = load (load (sp vm + n) vm + 1) vm
+
+arg :: Int -> VM -> (Int, VM)
+arg n vm = (arg' n vm, vm)
+
+-- app' :: (VM -> (Int, VM)) -> (VM -> (Int, VM)) -> VM -> (Int, VM)
+-- app' f g vm =
+-- let (x, vm1) = f vm
+-- (y, vm2) = g vm1
+-- in app x y vm2
+
+apparg :: Int -> Int -> VM -> (Int, VM)
+apparg m n vm = app (arg' m vm) (arg' n vm) vm
+
+wor :: a -> b -> (a, b)
+wor = (,)
+
+com :: Char -> b -> (Int, b)
+com = wor . ord
+
+lazy :: Int -> (VM -> (Int, VM)) -> (VM -> (Int, VM)) -> VM -> VM
+lazy d f a vm = do
+ let fix i _ | i > d = Nothing
+ fix i vm' =
+ if load (load (sp vm' + i + 1) vm') vm' == load (sp vm' + i) vm'
+ then fix (i + 1) vm'
+ else do
+ let vm1' = vm' { sp = sp vm' - 2 }
+ let cnvar = nvar vm1'
+ let (n1, vm2') = app (ord 'V') cnvar vm1' { nvar = cnvar + 1 }
+ let spi2 = load (sp vm2' + i + 2) vm2'
+ let vm3' = store (sp vm2' + i) spi2 vm2'
+ let (n2, vm4') = app spi2 n1 vm3'
+ let vm5' = store (sp vm4' + i + 1) n2 vm4'
+ let (n3, vm6') = app (ord 'L') n2 vm5'
+ let vm7' = store (sp vm6' + i + 2) n3 vm6'
+ let vm8' = store (load (sp vm7' + i + 3) vm7' + 1) n3 vm7'
+ Just $ vm8' { sp = sp vm8' + i }
+ case fix 1 vm of
+ Just vm' -> vm'
+ Nothing -> do
+ let (f', vm1) = f vm
+ let (a', vm2) = a vm1
+ let vm3 = vm2 { sp = sp vm + d }
+ let
+ dst = trace ("LAZY: " ++ show f' ++ " " ++ show a')
+ $ load (sp vm + d + 1) vm
+ store (sp vm3) f' (store dst f' (store (dst + 1) a' vm3))
+
+-- numberArg :: Int -> VM -> Int
+-- numberArg n vm = load (fst (arg n vm) + 1) vm
+
+rules :: Int -> VM -> VM
+-- rules ch vm = case chr ch of
+rules ch vm = case trace (show $ chr ch) (chr ch) of
+ 'M' -> lvm 0 (arg 1) (arg 1)
+ 'Y' -> lvm 0 (arg 1) (app (ord 'Y') 1)
+ 'I' ->
+ if trace ("I: " ++ show (arg' 2 vm) ++ " " ++ show (load (sp vm + 1) vm))
+ $ arg' 2 vm
+ == load (sp vm + 1) vm
+ then lvm 1 (arg 1) (arg 1)
+ else lvm 1 (arg 1) (arg 2)
+ 'F' -> do
+ let xv = arg' 2 vm
+ if isComb xv
+ then lvm 1 (com 'I') (arg 2)
+ else lvm 1 (wor $ load xv vm) (wor $ load (xv + 1) vm)
+ 'f' -> do
+ let x = load (load (sp vm + 2) vm + 1) vm
+ let (v, vm') = if isComb x
+ then do
+ let cnvar = nvar vm
+ let (a, vm1) =
+ app (ord 'V') cnvar vm { sp = sp vm + 1, nvar = cnvar + 1 }
+ let (b, vm2) = app x a vm1
+ let (c, vm3) = app (ord 'L') b vm2
+ (c, store (load (sp vm3 + 1) vm3 + 1) c vm3)
+ else (x, vm { sp = sp vm + 1 })
+ store (sp vm') v vm'
+ 'K' -> do
+ let (xv, _) = arg 1 vm
+ if isComb xv
+ then lvm 1 (com 'I') (wor xv)
+ else lvm 1 (wor $ load xv vm) (wor $ load (xv + 1) vm)
+ 'T' -> lvm 1 (arg 2) (arg 1)
+ 'D' -> lvm 2 (arg 1) (arg 2)
+ 'B' -> lvm 2 (arg 1) (apparg 2 3)
+ 'C' -> lvm 2 (apparg 1 3) (arg 2)
+ 'R' -> lvm 2 (apparg 2 3) (arg 1)
+ ':' -> lvm 2 (apparg 3 1) (arg 2)
+ 'S' -> lvm 2 (apparg 1 3) (apparg 2 3)
+ 'L' -> store (sp vm) (arg' 1 vm) vm
+ 'V' -> do
+ let iter vm'@VM { sp } | sp == spTop = vm' { sp = -1 }
+ iter vm'@VM { sp } | load (load (sp + 1) vm') vm' == load sp vm' = vm'
+ iter vm'@VM { sp } =
+ let parent = load (sp + 1) vm'
+ left = load parent vm'
+ vm'' = if left == ord 'L'
+ then vm' { nvar = nvar vm' - 1 }
+ else store parent (load (left + 1) vm') vm'
+ in iter vm'' { sp = sp + 1 }
+ let vm1 = iter vm { sp = sp vm + 1 }
+ if sp vm1 == -1
+ then vm1
+ else do
+ let parentVal = load (load (sp vm1 + 1) vm1 + 1) vm1
+ let (a, vm2) = app (ord 'f') (load (sp vm1) vm1) vm1
+ lazy 0 (wor a) (wor parentVal) (store (sp vm2) parentVal vm2)
+ _ -> error "invalid combinator"
+ where lvm n f g = lazy n f g vm
+ -- num n = numberArg n vm
+
+eval :: VM -> VM
+eval vm@VM { sp } | sp == -1 = vm { sp = spTop }
+eval vm@VM { sp } =
+ -- let x1 = trace ("x1: " ++ show (load sp vm)) $ load sp vm
+ let x1 =
+ trace ("x1: " ++ show (load sp vm) ++ ", sp: " ++ show (spTop - sp))
+ $ load sp vm
+ x2 = load x1 vm
+ vm1 = store (sp - 1) x2 vm { sp = sp - 1 }
+ x3 = load x2 vm1
+ vm2 = store (sp - 2) x3 vm1 { sp = sp - 2 }
+ in if isComb x1
+ then trace ("1" ++ dumpStack vm ++ "\n" ++ dumpMem vm)
+ (eval $ rules x1 vm)
+ else if isComb x2
+ then trace ("2" ++ dumpStack vm1 ++ "\n" ++ dumpMem vm1)
+ (eval $ rules x2 vm1)
+ else if isComb x3
+ then trace ("3" ++ dumpStack vm2 ++ "\n" ++ dumpMem vm2)
+ (eval $ rules x3 vm2)
+ else trace ("continue") (eval vm2)
+ -- else eval $ store (sp - 1) (load n vm) vm { sp = sp - 1 }
+
+run :: Int -> VM -> VM
+run i vm@VM { sp } = eval $ store sp i vm
+
+hasVar0 :: Int -> Int -> VM -> Bool
+hasVar0 db depth vm = do
+ let f = load db vm
+ let a = load (db + 1) vm
+ case chr f of
+ 'V' -> a == depth
+ 'L' -> hasVar0 a (depth + 1) vm
+ _ -> hasVar0 f depth vm || hasVar0 a depth vm
+
+eta :: Int -> VM -> (Int, VM)
+eta x vm = do
+ let f = load x vm
+ let a = load (x + 1) vm
+ if not (isComb f) && load a vm == ord 'V' && load (a + 1) vm == 0 && not
+ (hasVar0 f 0 vm)
+ then (f, vm)
+ else app (ord 'L') x vm
+
+dbIndex :: Int -> Int -> VM -> (Int, VM)
+dbIndex x depth vm = do
+ let f = load x vm
+ let a = load (x + 1) vm
+ case
+ trace (show x ++ "," ++ show depth ++ ":" ++ show f ++ " " ++ show a)
+ (chr f)
+ of
+ 'V' -> app f (depth - 1 - a) vm
+ 'L' -> do
+ let (idx, vm1) = dbIndex a (depth + 1) vm
+ eta idx vm1
+ _ -> do
+ let (f', vm1) = dbIndex f depth vm
+ let (a', vm2) = dbIndex a depth vm1
+ app f' a' vm2
+
+clapp :: (Int, Int) -> VM -> (Int, VM)
+clapp (f, a) vm = case (chr f, chr a) of
+ ('K', 'I') -> vord 'F'
+ ('B', 'K') -> vord 'D'
+ ('C', 'I') -> vord 'T'
+ ('D', 'I') -> vord 'K'
+ (_, 'I') | load f vm == ord 'B' -> (load (f + 1) vm, vm)
+ (_, 'I') | load f vm == ord 'R' -> app (ord 'T') (load (f + 1) vm) vm
+ (_, 'T') | load f vm == ord 'B' && load (f + 1) vm == ord 'C' -> vord ':'
+ (_, 'I') | load f vm == ord 'S' && load (f + 1) vm == ord 'I' -> vord 'M'
+ _ -> app f a vm
+ where vord c = (ord c, vm)
+
+unDoubleVar :: Int -> Int -> VM -> (Int, VM)
+unDoubleVar n db vm = do
+ let f = load db vm
+ let a = load (db + 1) vm
+ let qf = load f vm == ord 'V' && load (f + 1) vm == n
+ let qa = load a vm == ord 'V' && load (a + 1) vm == n
+ if f == ord 'V'
+ then (db, vm)
+ else if f == ord 'L'
+ then
+ let (uda, vm') = unDoubleVar (n + 1) a vm
+ in if uda == 0 then (0, vm') else app (ord 'L') uda vm'
+ else if qf && qa
+ then app (ord 'V') n vm
+ else if qf || qa
+ then (0, vm)
+ else
+ let (udf, vm' ) = unDoubleVar n f vm
+ (uda, vm'') = unDoubleVar n a vm'
+ in if udf == 0
+ then (0, vm')
+ else if uda == 0 then (0, vm'') else app udf uda vm''
+
+recursive :: Int -> Int -> VM -> (Int, VM)
+recursive f a vm = do
+ let f' = load (a + 1) vm
+ let (f'', vm') = unDoubleVar 0 f' vm
+ -- logical subtleties!
+ if f == ord 'M' && load a vm == ord 'L' && load f' vm /= ord 'V'
+ then if f'' /= 0 then app (ord 'L') f'' vm' else (0, vm')
+ else (0, vm)
+
+combineK :: (Int, Int, Int, Int) -> VM -> (Int, VM)
+combineK huh@(n1, d1, n2, d2) vm = do
+ if trace (dumpMem vm ++ "\n" ++ show huh) $ n1 == 0
+ then if n2 == 0
+ then clapp (d1, d2) vm
+ else if load (n2 + 1) vm /= 0
+ then
+ let (n, vm1) = clapp (ord 'B', d1) vm
+ in combineK (0, n, load n2 vm1, d2) vm1
+ else combineK (0, d1, load n2 vm, d2) vm
+ else if load (n1 + 1) vm /= 0
+ then if n2 == 0
+ then
+ let (n, vm1) = clapp (ord 'R', d2) vm
+ in combineK (0, n, load n1 vm1, d1) vm1
+ else if load (n2 + 1) vm /= 0
+ then
+ let (n, vm1) = combineK (0, ord 'S', load n1 vm, d1) vm
+ in combineK (load n1 vm, n, load n2 vm1, d2) vm1
+ else
+ let (n, vm1) = combineK (0, ord 'C', load n1 vm, d1) vm
+ in combineK (load n1 vm, n, load n2 vm1, d2) vm1
+ else if n2 == 0
+ then combineK (load n1 vm, d1, 0, d2) vm
+ else if load (n2 + 1) vm /= 0
+ then if load n2 vm == 0 && load (n2 + 1) vm /= 0 && d2 == ord 'I'
+ then (d1, vm) -- eta optimization
+ else
+ let (n, vm1) = combineK (0, ord 'B', load n1 vm, d1) vm
+ in combineK (load n1 vm, n, load n2 vm1, d2) vm1
+ else combineK (load n1 vm, d1, load n2 vm, d2) vm
+
+zipN :: Int -> Int -> VM -> (Int, VM)
+zipN nf na vm | nf == 0 = (na, vm)
+zipN nf na vm | na == 0 = (nf, vm)
+zipN nf na vm = do
+ let (z, vm1) = zipN (load nf vm) (load na vm) vm
+ app z ((load (nf + 1) vm1) .|. (load (na + 1) vm1)) vm1
+
+convertK :: Int -> VM -> (Int, Int, VM)
+convertK db vm = do
+ let f = load db vm
+ let a = load (db + 1) vm
+ case chr f of
+ 'V' -> do
+ let iter n 0 vm' = (n, vm')
+ iter n i vm' = let (n', vm1) = app n 0 vm' in iter n' (i - 1) vm1
+ let (nf, vm1) = let (n, vm') = app 0 1 vm in iter n a vm'
+ (nf, ord 'I', vm1)
+ 'L' -> do
+ let (na, ca, vm1) = convertK a vm
+ let pn = load na vm1
+ if na == 0
+ then let (n, vm2) = clapp (ord 'K', ca) vm1 in (0, n, vm2)
+ else if load (na + 1) vm1 == 0
+ then let (n, vm2) = combineK (0, ord 'K', pn, ca) vm1 in (pn, n, vm2)
+ else (pn, ca, vm1)
+ _ -> do
+ let (nf, cf, vm1) = convertK f vm
+ let (cf', a', vm2) =
+ let (ca', vm') = recursive cf a vm1
+ in if nf == 0
+ then if ca' /= 0 then (ord 'Y', ca', vm') else (cf, a, vm')
+ else (cf, a, vm1)
+ let (na, ca, vm3) = convertK a' vm2
+ let (pn, vm4) = zipN nf na vm3
+ let (n, vm5) = combineK (nf, cf', na, ca) vm4
+ (pn, n, vm5)
+
+toCLK :: Int -> VM -> (Int, VM)
+toCLK db vm = do
+ let (n, cl, vm1) = convertK db vm
+ if n == 0 then (cl, vm1) else error "term not closed"
+
+resolveExpression :: Int -> VM -> Expression
+resolveExpression n _ | isComb n = error "unexpected combinator"
+resolveExpression n vm = do
+ let f = load n vm
+ let a = load (n + 1) vm
+ case chr f of
+ 'V' -> Idx a
+ 'L' -> Abs $ resolveExpression a vm
+ _ -> App (resolveExpression f vm) (resolveExpression a vm)
+
+parseExpression :: Expression -> VM -> IO (Int, VM)
+parseExpression (Abs t) vm = do
+ (t', vm1) <- parseExpression t vm
+ pure $ app (ord 'L') t' vm1
+parseExpression (App l r) vm = do
+ (l', vm1) <- parseExpression l vm
+ (r', vm2) <- parseExpression r vm1
+ pure $ app l' r' vm2
+parseExpression (Idx i) vm = do
+ pure $ app (ord 'V') i vm
+
+go :: String -> IO ()
+go s = do
+ let vm = new
+ let term = parseBLC s
+ (db, vm1) <- parseExpression term vm
+ let (cl, vm2) = toCLK db vm1
+ let (i, vm3) = app (ord 'V') (nvar vm2) vm2 { nvar = nvar vm2 + 1 }
+ let (j, vm4) = app cl i vm3
+ let (k, vm5) = app (ord 'L') j vm4
+ putStrLn $ dumpStack vm2
+ let res = run k vm5
+ let (idx, fin) = dbIndex (load spTop res) 0 res
+ print $ resolveExpression idx fin
reduce :: Expression -> Expression
reduce e = e