animascii-0.1.0.0: src/Parse.hs
module Parse where
import Text.Parsec
import Terminal.Game
import Data.List
import qualified Data.Functor.Identity as I
import qualified Data.Bifunctor as B
import qualified Data.Tuple as T
-- reads an animation file, returns frames (or an error)
readAnimation :: FilePath -> IO (Either String Animation)
readAnimation fp = readFile fp >>= \c ->
let parsed = parse animation fp c in
return (B.first (("readFrames: " ++) . show) parsed)
type Frame p i = (i, p)
-------------
-- PARSERS --
-------------
type Parser a = ParsecT String () (I.Identity) a
animation :: Parser Animation
animation = top >>= \(w, mc, l) ->
many1 (frame mc w) >>= \fs ->
let fhs = fixHeight w fs in
return (creaAni l fhs)
<?> "animation"
-- get every frame at the same height
fixHeight :: Integer -> [Frame Plane Integer] -> [Frame Plane Integer]
fixHeight w fs = let ps = map snd fs
hs = map (snd . planeSize) ps
mh = maximum hs
in map (fhf w mh) fs
where
fhf :: Width -> Integer -> Frame Plane Integer ->
Frame Plane Integer
fhf wh hh (i, p) = let p' = blankPlane wh hh &
(1, 1) % p
in (i, p')
top :: Parser (Integer, Maybe Char, Loop)
top = lLength >>= \ln ->
indicators >>= \(tc, l) ->
return (ln, tc, l)
<?> "top-of-file indicator"
where
lLength :: Parser Integer
lLength = lookAhead . try $
genericLength <$> manyTill anyChar (char '\n')
indicators :: Parser (Maybe Char, Loop)
indicators = inverted transChar loops
-- vedi places
inverted pa pb = try (places pa pb) <|>
T.swap <$> places pb pa
-- per 2 parser interscambiabili
places pa pb = tp1 *> pa >>= \x ->
tp *> pb >>= \y ->
tp >>
endOfLine >>
return (x, y)
tp :: Parser ()
tp = () <$ many (char '=')
tp1 :: Parser ()
tp1 = () <$ many1 (char '=')
-- Tc=
transChar :: Parser (Maybe Char)
transChar = optionMaybe (try $ char 'T' *> anyChar <* char '=')
<?> "transparent char indicator"
-- L2=
loops :: Parser (Loop)
loops = option AlwaysLoop
(try $ char 'L'
*> fmap (flip Times Elapse) integer <* char '=')
<?> "loop-times indicator"
-- frame of width w
frame :: Maybe Char -> Integer -> Parser (Frame Plane Integer)
frame mc w = content mc w >>= \tart ->
endFrame >>= \delay ->
return (delay, tart)
<?> "frame (content + division line)"
content :: Maybe Char -> Integer -> Parser Plane
content mc w = stringPlane mc w <$> manyTill1 anyChar end <* endOfLine
<?> "frame content"
where
manyTill1 p e = (:) <$> p <*> manyTill p e
end = lookAhead . try $ endOfLine *> endFrame
endFrame :: Parser Integer
endFrame = frameLine *> integer <* (frameLine *> fin)
<?> "frame division line"
where
fin = () <$ endOfLine <|> eof
frameLine = () <$ many1 (char '-')
-----------------
-- ANCILLARIES --
-----------------
integer :: Parser Integer
integer = fmap read (many1 digit) <?> "integer"