{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleContexts #-} module Scanner where import Control.Monad (forever) import System.IO import Data.Ord import Data.Bits import Data.Char import Prelude hiding ((/)) import Control.Applicative import Control.Monad.Error import Control.Monad.State import Control.Monad.Writer import Data.Time.Clock -- high level interface getKey :: IO String getKey = do _ <- hLookAhead stdin -- wait for input ((_, raw_s), _) <- runScanner scan return $ map toChar raw_s type P = C type I = C type F = C data Token = CS [P] [I] F | Chr C deriving (Show) type ScanLog = [C] type ScanError = String data ScanState = ScanState { result :: Maybe Token , buffer :: [C] } emptyScanState = ScanState Nothing [] newtype Scanner m a = Scanner (ErrorT ScanError (WriterT ScanLog (StateT ScanState m)) a) deriving ( Applicative , Functor , Monad , MonadIO , MonadState ScanState , MonadError ScanError , MonadWriter ScanLog ) runScanner :: Scanner m a -> m ((Either ScanError a, ScanLog), ScanState) runScanner (Scanner a) = runStateT (runWriterT (runErrorT a)) emptyScanState -- TODO max timeout timeout = 1 main :: IO () main = do hSetEcho stdin False hSetBuffering stdin NoBuffering forever $ do _ <- hLookAhead stdin -- wait for input t0 <- getCurrentTime ((res, s), _) <- runScanner scan t1 <- getCurrentTime putStrLn $ "====> \ESC[32;1m" ++ show s ++ "\ESC[m in " ++ (show $ diffUTCTime t1 t0) ++ ": \"\ESC[35m" ++ (s >>= colorize . toChar) ++ "\ESC[m\"" case res of Left msg -> putStrLn $ " error: " ++ msg Right _ -> return () scan, scanESC, scanCS :: ( Monad m , MonadIO m , MonadError ScanError m , MonadState ScanState m , MonadWriter ScanLog m ) => m () scan = do c <- liftIO $ hGetC stdin tell [c] case () of _ | c == 01/11 -> scanESC | otherwise -> return () scanESC = do mb_c <- liftIO $ hWaitGetC timeout stdin whenJust mb_c $ \ c -> do tell [c] case () of _ | c == 05/11 -> -- CSI scanCS | c == 01/11 -> -- XXX M-F1 and other crazy chords may cause -- \ESC\ESC... on wu, so we just recurse here... scanESC | c == 04/15 -> -- XXX Non-CSI SS3 one $ between (04/00) (07/14) | otherwise -> return () scanCS = do zeroOrMore $ between (03/00) (03/15) -- parameter bytes zeroOrMore $ between (02/00) (02/15) -- intermediate bytes one $ between (04/00) (07/14) -- final byte between :: C -> C -> (C -> Bool) between lo hi = \ x -> lo <= x && x <= hi zeroOrMore, one :: ( Monad m , MonadIO m , MonadError ScanError m , MonadState ScanState m , MonadWriter ScanLog m ) => (C -> Bool) -> m () zeroOrMore p = do mb_c <- liftIO $ hWaitLookAheadC timeout stdin whenJust mb_c $ \ c -> when (p c) $ do _ <- liftIO $ hGetC stdin -- drop tell [c] modify $ \q -> q { buffer = buffer q ++ [c] } zeroOrMore p one p = do mb_c <- liftIO $ hWaitLookAheadC timeout stdin whenJust mb_c $ \ c -> do if p c then do _ <- liftIO getChar tell [c] modify $ \q -> q { buffer = buffer q ++ [c] } else do throwError "expected one TODO" whenJust :: Monad m => Maybe a -> (a -> m ()) -> m () whenJust mb f = case mb of Just a -> f a Nothing -> return () c / r = C c r data C = C { column :: Int, row :: Int } deriving (Eq) instance Show C where show C{..} = (padl 2 '0' $ show column) ++ "/" ++ (padl 2 '0' $ show row) where padl n c s | length s < n = padl n c (c : s) | otherwise = s instance Ord C where compare (C c1 r1) (C c2 r2) = case compare c1 c2 of EQ -> compare r1 r2 x -> x fromChar :: Char -> C fromChar c = let i = ord c in C ((shift i (-4)) .&. 0xf) (i .&. 0xf) toChar :: C -> Char toChar (C col row) = chr $ (shift col 4) .|. row colorize :: Char -> String colorize c | isPrint c = [c] | otherwise = "\ESC[1m" ++ (showLitChar c "") ++ "\ESC[22m" -- hWaitGetChar t h = do ready <- hWaitForInput h t if ready then hGetChar h >>= return . Just else return Nothing hGetC h = hGetChar h >>= return . fromChar hWaitGetC t h = do mb_ch <- hWaitGetChar t h case mb_ch of Nothing -> return Nothing Just ch -> return $ Just $ fromChar $ ch hWaitLookAheadC t h = do ready <- hWaitForInput h t if ready then hLookAhead h >>= return . Just . fromChar else return Nothing -- CRUFT --expect cx ca = -- when (cx /= ca) $ -- throwError $ "expected: " ++ (show cx) ++ ", got: " ++ (show ca) -- -- -- -- expect (01/11) c -- -- c <- (liftIO getChar) >>= return . fromChar -- -- tell [c] -- -- expect (05/11) c --liftIO $ putStrLn $ (show c) ++ " -> " ++ (show s)