├── Setup.hs ├── .gitignore ├── cabal.haskell-ci ├── CHANGELOG.md ├── .github └── workflows │ ├── bump.yml │ ├── stack.yml │ └── haskell-ci.yml ├── README.md ├── src └── Data │ └── Aeson │ ├── Micro │ ├── Parser.hs │ └── Scanner.x │ └── Micro.hs ├── src-tests └── tests.hs ├── microaeson.cabal └── LICENSE /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist-newstyle/ 2 | /.stack-work/ 3 | stack*.yaml.lock 4 | .ghc.environment.* 5 | *~ 6 | -------------------------------------------------------------------------------- /cabal.haskell-ci: -------------------------------------------------------------------------------- 1 | branches: master 2 | 3 | constraint-set containers-0.8 4 | ghc: >= 8.2 5 | constraints: containers ^>= 0.8 6 | tests: True 7 | run-tests: True 8 | 9 | raw-project 10 | allow-newer: containers 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.1.0.3 2 | 3 | _Andreas Abel, 2025-09-01_ 4 | 5 | - Drop support for `aeson-1.*`. 6 | - Remove unused dependencies. 7 | - Tested with GHC 8.0 - 9.14 alpha1. 8 | 9 | 10 | ### 0.1.0.2 11 | 12 | _Andreas Abel, 2024-06-25_ 13 | 14 | - Drop support for GHC 7. 15 | - Tested with GHC 8.0 - 9.10. 16 | 17 | 18 | ### 0.1.0.1 19 | 20 | _Andreas Abel, 2022-05-26_ 21 | 22 | - Allow `base >= 4.17`. 23 | - Tested with GHC 7.10 - 9.4. 24 | -------------------------------------------------------------------------------- /.github/workflows/bump.yml: -------------------------------------------------------------------------------- 1 | name: Create dependency bump PR 2 | on: 3 | # allows manual triggering from https://github.com/haskell-hvr/microaeson/actions/workflows/bump.yml 4 | workflow_dispatch: 5 | # runs weekly on Thursday at 5:00 6 | schedule: 7 | - cron: '0 5 * * 4' 8 | 9 | permissions: 10 | contents: write 11 | pull-requests: write 12 | 13 | jobs: 14 | bump: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: nomeata/haskell-bounds-bump-action@main 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # microaeson: A tiny JSON library with light dependency footprint 2 | 3 | `microaeson` aims to provide a [RFC 8259](https://tools.ietf.org/html/rfc8259) 4 | compliant JavaScript Object Notation (JSON) implementation. 5 | 6 | The [microaeson](https://hackage.haskell.org/package/microaeson) package provides 7 | a smaller subset of the [aeson](https://hackage.haskell.org/package/aeson) API 8 | with less dependencies and a simpler implementation. 9 | 10 | The API is designed in such a way to allow to easily convert client 11 | code written against `microaeson`'s API to use the full 12 | [aeson](https://hackage.haskell.org/package/aeson) API. 13 | 14 | (C) 2016-2018 Herbert Valerio Riedel 15 | 16 | License: GPL-3 17 | -------------------------------------------------------------------------------- /src/Data/Aeson/Micro/Parser.hs: -------------------------------------------------------------------------------- 1 | module Data.Aeson.Micro.Parser where 2 | 3 | import Control.Exception as E 4 | import Control.Monad 5 | import Data.Char 6 | import Data.Word 7 | import qualified GHC.Foreign as GHC 8 | import GHC.IO.Encoding 9 | import System.IO.Unsafe 10 | import Text.Read (readMaybe) 11 | 12 | import qualified Data.ByteString as BS 13 | import qualified Data.ByteString.Char8 as BS.Char8 14 | 15 | decodeEscapedHex :: BS.ByteString -> Maybe Char 16 | decodeEscapedHex bs = do 17 | [0x5c,0x75,d1,d2,d3,d4] <- pure (BS.unpack bs) 18 | 19 | let cp = (0x10*((0x10*((0x10 * h2n d1) + h2n d2)) + h2n d3)) + h2n d4 20 | 21 | guard (not (0xd800 <= cp && cp <= 0xdfff)) 22 | 23 | pure (chr cp) 24 | 25 | decodeEscapedHexSurr :: BS.ByteString -> Maybe Char 26 | decodeEscapedHexSurr bs = do 27 | [0x5c,0x75,h1,h2,h3,h4,0x5c,0x75,l1,l2,l3,l4] <- pure (BS.unpack bs) 28 | 29 | let hsurr = (0x10*((0x10*((0x10 * h2n h1) + h2n h2)) + h2n h3)) + h2n h4 30 | lsurr = (0x10*((0x10*((0x10 * h2n l1) + h2n l2)) + h2n l3)) + h2n l4 31 | 32 | guard ((0xd800 <= hsurr && hsurr <= 0xdbff) && (0xdc00 <= lsurr && lsurr <= 0xdfff)) 33 | 34 | let cp = 0x10000 + ((hsurr-0xd800)*0x400) + (lsurr-0xdc00) 35 | 36 | pure (chr cp) 37 | 38 | decodeNumber :: BS.ByteString -> Maybe Double 39 | decodeNumber = readMaybe . BS.Char8.unpack 40 | 41 | h2n :: Word8 -> Int 42 | h2n w 43 | | 0x30 <= w && w <= 0x39 = fromIntegral (w-0x30) 44 | | 0x41 <= w && w <= 0x46 = fromIntegral (w-0x37) 45 | | 0x61 <= w && w <= 0x66 = fromIntegral (w-0x57) 46 | | otherwise = undefined 47 | 48 | decodeEscaped :: BS.ByteString -> Maybe Char 49 | decodeEscaped bs = do 50 | [0x5c,c] <- pure (BS.unpack bs) 51 | case c of 52 | 0x22 -> pure '\x22' 53 | 0x5c -> pure '\x5c' 54 | 0x2f -> pure '\x2f' 55 | 0x62 -> pure '\x08' 56 | 0x66 -> pure '\x0c' 57 | 0x6e -> pure '\x0a' 58 | 0x72 -> pure '\x0d' 59 | 0x74 -> pure '\x09' 60 | _ -> Nothing 61 | 62 | decodeUnescaped :: BS.ByteString -> Maybe String 63 | decodeUnescaped = decodeString utf8 64 | 65 | decodeString :: TextEncoding -> BS.ByteString -> Maybe String 66 | decodeString te bs = unsafePerformIO (decodeStringIO te bs) 67 | 68 | {-# NOINLINE decodeStringIO #-} 69 | decodeStringIO :: TextEncoding -> BS.ByteString -> IO (Maybe String) 70 | decodeStringIO te bs = cvtEx <$> try (BS.useAsCStringLen bs (GHC.peekCStringLen te)) 71 | where 72 | cvtEx :: Either IOException a -> Maybe a 73 | cvtEx = either (const Nothing) Just 74 | 75 | -------------------------------------------------------------------------------- /src-tests/tests.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-orphans #-} 2 | 3 | module Main where 4 | 5 | import qualified Data.Aeson.KeyMap as KeyMap 6 | import Data.Aeson.Key (fromText, toText) 7 | import qualified Data.Map.Strict as Map 8 | import qualified Data.Vector as V 9 | 10 | import Test.Tasty 11 | import Test.Tasty.QuickCheck as QC 12 | import Test.QuickCheck.Instances () 13 | 14 | import qualified Data.Aeson as REF 15 | import qualified Data.Aeson.Micro as IUT 16 | 17 | main :: IO () 18 | main = defaultMain tests 19 | 20 | tests :: TestTree 21 | tests = testGroup "Tests" [qcProps] 22 | where 23 | qcProps = testGroup "QC" 24 | [ QC.testProperty "roundtrip(ref/ref)" $ 25 | \x -> (fmap fromAeson . REF.decode . REF.encode . toAeson) x == Just x 26 | 27 | , QC.testProperty "roundtrip(ref/iut)" $ 28 | \x -> (fmap fromAeson . REF.decode . IUT.encode) x == Just x 29 | 30 | , QC.testProperty "roundtrip(iut/ref)" $ 31 | \x -> (IUT.decode . REF.encode . toAeson) x == Just x 32 | 33 | , QC.testProperty "roundtrip(iut/iut)" $ 34 | \x -> (IUT.decodeStrict . IUT.encodeStrict) x == Just (x :: IUT.Value) 35 | ] 36 | 37 | 38 | instance Arbitrary IUT.Value where 39 | arbitrary = sized value 40 | where 41 | value 0 = oneof [ IUT.String <$> arbitrary 42 | , IUT.Number <$> arbitrary 43 | , IUT.Bool <$> arbitrary 44 | , pure IUT.Null 45 | ] 46 | value n | n>0 47 | = oneof [ IUT.String <$> arbitrary 48 | , IUT.Number <$> arbitrary 49 | , IUT.Bool <$> arbitrary 50 | , pure IUT.Null 51 | , IUT.Array <$> resize ((10*n) `div` 14) arbitrary 52 | , IUT.Object <$> resize ((10*n) `div` 14) arbitrary 53 | ] 54 | | otherwise = pure IUT.Null 55 | 56 | 57 | toAeson :: IUT.Value -> REF.Value 58 | toAeson j = case j of 59 | IUT.String t -> REF.String t 60 | IUT.Number n -> REF.toJSON n 61 | IUT.Bool b -> REF.Bool b 62 | IUT.Null -> REF.Null 63 | IUT.Array l -> REF.Array (V.fromList (map toAeson l)) 64 | IUT.Object m -> REF.object [ (fromText k, toAeson v) | (k,v) <- Map.toList m ] 65 | 66 | fromAeson :: REF.Value -> IUT.Value 67 | fromAeson j = case j of 68 | REF.String t -> IUT.String t 69 | REF.Bool b -> IUT.Bool b 70 | REF.Null -> IUT.Null 71 | REF.Number s -> IUT.Number (realToFrac s) 72 | REF.Array v -> IUT.Array (map fromAeson (V.toList v)) 73 | REF.Object m -> IUT.object [ (toText k, fromAeson v) | (k,v) <- KeyMap.toList m ] 74 | -------------------------------------------------------------------------------- /.github/workflows/stack.yml: -------------------------------------------------------------------------------- 1 | name: Stack build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | defaults: 12 | run: 13 | shell: bash 14 | 15 | jobs: 16 | stack: 17 | name: ${{ matrix.os }} Stack ${{ matrix.plan.resolver }} / ${{ matrix.plan.ghc }} 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | os: [ubuntu-latest] 22 | plan: 23 | - ghc: '9.12.2' 24 | resolver: 'nightly-2025-09-01' 25 | - ghc: '9.10.2' 26 | resolver: 'lts-24.8' 27 | - ghc: '9.8.4' 28 | resolver: 'lts-23.28' 29 | - ghc: '9.6.7' 30 | resolver: 'lts-22.44' 31 | - ghc: '9.4.8' 32 | resolver: 'lts-21.25' 33 | - ghc: '9.2.8' 34 | resolver: 'lts-20.26' 35 | - ghc: '9.0.2' 36 | resolver: 'lts-19.33' 37 | # Dropping support for aeson<2 38 | # - ghc: '8.10.7' 39 | # resolver: 'lts-18.28' 40 | # - ghc: '8.8.4' 41 | # resolver: 'lts-16.31' 42 | # - ghc: '8.6.5' 43 | # resolver: 'lts-14.27' 44 | # - ghc: '8.4.4' 45 | # resolver: 'lts-12.26' 46 | 47 | include: 48 | - os: windows-latest 49 | plan: 50 | ghc: '9.12.2' 51 | resolver: 'nightly-2025-09-01' 52 | 53 | - os: macos-latest 54 | plan: 55 | ghc: '9.12.2' 56 | resolver: 'nightly-2025-09-01' 57 | 58 | runs-on: ${{ matrix.os }} 59 | env: 60 | STACK: stack --system-ghc --no-terminal --resolver ${{ matrix.plan.resolver }} 61 | 62 | steps: 63 | - uses: actions/checkout@v5 64 | 65 | - uses: haskell-actions/setup@latest 66 | id: setup 67 | with: 68 | ghc-version: ${{ matrix.plan.ghc }} 69 | enable-stack: true 70 | cabal-update: false 71 | 72 | - uses: actions/cache/restore@v4 73 | id: cache 74 | env: 75 | key: ${{ runner.os }}-stack-${{ steps.setup.outputs.stack-version }}-ghc-${{ steps.setup.outputs.ghc-version }} 76 | with: 77 | path: ${{ steps.setup.outputs.stack-root }} 78 | key: ${{ env.key }}-${{ github.sha }} 79 | restore-keys: ${{ env.key }}- 80 | 81 | - name: Configure 82 | run: $STACK init 83 | 84 | - name: Install dependencies 85 | run: $STACK test --only-dependencies 86 | 87 | - name: Build 88 | run: $STACK test --haddock --no-haddock-deps --no-run-tests 89 | 90 | - name: Test 91 | run: $STACK -j 1 test --haddock --no-haddock-deps 92 | 93 | - uses: actions/cache/save@v4 94 | if: always() && steps.cache.outputs.cache-hit != 'true' 95 | with: 96 | path: ${{ steps.setup.outputs.stack-root }} 97 | key: ${{ steps.cache.outputs.cache-primary-key }} 98 | -------------------------------------------------------------------------------- /src/Data/Aeson/Micro/Scanner.x: -------------------------------------------------------------------------------- 1 | -- -*- haskell -*- 2 | { 3 | module Data.Aeson.Micro.Scanner (Lexeme(..), scanLexemes) where 4 | 5 | import qualified Data.ByteString as B 6 | import Data.Word 7 | } 8 | 9 | %encoding "latin1" 10 | 11 | -- c.f. RFC 7159 12 | 13 | $ws = [\x20\x09\x0a\x0d] 14 | 15 | -- unescaped = %x20-21 / %x23-5B / %x5D-10FFFF 16 | $escaped = [\x00-\x1f\x22\x5c] 17 | $unescaped = [\x00-\xff] # $escaped 18 | 19 | -- zero / ( digit1-9 *DIGIT ) 20 | @int = "0"|[1-9][0-9]* 21 | 22 | -- decimal-point 1*DIGIT 23 | @frac = "."[0-9]+ 24 | 25 | -- e [ minus / plus ] 1*DIGIT 26 | @exp = [eE][\-\+]?[0-9]+ 27 | 28 | -- [ minus ] int [ frac ] [ exp ] 29 | @num = "-"? @int @frac? @exp? 30 | 31 | :- 32 | 33 | <0> $ws ; 34 | <0> "{" { L_ObjStart } 35 | <0> "}" { L_ObjEnd } 36 | <0> "[" { L_ArrStart } 37 | <0> "]" { L_ArrEnd } 38 | <0> \" { L_StrStart } 39 | <0> \: { L_Colon } 40 | <0> \, { L_Comma } 41 | <0> "true" { L_True } 42 | <0> "false" { L_False } 43 | <0> "null" { L_Null } 44 | <0> @num { L_Number } 45 | 46 | [\x22] { L_StrEnd } 47 | $unescaped+ { L_StrUnescaped } 48 | \\[\x22\x5c\x2f\x62\x66\x6e\x72\x74] { L_StrEscaped } 49 | 50 | \\"u"[0-9a-cA-CefEF][0-9a-fA-F]{3} { L_StrEscapedHex } 51 | \\"u"[dD][0-7][0-9a-fA-F]{2} { L_StrEscapedHex } 52 | \\"u"[dD][89abAB][0-9a-fA-F]{2} \\"u"[dD][c-fC-F][0-9a-fA-F]{2} { L_StrEscapedHexSurr } 53 | 54 | { 55 | data Lexeme 56 | = L_ArrStart 57 | | L_ArrEnd 58 | | L_Colon 59 | | L_Comma 60 | | L_False 61 | | L_Null 62 | | L_Number 63 | | L_ObjStart 64 | | L_ObjEnd 65 | | L_StrStart 66 | | L_StrEnd 67 | | L_StrEscaped 68 | | L_StrEscapedHex 69 | | L_StrEscapedHexSurr 70 | | L_StrUnescaped 71 | | L_True 72 | | L_LexError 73 | deriving (Eq,Ord,Show) 74 | 75 | type AlexInput = B.ByteString 76 | 77 | alexGetByte :: AlexInput -> Maybe (Word8,AlexInput) 78 | alexGetByte = B.uncons 79 | 80 | -- alexInputPrevChar :: AlexInput -> Char 81 | 82 | -- generated by @alex@ 83 | alexScan :: AlexInput -> Int -> AlexReturn Lexeme 84 | 85 | scanLexemes :: B.ByteString -> [(Lexeme, B.ByteString)] 86 | scanLexemes = go False 87 | where 88 | go inStr bs = case alexScan bs (if inStr then string else 0) of 89 | AlexEOF -> [] 90 | AlexError inp' -> [(L_LexError,inp')] 91 | AlexSkip inp' _len -> go inStr inp' 92 | AlexToken inp' len L_StrUnescaped -- workaround for https://github.com/simonmar/alex/issues/119 93 | | B.length bs - B.length inp' > len 94 | -> (L_StrUnescaped,B.take (B.length bs - B.length inp') bs) 95 | : go inStr inp' 96 | AlexToken inp' len act 97 | -> (act,B.take len bs) 98 | : go (if inStr then act /= L_StrEnd else act == L_StrStart) inp' 99 | } 100 | -------------------------------------------------------------------------------- /microaeson.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 2.0 2 | name: microaeson 3 | version: 0.1.0.3 4 | 5 | synopsis: A tiny JSON library with light dependency footprint 6 | license: GPL-3 7 | license-file: LICENSE 8 | author: Herbert Valerio Riedel 9 | maintainer: Andreas Abel 10 | category: Text, Web, JSON 11 | build-type: Simple 12 | description: { 13 | 14 | @microaeson@ aims to provide a [RFC 8259](https://tools.ietf.org/html/rfc8259) compliant JavaScript Object Notation (JSON) implementation. 15 | 16 | The [microaeson](https://hackage.haskell.org/package/microaeson) package provides a smaller subset of the [aeson](https://hackage.haskell.org/package/aeson) API with less dependencies and a simpler implementation. 17 | . 18 | The API is designed in such a way to allow to easily convert client code written against @microaeson@'s API to use the full [aeson](https://hackage.haskell.org/package/aeson) API. 19 | 20 | } 21 | 22 | tested-with: 23 | GHC == 9.14.1 24 | GHC == 9.12.2 25 | GHC == 9.10.2 26 | GHC == 9.8.4 27 | GHC == 9.6.7 28 | GHC == 9.4.8 29 | GHC == 9.2.8 30 | GHC == 9.0.2 31 | GHC == 8.10.7 32 | GHC == 8.8.4 33 | GHC == 8.6.5 34 | GHC == 8.4.4 35 | GHC == 8.2.2 36 | GHC == 8.0.2 37 | 38 | extra-doc-files: 39 | CHANGELOG.md 40 | README.md 41 | 42 | source-repository head 43 | type: git 44 | location: https://github.com/haskell-hvr/microaeson.git 45 | 46 | library 47 | default-language: Haskell2010 48 | other-extensions: CPP 49 | DeriveDataTypeable 50 | DeriveGeneric 51 | FlexibleInstances 52 | GeneralizedNewtypeDeriving 53 | MagicHash 54 | OverloadedStrings 55 | 56 | hs-source-dirs: src 57 | 58 | exposed-modules: Data.Aeson.Micro 59 | other-modules: Data.Aeson.Micro.Parser 60 | Data.Aeson.Micro.Scanner 61 | 62 | build-depends: array ^>= 0.5.1.1 63 | , base >= 4.9.0.0 && < 5 64 | , bytestring >= 0.10.8.0 && < 0.13 65 | , containers >= 0.5.7.1 && < 1 66 | , deepseq >= 1.4.2.0 && < 1.6 67 | , text >= 1.2.2.2 && < 1.3 || >= 2.0 && < 3 68 | 69 | build-tool-depends: alex:alex >= 3.2.0 70 | 71 | ghc-options: 72 | -Wall 73 | -Wcompat 74 | -Wnoncanonical-monad-instances 75 | 76 | if impl(ghc < 8.8) 77 | ghc-options: 78 | -Wnoncanonical-monadfail-instances 79 | 80 | test-suite microaeson 81 | default-language: Haskell2010 82 | 83 | hs-source-dirs: src-tests 84 | main-is: tests.hs 85 | 86 | type: exitcode-stdio-1.0 87 | 88 | -- internal dependency 89 | build-depends: microaeson 90 | 91 | -- constraints inherited via lib:microaeson component 92 | build-depends: base 93 | , containers 94 | 95 | -- dependencies requiring constraints 96 | build-depends: aeson >= 2.0.3.0 && < 2.3 97 | , quickcheck-instances ^>= 0.3.16 98 | , tasty >= 1.0.1.1 && < 1.6 99 | , tasty-quickcheck >= 0.10 && < 1 100 | , vector ^>= 0.12.0.1 101 | || ^>= 0.13.0.0 102 | 103 | 104 | ghc-options: 105 | -Wall 106 | -Wcompat 107 | -Wnoncanonical-monad-instances 108 | 109 | if impl(ghc < 8.8) 110 | ghc-options: 111 | -Wnoncanonical-monadfail-instances 112 | -------------------------------------------------------------------------------- /.github/workflows/haskell-ci.yml: -------------------------------------------------------------------------------- 1 | # This GitHub workflow config has been generated by a script via 2 | # 3 | # haskell-ci 'github' 'microaeson.cabal' 4 | # 5 | # To regenerate the script (for example after adjusting tested-with) run 6 | # 7 | # haskell-ci regenerate 8 | # 9 | # For more information, see https://github.com/haskell-CI/haskell-ci 10 | # 11 | # version: 0.19.20250821 12 | # 13 | # REGENDATA ("0.19.20250821",["github","microaeson.cabal"]) 14 | # 15 | name: Haskell-CI 16 | on: 17 | push: 18 | branches: 19 | - master 20 | pull_request: 21 | branches: 22 | - master 23 | jobs: 24 | linux: 25 | name: Haskell-CI - Linux - ${{ matrix.compiler }} 26 | runs-on: ubuntu-24.04 27 | timeout-minutes: 28 | 60 29 | container: 30 | image: buildpack-deps:jammy 31 | continue-on-error: ${{ matrix.allow-failure }} 32 | strategy: 33 | matrix: 34 | include: 35 | - compiler: ghc-9.14.0.20250819 36 | compilerKind: ghc 37 | compilerVersion: 9.14.0.20250819 38 | setup-method: ghcup-prerelease 39 | allow-failure: false 40 | - compiler: ghc-9.12.2 41 | compilerKind: ghc 42 | compilerVersion: 9.12.2 43 | setup-method: ghcup 44 | allow-failure: false 45 | - compiler: ghc-9.10.2 46 | compilerKind: ghc 47 | compilerVersion: 9.10.2 48 | setup-method: ghcup 49 | allow-failure: false 50 | - compiler: ghc-9.8.4 51 | compilerKind: ghc 52 | compilerVersion: 9.8.4 53 | setup-method: ghcup 54 | allow-failure: false 55 | - compiler: ghc-9.6.7 56 | compilerKind: ghc 57 | compilerVersion: 9.6.7 58 | setup-method: ghcup 59 | allow-failure: false 60 | - compiler: ghc-9.4.8 61 | compilerKind: ghc 62 | compilerVersion: 9.4.8 63 | setup-method: ghcup 64 | allow-failure: false 65 | - compiler: ghc-9.2.8 66 | compilerKind: ghc 67 | compilerVersion: 9.2.8 68 | setup-method: ghcup 69 | allow-failure: false 70 | - compiler: ghc-9.0.2 71 | compilerKind: ghc 72 | compilerVersion: 9.0.2 73 | setup-method: ghcup 74 | allow-failure: false 75 | - compiler: ghc-8.10.7 76 | compilerKind: ghc 77 | compilerVersion: 8.10.7 78 | setup-method: ghcup 79 | allow-failure: false 80 | - compiler: ghc-8.8.4 81 | compilerKind: ghc 82 | compilerVersion: 8.8.4 83 | setup-method: ghcup 84 | allow-failure: false 85 | - compiler: ghc-8.6.5 86 | compilerKind: ghc 87 | compilerVersion: 8.6.5 88 | setup-method: ghcup 89 | allow-failure: false 90 | - compiler: ghc-8.4.4 91 | compilerKind: ghc 92 | compilerVersion: 8.4.4 93 | setup-method: ghcup 94 | allow-failure: false 95 | - compiler: ghc-8.2.2 96 | compilerKind: ghc 97 | compilerVersion: 8.2.2 98 | setup-method: ghcup 99 | allow-failure: false 100 | - compiler: ghc-8.0.2 101 | compilerKind: ghc 102 | compilerVersion: 8.0.2 103 | setup-method: ghcup 104 | allow-failure: false 105 | fail-fast: false 106 | steps: 107 | - name: apt-get install 108 | run: | 109 | apt-get update 110 | apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5 libnuma-dev 111 | - name: Install GHCup 112 | run: | 113 | mkdir -p "$HOME/.ghcup/bin" 114 | curl -sL https://downloads.haskell.org/ghcup/0.1.50.1/x86_64-linux-ghcup-0.1.50.1 > "$HOME/.ghcup/bin/ghcup" 115 | chmod a+x "$HOME/.ghcup/bin/ghcup" 116 | - name: Install cabal-install 117 | run: | 118 | "$HOME/.ghcup/bin/ghcup" install cabal 3.16.0.0 || (cat "$HOME"/.ghcup/logs/*.* && false) 119 | echo "CABAL=$HOME/.ghcup/bin/cabal-3.16.0.0 -vnormal+nowrap" >> "$GITHUB_ENV" 120 | - name: Install GHC (GHCup) 121 | if: matrix.setup-method == 'ghcup' 122 | run: | 123 | "$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false) 124 | HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER") 125 | HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#') 126 | HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#') 127 | echo "HC=$HC" >> "$GITHUB_ENV" 128 | echo "HCPKG=$HCPKG" >> "$GITHUB_ENV" 129 | echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV" 130 | env: 131 | HCKIND: ${{ matrix.compilerKind }} 132 | HCNAME: ${{ matrix.compiler }} 133 | HCVER: ${{ matrix.compilerVersion }} 134 | - name: Install GHC (GHCup prerelease) 135 | if: matrix.setup-method == 'ghcup-prerelease' 136 | run: | 137 | "$HOME/.ghcup/bin/ghcup" config add-release-channel prereleases 138 | "$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false) 139 | HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER") 140 | HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#') 141 | HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#') 142 | echo "HC=$HC" >> "$GITHUB_ENV" 143 | echo "HCPKG=$HCPKG" >> "$GITHUB_ENV" 144 | echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV" 145 | env: 146 | HCKIND: ${{ matrix.compilerKind }} 147 | HCNAME: ${{ matrix.compiler }} 148 | HCVER: ${{ matrix.compilerVersion }} 149 | - name: Set PATH and environment variables 150 | run: | 151 | echo "$HOME/.cabal/bin" >> $GITHUB_PATH 152 | echo "LANG=C.UTF-8" >> "$GITHUB_ENV" 153 | echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV" 154 | echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV" 155 | HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))') 156 | echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV" 157 | echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV" 158 | echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV" 159 | if [ $((HCNUMVER >= 91400)) -ne 0 ] ; then echo "HEADHACKAGE=true" >> "$GITHUB_ENV" ; else echo "HEADHACKAGE=false" >> "$GITHUB_ENV" ; fi 160 | echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV" 161 | env: 162 | HCKIND: ${{ matrix.compilerKind }} 163 | HCNAME: ${{ matrix.compiler }} 164 | HCVER: ${{ matrix.compilerVersion }} 165 | - name: env 166 | run: | 167 | env 168 | - name: write cabal config 169 | run: | 170 | mkdir -p $CABAL_DIR 171 | cat >> $CABAL_CONFIG <> $CABAL_CONFIG <> $CABAL_CONFIG < cabal-plan.xz 216 | echo 'f62ccb2971567a5f638f2005ad3173dba14693a45154c1508645c52289714cb2 cabal-plan.xz' | sha256sum -c - 217 | xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan 218 | rm -f cabal-plan.xz 219 | chmod a+x $HOME/.cabal/bin/cabal-plan 220 | cabal-plan --version 221 | - name: checkout 222 | uses: actions/checkout@v4 223 | with: 224 | path: source 225 | - name: initial cabal.project for sdist 226 | run: | 227 | touch cabal.project 228 | echo "packages: $GITHUB_WORKSPACE/source/." >> cabal.project 229 | cat cabal.project 230 | - name: sdist 231 | run: | 232 | mkdir -p sdist 233 | $CABAL sdist all --output-dir $GITHUB_WORKSPACE/sdist 234 | - name: unpack 235 | run: | 236 | mkdir -p unpacked 237 | find sdist -maxdepth 1 -type f -name '*.tar.gz' -exec tar -C $GITHUB_WORKSPACE/unpacked -xzvf {} \; 238 | - name: generate cabal.project 239 | run: | 240 | PKGDIR_microaeson="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/microaeson-[0-9.]*')" 241 | echo "PKGDIR_microaeson=${PKGDIR_microaeson}" >> "$GITHUB_ENV" 242 | rm -f cabal.project cabal.project.local 243 | touch cabal.project 244 | touch cabal.project.local 245 | echo "packages: ${PKGDIR_microaeson}" >> cabal.project 246 | if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then echo "package microaeson" >> cabal.project ; fi 247 | if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then echo " ghc-options: -Werror=missing-methods -Werror=missing-fields" >> cabal.project ; fi 248 | if [ $((HCNUMVER >= 90400)) -ne 0 ] ; then echo "package microaeson" >> cabal.project ; fi 249 | if [ $((HCNUMVER >= 90400)) -ne 0 ] ; then echo " ghc-options: -Werror=unused-packages" >> cabal.project ; fi 250 | if [ $((HCNUMVER >= 90000)) -ne 0 ] ; then echo "package microaeson" >> cabal.project ; fi 251 | if [ $((HCNUMVER >= 90000)) -ne 0 ] ; then echo " ghc-options: -Werror=incomplete-patterns -Werror=incomplete-uni-patterns" >> cabal.project ; fi 252 | cat >> cabal.project <> cabal.project 257 | fi 258 | $HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: any.$_ installed\n" unless /^(microaeson)$/; }' >> cabal.project.local 259 | cat cabal.project 260 | cat cabal.project.local 261 | - name: dump install plan 262 | run: | 263 | $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all 264 | cabal-plan 265 | - name: restore cache 266 | uses: actions/cache/restore@v4 267 | with: 268 | key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }} 269 | path: ~/.cabal/store 270 | restore-keys: ${{ runner.os }}-${{ matrix.compiler }}- 271 | - name: install dependencies 272 | run: | 273 | $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --dependencies-only -j2 all 274 | $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dependencies-only -j2 all 275 | - name: build w/o tests 276 | run: | 277 | $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all 278 | - name: build 279 | run: | 280 | $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --write-ghc-environment-files=always 281 | - name: tests 282 | run: | 283 | $CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct 284 | - name: cabal check 285 | run: | 286 | cd ${PKGDIR_microaeson} || false 287 | ${CABAL} -vnormal check 288 | - name: haddock 289 | run: | 290 | $CABAL v2-haddock --disable-documentation --haddock-all $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all 291 | - name: unconstrained build 292 | run: | 293 | rm -f cabal.project.local 294 | $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all 295 | - name: prepare for constraint sets 296 | run: | 297 | rm -f cabal.project.local 298 | - name: constraint set containers-0.8 299 | run: | 300 | if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then $CABAL v2-build $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='containers ^>= 0.8' all --dry-run ; fi 301 | if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then cabal-plan topo | sort ; fi 302 | if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then $CABAL v2-build $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='containers ^>= 0.8' --dependencies-only -j2 all ; fi 303 | if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then $CABAL v2-build $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='containers ^>= 0.8' all ; fi 304 | if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then $CABAL v2-test $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='containers ^>= 0.8' all ; fi 305 | - name: save cache 306 | if: always() 307 | uses: actions/cache/save@v4 308 | with: 309 | key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }} 310 | path: ~/.cabal/store 311 | -------------------------------------------------------------------------------- /src/Data/Aeson/Micro.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | {-# LANGUAGE DeriveDataTypeable #-} 3 | {-# LANGUAGE DeriveGeneric #-} 4 | {-# LANGUAGE FlexibleInstances #-} 5 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} 6 | {-# LANGUAGE OverloadedStrings #-} 7 | 8 | -- | Minimal JavaScript Object Notation (JSON) support as per . 9 | -- 10 | -- This API provides a subset (with a couple of divergences; see below) of 11 | -- [aeson API](https://hackage.haskell.org/package/aeson/docs/Data-Aeson.html) 12 | -- but puts the emphasis on simplicity rather than performance and features. 13 | -- 14 | -- The 'ToJSON' and 'FromJSON' instances are intended to have an encoding 15 | -- compatible with @aeson@'s encoding. 16 | -- 17 | -- == Limitations and divergences from @aeson@'s API 18 | -- 19 | -- In order to reduce the dependency footprint and keep the code 20 | -- simpler, the following divergences from the @aeson@ API have to be 21 | -- made: 22 | -- 23 | -- * There are no `FromJSON`/`ToJSON` instances for `Char` & `String`. 24 | -- * The type synonym (& the constructor of the same name) 'Object' uses @containers@'s 'Map.Map' rather than a 'HashMap' @unordered-containers@. 25 | -- * 'Array' is represented by an ordinary list rather than a 'Vector' from the @vector@ package. 26 | -- * 'Number' uses 'Double' instead of 'Scientific' 27 | -- 28 | module Data.Aeson.Micro 29 | ( -- * Core JSON types 30 | Value(..) 31 | , Object 32 | , Pair 33 | 34 | -- ** Constructors 35 | , (.=) 36 | , object 37 | , emptyArray 38 | , emptyObject 39 | 40 | -- ** Accessors 41 | , (.:) 42 | , (.:?) 43 | , (.:!) 44 | , (.!=) 45 | 46 | -- * Encoding and decoding 47 | , encode 48 | , encodeStrict 49 | , encodeToBuilder 50 | 51 | , decodeStrict 52 | , decode 53 | 54 | , decodeStrictN 55 | 56 | -- * Prism-style parsers 57 | , withObject 58 | , withText 59 | , withArray 60 | , withNumber 61 | , withBool 62 | 63 | -- * Type conversion 64 | , FromJSON(parseJSON) 65 | , Parser, parseMaybe 66 | , ToJSON(toJSON) 67 | 68 | ) where 69 | 70 | import Control.Monad 71 | #if !MIN_VERSION_base(4,13,0) 72 | import Control.Monad.Fail 73 | #endif 74 | import Data.Char 75 | import Data.Data (Data) 76 | import Data.Int 77 | import Data.List (intersperse) 78 | import Data.Monoid 79 | import Data.String 80 | import Data.Word 81 | import GHC.Generics (Generic) 82 | 83 | import Control.DeepSeq 84 | import qualified Data.ByteString as BS 85 | import Data.ByteString.Builder (Builder) 86 | import qualified Data.ByteString.Builder as BB 87 | import qualified Data.ByteString.Lazy as BS.Lazy 88 | import qualified Data.Map.Strict as Map 89 | import Data.Text (Text) 90 | import qualified Data.Text as T 91 | import qualified Data.Text.Encoding as T 92 | import qualified Data.Text.Lazy as TL 93 | 94 | import Data.Aeson.Micro.Parser 95 | import Data.Aeson.Micro.Scanner (Lexeme (..), scanLexemes) 96 | 97 | -- TODO: We may want to replace 'String' with 'Text' or 'ByteString' 98 | 99 | -- | A JSON value represented as a Haskell value. 100 | data Value = Object !Object 101 | | Array [Value] 102 | | String !Text 103 | | Number !Double 104 | | Bool !Bool 105 | | Null 106 | deriving (Eq, Read, Show, Generic, Data) 107 | 108 | instance NFData Value 109 | 110 | instance IsString Value where 111 | fromString = String . fromString 112 | 113 | -- | A key\/value pair for an 'Object' 114 | type Pair = (Text, Value) 115 | 116 | -- | A JSON \"object\" (key/value map). 117 | type Object = Map.Map Text Value 118 | 119 | infixr 8 .= 120 | 121 | -- | A key-value pair for encoding a JSON object. 122 | (.=) :: ToJSON v => Text -> v -> Pair 123 | k .= v = (k, toJSON v) 124 | 125 | -- | Create a 'Value' from a list of name\/value 'Pair's. 126 | object :: [Pair] -> Value 127 | object = Object . Map.fromList 128 | 129 | -- | The empty JSON 'Object' (i.e. @{}@). 130 | emptyObject :: Value 131 | emptyObject = Object mempty 132 | 133 | -- | The empty JSON 'Array' (i.e. @[]@). 134 | emptyArray :: Value 135 | emptyArray = Array mempty 136 | 137 | ---------------------------------------------------------------------------- 138 | 139 | (.:) :: FromJSON a => Object -> Text -> Parser a 140 | m .: k = maybe (pfail "key not found") parseJSON (Map.lookup k m) 141 | 142 | (.:?) :: FromJSON a => Object -> Text -> Parser (Maybe a) 143 | m .:? k = maybe (pure Nothing) parseJSON (Map.lookup k m) 144 | 145 | (.:!) :: FromJSON a => Object -> Text -> Parser (Maybe a) 146 | m .:! k = maybe (pure Nothing) (fmap Just . parseJSON) (Map.lookup k m) 147 | 148 | (.!=) :: Parser (Maybe a) -> a -> Parser a 149 | mv .!= def = fmap (maybe def id) mv 150 | 151 | ---------------------------------------------------------------------------- 152 | 153 | -- | A type that can be converted to JSON. 154 | class ToJSON a where 155 | -- | Convert a Haskell value to a JSON-friendly intermediate type. 156 | toJSON :: a -> Value 157 | 158 | instance ToJSON () where 159 | toJSON () = Array [] 160 | 161 | instance ToJSON Value where 162 | toJSON = id 163 | 164 | instance ToJSON Bool where 165 | toJSON = Bool 166 | 167 | instance ToJSON a => ToJSON [a] where 168 | toJSON = Array . map toJSON 169 | 170 | instance ToJSON v => ToJSON (Map.Map Text v) where 171 | toJSON = Object . Map.map toJSON 172 | 173 | instance ToJSON a => ToJSON (Maybe a) where 174 | toJSON Nothing = Null 175 | toJSON (Just a) = toJSON a 176 | 177 | instance (ToJSON a,ToJSON b) => ToJSON (a,b) where 178 | toJSON (a,b) = Array [toJSON a, toJSON b] 179 | 180 | instance (ToJSON a,ToJSON b,ToJSON c) => ToJSON (a,b,c) where 181 | toJSON (a,b,c) = Array [toJSON a, toJSON b, toJSON c] 182 | 183 | instance (ToJSON a,ToJSON b,ToJSON c, ToJSON d) => ToJSON (a,b,c,d) where 184 | toJSON (a,b,c,d) = Array [toJSON a, toJSON b, toJSON c, toJSON d] 185 | 186 | instance ToJSON Text where 187 | toJSON = String 188 | 189 | instance ToJSON TL.Text where 190 | toJSON = toJSON . TL.toStrict 191 | 192 | instance ToJSON Float where 193 | toJSON = Number . realToFrac 194 | 195 | instance ToJSON Double where 196 | toJSON = Number 197 | 198 | instance ToJSON Int where toJSON = Number . realToFrac 199 | instance ToJSON Int8 where toJSON = Number . realToFrac 200 | instance ToJSON Int16 where toJSON = Number . realToFrac 201 | instance ToJSON Int32 where toJSON = Number . realToFrac 202 | 203 | instance ToJSON Word where toJSON = Number . realToFrac 204 | instance ToJSON Word8 where toJSON = Number . realToFrac 205 | instance ToJSON Word16 where toJSON = Number . realToFrac 206 | instance ToJSON Word32 where toJSON = Number . realToFrac 207 | 208 | -- | Possibly lossy due to conversion to 'Double' 209 | instance ToJSON Int64 where toJSON = Number . realToFrac 210 | 211 | -- | Possibly lossy due to conversion to 'Double' 212 | instance ToJSON Word64 where toJSON = Number . realToFrac 213 | 214 | -- | Possibly lossy due to conversion to 'Double' 215 | instance ToJSON Integer where toJSON = Number . fromInteger 216 | 217 | ------------------------------------------------------------------------------ 218 | -- 'BB.Builder'-based encoding 219 | 220 | -- | Serialise value as JSON/UTF-8-encoded strict 'BS.ByteString' 221 | encodeStrict :: ToJSON a => a -> BS.ByteString 222 | encodeStrict = BS.Lazy.toStrict . encode 223 | 224 | -- | Serialise value as JSON/UTF-8-encoded lazy 'BS.Lazy.ByteString' 225 | encode :: ToJSON a => a -> BS.Lazy.ByteString 226 | encode = BB.toLazyByteString . encodeToBuilder 227 | 228 | -- | Serialise value as JSON/UTF8-encoded 'Builder' 229 | encodeToBuilder :: ToJSON a => a -> Builder 230 | encodeToBuilder = encodeValueBB . toJSON 231 | 232 | encodeValueBB :: Value -> Builder 233 | encodeValueBB jv = case jv of 234 | Bool True -> "true" 235 | Bool False -> "false" 236 | Null -> "null" 237 | Number n 238 | | isNaN n || isInfinite n -> encodeValueBB Null 239 | | Just i <- doubleToInt64 n -> BB.int64Dec i 240 | | otherwise -> BB.doubleDec n 241 | Array a -> encodeArrayBB a 242 | String s -> encodeStringBB s 243 | Object o -> encodeObjectBB o 244 | 245 | encodeArrayBB :: [Value] -> Builder 246 | encodeArrayBB [] = "[]" 247 | encodeArrayBB jvs = BB.char8 '[' <> go jvs <> BB.char8 ']' 248 | where 249 | go = Data.Monoid.mconcat . intersperse (BB.char8 ',') . map encodeValueBB 250 | 251 | encodeObjectBB :: Object -> Builder 252 | encodeObjectBB m 253 | | Map.null m = "{}" 254 | | otherwise = BB.char8 '{' <> go jvs <> BB.char8 '}' 255 | where 256 | jvs = Map.toList m 257 | go = Data.Monoid.mconcat . intersperse (BB.char8 ',') . map encPair 258 | encPair (l,x) = encodeStringBB l <> BB.char8 ':' <> encodeValueBB x 259 | 260 | encodeStringBB :: Text -> Builder 261 | encodeStringBB str = BB.char8 '"' <> go str <> BB.char8 '"' 262 | where 263 | go = T.encodeUtf8Builder . escapeText 264 | 265 | ------------------------------------------------------------------------------ 266 | -- helpers 267 | 268 | -- | Try to convert 'Double' into 'Int64', return 'Nothing' if not 269 | -- representable loss-free as integral 'Int64' value. 270 | doubleToInt64 :: Double -> Maybe Int64 271 | doubleToInt64 x 272 | | fromInteger x' == x 273 | , x' <= toInteger (maxBound :: Int64) 274 | , x' >= toInteger (minBound :: Int64) 275 | = Just (fromIntegral x') 276 | | otherwise = Nothing 277 | where 278 | x' = round x 279 | 280 | -- | Minimally escape a 'String' in accordance with [RFC 8259, "7. Strings"](https://tools.ietf.org/html/rfc8259#section-7) 281 | escapeText :: Text -> Text 282 | escapeText s 283 | | not (T.any needsEscape s) = s 284 | | otherwise = (T.pack . escape . T.unpack) s 285 | where 286 | escape [] = [] 287 | escape (x:xs) = case x of 288 | '\\' -> '\\':'\\':escape xs 289 | '"' -> '\\':'"':escape xs 290 | '\b' -> '\\':'b':escape xs 291 | '\f' -> '\\':'f':escape xs 292 | '\n' -> '\\':'n':escape xs 293 | '\r' -> '\\':'r':escape xs 294 | '\t' -> '\\':'t':escape xs 295 | c | ord c < 0x10 -> '\\':'u':'0':'0':'0':intToDigit (ord c):escape xs 296 | | ord c < 0x20 -> '\\':'u':'0':'0':'1':intToDigit (ord c - 0x10):escape xs 297 | | otherwise -> c : escape xs 298 | 299 | -- unescaped = %x20-21 / %x23-5B / %x5D-10FFFF 300 | needsEscape c = ord c < 0x20 || c `elem` ['\\','"'] 301 | 302 | ---------------------------------------------------------------------------- 303 | ---------------------------------------------------------------------------- 304 | 305 | -- | JSON Parser 'Monad' used by 'FromJSON' 306 | newtype Parser a = P { unP :: Maybe a } 307 | deriving (Functor,Applicative,Monad,MonadFail) 308 | 309 | -- | Run 'Parser'. 310 | -- 311 | -- A common use-case is @'parseMaybe' 'parseJSON'@. 312 | parseMaybe :: (a -> Parser b) -> a -> Maybe b 313 | parseMaybe m v = unP (m v) 314 | 315 | pfail :: String -> Parser a 316 | pfail _ = P Nothing 317 | 318 | -- | A type that JSON can be deserialised into 319 | class FromJSON a where 320 | -- | Decode a JSON value into a native Haskell type 321 | parseJSON :: Value -> Parser a 322 | 323 | instance FromJSON Value where 324 | parseJSON = pure 325 | 326 | instance FromJSON Bool where 327 | parseJSON = withBool "Bool" pure 328 | 329 | instance FromJSON Text where 330 | parseJSON = withText "Text" pure 331 | 332 | instance FromJSON TL.Text where 333 | parseJSON = withText "Text" (pure . TL.fromStrict) 334 | 335 | instance FromJSON a => FromJSON [a] where 336 | parseJSON = withArray "[a]" (mapM parseJSON) 337 | 338 | instance FromJSON Double where 339 | parseJSON Null = pure (0/0) 340 | parseJSON j = withNumber "Double" pure j 341 | 342 | instance FromJSON Float where 343 | parseJSON Null = pure (0/0) 344 | parseJSON j = withNumber "Float" (pure . realToFrac) j 345 | 346 | -- FIXME: lossy conversions 347 | 348 | instance FromJSON Integer where 349 | parseJSON = withNumber "Int" (pure . round) 350 | 351 | instance FromJSON Int where 352 | parseJSON = withNumber "Int" (pure . fromInteger . round) 353 | 354 | instance FromJSON Int8 where 355 | parseJSON = withNumber "Int8" (pure . fromInteger . round) 356 | 357 | instance FromJSON Int16 where 358 | parseJSON = withNumber "Int16" (pure . fromInteger . round) 359 | 360 | instance FromJSON Int32 where 361 | parseJSON = withNumber "Int32" (pure . fromInteger . round) 362 | 363 | instance FromJSON Int64 where 364 | parseJSON = withNumber "Int64" (pure . fromInteger . round) 365 | 366 | instance FromJSON Word where 367 | parseJSON = withNumber "Word" (pure . fromInteger . round) 368 | 369 | instance FromJSON Word8 where 370 | parseJSON = withNumber "Word8" (pure . fromInteger . round) 371 | 372 | instance FromJSON Word16 where 373 | parseJSON = withNumber "Word16" (pure . fromInteger . round) 374 | 375 | instance FromJSON Word32 where 376 | parseJSON = withNumber "Word32" (pure . fromInteger . round) 377 | 378 | instance FromJSON Word64 where 379 | parseJSON = withNumber "Word64" (pure . fromInteger . round) 380 | 381 | 382 | instance FromJSON () where 383 | parseJSON = withArray "()" $ \lst -> 384 | case lst of 385 | [] -> pure () 386 | _ -> pfail "expected ()" 387 | 388 | instance (FromJSON a, FromJSON b) => FromJSON (a,b) where 389 | parseJSON = withArray "(a,b)" $ \lst -> 390 | case lst of 391 | [a,b] -> liftM2 (,) (parseJSON a) (parseJSON b) 392 | _ -> pfail "expected (a,b)" 393 | 394 | instance (FromJSON a, FromJSON b, FromJSON c) => FromJSON (a,b,c) where 395 | parseJSON = withArray "(a,b,c)" $ \lst -> 396 | case lst of 397 | [a,b,c] -> liftM3 (,,) (parseJSON a) (parseJSON b) (parseJSON c) 398 | _ -> pfail "expected (a,b,c)" 399 | 400 | instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d) => FromJSON (a,b,c,d) where 401 | parseJSON = withArray "(a,b,c,d)" $ \lst -> 402 | case lst of 403 | [a,b,c,d] -> liftM4 (,,,) (parseJSON a) (parseJSON b) (parseJSON c) (parseJSON d) 404 | _ -> pfail "expected (a,b,c,d)" 405 | 406 | instance FromJSON a => FromJSON (Maybe a) where 407 | parseJSON Null = pure Nothing 408 | parseJSON j = Just <$> parseJSON j 409 | 410 | instance FromJSON Ordering where 411 | parseJSON = withText "{'LT','EQ','GT'}" $ \s -> 412 | case s of 413 | "LT" -> pure LT 414 | "EQ" -> pure EQ 415 | "GT" -> pure GT 416 | _ -> pfail "expected {'LT','EQ','GT'}" 417 | 418 | instance FromJSON v => FromJSON (Map.Map Text v) where 419 | parseJSON = withObject "Map Text v" $ mapM parseJSON 420 | 421 | -- "prisms" 422 | 423 | withBool :: String -> (Bool -> Parser a) -> Value -> Parser a 424 | withBool _ f (Bool arr) = f arr 425 | withBool expected _ v = typeMismatch expected v 426 | 427 | withText :: String -> (Text -> Parser a) -> Value -> Parser a 428 | withText _ f (String txt) = f txt 429 | withText expected _ v = typeMismatch expected v 430 | 431 | withArray :: String -> ([Value] -> Parser a) -> Value -> Parser a 432 | withArray _ f (Array lst) = f lst 433 | withArray expected _ v = typeMismatch expected v 434 | 435 | withObject :: String -> (Object -> Parser a) -> Value -> Parser a 436 | withObject _ f (Object obj) = f obj 437 | withObject expected _ v = typeMismatch expected v 438 | 439 | withNumber :: String -> (Double -> Parser a) -> Value -> Parser a 440 | withNumber _ f (Number n) = f n 441 | withNumber expected _ v = typeMismatch expected v 442 | 443 | typeMismatch :: String -> Value -> Parser a 444 | typeMismatch expected _ = pfail ("expected " ++ expected) 445 | 446 | ---------------------------------------------------------------------------- 447 | 448 | -- | Decode a single JSON document 449 | decode :: FromJSON a => BS.Lazy.ByteString -> Maybe a 450 | decode = decodeStrict . BS.Lazy.toStrict 451 | 452 | -- | Decode a single JSON document 453 | decodeStrict :: FromJSON a => BS.ByteString -> Maybe a 454 | decodeStrict bs = do 455 | v <- decodeValue bs 456 | unP (parseJSON v) 457 | 458 | -- | Decode multiple concatenated JSON documents 459 | decodeStrictN :: FromJSON a => BS.ByteString -> Maybe [a] 460 | decodeStrictN = go [] . scanLexemes 461 | where 462 | go acc [] = Just $! reverse acc 463 | go acc ls = do 464 | (ls', v) <- parseValue ls 465 | a <- unP (parseJSON v) 466 | go (a:acc) ls' 467 | 468 | ---- 469 | 470 | type LexStream = [(Lexeme,BS.ByteString)] 471 | 472 | decodeValue :: BS.ByteString -> Maybe Value 473 | decodeValue bs = case parseValue (scanLexemes bs) of 474 | Just ([], v) -> Just v 475 | _ -> Nothing 476 | 477 | parseValue :: LexStream -> Maybe (LexStream, Value) 478 | parseValue = goValue 479 | where 480 | goValue :: LexStream -> Maybe (LexStream, Value) 481 | goValue ((L_True,_):xs) = Just (xs,Bool True) 482 | goValue ((L_False,_):xs) = Just (xs,Bool False) 483 | goValue ((L_Null,_):xs) = Just (xs,Null) 484 | goValue ((L_Number,bs):xs) = (\n->(xs,Number n)) <$> decodeNumber bs 485 | goValue ((L_StrStart,_):xs) = goString xs 486 | goValue ((L_ArrStart,_):xs) = goArray xs 487 | goValue ((L_ObjStart,_):xs) = goObject xs 488 | goValue _ = Nothing 489 | 490 | goArray :: LexStream -> Maybe (LexStream, Value) 491 | goArray xs0 = (Array <$>) <$> go0 xs0 492 | where 493 | go0 ((L_ArrEnd,_):xs) = pure (xs, []) 494 | go0 xs = do 495 | (xs', v) <- goValue xs 496 | go1 [v] xs' 497 | 498 | go1 acc ((L_ArrEnd,_):xs) = pure (xs, reverse acc) 499 | go1 acc ((L_Comma, _):xs) = do 500 | (xs', v) <- goValue xs 501 | go1 (v:acc) xs' 502 | go1 _ _ = Nothing 503 | 504 | goObject :: LexStream -> Maybe (LexStream, Value) 505 | goObject xs0 = ((Object . Map.fromList) <$>) <$> go0 xs0 506 | where 507 | go0 ((L_ObjEnd,_):xs) = pure (xs, []) 508 | go0 xs = do 509 | ((L_Colon,_):xs', String k) <- goValue xs 510 | (xs'',v) <- goValue xs' 511 | go1 [(k,v)] xs'' 512 | 513 | go1 acc ((L_ObjEnd,_):xs) = pure (xs, reverse acc) 514 | go1 acc ((L_Comma, _):xs) = do 515 | ((L_Colon,_):xs', String k) <- goValue xs 516 | (xs'',v) <- goValue xs' 517 | go1 ((k,v):acc) xs'' 518 | go1 _ _ = Nothing 519 | 520 | goString :: LexStream -> Maybe (LexStream, Value) 521 | goString xs0 = ((String . T.pack) <$>) <$> go [] xs0 522 | where 523 | go _ [] = Nothing 524 | go acc ((lx,chunk):xs) = case lx of 525 | L_StrEnd -> pure (xs, concat (reverse acc)) 526 | 527 | L_StrUnescaped -> do 528 | s <- decodeUnescaped chunk 529 | go (s:acc) xs 530 | 531 | L_StrEscaped -> do 532 | c <- decodeEscaped chunk 533 | go ([c]:acc) xs 534 | 535 | L_StrEscapedHex -> do 536 | c <- decodeEscapedHex chunk 537 | go ([c]:acc) xs 538 | 539 | L_StrEscapedHexSurr -> do 540 | c <- decodeEscapedHexSurr chunk 541 | go ([c]:acc) xs 542 | 543 | _ -> Nothing 544 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------