{-# Language BlockArguments, TemplateHaskell, ViewPatterns #-}
module Advent.Format (format) where
import Advent.Prelude (countBy)
import Advent.Input (getRawInput)
import Advent.Format.Lexer (alexScanTokens, AlexPosn(..))
import Advent.Format.Parser (parseFormat, ParseError(..))
import Advent.Format.Types
import Control.Applicative ((<|>), some)
import Control.Monad ((<=<), void)
import Data.Char (isDigit, isSpace, isUpper, isAsciiLower, isAsciiUpper)
import Data.Maybe ( listToMaybe )
import Language.Haskell.TH
import Language.Haskell.TH.Quote (QuasiQuoter(..))
import Text.ParserCombinators.ReadP
parse :: String -> Q Format
parse :: String -> Q Format
parse String
txt =
case [(AlexPosn, Token)] -> Either ParseError Format
parseFormat (String -> [(AlexPosn, Token)]
alexScanTokens String
txt) of
Right Format
fmt -> Format -> Q Format
forall a. a -> Q a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Format
fmt
Left (Unclosed AlexPosn
p) -> AlexPosn -> String -> Q Format
forall a. AlexPosn -> String -> Q a
failAt AlexPosn
p String
"Unclosed parenthesis"
Left (UnexpectedToken AlexPosn
p Token
t) -> AlexPosn -> String -> Q Format
forall a. AlexPosn -> String -> Q a
failAt AlexPosn
p (String
"Unexpected token " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Token -> String
showToken Token
t)
Left ParseError
UnexpectedEOF -> String -> Q Format
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Format parse error, unexpected end-of-input"
failAt :: AlexPosn -> String -> Q a
failAt :: forall a. AlexPosn -> String -> Q a
failAt (AlexPn Int
_ Int
line Int
col) String
msg = String -> Q a
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Format parse error at " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
line String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
":" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
col String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
", " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
msg)
format :: QuasiQuoter
format :: QuasiQuoter
format = QuasiQuoter
{ quoteExp :: String -> Q Exp
quoteExp = (Maybe (Int, Int) -> String -> Q Exp)
-> (Maybe (Int, Int), String) -> Q Exp
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Maybe (Int, Int) -> String -> Q Exp
makeParser ((Maybe (Int, Int), String) -> Q Exp)
-> (String -> Q (Maybe (Int, Int), String)) -> String -> Q Exp
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< String -> Q (Maybe (Int, Int), String)
prepare
, quotePat :: String -> Q Pat
quotePat = \String
_ -> String -> Q Pat
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"format: patterns not supported"
, quoteType :: String -> Q Type
quoteType = Format -> Q Type
toType (Format -> Q Type) -> (String -> Q Format) -> String -> Q Type
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< String -> Q Format
parse (String -> Q Format)
-> ((Maybe (Int, Int), String) -> String)
-> (Maybe (Int, Int), String)
-> Q Format
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe (Int, Int), String) -> String
forall a b. (a, b) -> b
snd ((Maybe (Int, Int), String) -> Q Format)
-> (String -> Q (Maybe (Int, Int), String)) -> String -> Q Format
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< String -> Q (Maybe (Int, Int), String)
prepare
, quoteDec :: String -> Q [Dec]
quoteDec = \String
_ -> String -> Q [Dec]
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"format: declarations not supported"
}
prepare :: String -> Q (Maybe (Int, Int), String)
prepare :: String -> Q (Maybe (Int, Int), String)
prepare String
str =
case String -> [String]
lines ((Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
filter (Char
'\r' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/=) String
str) of
[] -> String -> Q (Maybe (Int, Int), String)
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Empty input format"
[String
x]
| Just (Maybe (Int, Int)
yd, String
str') <- String -> Maybe (Maybe (Int, Int), String)
splitLeader String
x -> (Maybe (Int, Int), String) -> Q (Maybe (Int, Int), String)
forall a. a -> Q a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe (Int, Int)
yd, String
str')
| Bool
otherwise -> String -> Q (Maybe (Int, Int), String)
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Failed to parse single-line input pattern"
String
x:[String]
xs
| Just (Maybe (Int, Int)
yd, String
"") <- String -> Maybe (Maybe (Int, Int), String)
splitLeader String
x ->
(Maybe (Int, Int), String) -> Q (Maybe (Int, Int), String)
forall a. a -> Q a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe (Int, Int)
yd, (String -> String) -> [String] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Int -> String -> String
forall a. Int -> [a] -> [a]
drop Int
indent) [String]
xs1)
where
xs1 :: [String]
xs1 = (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Char
' ' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/=)) [String]
xs
indent :: Int
indent = [Int] -> Int
forall a. Ord a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
minimum ((String -> Int) -> [String] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (String -> Int) -> (String -> String) -> String -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Char
' 'Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==)) [String]
xs1)
[String]
_ -> String -> Q (Maybe (Int, Int), String)
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Failed to parse multi-line input pattern"
splitLeader :: String -> Maybe (Maybe (Int, Int), String)
splitLeader :: String -> Maybe (Maybe (Int, Int), String)
splitLeader (ReadS Int
forall a. Read a => ReadS a
reads -> [(Int
y,ReadS Int
forall a. Read a => ReadS a
reads -> [(Int
d, String
rest)])]) = (Maybe (Int, Int), String) -> Maybe (Maybe (Int, Int), String)
forall a. a -> Maybe a
Just ((Int, Int) -> Maybe (Int, Int)
forall a. a -> Maybe a
Just (Int
y, Int
d), (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Char
' 'Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==) String
rest)
splitLeader (ReadS String
lex -> [(String
"-", String
rest)]) = (Maybe (Int, Int), String) -> Maybe (Maybe (Int, Int), String)
forall a. a -> Maybe a
Just (Maybe (Int, Int)
forall a. Maybe a
Nothing, (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Char
' 'Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==) String
rest)
splitLeader String
_ = Maybe (Maybe (Int, Int), String)
forall a. Maybe a
Nothing
makeParser :: Maybe (Int, Int) -> String -> ExpQ
makeParser :: Maybe (Int, Int) -> String -> Q Exp
makeParser Maybe (Int, Int)
mb String
str =
do Format
fmt <- String -> Q Format
parse String
str
let formats :: Q Exp
formats = [| readP_to_S ($(Format -> Q Exp
toReadP Format
fmt) <* eof) |]
let qf :: Q Exp
qf = [| maybe (error "bad input parse") fst . listToMaybe . $Q Exp
formats |]
case Maybe (Int, Int)
mb of
Maybe (Int, Int)
Nothing -> Q Exp
qf
Just (Int
y,Int
d) -> [| $Q Exp
qf <$> getRawInput y d |]
toReadP :: Format -> ExpQ
toReadP :: Format -> Q Exp
toReadP Format
s =
case Format
s of
Literal String
xs -> [| void (string xs) |]
Gather Format
p -> [| fst <$> gather $(Format -> Q Exp
toReadP Format
p) |]
Named String
n
| Char -> Bool
isUpper (String -> Char
forall a. HasCallStack => [a] -> a
head String
n) -> String -> Q Exp
enumParser String
n
| Bool
otherwise -> Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
varE (String -> Name
mkName String
n)
Format
UnsignedInteger -> [| (read :: String -> Integer) <$> munch1 isDigit |]
Format
SignedInteger -> [| (read :: String -> Integer) <$> ((++) <$> option "" (string "-") <*> munch1 isDigit) |]
Format
UnsignedInt -> [| (read :: String -> Int ) <$> munch1 isDigit |]
Format
SignedInt -> [| (read :: String -> Int ) <$> ((++) <$> option "" (string "-") <*> munch1 isDigit) |]
Format
Char -> [| satisfy ('\n' /=) |]
Format
Letter -> [| satisfy (\x -> isAsciiLower x || isAsciiUpper x) |]
Format
Word -> [| some (satisfy (not . isSpace)) |]
Many Format
x ->
do Q Bool -> Q () -> Q ()
forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
whenM (Format -> Q Bool
acceptsEmpty Format
x) (String -> Q ()
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Argument to * accepts ε: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> Format -> String -> String
showFormat Int
0 Format
s String
""))
if Format -> Bool
interesting Format
x then
[| many $(Format -> Q Exp
toReadP Format
x) |]
else
[| void (many $(Format -> Q Exp
toReadP Format
x)) |]
Some Format
x ->
do Q Bool -> Q () -> Q ()
forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
whenM (Format -> Q Bool
acceptsEmpty Format
x) (String -> Q ()
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Argument to + accepts ε: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> Format -> String -> String
showFormat Int
0 Format
s String
""))
if Format -> Bool
interesting Format
x then
[| some $(Format -> Q Exp
toReadP Format
x) |]
else
[| void (some $(Format -> Q Exp
toReadP Format
x)) |]
SepBy Format
x Format
y ->
do Q Bool -> Q () -> Q ()
forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
whenM (Q Bool -> Q Bool -> Q Bool
forall (m :: * -> *). Monad m => m Bool -> m Bool -> m Bool
andM (Format -> Q Bool
acceptsEmpty Format
x) (Format -> Q Bool
acceptsEmpty Format
y)) (String -> Q ()
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Both arguments to & accept ε: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> Format -> String -> String
showFormat Int
0 Format
s String
""))
if Format -> Bool
interesting Format
x then
[| sepBy $(Format -> Q Exp
toReadP Format
x) $(Format -> Q Exp
toReadP Format
y) |]
else
[| void (sepBy $(Format -> Q Exp
toReadP Format
x) $(Format -> Q Exp
toReadP Format
y)) |]
Alt Format
x Format
y
| Bool
xi, Bool
yi -> [| Left <$> $Q Exp
xp <|> Right <$> $Q Exp
yp |]
| Bool
xi -> [| Just <$> $Q Exp
xp <|> Nothing <$ $Q Exp
yp |]
| Bool
yi -> [| Nothing <$ $Q Exp
xp <|> Just <$> $Q Exp
yp |]
| Bool
otherwise -> [| $Q Exp
xp <|> $Q Exp
yp |]
where
xi :: Bool
xi = Format -> Bool
interesting Format
x
yi :: Bool
yi = Format -> Bool
interesting Format
y
xp :: Q Exp
xp = Format -> Q Exp
toReadP Format
x
yp :: Q Exp
yp = Format -> Q Exp
toReadP Format
y
Group Format
x -> Format -> Q Exp
toReadP Format
x
Format
_ ->
case [(Format -> Bool
interesting Format
x, Format -> Q Exp
toReadP Format
x) | Format
x <- Format -> [Format] -> [Format]
follows Format
s []] of
[] -> [| pure () |]
xxs :: [(Bool, Q Exp)]
xxs@((Bool
ix,Q Exp
x):[(Bool, Q Exp)]
xs)
| Int
n Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 -> (Q Exp -> (Bool, Q Exp) -> Q Exp)
-> Q Exp -> [(Bool, Q Exp)] -> Q Exp
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl Q Exp -> (Bool, Q Exp) -> Q Exp
forall {m :: * -> *} {a}. Quote m => m Exp -> (a, m Exp) -> m Exp
apply0 Q Exp
x [(Bool, Q Exp)]
xs
| Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
1 -> (Q Exp -> (Bool, Q Exp) -> Q Exp)
-> Q Exp -> [(Bool, Q Exp)] -> Q Exp
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl Q Exp -> (Bool, Q Exp) -> Q Exp
forall {m :: * -> *}. Quote m => m Exp -> (Bool, m Exp) -> m Exp
apply1 Q Exp
x [(Bool, Q Exp)]
xs
| Bool
ix -> (Q Exp -> (Bool, Q Exp) -> Q Exp)
-> Q Exp -> [(Bool, Q Exp)] -> Q Exp
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl Q Exp -> (Bool, Q Exp) -> Q Exp
forall {m :: * -> *}. Quote m => m Exp -> (Bool, m Exp) -> m Exp
applyN [| $Q Exp
tup <$> $Q Exp
x |] [(Bool, Q Exp)]
xs
| Bool
otherwise -> (Q Exp -> (Bool, Q Exp) -> Q Exp)
-> Q Exp -> [(Bool, Q Exp)] -> Q Exp
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl Q Exp -> (Bool, Q Exp) -> Q Exp
forall {m :: * -> *}. Quote m => m Exp -> (Bool, m Exp) -> m Exp
applyN [| $Q Exp
tup <$ $Q Exp
x |] [(Bool, Q Exp)]
xs
where
tup :: Q Exp
tup = Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
conE (Int -> Name
tupleDataName Int
n)
n :: Int
n = ((Bool, Q Exp) -> Bool) -> [(Bool, Q Exp)] -> Int
forall (f :: * -> *) a. Foldable f => (a -> Bool) -> f a -> Int
countBy (Bool, Q Exp) -> Bool
forall a b. (a, b) -> a
fst [(Bool, Q Exp)]
xxs
apply0 :: m Exp -> (a, m Exp) -> m Exp
apply0 m Exp
l (a
_,m Exp
r) = [| $m Exp
l *> $m Exp
r |]
apply1 :: m Exp -> (Bool, m Exp) -> m Exp
apply1 m Exp
l (Bool
i,m Exp
r) = if Bool
i then [| $m Exp
l *> $m Exp
r |] else [| $m Exp
l <* $m Exp
r |]
applyN :: m Exp -> (Bool, m Exp) -> m Exp
applyN m Exp
l (Bool
i,m Exp
r) = if Bool
i then [| $m Exp
l <*> $m Exp
r |] else [| $m Exp
l <* $m Exp
r |]
toType :: Format -> TypeQ
toType :: Format -> Q Type
toType Format
fmt =
case Format
fmt of
Literal String
_ -> [t| () |]
Gather Format
_ -> [t| String |]
Named String
n
| Char -> Bool
isUpper (String -> Char
forall a. HasCallStack => [a] -> a
head String
n) -> Name -> Q Type
forall (m :: * -> *). Quote m => Name -> m Type
conT (String -> Name
mkName String
n)
| Bool
otherwise -> String -> Q Type
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"toType: not implemented for variable yet"
Format
UnsignedInteger -> [t| Integer |]
Format
SignedInteger -> [t| Integer |]
Format
UnsignedInt -> [t| Int |]
Format
SignedInt -> [t| Int |]
Format
Char -> [t| Char |]
Format
Letter -> [t| Char |]
Format
Word -> [t| String |]
Many Format
x ->
do Q Bool -> Q () -> Q ()
forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
whenM (Format -> Q Bool
acceptsEmpty Format
x) (String -> Q ()
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Argument to * accepts ε: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> Format -> String -> String
showFormat Int
0 Format
fmt String
""))
if Format -> Bool
interesting Format
x then
[t| [$(Format -> Q Type
toType Format
x)] |]
else
[t| () |]
Some Format
x ->
do Q Bool -> Q () -> Q ()
forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
whenM (Format -> Q Bool
acceptsEmpty Format
x) (String -> Q ()
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Argument to + accepts ε: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> Format -> String -> String
showFormat Int
0 Format
fmt String
""))
if Format -> Bool
interesting Format
x then
[t| [$(Format -> Q Type
toType Format
x)] |]
else
[t| () |]
SepBy Format
x Format
y ->
do Q Bool -> Q () -> Q ()
forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
whenM (Q Bool -> Q Bool -> Q Bool
forall (m :: * -> *). Monad m => m Bool -> m Bool -> m Bool
andM (Format -> Q Bool
acceptsEmpty Format
x) (Format -> Q Bool
acceptsEmpty Format
y)) (String -> Q ()
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Both arguments to & accept ε: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> Format -> String -> String
showFormat Int
0 Format
fmt String
""))
if Format -> Bool
interesting Format
x then
[t| [$(Format -> Q Type
toType Format
x)] |]
else
[t| () |]
Alt Format
x Format
y
| Bool
xi, Bool
yi -> [t| Either $Q Type
xt $Q Type
yt |]
| Bool
xi -> [t| Maybe $Q Type
xt |]
| Bool
yi -> [t| Maybe $Q Type
yt |]
| Bool
otherwise -> [t| () |]
where
xi :: Bool
xi = Format -> Bool
interesting Format
x
yi :: Bool
yi = Format -> Bool
interesting Format
y
xt :: Q Type
xt = Format -> Q Type
toType Format
x
yt :: Q Type
yt = Format -> Q Type
toType Format
y
Group Format
x -> Format -> Q Type
toType Format
x
Format
_ ->
case [Format -> Q Type
toType Format
x | Format
x <- Format -> [Format] -> [Format]
follows Format
fmt [], Format -> Bool
interesting Format
x] of
[] -> [t| () |]
[Q Type
t] -> Q Type
t
[Q Type]
ts -> (Q Type -> Q Type -> Q Type) -> Q Type -> [Q Type] -> Q Type
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl Q Type -> Q Type -> Q Type
forall (m :: * -> *). Quote m => m Type -> m Type -> m Type
appT (Int -> Q Type
forall (m :: * -> *). Quote m => Int -> m Type
tupleT ([Q Type] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Q Type]
ts)) [Q Type]
ts
follows :: Format -> [Format] -> [Format]
follows :: Format -> [Format] -> [Format]
follows (Follow Format
x Format
y) [Format]
zs = Format -> [Format] -> [Format]
follows Format
x (Format -> [Format] -> [Format]
follows Format
y [Format]
zs)
follows Format
Empty [Format]
zs = [Format]
zs
follows (Literal String
x) (Literal String
y : [Format]
zs) = Format -> [Format] -> [Format]
follows (String -> Format
Literal (String
x String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
y)) [Format]
zs
follows Format
x [Format]
zs = Format
x Format -> [Format] -> [Format]
forall a. a -> [a] -> [a]
: [Format]
zs
enumParser :: String -> ExpQ
enumParser :: String -> Q Exp
enumParser String
nameStr =
do [(Name, String)]
entries <- String -> Q [(Name, String)]
enumCases String
nameStr
let parsers :: [Q Exp]
parsers = [[| $(Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
conE Name
name) <$ string str |] | (Name
name, String
str) <- [(Name, String)]
entries]
[| choice $([Q Exp] -> Q Exp
forall (m :: * -> *). Quote m => [m Exp] -> m Exp
listE [Q Exp]
parsers) |]