syntax-0.3.0.0: Data/Syntax/Indent.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{- |
Module : Data.Syntax.Indent
Description : Indentation.
Copyright : (c) Paweł Nowak
License : MIT
Maintainer : Paweł Nowak <pawel834@gmail.com>
Stability : experimental
Provides a very simple indentation as a \"monad\" transformer.
-}
module Data.Syntax.Indent (
Indent,
runIndent,
breakLine,
indented
) where
import Data.SemiIsoFunctor
import Data.Syntax
import Data.Syntax.Char
import Data.Syntax.Combinator
import Prelude hiding (takeWhile, take)
-- | Adds indentation to a syntax description.
newtype Indent m a = Indent { unIndent :: (Int, m ()) -> m a }
instance SemiIsoFunctor m => SemiIsoFunctor (Indent m) where
simap f (Indent g) = Indent $ \i -> simap f (g i)
instance SemiIsoApply m => SemiIsoApply (Indent m) where
sipure ai = Indent $ \_ -> sipure ai
Indent f /*/ Indent g = Indent $ \i -> f i /*/ g i
instance SemiIsoAlternative m => SemiIsoAlternative (Indent m) where
siempty = Indent $ \_ -> siempty
Indent f /|/ Indent g = Indent $ \i -> f i /|/ g i
instance SemiIsoMonad m => SemiIsoMonad (Indent m) where
(Indent m) //= f = Indent $ \i -> m i //= (\x -> unIndent (f x) i)
instance SemiIsoFix m => SemiIsoFix (Indent m) where
sifix f = Indent $ \i -> sifix $ \y -> unIndent (f y) i
instance Syntax syn seq => Syntax (Indent syn) seq where
anyChar = Indent $ const anyChar
char = Indent . const . char
notChar = Indent . const . notChar
satisfy = Indent . const . satisfy
satisfyWith ai = Indent . const . satisfyWith ai
string = Indent . const . string
take = Indent . const . take
takeWhile = Indent . const . takeWhile
takeWhile1 = Indent . const . takeWhile1
takeTill = Indent . const . takeTill
takeTill1 = Indent . const . takeTill1
instance SyntaxChar syn seq => SyntaxChar (Indent syn) seq where
decimal = Indent $ const decimal
scientific = Indent $ const scientific
-- | @runIndent m tab@ runs the 'Indent' transformer using @tab@ once for each
-- level of indentation.
runIndent :: Indent m a -> m () -> m a
runIndent = ($ 0) . curry . unIndent
-- | Inserts a new line and correct indentation, but does not
-- require any formatting when parsing (it just skips all white space).
breakLine :: SyntaxChar syn seq => Indent syn ()
breakLine = Indent $ \(i, tab) -> opt (char '\n') /* opt (sireplicate_ i tab) /* spaces_
-- | Increases the indentation level of its argument by one.
indented :: Indent m a -> Indent m a
indented (Indent f) = Indent $ \(i, tab) -> f (i + 1, tab)