packages feed

syntax-1.0.0.0: Data/Syntax/Indent.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{- |
Module      :  Data.Syntax.Indent
Description :  Simple indentation.
Copyright   :  (c) Paweł Nowak
License     :  MIT

Maintainer  :  Paweł Nowak <pawel834@gmail.com>
Stability   :  experimental

Provides a very simple indentation as a category transformer.
-}
module Data.Syntax.Indent (
    Indent,
    runIndent,
    breakLine,
    indented
    ) where

import Control.Category
import Control.Category.Reader
import Control.Category.Structures
import Control.SIArrow
import Data.Syntax
import Data.Syntax.Char
import Data.Syntax.Combinator
import Prelude hiding (takeWhile, take, id, (.))

-- | Adds indentation to a syntax description.
newtype Indent cat a b = Indent { getIndent :: ReaderCT (Int, cat () ()) cat a b }
    deriving (Category, Products, Coproducts, CatPlus, SIArrow)

instance CatTrans Indent where
    clift = Indent . clift

-- Generalized newtype deriving cannot derive this :(

instance Syntax syn => Syntax (Indent syn) where
    type Seq (Indent syn) = Seq syn
    anyChar = Indent anyChar
    char = Indent . char
    notChar = Indent . notChar
    satisfy = Indent . satisfy
    satisfyWith ai = Indent . satisfyWith ai
    string = Indent . string
    take = Indent . take
    takeWhile = Indent . takeWhile
    takeWhile1 = Indent . takeWhile1
    takeTill = Indent . takeTill
    takeTill1 = Indent . takeTill1

instance SyntaxChar syn => SyntaxChar (Indent syn) where
    decimal = Indent decimal
    hexadecimal = Indent hexadecimal
    scientific = Indent scientific
    realFloat = Indent realFloat

-- | @runIndent m tab@ runs the 'Indent' transformer using @tab@ once for each
-- level of indentation.
runIndent :: Indent cat a b -> cat () () -> cat a b
runIndent (Indent m) tab = runReaderCT m (0, tab)

-- | Inserts a new line and correct indentation, but does not 
-- require any formatting when parsing (it just skips all white space).
breakLine :: SyntaxChar syn => Indent syn () ()
breakLine = Indent . ReaderCT $ \(i, tab) -> 
    opt (char '\n') /* opt (sireplicate_ i tab) /* spaces_

-- | Increases the indentation level of its argument by one.
indented :: Indent cat a b -> Indent cat a b
indented (Indent f) = Indent . ReaderCT $ \(i, tab) -> runReaderCT f (i + 1, tab)