on b6dbe1a help sign in
syntax/Data/Syntax.hs
157 lines 5.8 kB history raw
1{-# LANGUAGE TypeFamilies #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE DefaultSignatures #-}
4{- |
5Module : Data.Syntax
6Description : Abstract syntax description.
7Copyright : (c) Paweł Nowak
8License : MIT
10Maintainer : Paweł Nowak <pawel834@gmail.com>
11Stability : experimental
13Reversible parsing and pretty-printing.
14-}
15module Data.Syntax (
16 -- * Syntax.
17 Syntax(..),
18 Isolable(..),
19 -- * Common isomorphisms.
20 packed
21 ) where
23import Prelude hiding (take, takeWhile, id, (.))
25import Control.Category
26import Control.Category.Reader
27import Control.Category.Structures
28import Control.Lens.Iso
29import Control.Lens.SemiIso
30import Control.SIArrow
31import Data.MonoTraversable
32import Data.Sequences hiding (take, takeWhile, replicate)
33import Data.Vector (Vector)
35-- | An isomorphism between a sequence and a list of its elements.
36packed :: IsSequence seq => Iso' seq [Element seq]
37packed = iso otoList fromList
39-- | An abstract syntax description based on semi-isomorphisms.
40--
41-- This class can be implemented by both parsers and printers.
42--
43-- The usual use is to write a polymorphic syntax description and instantiate it
44-- both as a parser and a printer. Examples are available in 'syntax-example' and
45-- 'syntax-example-json' packages.
46--
47-- Methods of this class try to mimic "Data.Attoparsec.Text" interface.
48class ( SIArrow syn
49 , IsSequence (Seq syn)
50 , Eq (Seq syn)
51 , Eq (Element (Seq syn)))
52 => Syntax syn
53 where
54 -- | The sequence type used by this syntax.
55 type Seq syn :: *
57 -- | Any character.
58 anyChar :: syn () (Element (Seq syn))
60 -- | A specific character.
61 char :: Element (Seq syn) -> syn () ()
62 char c = rev (exact c) /$/ anyChar
64 -- | Any character except the given one.
65 notChar :: Element (Seq syn) -> syn () (Element (Seq syn))
66 notChar c = bifiltered (/= c) /$/ anyChar
68 -- | Any character satisfying a predicate.
69 satisfy :: (Element (Seq syn) -> Bool) -> syn () (Element (Seq syn))
70 satisfy p = bifiltered p /$/ anyChar
72 -- | Transforms a character using a SemiIso and filters out values
73 -- not satisfying the predicate.
74 satisfyWith :: ASemiIso' a (Element (Seq syn)) -> (a -> Bool) -> syn () a
75 satisfyWith ai p = bifiltered p . ai /$/ anyChar
77 -- | A specific string.
78 string :: Seq syn -> syn () ()
79 string s = rev (exact s) /$/ take (olength s)
81 -- | A string of length @n@.
82 take :: Int -> syn () (Seq syn)
83 take n = packed /$/ sireplicate n anyChar
85 -- | Maximal string which elements satisfy a predicate.
86 takeWhile :: (Element (Seq syn) -> Bool) -> syn () (Seq syn)
87 takeWhile p = packed /$/ simany (satisfy p)
89 -- | Maximal non-empty string which elements satisfy a predicate.
90 takeWhile1 :: (Element (Seq syn) -> Bool) -> syn () (Seq syn)
91 takeWhile1 p = packed /$/ sisome (satisfy p)
93 -- | Maximal string which elements do not satisfy a predicate.
94 takeTill :: (Element (Seq syn) -> Bool) -> syn () (Seq syn)
95 takeTill p = takeWhile (not . p)
97 -- | Maximal non-empty string which elements do not satisfy a predicate.
98 takeTill1 :: (Element (Seq syn) -> Bool) -> syn () (Seq syn)
99 takeTill1 p = takeWhile1 (not . p)
101 -- | Constant size vector. The default implementation uses lists, but
102 -- "syntax-attoparsec" and "syntax-printer" override it with an efficient
103 -- implementation that works on a vector directly.
104 --
105 -- @vecN n e@ describes a size @n@ vector with elements @e@.
106 --
107 -- Also see 'Data.Syntax.Combinator.vec'.
108 vecN :: Int -> syn () a -> syn () (Vector a)
109 vecN n e = packed /$/ sireplicate n e
111 -- | Constant size vector with index-aware element. The default implementation
112 -- uses lists, but "syntax-attoparsec" and "syntax-printer" override it with an
113 -- efficient implementation that works on a vector directly.
114 --
115 -- @ivecN n e@ describes a size @n@ vector with elements @e@. Each element
116 -- gets its index and should output a value and the index unchanged.
117 --
118 -- Also see 'Data.Syntax.Combinator.ivec'.
119 ivecN :: Int -> syn Int (Int, a) -> syn () (Vector a)
120 ivecN n e = (packed /$/)
121 $ sisequence
122 $ map (\(i, e') -> constant i ^>> e'
123 >>> first (sipure (constant i))
124 >># unit . swapped)
125 $ zip [0 .. n-1] (replicate n e)
127 {-# MINIMAL anyChar #-}
129instance Syntax syn => Syntax (ReaderCT env syn) where
130 type Seq (ReaderCT env syn) = Seq syn
131 anyChar = clift anyChar
132 char = clift . char
133 notChar = clift . notChar
134 satisfy = clift . satisfy
135 satisfyWith ai = clift . satisfyWith ai
136 string = clift . string
137 take = clift . take
138 takeWhile = clift . takeWhile
139 takeWhile1 = clift . takeWhile1
140 takeTill = clift . takeTill
141 takeTill1 = clift . takeTill1
143-- | Execute a computation in an isolated context.
144--
145-- The motivating example: you want to write a function
146--
147-- > serializeList :: Syntax syn => syn () a -> syn () [a]
148--
149-- Notice that we cannot just use simany, because the first @syn () a@
150-- could eat the entire sequence (even though we printed more than 1 value!), so
151-- we have to insert some kind of separators between the element. But how can we
152-- be sure that @syn () a@ will not eat our separators? We can't! Thats why we
153-- have to do the parsing in two stages: first extract the sequence between separators,
154-- then run the @syn () a@ on this sequence.
155class Syntax syn => Isolable syn where
156 -- | Turns a computation into one executed in an isolated context.
157 isolate :: syn () b -> syn (Seq syn) b