[hs] Clean up parsing a bit
This commit is contained in:
parent
155118b5f2
commit
098214e56e
4 changed files with 41 additions and 30 deletions
|
|
@ -3,10 +3,12 @@ module Aoc.Parse
|
||||||
, module Text.Megaparsec.Char
|
, module Text.Megaparsec.Char
|
||||||
, module Text.Megaparsec.Char.Lexer
|
, module Text.Megaparsec.Char.Lexer
|
||||||
, Parser
|
, Parser
|
||||||
|
, around
|
||||||
, manyLines
|
, manyLines
|
||||||
, oneSpace
|
, lineWhile
|
||||||
, untilSpace
|
, lineUntil
|
||||||
, untilEol
|
, lineSatisfy
|
||||||
|
, line
|
||||||
, lineChar
|
, lineChar
|
||||||
, word
|
, word
|
||||||
, digit
|
, digit
|
||||||
|
|
@ -25,22 +27,37 @@ import Text.Megaparsec.Char.Lexer (binary, decimal, float,
|
||||||
hexadecimal, octal, scientific,
|
hexadecimal, octal, scientific,
|
||||||
signed)
|
signed)
|
||||||
|
|
||||||
|
-- General combinators
|
||||||
|
|
||||||
|
-- | Like 'between', but keeps the outer results instead of the inner result
|
||||||
|
around :: Applicative m => m i -> m l -> m r -> m (l, r)
|
||||||
|
around inner left right = (,) <$> (left <* inner) <*> right
|
||||||
|
|
||||||
type Parser = Parsec Void T.Text
|
type Parser = Parsec Void T.Text
|
||||||
|
|
||||||
|
-- AoC-specific parsers
|
||||||
|
|
||||||
manyLines :: Parser a -> Parser [a]
|
manyLines :: Parser a -> Parser [a]
|
||||||
manyLines p = endBy (try p) newline
|
manyLines p = endBy (try p) newline
|
||||||
|
|
||||||
oneSpace :: Parser Char
|
onLine :: (Char -> Bool) -> Char -> Bool
|
||||||
oneSpace = label "whitespace character" $ satisfy isSpace
|
onLine _ '\n' = False
|
||||||
|
onLine p c = p c
|
||||||
|
|
||||||
untilSpace :: Parser T.Text
|
lineWhile :: (Char -> Bool) -> Parser T.Text
|
||||||
untilSpace = takeWhileP (Just "non-whitespace character") (not . isSpace)
|
lineWhile = takeWhileP Nothing . onLine
|
||||||
|
|
||||||
untilEol :: Parser T.Text
|
lineUntil :: (Char -> Bool) -> Parser T.Text
|
||||||
untilEol = takeWhileP (Just "non-newline character") (/= '\n')
|
lineUntil p = lineWhile (not . p)
|
||||||
|
|
||||||
|
lineSatisfy :: (Char -> Bool) -> Parser Char
|
||||||
|
lineSatisfy = satisfy . onLine
|
||||||
|
|
||||||
|
line :: Parser T.Text
|
||||||
|
line = lineWhile (const True)
|
||||||
|
|
||||||
lineChar :: Parser Char
|
lineChar :: Parser Char
|
||||||
lineChar = label "non-newline character" $ satisfy (/= '\n')
|
lineChar = lineSatisfy (const True)
|
||||||
|
|
||||||
word :: Parser T.Text
|
word :: Parser T.Text
|
||||||
word = takeWhileP (Just "alphanumeric character") isAlphaNum
|
word = takeWhileP (Just "alphanumeric character") isAlphaNum
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ parser = manyLines $ Line
|
||||||
<$> (decimal <* char '-')
|
<$> (decimal <* char '-')
|
||||||
<*> (decimal <* space)
|
<*> (decimal <* space)
|
||||||
<*> (anySingle <* char ':' <* space)
|
<*> (anySingle <* char ':' <* space)
|
||||||
<*> untilEol
|
<*> line
|
||||||
|
|
||||||
validCount :: Line -> Bool
|
validCount :: Line -> Bool
|
||||||
validCount l = n >= lMin l && n <= lMax l
|
validCount l = n >= lMin l && n <= lMax l
|
||||||
|
|
|
||||||
|
|
@ -72,10 +72,10 @@ mapFieldsExceptCid f p = [f $ byr p, f $ iyr p, f $ eyr p, f $ hgt p, f $ hcl p,
|
||||||
|
|
||||||
pField :: T.Text -> Parser a -> Parser (Field a)
|
pField :: T.Text -> Parser a -> Parser (Field a)
|
||||||
pField name p = do
|
pField name p = do
|
||||||
notFollowedBy oneSpace
|
notFollowedBy spaceChar
|
||||||
void $ string name
|
void $ string name
|
||||||
void $ char ':'
|
void $ char ':'
|
||||||
(Valid <$> try p) <|> (Invalid <$> untilSpace)
|
(Valid <$> try p) <|> (Invalid <$> lineUntil isSpace)
|
||||||
|
|
||||||
nDigits :: Int -> Parser Int
|
nDigits :: Int -> Parser Int
|
||||||
nDigits n = do
|
nDigits n = do
|
||||||
|
|
@ -115,7 +115,7 @@ pHcl :: Parser Passport
|
||||||
pHcl = do
|
pHcl = do
|
||||||
f <- pField "hcl" $ do
|
f <- pField "hcl" $ do
|
||||||
void $ char '#'
|
void $ char '#'
|
||||||
t <- untilSpace
|
t <- lineUntil isSpace
|
||||||
guard $ T.length t == 6 && T.all isHexDigit t
|
guard $ T.length t == 6 && T.all isHexDigit t
|
||||||
pure t
|
pure t
|
||||||
pure mempty{hcl = f}
|
pure mempty{hcl = f}
|
||||||
|
|
@ -138,7 +138,7 @@ pCid = do
|
||||||
parser :: Parser [Passport]
|
parser :: Parser [Passport]
|
||||||
parser = passport `sepBy` newline
|
parser = passport `sepBy` newline
|
||||||
where
|
where
|
||||||
passport = mconcat <$> field `endBy1` oneSpace
|
passport = mconcat <$> field `endBy1` spaceChar
|
||||||
field = pByr <|> pIyr <|> pEyr <|> pHgt <|> pHcl <|> pEcl <|> pPid <|> pCid
|
field = pByr <|> pIyr <|> pEyr <|> pHgt <|> pHcl <|> pEcl <|> pPid <|> pCid
|
||||||
|
|
||||||
hasRequiredKeys :: Passport -> Bool
|
hasRequiredKeys :: Passport -> Bool
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ module Aoc.Y2020.D16
|
||||||
( day
|
( day
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad
|
|
||||||
import Data.Bifunctor
|
import Data.Bifunctor
|
||||||
import Data.List
|
import Data.List
|
||||||
|
|
||||||
|
|
@ -16,22 +15,17 @@ import Aoc.Parse
|
||||||
data Input = Input [(T.Text, Int -> Bool)] [Int] [[Int]]
|
data Input = Input [(T.Text, Int -> Bool)] [Int] [[Int]]
|
||||||
|
|
||||||
parser :: Parser Input
|
parser :: Parser Input
|
||||||
parser = do
|
parser = Input
|
||||||
fields <- many (fieldLine <* newline)
|
<$> many (field <* newline)
|
||||||
void $ string "\nyour ticket:\n"
|
<*> (string "\nyour ticket:\n" *> ticket)
|
||||||
myTicket <- ticket <* newline
|
<*> (string "\nnearby tickets:\n" *> many ticket)
|
||||||
void $ string "\nnearby tickets:\n"
|
|
||||||
nearbyTickets <- many (ticket <* newline)
|
|
||||||
pure $ Input fields myTicket nearbyTickets
|
|
||||||
where
|
where
|
||||||
fieldLine = do
|
bound = around (string "-") decimal decimal
|
||||||
name <- takeWhileP Nothing $ \c -> (c /= ':') && (c /= '\n')
|
field = do
|
||||||
void $ string ": "
|
name <- lineUntil (==':') <* string ": "
|
||||||
(a, b) <- (,) <$> (decimal <* string "-") <*> decimal
|
((a, b), (c, d)) <- around (string " or ") bound bound
|
||||||
void $ string " or "
|
|
||||||
(c, d) <- (,) <$> (decimal <* string "-") <*> decimal
|
|
||||||
pure (name, \n -> (a <= n && n <= b) || (c <= n && n <= d))
|
pure (name, \n -> (a <= n && n <= b) || (c <= n && n <= d))
|
||||||
ticket = decimal `sepBy` string ","
|
ticket = (decimal `sepBy` string ",") <* newline
|
||||||
|
|
||||||
anyValid :: [(T.Text, Int -> Bool)] -> Int -> Bool
|
anyValid :: [(T.Text, Int -> Bool)] -> Int -> Bool
|
||||||
anyValid fields n = any (($ n) . snd) fields
|
anyValid fields n = any (($ n) . snd) fields
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue