Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| {-# LANGUAGE TypeFamilies #-} | |
| {-# LANGUAGE TemplateHaskell #-} | |
| {-# LANGUAGE ConstraintKinds #-} | |
| {-# LANGUAGE FlexibleContexts #-} | |
| {-# LANGUAGE OverloadedStrings #-} | |
| import Control.Applicative | |
| import Control.Category.Structures | |
| import Control.Lens | |
| import Control.Lens.SemiIso | |
| import Control.SIArrow | |
| import qualified Data.Attoparsec.Text.Lazy as AP | |
| import Data.Map (Map) | |
| import qualified Data.Map as Map | |
| import Data.Scientific (Scientific) | |
| import qualified Data.Syntax as S | |
| import qualified Data.Syntax.Attoparsec.Text.Lazy as S | |
| import Data.Syntax.Char (SyntaxText) | |
| import qualified Data.Syntax.Char as S | |
| import qualified Data.Syntax.Combinator as S | |
| import Data.Syntax.Indent (Indent) | |
| import qualified Data.Syntax.Indent as S | |
| import qualified Data.Syntax.Printer.Text as S | |
| import Data.Text (Text) | |
| import qualified Data.Text.Lazy.Builder as T | |
| import qualified Data.Text.Lazy.IO as T | |
| -- | A JSON value. | |
| data Value = Object (Map Text Value) | |
| | Array [Value] | |
| | String Text | |
| | Number Scientific | |
| | Bool Bool | |
| | Null | |
| deriving (Show) | |
| $(makePrisms ''Value) | |
| -- Indent is basically a Reader transformer. | |
| -- S.breakLine inserts a new line and correct indentation, | |
| -- but does not require any formatting when parsing (it just | |
| -- skips all white space). | |
| -- S.indented increases the indentation level. | |
| -- | Char encoded as 4 hex digits. | |
| hexCode :: SyntaxText syn => Indent syn () Char | |
| hexCode = from enum . bifoldl1_ (iso u f) /$/ sireplicate 4 S.digitHex | |
| where | |
| f (x, y) = 16 * x + y | |
| u 0 = Nothing | |
| u n = Just (n `quotRem` 16) | |
| string :: SyntaxText syn => Indent syn () Text | |
| string = S.packed /$/ S.char '"' */ simany character /* S.char '"' | |
| where | |
| character = S.satisfy (\x -> x /= '"' && x /= '\\') | |
| /+/ S.char '\\' */ escapedChar | |
| escapedChar = S.char 'u' */ hexCode | |
| /+/ S.choice | |
| [ exact v /$/ S.char e | |
| | (v, e) <- [ ('"', '"'), ('\\', '\\'), ('/', '/'), ('\b', 'b') | |
| , ('\f', 'f'), ('\n', 'n') , ('\r', 'r'), ('\t', 't')] | |
| ] | |
| mapFromList :: Ord i => Iso' (Map i v) [(i, v)] | |
| mapFromList = iso Map.toList Map.fromList | |
| object :: SyntaxText syn => Indent syn () Value | |
| object = _Object . mapFromList | |
| /$~ S.char '{' | |
| /*/ S.indented ( | |
| S.breakLine /*/ | |
| S.sepBy field (S.spaces_ /* S.char ',' /* S.breakLine) | |
| ) | |
| /*/ S.breakLine /*/ S.char '}' | |
| where field = string /* S.spaces_ /* S.char ':' /* S.spaces /*/ value | |
| array :: SyntaxText syn => Indent syn () Value | |
| array = _Array | |
| /$~ S.char '[' | |
| /*/ S.indented ( | |
| S.breakLine /*/ | |
| S.sepBy value (S.spaces_ /* S.char ',' /* S.breakLine) | |
| ) | |
| /*/ S.breakLine /*/ S.char ']' | |
| value :: SyntaxText syn => Indent syn () Value | |
| value = _String /$/ string | |
| /+/ _Number /$/ S.scientific | |
| /+/ object | |
| /+/ array | |
| /+/ _Bool . exact True /$/ S.string "true" | |
| /+/ _Bool . exact False /$/ S.string "false" | |
| /+/ _Null /$/ S.string "null" | |
| /?/ "Invalid JSON." | |
| main :: IO () | |
| main = do | |
| -- Load the standard input. | |
| t <- T.getContents | |
| -- Indent with 2 spaces. | |
| let tab :: SyntaxText syn => syn () () | |
| tab = sireplicate_ 2 (S.char ' ') | |
| parser = S.getParser_ $ S.runIndent value tab | |
| printer = S.runPrinter_ $ S.runIndent value tab | |
| -- Try to parse it. | |
| case AP.parse (AP.skipSpace *> parser <* AP.skipSpace <* AP.endOfInput) t of | |
| AP.Fail _ _ err -> putStrLn err | |
| AP.Done _ val -> do | |
| -- Try to pretty print it. | |
| -- (Printing cannot really fail in this example) | |
| case printer val of | |
| Left err -> putStrLn err | |
| Right bld -> T.putStrLn (T.toLazyText bld) | |
| return () |