parabox/app/Parser.hs

147 lines
4.2 KiB
Haskell
Raw Permalink Normal View History

2022-12-07 17:31:07 -08:00
module Parser (parse) where
2022-12-04 16:45:15 -08:00
2022-12-07 17:31:07 -08:00
import Control.Applicative
import Control.Monad
2022-12-08 09:34:27 -08:00
import Data.Array.Unboxed (UArray, listArray)
2022-12-04 16:45:15 -08:00
import Data.Map qualified as Map
import Data.Set qualified as Set
2022-12-07 17:31:07 -08:00
import Graphics.Vty.Attributes
2022-12-07 10:11:06 -08:00
import Text.ParserCombinators.ReadP hiding (many)
2022-12-07 17:31:07 -08:00
import Model
2022-12-04 16:45:15 -08:00
2022-12-08 18:58:07 -08:00
parse :: String -> Either String World
2022-12-07 10:11:06 -08:00
parse str =
case readP_to_S ((,) <$> parseHeader <*> many parseBlock <* eof) str of
2022-12-08 18:58:07 -08:00
[] -> Left "world parsing failed"
2022-12-07 10:11:06 -08:00
(((p,h,w),bs),_):_ ->
do let m = Map.fromListWith (++) [(k,[v]) | (_,_,kvs) <- bs, (k,v) <- kvs]
2022-12-08 18:58:07 -08:00
home <-
case Map.lookup '=' m of
Nothing -> Left "no home location defined"
Just [home] -> Right home
Just _ -> Left "ambiguous home defined"
boxes <- sequence $
Map.fromList
[(n, case Map.lookup n m of
Nothing -> Right b {boxLocation = Nothing}
Just [l] -> Right b {boxLocation = Just l}
Just _ -> Left ("ambiguous location for: " ++ [n])
) | (n,b,_) <- bs]
Right World {
worldBoxes = boxes,
worldMe = p,
worldButtons = Set.fromList (Map.findWithDefault [] '-' m),
worldHome = home,
worldSize = (h, w)
}
2022-12-07 10:11:06 -08:00
parseHeader :: ReadP (Char,Int,Int)
parseHeader =
do "player" <- token
[p] <- token
h <- option 81
do "height" <- token
integer
2022-12-08 18:58:07 -08:00
w <- option (h*2)
2022-12-07 10:11:06 -08:00
do "width" <- token
integer
pure (p,h,w)
2022-12-04 16:45:15 -08:00
2022-12-07 10:11:06 -08:00
integer :: ReadP Int
integer = readS_to_P reads
2022-12-04 16:45:15 -08:00
2022-12-07 10:11:06 -08:00
parseBoring :: ReadP Bool
parseBoring =
do t <- token
case t of
2022-12-07 17:31:07 -08:00
"boring" -> pure True
2022-12-07 10:11:06 -08:00
"interesting" -> pure False
2022-12-07 17:31:07 -08:00
_ -> empty
2022-12-04 16:45:15 -08:00
2022-12-07 10:11:06 -08:00
parseBlock :: ReadP (Char, Box, [(Char, Location)])
parseBlock =
do cmd <- token
case cmd of
"block" ->
do [name] <- token
color <- parseColor
boring <- parseBoring
skipMany (satisfy (' '==))
_ <- char '\n'
xs1 <- parseWalls
let locs = findLocs name xs1
let b = Box Nothing (Original (walls xs1)) color boring
2022-12-07 10:11:06 -08:00
pure (name, b, locs)
"link" ->
do [name] <- token
[target] <- token
color <- parseColor
_ <- char '\n'
2022-12-07 17:31:07 -08:00
pure (name, Box Nothing (Link target) color True, [])
2022-12-07 10:11:06 -08:00
"infinity" ->
do [name] <- token
[target] <- token
color <- parseColor
_ <- char '\n'
2022-12-07 17:31:07 -08:00
pure (name, Box Nothing (Infinity target) color True,[])
2022-12-07 10:11:06 -08:00
"epsilon" ->
do [name] <- token
[target] <- token
color <- parseColor
skipMany (satisfy (' '==))
_ <- char '\n'
xs1 <- parseWalls
let locs = findLocs name xs1
2022-12-07 17:31:07 -08:00
let b = Box Nothing (Epsilon target (walls xs1)) color False
2022-12-07 10:11:06 -08:00
pure (name, b, locs)
_ -> empty
2022-12-06 13:39:30 -08:00
2022-12-07 10:11:06 -08:00
parseWalls :: ReadP [String]
parseWalls =
do row0 <- munch1 ('\n' /=) <* char '\n'
rows <- replicateM (length row0 - 1) (munch1 ('\n' /=) <* char '\n')
pure (row0:rows)
token :: ReadP String
token = readS_to_P lex
parseColor :: ReadP Attr
parseColor =
do t <- token
case t of
"white" -> pure (withForeColor defAttr white)
"cyan" -> pure (withForeColor defAttr cyan)
"blue" -> pure (withForeColor defAttr blue)
"red" -> pure (withForeColor defAttr red)
"yellow" -> pure (withForeColor defAttr yellow)
"magenta" -> pure (withForeColor defAttr magenta)
"black" -> pure (withForeColor defAttr black)
"green" -> pure (withForeColor defAttr green)
_ -> empty
2022-12-04 16:45:15 -08:00
2022-12-08 09:34:27 -08:00
walls :: [String] -> UArray Coord Bool
2022-12-06 13:39:30 -08:00
walls rows = listArray ((ylo,xlo),(yhi,xhi)) (map ('▓'==) (concat rows))
where
2024-05-22 14:16:46 -07:00
width = case rows of x:_ -> length x; [] -> 0
2022-12-06 13:39:30 -08:00
height = length rows
ylo = - ((height - 1) `div` 2)
yhi = height `div` 2
xlo = - ((width - 1) `div` 2)
xhi = width `div` 2
mkRange :: Int -> (Int, Int)
2024-05-22 14:16:46 -07:00
mkRange n = (-((n-1)`div`2), n`div`2)
2022-12-04 16:45:15 -08:00
findLocs :: Char -> [[Char]] -> [(Char, Location)]
findLocs name xs =
[ (c, Location name y x)
2022-12-06 13:39:30 -08:00
| let (ylo, yhi) = mkRange (length xs)
, (y, row) <- zip [ylo .. yhi] xs
, let (xlo,xhi) = mkRange (length row)
, (x, c ) <- zip [xlo..xhi] row
]