hakyllbars-1.0.0.0: src/Hakyllbars/Ast.hs
module Hakyllbars.Ast
( Template (..),
Block (..),
getBlockName,
getBlockPos,
ApplyBlock (..),
DefaultBlock (..),
Expression (..),
getExpressionPos,
)
where
import Data.Binary
import qualified Data.ByteString.Char8 as Char8
import Data.List.NonEmpty (NonEmpty)
import Data.Yaml (ToJSON (..), object, (.=))
import Data.Yaml.Pretty (defConfig, encodePretty)
import GHC.Generics (Generic)
import Hakyllbars.Common
import Text.Parsec hiding (getPosition)
import Text.Parsec.Pos
showJSON :: (ToJSON a) => a -> String
showJSON = Char8.unpack . encodePretty defConfig . toJSON
data Template = Template [Block] FilePath
deriving (Generic)
instance Binary Template where
get = Template <$> get <*> get
put (Template blocks pos) = do
put blocks
put pos
instance Writable Template where
write _ _ = return ()
instance ToJSON Template
instance Show Template where
show = showJSON
data Block
= TextBlock String SourcePos
| ExpressionBlock Expression SourcePos
| CommentBlock String SourcePos
| ChromeBlock Expression [Block] SourcePos
| AltBlock
(NonEmpty ApplyBlock)
(Maybe DefaultBlock)
SourcePos
deriving (Generic)
getBlockName :: Block -> String
getBlockName = \case
TextBlock {} -> "TextBlock"
ExpressionBlock {} -> "ExpressionBlock"
CommentBlock {} -> "CommentBlock"
ChromeBlock {} -> "ChromeBlock"
AltBlock {} -> "AltBlock"
getBlockPos :: Block -> SourcePos
getBlockPos = \case
TextBlock _ pos -> pos
ExpressionBlock _ pos -> pos
CommentBlock _ pos -> pos
ChromeBlock _ _ pos -> pos
AltBlock _ _ pos -> pos
getBlockTag :: Block -> Int
getBlockTag = \case
TextBlock {} -> 1
ExpressionBlock {} -> 2
CommentBlock {} -> 3
ChromeBlock {} -> 4
AltBlock {} -> 5
instance Binary Block where
get = do
tag <- get :: Get Int
f <- case tag of
1 -> TextBlock <$> get
2 -> ExpressionBlock <$> get
3 -> CommentBlock <$> get
4 -> ChromeBlock <$> get <*> get
5 -> AltBlock <$> get <*> get
_ -> error $ "Unrecognized block tag " ++ show tag
binaryPos <- get :: Get BinaryPos
return $ f (unBinaryPos binaryPos)
put block = do
put $ getBlockTag block
case block of
TextBlock text _ -> put text
ExpressionBlock expression _ -> put expression
CommentBlock text _ -> put text
ChromeBlock expression blocks _ -> put expression >> put blocks
AltBlock blocks default' _ -> put blocks >> put default'
put $ BinaryPos (getBlockPos block)
instance ToJSON Block where
toJSON = \case
TextBlock text pos ->
object
[ "type" .= ("TextBlock" :: String),
"text" .= text,
"pos" .= show pos
]
ExpressionBlock expression pos ->
object
[ "type" .= ("ExpressionBlock" :: String),
"expression" .= expression,
"pos" .= show pos
]
CommentBlock text pos ->
object
[ "type" .= ("CommentBlock" :: String),
"text" .= text,
"pos" .= show pos
]
ChromeBlock expression blocks _ ->
object
[ "type" .= ("ChromeBlock" :: String),
"expression" .= expression,
"blocks" .= blocks
]
AltBlock blocks default' _ ->
object
[ "type" .= ("AltBlock" :: String),
"blocks" .= blocks,
"default" .= default'
]
instance Show Block where
show = showJSON
newtype BinaryPos = BinaryPos SourcePos
deriving (Show)
unBinaryPos :: BinaryPos -> SourcePos
unBinaryPos (BinaryPos pos) = pos
instance Binary BinaryPos where
get = BinaryPos <$> (newPos <$> get <*> get <*> get)
put binaryPos = do
let pos = unBinaryPos binaryPos
put $ sourceName pos
put $ sourceLine pos
put $ sourceColumn pos
data ApplyBlock = ApplyBlock Expression [Block] SourcePos
deriving (Generic)
instance Binary ApplyBlock where
get = do
f <- ApplyBlock <$> get <*> get
binaryPos <- get :: Get BinaryPos
return $ f (unBinaryPos binaryPos)
put (ApplyBlock expression blocks pos) = do
put expression
put blocks
put $ BinaryPos pos
instance ToJSON ApplyBlock where
toJSON (ApplyBlock expression blocks pos) =
object
[ "type" .= ("ApplyBlock" :: String),
"expression" .= expression,
"blocks" .= blocks,
"pos" .= show pos
]
instance Show ApplyBlock where
show = showJSON
data DefaultBlock = DefaultBlock [Block] SourcePos
deriving (Generic)
instance Binary DefaultBlock where
get = do
f <- DefaultBlock <$> get
binaryPos <- get :: Get BinaryPos
return $ f (unBinaryPos binaryPos)
put (DefaultBlock blocks pos) = do
put blocks
put $ BinaryPos pos
instance ToJSON DefaultBlock where
toJSON (DefaultBlock blocks pos) =
object
[ "type" .= ("DefaultBlock" :: String),
"blocks" .= blocks,
"pos" .= show pos
]
instance Show DefaultBlock where
show = showJSON
data Expression
= NameExpression String SourcePos -- name, id
| StringExpression String SourcePos -- "a string of text"
| IntExpression Int SourcePos -- 123, 456, 2
| DoubleExpression Double SourcePos -- 0.2, 1.45
| BoolExpression Bool SourcePos -- true, false
| ApplyExpression Expression Expression SourcePos -- fn arg
| AccessExpression Expression Expression SourcePos -- target.field
| FilterExpression Expression Expression SourcePos -- arg | fn
| ContextExpression [(String, Expression)] SourcePos -- { name0: value0, name1: value1 }
| ListExpression [Expression] SourcePos -- [a, b, c]
deriving (Generic)
getExpressionPos :: Expression -> SourcePos
getExpressionPos = \case
NameExpression _ pos -> pos
StringExpression _ pos -> pos
IntExpression _ pos -> pos
DoubleExpression _ pos -> pos
BoolExpression _ pos -> pos
ApplyExpression _ _ pos -> pos
AccessExpression _ _ pos -> pos
FilterExpression _ _ pos -> pos
ContextExpression _ pos -> pos
ListExpression _ pos -> pos
getExpressionTag :: Expression -> Int
getExpressionTag = \case
NameExpression {} -> 1
StringExpression {} -> 2
IntExpression {} -> 3
DoubleExpression {} -> 4
BoolExpression {} -> 5
ApplyExpression {} -> 6
AccessExpression {} -> 7
FilterExpression {} -> 8
ContextExpression {} -> 9
ListExpression {} -> 10
instance Binary Expression where
get = do
tag <- get :: Get Int
f <- case tag of
1 -> NameExpression <$> get
2 -> StringExpression <$> get
3 -> IntExpression <$> get
4 -> DoubleExpression <$> get
5 -> BoolExpression <$> get
6 -> ApplyExpression <$> get <*> get
7 -> AccessExpression <$> get <*> get
8 -> FilterExpression <$> get <*> get
9 -> ContextExpression <$> get
10 -> ListExpression <$> get
_ -> error $ "Unrecognized expression tag " ++ show tag
binaryPos <- get :: Get BinaryPos
return $ f (unBinaryPos binaryPos)
put expression = do
put $ getExpressionTag expression
case expression of
NameExpression value _ -> put value
StringExpression value _ -> put value
IntExpression value _ -> put value
DoubleExpression value _ -> put value
BoolExpression value _ -> put value
ApplyExpression fn arg _ -> put fn >> put arg
AccessExpression target field _ -> put target >> put field
FilterExpression arg fn _ -> put arg >> put fn
ContextExpression pairs _ -> put pairs
ListExpression values _ -> put values
put $ BinaryPos (getExpressionPos expression)
instance ToJSON Expression where
toJSON = \case
NameExpression value pos ->
object
[ "type" .= ("NameExpression" :: String),
"value" .= value,
"pos" .= show pos
]
StringExpression value pos ->
object
[ "type" .= ("StringExpression" :: String),
"value" .= value,
"pos" .= show pos
]
IntExpression value pos ->
object
[ "type" .= ("IntExpression" :: String),
"value" .= value,
"pos" .= show pos
]
DoubleExpression value pos ->
object
[ "type" .= ("DoubleExpression" :: String),
"value" .= value,
"pos" .= show pos
]
BoolExpression value pos ->
object
[ "type" .= ("BoolExpression" :: String),
"value" .= value,
"pos" .= show pos
]
ApplyExpression fn arg pos ->
object
[ "type" .= ("ApplyExpression" :: String),
"fn" .= fn,
"arg" .= arg,
"pos" .= show pos
]
AccessExpression target field pos ->
object
[ "type" .= ("AccessExpression" :: String),
"target" .= target,
"field" .= field,
"pos" .= show pos
]
FilterExpression arg fn pos ->
object
[ "type" .= ("FilterExpression" :: String),
"arg" .= arg,
"fn" .= fn,
"pos" .= show pos
]
ContextExpression pairs pos ->
object
[ "type" .= ("ContextExpression" :: String),
"pairs" .= pairs,
"pos" .= show pos
]
ListExpression values pos ->
object
[ "type" .= ("ListExpression" :: String),
"values" .= values,
"pos" .= show pos
]
instance Show Expression where
show = showJSON