From a5a3f35da8d06ee4e5db4bc4ebc29b4c60fec6fe Mon Sep 17 00:00:00 2001 From: tv Date: Tue, 30 Dec 2014 14:51:13 +0100 Subject: rewrite Scanner to use Event --- src/Scanner.hs | 283 ++++++++++++++++----------------------------------------- 1 file changed, 80 insertions(+), 203 deletions(-) diff --git a/src/Scanner.hs b/src/Scanner.hs index 3c96a6e..361a1c6 100644 --- a/src/Scanner.hs +++ b/src/Scanner.hs @@ -1,217 +1,102 @@ -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} module Scanner - ( getKey - , hGetKey + ( scan ) where -import Prelude hiding ((/)) -import Control.Applicative -import Control.Monad.Error -import Control.Monad.State -import Control.Monad.Writer -import Data.Bits -import Data.Char -import System.IO +import Data.Char (ord) +import Event +import System.IO (Handle, hGetChar, hLookAhead, hWaitForInput) --- high level interface - -getKey :: IO String -getKey = hGetKey stdin - -hGetKey :: Handle -> IO String -hGetKey h = do - _ <- hLookAhead h -- wait for input - ((_, raw_s), _) <- runScanner $ scan h - 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 -- TODO underscore supresses warning, rename before usage.. - , buffer :: [C] - } - - -emptyScanState :: ScanState -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 :: Int timeout = 1 -scan, scanESC, scanCS :: - ( Monad m - , MonadIO m - , MonadError ScanError m - , MonadState ScanState m - , MonadWriter ScanLog m - ) - => Handle - -> m () - -scan h = do - c <- liftIO $ hGetC h - tell [c] - case () of _ - | c == 01/11 -> scanESC h - | otherwise -> return () - - -scanESC h = do - mb_c <- liftIO $ hWaitGetC timeout h - whenJust mb_c $ \ c -> do - tell [c] - case () of _ - | c == 05/11 -> - -- CSI - scanCS h - - | c == 01/11 -> - -- XXX M-F1 and other crazy chords may cause - -- \ESC\ESC... on wu, so we just recurse here... - scanESC h - - | c == 04/15 -> - -- XXX Non-CSI SS3 - one h $ between (04/00) (07/14) - - | otherwise -> return () - - -scanCS h = do - zeroOrMore h $ between (03/00) (03/15) -- parameter bytes - zeroOrMore h $ between (02/00) (02/15) -- intermediate bytes - one h $ 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 - ) - => Handle - -> (C -> Bool) - -> m () - -zeroOrMore h p = do - mb_c <- liftIO $ hWaitLookAheadC timeout h - whenJust mb_c $ \ c -> - when (p c) $ do - _ <- liftIO $ hGetC h -- drop - tell [c] - modify $ \q -> q { buffer = buffer q ++ [c] } - zeroOrMore h p - - -one h p = do - mb_c <- liftIO $ hWaitLookAheadC timeout h - 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 () - - - -(/) :: Int -> Int -> C -c / r = C c r +scan :: Handle -> IO Event +scan h = + hGetChar h >>= \case + '\ESC' -> scanESC h + c -> return $ EKey [c] + -data C = C { column :: Int, row :: Int } - deriving (Eq) +scanESC :: Handle -> IO Event +scanESC h = + hWaitGetChar timeout h >>= \case + Nothing -> return $ EKey "\ESC" + Just c + | c == '[' -> -- 05/11 + scanCS h + | c == '\ESC' -> -- 01/11 + -- XXX M-F1 and other crazy chords may cause + -- \ESC\ESC... on wu, so we just recurse here... + scanESC h + | c == 'O' -> -- 04/15 + -- XXX Non-CSI SS3 + -- XXX finalByte is maybe calles somehow else here, but it's + -- the same range + one h finalByte ['O','\ESC'] >>= + return . EKey . reverse + | otherwise -> + return $ EKey ['\ESC',c] + + +scanCS :: Handle -> IO Event +scanCS h = + hWaitLookAhead timeout h >>= \case + Nothing -> return $ EKey "\ESC" -- TODO move this to scanESC + Just c + | c == 'M' -> do + -- VT200 mouse + _ <- hGetChar h -- drop 'M' + b <- hGetChar h + x <- hGetChar h + y <- hGetChar h + return $ EMouse b (ord x - 32) (ord y - 32) + | otherwise -> + zeroOrMore h parameterByte ['[', '\ESC'] >>= + zeroOrMore h intermediateByte >>= + one h finalByte >>= + return . EKey . reverse + -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 +zeroOrMore :: Handle -> (Char -> Bool) -> [Char] -> IO [Char] +zeroOrMore h p buf = + hWaitLookAhead timeout h >>= \case + Nothing -> return buf + Just c + | p c -> + hGetChar h {-drop c-} >> zeroOrMore h p (c:buf) + | otherwise -> + return buf -instance Ord C where - compare (C c1 r1) (C c2 r2) = - case compare c1 c2 of - EQ -> compare r1 r2 - x -> x +one :: Handle -> (Char -> Bool) -> [Char] -> IO [Char] +one h p buf = + hWaitLookAhead timeout h >>= \case + Nothing -> return buf -- TODO error? + Just c + | p c -> do + _ <- hGetChar h -- drop c + return (c:buf) + | otherwise -> + error "expected one TODO" -fromChar :: Char -> C -fromChar c = let i = ord c in C ((shift i (-4)) .&. 0xf) (i .&. 0xf) +parameterByte :: Char -> Bool +parameterByte = between '0' '?' -- 03/00 03/15 -toChar :: C -> Char -toChar (C col row) = chr $ (shift col 4) .|. row +intermediateByte :: Char -> Bool +intermediateByte = between ' ' '/' -- 02/00 02/15 - --- +finalByte :: Char -> Bool +finalByte = between '@' '~' -- 04/00 07/14 -hGetC :: Handle -> IO C -hGetC h = hGetChar h >>= return . fromChar +between :: Ord a => a -> a -> (a -> Bool) +between lo hi = \ x -> lo <= x && x <= hi hWaitGetChar :: Int -> Handle -> IO (Maybe Char) @@ -222,17 +107,9 @@ hWaitGetChar t h = do else return Nothing -hWaitGetC :: Int -> Handle -> IO (Maybe C) -hWaitGetC t h = do - mb_ch <- hWaitGetChar t h - case mb_ch of - Nothing -> return Nothing - Just ch -> return $ Just $ fromChar $ ch - - -hWaitLookAheadC :: Int -> Handle -> IO (Maybe C) -hWaitLookAheadC t h = do +hWaitLookAhead :: Int -> Handle -> IO (Maybe Char) +hWaitLookAhead t h = do ready <- hWaitForInput h t if ready - then hLookAhead h >>= return . Just . fromChar + then hLookAhead h >>= return . Just else return Nothing -- cgit v1.2.3