|
| 1 | +-- | The combinators of this library are all pairs of functions going in |
| 2 | +-- opposite directions. These pairs are called /cassettes/, sporting two |
| 3 | +-- tracks (the two functions), one of which is read is one direction, the |
| 4 | +-- other of which (accessed by flipping the cassette) is read in the opossite |
| 5 | +-- direction. |
| 6 | +-- |
| 7 | +-- Here is an example specification for the lambda-calculus: |
| 8 | +-- |
| 9 | +-- > varL = K7 leadout leadin where |
| 10 | +-- > leadout k k' s x = k (\ s _ -> k' s x) s (Var x) |
| 11 | +-- > leadin k k' s t@(Var x) = k (\ s _ -> k' s t) s x |
| 12 | +-- > leadin k k' s t = k' s t |
| 13 | +-- > |
| 14 | +-- > absL = K7 leadout leadin where |
| 15 | +-- > leadout k k' s t' x = k (\ s _ -> k' s t' x) s (Lam x t') |
| 16 | +-- > leadin k k' s t@(Lam x t) = k (\ s _ _ -> k' s t) s t x |
| 17 | +-- > leadin k k' s t = k' s t |
| 18 | +-- > |
| 19 | +-- > appL = K7 leadout leadin where |
| 20 | +-- > leadout k s t2 t1 = k (\ s _ -> k' s t2 t1) s (App t1 t2) |
| 21 | +-- > leadin k k' s t@(App t1 t2) = k (\ s _ _ -> k' s t) s t2 t1 |
| 22 | +-- > leadin k k' s t = k' s t |
| 23 | +-- > |
| 24 | +-- > parens p = char '(' <> p <> char ')' |
| 25 | +-- > |
| 26 | +-- > term :: PP Term |
| 27 | +-- > term = varL --> ident |
| 28 | +-- > <|> absL --> char '\' <> ident <> term |
| 29 | +-- > <|> appL --> parens (term <> sepSpace <> term) |
| 30 | +-- |
| 31 | +-- From this single specification, we can extract a parser, |
| 32 | +-- |
| 33 | +-- > parse term :: PP Term -> String -> Maybe Term |
| 34 | +-- |
| 35 | +-- and also a pretty printer, |
| 36 | +-- |
| 37 | +-- > pretty term :: PP Term -> Term -> Maybe String |
| 38 | +-- |
| 39 | +-- Specifications are built from primitive and derived combinators, which |
| 40 | +-- affect the input string in some way. For each constructor of each datatype, |
| 41 | +-- we need to write a /lead/, which is a pair of a construction function and a |
| 42 | +-- destruction function. Leads are pure combinators that do not affect the |
| 43 | +-- input string. By convention, we suffix their name with "L". |
| 44 | +-- |
| 45 | +-- Internally, the primitive combinators are written in CPS. Leads also need |
| 46 | +-- to be written in this style, being primitive. They can, however, be |
| 47 | +-- automatically generated for every datatype using some Template Haskell |
| 48 | +-- hackery (in a separate package). A number of leads for standard datatypes |
| 49 | +-- are defined in the 'Text.Cassette.Lead' module. |
| 50 | + |
1 | 51 | module Text.Cassette |
2 | 52 | ( module Text.Cassette.Prim |
3 | 53 | , module Text.Cassette.Lead |
|
0 commit comments