parent
43a6269dcd
commit
21f1b20675
7 changed files with 233 additions and 0 deletions
@ -0,0 +1,26 @@ |
||||
# 29 IO |
||||
|
||||
## 29.9 Chapter Exercises |
||||
|
||||
### File I/O with Vigenère |
||||
|
||||
see [src/Vigenere.hs](./src/Vigenere.hs) |
||||
|
||||
```shell |
||||
> echo "this is a test!" | ./Vigenere -e blah | ./Vigenere -d blah |
||||
this is a test! |
||||
``` |
||||
|
||||
### Add timeouts to your utility |
||||
|
||||
see [src/Vigenere.hs](./src/Vigenere.hs) |
||||
|
||||
The `hWaitForInput` does not work so well though. It needs a newline to not |
||||
timeout. Or, of course, an EOF. |
||||
|
||||
### Config Directories |
||||
|
||||
see [src/IniDir.hs](./src/IniDir.hs) |
||||
|
||||
There are some bugs in my parser (e.g. comments after values), but the logic |
||||
of checking the dirctory works! |
@ -0,0 +1,100 @@ |
||||
{-# LANGUAGE OverloadedStrings #-} |
||||
{-# LANGUAGE QuasiQuotes #-} |
||||
|
||||
module Main where |
||||
|
||||
import Control.Applicative |
||||
import Data.ByteString (ByteString) |
||||
import Data.Char (isAlpha) |
||||
import Data.Map (Map) |
||||
import qualified Data.Map as M |
||||
import Data.Text (Text) |
||||
import qualified Data.Text.IO as TIO |
||||
import Text.Trifecta |
||||
import System.Directory (listDirectory) |
||||
import System.FilePath (takeExtension) |
||||
import Control.Exception (try) |
||||
import System.Environment (getArgs) |
||||
|
||||
newtype Header = Header String deriving (Eq, Ord, Show) |
||||
|
||||
parseBracketPair :: Parser a -> Parser a |
||||
parseBracketPair p = char '[' *> p <* char ']' |
||||
|
||||
parseHeader :: Parser Header |
||||
parseHeader = parseBracketPair (Header <$> some letter) |
||||
|
||||
assignmentEx :: ByteString |
||||
assignmentEx = "woot=1" |
||||
|
||||
type Name = String |
||||
type Value = String |
||||
type Assignments = Map Name Value |
||||
|
||||
parseAssignment :: Parser (Name, Value) |
||||
parseAssignment = do |
||||
name <- some letter |
||||
_ <- char '=' |
||||
val <- some (noneOf "\n") |
||||
skipEOL -- important! |
||||
return (name, val) |
||||
|
||||
-- | Skip end of lien and whitespace beyond. |
||||
skipEOL :: Parser () |
||||
skipEOL = skipMany (oneOf "\n") |
||||
|
||||
-- | Skip comments starting at the beginning of the line |
||||
skipComments :: Parser () |
||||
skipComments = skipMany $ do |
||||
_ <- char ';' <|> char '#' |
||||
skipMany (noneOf "\n") |
||||
skipEOL |
||||
|
||||
data Section = Section Header Assignments deriving (Eq, Show) |
||||
|
||||
newtype Config = Config (Map Header Assignments) deriving (Eq, Show) |
||||
|
||||
skipWhitespace :: Parser () |
||||
skipWhitespace = skipMany (char ' ' <|> char '\n') |
||||
|
||||
parseSection :: Parser Section |
||||
parseSection = do |
||||
skipWhitespace |
||||
skipComments |
||||
h <- parseHeader |
||||
skipEOL |
||||
assignments <- some parseAssignment |
||||
return $ Section h (M.fromList assignments) |
||||
|
||||
rollup :: Section -> Map Header Assignments -> Map Header Assignments |
||||
rollup (Section h a) m = M.insert h a m |
||||
|
||||
parseIni :: Parser Config |
||||
parseIni = do |
||||
sections <- some parseSection |
||||
let mapOfSections = foldr rollup M.empty sections |
||||
return $ Config mapOfSections |
||||
|
||||
parseIniFromFile :: FilePath -> IO (Maybe Config) |
||||
parseIniFromFile fp = do |
||||
s <- readFile fp |
||||
let parsed = parseString parseIni mempty s |
||||
case parsed of |
||||
(Success a) -> return $ Just a |
||||
_ -> return Nothing |
||||
|
||||
main :: IO () |
||||
main = do |
||||
args <- getArgs |
||||
case args of |
||||
[dir] -> do |
||||
l <- Control.Exception.try $ listDirectory dir |
||||
case l of |
||||
Left e -> ioError e |
||||
Right xs -> do |
||||
let xs' = filter ((==) ".ini" . takeExtension) xs |
||||
f x = sequenceA (x, parseIniFromFile (dir ++ "/" ++ x)) |
||||
xs'' = sequenceA $ fmap f xs' |
||||
xs''' <- xs'' |
||||
print $ M.fromList xs''' |
||||
_ -> putStrLn "Need directory as argument." |
@ -0,0 +1,78 @@ |
||||
module Main where |
||||
|
||||
import Data.Char |
||||
import System.Environment (getArgs) |
||||
import System.IO (hPutStr, hGetChar, stdout, stdin, hWaitForInput |
||||
, stderr, interact) |
||||
import System.Exit (exitFailure) |
||||
import Control.Exception (try) |
||||
import System.IO.Error (isEOFError, ioError) |
||||
|
||||
alphaIndex :: Char -> Int |
||||
alphaIndex c |
||||
| elem c ['a'..'z'] = ord c - ord 'a' |
||||
| elem c ['A'..'Z'] = ord c - ord 'A' |
||||
| otherwise = 0 |
||||
|
||||
shift :: (Int -> Int -> Int) -> Char -> Char -> Char |
||||
shift f c k |
||||
| elem c ['a'..'z'] = go c k 'a' |
||||
| elem c ['A'..'Z'] = go c k 'A' |
||||
| otherwise = c |
||||
where go p key base = let rel = f (alphaIndex p) (alphaIndex key) |
||||
r = 26 |
||||
b = ord base |
||||
in chr $ (mod rel r) + b |
||||
|
||||
-- wrote own zipWith variant which maps only when isAlpha |
||||
vigenere :: (Int -> Int -> Int) -> [Char] -> [Char] -> [Char] |
||||
vigenere _ xs [] = xs -- necessary to avoid bottom |
||||
vigenere f xs ys = myZipWith (shift f) xs ys |
||||
where myZipWith _ [] _ = [] |
||||
myZipWith f s [] = myZipWith f s ys |
||||
myZipWith f (a:as) k@(b:bs) = |
||||
if isAlpha a |
||||
then f a b : myZipWith f as bs |
||||
else a : myZipWith f as k |
||||
|
||||
readAll :: IO String |
||||
readAll = do |
||||
b <- try $ hWaitForInput stdin 5000 |
||||
case b of |
||||
Left e -> if isEOFError e |
||||
then return "" |
||||
else ioError e |
||||
Right False -> do |
||||
hPutStr stderr $ "Timeout." |
||||
exitFailure |
||||
Right True -> do |
||||
c <- hGetChar stdin |
||||
fmap (c:) readAll |
||||
|
||||
encrypt :: String -> IO () |
||||
encrypt key = do |
||||
s <- readAll |
||||
let s' = vigenere (+) s key |
||||
hPutStr stdout s' |
||||
|
||||
decrypt :: String -> IO () |
||||
decrypt key = do |
||||
s <- readAll |
||||
let s' = vigenere (-) s key |
||||
hPutStr stdout s' |
||||
|
||||
main :: IO () |
||||
main = do |
||||
args <- getArgs |
||||
case args of |
||||
["-e", key] -> encrypt key |
||||
["-d", key] -> decrypt key |
||||
otherwise -> putStrLn "Invalid arguments. Use (-d|-e) <key>." |
||||
|
||||
anotherWay :: IO () |
||||
anotherWay = do |
||||
args <- getArgs |
||||
case args of |
||||
["-e", key] -> interact $ flip (vigenere (+)) key |
||||
["-d", key] -> interact $ flip (vigenere (-)) key |
||||
otherwise -> putStrLn "Invalid arguments. Use (-d|-e) <key>." |
@ -0,0 +1,15 @@ |
||||
module WhatHappens where |
||||
|
||||
import Control.Concurrent |
||||
|
||||
myData :: IO (MVar Int) |
||||
myData = newEmptyMVar |
||||
|
||||
main :: IO () |
||||
main = do |
||||
mv <- myData |
||||
putMVar mv 0 |
||||
mv' <- myData |
||||
putMVar mv' 0 |
||||
zero <- takeMVar mv' |
||||
print zero |
@ -0,0 +1 @@ |
||||
don't parse me! |
@ -0,0 +1,6 @@ |
||||
[Main] |
||||
answer=42 |
||||
name=gael ; this is a test |
||||
|
||||
[SubSection] |
||||
description=hmmm |
@ -0,0 +1,7 @@ |
||||
# boy o boy |
||||
|
||||
[Phasers] |
||||
set=stun |
||||
|
||||
[Second] |
||||
first=last |
Loading…
Reference in new issue