{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FunctionalDependencies #-} {- | Module : Data.Syntax Description : Abstract syntax description. Copyright : (c) Paweł Nowak License : MIT Maintainer : Paweł Nowak Stability : experimental Abstract syntax descriptions based on semi-isomorphisms. -} module Data.Syntax ( -- * Syntax. Syntax(..), -- * Common isomorphisms. packed ) where import Prelude hiding (take, takeWhile) import Control.Lens import Control.Lens.SemiIso import Data.MonoTraversable import Data.SemiIsoFunctor import Data.Sequences hiding (take, takeWhile) -- | An isomorphism between a sequence and a list of its elements. packed :: IsSequence seq => Iso' seq [Element seq] packed = iso otoList fromList -- | An abstract syntax description based on semi-isomorphisms. -- -- This class can be implemented by both parsers and printers (and maybe more?). -- -- The usual use is to write a polymorphic syntax description and instantiate it -- both as a parser and a printer. An example syntax description: -- -- > | A simple untyped lambda calculus. -- > data AST = Var Text -- > | App AST AST -- > | Abs Text AST -- > deriving (Show) -- > -- > $(makePrisms ''AST) -- > -- > -- | A variable name. -- > name :: Syntax syn Text => syn Text -- > name = S.takeWhile1 isAlphaNum -- > -- > -- | Encloses a symbol in parentheses. -- > parens :: Syntax syn Text => syn a -> syn a -- > parens m = S.char '(' */ S.spaces_ */ m /* S.spaces_ /* S.char ')' -- > -- > -- | An atom is a variable or an expression in parentheses. -- > atom :: Syntax syn Text => syn AST -- > atom = _Var /$/ name -- > /|/ parens expr -- > -- > -- | An expression of our lambda calculus. -- > expr :: Syntax syn Text => syn AST -- > expr = _App /$/ atom /* S.spaces1 /*/ atom -- > /|/ _Abs /$/ S.char '\\' /* S.spaces_ -- > */ name /* S.spaces -- > /* S.string "->" /* S.spaces -- > /*/ expr -- > /|/ atom -- -- Methods of this class try to mimic "Data.Attoparsec.Text" interface. class ( SemiIsoAlternative syn , IsSequence seq , Eq seq , Eq (Element seq)) => Syntax syn seq | syn -> seq where -- | Any character. anyChar :: syn (Element seq) -- | A specific character. char :: Element seq -> syn () char c = exact c /$/ anyChar -- | Any character except the given one. notChar :: Element seq -> syn (Element seq) notChar c = filtered (/= c) /$/ anyChar -- | Any character satisfying a predicate. satisfy :: (Element seq -> Bool) -> syn (Element seq) satisfy p = filtered p /$/ anyChar -- | Transforms a character using a SemiIso and filters out values -- not satisfying the predicate. satisfyWith :: ASemiIso' a (Element seq) -> (a -> Bool) -> syn a satisfyWith ai p = filtered p . ai /$/ anyChar -- | A specific string. string :: seq -> syn () string s = exact s /$/ take (olength s) -- | A string of length @n@. take :: Int -> syn seq take n = packed /$/ sireplicate n anyChar -- | Maximal string which elements satisfy a predicate. takeWhile :: (Element seq -> Bool) -> syn seq takeWhile p = packed /$/ simany (satisfy p) -- | Maximal non-empty string which elements satisfy a predicate. takeWhile1 :: (Element seq -> Bool) -> syn seq takeWhile1 p = packed /$/ sisome (satisfy p) -- | Maximal string which elements do not satisfy a predicate. takeTill :: (Element seq -> Bool) -> syn seq takeTill p = takeWhile (not . p) -- | Maximal non-empty string which elements do not satisfy a predicate. takeTill1 :: (Element seq -> Bool) -> syn seq takeTill1 p = takeWhile1 (not . p) {-# MINIMAL anyChar #-}