|
| 1 | +module Data.Json.Extended.Signature.Parse |
| 2 | + ( parseEJsonF |
| 3 | + ) where |
| 4 | + |
| 5 | +import Prelude |
| 6 | + |
| 7 | +import Control.Alt ((<|>)) |
| 8 | +import Control.Apply ((*>), (<*)) |
| 9 | +import Data.Functor ((<$), ($>)) |
| 10 | + |
| 11 | +import Data.Array as A |
| 12 | +import Data.Foldable as F |
| 13 | +import Data.HugeNum as HN |
| 14 | +import Data.Int as Int |
| 15 | +import Data.List as L |
| 16 | +import Data.Maybe as M |
| 17 | +import Data.String as S |
| 18 | +import Data.Tuple as T |
| 19 | + |
| 20 | +import Data.Json.Extended.Signature.Core (EJsonF(..)) |
| 21 | + |
| 22 | +import Text.Parsing.Parser as P |
| 23 | +import Text.Parsing.Parser.Combinators as PC |
| 24 | +import Text.Parsing.Parser.String as PS |
| 25 | + |
| 26 | +parens |
| 27 | + ∷ ∀ m a |
| 28 | + . (Monad m) |
| 29 | + ⇒ P.ParserT String m a |
| 30 | + → P.ParserT String m a |
| 31 | +parens = |
| 32 | + PC.between |
| 33 | + (PS.string "(") |
| 34 | + (PS.string ")") |
| 35 | + |
| 36 | +squares |
| 37 | + ∷ ∀ m a |
| 38 | + . (Monad m) |
| 39 | + ⇒ P.ParserT String m a |
| 40 | + → P.ParserT String m a |
| 41 | +squares = |
| 42 | + PC.between |
| 43 | + (PS.string "[") |
| 44 | + (PS.string "]") |
| 45 | + |
| 46 | +braces |
| 47 | + ∷ ∀ m a |
| 48 | + . (Monad m) |
| 49 | + ⇒ P.ParserT String m a |
| 50 | + → P.ParserT String m a |
| 51 | +braces = |
| 52 | + PC.between |
| 53 | + (PS.string "{") |
| 54 | + (PS.string "}") |
| 55 | + |
| 56 | +commaSep |
| 57 | + ∷ ∀ m a |
| 58 | + . (Monad m) |
| 59 | + ⇒ P.ParserT String m a |
| 60 | + → P.ParserT String m (L.List a) |
| 61 | +commaSep = |
| 62 | + flip PC.sepBy $ |
| 63 | + PS.skipSpaces |
| 64 | + *> PS.string "," |
| 65 | + <* PS.skipSpaces |
| 66 | + |
| 67 | +stringLiteral |
| 68 | + ∷ ∀ m |
| 69 | + . (Monad m) |
| 70 | + ⇒ P.ParserT String m String |
| 71 | +stringLiteral = |
| 72 | + PC.between quote quote (A.many stringChar) |
| 73 | + <#> S.fromCharArray |
| 74 | + |
| 75 | + where |
| 76 | + quote = PS.string "\"" |
| 77 | + |
| 78 | + stringChar = |
| 79 | + PC.try stringEscape |
| 80 | + <|> stringLetter |
| 81 | + |
| 82 | + stringLetter = |
| 83 | + PS.satisfy \c → |
| 84 | + c /= '"' |
| 85 | + |
| 86 | + stringEscape = |
| 87 | + PS.string "\\\"" $> '"' |
| 88 | + |
| 89 | +taggedLiteral |
| 90 | + ∷ ∀ m |
| 91 | + . (Monad m) |
| 92 | + ⇒ String |
| 93 | + → P.ParserT String m String |
| 94 | +taggedLiteral tag = |
| 95 | + PC.try $ |
| 96 | + PS.string tag |
| 97 | + *> parens stringLiteral |
| 98 | + |
| 99 | +anyString |
| 100 | + ∷ ∀ m |
| 101 | + . (Monad m) |
| 102 | + ⇒ P.ParserT String m String |
| 103 | +anyString = |
| 104 | + A.many PS.anyChar |
| 105 | + <#> S.fromCharArray |
| 106 | + |
| 107 | +parseBoolean |
| 108 | + ∷ ∀ m |
| 109 | + . (Monad m) |
| 110 | + ⇒ P.ParserT String m Boolean |
| 111 | +parseBoolean = |
| 112 | + PC.choice |
| 113 | + [ true <$ PS.string "true" |
| 114 | + , false <$ PS.string "false" |
| 115 | + ] |
| 116 | + |
| 117 | +parseDigit |
| 118 | + ∷ ∀ m |
| 119 | + . (Monad m) |
| 120 | + ⇒ P.ParserT String m Int |
| 121 | +parseDigit = |
| 122 | + PC.choice |
| 123 | + [ 0 <$ PS.string "0" |
| 124 | + , 1 <$ PS.string "1" |
| 125 | + , 2 <$ PS.string "2" |
| 126 | + , 3 <$ PS.string "3" |
| 127 | + , 4 <$ PS.string "4" |
| 128 | + , 5 <$ PS.string "5" |
| 129 | + , 6 <$ PS.string "6" |
| 130 | + , 7 <$ PS.string "7" |
| 131 | + , 8 <$ PS.string "8" |
| 132 | + , 9 <$ PS.string "9" |
| 133 | + ] |
| 134 | + |
| 135 | +many1 |
| 136 | + ∷ ∀ m s a |
| 137 | + . (Monad m) |
| 138 | + ⇒ P.ParserT s m a |
| 139 | + → P.ParserT s m (L.List a) |
| 140 | +many1 p = |
| 141 | + L.Cons |
| 142 | + <$> p |
| 143 | + <*> L.many p |
| 144 | + |
| 145 | +parseNat |
| 146 | + ∷ ∀ m |
| 147 | + . (Monad m) |
| 148 | + ⇒ P.ParserT String m Int |
| 149 | +parseNat = |
| 150 | + many1 parseDigit |
| 151 | + <#> F.foldl (\a i → a * 10 + i) 0 |
| 152 | + |
| 153 | +parseNegative |
| 154 | + ∷ ∀ m a |
| 155 | + . (Monad m, Ring a) |
| 156 | + ⇒ P.ParserT String m a |
| 157 | + → P.ParserT String m a |
| 158 | +parseNegative p = |
| 159 | + PS.string "-" |
| 160 | + *> PS.skipSpaces |
| 161 | + *> p |
| 162 | + <#> negate |
| 163 | + |
| 164 | +parsePositive |
| 165 | + ∷ ∀ m a |
| 166 | + . (Monad m, Ring a) |
| 167 | + ⇒ P.ParserT String m a |
| 168 | + → P.ParserT String m a |
| 169 | +parsePositive p = |
| 170 | + PC.optional (PS.string "+" *> PS.skipSpaces) |
| 171 | + *> p |
| 172 | + |
| 173 | +parseSigned |
| 174 | + ∷ ∀ m a |
| 175 | + . (Monad m, Ring a) |
| 176 | + ⇒ P.ParserT String m a |
| 177 | + → P.ParserT String m a |
| 178 | +parseSigned p = |
| 179 | + parseNegative p |
| 180 | + <|> parsePositive p |
| 181 | + |
| 182 | +parseInt |
| 183 | + ∷ ∀ m |
| 184 | + . (Monad m) |
| 185 | + ⇒ P.ParserT String m Int |
| 186 | +parseInt = |
| 187 | + parseSigned parseNat |
| 188 | + |
| 189 | +parseExponent |
| 190 | + ∷ ∀ m |
| 191 | + . (Monad m) |
| 192 | + ⇒ P.ParserT String m Int |
| 193 | +parseExponent = |
| 194 | + (PS.string "e" <|> PS.string "E") |
| 195 | + *> parseInt |
| 196 | + |
| 197 | +parsePositiveScientific |
| 198 | + ∷ ∀ m |
| 199 | + . (Monad m) |
| 200 | + ⇒ P.ParserT String m HN.HugeNum |
| 201 | +parsePositiveScientific = do |
| 202 | + let ten = HN.fromNumber 10.0 |
| 203 | + lhs ← PC.try $ fromInt <$> parseNat <* PS.string "." |
| 204 | + rhs ← A.many parseDigit <#> F.foldr (\d f → divNum (f + fromInt d) ten) zero |
| 205 | + exp ← parseExponent |
| 206 | + pure $ (lhs + rhs) * safePow ten exp |
| 207 | + |
| 208 | + where |
| 209 | + fromInt = HN.fromNumber <<< Int.toNumber |
| 210 | + |
| 211 | + -- TODO: remove when HugeNum adds division |
| 212 | + divNum a b = |
| 213 | + HN.fromNumber $ |
| 214 | + HN.toNumber a / HN.toNumber b |
| 215 | + |
| 216 | + -- To work around: https://github.com/Thimoteus/purescript-hugenums/issues/6 |
| 217 | + safePow a 0 = one |
| 218 | + safePow a n = HN.pow a n |
| 219 | + |
| 220 | +parseHugeNum |
| 221 | + ∷ ∀ m |
| 222 | + . (Monad m) |
| 223 | + ⇒ P.ParserT String m HN.HugeNum |
| 224 | +parseHugeNum = do |
| 225 | + chars ← A.many (PS.oneOf ['0','1','2','3','4','5','6','7','8','9','-','.']) <#> S.fromCharArray |
| 226 | + case HN.fromString chars of |
| 227 | + M.Just num → pure num |
| 228 | + M.Nothing → P.fail $ "Failed to parse decimal: " <> chars |
| 229 | + |
| 230 | +parseScientific |
| 231 | + ∷ ∀ m |
| 232 | + . (Monad m) |
| 233 | + ⇒ P.ParserT String m HN.HugeNum |
| 234 | +parseScientific = |
| 235 | + parseSigned parsePositiveScientific |
| 236 | + |
| 237 | +parseDecimal |
| 238 | + ∷ ∀ m |
| 239 | + . (Monad m) |
| 240 | + ⇒ P.ParserT String m HN.HugeNum |
| 241 | +parseDecimal = |
| 242 | + parseHugeNum |
| 243 | + <|> parseScientific |
| 244 | + |
| 245 | +-- | Parse one layer of structure. |
| 246 | +parseEJsonF |
| 247 | + ∷ ∀ m a |
| 248 | + . (Monad m) |
| 249 | + ⇒ P.ParserT String m a |
| 250 | + → P.ParserT String m (EJsonF a) |
| 251 | +parseEJsonF rec = |
| 252 | + PC.choice $ |
| 253 | + [ Null <$ PS.string "null" |
| 254 | + , Boolean <$> parseBoolean |
| 255 | + , Decimal <$> PC.try parseDecimal |
| 256 | + , Integer <$> parseInt |
| 257 | + , String <$> stringLiteral |
| 258 | + , Timestamp <$> taggedLiteral "TIMESTAMP" |
| 259 | + , Time <$> taggedLiteral "TIME" |
| 260 | + , Date <$> taggedLiteral "DATE" |
| 261 | + , Interval <$> taggedLiteral "INTERVAL" |
| 262 | + , ObjectId <$> taggedLiteral "OID" |
| 263 | + , OrderedSet <<< L.fromList <$> parens (commaSep rec) |
| 264 | + , Array <<< L.fromList <$> squares (commaSep rec) |
| 265 | + , Object <<< L.fromList <$> braces (commaSep parseAssignment) |
| 266 | + ] |
| 267 | + |
| 268 | + where |
| 269 | + parseColon ∷ P.ParserT String m String |
| 270 | + parseColon = |
| 271 | + PS.skipSpaces |
| 272 | + *> PS.string ":" |
| 273 | + <* PS.skipSpaces |
| 274 | + |
| 275 | + parseAssignment ∷ P.ParserT String m (T.Tuple a a) |
| 276 | + parseAssignment = |
| 277 | + T.Tuple |
| 278 | + <$> rec <* parseColon |
| 279 | + <*> rec |
| 280 | + |
0 commit comments