-- | The combinators of this library are all pairs of functions going in -- opposite directions. These pairs are called /cassettes/, sporting two tracks -- (the two functions), one of which is read in one direction, the other of -- which (accessed by flipping the cassette) is read in the opposite direction. -- -- = __Example__ -- -- Consider the data type for abstract syntax trees of the λ-calculus: -- -- >>> :{ -- type Ident = String -- data Term where -- Var :: Ident -> Term -- Abs :: Ident -> Term -> Term -- App :: Term -> Term -> Term -- deriving instance Show Term -- makePrisms ''Term -- :} -- -- Given a few constructor-wrapping prisms (generated by @makePrisms@ from the -- lens library or otherwise) and lifting them to cassette /leads/ (the -- definitions are mechanical), ... -- -- >>> :{ -- varL = prismL _Var -- absL = prismL _Abs . pairL -- appL = prismL _App . pairL -- :} -- -- ... the concrete syntax for terms of the λ-calculus can be defined -- as follows: -- -- >>> :{ -- term :: PP Term -- term = -- varL --> ident <> -- absL --> char '^' . ident . char '.' . optSpace . term <> -- appL --> parens (term . sepSpace . term) -- parens p = char '(' . p . char ')' -- ident = consL --> letter . many alphaNum -- :} -- -- From this single specification, we can extract a parser, using -- 'parse', and also a pretty printer, using 'pretty'. -- -- >>> parse term "^x. (x x)" -- Just (Abs "x" (App (Var "x") (Var "x"))) -- -- >>> pretty term (Abs "x" (App (Var "x") (Var "x"))) -- Just "^x. (x x)" -- -- = Grammar specifications -- -- Specifications are built from primitive and derived combinators, which affect -- the input string in some way. For each constructor of each datatype, we need -- to write a /lead/, which is a pair of a construction function and -- a destruction function. Leads are pure combinators that do not affect the -- input string. By convention, we suffix their name with "L". A number of leads -- for standard data types are defined in the "Text.Cassette.Lead" module. module Text.Cassette (module X) where import Prelude hiding ((.)) import Text.Cassette.Char as X import Text.Cassette.Combinator as X import Text.Cassette.Lead as X import Text.Cassette.Number as X import Text.Cassette.Prim as X -- $setup -- >>> :seti -XStandaloneDeriving -XGADTSyntax -- >>> import Control.Category -- >>> import Control.Lens.TH