packages feed

hs2bf-0.6: Util.hs

{-# LANGUAGE FlexibleInstances #-}
-- | Modular error reporting and common functions
--
-- note for future me: Arrow vs. Monad
--    Suppose each process can return either error or result.
--   Parallel execution of such processes can be written as:
--    (a -> m b) -> [a] -> m [b]  : Monadic form
--    w a b -> w [a] [b] : Arrow form
-- 
-- How to decide on one?
--    If you can write [m b] -> m [b] , then use Monad.
--   Otherwise use Arrow.
module Util(module Util,throwError) where
import Control.Arrow
import Control.Monad.Error
import Control.Monad.State
import Control.Monad.Identity
import Data.List
import Data.Word
import qualified Data.IntMap as IM


-- | Process that may fail with ['CompileError']
-- ErrorT Identity is used for future use. (cf. ErrorT Writer)
type Process a=ErrorT [CompileError] Identity a

runProcess :: Process a -> Either [CompileError] a
runProcess=runIdentity . runErrorT

runProcessWithIO :: (a->IO ()) -> Process a -> IO ()
runProcessWithIO f=either (putStr . unlines . map show) f . runProcess




-- | A detailed compile error
-- 
-- * 'CompileError' is used in early stages where filename and line number is known
--
-- * 'CompileErrorN' is used in later stages where only non-numerical position is available
data CompileError
    =CompileError String String String -- ^ e.g. CompileError "Haskell->Core" "Main.hs:12" "parse error"
    |CompileErrorN String String [String] -- ^ e.g. CompileErrorN "SAM->SAM" "unknown register x" ["while","proc foo"]

instance Show CompileError where
    show (CompileError m p d)=m++":"++p++":\n"++d
    show (CompileErrorN m d ps)=m++":\n"++d++"\n"++concatMap (\x->"in "++x++"\n") ps

instance Error [CompileError] where
    noMsg=[]


-- | Nested structure multiple reporter monad
type NMR p e a=State ([p],[([p],e)]) a

report :: e -> NMR p e ()
report e=do
    (pos,es)<-get
    put (pos,(pos,e):es)

within :: p -> NMR p e a -> NMR p e a
within x f=do
    modify (first (x:))
    r<-f
    modify (first tail)
    return r

runNMR :: NMR p e a -> (a,[([p],e)])
runNMR=second snd . flip runState ([],[])





data SBlock
    =EmptyLine
    |Line IBlock
    |Group [SBlock]
    |Indent SBlock

-- | Inline string
data IBlock
    =Prim String
    |Pack [IBlock]
    |Span [IBlock]

-- | Render 'SBlock'
compileSB :: SBlock -> String
compileSB=unlines . saux 0

saux :: Int -> SBlock -> [String]
saux _ EmptyLine=[""]
saux n (Line i)=[replicate (n*4) ' '++compileIB i]
saux n (Group xs)=concatMap (saux n) xs
saux n (Indent x)=saux (n+1) x

-- | Render 'IBlock'
compileIB :: IBlock -> String
compileIB (Prim x)=x
compileIB (Pack xs)=concatMap compileIB xs
compileIB (Span xs)=concatMap compileIB $ intersperse (Prim " ") xs


-- | Moderately fast memory suitable for use in interpreters.
data FlatMemory=FlatMemory (IM.IntMap Word8)

minit :: FlatMemory
minit=FlatMemory $ IM.empty

mread :: FlatMemory -> Int -> Word8
mread (FlatMemory m) i=maybe 0 id $ IM.lookup i m

mwrite :: FlatMemory -> Int -> Word8 -> FlatMemory
mwrite (FlatMemory m) i v=FlatMemory $ IM.insert i v m

mmodify :: FlatMemory -> Int -> (Word8 -> Word8) -> FlatMemory
mmodify fm i f=mwrite fm i (f $ mread fm i)

msize :: FlatMemory -> Int
msize (FlatMemory m)=case IM.maxViewWithKey m of
    Nothing -> 0
    Just ((k,v),m') -> if v/=0 then k+1 else msize $ FlatMemory m'


-- | a b c ... z aa ab ac ... az ba ...
-- avoid CAF.
stringSeq :: String -> [String]
stringSeq prefix=tail $ map ((prefix++) . reverse) $ iterate stringInc []

stringInc :: String -> String
stringInc []="a"
stringInc ('z':xs)='a':stringInc xs
stringInc (x:xs)=succ x:xs

-- | usage
--
-- > change1 "XYZ" "abc"
--
-- evaluates to
--
-- > ["Xbc","aYc","abZ"]
change1 :: [a] -> [a] -> [[a]]
change1 (x:xs) (y:ys)=(x:ys):map (y:) (change1 xs ys)
change1 _ _=[]

mapAt :: Int -> (a->a) -> [a] -> [a]
mapAt ix0 f=zipWith g [0..]
    where g ix x=if ix==ix0 then f x else x

fst3 (x,_,_)=x
snd3 (_,y,_)=y
thr3 (_,_,z)=z


equaling :: Eq b => (a -> b) -> (a -> a -> Bool)
equaling f x y=f x==f y