25 | 25 |
import Data.SCargot.Repr as R
|
26 | 26 |
|
27 | 27 |
-- | A traversal with access to the first element of a pair.
|
| 28 |
--
|
| 29 |
-- >>> import Lens.Family
|
| 30 |
-- >>> set _car (A "elephant") (A "one" ::: A "two" ::: A "three" ::: Nil)
|
| 31 |
-- A "elelphant" ::: A "two" ::: A "three" ::: Nil
|
| 32 |
-- >>> set _car (A "two" ::: A "three" ::: Nil) (A "one" ::: A "elephant")
|
| 33 |
-- (A "two" ::: A "three" ::: Nil) ::: A "elephant"
|
28 | 34 |
_car :: Applicative f => (SExpr a -> f (SExpr a)) -> SExpr a -> f (SExpr a)
|
29 | 35 |
_car f (x ::: xs) = (:::) <$> f x <*> pure xs
|
30 | 36 |
_car _ (A a) = pure (A a)
|
31 | 37 |
_car _ Nil = pure Nil
|
32 | 38 |
|
33 | 39 |
-- | A traversal with access to the second element of a pair.
|
| 40 |
--
|
| 41 |
-- >>> import Lens.Family
|
| 42 |
-- >>> set _cdr (A "elephant") (A "one" ::: A "two" ::: A "three" ::: Nil)
|
| 43 |
-- A "one" ::: A "elephant"
|
| 44 |
-- >>> set _cdr (A "two" ::: A "three" ::: Nil) (A "one" ::: A "elephant")
|
| 45 |
-- A "one" ::: A "two" ::: A "three" ::: Nil
|
34 | 46 |
_cdr :: Applicative f => (SExpr a -> f (SExpr a)) -> SExpr a -> f (SExpr a)
|
35 | 47 |
_cdr f (x ::: xs) = (:::) <$> pure x <*> f xs
|
36 | 48 |
_cdr _ (A a) = pure (A a)
|
|
47 | 59 |
-- | A (slightly) shorter alias for `SNil`
|
48 | 60 |
pattern Nil = SNil
|
49 | 61 |
|
| 62 |
getShape :: SExpr a -> String
|
| 63 |
getShape Nil = "empty list"
|
| 64 |
getShape sx = go 0 sx
|
| 65 |
where go n Nil = "list of length " ++ show n
|
| 66 |
go n A {} = "dotted list of length " ++ show n
|
| 67 |
go n (_:::xs) = go (n+1) xs
|
| 68 |
|
50 | 69 |
-- | Utility function for parsing a pair of things.
|
| 70 |
--
|
| 71 |
-- >>> fromPair (isAtom "pachy") (asAtom return) (A "pachy" ::: A "derm" ::: Nil)
|
| 72 |
-- Right ((), "derm")
|
| 73 |
-- >>> fromPair (isAtom "pachy") fromAtom (A "pachy" ::: Nil)
|
| 74 |
-- Left "Expected two-element list"
|
51 | 75 |
fromPair :: (SExpr t -> Either String a)
|
52 | 76 |
-> (SExpr t -> Either String b)
|
53 | 77 |
-> SExpr t -> Either String (a, b)
|
54 | 78 |
fromPair pl pr (l ::: r ::: Nil) = (,) <$> pl l <*> pr r
|
55 | |
fromPair _ _ sx = Left ("Expected two-element list")
|
| 79 |
fromPair _ _ sx = Left ("fromPair: expected two-element list; found " ++ getShape sx)
|
56 | 80 |
|
57 | 81 |
-- | Utility function for parsing a list of things.
|
58 | 82 |
fromList :: (SExpr t -> Either String a) -> SExpr t -> Either String [a]
|
59 | 83 |
fromList p (s ::: ss) = (:) <$> p s <*> fromList p ss
|
60 | 84 |
fromList p Nil = pure []
|
61 | |
fromList _ sx = Left ("Expected list")
|
| 85 |
fromList _ sx = Left ("fromList: expected list; found " ++ getShape sx)
|
62 | 86 |
|
63 | 87 |
-- | Utility function for parsing a single atom
|
64 | 88 |
fromAtom :: SExpr t -> Either String t
|
65 | 89 |
fromAtom (A a) = return a
|
66 | |
fromAtom _ = Left "Expected atom; found list"
|
| 90 |
fromAtom sx = Left ("fromAtom: expected atom; found list" ++ getShape sx)
|
67 | 91 |
|
68 | 92 |
gatherList :: SExpr t -> Either String [SExpr t]
|
69 | 93 |
gatherList (x ::: xs) = (:) <$> pure x <*> gatherList xs
|
70 | 94 |
gatherList Nil = pure []
|
71 | |
gatherList sx = Left ("Expected list")
|
| 95 |
gatherList sx = Left ("gatherList: expected list; found " ++ getShape sx)
|
72 | 96 |
|
73 | 97 |
-- | Parse a two-element list (NOT a dotted pair) using the
|
74 | 98 |
-- provided function.
|
| 99 |
--
|
| 100 |
-- >>> let go (A l) (A r) = return (l ++ r); go _ _ = Left "expected atoms"
|
| 101 |
-- >>> asPair go (A "pachy" ::: A "derm" ::: Nil)
|
| 102 |
-- Right "pachyderm"
|
| 103 |
-- >>> asPair go (A "elephant" ::: Nil)
|
| 104 |
-- Left "asPair: expected two-element list; found list of length 1"
|
75 | 105 |
asPair :: ((SExpr t, SExpr t) -> Either String a)
|
76 | 106 |
-> SExpr t -> Either String a
|
77 | 107 |
asPair f (l ::: r ::: SNil) = f (l, r)
|
78 | |
asPair _ sx = Left ("Expected two-element list")
|
| 108 |
asPair _ sx = Left ("asPair: expected two-element list; found " ++ getShape sx)
|
79 | 109 |
|
80 | 110 |
-- | Parse an arbitrary-length list using the provided function.
|
| 111 |
--
|
| 112 |
-- >>> let go xs = concat <$> mapM fromAtom xs
|
| 113 |
-- >>> asList go (A "el" ::: A "eph" ::: A "ant" ::: Nil)
|
| 114 |
-- Right "elephant"
|
| 115 |
-- >>> asList go (A "el" ::: A "eph" ::: A "ant")
|
| 116 |
-- Left "asList: expected list; found dotted list of length 3"
|
81 | 117 |
asList :: ([SExpr t] -> Either String a) -> SExpr t -> Either String a
|
82 | 118 |
asList f ls = gatherList ls >>= f
|
83 | 119 |
|
84 | 120 |
-- | Match a given literal atom, failing otherwise.
|
| 121 |
--
|
| 122 |
-- >>> isAtom "elephant" (A "elephant")
|
| 123 |
-- Right ()
|
| 124 |
-- >>> isAtom "elephant" (A "elephant" ::: Nil)
|
| 125 |
-- Left "isAtom: expected atom; found list"
|
85 | 126 |
isAtom :: Eq t => t -> SExpr t -> Either String ()
|
86 | 127 |
isAtom s (A s')
|
87 | 128 |
| s == s' = return ()
|
88 | |
| otherwise = Left ".."
|
89 | |
isAtom _ _ = Left ".."
|
| 129 |
| otherwise = Left "isAtom: failed to match atom"
|
| 130 |
isAtom _ sx = Left ("isAtom: expected atom; found " ++ getShape sx)
|
90 | 131 |
|
91 | 132 |
-- | Parse an atom using the provided function.
|
| 133 |
--
|
| 134 |
-- >>> import Data.Char (toUpper)
|
| 135 |
-- >>> asAtom (return . map toUpper) (A "elephant")
|
| 136 |
-- Right "ELEPHANT"
|
| 137 |
-- >>> asAtom (return . map toUpper) Nil
|
| 138 |
-- Left "asAtom: expected atom; found empty list"
|
92 | 139 |
asAtom :: (t -> Either String a) -> SExpr t -> Either String a
|
93 | 140 |
asAtom f (A s) = f s
|
94 | |
asAtom _ sx = Left ("Expected symbol")
|
| 141 |
asAtom _ sx = Left ("asAtom: expected atom; found " ++ getShape sx)
|
95 | 142 |
|
96 | 143 |
-- | Parse an assoc-list using the provided function.
|
| 144 |
--
|
| 145 |
-- >>> let def (x, y) = do { a <- fromAtom x; b <- fromAtom y; return (a ++ ": " ++ b) }
|
| 146 |
-- >>> let defList xs = do { defs <- mapM def xs; return (unlines defs) }
|
| 147 |
-- >>> asAssoc defList ((A "legs" ::: A "four" ::: Nil) ::: (A "trunk" ::: A "one" ::: Nil) ::: Nil)
|
| 148 |
-- Right "legs: four\ntrunk: one\n"
|
| 149 |
-- >>> asAssoc defList ((A "legs" ::: A "four" ::: Nil) ::: (A "elephant") ::: Nil)
|
| 150 |
-- Left "asAssoc: expected pair; found list of length 1"
|
97 | 151 |
asAssoc :: ([(SExpr t, SExpr t)] -> Either String a)
|
98 | 152 |
-> SExpr t -> Either String a
|
99 | 153 |
asAssoc f ss = gatherList ss >>= mapM go >>= f
|
100 | 154 |
where go (a ::: b ::: Nil) = return (a, b)
|
101 | |
go sx = Left ("Expected two-element list")
|
| 155 |
go sx = Left ("asAssoc: expected pair; found " ++ getShape sx)
|